From 22a33632fb607a18148815548478265a672d5295 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 13 Jan 2016 16:20:07 +0200 Subject: spi: pxa2xx: Remove CONFIG_SPI_PXA2XX_DMA After removal of legacy PXA DMA code by the commit 6356437e65c2 ("spi: spi-pxa2xx: remove legacy PXA DMA bits") the CONFIG_SPI_PXA2XX_DMA follows the CONFIG_SPI_PXA2XX and cannot be disabled alone. Therefore remove this config symbol and dead definitions from the spi-pxa2xx.h. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 4 ---- drivers/spi/Makefile | 3 +-- drivers/spi/spi-pxa2xx.h | 35 ----------------------------------- 3 files changed, 1 insertion(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8b9c2a38d1cc..c0604aecf45f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -415,10 +415,6 @@ config SPI_PPC4xx help This selects a driver for the PPC4xx SPI Controller. -config SPI_PXA2XX_DMA - def_bool y - depends on SPI_PXA2XX - config SPI_PXA2XX tristate "PXA2xx SSP SPI master" depends on (ARCH_PXA || PCI || ACPI) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 31fb7fb2a0b6..13cee44ec94d 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -61,8 +61,7 @@ obj-$(CONFIG_SPI_TI_QSPI) += spi-ti-qspi.o obj-$(CONFIG_SPI_ORION) += spi-orion.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o -spi-pxa2xx-platform-objs := spi-pxa2xx.o -spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o +spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_QUP) += spi-qup.o diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 58efa98313aa..8e781c8b4374 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -147,20 +147,9 @@ static inline void write_SSSR_CS(struct driver_data *drv_data, u32 val) extern int pxa2xx_spi_flush(struct driver_data *drv_data); extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data); -/* - * Select the right DMA implementation. - */ -#if defined(CONFIG_SPI_PXA2XX_DMA) -#define SPI_PXA2XX_USE_DMA 1 #define MAX_DMA_LEN SZ_64K #define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) -#else -#undef SPI_PXA2XX_USE_DMA -#define MAX_DMA_LEN 0 -#define DEFAULT_DMA_CR1 0 -#endif -#ifdef SPI_PXA2XX_USE_DMA extern bool pxa2xx_spi_dma_is_possible(size_t len); extern int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data); extern irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data); @@ -173,29 +162,5 @@ extern int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, u8 bits_per_word, u32 *burst_code, u32 *threshold); -#else -static inline bool pxa2xx_spi_dma_is_possible(size_t len) { return false; } -static inline int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data) -{ - return 0; -} -#define pxa2xx_spi_dma_transfer NULL -static inline void pxa2xx_spi_dma_prepare(struct driver_data *drv_data, - u32 dma_burst) {} -static inline void pxa2xx_spi_dma_start(struct driver_data *drv_data) {} -static inline int pxa2xx_spi_dma_setup(struct driver_data *drv_data) -{ - return 0; -} -static inline void pxa2xx_spi_dma_release(struct driver_data *drv_data) {} -static inline int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, - struct spi_device *spi, - u8 bits_per_word, - u32 *burst_code, - u32 *threshold) -{ - return -ENODEV; -} -#endif #endif /* SPI_PXA2XX_H */ -- cgit v1.2.1 From ee03672d961bdac2ac1e593e9c29b0d3b7320409 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 26 Jan 2016 15:33:21 +0200 Subject: spi: pxa2xx: Print actual DMA/PIO transfer mode in debug messages Transfer debug messages don't actually show is the transfer really using DMA. Driver may fall back to PIO in case transfer size is not within the certain limits or fails to map DMA buffers but debug messages don't reveal that. Move these debug messages further in pump_transfers() where the actual transfer mode is known and use drv_data->dma_mapped flag instead of chip->enable_dma for printing the mode. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index ab9914ad8365..227e8bad19e6 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1001,19 +1001,6 @@ static void pump_transfers(unsigned long data) "pump_transfers: DMA burst size reduced to match bits_per_word\n"); } - /* NOTE: PXA25x_SSP _could_ use external clocking ... */ - cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, bits); - if (!pxa25x_ssp_comp(drv_data)) - dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", - drv_data->master->max_speed_hz - / (1 + ((cr0 & SSCR0_SCR(0xfff)) >> 8)), - chip->enable_dma ? "DMA" : "PIO"); - else - dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", - drv_data->master->max_speed_hz / 2 - / (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)), - chip->enable_dma ? "DMA" : "PIO"); - message->state = RUNNING_STATE; drv_data->dma_mapped = 0; @@ -1040,6 +1027,19 @@ static void pump_transfers(unsigned long data) write_SSSR_CS(drv_data, drv_data->clear_sr); } + /* NOTE: PXA25x_SSP _could_ use external clocking ... */ + cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, bits); + if (!pxa25x_ssp_comp(drv_data)) + dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", + drv_data->master->max_speed_hz + / (1 + ((cr0 & SSCR0_SCR(0xfff)) >> 8)), + drv_data->dma_mapped ? "DMA" : "PIO"); + else + dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", + drv_data->master->max_speed_hz / 2 + / (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)), + drv_data->dma_mapped ? "DMA" : "PIO"); + if (is_lpss_ssp(drv_data)) { if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff) != chip->lpss_rx_threshold) -- cgit v1.2.1 From 07550df04712c88717d2ab6578bb36bbd4305e35 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 4 Feb 2016 12:30:56 +0200 Subject: spi: pxa2xx: Update comment in int_transfer_complete() The register writes here actually don't stop the SSP but clean and disable interrupts and set the receive FIFO inactivity timeout to zero. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 227e8bad19e6..9b9a528a9fbd 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -572,7 +572,7 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) static void int_transfer_complete(struct driver_data *drv_data) { - /* Stop SSP */ + /* Clear and disable interrupts */ write_SSSR_CS(drv_data, drv_data->clear_sr); reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) -- cgit v1.2.1 From 7a8d44bc89e5cddcd5c0704a11a90484d36ba6ba Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 4 Feb 2016 12:30:57 +0200 Subject: spi: pxa2xx: Fix too early chipselect deassert There is a chance that chipselect is deasserted too early while the last clock cycle is still running. Protocol analyzers will see this as a failed last byte. This is more likely to occur with slow bitrates, for instance at 25 kbps. Reason for this is when using SPI mode 0 that both SPI host controller and SPI slave will drive the data lines at the falling edge of clock signal and sample at the rising edge. Receive FIFO gets the last bit now at the rising edge and code sees transfer to be finished either by the interrupt in PIO mode or by the DMA completion in DMA mode. The SSP Time Out register SSTO should take care of delaying the completion but it does not seems to have effect at least on Intel Skylake and Broxton even when using long enough values. Depending on timing code may get into point where chipselect is deasserted while the last clock cycle is still running at its second half cycle. Fix this by adding a wait loop in giveback() that waits until SSP becomes idle before deasserting the chipselect. Reported-by: Weifeng Voon Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 9b9a528a9fbd..ce66cf44bba5 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -496,6 +496,7 @@ static void giveback(struct driver_data *drv_data) { struct spi_transfer* last_transfer; struct spi_message *msg; + unsigned long timeout; msg = drv_data->cur_msg; drv_data->cur_msg = NULL; @@ -508,6 +509,12 @@ static void giveback(struct driver_data *drv_data) if (last_transfer->delay_usecs) udelay(last_transfer->delay_usecs); + /* Wait until SSP becomes idle before deasserting the CS */ + timeout = jiffies + msecs_to_jiffies(10); + while (pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY && + !time_after(jiffies, timeout)) + cpu_relax(); + /* Drop chip select UNLESS cs_change is true or we are returning * a message with an error, or next message is for another chip */ -- cgit v1.2.1 From 0c27d9cf7245d832d3d34f289c1bcd4f7f3fbd30 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 8 Feb 2016 17:14:29 +0200 Subject: spi: pxa2xx: Translate ACPI DeviceSelection to Linux chip select on Baytrail The Windows Baytrail SPI host controller driver uses 1 as the first (and only) value for ACPI DeviceSelection like can be seen in DSDT taken from Lenovo Thinkpad 10: Device (FPNT) { ... Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (UBUF, ResourceTemplate () { SpiSerialBus (0x0001, // DeviceSelection PolarityLow, FourWireMode, 0x08, ControllerInitiated, 0x007A1200, ClockPolarityLow, ClockPhaseFirst, "\\_SB.SPI1", 0x00, ResourceConsumer,,) This will fail to enumerate in Linux with following error: [ 0.241296] pxa2xx-spi 80860F0E:00: cs1 >= max 1 [ 0.241312] spi_master spi32766: failed to add SPI device VFSI6101:00 from ACPI To make the Linux SPI core successfully enumerate the device we provide a custom version of ->fw_translate_cs() that translates DeviceSelection correctly. Signed-off-by: Mika Westerberg Reviewed-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index ce66cf44bba5..7d6d3b74d25b 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1445,6 +1445,28 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) } #endif +static int pxa2xx_spi_fw_translate_cs(struct spi_master *master, unsigned cs) +{ + struct driver_data *drv_data = spi_master_get_devdata(master); + + if (has_acpi_companion(&drv_data->pdev->dev)) { + switch (drv_data->ssp_type) { + /* + * For Atoms the ACPI DeviceSelection used by the Windows + * driver starts from 1 instead of 0 so translate it here + * to match what Linux expects. + */ + case LPSS_BYT_SSP: + return cs - 1; + + default: + break; + } + } + + return cs; +} + static int pxa2xx_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1497,6 +1519,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->setup = setup; master->transfer_one_message = pxa2xx_spi_transfer_one_message; master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; + master->fw_translate_cs = pxa2xx_spi_fw_translate_cs; master->auto_runtime_pm = true; drv_data->ssp_type = ssp->type; -- cgit v1.2.1 From c1e4a53c6b8161ded3a44e3352ef38206d0967ea Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 8 Feb 2016 17:14:30 +0200 Subject: spi: pxa2xx: Move chip select control bits into lpss_config structure Some Intel LPSS SPI controllers, like the one in Braswell has these bits in a different location so move these bits to be part of the LPSS configuration. Since not all LPSS SPI controllers support multiple native chip selects we refactor selecting chip select to its own function and check control->cs_sel_mask before switching to another chip select. Signed-off-by: Mika Westerberg Reviewed-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 64 ++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 7d6d3b74d25b..81d68e01046a 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -65,8 +65,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) #define LPSS_CS_CONTROL_SW_MODE BIT(0) #define LPSS_CS_CONTROL_CS_HIGH BIT(1) -#define LPSS_CS_CONTROL_CS_SEL_SHIFT 8 -#define LPSS_CS_CONTROL_CS_SEL_MASK (3 << LPSS_CS_CONTROL_CS_SEL_SHIFT) #define LPSS_CAPS_CS_EN_SHIFT 9 #define LPSS_CAPS_CS_EN_MASK (0xf << LPSS_CAPS_CS_EN_SHIFT) @@ -82,6 +80,9 @@ struct lpss_config { u32 rx_threshold; u32 tx_threshold_lo; u32 tx_threshold_hi; + /* Chip select control */ + unsigned cs_sel_shift; + unsigned cs_sel_mask; }; /* Keep these sorted with enum pxa_ssp_type */ @@ -125,6 +126,8 @@ static const struct lpss_config lpss_platforms[] = { .rx_threshold = 1, .tx_threshold_lo = 16, .tx_threshold_hi = 48, + .cs_sel_shift = 8, + .cs_sel_mask = 3 << 8, }, }; @@ -288,37 +291,50 @@ static void lpss_ssp_setup(struct driver_data *drv_data) } } +static void lpss_ssp_select_cs(struct driver_data *drv_data, + const struct lpss_config *config) +{ + u32 value, cs; + + if (!config->cs_sel_mask) + return; + + value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); + + cs = drv_data->cur_msg->spi->chip_select; + cs <<= config->cs_sel_shift; + if (cs != (value & config->cs_sel_mask)) { + /* + * When switching another chip select output active the + * output must be selected first and wait 2 ssp_clk cycles + * before changing state to active. Otherwise a short + * glitch will occur on the previous chip select since + * output select is latched but state control is not. + */ + value &= ~config->cs_sel_mask; + value |= cs; + __lpss_ssp_write_priv(drv_data, + config->reg_cs_ctrl, value); + ndelay(1000000000 / + (drv_data->master->max_speed_hz / 2)); + } +} + static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) { const struct lpss_config *config; - u32 value, cs; + u32 value; config = lpss_get_config(drv_data); + if (enable) + lpss_ssp_select_cs(drv_data, config); + value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); - if (enable) { - cs = drv_data->cur_msg->spi->chip_select; - cs <<= LPSS_CS_CONTROL_CS_SEL_SHIFT; - if (cs != (value & LPSS_CS_CONTROL_CS_SEL_MASK)) { - /* - * When switching another chip select output active - * the output must be selected first and wait 2 ssp_clk - * cycles before changing state to active. Otherwise - * a short glitch will occur on the previous chip - * select since output select is latched but state - * control is not. - */ - value &= ~LPSS_CS_CONTROL_CS_SEL_MASK; - value |= cs; - __lpss_ssp_write_priv(drv_data, - config->reg_cs_ctrl, value); - ndelay(1000000000 / - (drv_data->master->max_speed_hz / 2)); - } + if (enable) value &= ~LPSS_CS_CONTROL_CS_HIGH; - } else { + else value |= LPSS_CS_CONTROL_CS_HIGH; - } __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value); } -- cgit v1.2.1 From 30f3a6ab44d8d06bb3d94f6320e4aa76df59d025 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 8 Feb 2016 17:14:31 +0200 Subject: spi: pxa2xx: Add support for both chip selects on Intel Braswell Intel Braswell LPSS SPI controller actually has two chip selects and there is no capabilities register where this could be found out. These two chip selects are controlled by bits which are in slightly differrent location than Broxton has. Braswell Windows driver also starts chip select (ACPI DeviceSelection) numbering from 1 so translate it to be suitable for Linux as well. Signed-off-by: Mika Westerberg Reviewed-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 81d68e01046a..0eb79368eabc 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -83,6 +83,7 @@ struct lpss_config { /* Chip select control */ unsigned cs_sel_shift; unsigned cs_sel_mask; + unsigned cs_num; }; /* Keep these sorted with enum pxa_ssp_type */ @@ -107,6 +108,19 @@ static const struct lpss_config lpss_platforms[] = { .tx_threshold_lo = 160, .tx_threshold_hi = 224, }, + { /* LPSS_BSW_SSP */ + .offset = 0x400, + .reg_general = 0x08, + .reg_ssp = 0x0c, + .reg_cs_ctrl = 0x18, + .reg_capabilities = -1, + .rx_threshold = 64, + .tx_threshold_lo = 160, + .tx_threshold_hi = 224, + .cs_sel_shift = 2, + .cs_sel_mask = 1 << 2, + .cs_num = 2, + }, { /* LPSS_SPT_SSP */ .offset = 0x200, .reg_general = -1, @@ -142,6 +156,7 @@ static bool is_lpss_ssp(const struct driver_data *drv_data) switch (drv_data->ssp_type) { case LPSS_LPT_SSP: case LPSS_BYT_SSP: + case LPSS_BSW_SSP: case LPSS_SPT_SSP: case LPSS_BXT_SSP: return true; @@ -1189,6 +1204,7 @@ static int setup(struct spi_device *spi) break; case LPSS_LPT_SSP: case LPSS_BYT_SSP: + case LPSS_BSW_SSP: case LPSS_SPT_SSP: case LPSS_BXT_SSP: config = lpss_get_config(drv_data); @@ -1336,7 +1352,7 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { { "INT3430", LPSS_LPT_SSP }, { "INT3431", LPSS_LPT_SSP }, { "80860F0E", LPSS_BYT_SSP }, - { "8086228E", LPSS_BYT_SSP }, + { "8086228E", LPSS_BSW_SSP }, { }, }; MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); @@ -1473,6 +1489,7 @@ static int pxa2xx_spi_fw_translate_cs(struct spi_master *master, unsigned cs) * to match what Linux expects. */ case LPSS_BYT_SSP: + case LPSS_BSW_SSP: return cs - 1; default: @@ -1622,6 +1639,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) tmp &= LPSS_CAPS_CS_EN_MASK; tmp >>= LPSS_CAPS_CS_EN_SHIFT; platform_info->num_chipselect = ffz(tmp); + } else if (config->cs_num) { + platform_info->num_chipselect = config->cs_num; } } master->num_chipselect = platform_info->num_chipselect; -- cgit v1.2.1 From d780c3711d9df9bacd56b71cf23443b895a331ca Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 14 Dec 2015 15:20:18 +0000 Subject: spi: core: added spi_resource management SPI resource management framework used while processing a spi_message via the spi-core. The basic idea is taken from devres, but as the allocation may happen fairly frequently, some provisioning (in the form of an unused spi_device pointer argument to spi_res_alloc) has been made so that at a later stage we may implement reuse objects allocated earlier avoiding the repeated allocation by keeping a cache of objects that we can reuse. This framework can get used for: * rewriting spi_messages * to fullfill alignment requirements of the spi_master HW * to fullfill transfer length requirements (e.g: transfers need to be less than 64k) * consolidate spi_messages with multiple transfers into a single transfer when the total transfer length is below a threshold. * reimplement spi_unmap_buf without explicitly needing to check if it has been mapped Signed-off-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 47eff8012a77..894ed0357dd7 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1024,6 +1024,8 @@ out: if (msg->status && master->handle_err) master->handle_err(master, msg); + spi_res_release(master, msg); + spi_finalize_current_message(master); return ret; @@ -2013,6 +2015,95 @@ struct spi_master *spi_busnum_to_master(u16 bus_num) } EXPORT_SYMBOL_GPL(spi_busnum_to_master); +/*-------------------------------------------------------------------------*/ + +/* Core methods for SPI resource management */ + +/** + * spi_res_alloc - allocate a spi resource that is life-cycle managed + * during the processing of a spi_message while using + * spi_transfer_one + * @spi: the spi device for which we allocate memory + * @release: the release code to execute for this resource + * @size: size to alloc and return + * @gfp: GFP allocation flags + * + * Return: the pointer to the allocated data + * + * This may get enhanced in the future to allocate from a memory pool + * of the @spi_device or @spi_master to avoid repeated allocations. + */ +void *spi_res_alloc(struct spi_device *spi, + spi_res_release_t release, + size_t size, gfp_t gfp) +{ + struct spi_res *sres; + + sres = kzalloc(sizeof(*sres) + size, gfp); + if (!sres) + return NULL; + + INIT_LIST_HEAD(&sres->entry); + sres->release = release; + + return sres->data; +} +EXPORT_SYMBOL_GPL(spi_res_alloc); + +/** + * spi_res_free - free an spi resource + * @res: pointer to the custom data of a resource + * + */ +void spi_res_free(void *res) +{ + struct spi_res *sres = container_of(res, struct spi_res, data); + + if (!res) + return; + + WARN_ON(!list_empty(&sres->entry)); + kfree(sres); +} +EXPORT_SYMBOL_GPL(spi_res_free); + +/** + * spi_res_add - add a spi_res to the spi_message + * @message: the spi message + * @res: the spi_resource + */ +void spi_res_add(struct spi_message *message, void *res) +{ + struct spi_res *sres = container_of(res, struct spi_res, data); + + WARN_ON(!list_empty(&sres->entry)); + list_add_tail(&sres->entry, &message->resources); +} +EXPORT_SYMBOL_GPL(spi_res_add); + +/** + * spi_res_release - release all spi resources for this message + * @master: the @spi_master + * @message: the @spi_message + */ +void spi_res_release(struct spi_master *master, + struct spi_message *message) +{ + struct spi_res *res; + + while (!list_empty(&message->resources)) { + res = list_last_entry(&message->resources, + struct spi_res, entry); + + if (res->release) + res->release(master, message, res->data); + + list_del(&res->entry); + + kfree(res); + } +} +EXPORT_SYMBOL_GPL(spi_res_release); /*-------------------------------------------------------------------------*/ -- cgit v1.2.1 From 523baf5a0609690cb742b3662b7ccac0ea0b2ef2 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 14 Dec 2015 15:20:19 +0000 Subject: spi: core: add spi_replace_transfers method Add the spi_replace_transfers method that can get used to replace some spi_transfers from a spi_message with other transfers. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 894ed0357dd7..2ec8e66a8098 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2107,6 +2107,138 @@ EXPORT_SYMBOL_GPL(spi_res_release); /*-------------------------------------------------------------------------*/ +/* Core methods for spi_message alterations */ + +static void __spi_replace_transfers_release(struct spi_master *master, + struct spi_message *msg, + void *res) +{ + struct spi_replaced_transfers *rxfer = res; + size_t i; + + /* call extra callback if requested */ + if (rxfer->release) + rxfer->release(master, msg, res); + + /* insert replaced transfers back into the message */ + list_splice(&rxfer->replaced_transfers, rxfer->replaced_after); + + /* remove the formerly inserted entries */ + for (i = 0; i < rxfer->inserted; i++) + list_del(&rxfer->inserted_transfers[i].transfer_list); +} + +/** + * spi_replace_transfers - replace transfers with several transfers + * and register change with spi_message.resources + * @msg: the spi_message we work upon + * @xfer_first: the first spi_transfer we want to replace + * @remove: number of transfers to remove + * @insert: the number of transfers we want to insert instead + * @release: extra release code necessary in some circumstances + * @extradatasize: extra data to allocate (with alignment guarantees + * of struct @spi_transfer) + * + * Returns: pointer to @spi_replaced_transfers, + * PTR_ERR(...) in case of errors. + */ +struct spi_replaced_transfers *spi_replace_transfers( + struct spi_message *msg, + struct spi_transfer *xfer_first, + size_t remove, + size_t insert, + spi_replaced_release_t release, + size_t extradatasize, + gfp_t gfp) +{ + struct spi_replaced_transfers *rxfer; + struct spi_transfer *xfer; + size_t i; + + /* allocate the structure using spi_res */ + rxfer = spi_res_alloc(msg->spi, __spi_replace_transfers_release, + insert * sizeof(struct spi_transfer) + + sizeof(struct spi_replaced_transfers) + + extradatasize, + gfp); + if (!rxfer) + return ERR_PTR(-ENOMEM); + + /* the release code to invoke before running the generic release */ + rxfer->release = release; + + /* assign extradata */ + if (extradatasize) + rxfer->extradata = + &rxfer->inserted_transfers[insert]; + + /* init the replaced_transfers list */ + INIT_LIST_HEAD(&rxfer->replaced_transfers); + + /* assign the list_entry after which we should reinsert + * the @replaced_transfers - it may be spi_message.messages! + */ + rxfer->replaced_after = xfer_first->transfer_list.prev; + + /* remove the requested number of transfers */ + for (i = 0; i < remove; i++) { + /* if the entry after replaced_after it is msg->transfers + * then we have been requested to remove more transfers + * than are in the list + */ + if (rxfer->replaced_after->next == &msg->transfers) { + dev_err(&msg->spi->dev, + "requested to remove more spi_transfers than are available\n"); + /* insert replaced transfers back into the message */ + list_splice(&rxfer->replaced_transfers, + rxfer->replaced_after); + + /* free the spi_replace_transfer structure */ + spi_res_free(rxfer); + + /* and return with an error */ + return ERR_PTR(-EINVAL); + } + + /* remove the entry after replaced_after from list of + * transfers and add it to list of replaced_transfers + */ + list_move_tail(rxfer->replaced_after->next, + &rxfer->replaced_transfers); + } + + /* create copy of the given xfer with identical settings + * based on the first transfer to get removed + */ + for (i = 0; i < insert; i++) { + /* we need to run in reverse order */ + xfer = &rxfer->inserted_transfers[insert - 1 - i]; + + /* copy all spi_transfer data */ + memcpy(xfer, xfer_first, sizeof(*xfer)); + + /* add to list */ + list_add(&xfer->transfer_list, rxfer->replaced_after); + + /* clear cs_change and delay_usecs for all but the last */ + if (i) { + xfer->cs_change = false; + xfer->delay_usecs = 0; + } + } + + /* set up inserted */ + rxfer->inserted = insert; + + /* and register it with spi_res/spi_message */ + spi_res_add(msg, rxfer); + + return rxfer; +} +EXPORT_SYMBOL_GPL(spi_replace_transfers); + +/*-------------------------------------------------------------------------*/ + /* Core methods for SPI master protocol drivers. Some of the * other core methods are currently defined as inline functions. */ -- cgit v1.2.1 From d9f1212272818420fcde611a940c1ad611a8b785 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 14 Dec 2015 15:20:20 +0000 Subject: spi: core: add spi_split_transfers_maxsize Add spi_split_transfers_maxsize method that splits spi_transfers transparently into multiple transfers that are below the given max-size. This makes use of the spi_res framework via spi_replace_transfers to allocate/free the extra transfers as well as reverting back the changes applied while processing the spi_message. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 2ec8e66a8098..34e3741504f9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -144,6 +144,8 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535"); SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+"); +SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu"); + static struct attribute *spi_dev_attrs[] = { &dev_attr_modalias.attr, NULL, @@ -181,6 +183,7 @@ static struct attribute *spi_device_statistics_attrs[] = { &dev_attr_spi_device_transfer_bytes_histo14.attr, &dev_attr_spi_device_transfer_bytes_histo15.attr, &dev_attr_spi_device_transfer_bytes_histo16.attr, + &dev_attr_spi_device_transfers_split_maxsize.attr, NULL, }; @@ -223,6 +226,7 @@ static struct attribute *spi_master_statistics_attrs[] = { &dev_attr_spi_master_transfer_bytes_histo14.attr, &dev_attr_spi_master_transfer_bytes_histo15.attr, &dev_attr_spi_master_transfer_bytes_histo16.attr, + &dev_attr_spi_master_transfers_split_maxsize.attr, NULL, }; @@ -2237,6 +2241,113 @@ struct spi_replaced_transfers *spi_replace_transfers( } EXPORT_SYMBOL_GPL(spi_replace_transfers); +int __spi_split_transfer_maxsize(struct spi_master *master, + struct spi_message *msg, + struct spi_transfer **xferp, + size_t maxsize, + gfp_t gfp) +{ + struct spi_transfer *xfer = *xferp, *xfers; + struct spi_replaced_transfers *srt; + size_t offset; + size_t count, i; + + /* warn once about this fact that we are splitting a transfer */ + dev_warn_once(&msg->spi->dev, + "spi_transfer of length %i exceed max length of %i - needed to split transfers\n", + xfer->len, maxsize); + + /* calculate how many we have to replace */ + count = DIV_ROUND_UP(xfer->len, maxsize); + + /* create replacement */ + srt = spi_replace_transfers(msg, xfer, 1, count, NULL, 0, gfp); + if (!srt) + return -ENOMEM; + xfers = srt->inserted_transfers; + + /* now handle each of those newly inserted spi_transfers + * note that the replacements spi_transfers all are preset + * to the same values as *xferp, so tx_buf, rx_buf and len + * are all identical (as well as most others) + * so we just have to fix up len and the pointers. + * + * this also includes support for the depreciated + * spi_message.is_dma_mapped interface + */ + + /* the first transfer just needs the length modified, so we + * run it outside the loop + */ + xfers[0].len = min(maxsize, xfer[0].len); + + /* all the others need rx_buf/tx_buf also set */ + for (i = 1, offset = maxsize; i < count; offset += maxsize, i++) { + /* update rx_buf, tx_buf and dma */ + if (xfers[i].rx_buf) + xfers[i].rx_buf += offset; + if (xfers[i].rx_dma) + xfers[i].rx_dma += offset; + if (xfers[i].tx_buf) + xfers[i].tx_buf += offset; + if (xfers[i].tx_dma) + xfers[i].tx_dma += offset; + + /* update length */ + xfers[i].len = min(maxsize, xfers[i].len - offset); + } + + /* we set up xferp to the last entry we have inserted, + * so that we skip those already split transfers + */ + *xferp = &xfers[count - 1]; + + /* increment statistics counters */ + SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, + transfers_split_maxsize); + SPI_STATISTICS_INCREMENT_FIELD(&msg->spi->statistics, + transfers_split_maxsize); + + return 0; +} + +/** + * spi_split_tranfers_maxsize - split spi transfers into multiple transfers + * when an individual transfer exceeds a + * certain size + * @master: the @spi_master for this transfer + * @message: the @spi_message to transform + * @max_size: the maximum when to apply this + * + * Return: status of transformation + */ +int spi_split_transfers_maxsize(struct spi_master *master, + struct spi_message *msg, + size_t maxsize, + gfp_t gfp) +{ + struct spi_transfer *xfer; + int ret; + + /* iterate over the transfer_list, + * but note that xfer is advanced to the last transfer inserted + * to avoid checking sizes again unnecessarily (also xfer does + * potentiall belong to a different list by the time the + * replacement has happened + */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (xfer->len > maxsize) { + ret = __spi_split_transfer_maxsize( + master, msg, &xfer, maxsize, gfp); + if (ret) + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(spi_split_transfers_maxsize); + /*-------------------------------------------------------------------------*/ /* Core methods for SPI master protocol drivers. Some of the -- cgit v1.2.1 From 657d32efe04197736ed328a34ba659b5172c8cce Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 12 Feb 2016 09:38:33 +0300 Subject: spi: checking for NULL instead of IS_ERR spi_replace_transfers() returns error pointers on error, it never returns NULL. Fixes: d9f121227281 ('spi: core: add spi_split_transfers_maxsize') Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 34e3741504f9..801e8900898b 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2262,8 +2262,8 @@ int __spi_split_transfer_maxsize(struct spi_master *master, /* create replacement */ srt = spi_replace_transfers(msg, xfer, 1, count, NULL, 0, gfp); - if (!srt) - return -ENOMEM; + if (IS_ERR(srt)) + return PTR_ERR(srt); xfers = srt->inserted_transfers; /* now handle each of those newly inserted spi_transfers -- cgit v1.2.1 From 08933418d71e52f60859319ef9fb4e6f754297c1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 14 Feb 2016 13:33:50 -0200 Subject: spi: core: Staticize __spi_split_transfer_maxsize() __spi_split_transfer_maxsize() can be made static as it is only used in this file. This fixes the following sparse warning: drivers/spi/spi.c:2266:5: warning: symbol '__spi_split_transfer_maxsize' was not declared. Should it be static? Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 801e8900898b..9490273ed811 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2241,11 +2241,11 @@ struct spi_replaced_transfers *spi_replace_transfers( } EXPORT_SYMBOL_GPL(spi_replace_transfers); -int __spi_split_transfer_maxsize(struct spi_master *master, - struct spi_message *msg, - struct spi_transfer **xferp, - size_t maxsize, - gfp_t gfp) +static int __spi_split_transfer_maxsize(struct spi_master *master, + struct spi_message *msg, + struct spi_transfer **xferp, + size_t maxsize, + gfp_t gfp) { struct spi_transfer *xfer = *xferp, *xfers; struct spi_replaced_transfers *srt; -- cgit v1.2.1 From 22640c8a2736d1127a3fd2380f6ed08670b22ed9 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 17 Feb 2016 13:52:12 +0200 Subject: spi: pl022: Remove obsolete struct pl022 members from kerneldoc These structure members were removed in 2012 by the commit ffbbdd21329f ("spi: create a message queueing infrastructure"). Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 5e5fd77e2711..f7f7ba17b40e 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -346,13 +346,6 @@ struct vendor_data { * @clk: outgoing clock "SPICLK" for the SPI bus * @master: SPI framework hookup * @master_info: controller-specific data from machine setup - * @kworker: thread struct for message pump - * @kworker_task: pointer to task for message pump kworker thread - * @pump_messages: work struct for scheduling work to the message pump - * @queue_lock: spinlock to syncronise access to message queue - * @queue: message queue - * @busy: message pump is busy - * @running: message pump is running * @pump_transfers: Tasklet used in Interrupt Transfer mode * @cur_msg: Pointer to current spi_message being processed * @cur_transfer: Pointer to current spi_transfer -- cgit v1.2.1 From 7d62f51e74c62afc79f63c70ae48f1b8de57f5d4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 17 Feb 2016 15:42:27 -0200 Subject: spi: core: Use %zu for printing 'size_t' type Use %zu for printing 'size_t' type in order to fix the following build warning on ARM64: drivers/spi/spi.c: In function '__spi_split_transfer_maxsize': drivers/spi/spi.c:2278:2: warning: format '%i' expects argument of type 'int', but argument 4 has type 'size_t' [-Wformat=] Signed-off-by: Fabio Estevam Acked-by: Geert Uytterhoeven Acked-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9490273ed811..9ae199db171b 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2254,7 +2254,7 @@ static int __spi_split_transfer_maxsize(struct spi_master *master, /* warn once about this fact that we are splitting a transfer */ dev_warn_once(&msg->spi->dev, - "spi_transfer of length %i exceed max length of %i - needed to split transfers\n", + "spi_transfer of length %i exceed max length of %zu - needed to split transfers\n", xfer->len, maxsize); /* calculate how many we have to replace */ -- cgit v1.2.1 From c8dab77a1f92095674e4d7fcb151cac4630e10f9 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 17 Feb 2016 15:42:28 -0200 Subject: spi: core: Use min_t(size_t,..) Use min_t(size_t,..) in order to avoid the following build warning on ARM64: include/linux/kernel.h:754:17: warning: comparison of distinct pointer types lacks a cast (void) (&_min1 == &_min2); \ ^ drivers/spi/spi.c:2304:17: note: in expansion of macro 'min' xfers[0].len = min(maxsize, xfer[0].len); Suggested-by: Geert Uytterhoeven Signed-off-by: Fabio Estevam Acked-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9ae199db171b..390d9ee937b6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2279,7 +2279,7 @@ static int __spi_split_transfer_maxsize(struct spi_master *master, /* the first transfer just needs the length modified, so we * run it outside the loop */ - xfers[0].len = min(maxsize, xfer[0].len); + xfers[0].len = min_t(size_t, maxsize, xfer[0].len); /* all the others need rx_buf/tx_buf also set */ for (i = 1, offset = maxsize; i < count; offset += maxsize, i++) { -- cgit v1.2.1 From 058853979ffc6a1e83c913a692aff8db84f6c417 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Thu, 18 Feb 2016 15:53:11 +0000 Subject: spi: docbook: add missing parameter documentation Add missing docbook documentation for the gfp parameter in function spi_replace_transfers. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 390d9ee937b6..b0c5d45641ec 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2142,6 +2142,7 @@ static void __spi_replace_transfers_release(struct spi_master *master, * @release: extra release code necessary in some circumstances * @extradatasize: extra data to allocate (with alignment guarantees * of struct @spi_transfer) + * @gfp: gfp flags * * Returns: pointer to @spi_replaced_transfers, * PTR_ERR(...) in case of errors. -- cgit v1.2.1 From 7c04b792d30ef8d62259f24065cdeddbfb4afe3d Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Fri, 19 Feb 2016 15:01:21 +0200 Subject: spi: pxa2xx: Remove unused DMA buffer mappings Only legacy PXA DMA implementation was using these rx_dma and tx_dma DMA addresses so they are not needed after commit 6356437e65c2 ("spi: spi-pxa2xx: remove legacy PXA DMA bits"). Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 2 -- drivers/spi/spi-pxa2xx.h | 2 -- 2 files changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 0eb79368eabc..2c4e8ccd573b 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -995,8 +995,6 @@ static void pump_transfers(unsigned long data) drv_data->tx_end = drv_data->tx + transfer->len; drv_data->rx = transfer->rx_buf; drv_data->rx_end = drv_data->rx + transfer->len; - drv_data->rx_dma = transfer->rx_dma; - drv_data->tx_dma = transfer->tx_dma; drv_data->len = transfer->len; drv_data->write = drv_data->tx ? chip->write : null_writer; drv_data->read = drv_data->rx ? chip->read : null_reader; diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 8e781c8b4374..a1ef88948144 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -69,8 +69,6 @@ struct driver_data { void *rx; void *rx_end; int dma_mapped; - dma_addr_t rx_dma; - dma_addr_t tx_dma; size_t rx_map_len; size_t tx_map_len; u8 n_bytes; -- cgit v1.2.1 From caba248db2863466a886735cb8d5704048063ac9 Mon Sep 17 00:00:00 2001 From: Leif Liddy Date: Sat, 20 Feb 2016 20:20:22 +0100 Subject: spi: spi-pxa2xx-pci: Add ID and driver type for WildcatPoint PCH WildcatPoint PCH as seen on MacBook 12-inch (Early 2015) has a PCI enabled SPI controller. Enable it by adding its ID to the corresponding driver. The ACPI enumerated name for this SPI controller (found in spi-pxa2xx.c) is INT33C1. Therefore, we associate the SPI controller with the corresponding type of LPSS_LPT_SSP. Signed-off-by: Leif Liddy Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index d19d7f28aecb..520ed1dd5780 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -19,6 +19,7 @@ enum { PORT_BSW1, PORT_BSW2, PORT_QUARK_X1000, + PORT_LPT, }; struct pxa_spi_info { @@ -42,6 +43,9 @@ static struct dw_dma_slave bsw1_rx_param = { .src_id = 7 }; static struct dw_dma_slave bsw2_tx_param = { .dst_id = 8 }; static struct dw_dma_slave bsw2_rx_param = { .src_id = 9 }; +static struct dw_dma_slave lpt_tx_param = { .dst_id = 0 }; +static struct dw_dma_slave lpt_rx_param = { .src_id = 1 }; + static bool lpss_dma_filter(struct dma_chan *chan, void *param) { struct dw_dma_slave *dws = param; @@ -98,6 +102,14 @@ static struct pxa_spi_info spi_info_configs[] = { .num_chipselect = 1, .max_clk_rate = 50000000, }, + [PORT_LPT] = { + .type = LPSS_LPT_SSP, + .port_id = 0, + .num_chipselect = 1, + .max_clk_rate = 50000000, + .tx_param = &lpt_tx_param, + .rx_param = &lpt_rx_param, + }, }; static int pxa2xx_spi_pci_probe(struct pci_dev *dev, @@ -202,6 +214,7 @@ static const struct pci_device_id pxa2xx_spi_pci_devices[] = { { PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 }, { PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 }, { PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 }, + { PCI_VDEVICE(INTEL, 0x9ce6), PORT_LPT }, { }, }; MODULE_DEVICE_TABLE(pci, pxa2xx_spi_pci_devices); -- cgit v1.2.1 From 3700ce951c405f748a4767cabe6ac65727cbf767 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Mon, 22 Feb 2016 20:33:44 +0900 Subject: spi: make xmldocs warning caused by spi.c This patch fix following warnings while make xmldocs. .//drivers/spi/spi.c:2354: warning: Excess function parameter 'message' description in 'spi_split_transfers_maxsize' .//drivers/spi/spi.c:2354: warning: Excess function parameter 'max_size' description in 'spi_split_transfers_maxsize' Signed-off-by: Masanari Iida Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b0c5d45641ec..96c764d7efe6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2317,8 +2317,8 @@ static int __spi_split_transfer_maxsize(struct spi_master *master, * when an individual transfer exceeds a * certain size * @master: the @spi_master for this transfer - * @message: the @spi_message to transform - * @max_size: the maximum when to apply this + * @msg: the @spi_message to transform + * @maxsize: the maximum when to apply this * * Return: status of transformation */ -- cgit v1.2.1 From 7ecbfff6711fb331591003ac32c002ce55a0758f Mon Sep 17 00:00:00 2001 From: Sergei Ianovich Date: Tue, 23 Feb 2016 13:44:28 +0300 Subject: spi: master driver to enable RTC on ICPDAS LP-8841 ICP DAS LP-8841 contains a DS-1302 RTC. This driver provides an SPI master which makes the RTC usable. The driver is not supposed to work with anything else. The driver uses the standard MicroWire half-duplex transfer timing. Master output is set on low clock and sensed by the RTC on the rising edge. Master input is set by the RTC on the trailing edge and is sensed by the master on low clock. Signed-off-by: Sergei Ianovich Acked-by: Rob Herring Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 10 ++ drivers/spi/Makefile | 1 + drivers/spi/spi-lp8841-rtc.c | 255 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 drivers/spi/spi-lp8841-rtc.c (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 77064160dd76..1206a6651e70 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -271,6 +271,16 @@ config SPI_LM70_LLP which interfaces to an LM70 temperature sensor using a parallel port. +config SPI_LP8841_RTC + tristate "ICP DAS LP-8841 SPI Controller for RTC" + depends on MACH_PXA27X_DT || COMPILE_TEST + help + This driver provides an SPI master device to drive Maxim + DS-1302 real time clock. + + Say N here unless you plan to run the kernel on an ICP DAS + LP-8x4x industrial computer. + config SPI_MPC52xx tristate "Freescale MPC52xx SPI (non-PSC) controller support" depends on PPC_MPC52xx diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8991ffce6e12..9d10ddc1c51f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_SPI_GPIO) += spi-gpio.o obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o obj-$(CONFIG_SPI_IMX) += spi-imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o +obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o diff --git a/drivers/spi/spi-lp8841-rtc.c b/drivers/spi/spi-lp8841-rtc.c new file mode 100644 index 000000000000..44bb69c3f1d6 --- /dev/null +++ b/drivers/spi/spi-lp8841-rtc.c @@ -0,0 +1,255 @@ +/* + * SPI master driver for ICP DAS LP-8841 RTC + * + * Copyright (C) 2016 Sergei Ianovich + * + * based on + * + * Dallas DS1302 RTC Support + * Copyright (C) 2002 David McCullough + * Copyright (C) 2003 - 2007 Paul Mundt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "spi_lp8841_rtc" + +#define SPI_LP8841_RTC_CE 0x01 +#define SPI_LP8841_RTC_CLK 0x02 +#define SPI_LP8841_RTC_nWE 0x04 +#define SPI_LP8841_RTC_MOSI 0x08 +#define SPI_LP8841_RTC_MISO 0x01 + +/* + * REVISIT If there is support for SPI_3WIRE and SPI_LSB_FIRST in SPI + * GPIO driver, this SPI driver can be replaced by a simple GPIO driver + * providing 3 GPIO pins. + */ + +struct spi_lp8841_rtc { + void *iomem; + unsigned long state; +}; + +static inline void +setsck(struct spi_lp8841_rtc *data, int is_on) +{ + if (is_on) + data->state |= SPI_LP8841_RTC_CLK; + else + data->state &= ~SPI_LP8841_RTC_CLK; + writeb(data->state, data->iomem); +} + +static inline void +setmosi(struct spi_lp8841_rtc *data, int is_on) +{ + if (is_on) + data->state |= SPI_LP8841_RTC_MOSI; + else + data->state &= ~SPI_LP8841_RTC_MOSI; + writeb(data->state, data->iomem); +} + +static inline int +getmiso(struct spi_lp8841_rtc *data) +{ + return ioread8(data->iomem) & SPI_LP8841_RTC_MISO; +} + +static inline u32 +bitbang_txrx_be_cpha0_lsb(struct spi_lp8841_rtc *data, + unsigned usecs, unsigned cpol, unsigned flags, + u32 word, u8 bits) +{ + /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */ + + u32 shift = 32 - bits; + /* clock starts at inactive polarity */ + for (; likely(bits); bits--) { + + /* setup LSB (to slave) on leading edge */ + if ((flags & SPI_MASTER_NO_TX) == 0) + setmosi(data, (word & 1)); + + usleep_range(usecs, usecs + 1); /* T(setup) */ + + /* sample LSB (from slave) on trailing edge */ + word >>= 1; + if ((flags & SPI_MASTER_NO_RX) == 0) + word |= (getmiso(data) << 31); + + setsck(data, !cpol); + usleep_range(usecs, usecs + 1); + + setsck(data, cpol); + } + + word >>= shift; + return word; +} + +static int +spi_lp8841_rtc_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct spi_lp8841_rtc *data = spi_master_get_devdata(master); + unsigned count = t->len; + const u8 *tx = t->tx_buf; + u8 *rx = t->rx_buf; + u8 word = 0; + int ret = 0; + + if (tx) { + data->state &= ~SPI_LP8841_RTC_nWE; + writeb(data->state, data->iomem); + while (likely(count > 0)) { + word = *tx++; + bitbang_txrx_be_cpha0_lsb(data, 1, 0, + SPI_MASTER_NO_RX, word, 8); + count--; + } + } else if (rx) { + data->state |= SPI_LP8841_RTC_nWE; + writeb(data->state, data->iomem); + while (likely(count > 0)) { + word = bitbang_txrx_be_cpha0_lsb(data, 1, 0, + SPI_MASTER_NO_TX, word, 8); + *rx++ = word; + count--; + } + } else { + ret = -EINVAL; + } + + spi_finalize_current_transfer(master); + + return ret; +} + +static void +spi_lp8841_rtc_set_cs(struct spi_device *spi, bool enable) +{ + struct spi_lp8841_rtc *data = spi_master_get_devdata(spi->master); + + data->state = 0; + writeb(data->state, data->iomem); + if (enable) { + usleep_range(4, 5); + data->state |= SPI_LP8841_RTC_CE; + writeb(data->state, data->iomem); + usleep_range(4, 5); + } +} + +static int +spi_lp8841_rtc_setup(struct spi_device *spi) +{ + if ((spi->mode & SPI_CS_HIGH) == 0) { + dev_err(&spi->dev, "unsupported active low chip select\n"); + return -EINVAL; + } + + if ((spi->mode & SPI_LSB_FIRST) == 0) { + dev_err(&spi->dev, "unsupported MSB first mode\n"); + return -EINVAL; + } + + if ((spi->mode & SPI_3WIRE) == 0) { + dev_err(&spi->dev, "unsupported wiring. 3 wires required\n"); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id spi_lp8841_rtc_dt_ids[] = { + { .compatible = "icpdas,lp8841-spi-rtc" }, + { } +}; + +MODULE_DEVICE_TABLE(of, spi_lp8841_rtc_dt_ids); +#endif + +static int +spi_lp8841_rtc_probe(struct platform_device *pdev) +{ + int ret; + struct spi_master *master; + struct spi_lp8841_rtc *data; + void *iomem; + + master = spi_alloc_master(&pdev->dev, sizeof(*data)); + if (!master) + return -ENOMEM; + platform_set_drvdata(pdev, master); + + master->flags = SPI_MASTER_HALF_DUPLEX; + master->mode_bits = SPI_CS_HIGH | SPI_3WIRE | SPI_LSB_FIRST; + + master->bus_num = pdev->id; + master->num_chipselect = 1; + master->setup = spi_lp8841_rtc_setup; + master->set_cs = spi_lp8841_rtc_set_cs; + master->transfer_one = spi_lp8841_rtc_transfer_one; + master->bits_per_word_mask = SPI_BPW_MASK(8); +#ifdef CONFIG_OF + master->dev.of_node = pdev->dev.of_node; +#endif + + data = spi_master_get_devdata(master); + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->iomem = devm_ioremap_resource(&pdev->dev, iomem); + if (IS_ERR(data->iomem)) { + dev_err(&pdev->dev, "failed to get IO address\n"); + goto err_put_master; + } + + /* register with the SPI framework */ + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "cannot register spi master\n"); + goto err_put_master; + } + + return ret; + + +err_put_master: + spi_master_put(master); + + return ret; +} + +MODULE_ALIAS("platform:" DRIVER_NAME); + +static struct platform_driver spi_lp8841_rtc_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(spi_lp8841_rtc_dt_ids), + }, + .probe = spi_lp8841_rtc_probe, +}; +module_platform_driver(spi_lp8841_rtc_driver); + +MODULE_DESCRIPTION("SPI master driver for ICP DAS LP-8841 RTC"); +MODULE_AUTHOR("Sergei Ianovich"); +MODULE_LICENSE("GPL"); -- cgit v1.2.1 From 14a20428e2e02600195a71ad366c0caef97bf3d4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 25 Feb 2016 12:37:40 +0100 Subject: spi: lp-8841: return correct error code from probe The spi_lp8841_rtc_probe() function misses an initialization of the return code when it fails to get its memory resource, as gcc notices: drivers/spi/spi-lp8841-rtc.c: In function 'spi_lp8841_rtc_probe': drivers/spi/spi-lp8841-rtc.c:239:9: error: 'ret' may be used uninitialized in this function [-Werror=maybe-uninitialized] This changes the code to propagate the error from devm_ioremap_resource(). Fixes: 7ecbfff6711f ("spi: master driver to enable RTC on ICPDAS LP-8841") Signed-off-by: Arnd Bergmann Tested-by: Sergei Ianovich Signed-off-by: Mark Brown --- drivers/spi/spi-lp8841-rtc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-lp8841-rtc.c b/drivers/spi/spi-lp8841-rtc.c index 44bb69c3f1d6..faa577d282c0 100644 --- a/drivers/spi/spi-lp8841-rtc.c +++ b/drivers/spi/spi-lp8841-rtc.c @@ -217,8 +217,9 @@ spi_lp8841_rtc_probe(struct platform_device *pdev) data = spi_master_get_devdata(master); iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->iomem = devm_ioremap_resource(&pdev->dev, iomem); - if (IS_ERR(data->iomem)) { + data->iomem = devm_ioremap_resource(&pdev->dev, iomem); + ret = PTR_ERR_OR_ZERO(data->iomem); + if (ret) { dev_err(&pdev->dev, "failed to get IO address\n"); goto err_put_master; } -- cgit v1.2.1 From c1b03f116453cebb9381fa64311ab047ecece485 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 2 Mar 2016 09:54:14 +0200 Subject: spi: pxa2xx: Add support for Intel Broxton B-Step Add support for third Intel Broxton variant and update comment for A-Step variant. Signed-off-by: Jarkko Nikula Tested-by: Steve Sakoman Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 2c4e8ccd573b..85e59a406a4c 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1384,10 +1384,14 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { /* SPT-H */ { PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP }, { PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP }, - /* BXT */ + /* BXT A-Step */ { PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x0ac6), LPSS_BXT_SSP }, + /* BXT B-Step */ + { PCI_VDEVICE(INTEL, 0x1ac2), LPSS_BXT_SSP }, + { PCI_VDEVICE(INTEL, 0x1ac4), LPSS_BXT_SSP }, + { PCI_VDEVICE(INTEL, 0x1ac6), LPSS_BXT_SSP }, /* APL */ { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, -- cgit v1.2.1 From 590745017ef3658b33a581fe0c6d60b70e92df85 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 3 Mar 2016 13:02:14 +0200 Subject: spi: pxa2xx: Use newer more explicit DMAengine terminate API Commit b36f09c3c441 ("dmaengine: Add transfer termination synchronization support") marked dmaengine_terminate_all() as deprecated and is being replaced by explicit synchronous and asynchronous terminate functions. Here DMA termination are done in two cases: FIFO overrun and module removal. FIFO overrun is handled in interrupt context and converting dmaengine_terminate_all() to dmaengine_terminate_async() does the same than before. Using synchronous termination in module removal however adds a bit more robustness as it waits all completion callbacks have finished. Although it looks all known DMA engines used with spi-pxa2xx don't implement device_synchronize() callback so this too appears to be a no-op in practice. Signed-off-by: Jarkko Nikula Reviewed-by: Mika Westerberg Acked-by: Robert Jarzmik Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-dma.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index bd8b369a343c..365fc22c3572 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -254,8 +254,8 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) if (status & SSSR_ROR) { dev_err(&drv_data->pdev->dev, "FIFO overrun\n"); - dmaengine_terminate_all(drv_data->rx_chan); - dmaengine_terminate_all(drv_data->tx_chan); + dmaengine_terminate_async(drv_data->rx_chan); + dmaengine_terminate_async(drv_data->tx_chan); pxa2xx_spi_dma_transfer_complete(drv_data, true); return IRQ_HANDLED; @@ -331,13 +331,13 @@ int pxa2xx_spi_dma_setup(struct driver_data *drv_data) void pxa2xx_spi_dma_release(struct driver_data *drv_data) { if (drv_data->rx_chan) { - dmaengine_terminate_all(drv_data->rx_chan); + dmaengine_terminate_sync(drv_data->rx_chan); dma_release_channel(drv_data->rx_chan); sg_free_table(&drv_data->rx_sgt); drv_data->rx_chan = NULL; } if (drv_data->tx_chan) { - dmaengine_terminate_all(drv_data->tx_chan); + dmaengine_terminate_sync(drv_data->tx_chan); dma_release_channel(drv_data->tx_chan); sg_free_table(&drv_data->tx_sgt); drv_data->tx_chan = NULL; -- cgit v1.2.1 From 10f11a2238d4d66e74438b629214b530a77db8e1 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 10 Mar 2016 15:01:14 -0300 Subject: spi: Add gfp parameter to kernel-doc to fix build warning The spi_split_transfers_maxsize() gfp parameter is missing in the function kernel-doc so building gives the following warning: .//drivers/spi/spi.c:2359: warning: No description found for parameter 'gfp' Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/spi/spi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 96c764d7efe6..c881493974d8 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2319,6 +2319,7 @@ static int __spi_split_transfer_maxsize(struct spi_master *master, * @master: the @spi_master for this transfer * @msg: the @spi_message to transform * @maxsize: the maximum when to apply this + * @gfp: GFP allocation flags * * Return: status of transformation */ -- cgit v1.2.1