diff options
-rw-r--r-- | arch/arm/plat-samsung/include/plat/s3c64xx-spi.h | 3 | ||||
-rw-r--r-- | drivers/spi/spi_s3c64xx.c | 94 |
2 files changed, 59 insertions, 38 deletions
diff --git a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h index e5aba8f95b79..b226f7405e6b 100644 --- a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h +++ b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h | |||
@@ -32,6 +32,8 @@ struct s3c64xx_spi_csinfo { | |||
32 | * struct s3c64xx_spi_info - SPI Controller defining structure | 32 | * struct s3c64xx_spi_info - SPI Controller defining structure |
33 | * @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field. | 33 | * @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field. |
34 | * @src_clk_name: Platform name of the corresponding clock. | 34 | * @src_clk_name: Platform name of the corresponding clock. |
35 | * @clk_from_cmu: If the SPI clock/prescalar control block is present | ||
36 | * by the platform's clock-management-unit and not in SPI controller. | ||
35 | * @num_cs: Number of CS this controller emulates. | 37 | * @num_cs: Number of CS this controller emulates. |
36 | * @cfg_gpio: Configure pins for this SPI controller. | 38 | * @cfg_gpio: Configure pins for this SPI controller. |
37 | * @fifo_lvl_mask: All tx fifo_lvl fields start at offset-6 | 39 | * @fifo_lvl_mask: All tx fifo_lvl fields start at offset-6 |
@@ -41,6 +43,7 @@ struct s3c64xx_spi_csinfo { | |||
41 | struct s3c64xx_spi_info { | 43 | struct s3c64xx_spi_info { |
42 | int src_clk_nr; | 44 | int src_clk_nr; |
43 | char *src_clk_name; | 45 | char *src_clk_name; |
46 | bool clk_from_cmu; | ||
44 | 47 | ||
45 | int num_cs; | 48 | int num_cs; |
46 | 49 | ||
diff --git a/drivers/spi/spi_s3c64xx.c b/drivers/spi/spi_s3c64xx.c index e7b893f2a21b..9e0aa3c7b4d1 100644 --- a/drivers/spi/spi_s3c64xx.c +++ b/drivers/spi/spi_s3c64xx.c | |||
@@ -399,13 +399,18 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, | |||
399 | 399 | ||
400 | static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) | 400 | static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) |
401 | { | 401 | { |
402 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
402 | void __iomem *regs = sdd->regs; | 403 | void __iomem *regs = sdd->regs; |
403 | u32 val; | 404 | u32 val; |
404 | 405 | ||
405 | /* Disable Clock */ | 406 | /* Disable Clock */ |
406 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | 407 | if (sci->clk_from_cmu) { |
407 | val &= ~S3C64XX_SPI_ENCLK_ENABLE; | 408 | clk_disable(sdd->src_clk); |
408 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | 409 | } else { |
410 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | ||
411 | val &= ~S3C64XX_SPI_ENCLK_ENABLE; | ||
412 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
413 | } | ||
409 | 414 | ||
410 | /* Set Polarity and Phase */ | 415 | /* Set Polarity and Phase */ |
411 | val = readl(regs + S3C64XX_SPI_CH_CFG); | 416 | val = readl(regs + S3C64XX_SPI_CH_CFG); |
@@ -441,17 +446,25 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) | |||
441 | 446 | ||
442 | writel(val, regs + S3C64XX_SPI_MODE_CFG); | 447 | writel(val, regs + S3C64XX_SPI_MODE_CFG); |
443 | 448 | ||
444 | /* Configure Clock */ | 449 | if (sci->clk_from_cmu) { |
445 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | 450 | /* Configure Clock */ |
446 | val &= ~S3C64XX_SPI_PSR_MASK; | 451 | /* There is half-multiplier before the SPI */ |
447 | val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) | 452 | clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); |
448 | & S3C64XX_SPI_PSR_MASK); | 453 | /* Enable Clock */ |
449 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | 454 | clk_enable(sdd->src_clk); |
450 | 455 | } else { | |
451 | /* Enable Clock */ | 456 | /* Configure Clock */ |
452 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | 457 | val = readl(regs + S3C64XX_SPI_CLK_CFG); |
453 | val |= S3C64XX_SPI_ENCLK_ENABLE; | 458 | val &= ~S3C64XX_SPI_PSR_MASK; |
454 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | 459 | val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) |
460 | & S3C64XX_SPI_PSR_MASK); | ||
461 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
462 | |||
463 | /* Enable Clock */ | ||
464 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | ||
465 | val |= S3C64XX_SPI_ENCLK_ENABLE; | ||
466 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
467 | } | ||
455 | } | 468 | } |
456 | 469 | ||
457 | static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id, | 470 | static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id, |
@@ -806,7 +819,6 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
806 | struct s3c64xx_spi_driver_data *sdd; | 819 | struct s3c64xx_spi_driver_data *sdd; |
807 | struct s3c64xx_spi_info *sci; | 820 | struct s3c64xx_spi_info *sci; |
808 | struct spi_message *msg; | 821 | struct spi_message *msg; |
809 | u32 psr, speed; | ||
810 | unsigned long flags; | 822 | unsigned long flags; |
811 | int err = 0; | 823 | int err = 0; |
812 | 824 | ||
@@ -849,32 +861,37 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
849 | } | 861 | } |
850 | 862 | ||
851 | /* Check if we can provide the requested rate */ | 863 | /* Check if we can provide the requested rate */ |
852 | speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); /* Max possible */ | 864 | if (!sci->clk_from_cmu) { |
853 | 865 | u32 psr, speed; | |
854 | if (spi->max_speed_hz > speed) | 866 | |
855 | spi->max_speed_hz = speed; | 867 | /* Max possible */ |
856 | 868 | speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); | |
857 | psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; | 869 | |
858 | psr &= S3C64XX_SPI_PSR_MASK; | 870 | if (spi->max_speed_hz > speed) |
859 | if (psr == S3C64XX_SPI_PSR_MASK) | 871 | spi->max_speed_hz = speed; |
860 | psr--; | 872 | |
873 | psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; | ||
874 | psr &= S3C64XX_SPI_PSR_MASK; | ||
875 | if (psr == S3C64XX_SPI_PSR_MASK) | ||
876 | psr--; | ||
877 | |||
878 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | ||
879 | if (spi->max_speed_hz < speed) { | ||
880 | if (psr+1 < S3C64XX_SPI_PSR_MASK) { | ||
881 | psr++; | ||
882 | } else { | ||
883 | err = -EINVAL; | ||
884 | goto setup_exit; | ||
885 | } | ||
886 | } | ||
861 | 887 | ||
862 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | 888 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); |
863 | if (spi->max_speed_hz < speed) { | 889 | if (spi->max_speed_hz >= speed) |
864 | if (psr+1 < S3C64XX_SPI_PSR_MASK) { | 890 | spi->max_speed_hz = speed; |
865 | psr++; | 891 | else |
866 | } else { | ||
867 | err = -EINVAL; | 892 | err = -EINVAL; |
868 | goto setup_exit; | ||
869 | } | ||
870 | } | 893 | } |
871 | 894 | ||
872 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | ||
873 | if (spi->max_speed_hz >= speed) | ||
874 | spi->max_speed_hz = speed; | ||
875 | else | ||
876 | err = -EINVAL; | ||
877 | |||
878 | setup_exit: | 895 | setup_exit: |
879 | 896 | ||
880 | /* setup() returns with device de-selected */ | 897 | /* setup() returns with device de-selected */ |
@@ -896,7 +913,8 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) | |||
896 | /* Disable Interrupts - we use Polling if not DMA mode */ | 913 | /* Disable Interrupts - we use Polling if not DMA mode */ |
897 | writel(0, regs + S3C64XX_SPI_INT_EN); | 914 | writel(0, regs + S3C64XX_SPI_INT_EN); |
898 | 915 | ||
899 | writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, | 916 | if (!sci->clk_from_cmu) |
917 | writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, | ||
900 | regs + S3C64XX_SPI_CLK_CFG); | 918 | regs + S3C64XX_SPI_CLK_CFG); |
901 | writel(0, regs + S3C64XX_SPI_MODE_CFG); | 919 | writel(0, regs + S3C64XX_SPI_MODE_CFG); |
902 | writel(0, regs + S3C64XX_SPI_PACKET_CNT); | 920 | writel(0, regs + S3C64XX_SPI_PACKET_CNT); |