From 50c959fc3366693174cdf3f1c82d0c01bbee926f Mon Sep 17 00:00:00 2001 From: Lukasz Czerwinski Date: Mon, 9 Sep 2013 16:09:25 +0200 Subject: [PATCH 01/13] spi: spi-s3c64xx: Use module_platform_driver() subsys_init_call() initializes driver too early. It's preventing to move DMA channel allocation at the begining (driver probe). This patch reduces and simplifies initalization code by using module_platform_driver() macro. It's also efficiently delaying driver startup. Signed-off-by: Lukasz Czerwinski Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 512b8893893b..20dd71237d3b 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1633,22 +1633,13 @@ static struct platform_driver s3c64xx_spi_driver = { .pm = &s3c64xx_spi_pm, .of_match_table = of_match_ptr(s3c64xx_spi_dt_match), }, + .probe = s3c64xx_spi_probe, .remove = s3c64xx_spi_remove, .id_table = s3c64xx_spi_driver_ids, }; MODULE_ALIAS("platform:s3c64xx-spi"); -static int __init s3c64xx_spi_init(void) -{ - return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe); -} -subsys_initcall(s3c64xx_spi_init); - -static void __exit s3c64xx_spi_exit(void) -{ - platform_driver_unregister(&s3c64xx_spi_driver); -} -module_exit(s3c64xx_spi_exit); +module_platform_driver(s3c64xx_spi_driver); MODULE_AUTHOR("Jaswinder Singh "); MODULE_DESCRIPTION("S3C64XX SPI Controller Driver"); From 666d5b4c742ba666eb68b467d777b7862f362ae5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 18:50:52 +0100 Subject: [PATCH 02/13] spi: core: Add devm_spi_register_master() Help simplify the cleanup code for SPI master drivers by providing a managed master registration function, ensuring that the master is automatically unregistered whenever the device is unbound. Signed-off-by: Mark Brown --- Documentation/driver-model/devres.txt | 3 +++ drivers/spi/spi.c | 35 +++++++++++++++++++++++++++ include/linux/spi/spi.h | 2 ++ 3 files changed, 40 insertions(+) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index fcb34a5697ea..84ea8216cc7d 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -302,3 +302,6 @@ PHY SLAVE DMA ENGINE devm_acpi_dma_controller_register() + +SPI + devm_spi_register_master() diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9e039c60c068..a586ceb111fc 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1245,6 +1245,41 @@ int spi_register_master(struct spi_master *master) } EXPORT_SYMBOL_GPL(spi_register_master); +static void devm_spi_unregister(struct device *dev, void *res) +{ + spi_unregister_master(*(struct spi_master **)res); +} + +/** + * dev_spi_register_master - register managed SPI master controller + * @dev: device managing SPI master + * @master: initialized master, originally from spi_alloc_master() + * Context: can sleep + * + * Register a SPI device as with spi_register_master() which will + * automatically be unregister + */ +int devm_spi_register_master(struct device *dev, struct spi_master *master) +{ + struct spi_master **ptr; + int ret; + + ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = spi_register_master(master); + if (ret != 0) { + *ptr = master; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_spi_register_master); + static int __unregister(struct device *dev, void *null) { spi_unregister_device(to_spi_device(dev)); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 887116dbce2c..4d634d66ba0b 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -434,6 +434,8 @@ extern struct spi_master * spi_alloc_master(struct device *host, unsigned size); extern int spi_register_master(struct spi_master *master); +extern int devm_spi_register_master(struct device *dev, + struct spi_master *master); extern void spi_unregister_master(struct spi_master *master); extern struct spi_master *spi_busnum_to_master(u16 busnum); From 91800f0e90050a4db4c77e940796f501e02af8be Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 18:55:53 +0100 Subject: [PATCH 03/13] spi/s3c64xx: Use managed registration Also improve the error reporting on failure and remove a duplicate put. This provides a small code saving. Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 20dd71237d3b..8bed27a4108c 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1428,9 +1428,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, sdd->regs + S3C64XX_SPI_INT_EN); - if (spi_register_master(master)) { - dev_err(&pdev->dev, "cannot register SPI master\n"); - ret = -EBUSY; + ret = devm_spi_register_master(&pdev->dev, master); + if (ret != 0) { + dev_err(&pdev->dev, "cannot register SPI master: %d\n", ret); goto err3; } @@ -1461,16 +1461,12 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); - spi_unregister_master(master); - writel(0, sdd->regs + S3C64XX_SPI_INT_EN); clk_disable_unprepare(sdd->src_clk); clk_disable_unprepare(sdd->clk); - spi_master_put(master); - return 0; } From a3b924df8fa9083d2291efc2d1dca93609953718 Mon Sep 17 00:00:00 2001 From: Mateusz Krawczuk Date: Mon, 23 Sep 2013 11:45:45 +0200 Subject: [PATCH 04/13] spi: s3c64xx: Add missing compatibles Add compatibles for s3c6410, s5pc100 and s5pc110/s5pv210 boards. Signed-off-by: Mateusz Krawczuk Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 8bed27a4108c..84cc6ac801fe 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1612,6 +1612,18 @@ static struct platform_device_id s3c64xx_spi_driver_ids[] = { }; static const struct of_device_id s3c64xx_spi_dt_match[] = { + { .compatible = "samsung,s3c2443-spi", + .data = (void *)&s3c2443_spi_port_config, + }, + { .compatible = "samsung,s3c6410-spi", + .data = (void *)&s3c6410_spi_port_config, + }, + { .compatible = "samsung,s5pc100-spi", + .data = (void *)&s5pc100_spi_port_config, + }, + { .compatible = "samsung,s5pv210-spi", + .data = (void *)&s5pv210_spi_port_config, + }, { .compatible = "samsung,exynos4210-spi", .data = (void *)&exynos4_spi_port_config, }, From 7d5f880b46f222ff83a76fd36b01f5a59bf32122 Mon Sep 17 00:00:00 2001 From: Mateusz Krawczuk Date: Tue, 24 Sep 2013 18:07:46 +0200 Subject: [PATCH 05/13] spi: s3c64xx: Allow build on all Samsung platforms Replace all symbols by simple dependency PLAT_SAMSUNG. Signed-off-by: Mateusz Krawczuk Signed-off-by: Kyungmin Park Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b9c53cc40e1f..d92d6690d615 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -393,7 +393,7 @@ config SPI_S3C24XX_FIQ config SPI_S3C64XX tristate "Samsung S3C64XX series type SPI" - depends on (ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5P64X0 || ARCH_EXYNOS) + depends on PLAT_SAMSUNG select S3C64XX_DMA if ARCH_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. From 3e2bd64d243ff0f3dc08c49ff218fed42ee9d981 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 11:52:35 +0100 Subject: [PATCH 06/13] spi/s3c64xx: Ensure runtime PM is enabled prior to registration Otherwise we may try to start transfers immediately and then fail to runtime resume the device causing us not to have clocks enabled. Signed-off-by: Mark Brown Reviewed-by: Sylwester Nawrocki Conflicts: drivers/spi/spi-s3c64xx.c --- drivers/spi/spi-s3c64xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 512b8893893b..a80376dc3a10 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1428,6 +1428,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, sdd->regs + S3C64XX_SPI_INT_EN); + pm_runtime_enable(&pdev->dev); + if (spi_register_master(master)) { dev_err(&pdev->dev, "cannot register SPI master\n"); ret = -EBUSY; @@ -1440,8 +1442,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) mem_res, sdd->rx_dma.dmach, sdd->tx_dma.dmach); - pm_runtime_enable(&pdev->dev); - return 0; err3: From 67651b29ef15e5ef9897ed8cb9072222c4cf3432 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 20:10:26 +0100 Subject: [PATCH 07/13] spi/s3c64xx: Flush FIFOs prior to cleaning up transfer Ensure that the FIFOs are fully drained before we deassert /CS or do any delays that have been requested in order to ensure that the behaviour visible on the bus matches that which was requested by the caller. Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 229c6b994be3..2e267ce9dc19 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -960,6 +960,8 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, goto out; } + flush_fifo(sdd); + if (xfer->delay_usecs) udelay(xfer->delay_usecs); @@ -972,8 +974,6 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, } msg->actual_length += xfer->len; - - flush_fifo(sdd); } out: From 8b06d5b8576d1df4122fa9ca40578ec7f094cb3e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 18:44:53 +0100 Subject: [PATCH 08/13] spi/s3c64xx: Check that clock enables succeed on runtime resume Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 2e267ce9dc19..dd5ed13436e5 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1524,9 +1524,17 @@ static int s3c64xx_spi_runtime_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + int ret; - clk_prepare_enable(sdd->src_clk); - clk_prepare_enable(sdd->clk); + ret = clk_prepare_enable(sdd->src_clk); + if (ret != 0) + return ret; + + ret = clk_prepare_enable(sdd->clk); + if (ret != 0) { + clk_disable_unprepare(sdd->src_clk); + return ret; + } return 0; } From 64d930ac116db25db814f76c31bfd4a7ea428f74 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 18:45:23 +0100 Subject: [PATCH 09/13] spi/s3c64xx: Remove unused gpios field from driver data Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index dd5ed13436e5..7f960db66763 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -205,7 +205,6 @@ struct s3c64xx_spi_driver_data { #endif struct s3c64xx_spi_port_config *port_conf; unsigned int port_id; - unsigned long gpios[4]; bool cs_gpio; }; From dd97e26849c16f484191f056b6e080cc30ebe98b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 18:58:55 +0100 Subject: [PATCH 10/13] spi/s3c64xx: Use core cs_gpio field Rather than using the driver custom platform data to store the chip select GPIO use the cs_gpio field provided by the SPI core, supporting future refectoring. Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 7f960db66763..43caeee8641a 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -558,22 +558,18 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi) { - struct s3c64xx_spi_csinfo *cs; - if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */ if (sdd->tgl_spi != spi) { /* if last mssg on diff device */ /* Deselect the last toggled device */ - cs = sdd->tgl_spi->controller_data; - if (sdd->cs_gpio) - gpio_set_value(cs->line, + if (spi->cs_gpio >= 0) + gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); } sdd->tgl_spi = NULL; } - cs = spi->controller_data; - if (sdd->cs_gpio) - gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0); + if (spi->cs_gpio >= 0) + gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 1 : 0); /* Start the signals */ writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); @@ -701,13 +697,11 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi) { - struct s3c64xx_spi_csinfo *cs = spi->controller_data; - if (sdd->tgl_spi == spi) sdd->tgl_spi = NULL; - if (sdd->cs_gpio) - gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1); + if (spi->cs_gpio >= 0) + gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); /* Quiese the signals */ writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); @@ -1070,6 +1064,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) cs->line, err); goto err_gpio_req; } + + spi->cs_gpio = cs->line; } spi_set_ctldata(spi, cs); @@ -1139,8 +1135,8 @@ static void s3c64xx_spi_cleanup(struct spi_device *spi) struct s3c64xx_spi_driver_data *sdd; sdd = spi_master_get_devdata(spi->master); - if (cs && sdd->cs_gpio) { - gpio_free(cs->line); + if (spi->cs_gpio) { + gpio_free(spi->cs_gpio); if (spi->dev.of_node) kfree(cs); } From 8c09daa1c963e2ab58029520dd2f3d54184d7258 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 19:56:31 +0100 Subject: [PATCH 11/13] spi/s3c64xx: Factor transfer start out of enable/disable_cs() The hardware level /CS handling is tied to the start of the data path so is rolled into the same function as we use to manipulate GPIO /CS. In order to support factoring out the /CS handling into the core separate the two and explicitly start transfers separately to the /CS handling. Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 43caeee8641a..2ab96fb28d22 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -570,9 +570,6 @@ static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd, if (spi->cs_gpio >= 0) gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 1 : 0); - - /* Start the signals */ - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); } static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, @@ -702,9 +699,6 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, if (spi->cs_gpio >= 0) gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); - - /* Quiese the signals */ - writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); } static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) @@ -930,6 +924,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, /* Slave Select */ enable_cs(sdd, spi); + /* Start the signals */ + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + spin_unlock_irqrestore(&sdd->lock, flags); status = wait_for_xfer(sdd, xfer, use_dma); @@ -970,10 +967,14 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, } out: - if (!cs_toggle || status) + if (!cs_toggle || status) { + /* Quiese the signals */ + writel(S3C64XX_SPI_SLAVE_SIG_INACT, + sdd->regs + S3C64XX_SPI_SLAVE_SEL); disable_cs(sdd, spi); - else + } else { sdd->tgl_spi = spi; + } s3c64xx_spi_unmap_mssg(sdd, msg); @@ -1112,11 +1113,13 @@ static int s3c64xx_spi_setup(struct spi_device *spi) } pm_runtime_put(&sdd->pdev->dev); + writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); disable_cs(sdd, spi); return 0; setup_exit: /* setup() returns with device de-selected */ + writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); disable_cs(sdd, spi); gpio_free(cs->line); From 0f5a751ace250097d3ad7835df7d376f21f44509 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 20:38:01 +0100 Subject: [PATCH 12/13] spi/s3c64xx: Enable GPIO /CS prior to starting hardware To help with bisection of future refactoring to share more of the code for handling a spi_message pull the enabling of GPIO based /CS prior to all the hardware setup for starting a transfer. Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 2ab96fb28d22..e6312322f66a 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -906,6 +906,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, s3c64xx_spi_config(sdd); } + /* Slave Select */ + enable_cs(sdd, spi); + /* Polling method for xfers not bigger than FIFO capacity */ use_dma = 0; if (!is_polling(sdd) && @@ -921,9 +924,6 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, enable_datapath(sdd, spi, xfer, use_dma); - /* Slave Select */ - enable_cs(sdd, spi); - /* Start the signals */ writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); From ebd805cc14bec607e74795b7933570f240508cb4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Sep 2013 19:56:31 +0100 Subject: [PATCH 13/13] spi/s3c64xx: Factor transfer start out of enable/disable_cs() The hardware level /CS handling is tied to the start of the data path so is rolled into the same function as we use to manipulate GPIO /CS. In order to support factoring out the /CS handling into the core separate the two and explicitly start transfers separately to the /CS handling. Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index e6312322f66a..8e732a1b8a9b 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -927,6 +927,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, /* Start the signals */ writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + /* Start the signals */ + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + spin_unlock_irqrestore(&sdd->lock, flags); status = wait_for_xfer(sdd, xfer, use_dma);