Skip to content

Commit

Permalink
spi/s3c64xx: Consider the clk_from_cmu flag
Browse files Browse the repository at this point in the history
Newer SoCs have the SPI clock scaling control in platform's
clock management unit. Inorder for such SoCs to work, we need
to check the flag clk_from_cmu before making any clock changes.

Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
  • Loading branch information
Jassi Brar authored and Grant Likely committed Sep 29, 2010
1 parent e02ddd4 commit b42a81c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 38 deletions.
3 changes: 3 additions & 0 deletions arch/arm/plat-samsung/include/plat/s3c64xx-spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ struct s3c64xx_spi_csinfo {
* struct s3c64xx_spi_info - SPI Controller defining structure
* @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field.
* @src_clk_name: Platform name of the corresponding clock.
* @clk_from_cmu: If the SPI clock/prescalar control block is present
* by the platform's clock-management-unit and not in SPI controller.
* @num_cs: Number of CS this controller emulates.
* @cfg_gpio: Configure pins for this SPI controller.
* @fifo_lvl_mask: All tx fifo_lvl fields start at offset-6
Expand All @@ -41,6 +43,7 @@ struct s3c64xx_spi_csinfo {
struct s3c64xx_spi_info {
int src_clk_nr;
char *src_clk_name;
bool clk_from_cmu;

int num_cs;

Expand Down
94 changes: 56 additions & 38 deletions drivers/spi/spi_s3c64xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,13 +399,18 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,

static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
{
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
u32 val;

/* Disable Clock */
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val &= ~S3C64XX_SPI_ENCLK_ENABLE;
writel(val, regs + S3C64XX_SPI_CLK_CFG);
if (sci->clk_from_cmu) {
clk_disable(sdd->src_clk);
} else {
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val &= ~S3C64XX_SPI_ENCLK_ENABLE;
writel(val, regs + S3C64XX_SPI_CLK_CFG);
}

/* Set Polarity and Phase */
val = readl(regs + S3C64XX_SPI_CH_CFG);
Expand Down Expand Up @@ -441,17 +446,25 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)

writel(val, regs + S3C64XX_SPI_MODE_CFG);

/* Configure Clock */
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val &= ~S3C64XX_SPI_PSR_MASK;
val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
& S3C64XX_SPI_PSR_MASK);
writel(val, regs + S3C64XX_SPI_CLK_CFG);

/* Enable Clock */
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val |= S3C64XX_SPI_ENCLK_ENABLE;
writel(val, regs + S3C64XX_SPI_CLK_CFG);
if (sci->clk_from_cmu) {
/* Configure Clock */
/* There is half-multiplier before the SPI */
clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
/* Enable Clock */
clk_enable(sdd->src_clk);
} else {
/* Configure Clock */
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val &= ~S3C64XX_SPI_PSR_MASK;
val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
& S3C64XX_SPI_PSR_MASK);
writel(val, regs + S3C64XX_SPI_CLK_CFG);

/* Enable Clock */
val = readl(regs + S3C64XX_SPI_CLK_CFG);
val |= S3C64XX_SPI_ENCLK_ENABLE;
writel(val, regs + S3C64XX_SPI_CLK_CFG);
}
}

static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id,
Expand Down Expand Up @@ -806,7 +819,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
struct spi_message *msg;
u32 psr, speed;
unsigned long flags;
int err = 0;

Expand Down Expand Up @@ -849,32 +861,37 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
}

/* Check if we can provide the requested rate */
speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); /* Max possible */

if (spi->max_speed_hz > speed)
spi->max_speed_hz = speed;

psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
psr &= S3C64XX_SPI_PSR_MASK;
if (psr == S3C64XX_SPI_PSR_MASK)
psr--;
if (!sci->clk_from_cmu) {
u32 psr, speed;

/* Max possible */
speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1);

if (spi->max_speed_hz > speed)
spi->max_speed_hz = speed;

psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
psr &= S3C64XX_SPI_PSR_MASK;
if (psr == S3C64XX_SPI_PSR_MASK)
psr--;

speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz < speed) {
if (psr+1 < S3C64XX_SPI_PSR_MASK) {
psr++;
} else {
err = -EINVAL;
goto setup_exit;
}
}

speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz < speed) {
if (psr+1 < S3C64XX_SPI_PSR_MASK) {
psr++;
} else {
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz >= speed)
spi->max_speed_hz = speed;
else
err = -EINVAL;
goto setup_exit;
}
}

speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
if (spi->max_speed_hz >= speed)
spi->max_speed_hz = speed;
else
err = -EINVAL;

setup_exit:

/* setup() returns with device de-selected */
Expand All @@ -896,7 +913,8 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
/* Disable Interrupts - we use Polling if not DMA mode */
writel(0, regs + S3C64XX_SPI_INT_EN);

writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
if (!sci->clk_from_cmu)
writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
regs + S3C64XX_SPI_CLK_CFG);
writel(0, regs + S3C64XX_SPI_MODE_CFG);
writel(0, regs + S3C64XX_SPI_PACKET_CNT);
Expand Down

0 comments on commit b42a81c

Please sign in to comment.