From 87306eb7008ac87940c13c200ace585f7c860042 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 22 May 2016 11:06:21 +0200 Subject: spi: Add file patterns for spi device tree bindings Submitters of device tree binding documentation may forget to CC the subsystem maintainer if this is missing. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/MAINTAINERS b/MAINTAINERS index 7304d2e..bb1a63a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10777,6 +10777,7 @@ L: linux-spi@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git Q: http://patchwork.kernel.org/project/spi-devel-general/list/ S: Maintained +F: Documentation/devicetree/bindings/spi/ F: Documentation/spi/ F: drivers/spi/ F: include/linux/spi/ -- cgit v0.10.2 From b3c195b3a75b0aff9ede850ba2208cd1f40a702b Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 19 May 2016 09:07:05 +0200 Subject: spi: orion: Add direct access mode This patch adds support for the direct access mode to the Orion SPI driver which is used on the Marvell Armada based SoCs. In this direct mode, all data written to (or read from) a specifically mapped MBus window (linked to one SPI chip-select on one of the SPI controllers) will be transferred directly to the SPI bus. Without the need to control the SPI registers in between. This can improve the SPI transfer rate in such cases. Both, direct-read and -write mode are supported. But only the write mode has been tested. This mode especially benefits from the SPI direct mode, as the data bytes are written head-to-head to the SPI bus, without any additional addresses. One use-case for this direct write mode is, programming a FPGA bitstream image into the FPGA connected to the SPI bus at maximum speed. This mode is described in chapter "22.5.2 Direct Write to SPI" in the Marvell Armada XP Functional Spec Datasheet. Signed-off-by: Stefan Roese Acked-by: Arnd Bergmann Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-orion.txt b/Documentation/devicetree/bindings/spi/spi-orion.txt index 98bc698..4f629cc 100644 --- a/Documentation/devicetree/bindings/spi/spi-orion.txt +++ b/Documentation/devicetree/bindings/spi/spi-orion.txt @@ -8,7 +8,15 @@ Required properties: - "marvell,armada-380-spi", for the Armada 38x SoCs - "marvell,armada-390-spi", for the Armada 39x SoCs - "marvell,armada-xp-spi", for the Armada XP SoCs -- reg : offset and length of the register set for the device +- reg : offset and length of the register set for the device. + This property can optionally have additional entries to configure + the SPI direct access mode that some of the Marvell SoCs support + additionally to the normal indirect access (PIO) mode. The values + for the MBus "target" and "attribute" are defined in the Marvell + SoC "Functional Specifications" Manual in the chapter "Marvell + Core Processor Address Decoding". + The eight register sets following the control registers refer to + chip-select lines 0 through 7 respectively. - cell-index : Which of multiple SPI controllers is this. Optional properties: - interrupts : Is currently not used. @@ -23,3 +31,42 @@ Example: interrupts = <23>; status = "disabled"; }; + +Example with SPI direct mode support (optionally): + spi0: spi@10600 { + compatible = "marvell,orion-spi"; + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + reg = , /* control */ + , /* CS0 */ + , /* CS1 */ + , /* CS2 */ + , /* CS3 */ + , /* CS4 */ + , /* CS5 */ + , /* CS6 */ + ; /* CS7 */ + interrupts = <23>; + status = "disabled"; + }; + +To enable the direct mode, the board specific 'ranges' property in the +'soc' node needs to add the entries for the desired SPI controllers +and its chip-selects that are used in the direct mode instead of PIO +mode. Here an example for this (SPI controller 0, device 1 and SPI +controller 1, device 2 are used in direct mode. All other SPI device +are used in the default indirect (PIO) mode): + soc { + /* + * Enable the SPI direct access by configuring an entry + * here in the board-specific ranges property + */ + ranges = , /* internal regs */ + , /* BootROM */ + , /* SPI0-DEV1 */ + ; /* SPI1-DEV2 */ + +For further information on the MBus bindings, please see the MBus +DT documentation: +Documentation/devicetree/bindings/bus/mvebu-mbus.txt diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index a87cfd4..d0d9c86 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,9 @@ #define ORION_SPI_INT_CAUSE_REG 0x10 #define ORION_SPI_TIMING_PARAMS_REG 0x18 +/* Register for the "Direct Mode" */ +#define SPI_DIRECT_WRITE_CONFIG_REG 0x20 + #define ORION_SPI_TMISO_SAMPLE_MASK (0x3 << 6) #define ORION_SPI_TMISO_SAMPLE_1 (1 << 6) #define ORION_SPI_TMISO_SAMPLE_2 (2 << 6) @@ -78,11 +82,18 @@ struct orion_spi_dev { bool is_errata_50mhz_ac; }; +struct orion_direct_acc { + void __iomem *vaddr; + u32 size; +}; + struct orion_spi { struct spi_master *master; void __iomem *base; struct clk *clk; const struct orion_spi_dev *devdata; + + struct orion_direct_acc direct_access[ORION_NUM_CHIPSELECTS]; }; static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) @@ -372,10 +383,39 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) { unsigned int count; int word_len; + struct orion_spi *orion_spi; + int cs = spi->chip_select; word_len = spi->bits_per_word; count = xfer->len; + orion_spi = spi_master_get_devdata(spi->master); + + /* + * Use SPI direct write mode if base address is available. Otherwise + * fall back to PIO mode for this transfer. + */ + if ((orion_spi->direct_access[cs].vaddr) && (xfer->tx_buf) && + (word_len == 8)) { + unsigned int cnt = count / 4; + unsigned int rem = count % 4; + + /* + * Send the TX-data to the SPI device via the direct + * mapped address window + */ + iowrite32_rep(orion_spi->direct_access[cs].vaddr, + xfer->tx_buf, cnt); + if (rem) { + u32 *buf = (u32 *)xfer->tx_buf; + + iowrite8_rep(orion_spi->direct_access[cs].vaddr, + &buf[cnt], rem); + } + + return count; + } + if (word_len == 8) { const u8 *tx = xfer->tx_buf; u8 *rx = xfer->rx_buf; @@ -425,6 +465,10 @@ static int orion_spi_reset(struct orion_spi *orion_spi) { /* Verify that the CS is deasserted */ orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); + + /* Don't deassert CS between the direct mapped SPI transfers */ + writel(0, spi_reg(orion_spi, SPI_DIRECT_WRITE_CONFIG_REG)); + return 0; } @@ -504,6 +548,7 @@ static int orion_spi_probe(struct platform_device *pdev) struct resource *r; unsigned long tclk_hz; int status = 0; + struct device_node *np; master = spi_alloc_master(&pdev->dev, sizeof(*spi)); if (master == NULL) { @@ -576,6 +621,49 @@ static int orion_spi_probe(struct platform_device *pdev) goto out_rel_clk; } + /* Scan all SPI devices of this controller for direct mapped devices */ + for_each_available_child_of_node(pdev->dev.of_node, np) { + u32 cs; + + /* Get chip-select number from the "reg" property */ + status = of_property_read_u32(np, "reg", &cs); + if (status) { + dev_err(&pdev->dev, + "%s has no valid 'reg' property (%d)\n", + np->full_name, status); + status = 0; + continue; + } + + /* + * Check if an address is configured for this SPI device. If + * not, the MBus mapping via the 'ranges' property in the 'soc' + * node is not configured and this device should not use the + * direct mode. In this case, just continue with the next + * device. + */ + status = of_address_to_resource(pdev->dev.of_node, cs + 1, r); + if (status) + continue; + + /* + * Only map one page for direct access. This is enough for the + * simple TX transfer which only writes to the first word. + * This needs to get extended for the direct SPI-NOR / SPI-NAND + * support, once this gets implemented. + */ + spi->direct_access[cs].vaddr = devm_ioremap(&pdev->dev, + r->start, + PAGE_SIZE); + if (IS_ERR(spi->direct_access[cs].vaddr)) { + status = PTR_ERR(spi->direct_access[cs].vaddr); + goto out_rel_clk; + } + spi->direct_access[cs].size = PAGE_SIZE; + + dev_info(&pdev->dev, "CS%d configured for direct access\n", cs); + } + pm_runtime_set_active(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); -- cgit v0.10.2 From c64e1265ae8516c0282b45e099ac5fbdb8486c4c Mon Sep 17 00:00:00 2001 From: Dan O'Donovan Date: Fri, 27 May 2016 19:57:48 +0100 Subject: spi: pxa2xx: use DMA by default if supported Currently, even if the PXA2xx SPI master supports DMA, it won't be enabled unless (i) the slave device is enumerated through ACPI, or (ii) the slave device is registered with board-specific controller_data specified. Even then, there isn't a field in the controller_data that explicitly enables dma - it just gets enabled if the master supports it and controller_data is non-NULL. This means that drivers which register SPI devices on a bus without awareness of this controller cannot avail of DMA performance gains. This patch allows DMA transfers to be used if supported. Signed-off-by: Dan O'Donovan Reviewed-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index fe07c05..27e0307 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1240,7 +1240,7 @@ static int setup(struct spi_device *spi) chip->frm = spi->chip_select; } else chip->gpio_cs = -1; - chip->enable_dma = 0; + chip->enable_dma = drv_data->master_info->enable_dma; chip->timeout = TIMOUT_DFLT; } @@ -1259,17 +1259,9 @@ static int setup(struct spi_device *spi) tx_hi_thres = chip_info->tx_hi_threshold; if (chip_info->rx_threshold) rx_thres = chip_info->rx_threshold; - chip->enable_dma = drv_data->master_info->enable_dma; chip->dma_threshold = 0; if (chip_info->enable_loopback) chip->cr1 = SSCR1_LBM; - } else if (ACPI_HANDLE(&spi->dev)) { - /* - * Slave devices enumerated from ACPI namespace don't - * usually have chip_info but we still might want to use - * DMA with them. - */ - chip->enable_dma = drv_data->master_info->enable_dma; } chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres); -- cgit v0.10.2 From 1eb1c5dc601a9f53d5bd53702d95afad337d35d2 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Fri, 20 May 2016 07:56:20 +0800 Subject: spi/rockchip: add rk3036/rk3228/rk3368 SoCs for spi document We had supported the rk3036/rk3066/rk3188/rk3228/rk3288/rk3368/rk3399 family SoCs in linux kernel. Let's add the other SoCs, in order to a better understanding from the rockchip spi document. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.txt b/Documentation/devicetree/bindings/spi/spi-rockchip.txt index 1b14d69..d2ca153 100644 --- a/Documentation/devicetree/bindings/spi/spi-rockchip.txt +++ b/Documentation/devicetree/bindings/spi/spi-rockchip.txt @@ -6,10 +6,13 @@ and display controllers using the SPI communication interface. Required Properties: - compatible: should be one of the following. - "rockchip,rk3066-spi" for rk3066. - "rockchip,rk3188-spi", "rockchip,rk3066-spi" for rk3188. - "rockchip,rk3288-spi", "rockchip,rk3066-spi" for rk3288. - "rockchip,rk3399-spi", "rockchip,rk3066-spi" for rk3399. + "rockchip,rk3036-spi" for rk3036 SoCS. + "rockchip,rk3066-spi" for rk3066 SoCs. + "rockchip,rk3188-spi" for rk3188 SoCs. + "rockchip,rk3228-spi" for rk3228 SoCS. + "rockchip,rk3288-spi" for rk3288 SoCs. + "rockchip,rk3368-spi" for rk3368 SoCs. + "rockchip,rk3399-spi" for rk3399 SoCs. - reg: physical base address of the controller and length of memory mapped region. - interrupts: The interrupt number to the cpu. The interrupt specifier format -- cgit v0.10.2 From aa29ea3df27dbba19ef59023d0f7330f2fdc58ae Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Fri, 20 May 2016 07:56:21 +0800 Subject: spi/rockchip: add the rk3036/rk3228/rk3368 to match for driver In gerenal, the "rockchip,rockchip-spi" string will match the dts that's great in spi driver. After all the most of rockchip SoCs ar same spi controller. Then, we should keep the old style to match the dts various. Signed-off-by: Caesar Wang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index cd89682..bead5bfc 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -892,9 +892,12 @@ static const struct dev_pm_ops rockchip_spi_pm = { }; static const struct of_device_id rockchip_spi_dt_match[] = { + { .compatible = "rockchip,rk3036-spi", }, { .compatible = "rockchip,rk3066-spi", }, { .compatible = "rockchip,rk3188-spi", }, + { .compatible = "rockchip,rk3228-spi", }, { .compatible = "rockchip,rk3288-spi", }, + { .compatible = "rockchip,rk3368-spi", }, { .compatible = "rockchip,rk3399-spi", }, { }, }; -- cgit v0.10.2 From f4502dd1da9b060a49d539eb754ff86cb97b89f0 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 8 Jun 2016 12:18:31 +0530 Subject: spi: Add DMA support for spi_flash_read() Few SPI devices provide accelerated read interfaces to read from SPI-NOR flash devices. These hardwares also support DMA to transfer data from flash to memory either via mem-to-mem DMA or dedicated slave DMA channels. Hence, add support for DMA in order to improve throughput and reduce CPU load. Use spi_map_buf() to get sg table for the buffer and pass it to SPI driver. Signed-off-by: Vignesh R Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 77e6e45..c9a8d54 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -849,6 +849,20 @@ static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg) return 0; } #else /* !CONFIG_HAS_DMA */ +static inline int spi_map_buf(struct spi_master *master, + struct device *dev, struct sg_table *sgt, + void *buf, size_t len, + enum dma_data_direction dir) +{ + return -EINVAL; +} + +static inline void spi_unmap_buf(struct spi_master *master, + struct device *dev, struct sg_table *sgt, + enum dma_data_direction dir) +{ +} + static inline int __spi_map_msg(struct spi_master *master, struct spi_message *msg) { @@ -2725,6 +2739,7 @@ int spi_flash_read(struct spi_device *spi, { struct spi_master *master = spi->master; + struct device *rx_dev = NULL; int ret; if ((msg->opcode_nbits == SPI_NBITS_DUAL || @@ -2750,9 +2765,22 @@ int spi_flash_read(struct spi_device *spi, return ret; } } + mutex_lock(&master->bus_lock_mutex); + if (master->dma_rx) { + rx_dev = master->dma_rx->device->dev; + ret = spi_map_buf(master, rx_dev, &msg->rx_sg, + msg->buf, msg->len, + DMA_FROM_DEVICE); + if (!ret) + msg->cur_msg_mapped = true; + } ret = master->spi_flash_read(spi, msg); + if (msg->cur_msg_mapped) + spi_unmap_buf(master, rx_dev, &msg->rx_sg, + DMA_FROM_DEVICE); mutex_unlock(&master->bus_lock_mutex); + if (master->auto_runtime_pm) pm_runtime_put(master->dev.parent); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 1f03483..7b53af4 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -1143,6 +1143,8 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd) * @opcode_nbits: number of lines to send opcode * @addr_nbits: number of lines to send address * @data_nbits: number of lines for data + * @rx_sg: Scatterlist for receive data read from flash + * @cur_msg_mapped: message has been mapped for DMA */ struct spi_flash_read_message { void *buf; @@ -1155,6 +1157,8 @@ struct spi_flash_read_message { u8 opcode_nbits; u8 addr_nbits; u8 data_nbits; + struct sg_table rx_sg; + bool cur_msg_mapped; }; /* SPI core interface for flash read support */ -- cgit v0.10.2 From 57c624ae1c0aa631f11768992a74998c531a7fee Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 13 Jun 2016 14:32:23 +0000 Subject: spi: orion: Fix return value check in orion_spi_probe() In case of error, the function devm_ioremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index d0d9c86..ded3702 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -655,8 +655,8 @@ static int orion_spi_probe(struct platform_device *pdev) spi->direct_access[cs].vaddr = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); - if (IS_ERR(spi->direct_access[cs].vaddr)) { - status = PTR_ERR(spi->direct_access[cs].vaddr); + if (!spi->direct_access[cs].vaddr) { + status = -ENOMEM; goto out_rel_clk; } spi->direct_access[cs].size = PAGE_SIZE; -- cgit v0.10.2 From 794912cff6dba8b5e93948243299bb0b2cb11277 Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Mon, 13 Jun 2016 17:46:50 +0000 Subject: spi: sunxi: expose maximum transfer size limit The sun4i spi hardware can trasfer at most 63 bytes of data without DMA support so report the limitation. Same for sun6i. Signed-off-by: Michal Suchanek Acked-by: Maxime Ripard Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 1ddd9e2..07ce7c9 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -167,6 +167,11 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) sun4i_spi_write(sspi, SUN4I_CTL_REG, reg); } +static size_t sun4i_spi_max_transfer_size(struct spi_device *spi) +{ + return SUN4I_FIFO_DEPTH - 1; +} + static int sun4i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) @@ -394,6 +399,7 @@ static int sun4i_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(8); master->dev.of_node = pdev->dev.of_node; master->auto_runtime_pm = true; + master->max_transfer_size = sun4i_spi_max_transfer_size; sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sspi->hclk)) { diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 42e2c4b..6fa6ff1 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -153,6 +153,10 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); } +static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) +{ + return SUN6I_FIFO_DEPTH - 1; +} static int sun6i_spi_transfer_one(struct spi_master *master, struct spi_device *spi, @@ -393,6 +397,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(8); master->dev.of_node = pdev->dev.of_node; master->auto_runtime_pm = true; + master->max_transfer_size = sun6i_spi_max_transfer_size; sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(sspi->hclk)) { -- cgit v0.10.2 From b36581df7e788b674a4efbb8da7fe4a00c207e8b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 8 Jun 2016 20:02:06 +0300 Subject: spi: imx: Using existing properties for chipselects Patch reuse existing "chip_select" and "cs_gpio(s)" fields from SPI core. Signed-off-by: Alexander Shiyan Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 50769078..cbcfb8b 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -60,7 +60,6 @@ struct spi_imx_config { unsigned int speed_hz; unsigned int bpw; unsigned int mode; - u8 cs; }; enum spi_imx_devtype { @@ -76,7 +75,7 @@ struct spi_imx_data; struct spi_imx_devtype_data { void (*intctrl)(struct spi_imx_data *, int); - int (*config)(struct spi_imx_data *, struct spi_imx_config *); + int (*config)(struct spi_device *, struct spi_imx_config *); void (*trigger)(struct spi_imx_data *); int (*rx_available)(struct spi_imx_data *); void (*reset)(struct spi_imx_data *); @@ -112,7 +111,6 @@ struct spi_imx_data { struct completion dma_tx_completion; const struct spi_imx_devtype_data *devtype_data; - int chipselect[0]; }; static inline int is_imx27_cspi(struct spi_imx_data *d) @@ -334,9 +332,10 @@ static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MX51_ECSPI_CTRL); } -static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, - struct spi_imx_config *config) +static int __maybe_unused mx51_ecspi_config(struct spi_device *spi, + struct spi_imx_config *config) { + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); u32 ctrl = MX51_ECSPI_CTRL_ENABLE; u32 clk = config->speed_hz, delay, reg; u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG); @@ -355,28 +354,28 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, spi_imx->spi_bus_clk = clk; /* set chip select to use */ - ctrl |= MX51_ECSPI_CTRL_CS(config->cs); + ctrl |= MX51_ECSPI_CTRL_CS(spi->chip_select); ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET; - cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs); + cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select); if (config->mode & SPI_CPHA) - cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs); + cfg |= MX51_ECSPI_CONFIG_SCLKPHA(spi->chip_select); else - cfg &= ~MX51_ECSPI_CONFIG_SCLKPHA(config->cs); + cfg &= ~MX51_ECSPI_CONFIG_SCLKPHA(spi->chip_select); if (config->mode & SPI_CPOL) { - cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs); - cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs); + cfg |= MX51_ECSPI_CONFIG_SCLKPOL(spi->chip_select); + cfg |= MX51_ECSPI_CONFIG_SCLKCTL(spi->chip_select); } else { - cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(config->cs); - cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(config->cs); + cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(spi->chip_select); + cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(spi->chip_select); } if (config->mode & SPI_CS_HIGH) - cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs); + cfg |= MX51_ECSPI_CONFIG_SSBPOL(spi->chip_select); else - cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(config->cs); + cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(spi->chip_select); if (spi_imx->usedma) ctrl |= MX51_ECSPI_CTRL_SMC; @@ -480,11 +479,11 @@ static void __maybe_unused mx31_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int __maybe_unused mx31_config(struct spi_imx_data *spi_imx, - struct spi_imx_config *config) +static int __maybe_unused mx31_config(struct spi_device *spi, + struct spi_imx_config *config) { + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; - int cs = spi_imx->chipselect[config->cs]; reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << MX31_CSPICTRL_DR_SHIFT; @@ -502,8 +501,8 @@ static int __maybe_unused mx31_config(struct spi_imx_data *spi_imx, reg |= MX31_CSPICTRL_POL; if (config->mode & SPI_CS_HIGH) reg |= MX31_CSPICTRL_SSPOL; - if (cs < 0) - reg |= (cs + 32) << + if (spi->cs_gpio < 0) + reg |= (spi->cs_gpio + 32) << (is_imx35_cspi(spi_imx) ? MX35_CSPICTRL_CS_SHIFT : MX31_CSPICTRL_CS_SHIFT); @@ -558,11 +557,11 @@ static void __maybe_unused mx21_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int __maybe_unused mx21_config(struct spi_imx_data *spi_imx, - struct spi_imx_config *config) +static int __maybe_unused mx21_config(struct spi_device *spi, + struct spi_imx_config *config) { + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_MASTER; - int cs = spi_imx->chipselect[config->cs]; unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18; reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz, max) << @@ -575,8 +574,8 @@ static int __maybe_unused mx21_config(struct spi_imx_data *spi_imx, reg |= MX21_CSPICTRL_POL; if (config->mode & SPI_CS_HIGH) reg |= MX21_CSPICTRL_SSPOL; - if (cs < 0) - reg |= (cs + 32) << MX21_CSPICTRL_CS_SHIFT; + if (spi->cs_gpio < 0) + reg |= (spi->cs_gpio + 32) << MX21_CSPICTRL_CS_SHIFT; writel(reg, spi_imx->base + MXC_CSPICTRL); @@ -625,9 +624,10 @@ static void __maybe_unused mx1_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int __maybe_unused mx1_config(struct spi_imx_data *spi_imx, - struct spi_imx_config *config) +static int __maybe_unused mx1_config(struct spi_device *spi, + struct spi_imx_config *config) { + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << @@ -747,15 +747,13 @@ MODULE_DEVICE_TABLE(of, spi_imx_dt_ids); static void spi_imx_chipselect(struct spi_device *spi, int is_active) { - struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); - int gpio = spi_imx->chipselect[spi->chip_select]; int active = is_active != BITBANG_CS_INACTIVE; int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH); - if (!gpio_is_valid(gpio)) + if (!gpio_is_valid(spi->cs_gpio)) return; - gpio_set_value(gpio, dev_is_lowactive ^ active); + gpio_set_value(spi->cs_gpio, dev_is_lowactive ^ active); } static void spi_imx_push(struct spi_imx_data *spi_imx) @@ -860,7 +858,6 @@ static int spi_imx_setupxfer(struct spi_device *spi, config.bpw = t ? t->bits_per_word : spi->bits_per_word; config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; config.mode = spi->mode; - config.cs = spi->chip_select; if (!config.speed_hz) config.speed_hz = spi->max_speed_hz; @@ -891,7 +888,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, return ret; } - spi_imx->devtype_data->config(spi_imx, &config); + spi_imx->devtype_data->config(spi, &config); return 0; } @@ -1080,14 +1077,12 @@ static int spi_imx_transfer(struct spi_device *spi, static int spi_imx_setup(struct spi_device *spi) { - struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); - int gpio = spi_imx->chipselect[spi->chip_select]; - dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__, spi->mode, spi->bits_per_word, spi->max_speed_hz); - if (gpio_is_valid(gpio)) - gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); + if (gpio_is_valid(spi->cs_gpio)) + gpio_direction_output(spi->cs_gpio, + spi->mode & SPI_CS_HIGH ? 0 : 1); spi_imx_chipselect(spi, BITBANG_CS_INACTIVE); @@ -1137,31 +1132,21 @@ static int spi_imx_probe(struct platform_device *pdev) struct spi_master *master; struct spi_imx_data *spi_imx; struct resource *res; - int i, ret, num_cs, irq; + int i, ret, irq; if (!np && !mxc_platform_info) { dev_err(&pdev->dev, "can't get the platform data\n"); return -EINVAL; } - ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs); - if (ret < 0) { - if (mxc_platform_info) - num_cs = mxc_platform_info->num_chipselect; - else - return ret; - } - - master = spi_alloc_master(&pdev->dev, - sizeof(struct spi_imx_data) + sizeof(int) * num_cs); + master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data)); if (!master) return -ENOMEM; platform_set_drvdata(pdev, master); master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); - master->bus_num = pdev->id; - master->num_chipselect = num_cs; + master->bus_num = np ? -1 : pdev->id; spi_imx = spi_master_get_devdata(master); spi_imx->bitbang.master = master; @@ -1170,22 +1155,16 @@ static int spi_imx_probe(struct platform_device *pdev) spi_imx->devtype_data = of_id ? of_id->data : (struct spi_imx_devtype_data *)pdev->id_entry->driver_data; - for (i = 0; i < master->num_chipselect; i++) { - int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); - if (!gpio_is_valid(cs_gpio) && mxc_platform_info) - cs_gpio = mxc_platform_info->chipselect[i]; - - spi_imx->chipselect[i] = cs_gpio; - if (!gpio_is_valid(cs_gpio)) - continue; + if (mxc_platform_info) { + master->num_chipselect = mxc_platform_info->num_chipselect; + master->cs_gpios = devm_kzalloc(&master->dev, + sizeof(int) * master->num_chipselect, GFP_KERNEL); + if (!master->cs_gpios) + return -ENOMEM; - ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i], - DRIVER_NAME); - if (ret) { - dev_err(&pdev->dev, "can't get cs gpios\n"); - goto out_master_put; - } - } + for (i = 0; i < master->num_chipselect; i++) + master->cs_gpios[i] = mxc_platform_info->chipselect[i]; + } spi_imx->bitbang.chipselect = spi_imx_chipselect; spi_imx->bitbang.setup_transfer = spi_imx_setupxfer; @@ -1267,6 +1246,19 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_clk_put; } + for (i = 0; i < master->num_chipselect; i++) { + if (!gpio_is_valid(master->cs_gpios[i])) + continue; + + ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i], + DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, "Can't get CS GPIO %i\n", + master->cs_gpios[i]); + goto out_clk_put; + } + } + dev_info(&pdev->dev, "probed\n"); clk_disable(spi_imx->clk_ipg); -- cgit v0.10.2 From c0c7a5d7962a4e2ad8626c33e2a2f6ee3bb3dbab Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 8 Jun 2016 20:02:07 +0300 Subject: spi: imx: Remove unnecessary field "mode" from struct spi_imx_config SPI mode can be obtained directly from spi-device, there is no need to keep a copy. Signed-off-by: Alexander Shiyan Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index cbcfb8b..0aa38c2 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -59,7 +59,6 @@ struct spi_imx_config { unsigned int speed_hz; unsigned int bpw; - unsigned int mode; }; enum spi_imx_devtype { @@ -360,19 +359,19 @@ static int __maybe_unused mx51_ecspi_config(struct spi_device *spi, cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi->chip_select); - if (config->mode & SPI_CPHA) + if (spi->mode & SPI_CPHA) cfg |= MX51_ECSPI_CONFIG_SCLKPHA(spi->chip_select); else cfg &= ~MX51_ECSPI_CONFIG_SCLKPHA(spi->chip_select); - if (config->mode & SPI_CPOL) { + if (spi->mode & SPI_CPOL) { cfg |= MX51_ECSPI_CONFIG_SCLKPOL(spi->chip_select); cfg |= MX51_ECSPI_CONFIG_SCLKCTL(spi->chip_select); } else { cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(spi->chip_select); cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(spi->chip_select); } - if (config->mode & SPI_CS_HIGH) + if (spi->mode & SPI_CS_HIGH) cfg |= MX51_ECSPI_CONFIG_SSBPOL(spi->chip_select); else cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(spi->chip_select); @@ -384,7 +383,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_device *spi, writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); reg = readl(spi_imx->base + MX51_ECSPI_TESTREG); - if (config->mode & SPI_LOOP) + if (spi->mode & SPI_LOOP) reg |= MX51_ECSPI_TESTREG_LBC; else reg &= ~MX51_ECSPI_TESTREG_LBC; @@ -495,11 +494,11 @@ static int __maybe_unused mx31_config(struct spi_device *spi, reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; } - if (config->mode & SPI_CPHA) + if (spi->mode & SPI_CPHA) reg |= MX31_CSPICTRL_PHA; - if (config->mode & SPI_CPOL) + if (spi->mode & SPI_CPOL) reg |= MX31_CSPICTRL_POL; - if (config->mode & SPI_CS_HIGH) + if (spi->mode & SPI_CS_HIGH) reg |= MX31_CSPICTRL_SSPOL; if (spi->cs_gpio < 0) reg |= (spi->cs_gpio + 32) << @@ -568,11 +567,11 @@ static int __maybe_unused mx21_config(struct spi_device *spi, MX21_CSPICTRL_DR_SHIFT; reg |= config->bpw - 1; - if (config->mode & SPI_CPHA) + if (spi->mode & SPI_CPHA) reg |= MX21_CSPICTRL_PHA; - if (config->mode & SPI_CPOL) + if (spi->mode & SPI_CPOL) reg |= MX21_CSPICTRL_POL; - if (config->mode & SPI_CS_HIGH) + if (spi->mode & SPI_CS_HIGH) reg |= MX21_CSPICTRL_SSPOL; if (spi->cs_gpio < 0) reg |= (spi->cs_gpio + 32) << MX21_CSPICTRL_CS_SHIFT; @@ -634,9 +633,9 @@ static int __maybe_unused mx1_config(struct spi_device *spi, MX1_CSPICTRL_DR_SHIFT; reg |= config->bpw - 1; - if (config->mode & SPI_CPHA) + if (spi->mode & SPI_CPHA) reg |= MX1_CSPICTRL_PHA; - if (config->mode & SPI_CPOL) + if (spi->mode & SPI_CPOL) reg |= MX1_CSPICTRL_POL; writel(reg, spi_imx->base + MXC_CSPICTRL); @@ -857,7 +856,6 @@ static int spi_imx_setupxfer(struct spi_device *spi, config.bpw = t ? t->bits_per_word : spi->bits_per_word; config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; - config.mode = spi->mode; if (!config.speed_hz) config.speed_hz = spi->max_speed_hz; -- cgit v0.10.2 From f989bc69585f207cbcd20e4798c69f7a4a596dd9 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 8 Jun 2016 20:02:08 +0300 Subject: spi: imx: Remove "maybe_unused" attributes The SPI-driver no longer contains the conditions for various CPUs, so "maybe_unused" attributes is no longer needed. This patch removes these attributes. Signed-off-by: Alexander Shiyan Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 0aa38c2..cafe42c 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -309,7 +309,7 @@ static unsigned int mx51_ecspi_clkdiv(struct spi_imx_data *spi_imx, (post << MX51_ECSPI_CTRL_POSTDIV_OFFSET); } -static void __maybe_unused mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int enable) +static void mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int enable) { unsigned val = 0; @@ -322,7 +322,7 @@ static void __maybe_unused mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int writel(val, spi_imx->base + MX51_ECSPI_INT); } -static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx) +static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx) { u32 reg; @@ -331,8 +331,8 @@ static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MX51_ECSPI_CTRL); } -static int __maybe_unused mx51_ecspi_config(struct spi_device *spi, - struct spi_imx_config *config) +static int mx51_ecspi_config(struct spi_device *spi, + struct spi_imx_config *config) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); u32 ctrl = MX51_ECSPI_CTRL_ENABLE; @@ -422,12 +422,12 @@ static int __maybe_unused mx51_ecspi_config(struct spi_device *spi, return 0; } -static int __maybe_unused mx51_ecspi_rx_available(struct spi_imx_data *spi_imx) +static int mx51_ecspi_rx_available(struct spi_imx_data *spi_imx) { return readl(spi_imx->base + MX51_ECSPI_STAT) & MX51_ECSPI_STAT_RR; } -static void __maybe_unused mx51_ecspi_reset(struct spi_imx_data *spi_imx) +static void mx51_ecspi_reset(struct spi_imx_data *spi_imx) { /* drain receive buffer */ while (mx51_ecspi_rx_available(spi_imx)) @@ -457,7 +457,7 @@ static void __maybe_unused mx51_ecspi_reset(struct spi_imx_data *spi_imx) * the i.MX35 has a slightly different register layout for bits * we do not use here. */ -static void __maybe_unused mx31_intctrl(struct spi_imx_data *spi_imx, int enable) +static void mx31_intctrl(struct spi_imx_data *spi_imx, int enable) { unsigned int val = 0; @@ -469,7 +469,7 @@ static void __maybe_unused mx31_intctrl(struct spi_imx_data *spi_imx, int enable writel(val, spi_imx->base + MXC_CSPIINT); } -static void __maybe_unused mx31_trigger(struct spi_imx_data *spi_imx) +static void mx31_trigger(struct spi_imx_data *spi_imx) { unsigned int reg; @@ -478,8 +478,7 @@ static void __maybe_unused mx31_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int __maybe_unused mx31_config(struct spi_device *spi, - struct spi_imx_config *config) +static int mx31_config(struct spi_device *spi, struct spi_imx_config *config) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; @@ -510,12 +509,12 @@ static int __maybe_unused mx31_config(struct spi_device *spi, return 0; } -static int __maybe_unused mx31_rx_available(struct spi_imx_data *spi_imx) +static int mx31_rx_available(struct spi_imx_data *spi_imx) { return readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR; } -static void __maybe_unused mx31_reset(struct spi_imx_data *spi_imx) +static void mx31_reset(struct spi_imx_data *spi_imx) { /* drain receive buffer */ while (readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR) @@ -535,7 +534,7 @@ static void __maybe_unused mx31_reset(struct spi_imx_data *spi_imx) #define MX21_CSPICTRL_DR_SHIFT 14 #define MX21_CSPICTRL_CS_SHIFT 19 -static void __maybe_unused mx21_intctrl(struct spi_imx_data *spi_imx, int enable) +static void mx21_intctrl(struct spi_imx_data *spi_imx, int enable) { unsigned int val = 0; @@ -547,7 +546,7 @@ static void __maybe_unused mx21_intctrl(struct spi_imx_data *spi_imx, int enable writel(val, spi_imx->base + MXC_CSPIINT); } -static void __maybe_unused mx21_trigger(struct spi_imx_data *spi_imx) +static void mx21_trigger(struct spi_imx_data *spi_imx) { unsigned int reg; @@ -556,8 +555,7 @@ static void __maybe_unused mx21_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int __maybe_unused mx21_config(struct spi_device *spi, - struct spi_imx_config *config) +static int mx21_config(struct spi_device *spi, struct spi_imx_config *config) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_MASTER; @@ -581,12 +579,12 @@ static int __maybe_unused mx21_config(struct spi_device *spi, return 0; } -static int __maybe_unused mx21_rx_available(struct spi_imx_data *spi_imx) +static int mx21_rx_available(struct spi_imx_data *spi_imx) { return readl(spi_imx->base + MXC_CSPIINT) & MX21_INTREG_RR; } -static void __maybe_unused mx21_reset(struct spi_imx_data *spi_imx) +static void mx21_reset(struct spi_imx_data *spi_imx) { writel(1, spi_imx->base + MXC_RESET); } @@ -602,7 +600,7 @@ static void __maybe_unused mx21_reset(struct spi_imx_data *spi_imx) #define MX1_CSPICTRL_MASTER (1 << 10) #define MX1_CSPICTRL_DR_SHIFT 13 -static void __maybe_unused mx1_intctrl(struct spi_imx_data *spi_imx, int enable) +static void mx1_intctrl(struct spi_imx_data *spi_imx, int enable) { unsigned int val = 0; @@ -614,7 +612,7 @@ static void __maybe_unused mx1_intctrl(struct spi_imx_data *spi_imx, int enable) writel(val, spi_imx->base + MXC_CSPIINT); } -static void __maybe_unused mx1_trigger(struct spi_imx_data *spi_imx) +static void mx1_trigger(struct spi_imx_data *spi_imx) { unsigned int reg; @@ -623,8 +621,7 @@ static void __maybe_unused mx1_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } -static int __maybe_unused mx1_config(struct spi_device *spi, - struct spi_imx_config *config) +static int mx1_config(struct spi_device *spi, struct spi_imx_config *config) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; @@ -643,12 +640,12 @@ static int __maybe_unused mx1_config(struct spi_device *spi, return 0; } -static int __maybe_unused mx1_rx_available(struct spi_imx_data *spi_imx) +static int mx1_rx_available(struct spi_imx_data *spi_imx) { return readl(spi_imx->base + MXC_CSPIINT) & MX1_INTREG_RR; } -static void __maybe_unused mx1_reset(struct spi_imx_data *spi_imx) +static void mx1_reset(struct spi_imx_data *spi_imx) { writel(1, spi_imx->base + MXC_RESET); } -- cgit v0.10.2 From 956fff7189e586c646a179f00386d0ebf9f0fd8b Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 8 Jun 2016 20:02:09 +0300 Subject: spi: imx: Update DT binding documentation Mark "fsl,spi-num-chipselects" property as obsolete. Signed-off-by: Alexander Shiyan Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt index 523341a..8bc95e2 100644 --- a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt +++ b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.txt @@ -11,7 +11,6 @@ Required properties: - "fsl,imx51-ecspi" for SPI compatible with the one integrated on i.MX51 - reg : Offset and length of the register set for the device - interrupts : Should contain CSPI/eCSPI interrupt -- fsl,spi-num-chipselects : Contains the number of the chipselect - cs-gpios : Specifies the gpio pins to be used for chipselects. - clocks : Clock specifiers for both ipg and per clocks. - clock-names : Clock names should include both "ipg" and "per" @@ -21,6 +20,9 @@ See the clock consumer binding, Documentation/devicetree/bindings/dma/dma.txt - dma-names: DMA request names should include "tx" and "rx" if present. +Obsolete properties: +- fsl,spi-num-chipselects : Contains the number of the chipselect + Example: ecspi@70010000 { @@ -29,7 +31,6 @@ ecspi@70010000 { compatible = "fsl,imx51-ecspi"; reg = <0x70010000 0x4000>; interrupts = <36>; - fsl,spi-num-chipselects = <2>; cs-gpios = <&gpio3 24 0>, /* GPIO3_24 */ <&gpio3 25 0>; /* GPIO3_25 */ dmas = <&sdma 3 7 1>, <&sdma 4 7 2>; -- cgit v0.10.2 From 2d7537d8f64184debed89a57aa7bea5d27b76a90 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 21 Jun 2016 13:21:33 +0300 Subject: spi: pxa2xx: Use local struct spi_master pointer in pump_transfers() We will find more use for struct spi_master pointer in pump_transfers() and code will be more readable if we access it using local pointer than through the drv_data->master. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 27e0307..e5c457a 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -915,6 +915,7 @@ static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data, static void pump_transfers(unsigned long data) { struct driver_data *drv_data = (struct driver_data *)data; + struct spi_master *master = drv_data->master; struct spi_message *message = NULL; struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; @@ -1072,12 +1073,12 @@ static void pump_transfers(unsigned long data) 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 + 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 + master->max_speed_hz / 2 / (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)), drv_data->dma_mapped ? "DMA" : "PIO"); -- cgit v0.10.2 From b6ced294fb61418e7d3fc30bac696f313551d412 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Tue, 21 Jun 2016 13:21:34 +0300 Subject: spi: pxa2xx: Switch to SPI core DMA mapping functionality SPI core provides DMA mapping with scatterlists. Start using it instead of own implementation in spi-pxa2xx. Major difference in addition to bunch of removed boilerplate code is that SPI core does mapping/unmapping for all transfers in a message before and after the message sending where spi-pxa2xx did mapping/unmapping for each transfers separately. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index a18a03d..db3ae1d 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -20,79 +20,6 @@ #include "spi-pxa2xx.h" -static int pxa2xx_spi_map_dma_buffer(struct driver_data *drv_data, - enum dma_data_direction dir) -{ - int i, nents, len = drv_data->len; - struct scatterlist *sg; - struct device *dmadev; - struct sg_table *sgt; - void *buf, *pbuf; - - if (dir == DMA_TO_DEVICE) { - dmadev = drv_data->tx_chan->device->dev; - sgt = &drv_data->tx_sgt; - buf = drv_data->tx; - } else { - dmadev = drv_data->rx_chan->device->dev; - sgt = &drv_data->rx_sgt; - buf = drv_data->rx; - } - - nents = DIV_ROUND_UP(len, SZ_2K); - if (nents != sgt->nents) { - int ret; - - sg_free_table(sgt); - ret = sg_alloc_table(sgt, nents, GFP_ATOMIC); - if (ret) - return ret; - } - - pbuf = buf; - for_each_sg(sgt->sgl, sg, sgt->nents, i) { - size_t bytes = min_t(size_t, len, SZ_2K); - - sg_set_buf(sg, pbuf, bytes); - pbuf += bytes; - len -= bytes; - } - - nents = dma_map_sg(dmadev, sgt->sgl, sgt->nents, dir); - if (!nents) - return -ENOMEM; - - return nents; -} - -static void pxa2xx_spi_unmap_dma_buffer(struct driver_data *drv_data, - enum dma_data_direction dir) -{ - struct device *dmadev; - struct sg_table *sgt; - - if (dir == DMA_TO_DEVICE) { - dmadev = drv_data->tx_chan->device->dev; - sgt = &drv_data->tx_sgt; - } else { - dmadev = drv_data->rx_chan->device->dev; - sgt = &drv_data->rx_sgt; - } - - dma_unmap_sg(dmadev, sgt->sgl, sgt->nents, dir); -} - -static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data) -{ - if (!drv_data->dma_mapped) - return; - - pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_FROM_DEVICE); - pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_TO_DEVICE); - - drv_data->dma_mapped = 0; -} - static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, bool error) { @@ -125,8 +52,6 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, pxa2xx_spi_write(drv_data, SSTO, 0); if (!error) { - pxa2xx_spi_unmap_dma_buffers(drv_data); - msg->actual_length += drv_data->len; msg->state = pxa2xx_spi_next_transfer(drv_data); } else { @@ -152,11 +77,12 @@ pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data, enum dma_transfer_direction dir) { struct chip_data *chip = drv_data->cur_chip; + struct spi_transfer *xfer = drv_data->cur_transfer; enum dma_slave_buswidth width; struct dma_slave_config cfg; struct dma_chan *chan; struct sg_table *sgt; - int nents, ret; + int ret; switch (drv_data->n_bytes) { case 1: @@ -178,17 +104,15 @@ pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data, cfg.dst_addr_width = width; cfg.dst_maxburst = chip->dma_burst_size; - sgt = &drv_data->tx_sgt; - nents = drv_data->tx_nents; - chan = drv_data->tx_chan; + sgt = &xfer->tx_sg; + chan = drv_data->master->dma_tx; } else { cfg.src_addr = drv_data->ssdr_physical; cfg.src_addr_width = width; cfg.src_maxburst = chip->dma_burst_size; - sgt = &drv_data->rx_sgt; - nents = drv_data->rx_nents; - chan = drv_data->rx_chan; + sgt = &xfer->rx_sg; + chan = drv_data->master->dma_rx; } ret = dmaengine_slave_config(chan, &cfg); @@ -197,46 +121,10 @@ pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data, return NULL; } - return dmaengine_prep_slave_sg(chan, sgt->sgl, nents, dir, + return dmaengine_prep_slave_sg(chan, sgt->sgl, sgt->nents, dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); } -bool pxa2xx_spi_dma_is_possible(size_t len) -{ - return len <= MAX_DMA_LEN; -} - -int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data) -{ - const struct chip_data *chip = drv_data->cur_chip; - int ret; - - if (!chip->enable_dma) - return 0; - - /* Don't bother with DMA if we can't do even a single burst */ - if (drv_data->len < chip->dma_burst_size) - return 0; - - ret = pxa2xx_spi_map_dma_buffer(drv_data, DMA_TO_DEVICE); - if (ret <= 0) { - dev_warn(&drv_data->pdev->dev, "failed to DMA map TX\n"); - return 0; - } - - drv_data->tx_nents = ret; - - ret = pxa2xx_spi_map_dma_buffer(drv_data, DMA_FROM_DEVICE); - if (ret <= 0) { - pxa2xx_spi_unmap_dma_buffer(drv_data, DMA_TO_DEVICE); - dev_warn(&drv_data->pdev->dev, "failed to DMA map RX\n"); - return 0; - } - - drv_data->rx_nents = ret; - return 1; -} - irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) { u32 status; @@ -245,8 +133,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_async(drv_data->rx_chan); - dmaengine_terminate_async(drv_data->tx_chan); + dmaengine_terminate_async(drv_data->master->dma_rx); + dmaengine_terminate_async(drv_data->master->dma_tx); pxa2xx_spi_dma_transfer_complete(drv_data, true); return IRQ_HANDLED; @@ -285,16 +173,15 @@ int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst) return 0; err_rx: - dmaengine_terminate_async(drv_data->tx_chan); + dmaengine_terminate_async(drv_data->master->dma_tx); err_tx: - pxa2xx_spi_unmap_dma_buffers(drv_data); return err; } void pxa2xx_spi_dma_start(struct driver_data *drv_data) { - dma_async_issue_pending(drv_data->rx_chan); - dma_async_issue_pending(drv_data->tx_chan); + dma_async_issue_pending(drv_data->master->dma_rx); + dma_async_issue_pending(drv_data->master->dma_tx); atomic_set(&drv_data->dma_running, 1); } @@ -303,21 +190,22 @@ int pxa2xx_spi_dma_setup(struct driver_data *drv_data) { struct pxa2xx_spi_master *pdata = drv_data->master_info; struct device *dev = &drv_data->pdev->dev; + struct spi_master *master = drv_data->master; dma_cap_mask_t mask; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - drv_data->tx_chan = dma_request_slave_channel_compat(mask, + master->dma_tx = dma_request_slave_channel_compat(mask, pdata->dma_filter, pdata->tx_param, dev, "tx"); - if (!drv_data->tx_chan) + if (!master->dma_tx) return -ENODEV; - drv_data->rx_chan = dma_request_slave_channel_compat(mask, + master->dma_rx = dma_request_slave_channel_compat(mask, pdata->dma_filter, pdata->rx_param, dev, "rx"); - if (!drv_data->rx_chan) { - dma_release_channel(drv_data->tx_chan); - drv_data->tx_chan = NULL; + if (!master->dma_rx) { + dma_release_channel(master->dma_tx); + master->dma_tx = NULL; return -ENODEV; } @@ -326,17 +214,17 @@ 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_sync(drv_data->rx_chan); - dma_release_channel(drv_data->rx_chan); - sg_free_table(&drv_data->rx_sgt); - drv_data->rx_chan = NULL; + struct spi_master *master = drv_data->master; + + if (master->dma_rx) { + dmaengine_terminate_sync(master->dma_rx); + dma_release_channel(master->dma_rx); + master->dma_rx = NULL; } - if (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; + if (master->dma_tx) { + dmaengine_terminate_sync(master->dma_tx); + dma_release_channel(master->dma_tx); + master->dma_tx = NULL; } } diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e5c457a..3e90a4c 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -912,6 +912,17 @@ static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data, return clk_div << 8; } +static bool pxa2xx_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct chip_data *chip = spi_get_ctldata(spi); + + return chip->enable_dma && + xfer->len <= MAX_DMA_LEN && + xfer->len >= chip->dma_burst_size; +} + static void pump_transfers(unsigned long data) { struct driver_data *drv_data = (struct driver_data *)data; @@ -929,6 +940,7 @@ static void pump_transfers(unsigned long data) u32 dma_burst = drv_data->cur_chip->dma_burst_size; u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data); int err; + int dma_mapped; /* Get current state information */ message = drv_data->cur_msg; @@ -963,7 +975,7 @@ static void pump_transfers(unsigned long data) } /* Check if we can DMA this transfer */ - if (!pxa2xx_spi_dma_is_possible(transfer->len) && chip->enable_dma) { + if (transfer->len > MAX_DMA_LEN && chip->enable_dma) { /* reject already-mapped transfers; PIO won't always work */ if (message->is_dma_mapped @@ -1040,10 +1052,10 @@ static void pump_transfers(unsigned long data) message->state = RUNNING_STATE; - drv_data->dma_mapped = 0; - if (pxa2xx_spi_dma_is_possible(drv_data->len)) - drv_data->dma_mapped = pxa2xx_spi_map_dma_buffers(drv_data); - if (drv_data->dma_mapped) { + dma_mapped = master->can_dma && + master->can_dma(master, message->spi, transfer) && + master->cur_msg_mapped; + if (dma_mapped) { /* Ensure we have the correct interrupt handler */ drv_data->transfer_handler = pxa2xx_spi_dma_transfer; @@ -1075,12 +1087,12 @@ static void pump_transfers(unsigned long data) dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", master->max_speed_hz / (1 + ((cr0 & SSCR0_SCR(0xfff)) >> 8)), - drv_data->dma_mapped ? "DMA" : "PIO"); + dma_mapped ? "DMA" : "PIO"); else dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", master->max_speed_hz / 2 / (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)), - drv_data->dma_mapped ? "DMA" : "PIO"); + dma_mapped ? "DMA" : "PIO"); if (is_lpss_ssp(drv_data)) { if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff) @@ -1594,6 +1606,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) if (status) { dev_dbg(dev, "no DMA channels available, using PIO\n"); platform_info->enable_dma = false; + } else { + master->can_dma = pxa2xx_spi_can_dma; } } diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index e6b0900..d217ad5 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -50,12 +50,6 @@ struct driver_data { struct tasklet_struct pump_transfers; /* DMA engine support */ - struct dma_chan *rx_chan; - struct dma_chan *tx_chan; - struct sg_table rx_sgt; - struct sg_table tx_sgt; - int rx_nents; - int tx_nents; atomic_t dma_running; /* Current message transfer state info */ @@ -67,7 +61,6 @@ struct driver_data { void *tx_end; void *rx; void *rx_end; - int dma_mapped; u8 n_bytes; int (*write)(struct driver_data *drv_data); int (*read)(struct driver_data *drv_data); @@ -145,8 +138,6 @@ extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data); #define MAX_DMA_LEN SZ_64K #define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) -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); extern int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst); extern void pxa2xx_spi_dma_start(struct driver_data *drv_data); -- cgit v0.10.2 From ff1ba3dac08eb10bd096d799430e5d1494f2ddfb Mon Sep 17 00:00:00 2001 From: Christian Gmeiner Date: Tue, 21 Jun 2016 14:12:54 +0200 Subject: spi: imx: wait_for_completion_timeout(..) for PIO transfers In some rare cases I see the following 'task blocked' information. It looks like the PIO transfer has some problems and never succeeds. Make use of wait_for_completion_timeout(..) to detect this case and return -ETIMEDOUT. [ 240.246067] INFO: task hexdump:1660 blocked for more than 120 seconds. [ 240.246089] Not tainted 4.1.17 0000001 [ 240.246099] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 240.246109] hexdump D c0575548 0 1660 1 0x00000000 [ 240.246132] Backtrace: [ 240.246166] [] (__schedule) from [] (schedule+0x40/0xa4) [ 240.246176] r10:00000000 r9:c07f1300 r8:c07b8408 r7:c0576518 r6:7fffffff r5:7fffffff [ 240.246210] r4:ee972e7c [ 240.246233] [] (schedule) from [] (schedule_timeout+0x174/0x274) [ 240.246254] [] (schedule_timeout) from [] (wait_for_common+0xc0/0x164) [ 240.246263] r10:00000000 r9:c07f1300 r8:00000002 r7:00000000 r6:7fffffff r5:ee972e78 [ 240.246294] r4:ee972e7c [ 240.246314] [] (wait_for_common) from [] (wait_for_completion+0x20/0x24) [ 240.246324] r10:ee972e50 r8:00000001 r7:c3976200 r6:ee972c00 r5:ee972e50 r4:c2c87d28 [ 240.246367] [] (wait_for_completion) from [] (spi_imx_transfer+0xe8/0x3cc) [ 240.246393] [] (spi_imx_transfer) from [] (spi_bitbang_transfer_one+0xb4/0x250) [ 240.246403] r10:ee972e50 r8:00000001 r7:00000000 r6:c2c87da0 r5:00000000 r4:c2c87d28 [ 240.246443] [] (spi_bitbang_transfer_one) from [] (__spi_pump_messages+0x36c/0x6b4) [ 240.246452] r10:ee9e5010 r9:00000001 r8:ee9e5010 r7:00000000 r6:c2c87da0 r5:c2c87d6c [ 240.246483] r4:ee972c00 [ 240.246503] [] (__spi_pump_messages) from [] (__spi_sync+0x138/0x1e4) [ 240.246512] r10:00000000 r9:00000000 r8:c03f25a8 r7:00000000 r6:ee972c00 r5:c3976200 [ 240.246542] r4:c2c87da0 [ 240.246562] [] (__spi_sync) from [] (spi_sync+0x1c/0x20) [ 240.246571] r10:00040000 r9:00000000 r8:c3976200 r7:00000000 r6:ee973300 r5:c2c87da0 [ 240.246602] r4:ee973014 [ 240.246623] [] (spi_sync) from [] (m25p80_read+0xf8/0x124) [ 240.246641] [] (m25p80_read) from [] (spi_nor_read+0x64/0x80) [ 240.246651] r10:00004000 r8:00004000 r7:00000000 r6:00040000 r5:00000000 r4:ee973014 [ 240.246698] [] (spi_nor_read) from [] (mtd_read+0x98/0xcc) [ 240.246708] r7:c2c87ea0 r6:ee973098 r5:00000000 r4:001c0000 [ 240.246740] [] (mtd_read) from [] (mtdchar_read+0xcc/0x204) [ 240.246750] r9:ed424000 r8:00000000 r7:b495d018 r6:c2c87f78 r5:00000000 r4:00040000 [ 240.246793] [] (mtdchar_read) from [] (__vfs_read+0x3c/0xe0) [ 240.246803] r10:00004000 r9:00000000 r8:c2c87f78 r7:b495d018 r6:c2c87f78 r5:c05c8104 [ 240.246833] r4:c32fe600 [ 240.246852] [] (__vfs_read) from [] (vfs_read+0x98/0x154) [ 240.246861] r10:00000000 r8:00040000 r7:00004000 r6:c2c87f78 r5:b495d018 r4:c32fe600 [ 240.246899] [] (vfs_read) from [] (SyS_read+0x50/0x90) [ 240.246908] r10:00000000 r8:00040000 r7:b495d018 r6:00004000 r5:c32fe601 r4:c32fe600 [ 240.246953] [] (SyS_read) from [] (ret_fast_syscall+0x0/0x3c) [ 240.246962] r9:c2c86000 r8:c000fc04 r7:00000003 r6:00004000 r5:00000000 r4:b495d018 Signed-off-by: Christian Gmeiner Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index cafe42c..f63cb30 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1042,6 +1042,8 @@ static int spi_imx_pio_transfer(struct spi_device *spi, struct spi_transfer *transfer) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + unsigned long transfer_timeout; + unsigned long timeout; spi_imx->tx_buf = transfer->tx_buf; spi_imx->rx_buf = transfer->rx_buf; @@ -1054,7 +1056,15 @@ static int spi_imx_pio_transfer(struct spi_device *spi, spi_imx->devtype_data->intctrl(spi_imx, MXC_INT_TE); - wait_for_completion(&spi_imx->xfer_done); + transfer_timeout = spi_imx_calculate_timeout(spi_imx, transfer->len); + + timeout = wait_for_completion_timeout(&spi_imx->xfer_done, + transfer_timeout); + if (!timeout) { + dev_err(&spi->dev, "I/O Error in PIO\n"); + spi_imx->devtype_data->reset(spi_imx); + return -ETIMEDOUT; + } return transfer->len; } -- cgit v0.10.2 From a777059a3fc13f51e91442ccf043c8e3bcd1c48f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 22 Jun 2016 14:48:26 +0200 Subject: spi: Improve DT binding documentation - Add missing verbs and periods, - Add spaces before opening parentheses, - Align list layout, - Correct grammar, - Move "cs-gpios" from the required to the optional properties section, - Remove spaces before tabs. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index 42d5954..1782286 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -8,11 +8,10 @@ in slave mode. The SPI master node requires the following properties: - #address-cells - number of cells required to define a chip select - address on the SPI bus. + address on the SPI bus. - #size-cells - should be zero. - compatible - name of SPI bus controller following generic names - recommended practice. -- cs-gpios - (optional) gpios chip select. + recommended practice. No other properties are required in the SPI bus node. It is assumed that a driver for an SPI bus device will understand that it is an SPI bus. However, the binding does not attempt to define the specific method for @@ -22,11 +21,12 @@ assumption that board specific platform code will be used to manage chip selects. Individual drivers can define additional properties to support describing the chip select layout. -Optional property: -- num-cs : total number of chipselects +Optional properties: +- cs-gpios - gpios chip select. +- num-cs - total number of chipselects. -If cs-gpios is used the number of chip select will automatically increased -with max(cs-gpios > hw cs) +If cs-gpios is used the number of chip selects will be increased automatically +with max(cs-gpios > hw cs). So if for example the controller has 2 CS lines, and the cs-gpios property looks like this: @@ -45,29 +45,30 @@ SPI slave nodes must be children of the SPI master node and can contain the following properties. - reg - (required) chip select address of device. - compatible - (required) name of SPI device following generic names - recommended practice -- spi-max-frequency - (required) Maximum SPI clocking speed of device in Hz + recommended practice. +- spi-max-frequency - (required) Maximum SPI clocking speed of device in Hz. - spi-cpol - (optional) Empty property indicating device requires - inverse clock polarity (CPOL) mode + inverse clock polarity (CPOL) mode. - spi-cpha - (optional) Empty property indicating device requires - shifted clock phase (CPHA) mode + shifted clock phase (CPHA) mode. - spi-cs-high - (optional) Empty property indicating device requires - chip select active high + chip select active high. - spi-3wire - (optional) Empty property indicating device requires - 3-wire mode. + 3-wire mode. - spi-lsb-first - (optional) Empty property indicating device requires LSB first mode. -- spi-tx-bus-width - (optional) The bus width(number of data wires) that +- spi-tx-bus-width - (optional) The bus width (number of data wires) that is used for MOSI. Defaults to 1 if not present. -- spi-rx-bus-width - (optional) The bus width(number of data wires) that +- spi-rx-bus-width - (optional) The bus width (number of data wires) that is used for MISO. Defaults to 1 if not present. - spi-rx-delay-us - (optional) Microsecond delay after a read transfer. - spi-tx-delay-us - (optional) Microsecond delay after a write transfer. Some SPI controllers and devices support Dual and Quad SPI transfer mode. -It allows data in the SPI system to be transferred in 2 wires(DUAL) or 4 wires(QUAD). +It allows data in the SPI system to be transferred using 2 wires (DUAL) or 4 +wires (QUAD). Now the value that spi-tx-bus-width and spi-rx-bus-width can receive is -only 1(SINGLE), 2(DUAL) and 4(QUAD). +only 1 (SINGLE), 2 (DUAL) and 4 (QUAD). Dual/Quad mode is not allowed when 3-wire mode is used. If a gpio chipselect is used for the SPI slave the gpio number will be passed -- cgit v0.10.2 From a6802cc05f9e48e86bc3f9f056c74a69dc5e37fa Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 22 Jun 2016 14:50:03 +0200 Subject: spi: sh-msiof: Remove sh_msiof_spi_priv.chipdata After probe time, the pointer to the sh_msiof_chipdata structure in the sh_msiof_spi_priv structure is used only for checking the SPI master flags. As these are also available in the spi_master structure, convert the users to access those, and remove the pointer. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index a7934ab..0f83ad1 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -45,7 +45,6 @@ struct sh_msiof_spi_priv { void __iomem *mapbase; struct clk *clk; struct platform_device *pdev; - const struct sh_msiof_chipdata *chipdata; struct sh_msiof_spi_info *info; struct completion done; unsigned int tx_fifo_size; @@ -271,7 +270,7 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, scr = sh_msiof_spi_div_table[k].brdv | SCR_BRPS(brps); sh_msiof_write(p, TSCR, scr); - if (!(p->chipdata->master_flags & SPI_MASTER_MUST_TX)) + if (!(p->master->flags & SPI_MASTER_MUST_TX)) sh_msiof_write(p, RSCR, scr); } @@ -336,7 +335,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, tmp |= lsb_first << MDR1_BITLSB_SHIFT; tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p); sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON); - if (p->chipdata->master_flags & SPI_MASTER_MUST_TX) { + if (p->master->flags & SPI_MASTER_MUST_TX) { /* These bits are reserved if RX needs TX */ tmp &= ~0x0000ffff; } @@ -360,7 +359,7 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, { u32 dr2 = MDR2_BITLEN1(bits) | MDR2_WDLEN1(words); - if (tx_buf || (p->chipdata->master_flags & SPI_MASTER_MUST_TX)) + if (tx_buf || (p->master->flags & SPI_MASTER_MUST_TX)) sh_msiof_write(p, TMDR2, dr2); else sh_msiof_write(p, TMDR2, dr2 | MDR2_GRPMASK1); @@ -1152,6 +1151,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) { struct resource *r; struct spi_master *master; + const struct sh_msiof_chipdata *chipdata; const struct of_device_id *of_id; struct sh_msiof_spi_priv *p; int i; @@ -1170,10 +1170,10 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) of_id = of_match_device(sh_msiof_match, &pdev->dev); if (of_id) { - p->chipdata = of_id->data; + chipdata = of_id->data; p->info = sh_msiof_spi_parse_dt(&pdev->dev); } else { - p->chipdata = (const void *)pdev->id_entry->driver_data; + chipdata = (const void *)pdev->id_entry->driver_data; p->info = dev_get_platdata(&pdev->dev); } @@ -1217,8 +1217,8 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); /* Platform data may override FIFO sizes */ - p->tx_fifo_size = p->chipdata->tx_fifo_size; - p->rx_fifo_size = p->chipdata->rx_fifo_size; + p->tx_fifo_size = chipdata->tx_fifo_size; + p->rx_fifo_size = chipdata->rx_fifo_size; if (p->info->tx_fifo_override) p->tx_fifo_size = p->info->tx_fifo_override; if (p->info->rx_fifo_override) @@ -1227,7 +1227,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) /* init master code */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE; - master->flags = p->chipdata->master_flags; + master->flags = chipdata->master_flags; master->bus_num = pdev->id; master->dev.of_node = pdev->dev.of_node; master->num_chipselect = p->info->num_chipselect; -- cgit v0.10.2 From 77cca63acc444d0eee66d3631659831b6339c95e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 23 Jun 2016 18:01:34 +0100 Subject: spi: ti-qspi: spelling mistake: "trasnfers" -> "transfers" trivial fix to spelling mistake in dev_dbg message Signed-off-by: Colin Ian King Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 443f664..e0e5c6aa 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -141,7 +141,7 @@ static int ti_qspi_setup(struct spi_device *spi) u32 clk_ctrl_reg, clk_rate, clk_mask; if (spi->master->busy) { - dev_dbg(qspi->dev, "master busy doing other trasnfers\n"); + dev_dbg(qspi->dev, "master busy doing other transfers\n"); return -EBUSY; } -- cgit v0.10.2 From 0b06d8cf4f9307887d52776d77bdbc8d6dd9d98d Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Tue, 14 Jun 2016 12:10:04 +0000 Subject: spi: sunxi: set maximum and minimum speed of SPI master The speed limits are unset in the sun4i and sun6i SPI drivers. The maximum speed of SPI master is used when maximum speed of SPI slave is not specified. Also the __spi_validate function should check that transfer speeds do not exceed the master limits. The user manual for A10 and A31 specifies maximum speed of the SPI clock as 100MHz and minimum as 3kHz. Setting the SPI clock to out-of-spec values can lock up the SoC. Signed-off-by: Michal Suchanek -- v2: new patch v3: fix constant style Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 07ce7c9..d02cf98 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -392,6 +392,8 @@ static int sun4i_spi_probe(struct platform_device *pdev) } sspi->master = master; + master->max_speed_hz = 100 * 1000 * 1000; + master->min_speed_hz = 3 * 1000; master->set_cs = sun4i_spi_set_cs; master->transfer_one = sun4i_spi_transfer_one; master->num_chipselect = 4; diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 6fa6ff1..d5a6a2e 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -390,6 +390,8 @@ static int sun6i_spi_probe(struct platform_device *pdev) } sspi->master = master; + master->max_speed_hz = 100 * 1000 * 1000; + master->min_speed_hz = 3 * 1000; master->set_cs = sun6i_spi_set_cs; master->transfer_one = sun6i_spi_transfer_one; master->num_chipselect = 4; -- cgit v0.10.2 From b7ddfb9f496e23eafccd1d630f25b68f105e0ee8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 28 Jun 2016 13:15:08 +0100 Subject: spi: loopback-test: fix spelling mistake: "missmatch" -> "mismatch" trivial fix to spelling mistake in dev_err message Signed-off-by: Colin Ian King Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index cf4bb36..faa1b13 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -536,7 +536,7 @@ static int spi_test_check_loopback_result(struct spi_device *spi, mismatch_error: dev_err(&spi->dev, - "loopback strangeness - transfer missmatch on byte %04zx - expected 0x%02x, but got 0x%02x\n", + "loopback strangeness - transfer mismatch on byte %04zx - expected 0x%02x, but got 0x%02x\n", i, txb, rxb); return -EINVAL; -- cgit v0.10.2 From 0fb7620fba7feb977a5138f8d7f6b42514f81ea9 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 29 Jun 2016 04:33:33 +0900 Subject: spi: Fix typo in devicetree/bindings/spi This patch fix spelling typos found in Documentation/devicetree/bingings/spi. Signed-off-by: Masanari Iida Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-davinci.txt b/Documentation/devicetree/bindings/spi/spi-davinci.txt index d1e914a..f5916c9 100644 --- a/Documentation/devicetree/bindings/spi/spi-davinci.txt +++ b/Documentation/devicetree/bindings/spi/spi-davinci.txt @@ -21,7 +21,7 @@ Required properties: IP to the interrupt controller within the SoC. Possible values are 0 and 1. Manual says one of the two possible interrupt lines can be tied to the interrupt controller. Set this - based on a specifc SoC configuration. + based on a specific SoC configuration. - interrupts: interrupt number mapped to CPU. - clocks: spi clk phandle diff --git a/Documentation/devicetree/bindings/spi/ti_qspi.txt b/Documentation/devicetree/bindings/spi/ti_qspi.txt index 50b14f6..e65fde4 100644 --- a/Documentation/devicetree/bindings/spi/ti_qspi.txt +++ b/Documentation/devicetree/bindings/spi/ti_qspi.txt @@ -20,7 +20,7 @@ Optional properties: chipselect register and offset of that register. NOTE: TI QSPI controller requires different pinmux and IODelay -paramaters for Mode-0 and Mode-3 operations, which needs to be set up by +parameters for Mode-0 and Mode-3 operations, which needs to be set up by the bootloader (U-Boot). Default configuration only supports Mode-0 operation. Hence, "spi-cpol" and "spi-cpha" DT properties cannot be specified in the slave nodes of TI QSPI controller without appropriate -- cgit v0.10.2 From aa4964c4eb3ed38666023bcb805403cb7cf2af63 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 28 Jun 2016 11:41:11 +0900 Subject: spi: s3c64xx: group the CS signalling writes in a single function To enable/disable the CS line, the driver performs a writel in the S3C64XX_SPI_SLAVE_SEL registers. Group the register's configuration in a single function. Signed-off-by: Andi Shyti Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 5a76a50..972367d 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -310,6 +310,28 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, dma_async_issue_pending(dma->ch); } +static void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct s3c64xx_spi_driver_data *sdd = + spi_master_get_devdata(spi->master); + + if (enable) { + if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) { + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + } else { + u32 ssel = readl(sdd->regs + S3C64XX_SPI_SLAVE_SEL); + + ssel |= (S3C64XX_SPI_SLAVE_AUTO | + S3C64XX_SPI_SLAVE_NSC_CNT_2); + writel(ssel, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + } + } else { + if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) + writel(S3C64XX_SPI_SLAVE_SIG_INACT, + sdd->regs + S3C64XX_SPI_SLAVE_SEL); + } +} + static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) { struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); @@ -706,12 +728,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, enable_datapath(sdd, spi, xfer, use_dma); /* Start the signals */ - if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); - else - writel(readl(sdd->regs + S3C64XX_SPI_SLAVE_SEL) - | S3C64XX_SPI_SLAVE_AUTO | S3C64XX_SPI_SLAVE_NSC_CNT_2, - sdd->regs + S3C64XX_SPI_SLAVE_SEL); + s3c64xx_spi_set_cs(spi, true); spin_unlock_irqrestore(&sdd->lock, flags); @@ -861,16 +878,15 @@ static int s3c64xx_spi_setup(struct spi_device *spi) pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); - if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) - writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + s3c64xx_spi_set_cs(spi, false); + return 0; setup_exit: pm_runtime_mark_last_busy(&sdd->pdev->dev); pm_runtime_put_autosuspend(&sdd->pdev->dev); /* setup() returns with device de-selected */ - if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) - writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + s3c64xx_spi_set_cs(spi, false); if (gpio_is_valid(spi->cs_gpio)) gpio_free(spi->cs_gpio); -- cgit v0.10.2 From a92e7c3d82a1313ab1954e5cdfd8f04efdb4ca78 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 28 Jun 2016 11:41:12 +0900 Subject: spi: s3c64xx: consider the case when the CS line is not connected When the CS line is not connected, it is not needed to enable or disable the chip selection functionality from the s3c64xx devices in order to perform a transfer. Set the CS controller logically always enabled already during initialization (by writing '0' in the S3C64XX_SPI_SLAVE_SEL register) and never disable it. Signed-off-by: Andi Shyti Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-samsung.txt b/Documentation/devicetree/bindings/spi/spi-samsung.txt index 6dbdeb3..930bc73 100644 --- a/Documentation/devicetree/bindings/spi/spi-samsung.txt +++ b/Documentation/devicetree/bindings/spi/spi-samsung.txt @@ -40,6 +40,9 @@ Optional Board Specific Properties: - cs-gpios: should specify GPIOs used for chipselects (see spi-bus.txt) +- no-cs-readback: the CS line is disconnected, therefore the device should not + operate based on CS signalling. + SPI Controller specific data in SPI slave nodes: - The spi slave nodes should provide the following information which is required diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 972367d..14269b0 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -315,6 +315,9 @@ static void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi->master); + if (sdd->cntrlr_info->no_cs) + return; + if (enable) { if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) { writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); @@ -960,7 +963,9 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) sdd->cur_speed = 0; - if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) + if (sci->no_cs) + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); /* Disable Interrupts - we use Polling if not DMA mode */ @@ -1015,6 +1020,8 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) sci->num_cs = temp; } + sci->no_cs = of_property_read_bool(dev->of_node, "broken-cs"); + return sci; } #else diff --git a/include/linux/platform_data/spi-s3c64xx.h b/include/linux/platform_data/spi-s3c64xx.h index fb5625b..5c1e21c 100644 --- a/include/linux/platform_data/spi-s3c64xx.h +++ b/include/linux/platform_data/spi-s3c64xx.h @@ -38,6 +38,7 @@ struct s3c64xx_spi_csinfo { struct s3c64xx_spi_info { int src_clk_nr; int num_cs; + bool no_cs; int (*cfg_gpio)(void); dma_filter_fn filter; void *dma_tx; -- cgit v0.10.2 From 11f66f0927bd044c17b2c2884d5103f8eb6cab38 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 28 Jun 2016 11:41:13 +0900 Subject: spi: s3c64xx: do not configure the device twice At the start of the transfer, the spi_config function is called twice, the first time when the 3c64xx_spi_prepare_message is called and the second time with the s3c64xx_spi_transfer_one, both called from the spi framework. Remove the first call at the prepare message because in that point we don't have the imformation about "bit per word" and frequency. Signed-off-by: Andi Shyti Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 14269b0..c65a9e6 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -676,16 +676,6 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master, struct spi_device *spi = msg->spi; struct s3c64xx_spi_csinfo *cs = spi->controller_data; - /* If Master's(controller) state differs from that needed by Slave */ - if (sdd->cur_speed != spi->max_speed_hz - || sdd->cur_mode != spi->mode - || sdd->cur_bpw != spi->bits_per_word) { - sdd->cur_bpw = spi->bits_per_word; - sdd->cur_speed = spi->max_speed_hz; - sdd->cur_mode = spi->mode; - s3c64xx_spi_config(sdd); - } - /* Configure feedback delay */ writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); @@ -712,6 +702,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { sdd->cur_bpw = bpw; sdd->cur_speed = speed; + sdd->cur_mode = spi->mode; s3c64xx_spi_config(sdd); } -- cgit v0.10.2 From 730d9d4d11cf48b17d07daaff1f0540642317e09 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 28 Jun 2016 11:41:14 +0900 Subject: spi: s3c64xx: simplify if statement in prepare_transfer function The whole function is inside an 'if' statement ("!is_polling(sdd)"). Check the opposite of that statement at the beginning and exit, this way we can have one level less of indentation. Remove the goto paths as they are redundant. Signed-off-by: Andi Shyti Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index c65a9e6..6d8486f 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -341,38 +341,32 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) dma_filter_fn filter = sdd->cntrlr_info->filter; struct device *dev = &sdd->pdev->dev; dma_cap_mask_t mask; - int ret; - if (!is_polling(sdd)) { - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - /* Acquire DMA channels */ - sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, - sdd->cntrlr_info->dma_rx, dev, "rx"); - if (!sdd->rx_dma.ch) { - dev_err(dev, "Failed to get RX DMA channel\n"); - ret = -EBUSY; - goto out; - } - spi->dma_rx = sdd->rx_dma.ch; - - sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, - sdd->cntrlr_info->dma_tx, dev, "tx"); - if (!sdd->tx_dma.ch) { - dev_err(dev, "Failed to get TX DMA channel\n"); - ret = -EBUSY; - goto out_rx; - } - spi->dma_tx = sdd->tx_dma.ch; + if (is_polling(sdd)) + return 0; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* Acquire DMA channels */ + sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, + sdd->cntrlr_info->dma_rx, dev, "rx"); + if (!sdd->rx_dma.ch) { + dev_err(dev, "Failed to get RX DMA channel\n"); + return -EBUSY; } + spi->dma_rx = sdd->rx_dma.ch; - return 0; + sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, + sdd->cntrlr_info->dma_tx, dev, "tx"); + if (!sdd->tx_dma.ch) { + dev_err(dev, "Failed to get TX DMA channel\n"); + dma_release_channel(sdd->rx_dma.ch); + return -EBUSY; + } + spi->dma_tx = sdd->tx_dma.ch; -out_rx: - dma_release_channel(sdd->rx_dma.ch); -out: - return ret; + return 0; } static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) -- cgit v0.10.2 From 9b96f0704b5ad75ef26085d03934bee21d3362f0 Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sat, 2 Jul 2016 14:12:00 +0530 Subject: spi: spi-bfin5xx: Remove deprecated create_singlethread_workqueue The workqueue "workqueue" serves as a driver message queue. It has a single work item(&drv_data->pump_messages) and hence doesn't require ordering. Also, it is not being used on a memory reclaim path. Hence, the singlethreaded workqueue has been replaced with the use of system_wq. System workqueues have been able to handle high level of concurrency for a long time now and hence it's not required to have a singlethreaded workqueue just to gain concurrency. Unlike a dedicated per-cpu workqueue created with create_singlethread_workqueue(), system_wq allows multiple work items to overlap executions even on the same CPU; however, a per-cpu workqueue doesn't have any CPU locality or global ordering guarantee unless the target CPU is explicitly specified and thus the increase of local concurrency shouldn't make any difference. Work item has been flushed in bfin_spi_destroy_queue() to ensure that there are no pending tasks while disconnecting the driver. Signed-off-by: Bhaktipriya Shridhar Acked-by: Tejun Heo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 1e91325..249c7a3 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -67,8 +67,6 @@ struct bfin_spi_master_data { /* BFIN hookup */ struct bfin5xx_spi_master *master_info; - /* Driver message queue */ - struct workqueue_struct *workqueue; struct work_struct pump_messages; spinlock_t lock; struct list_head queue; @@ -359,7 +357,7 @@ static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data) drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; drv_data->cur_chip = NULL; - queue_work(drv_data->workqueue, &drv_data->pump_messages); + schedule_work(&drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); msg->state = NULL; @@ -946,7 +944,7 @@ static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg) list_add_tail(&msg->queue, &drv_data->queue); if (drv_data->running && !drv_data->busy) - queue_work(drv_data->workqueue, &drv_data->pump_messages); + schedule_work(&drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); @@ -1177,12 +1175,7 @@ static int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data) tasklet_init(&drv_data->pump_transfers, bfin_spi_pump_transfers, (unsigned long)drv_data); - /* init messages workqueue */ INIT_WORK(&drv_data->pump_messages, bfin_spi_pump_messages); - drv_data->workqueue = create_singlethread_workqueue( - dev_name(drv_data->master->dev.parent)); - if (drv_data->workqueue == NULL) - return -EBUSY; return 0; } @@ -1204,7 +1197,7 @@ static int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data) drv_data->cur_chip = NULL; spin_unlock_irqrestore(&drv_data->lock, flags); - queue_work(drv_data->workqueue, &drv_data->pump_messages); + schedule_work(&drv_data->pump_messages); return 0; } @@ -1246,7 +1239,7 @@ static int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data) if (status != 0) return status; - destroy_workqueue(drv_data->workqueue); + flush_work(&drv_data->pump_messages); return 0; } -- cgit v0.10.2 From b43afff7dc3719612dd5140fe2b0357ae1faf9b7 Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sat, 2 Jul 2016 14:20:55 +0530 Subject: spi: spi-txx9: Remove deprecated create_singlethread_workqueue The workqueue "workqueue" has a single work item(&c->work) and hence doesn't require ordering. Also, it is not being used on a memory reclaim path. Hence, the singlethreaded workqueue has been replaced with the use of system_wq. System workqueues have been able to handle high level of concurrency for a long time now and hence it's not required to have a singlethreaded workqueue just to gain concurrency. Unlike a dedicated per-cpu workqueue created with create_singlethread_workqueue(), system_wq allows multiple work items to overlap executions even on the same CPU; however, a per-cpu workqueue doesn't have any CPU locality or global ordering guarantee unless the target CPU is explicitly specified and thus the increase of local concurrency shouldn't make any difference. Work item has been flushed in txx9spi_remove() to ensure that nothing is pending while disconnecting the driver. Signed-off-by: Bhaktipriya Shridhar Acked-by: Tejun Heo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index d69f8f8..7492ea3 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -72,7 +72,6 @@ struct txx9spi { - struct workqueue_struct *workqueue; struct work_struct work; spinlock_t lock; /* protect 'queue' */ struct list_head queue; @@ -315,7 +314,7 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) spin_lock_irqsave(&c->lock, flags); list_add_tail(&m->queue, &c->queue); - queue_work(c->workqueue, &c->work); + schedule_work(&c->work); spin_unlock_irqrestore(&c->lock, flags); return 0; @@ -374,10 +373,6 @@ static int txx9spi_probe(struct platform_device *dev) if (ret) goto exit; - c->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (!c->workqueue) - goto exit_busy; c->last_chipselect = -1; dev_info(&dev->dev, "at %#llx, irq %d, %dMHz\n", @@ -400,8 +395,6 @@ static int txx9spi_probe(struct platform_device *dev) exit_busy: ret = -EBUSY; exit: - if (c->workqueue) - destroy_workqueue(c->workqueue); clk_disable(c->clk); spi_master_put(master); return ret; @@ -412,7 +405,7 @@ static int txx9spi_remove(struct platform_device *dev) struct spi_master *master = platform_get_drvdata(dev); struct txx9spi *c = spi_master_get_devdata(master); - destroy_workqueue(c->workqueue); + flush_work(&c->work); clk_disable(c->clk); return 0; } -- cgit v0.10.2 From ac96b737cdec77023bf2ead142a3875174af0c7a Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sat, 2 Jul 2016 14:27:06 +0530 Subject: spi: spi-mpc52xx-psc: Remove deprecated create_singlethread_workqueue The workqueue "workqueue" has a single work item(&mps->work) doesn't require ordering. Also, it is not being used on a memory reclaim path. Hence, the singlethreaded workqueue has been replaced with the use of system_wq. System workqueues have been able to handle high level of concurrency for a long time now and hence it's not required to have a singlethreaded workqueue just to gain concurrency. Unlike a dedicated per-cpu workqueue created with create_singlethread_workqueue(), system_wq allows multiple work items to overlap executions even on the same CPU; however, a per-cpu workqueue doesn't have any CPU locality or global ordering guarantee unless the target CPU is explicitly specified and thus the increase of local concurrency shouldn't make any difference. Work item has been flushed in mpc52xx_psc_spi_of_remove() to ensure that nothing is pending while disconnecting the driver. Signed-off-by: Bhaktipriya Shridhar Acked-by: Tejun Heo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index 72d11eb..42a8b85 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -42,7 +42,6 @@ struct mpc52xx_psc_spi { u8 bits_per_word; u8 busy; - struct workqueue_struct *workqueue; struct work_struct work; struct list_head queue; @@ -299,7 +298,7 @@ static int mpc52xx_psc_spi_transfer(struct spi_device *spi, spin_lock_irqsave(&mps->lock, flags); list_add_tail(&m->queue, &mps->queue); - queue_work(mps->workqueue, &mps->work); + schedule_work(&mps->work); spin_unlock_irqrestore(&mps->lock, flags); return 0; @@ -425,21 +424,12 @@ static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr, INIT_WORK(&mps->work, mpc52xx_psc_spi_work); INIT_LIST_HEAD(&mps->queue); - mps->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (mps->workqueue == NULL) { - ret = -EBUSY; - goto free_irq; - } - ret = spi_register_master(master); if (ret < 0) - goto unreg_master; + goto free_irq; return ret; -unreg_master: - destroy_workqueue(mps->workqueue); free_irq: free_irq(mps->irq, mps); free_master: @@ -484,8 +474,7 @@ static int mpc52xx_psc_spi_of_remove(struct platform_device *op) struct spi_master *master = spi_master_get(platform_get_drvdata(op)); struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); - flush_workqueue(mps->workqueue); - destroy_workqueue(mps->workqueue); + flush_work(&mps->work); spi_unregister_master(master); free_irq(mps->irq, mps); if (mps->psc) -- cgit v0.10.2 From eac9837f87017c7a3a6c0a8908fdda66f7f9c076 Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sat, 2 Jul 2016 14:29:41 +0530 Subject: spi: spi-bfin-sport: Remove deprecated create_singlethread_workqueue The workqueue "workqueue" serves as a driver message queue. It has a single work item(&drv_data->pump_messages) and hence doesn't require ordering. Also, it is not being used on a memory reclaim path. Hence, the singlethreaded workqueue has been replaced with the use of system_wq. System workqueues have been able to handle high level of concurrency for a long time now and hence it's not required to have a singlethreaded workqueue just to gain concurrency. Unlike a dedicated per-cpu workqueue created with create_singlethread_workqueue(), system_wq allows multiple work items to overlap executions even on the same CPU; however, a per-cpu workqueue doesn't have any CPU locality or global ordering guarantee unless the target CPU is explicitly specified and thus the increase of local concurrency shouldn't make any difference. Work item has been flushed in bfin_sport_spi_destroy_queue() to ensure that there are no pending tasks while disconnecting the driver. Signed-off-by: Bhaktipriya Shridhar Acked-by: Tejun Heo Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 6c96755..01d0ba9 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -64,8 +64,6 @@ struct bfin_sport_spi_master_data { /* Pin request list */ u16 *pin_req; - /* Driver message queue */ - struct workqueue_struct *workqueue; struct work_struct pump_messages; spinlock_t lock; struct list_head queue; @@ -300,7 +298,7 @@ bfin_sport_spi_giveback(struct bfin_sport_spi_master_data *drv_data) drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; drv_data->cur_chip = NULL; - queue_work(drv_data->workqueue, &drv_data->pump_messages); + schedule_work(&drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); if (!drv_data->cs_change) @@ -556,7 +554,7 @@ bfin_sport_spi_transfer(struct spi_device *spi, struct spi_message *msg) list_add_tail(&msg->queue, &drv_data->queue); if (drv_data->run && !drv_data->busy) - queue_work(drv_data->workqueue, &drv_data->pump_messages); + schedule_work(&drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); @@ -666,12 +664,7 @@ bfin_sport_spi_init_queue(struct bfin_sport_spi_master_data *drv_data) tasklet_init(&drv_data->pump_transfers, bfin_sport_spi_pump_transfers, (unsigned long)drv_data); - /* init messages workqueue */ INIT_WORK(&drv_data->pump_messages, bfin_sport_spi_pump_messages); - drv_data->workqueue = - create_singlethread_workqueue(dev_name(drv_data->master->dev.parent)); - if (drv_data->workqueue == NULL) - return -EBUSY; return 0; } @@ -694,7 +687,7 @@ bfin_sport_spi_start_queue(struct bfin_sport_spi_master_data *drv_data) drv_data->cur_chip = NULL; spin_unlock_irqrestore(&drv_data->lock, flags); - queue_work(drv_data->workqueue, &drv_data->pump_messages); + schedule_work(&drv_data->pump_messages); return 0; } @@ -738,7 +731,7 @@ bfin_sport_spi_destroy_queue(struct bfin_sport_spi_master_data *drv_data) if (status) return status; - destroy_workqueue(drv_data->workqueue); + flush_work(&drv_data->pump_messages); return 0; } -- cgit v0.10.2 From 47c169ee67a6adea902d597b9e7bad428c7aac12 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 4 Jul 2016 10:47:48 +0300 Subject: spi: s3c64xx: indent an if statement It generates a static checker warning if an if statement isn't indented. I think the code is fine except for the white space issue. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 6d8486f..9f0119f 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -330,8 +330,8 @@ static void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) } } else { if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) - writel(S3C64XX_SPI_SLAVE_SIG_INACT, - sdd->regs + S3C64XX_SPI_SLAVE_SEL); + writel(S3C64XX_SPI_SLAVE_SIG_INACT, + sdd->regs + S3C64XX_SPI_SLAVE_SEL); } } -- cgit v0.10.2 From 704d2b07946fcae68afd86c222fde129e11f6bbc Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 4 Jul 2016 13:21:07 +0300 Subject: spi: pxa2xx: Add support for Intel Kaby Lake PCH-H Kaby Lake PCH-H has the same SPI host controller as Skylake. Add these new PCI IDs to the list of supported devices. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 3e90a4c..2f6272a 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1394,6 +1394,9 @@ 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 }, + /* KBL-H */ + { PCI_VDEVICE(INTEL, 0xa2a9), LPSS_SPT_SSP }, + { PCI_VDEVICE(INTEL, 0xa2aa), LPSS_SPT_SSP }, /* BXT A-Step */ { PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP }, -- cgit v0.10.2 From 743485ea3bee852fa816a2ec6c64b3d500e39895 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Jul 2016 12:44:24 +0300 Subject: spi: pxa2xx-pci: Do a specific setup in a separate function Move LPSS specific setup to a separate function. It makes ->probe() cleaner as well as allows extend the driver for different variation of hardware in the future, e.g. for Intel Merrifield. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 5202de9..8d58598 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -29,8 +29,11 @@ struct pxa_spi_info { unsigned long max_clk_rate; /* DMA channel request parameters */ + bool (*dma_filter)(struct dma_chan *chan, void *param); void *tx_param; void *rx_param; + + int (*setup)(struct pci_dev *pdev, struct pxa_spi_info *c); }; static struct dw_dma_slave byt_tx_param = { .dst_id = 0 }; @@ -57,6 +60,35 @@ static bool lpss_dma_filter(struct dma_chan *chan, void *param) return true; } +static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) +{ + struct pci_dev *dma_dev; + + c->num_chipselect = 1; + c->max_clk_rate = 50000000; + + dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + + if (c->tx_param) { + struct dw_dma_slave *slave = c->tx_param; + + slave->dma_dev = &dma_dev->dev; + slave->m_master = 0; + slave->p_master = 1; + } + + if (c->rx_param) { + struct dw_dma_slave *slave = c->rx_param; + + slave->dma_dev = &dma_dev->dev; + slave->m_master = 0; + slave->p_master = 1; + } + + c->dma_filter = lpss_dma_filter; + return 0; +} + static struct pxa_spi_info spi_info_configs[] = { [PORT_CE4100] = { .type = PXA25x_SSP, @@ -67,32 +99,28 @@ static struct pxa_spi_info spi_info_configs[] = { [PORT_BYT] = { .type = LPSS_BYT_SSP, .port_id = 0, - .num_chipselect = 1, - .max_clk_rate = 50000000, + .setup = lpss_spi_setup, .tx_param = &byt_tx_param, .rx_param = &byt_rx_param, }, [PORT_BSW0] = { .type = LPSS_BYT_SSP, .port_id = 0, - .num_chipselect = 1, - .max_clk_rate = 50000000, + .setup = lpss_spi_setup, .tx_param = &bsw0_tx_param, .rx_param = &bsw0_rx_param, }, [PORT_BSW1] = { .type = LPSS_BYT_SSP, .port_id = 1, - .num_chipselect = 1, - .max_clk_rate = 50000000, + .setup = lpss_spi_setup, .tx_param = &bsw1_tx_param, .rx_param = &bsw1_rx_param, }, [PORT_BSW2] = { .type = LPSS_BYT_SSP, .port_id = 2, - .num_chipselect = 1, - .max_clk_rate = 50000000, + .setup = lpss_spi_setup, .tx_param = &bsw2_tx_param, .rx_param = &bsw2_rx_param, }, @@ -105,8 +133,7 @@ static struct pxa_spi_info spi_info_configs[] = { [PORT_LPT] = { .type = LPSS_LPT_SSP, .port_id = 0, - .num_chipselect = 1, - .max_clk_rate = 50000000, + .setup = lpss_spi_setup, .tx_param = &lpt_tx_param, .rx_param = &lpt_rx_param, }, @@ -122,7 +149,6 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, struct ssp_device *ssp; struct pxa_spi_info *c; char buf[40]; - struct pci_dev *dma_dev; ret = pcim_enable_device(dev); if (ret) @@ -133,30 +159,15 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, return ret; c = &spi_info_configs[ent->driver_data]; - - memset(&spi_pdata, 0, sizeof(spi_pdata)); - spi_pdata.num_chipselect = (c->num_chipselect > 0) ? - c->num_chipselect : dev->devfn; - - dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - - if (c->tx_param) { - struct dw_dma_slave *slave = c->tx_param; - - slave->dma_dev = &dma_dev->dev; - slave->m_master = 0; - slave->p_master = 1; - } - - if (c->rx_param) { - struct dw_dma_slave *slave = c->rx_param; - - slave->dma_dev = &dma_dev->dev; - slave->m_master = 0; - slave->p_master = 1; + if (c->setup) { + ret = c->setup(dev, c); + if (ret) + return ret; } - spi_pdata.dma_filter = lpss_dma_filter; + memset(&spi_pdata, 0, sizeof(spi_pdata)); + spi_pdata.num_chipselect = (c->num_chipselect > 0) ? c->num_chipselect : dev->devfn; + spi_pdata.dma_filter = c->dma_filter; spi_pdata.tx_param = c->tx_param; spi_pdata.rx_param = c->rx_param; spi_pdata.enable_dma = c->rx_param && c->tx_param; -- cgit v0.10.2 From 4f4709109ef7e1248b5515c68df4b9c5ad39fbdf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Jul 2016 12:44:25 +0300 Subject: spi: pxa2xx-pci: Enable SPI on Intel Merrifield The SPI controllers used on Intel Merrifield are PXA2XX compatible. This patch enables them. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 8d58598..b025eaf 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -15,6 +15,7 @@ enum { PORT_CE4100, PORT_BYT, + PORT_MRFLD, PORT_BSW0, PORT_BSW1, PORT_BSW2, @@ -89,6 +90,27 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return 0; } +static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) +{ + switch (PCI_FUNC(dev->devfn)) { + case 0: + c->port_id = 3; + c->num_chipselect = 1; + break; + case 1: + c->port_id = 5; + c->num_chipselect = 4; + break; + case 2: + c->port_id = 6; + c->num_chipselect = 1; + break; + default: + return -ENODEV; + } + return 0; +} + static struct pxa_spi_info spi_info_configs[] = { [PORT_CE4100] = { .type = PXA25x_SSP, @@ -124,6 +146,11 @@ static struct pxa_spi_info spi_info_configs[] = { .tx_param = &bsw2_tx_param, .rx_param = &bsw2_rx_param, }, + [PORT_MRFLD] = { + .type = PXA27x_SSP, + .max_clk_rate = 25000000, + .setup = mrfld_spi_setup, + }, [PORT_QUARK_X1000] = { .type = QUARK_X1000_SSP, .port_id = -1, @@ -222,6 +249,7 @@ static const struct pci_device_id pxa2xx_spi_pci_devices[] = { { PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 }, { PCI_VDEVICE(INTEL, 0x0935), PORT_QUARK_X1000 }, { PCI_VDEVICE(INTEL, 0x0f0e), PORT_BYT }, + { PCI_VDEVICE(INTEL, 0x1194), PORT_MRFLD }, { PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 }, { PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 }, { PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 }, -- cgit v0.10.2 From 21ddba19ce851d322172a1f006d27e4f6ff3059d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Jul 2016 12:44:26 +0300 Subject: spi: pxa2xx-pci: Remove unused code pcim_iomap_table() can't fail when called after pcim_iomap_regions(). Moreover, we already dereference returned value and kernel will crash if it is not correct. Remove obvious leftover of commit 0202775bc3a2 ("spi/pxa2xx-pci: switch to use pcim_* interfaces"). Cc: Mika Westerberg Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index b025eaf..4e3c5a7 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -202,10 +202,6 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, ssp = &spi_pdata.ssp; ssp->phys_base = pci_resource_start(dev, 0); ssp->mmio_base = pcim_iomap_table(dev)[0]; - if (!ssp->mmio_base) { - dev_err(&dev->dev, "failed to ioremap() registers\n"); - return -EIO; - } ssp->irq = dev->irq; ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn; ssp->type = c->type; -- cgit v0.10.2 From e379d2cd35ca0a9e2a2578f40cf75632266f5741 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Jul 2016 12:44:27 +0300 Subject: spi: pxa2xx-pci: Sort header block alphabetically Simply sort header block alphabetically. While here, sort devices by PCI ID and add a copyright line for Intel. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 4e3c5a7..5953edc 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -1,25 +1,26 @@ /* * CE4100's SPI device is more or less the same one as found on PXA * + * Copyright (C) 2016, Intel Corporation */ +#include +#include +#include #include #include -#include -#include #include -#include #include #include enum { - PORT_CE4100, + PORT_QUARK_X1000, PORT_BYT, PORT_MRFLD, PORT_BSW0, PORT_BSW1, PORT_BSW2, - PORT_QUARK_X1000, + PORT_CE4100, PORT_LPT, }; @@ -242,13 +243,13 @@ static void pxa2xx_spi_pci_remove(struct pci_dev *dev) } static const struct pci_device_id pxa2xx_spi_pci_devices[] = { - { PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 }, { PCI_VDEVICE(INTEL, 0x0935), PORT_QUARK_X1000 }, { PCI_VDEVICE(INTEL, 0x0f0e), PORT_BYT }, { PCI_VDEVICE(INTEL, 0x1194), PORT_MRFLD }, { PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 }, { PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 }, { PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 }, + { PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 }, { PCI_VDEVICE(INTEL, 0x9ce6), PORT_LPT }, { }, }; -- cgit v0.10.2 From cf9f4327a306be58df6c0b3817d7ef06a2a3da03 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 4 Jul 2016 17:18:15 +0300 Subject: spi: spidev: Add ACPI probing support Some IoT and maker software stacks are using spidev to perform raw access to the SPI bus instead of relying existing drivers provided by the kernel. They then implement their own "drivers" in userspace on top of the spidev raw interface. This is far from being an ideal solution but we do not want to prevent using mainline Linux in these devices. Now, it turns out that Windows has similar SPI devices than spidev which allow raw access on the SPI bus to userspace programs as described in the link below: https://msdn.microsoft.com/windows/hardware/drivers/spb/spi-tests-in-mitt These SPI test devices are also meant to be used during development and testing. In order to allow usage of spidev for development and testing in Linux, add those same ACPI IDs to the spidev driver (which is Linux counterpart of the Windows SPI test devices), but complain loudly so that users know it is not good idea to use it in production systems. Instead they should be using proper drivers for peripherals connected to the SPI bus. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index e3c19f3..2e05046 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -700,6 +701,43 @@ static const struct of_device_id spidev_dt_ids[] = { MODULE_DEVICE_TABLE(of, spidev_dt_ids); #endif +#ifdef CONFIG_ACPI + +/* Dummy SPI devices not to be used in production systems */ +#define SPIDEV_ACPI_DUMMY 1 + +static const struct acpi_device_id spidev_acpi_ids[] = { + /* + * The ACPI SPT000* devices are only meant for development and + * testing. Systems used in production should have a proper ACPI + * description of the connected peripheral and they should also use + * a proper driver instead of poking directly to the SPI bus. + */ + { "SPT0001", SPIDEV_ACPI_DUMMY }, + { "SPT0002", SPIDEV_ACPI_DUMMY }, + { "SPT0003", SPIDEV_ACPI_DUMMY }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, spidev_acpi_ids); + +static void spidev_probe_acpi(struct spi_device *spi) +{ + const struct acpi_device_id *id; + + if (!has_acpi_companion(&spi->dev)) + return; + + id = acpi_match_device(spidev_acpi_ids, &spi->dev); + if (WARN_ON(!id)) + return; + + if (id->driver_data == SPIDEV_ACPI_DUMMY) + dev_warn(&spi->dev, "do not use this driver in production systems!\n"); +} +#else +static inline void spidev_probe_acpi(struct spi_device *spi) {} +#endif + /*-------------------------------------------------------------------------*/ static int spidev_probe(struct spi_device *spi) @@ -719,6 +757,8 @@ static int spidev_probe(struct spi_device *spi) !of_match_device(spidev_dt_ids, &spi->dev)); } + spidev_probe_acpi(spi); + /* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) @@ -789,6 +829,7 @@ static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev", .of_match_table = of_match_ptr(spidev_dt_ids), + .acpi_match_table = ACPI_PTR(spidev_acpi_ids), }, .probe = spidev_probe, .remove = spidev_remove, -- cgit v0.10.2 From 4bd00413cde851d84b297c1b0dae15109025e84b Mon Sep 17 00:00:00 2001 From: Franklin S Cooper Jr Date: Mon, 27 Jun 2016 09:54:08 -0500 Subject: spi: omap2-mcspi: Add comments for RX only DMA buffer workaround OMAP35x and OMAP37x mentions in the McSPI End-of-Transfer Sequences section that if the McSPI is configured as a Master and only DMA RX is being performed then the DMA transfer size needs to be reduced by 1 or 2. This was originally implemented by: commit 57c5c28dbc83 ("spi: omap2_mcspi rxdma bugfix") This patch adds comments to clarify what is going on in the code since its not obvious what problem its addressing. Signed-off-by: Franklin S Cooper Jr Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 1d237e9..c47f958 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -459,6 +459,11 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, count = xfer->len; dma_count = xfer->len; + /* + * In the "End-of-Transfer Procedure" section for DMA RX in OMAP35x TRM + * it mentions reducing DMA transfer length by one element in master + * normal mode. + */ if (mcspi->fifo_depth == 0) dma_count -= es; @@ -478,6 +483,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); + /* + * Reduce DMA transfer length by one more if McSPI is + * configured in turbo mode. + */ if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0) dma_count -= es; @@ -507,6 +516,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, if (mcspi->fifo_depth > 0) return count; + /* + * Due to the DMA transfer length reduction the missing bytes must + * be read manually to receive all of the expected data. + */ omap2_mcspi_set_enable(spi, 0); elements = element_count - 1; -- cgit v0.10.2 From d9aaf1dc984a448ae5630e53a611a8b3489d1f8d Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Thu, 7 Jul 2016 16:23:57 +0900 Subject: spi: s3c64xx: do not disable the clock while configuring the spi When the clock is coming from the cmu it is not required to be disabled and then re-enabled in order to change the rate. Besides, some exynos chipsets (e.g. exynos5433) do not deliver any to the SFR if one from the pclk ("spi" in this case) or sclk ("busclk") is disabled. Remove the clock disabling/enabling to avoid falling into this situation. Signed-off-by: Sylwester Nawrocki Signed-off-by: Andi Shyti Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 9f0119f..df88fa1 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -596,9 +596,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) u32 val; /* Disable Clock */ - if (sdd->port_conf->clk_from_cmu) { - clk_disable_unprepare(sdd->src_clk); - } else { + if (!sdd->port_conf->clk_from_cmu) { val = readl(regs + S3C64XX_SPI_CLK_CFG); val &= ~S3C64XX_SPI_ENCLK_ENABLE; writel(val, regs + S3C64XX_SPI_CLK_CFG); @@ -641,11 +639,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) writel(val, regs + S3C64XX_SPI_MODE_CFG); if (sdd->port_conf->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_prepare_enable(sdd->src_clk); } else { /* Configure Clock */ val = readl(regs + S3C64XX_SPI_CLK_CFG); -- cgit v0.10.2 From 6acaadc852f10b9f13b665a5798d4f2783317f80 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 6 Jul 2016 16:53:12 +0200 Subject: spi: clps711x: Driver refactor This is a complex patch for refactoring CLPS711X SPI driver. This change adds devicetree support and removes board support. Signed-off-by: Alexander Shiyan Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 8c30de0..18193df 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -1,7 +1,7 @@ /* * CLPS711X SPI bus driver * - * Copyright (C) 2012-2014 Alexander Shiyan + * Copyright (C) 2012-2016 Alexander Shiyan * * 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 @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -20,9 +19,8 @@ #include #include #include -#include -#define DRIVER_NAME "spi-clps711x" +#define DRIVER_NAME "clps711x-spi" #define SYNCIO_FRMLEN(x) ((x) << 8) #define SYNCIO_TXFRMEN (1 << 14) @@ -40,6 +38,17 @@ struct spi_clps711x_data { static int spi_clps711x_setup(struct spi_device *spi) { + if (!spi->controller_state) { + int ret; + + ret = devm_gpio_request(&spi->master->dev, spi->cs_gpio, + dev_name(&spi->master->dev)); + if (ret) + return ret; + + spi->controller_state = spi; + } + /* We are expect that SPI-device is not selected */ gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); @@ -104,20 +113,9 @@ static irqreturn_t spi_clps711x_isr(int irq, void *dev_id) static int spi_clps711x_probe(struct platform_device *pdev) { struct spi_clps711x_data *hw; - struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev); struct spi_master *master; struct resource *res; - int i, irq, ret; - - if (!pdata) { - dev_err(&pdev->dev, "No platform data supplied\n"); - return -EINVAL; - } - - if (pdata->num_chipselect < 1) { - dev_err(&pdev->dev, "At least one CS must be defined\n"); - return -EINVAL; - } + int irq, ret; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -127,40 +125,24 @@ static int spi_clps711x_probe(struct platform_device *pdev) if (!master) return -ENOMEM; - master->cs_gpios = devm_kzalloc(&pdev->dev, sizeof(int) * - pdata->num_chipselect, GFP_KERNEL); - if (!master->cs_gpios) { - ret = -ENOMEM; - goto err_out; - } - - master->bus_num = pdev->id; + master->bus_num = -1; master->mode_bits = SPI_CPHA | SPI_CS_HIGH; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 8); - master->num_chipselect = pdata->num_chipselect; + master->dev.of_node = pdev->dev.of_node; master->setup = spi_clps711x_setup; master->prepare_message = spi_clps711x_prepare_message; master->transfer_one = spi_clps711x_transfer_one; hw = spi_master_get_devdata(master); - for (i = 0; i < master->num_chipselect; i++) { - master->cs_gpios[i] = pdata->chipselect[i]; - ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i], - DRIVER_NAME); - if (ret) { - dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i); - goto err_out; - } - } - hw->spi_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(hw->spi_clk)) { ret = PTR_ERR(hw->spi_clk); goto err_out; } - hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3"); + hw->syscon = + syscon_regmap_lookup_by_compatible("cirrus,ep7209-syscon3"); if (IS_ERR(hw->syscon)) { ret = PTR_ERR(hw->syscon); goto err_out; @@ -185,14 +167,8 @@ static int spi_clps711x_probe(struct platform_device *pdev) goto err_out; ret = devm_spi_register_master(&pdev->dev, master); - if (!ret) { - dev_info(&pdev->dev, - "SPI bus driver initialized. Master clock %u Hz\n", - master->max_speed_hz); + if (!ret) return 0; - } - - dev_err(&pdev->dev, "Failed to register master\n"); err_out: spi_master_put(master); @@ -200,9 +176,16 @@ err_out: return ret; } +static const struct of_device_id clps711x_spi_dt_ids[] = { + { .compatible = "cirrus,ep7209-spi", }, + { } +}; +MODULE_DEVICE_TABLE(of, clps711x_spi_dt_ids); + static struct platform_driver clps711x_spi_driver = { .driver = { .name = DRIVER_NAME, + .of_match_table = clps711x_spi_dt_ids, }, .probe = spi_clps711x_probe, }; -- cgit v0.10.2 From ea2ff61ba3f8239483f5cd823ac0113d194b469f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 6 Jul 2016 16:53:13 +0200 Subject: spi: add binding for clps711x SPI This documents the binding used by Alexander Shiyan's DT support for the clps711x SPI controller. I've left the file name to match the ARM platform port name "clps711x" for consistency with the other bindings, even though the compatible string refers to the later ep7309 chip. Linux no longer supports the old clps711x and ep72xx product lines, but we still use the name. The entire family is now discontinued by the manufacturer. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-clps711x.txt b/Documentation/devicetree/bindings/spi/spi-clps711x.txt new file mode 100644 index 0000000..4c3ec13 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-clps711x.txt @@ -0,0 +1,33 @@ +Serial Peripheral Interface on Cirrus Logic CL-PS71xx, EP72xx, EP73xx + +Required properties +- #address-cells: must be <1> +- #size-cells: must be <0> +- compatible: should include "cirrus,ep7209-spi" +- reg: Address and length of one register range +- interrupts: one interrupt line +- clocks: One entry, refers to the SPI bus clock +- cs-gpios: Specifies the gpio pins to be used for chipselects. + See: Documentation/devicetree/bindings/spi/spi-bus.txt + +An additional register is present in the system controller, +which is assumed to be in the same device tree, with and marked +as compatible with "cirrus,ep7209-syscon3". + +Example: + +spi@80000500 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "cirrus,ep7209-spi"; + reg = <0x80000500 0x4>; + interrupts = <15>; + clocks = <&clks CLPS711X_CLK_SPI>; + status = "disabled"; +}; + +syscon3: syscon@80002200 { + compatible = "cirrus,ep7209-syscon3", "syscon"; + reg = <0x80002200 0x40>; +}; + -- cgit v0.10.2 From 152bc19e2fc2b7fce7ffbc2a9cea94b147223702 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 Jul 2016 12:08:11 +0300 Subject: spi: pxa2xx: Clear all RFT bits in reset_sccr1() on Intel Quark It seems the commit e5262d0568dc ("spi: spi-pxa2xx: SPI support for Intel Quark X1000") misses one place to be adapted for Intel Quark, i.e. in reset_sccr1(). Clear all RFT bits when call reset_sccr1() on Intel Quark. Fixes: e5262d0568dc ("spi: spi-pxa2xx: SPI support for Intel Quark X1000") Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index fe07c05..daf2844 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -585,7 +585,14 @@ static void reset_sccr1(struct driver_data *drv_data) u32 sccr1_reg; sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1; - sccr1_reg &= ~SSCR1_RFT; + switch (drv_data->ssp_type) { + case QUARK_X1000_SSP: + sccr1_reg &= ~QUARK_X1000_SSCR1_RFT; + break; + default: + sccr1_reg &= ~SSCR1_RFT; + break; + } sccr1_reg |= chip->threshold; pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg); } -- cgit v0.10.2 From ca80ef718b12eebe247b776d4b97a5e571bb489d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 5 Jul 2016 23:12:05 +0300 Subject: spi: pxa2xx-pci: Support both chipselects on Braswell The commit 30f3a6ab44d8 ("spi: pxa2xx: Add support for both chip selects on Intel Braswell") introduces a support of chipselects for Intel Braswell SPI host controller. Though it missed to convert the PCI part of the driver. Do conversion here which enables both chipselects on Intel Braswell when enumerated via PCI. We don't care about num_chipselect value since it is overrided inside core driver. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 5953edc..f3df522 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -127,21 +127,21 @@ static struct pxa_spi_info spi_info_configs[] = { .rx_param = &byt_rx_param, }, [PORT_BSW0] = { - .type = LPSS_BYT_SSP, + .type = LPSS_BSW_SSP, .port_id = 0, .setup = lpss_spi_setup, .tx_param = &bsw0_tx_param, .rx_param = &bsw0_rx_param, }, [PORT_BSW1] = { - .type = LPSS_BYT_SSP, + .type = LPSS_BSW_SSP, .port_id = 1, .setup = lpss_spi_setup, .tx_param = &bsw1_tx_param, .rx_param = &bsw1_rx_param, }, [PORT_BSW2] = { - .type = LPSS_BYT_SSP, + .type = LPSS_BSW_SSP, .port_id = 2, .setup = lpss_spi_setup, .tx_param = &bsw2_tx_param, -- cgit v0.10.2 From 2b32e987c48c65a1a40b3b4294435f761e063b6b Mon Sep 17 00:00:00 2001 From: Franklin S Cooper Jr Date: Thu, 7 Jul 2016 12:17:49 -0500 Subject: spi: omap2-mcspi: Select SPI_SPLIT The function sg_split will be used by spi-omap2-mcspi to handle a SoC workaround in the SPI driver. Therefore, select SG_SPLIT so this function is available to the driver. Signed-off-by: Franklin S Cooper Jr Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4b931ec..d6fb8d4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -411,6 +411,7 @@ config SPI_OMAP24XX tristate "McSPI driver for OMAP" depends on HAS_DMA depends on ARCH_OMAP2PLUS || COMPILE_TEST + select SG_SPLIT help SPI master controller for OMAP24XX and later Multichannel SPI (McSPI) modules. -- cgit v0.10.2 From 0ba1870f886501beca0e2c19ec367a85ae201ea8 Mon Sep 17 00:00:00 2001 From: Franklin S Cooper Jr Date: Thu, 7 Jul 2016 12:17:50 -0500 Subject: spi: omap2-mcspi: Use the SPI framework to handle DMA mapping Currently, the driver handles mapping buffers to be used by the DMA. However, there are times that the current mapping implementation will fail for certain buffers. Fortunately, the SPI framework can detect and map buffers so its usable by the DMA. Update the driver to utilize the SPI framework for buffer mapping instead. Also incorporate hooks that the framework uses to determine if the DMA can or can not be used. This will result in the original omap2_mcspi_transfer_one function being deleted and omap2_mcspi_work_one being renamed to omap2_mcspi_transfer_one. Previously transfer_one was only responsible for mapping and work_one handled the transfer. But now only transferring needs to be handled by the driver. Signed-off-by: Franklin S Cooper Jr Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index c47f958..d5157b2 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -419,16 +419,13 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; - struct scatterlist sg; dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); - sg_init_table(&sg, 1); - sg_dma_address(&sg) = xfer->tx_dma; - sg_dma_len(&sg) = xfer->len; - - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1, - DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_tx_callback; tx->callback_param = spi; @@ -449,7 +446,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - unsigned int count, dma_count; + unsigned int count, transfer_reduction = 0; + struct scatterlist *sg_out[2]; + int nb_sizes = 0, out_mapped_nents[2], ret, x; + size_t sizes[2]; u32 l; int elements = 0; int word_len, element_count; @@ -457,7 +457,6 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; count = xfer->len; - dma_count = xfer->len; /* * In the "End-of-Transfer Procedure" section for DMA RX in OMAP35x TRM @@ -465,7 +464,7 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, * normal mode. */ if (mcspi->fifo_depth == 0) - dma_count -= es; + transfer_reduction = es; word_len = cs->word_len; l = mcspi_cached_chconf0(spi); @@ -479,7 +478,6 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, if (mcspi_dma->dma_rx) { struct dma_async_tx_descriptor *tx; - struct scatterlist sg; dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); @@ -488,15 +486,38 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, * configured in turbo mode. */ if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0) - dma_count -= es; + transfer_reduction += es; - sg_init_table(&sg, 1); - sg_dma_address(&sg) = xfer->rx_dma; - sg_dma_len(&sg) = dma_count; + if (transfer_reduction) { + /* Split sgl into two. The second sgl won't be used. */ + sizes[0] = count - transfer_reduction; + sizes[1] = transfer_reduction; + nb_sizes = 2; + } else { + /* + * Don't bother splitting the sgl. This essentially + * clones the original sgl. + */ + sizes[0] = count; + nb_sizes = 1; + } + + ret = sg_split(xfer->rx_sg.sgl, xfer->rx_sg.nents, + 0, nb_sizes, + sizes, + sg_out, out_mapped_nents, + GFP_KERNEL); - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | - DMA_CTRL_ACK); + if (ret < 0) { + dev_err(&spi->dev, "sg_split failed\n"); + return 0; + } + + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, + sg_out[0], + out_mapped_nents[0], + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_rx_callback; tx->callback_param = spi; @@ -510,8 +531,9 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, omap2_mcspi_set_dma_req(spi, 1, 1); wait_for_completion(&mcspi_dma->dma_rx_completion); - dma_unmap_single(mcspi->dev, xfer->rx_dma, count, - DMA_FROM_DEVICE); + + for (x = 0; x < nb_sizes; x++) + kfree(sg_out[x]); if (mcspi->fifo_depth > 0) return count; @@ -628,8 +650,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) if (tx != NULL) { wait_for_completion(&mcspi_dma->dma_tx_completion); - dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len, - DMA_TO_DEVICE); if (mcspi->fifo_depth > 0) { irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; @@ -1087,8 +1107,9 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) gpio_free(spi->cs_gpio); } -static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, - struct spi_device *spi, struct spi_transfer *t) +static int omap2_mcspi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) { /* We only enable one channel at a time -- the one whose message is @@ -1098,7 +1119,7 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, * chipselect with the FORCE bit ... CS != channel enable. */ - struct spi_master *master; + struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_cs *cs; struct omap2_mcspi_device_config *cd; @@ -1106,7 +1127,7 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, int status = 0; u32 chconf; - master = spi->master; + mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; cs = spi->controller_state; cd = spi->controller_data; @@ -1166,7 +1187,8 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, unsigned count; if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (t->len >= DMA_MIN_BYTES)) + master->cur_msg_mapped && + master->can_dma(master, spi, t)) omap2_mcspi_set_fifo(spi, t, 1); omap2_mcspi_set_enable(spi, 1); @@ -1177,7 +1199,8 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, + OMAP2_MCSPI_TX0); if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (t->len >= DMA_MIN_BYTES)) + master->cur_msg_mapped && + master->can_dma(master, spi, t)) count = omap2_mcspi_txrx_dma(spi, t); else count = omap2_mcspi_txrx_pio(spi, t); @@ -1246,55 +1269,11 @@ static int omap2_mcspi_prepare_message(struct spi_master *master, return 0; } -static int omap2_mcspi_transfer_one(struct spi_master *master, - struct spi_device *spi, struct spi_transfer *t) +static bool omap2_mcspi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) { - struct omap2_mcspi *mcspi; - struct omap2_mcspi_dma *mcspi_dma; - const void *tx_buf = t->tx_buf; - void *rx_buf = t->rx_buf; - unsigned len = t->len; - - mcspi = spi_master_get_devdata(master); - mcspi_dma = mcspi->dma_channels + spi->chip_select; - - if ((len && !(rx_buf || tx_buf))) { - dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", - t->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - t->bits_per_word); - return -EINVAL; - } - - if (len < DMA_MIN_BYTES) - goto skip_dma_map; - - if (mcspi_dma->dma_tx && tx_buf != NULL) { - t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, - len, DMA_TO_DEVICE); - if (dma_mapping_error(mcspi->dev, t->tx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'T', len); - return -EINVAL; - } - } - if (mcspi_dma->dma_rx && rx_buf != NULL) { - t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(mcspi->dev, t->rx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'R', len); - if (tx_buf != NULL) - dma_unmap_single(mcspi->dev, t->tx_dma, - len, DMA_TO_DEVICE); - return -EINVAL; - } - } - -skip_dma_map: - return omap2_mcspi_work_one(mcspi, spi, t); + return (xfer->len >= DMA_MIN_BYTES); } static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) @@ -1374,6 +1353,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->setup = omap2_mcspi_setup; master->auto_runtime_pm = true; master->prepare_message = omap2_mcspi_prepare_message; + master->can_dma = omap2_mcspi_can_dma; master->transfer_one = omap2_mcspi_transfer_one; master->set_cs = omap2_mcspi_set_cs; master->cleanup = omap2_mcspi_cleanup; -- cgit v0.10.2 From 0d35773979b9c0925edfefeaac173bbeafc83d94 Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sun, 10 Jul 2016 19:42:13 +0530 Subject: spi: spi-topcliff-pch: Remove deprecated create_singlethread_workqueue The workqueue "wk" serves as a queue for carrying out execution of requests. It has a single work item(&drv_data->work) and hence doesn't require ordering. Also, it is not being used on a memory reclaim path. Hence, the singlethreaded workqueue has been replaced with the use of system_wq. System workqueues have been able to handle high level of concurrency for a long time now and hence it's not required to have a singlethreaded workqueue just to gain concurrency. Unlike a dedicated per-cpu workqueue created with create_singlethread_workqueue(), system_wq allows multiple work items to overlap executions even on the same CPU; however, a per-cpu workqueue doesn't have any CPU locality or global ordering guarantee unless the target CPU is explicitly specified and thus the increase of local concurrency shouldn't make any difference. Work item has been flushed in pch_spi_free_resources() to ensure that there are no pending tasks while disconnecting the driver. Also dropped the label 'err_return' since it's not being used anymore. Signed-off-by: Bhaktipriya Shridhar Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 93dfcee..c54ee66 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -133,8 +133,6 @@ struct pch_spi_dma_ctrl { * @io_remap_addr: The remapped PCI base address * @master: Pointer to the SPI master structure * @work: Reference to work queue handler - * @wk: Workqueue for carrying out execution of the - * requests * @wait: Wait queue for waking up upon receiving an * interrupt. * @transfer_complete: Status of SPI Transfer @@ -169,7 +167,6 @@ struct pch_spi_data { unsigned long io_base_addr; struct spi_master *master; struct work_struct work; - struct workqueue_struct *wk; wait_queue_head_t wait; u8 transfer_complete; u8 bcurrent_msg_processing; @@ -517,8 +514,7 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__); - /* schedule work queue to run */ - queue_work(data->wk, &data->work); + schedule_work(&data->work); dev_dbg(&pspi->dev, "%s - Invoked queue work\n", __func__); retval = 0; @@ -674,7 +670,7 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data) *more messages) */ dev_dbg(&data->master->dev, "%s:Invoke queue_work\n", __func__); - queue_work(data->wk, &data->work); + schedule_work(&data->work); } else if (data->board_dat->suspend_sts || data->status == STATUS_EXITING) { dev_dbg(&data->master->dev, @@ -1266,14 +1262,7 @@ static void pch_spi_free_resources(struct pch_spi_board_data *board_dat, { dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); - /* free workqueue */ - if (data->wk != NULL) { - destroy_workqueue(data->wk); - data->wk = NULL; - dev_dbg(&board_dat->pdev->dev, - "%s destroy_workqueue invoked successfully\n", - __func__); - } + flush_work(&data->work); } static int pch_spi_get_resources(struct pch_spi_board_data *board_dat, @@ -1283,14 +1272,6 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat, dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); - /* create workqueue */ - data->wk = create_singlethread_workqueue(KBUILD_MODNAME); - if (!data->wk) { - dev_err(&board_dat->pdev->dev, - "%s create_singlet hread_workqueue failed\n", __func__); - retval = -EBUSY; - goto err_return; - } /* reset PCH SPI h/w */ pch_spi_reset(data->master); @@ -1299,7 +1280,6 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat, dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__); -err_return: if (retval != 0) { dev_err(&board_dat->pdev->dev, "%s FAIL:invoking pch_spi_free_resources\n", __func__); -- cgit v0.10.2 From 38e099208c80b2617015385fe821b68a64757008 Mon Sep 17 00:00:00 2001 From: Bhaktipriya Shridhar Date: Sun, 10 Jul 2016 19:55:19 +0530 Subject: spi: spi-sh: Remove deprecated create_singlethread_workqueue The workqueue has a single workitem(&ss->ws) and hence doesn't require ordering. Also, it is not being used on a memory reclaim path. Hence, the singlethreaded workqueue has been replaced with the use of system_wq. System workqueues have been able to handle high level of concurrency for a long time now and hence it's not required to have a singlethreaded workqueue just to gain concurrency. Unlike a dedicated per-cpu workqueue created with create_singlethread_workqueue(), system_wq allows multiple work items to overlap executions even on the same CPU; however, a per-cpu workqueue doesn't have any CPU locality or global ordering guarantee unless the target CPU is explicitly specified and thus the increase of local concurrency shouldn't make any difference. Work item has been flushed in spi_sh_remove() to ensure that there are no pending tasks while disconnecting the driver. Signed-off-by: Bhaktipriya Shridhar Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index 5025011..2bf53f0 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -82,7 +82,6 @@ struct spi_sh_data { int irq; struct spi_master *master; struct list_head queue; - struct workqueue_struct *workqueue; struct work_struct ws; unsigned long cr1; wait_queue_head_t wait; @@ -380,7 +379,7 @@ static int spi_sh_transfer(struct spi_device *spi, struct spi_message *mesg) spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1); list_add_tail(&mesg->queue, &ss->queue); - queue_work(ss->workqueue, &ss->ws); + schedule_work(&ss->ws); spin_unlock_irqrestore(&ss->lock, flags); @@ -425,7 +424,7 @@ static int spi_sh_remove(struct platform_device *pdev) struct spi_sh_data *ss = platform_get_drvdata(pdev); spi_unregister_master(ss->master); - destroy_workqueue(ss->workqueue); + flush_work(&ss->ws); free_irq(ss->irq, ss); return 0; @@ -484,18 +483,11 @@ static int spi_sh_probe(struct platform_device *pdev) spin_lock_init(&ss->lock); INIT_WORK(&ss->ws, spi_sh_work); init_waitqueue_head(&ss->wait); - ss->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (ss->workqueue == NULL) { - dev_err(&pdev->dev, "create workqueue error\n"); - ret = -EBUSY; - goto error1; - } ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss); if (ret < 0) { dev_err(&pdev->dev, "request_irq error\n"); - goto error2; + goto error1; } master->num_chipselect = 2; @@ -514,8 +506,6 @@ static int spi_sh_probe(struct platform_device *pdev) error3: free_irq(irq, ss); - error2: - destroy_workqueue(ss->workqueue); error1: spi_master_put(master); -- cgit v0.10.2 From 9f135787b1d29b0069059b580c1c77965e5e8af4 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 12 Jul 2016 11:08:42 +0000 Subject: spi: s3c64xx: fix reference leak to master in s3c64xx_spi_remove() Once a spi_master_get() call succeeds, we need an additional spi_master_put() call to free the memory, otherwise we will leak a reference to master. Fix by removing the unnecessary spi_master_get() call. Signed-off-by: Wei Yongjun Reviewed-by: Andi Shyti Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index df88fa1..001c9eb 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1211,7 +1211,7 @@ err0: static int s3c64xx_spi_remove(struct platform_device *pdev) { - struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); + struct spi_master *master = platform_get_drvdata(pdev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); pm_runtime_get_sync(&pdev->dev); -- cgit v0.10.2 From c068042bf0872c9121f4a6eab281dd63f61df5d3 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 12 Jul 2016 19:02:10 +0900 Subject: spi: s3c64xx: add exynos5433 spi compatible The samsung,exynos5433-spi has some peculiarities that bring the need of creating a new compatible in the binding. One of those is the 3-clocks controller management where the spi is fed with three clocks: "spi", "busclkN" and "ioclk". By adding the exynos5433-spi, we deprecate the exynos7 compatible and discourage its use. Signed-off-by: Andi Shyti Reviewed-by: Michael Turquette Reviewed-by: Krzysztof Kozlowski Reviewed-by: Sylwester Nawrocki Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-samsung.txt b/Documentation/devicetree/bindings/spi/spi-samsung.txt index 930bc73..ab4f0a4 100644 --- a/Documentation/devicetree/bindings/spi/spi-samsung.txt +++ b/Documentation/devicetree/bindings/spi/spi-samsung.txt @@ -9,7 +9,8 @@ Required SoC Specific Properties: - samsung,s3c2443-spi: for s3c2443, s3c2416 and s3c2450 platforms - samsung,s3c6410-spi: for s3c6410 platforms - samsung,s5pv210-spi: for s5pv210 and s5pc110 platforms - - samsung,exynos7-spi: for exynos7 platforms + - samsung,exynos5433-spi: for exynos5433 compatible controllers + - samsung,exynos7-spi: for exynos7 platforms - reg: physical base address of the controller and length of memory mapped region. -- cgit v0.10.2 From 1ada32ede94fc91ff0f3236fcc70ae74dd0c2cb0 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 12 Jul 2016 19:02:11 +0900 Subject: spi: s3c64xx: document the clocks and the clock-name property These two properties were not documented but used in the spi dts. Add the related documentation. Suggested-by: Krzysztof Kozlowski Signed-off-by: Andi Shyti Reviewed-by: Michael Turquette Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-samsung.txt b/Documentation/devicetree/bindings/spi/spi-samsung.txt index ab4f0a4..49028a4 100644 --- a/Documentation/devicetree/bindings/spi/spi-samsung.txt +++ b/Documentation/devicetree/bindings/spi/spi-samsung.txt @@ -24,6 +24,15 @@ Required SoC Specific Properties: - dma-names: Names for the dma channels. There must be at least one channel named "tx" for transmit and named "rx" for receive. +- clocks: specifies the clock IDs provided to the SPI controller; they are + required for interacting with the controller itself, for synchronizing the bus + and as I/O clock (the latter is required by exynos5433 and exynos7). + +- clock-names: string names of the clocks in the 'clocks' property; for all the + the devices the names must be "spi", "spi_busclkN" (where N is determined by + "samsung,spi-src-clk"), while Exynos5433 should specify a third clock + "spi_ioclk" for the I/O clock. + Required Board Specific Properties: - #address-cells: should be 1. -- cgit v0.10.2 From 60a9a964420912a4c4b66efd210d98006177695a Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 12 Jul 2016 19:02:12 +0900 Subject: spi: s3c64xx: rename goto labels to meaningful names The goto labels of the style of err4: err3: err2: err1: are complex to insert in between new errors without renaming all the goto statements. Replace the errX naming style to meaningful names in order to make it easier to insert new goto exit points. Signed-off-by: Andi Shyti Reviewed-by: Krzysztof Kozlowski Reviewed-by: Michael Turquette Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 001c9eb..0a93eb9 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1078,7 +1078,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); - goto err0; + goto err_deref_master; } sdd->port_id = ret; } else { @@ -1116,13 +1116,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(sdd->regs)) { ret = PTR_ERR(sdd->regs); - goto err0; + goto err_deref_master; } if (sci->cfg_gpio && sci->cfg_gpio()) { dev_err(&pdev->dev, "Unable to config gpio\n"); ret = -EBUSY; - goto err0; + goto err_deref_master; } /* Setup clocks */ @@ -1130,13 +1130,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) if (IS_ERR(sdd->clk)) { dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n"); ret = PTR_ERR(sdd->clk); - goto err0; + goto err_deref_master; } if (clk_prepare_enable(sdd->clk)) { dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n"); ret = -EBUSY; - goto err0; + goto err_deref_master; } sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr); @@ -1145,13 +1145,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to acquire clock '%s'\n", clk_name); ret = PTR_ERR(sdd->src_clk); - goto err2; + goto err_disable_clk; } if (clk_prepare_enable(sdd->src_clk)) { dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name); ret = -EBUSY; - goto err2; + goto err_disable_clk; } pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); @@ -1171,7 +1171,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret); - goto err3; + goto err_pm_put; } writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | @@ -1181,7 +1181,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) ret = devm_spi_register_master(&pdev->dev, master); if (ret != 0) { dev_err(&pdev->dev, "cannot register SPI master: %d\n", ret); - goto err3; + goto err_pm_put; } dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", @@ -1195,15 +1195,15 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) return 0; -err3: +err_pm_put: pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); clk_disable_unprepare(sdd->src_clk); -err2: +err_disable_clk: clk_disable_unprepare(sdd->clk); -err0: +err_deref_master: spi_master_put(master); return ret; -- cgit v0.10.2 From 25981d8281ee4cbbd6f7c5bc0f8c048ad9548037 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 12 Jul 2016 19:02:13 +0900 Subject: spi: s3c64xx: use error code from clk_prepare_enable() If clk_prepare_enable() fails do not return -EBUSY but use the value provided by the function itself. Suggested-by: Krzysztof Kozlowski Signed-off-by: Andi Shyti Reviewed-by: Michael Turquette Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 0a93eb9..3d21f8a 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1133,9 +1133,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) goto err_deref_master; } - if (clk_prepare_enable(sdd->clk)) { + ret = clk_prepare_enable(sdd->clk); + if (ret) { dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n"); - ret = -EBUSY; goto err_deref_master; } @@ -1148,9 +1148,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) goto err_disable_clk; } - if (clk_prepare_enable(sdd->src_clk)) { + ret = clk_prepare_enable(sdd->src_clk); + if (ret) { dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name); - ret = -EBUSY; goto err_disable_clk; } -- cgit v0.10.2 From 7990b00819e765ca180d36b38969c2bde2734e53 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 12 Jul 2016 19:02:14 +0900 Subject: spi: s3c64xx: add Exynos5433 compatible for ioclk handling The new compatible is related to the Samsung Exynos5433 SoC. The difference between the previous is that in the exynos5433 the SPI controller is driven by three clocks instead of only one. The new clock (ioclk) is controlling the input/output clock whenever the controller is slave or master. The presence of the clock line is detected from the compatibility structure (exynos5433_spi_port_config) as a boolean value. The probe function checks whether the ioclk is present and if so, it acquires. The runtime suspend and resume functions will handle the clock enabling and disabling as well. Signed-off-by: Andi Shyti Reviewed-by: Michael Turquette Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 3d21f8a..a1e8468 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -156,12 +156,14 @@ struct s3c64xx_spi_port_config { int quirks; bool high_speed; bool clk_from_cmu; + bool clk_ioclk; }; /** * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver. * @clk: Pointer to the spi clock. * @src_clk: Pointer to the clock used to generate SPI signals. + * @ioclk: Pointer to the i/o clock between master and slave * @master: Pointer to the SPI Protocol master. * @cntrlr_info: Platform specific data for the controller this driver manages. * @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint. @@ -181,6 +183,7 @@ struct s3c64xx_spi_driver_data { void __iomem *regs; struct clk *clk; struct clk *src_clk; + struct clk *ioclk; struct platform_device *pdev; struct spi_master *master; struct s3c64xx_spi_info *cntrlr_info; @@ -1154,6 +1157,21 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) goto err_disable_clk; } + if (sdd->port_conf->clk_ioclk) { + sdd->ioclk = devm_clk_get(&pdev->dev, "spi_ioclk"); + if (IS_ERR(sdd->ioclk)) { + dev_err(&pdev->dev, "Unable to acquire 'ioclk'\n"); + ret = PTR_ERR(sdd->ioclk); + goto err_disable_src_clk; + } + + ret = clk_prepare_enable(sdd->ioclk); + if (ret) { + dev_err(&pdev->dev, "Couldn't enable clock 'ioclk'\n"); + goto err_disable_src_clk; + } + } + pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); @@ -1200,6 +1218,8 @@ err_pm_put: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + clk_disable_unprepare(sdd->ioclk); +err_disable_src_clk: clk_disable_unprepare(sdd->src_clk); err_disable_clk: clk_disable_unprepare(sdd->clk); @@ -1218,6 +1238,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) writel(0, sdd->regs + S3C64XX_SPI_INT_EN); + clk_disable_unprepare(sdd->ioclk); + clk_disable_unprepare(sdd->src_clk); clk_disable_unprepare(sdd->clk); @@ -1276,6 +1298,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev) clk_disable_unprepare(sdd->clk); clk_disable_unprepare(sdd->src_clk); + clk_disable_unprepare(sdd->ioclk); return 0; } @@ -1286,17 +1309,28 @@ static int s3c64xx_spi_runtime_resume(struct device *dev) struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); int ret; + if (sdd->port_conf->clk_ioclk) { + ret = clk_prepare_enable(sdd->ioclk); + if (ret != 0) + return ret; + } + ret = clk_prepare_enable(sdd->src_clk); if (ret != 0) - return ret; + goto err_disable_ioclk; ret = clk_prepare_enable(sdd->clk); - if (ret != 0) { - clk_disable_unprepare(sdd->src_clk); - return ret; - } + if (ret != 0) + goto err_disable_src_clk; return 0; + +err_disable_src_clk: + clk_disable_unprepare(sdd->src_clk); +err_disable_ioclk: + clk_disable_unprepare(sdd->ioclk); + + return ret; } #endif /* CONFIG_PM */ @@ -1352,6 +1386,16 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = { .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, }; +static struct s3c64xx_spi_port_config exynos5433_spi_port_config = { + .fifo_lvl_mask = { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff}, + .rx_lvl_offset = 15, + .tx_st_done = 25, + .high_speed = true, + .clk_from_cmu = true, + .clk_ioclk = true, + .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, +}; + static const struct platform_device_id s3c64xx_spi_driver_ids[] = { { .name = "s3c2443-spi", @@ -1382,6 +1426,9 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = { { .compatible = "samsung,exynos7-spi", .data = (void *)&exynos7_spi_port_config, }, + { .compatible = "samsung,exynos5433-spi", + .data = (void *)&exynos5433_spi_port_config, + }, { }, }; MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match); -- cgit v0.10.2 From 0dbe70a1feb411d17fbcf8e73d61e6c7f33fda72 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 12 Jul 2016 19:02:15 +0900 Subject: spi: s3c64xx: restore removed comments Patch a9e93e8 has erroneously removed some comments which are important to understand why the bus frequency is multiplied by two during the spi transfer. Reword the previous comment to a more appropriate message. Suggested-by: Sylwester Nawrocki Signed-off-by: Andi Shyti Reviewed-by: Michael Turquette Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index a1e8468..3c09e94 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -642,6 +642,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) writel(val, regs + S3C64XX_SPI_MODE_CFG); if (sdd->port_conf->clk_from_cmu) { + /* The src_clk clock is divided internally by 2 */ clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); } else { /* Configure Clock */ -- cgit v0.10.2 From 4db9bf548bb00d84ba7dfd70f124de16386c0c55 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 15 Jul 2016 11:04:18 +0200 Subject: spi: xilinx: Handle errors from platform_get_irq() The Xilinx SPI driver can operate without an IRQ, but not every error returned by platform_get_irq() means that no IRQ was specified. It will also return an error if the IRQ specification is invalid or the IRQ provider is not yet available (EPROBE_DEFER). So instead of ignoring all errors only ignore ENXIO, which means no IRQ was specified, and propagate all other errors to device driver core. Signed-off-by: Lars-Peter Clausen Acked-by: Ricardo Ribalda Delgado Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 3009121..e62eb9a 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -455,7 +455,10 @@ static int xilinx_spi_probe(struct platform_device *pdev) xspi->buffer_size = xilinx_spi_find_buffer_size(xspi); xspi->irq = platform_get_irq(pdev, 0); - if (xspi->irq >= 0) { + if (xspi->irq < 0 && xspi->irq != -ENXIO) { + ret = xspi->irq; + goto put_master; + } else if (xspi->irq >= 0) { /* Register for SPI Interrupt */ ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0, dev_name(&pdev->dev), xspi); -- cgit v0.10.2 From d33648478501407267557b1bf6897169625f4178 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 15 Jul 2016 11:04:19 +0200 Subject: spi: xilinx: Return IRQ_NONE if no interrupts were detected Return IRQ_NONE from the interrupt handler if the handler is running, but no interrupt was detected. This allows the system to recover in case of an interrupt storm due to an invalid interrupt configuration or faulty hardware. Signed-off-by: Lars-Peter Clausen Acked-by: Ricardo Ribalda Delgado Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index e62eb9a..bc7100b 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -341,9 +341,10 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */ complete(&xspi->done); + return IRQ_HANDLED; } - return IRQ_HANDLED; + return IRQ_NONE; } static int xilinx_spi_find_buffer_size(struct xilinx_spi *xspi) -- cgit v0.10.2 From 5185a81c02d4118b11e6cb7b5fbf6f15ff7aff90 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 14 Jul 2016 18:30:59 -0700 Subject: spi: rockchip: limit transfers to (64K - 1) bytes The Rockchip SPI controller's length register only supports 16-bits, yielding a maximum length of 64KiB (the CTRLR1 register holds "length - 1"). Trying to transfer more than that (e.g., with a large SPI flash read) will cause the driver to hang. Now, it seems that while theoretically we should be able to program CTRLR1 with 0xffff, and get a 64KiB transfer, but that also seems to cause the core to choke, so stick with a maximum of 64K - 1 bytes -- i.e., 0xffff. Signed-off-by: Brian Norris Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index cd89682..cf69f4d 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -142,6 +142,12 @@ /* sclk_out: spi master internal logic in rk3x can support 50Mhz */ #define MAX_SCLK_OUT 50000000 +/* + * SPI_CTRLR1 is 16-bits, so we should support lengths of 0xffff + 1. However, + * the controller seems to hang when given 0x10000, so stick with this for now. + */ +#define ROCKCHIP_SPI_MAX_TRANLEN 0xffff + enum rockchip_ssi_type { SSI_MOTO_SPI = 0, SSI_TI_SSP, @@ -573,6 +579,11 @@ static void rockchip_spi_config(struct rockchip_spi *rs) dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div); } +static size_t rockchip_spi_max_transfer_size(struct spi_device *spi) +{ + return ROCKCHIP_SPI_MAX_TRANLEN; +} + static int rockchip_spi_transfer_one( struct spi_master *master, struct spi_device *spi, @@ -589,6 +600,11 @@ static int rockchip_spi_transfer_one( return -EINVAL; } + if (xfer->len > ROCKCHIP_SPI_MAX_TRANLEN) { + dev_err(rs->dev, "Transfer is too long (%d)\n", xfer->len); + return -EINVAL; + } + rs->speed = xfer->speed_hz; rs->bpw = xfer->bits_per_word; rs->n_bytes = rs->bpw >> 3; @@ -728,6 +744,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) master->prepare_message = rockchip_spi_prepare_message; master->unprepare_message = rockchip_spi_unprepare_message; master->transfer_one = rockchip_spi_transfer_one; + master->max_transfer_size = rockchip_spi_max_transfer_size; master->handle_err = rockchip_spi_handle_err; rs->dma_tx.ch = dma_request_chan(rs->dev, "tx"); -- cgit v0.10.2 From 57c2b0ddd14d8831cbaab855373fe8797812ea53 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Sat, 23 Jul 2016 21:31:20 +0200 Subject: spi: pic32: fixup wait_for_completion_timeout return handling wait_for_completion_timeout returns unsigned long not int so the check for <= 0 should be == 0 here. Signed-off-by: Nicholas Mc Guire Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 73db87f..fefb688 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -507,6 +507,7 @@ static int pic32_spi_one_transfer(struct spi_master *master, { struct pic32_spi *pic32s; bool dma_issued = false; + unsigned long timeout; int ret; pic32s = spi_master_get_devdata(master); @@ -553,8 +554,8 @@ static int pic32_spi_one_transfer(struct spi_master *master, } /* wait for completion */ - ret = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ); - if (ret <= 0) { + timeout = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ); + if (timeout == 0) { dev_err(&spi->dev, "wait error/timedout\n"); if (dma_issued) { dmaengine_terminate_all(master->dma_rx); -- cgit v0.10.2 From 33d5097dbf650b84238c6ebdfea4a335f64f9608 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Sat, 23 Jul 2016 21:08:35 +0200 Subject: spi: pic32-sqi: fixup wait_for_completion_timeout return handling wait_for_completion_timeout returns unsigned long not int so the check for <= 0 should be == 0 here, and the type unsigned long. The function return is set to -ETIMEDOUT to reflect the actual problem. Signed-off-by: Nicholas Mc Guire Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c index ca3c8d9..c41abdd 100644 --- a/drivers/spi/spi-pic32-sqi.c +++ b/drivers/spi/spi-pic32-sqi.c @@ -354,6 +354,7 @@ static int pic32_sqi_one_message(struct spi_master *master, struct spi_transfer *xfer; struct pic32_sqi *sqi; int ret = 0, mode; + unsigned long timeout; u32 val; sqi = spi_master_get_devdata(master); @@ -419,10 +420,10 @@ static int pic32_sqi_one_message(struct spi_master *master, writel(val, sqi->regs + PESQI_BD_CTRL_REG); /* wait for xfer completion */ - ret = wait_for_completion_timeout(&sqi->xfer_done, 5 * HZ); - if (ret <= 0) { + timeout = wait_for_completion_timeout(&sqi->xfer_done, 5 * HZ); + if (timeout == 0) { dev_err(&sqi->master->dev, "wait timedout/interrupted\n"); - ret = -EIO; + ret = -ETIMEDOUT; msg->status = ret; } else { /* success */ -- cgit v0.10.2 From 187fc9b37458547e198eaf9af4b33c56999748ad Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Sat, 23 Jul 2016 12:42:50 +0200 Subject: spi: octeon: Convert driver to use readq()/writeq() functions Remove all calls to cvmx_read_csr()/cvmx_write_csr() and use the portable readq()/writeq() functions. Signed-off-by: Steven J. Hill Signed-off-by: Jan Glauber Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index 3b17009..b53ba53 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -27,7 +27,7 @@ #define OCTEON_SPI_MAX_CLOCK_HZ 16000000 struct octeon_spi { - u64 register_base; + void __iomem *register_base; u64 last_cfg; u64 cs_enax; }; @@ -40,7 +40,7 @@ static void octeon_spi_wait_ready(struct octeon_spi *p) do { if (loops++) __delay(500); - mpi_sts.u64 = cvmx_read_csr(p->register_base + OCTEON_SPI_STS); + mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS); } while (mpi_sts.s.busy); } @@ -85,7 +85,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, if (mpi_cfg.u64 != p->last_cfg) { p->last_cfg = mpi_cfg.u64; - cvmx_write_csr(p->register_base + OCTEON_SPI_CFG, mpi_cfg.u64); + writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG); } tx_buf = xfer->tx_buf; rx_buf = xfer->rx_buf; @@ -97,19 +97,19 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, d = *tx_buf++; else d = 0; - cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); + writeq(d, p->register_base + OCTEON_SPI_DAT0 + (8 * i)); } mpi_tx.u64 = 0; mpi_tx.s.csid = spi->chip_select; mpi_tx.s.leavecs = 1; mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; - cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX); octeon_spi_wait_ready(p); if (rx_buf) for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + u64 v = readq(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); *rx_buf++ = (u8)v; } len -= OCTEON_SPI_MAX_BYTES; @@ -121,7 +121,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, d = *tx_buf++; else d = 0; - cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); + writeq(d, p->register_base + OCTEON_SPI_DAT0 + (8 * i)); } mpi_tx.u64 = 0; @@ -132,12 +132,12 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, mpi_tx.s.leavecs = !xfer->cs_change; mpi_tx.s.txnum = tx_buf ? len : 0; mpi_tx.s.totnum = len; - cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX); octeon_spi_wait_ready(p); if (rx_buf) for (i = 0; i < len; i++) { - u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + u64 v = readq(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); *rx_buf++ = (u8)v; } @@ -193,7 +193,7 @@ static int octeon_spi_probe(struct platform_device *pdev) goto fail; } - p->register_base = (u64)reg_base; + p->register_base = reg_base; master->num_chipselect = 4; master->mode_bits = SPI_CPHA | @@ -225,10 +225,9 @@ static int octeon_spi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct octeon_spi *p = spi_master_get_devdata(master); - u64 register_base = p->register_base; /* Clear the CSENA* and put everything in a known state. */ - cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0); + writeq(0, p->register_base + OCTEON_SPI_CFG); return 0; } -- cgit v0.10.2 From b9e64763b68dbbf87a54b28edf89cd92c8ef51dd Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Sat, 23 Jul 2016 12:42:51 +0200 Subject: spi: octeon: Store system clock freqency in struct octeon_spi Storing the system clock frequency in struct octeon_spi avoids calling the MIPS specific octeon_get_io_clock_rate() for every transfer. Signed-off-by: Jan Glauber Tested-by: Steven J. Hill Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index b53ba53..e722040 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -30,6 +30,7 @@ struct octeon_spi { void __iomem *register_base; u64 last_cfg; u64 cs_enax; + int sys_freq; }; static void octeon_spi_wait_ready(struct octeon_spi *p) @@ -53,7 +54,6 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, union cvmx_mpi_cfg mpi_cfg; union cvmx_mpi_tx mpi_tx; unsigned int clkdiv; - unsigned int speed_hz; int mode; bool cpha, cpol; const u8 *tx_buf; @@ -65,9 +65,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, cpha = mode & SPI_CPHA; cpol = mode & SPI_CPOL; - speed_hz = xfer->speed_hz; - - clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz); + clkdiv = p->sys_freq / (2 * xfer->speed_hz); mpi_cfg.u64 = 0; @@ -194,6 +192,7 @@ static int octeon_spi_probe(struct platform_device *pdev) } p->register_base = reg_base; + p->sys_freq = octeon_get_io_clock_rate(); master->num_chipselect = 4; master->mode_bits = SPI_CPHA | -- cgit v0.10.2 From ee423c5322aece231415c316da5478e9d5241b4c Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Sat, 23 Jul 2016 12:42:52 +0200 Subject: spi: octeon: Put register offsets into a struct Instead of hard-coding the register offsets put them into a struct and set them in the probe function. Signed-off-by: Jan Glauber Tested-by: Steven J. Hill Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index e722040..209eddc 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -17,22 +17,30 @@ #include #include -#define OCTEON_SPI_CFG 0 -#define OCTEON_SPI_STS 0x08 -#define OCTEON_SPI_TX 0x10 -#define OCTEON_SPI_DAT0 0x80 - #define OCTEON_SPI_MAX_BYTES 9 #define OCTEON_SPI_MAX_CLOCK_HZ 16000000 +struct octeon_spi_regs { + int config; + int status; + int tx; + int data; +}; + struct octeon_spi { void __iomem *register_base; u64 last_cfg; u64 cs_enax; int sys_freq; + struct octeon_spi_regs regs; }; +#define OCTEON_SPI_CFG(x) (x->regs.config) +#define OCTEON_SPI_STS(x) (x->regs.status) +#define OCTEON_SPI_TX(x) (x->regs.tx) +#define OCTEON_SPI_DAT0(x) (x->regs.data) + static void octeon_spi_wait_ready(struct octeon_spi *p) { union cvmx_mpi_sts mpi_sts; @@ -41,7 +49,7 @@ static void octeon_spi_wait_ready(struct octeon_spi *p) do { if (loops++) __delay(500); - mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS); + mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p)); } while (mpi_sts.s.busy); } @@ -83,7 +91,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, if (mpi_cfg.u64 != p->last_cfg) { p->last_cfg = mpi_cfg.u64; - writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG); + writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p)); } tx_buf = xfer->tx_buf; rx_buf = xfer->rx_buf; @@ -95,19 +103,19 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, d = *tx_buf++; else d = 0; - writeq(d, p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); } mpi_tx.u64 = 0; mpi_tx.s.csid = spi->chip_select; mpi_tx.s.leavecs = 1; mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; - writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX); + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); octeon_spi_wait_ready(p); if (rx_buf) for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u64 v = readq(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); *rx_buf++ = (u8)v; } len -= OCTEON_SPI_MAX_BYTES; @@ -119,7 +127,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, d = *tx_buf++; else d = 0; - writeq(d, p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); } mpi_tx.u64 = 0; @@ -130,12 +138,12 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, mpi_tx.s.leavecs = !xfer->cs_change; mpi_tx.s.txnum = tx_buf ? len : 0; mpi_tx.s.totnum = len; - writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX); + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); octeon_spi_wait_ready(p); if (rx_buf) for (i = 0; i < len; i++) { - u64 v = readq(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); *rx_buf++ = (u8)v; } @@ -194,6 +202,11 @@ static int octeon_spi_probe(struct platform_device *pdev) p->register_base = reg_base; p->sys_freq = octeon_get_io_clock_rate(); + p->regs.config = 0; + p->regs.status = 0x08; + p->regs.tx = 0x10; + p->regs.data = 0x80; + master->num_chipselect = 4; master->mode_bits = SPI_CPHA | SPI_CPOL | @@ -226,7 +239,7 @@ static int octeon_spi_remove(struct platform_device *pdev) struct octeon_spi *p = spi_master_get_devdata(master); /* Clear the CSENA* and put everything in a known state. */ - writeq(0, p->register_base + OCTEON_SPI_CFG); + writeq(0, p->register_base + OCTEON_SPI_CFG(p)); return 0; } -- cgit v0.10.2 From 22cc1b6b35ce560ebf9252027397d5f1b92bfa9b Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Sat, 23 Jul 2016 12:42:53 +0200 Subject: spi: octeon: Move include file from arch/mips to drivers/spi Move the register definitions to the drivers directory because they are only used there. Signed-off-by: Jan Glauber Tested-by: Steven J. Hill Signed-off-by: Mark Brown diff --git a/arch/mips/include/asm/octeon/cvmx-mpi-defs.h b/arch/mips/include/asm/octeon/cvmx-mpi-defs.h deleted file mode 100644 index 4615b10..0000000 --- a/arch/mips/include/asm/octeon/cvmx-mpi-defs.h +++ /dev/null @@ -1,328 +0,0 @@ -/***********************license start*************** - * Author: Cavium Networks - * - * Contact: support@caviumnetworks.com - * This file is part of the OCTEON SDK - * - * Copyright (c) 2003-2012 Cavium Networks - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, Version 2, as - * published by the Free Software Foundation. - * - * This file is distributed in the hope that it will be useful, but - * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this file; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * or visit http://www.gnu.org/licenses/. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium Networks for more information - ***********************license end**************************************/ - -#ifndef __CVMX_MPI_DEFS_H__ -#define __CVMX_MPI_DEFS_H__ - -#define CVMX_MPI_CFG (CVMX_ADD_IO_SEG(0x0001070000001000ull)) -#define CVMX_MPI_DATX(offset) (CVMX_ADD_IO_SEG(0x0001070000001080ull) + ((offset) & 15) * 8) -#define CVMX_MPI_STS (CVMX_ADD_IO_SEG(0x0001070000001008ull)) -#define CVMX_MPI_TX (CVMX_ADD_IO_SEG(0x0001070000001010ull)) - -union cvmx_mpi_cfg { - uint64_t u64; - struct cvmx_mpi_cfg_s { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_29_63:35; - uint64_t clkdiv:13; - uint64_t csena3:1; - uint64_t csena2:1; - uint64_t csena1:1; - uint64_t csena0:1; - uint64_t cslate:1; - uint64_t tritx:1; - uint64_t idleclks:2; - uint64_t cshi:1; - uint64_t csena:1; - uint64_t int_ena:1; - uint64_t lsbfirst:1; - uint64_t wireor:1; - uint64_t clk_cont:1; - uint64_t idlelo:1; - uint64_t enable:1; -#else - uint64_t enable:1; - uint64_t idlelo:1; - uint64_t clk_cont:1; - uint64_t wireor:1; - uint64_t lsbfirst:1; - uint64_t int_ena:1; - uint64_t csena:1; - uint64_t cshi:1; - uint64_t idleclks:2; - uint64_t tritx:1; - uint64_t cslate:1; - uint64_t csena0:1; - uint64_t csena1:1; - uint64_t csena2:1; - uint64_t csena3:1; - uint64_t clkdiv:13; - uint64_t reserved_29_63:35; -#endif - } s; - struct cvmx_mpi_cfg_cn30xx { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_29_63:35; - uint64_t clkdiv:13; - uint64_t reserved_12_15:4; - uint64_t cslate:1; - uint64_t tritx:1; - uint64_t idleclks:2; - uint64_t cshi:1; - uint64_t csena:1; - uint64_t int_ena:1; - uint64_t lsbfirst:1; - uint64_t wireor:1; - uint64_t clk_cont:1; - uint64_t idlelo:1; - uint64_t enable:1; -#else - uint64_t enable:1; - uint64_t idlelo:1; - uint64_t clk_cont:1; - uint64_t wireor:1; - uint64_t lsbfirst:1; - uint64_t int_ena:1; - uint64_t csena:1; - uint64_t cshi:1; - uint64_t idleclks:2; - uint64_t tritx:1; - uint64_t cslate:1; - uint64_t reserved_12_15:4; - uint64_t clkdiv:13; - uint64_t reserved_29_63:35; -#endif - } cn30xx; - struct cvmx_mpi_cfg_cn31xx { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_29_63:35; - uint64_t clkdiv:13; - uint64_t reserved_11_15:5; - uint64_t tritx:1; - uint64_t idleclks:2; - uint64_t cshi:1; - uint64_t csena:1; - uint64_t int_ena:1; - uint64_t lsbfirst:1; - uint64_t wireor:1; - uint64_t clk_cont:1; - uint64_t idlelo:1; - uint64_t enable:1; -#else - uint64_t enable:1; - uint64_t idlelo:1; - uint64_t clk_cont:1; - uint64_t wireor:1; - uint64_t lsbfirst:1; - uint64_t int_ena:1; - uint64_t csena:1; - uint64_t cshi:1; - uint64_t idleclks:2; - uint64_t tritx:1; - uint64_t reserved_11_15:5; - uint64_t clkdiv:13; - uint64_t reserved_29_63:35; -#endif - } cn31xx; - struct cvmx_mpi_cfg_cn30xx cn50xx; - struct cvmx_mpi_cfg_cn61xx { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_29_63:35; - uint64_t clkdiv:13; - uint64_t reserved_14_15:2; - uint64_t csena1:1; - uint64_t csena0:1; - uint64_t cslate:1; - uint64_t tritx:1; - uint64_t idleclks:2; - uint64_t cshi:1; - uint64_t reserved_6_6:1; - uint64_t int_ena:1; - uint64_t lsbfirst:1; - uint64_t wireor:1; - uint64_t clk_cont:1; - uint64_t idlelo:1; - uint64_t enable:1; -#else - uint64_t enable:1; - uint64_t idlelo:1; - uint64_t clk_cont:1; - uint64_t wireor:1; - uint64_t lsbfirst:1; - uint64_t int_ena:1; - uint64_t reserved_6_6:1; - uint64_t cshi:1; - uint64_t idleclks:2; - uint64_t tritx:1; - uint64_t cslate:1; - uint64_t csena0:1; - uint64_t csena1:1; - uint64_t reserved_14_15:2; - uint64_t clkdiv:13; - uint64_t reserved_29_63:35; -#endif - } cn61xx; - struct cvmx_mpi_cfg_cn66xx { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_29_63:35; - uint64_t clkdiv:13; - uint64_t csena3:1; - uint64_t csena2:1; - uint64_t reserved_12_13:2; - uint64_t cslate:1; - uint64_t tritx:1; - uint64_t idleclks:2; - uint64_t cshi:1; - uint64_t reserved_6_6:1; - uint64_t int_ena:1; - uint64_t lsbfirst:1; - uint64_t wireor:1; - uint64_t clk_cont:1; - uint64_t idlelo:1; - uint64_t enable:1; -#else - uint64_t enable:1; - uint64_t idlelo:1; - uint64_t clk_cont:1; - uint64_t wireor:1; - uint64_t lsbfirst:1; - uint64_t int_ena:1; - uint64_t reserved_6_6:1; - uint64_t cshi:1; - uint64_t idleclks:2; - uint64_t tritx:1; - uint64_t cslate:1; - uint64_t reserved_12_13:2; - uint64_t csena2:1; - uint64_t csena3:1; - uint64_t clkdiv:13; - uint64_t reserved_29_63:35; -#endif - } cn66xx; - struct cvmx_mpi_cfg_cn61xx cnf71xx; -}; - -union cvmx_mpi_datx { - uint64_t u64; - struct cvmx_mpi_datx_s { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_8_63:56; - uint64_t data:8; -#else - uint64_t data:8; - uint64_t reserved_8_63:56; -#endif - } s; - struct cvmx_mpi_datx_s cn30xx; - struct cvmx_mpi_datx_s cn31xx; - struct cvmx_mpi_datx_s cn50xx; - struct cvmx_mpi_datx_s cn61xx; - struct cvmx_mpi_datx_s cn66xx; - struct cvmx_mpi_datx_s cnf71xx; -}; - -union cvmx_mpi_sts { - uint64_t u64; - struct cvmx_mpi_sts_s { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_13_63:51; - uint64_t rxnum:5; - uint64_t reserved_1_7:7; - uint64_t busy:1; -#else - uint64_t busy:1; - uint64_t reserved_1_7:7; - uint64_t rxnum:5; - uint64_t reserved_13_63:51; -#endif - } s; - struct cvmx_mpi_sts_s cn30xx; - struct cvmx_mpi_sts_s cn31xx; - struct cvmx_mpi_sts_s cn50xx; - struct cvmx_mpi_sts_s cn61xx; - struct cvmx_mpi_sts_s cn66xx; - struct cvmx_mpi_sts_s cnf71xx; -}; - -union cvmx_mpi_tx { - uint64_t u64; - struct cvmx_mpi_tx_s { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_22_63:42; - uint64_t csid:2; - uint64_t reserved_17_19:3; - uint64_t leavecs:1; - uint64_t reserved_13_15:3; - uint64_t txnum:5; - uint64_t reserved_5_7:3; - uint64_t totnum:5; -#else - uint64_t totnum:5; - uint64_t reserved_5_7:3; - uint64_t txnum:5; - uint64_t reserved_13_15:3; - uint64_t leavecs:1; - uint64_t reserved_17_19:3; - uint64_t csid:2; - uint64_t reserved_22_63:42; -#endif - } s; - struct cvmx_mpi_tx_cn30xx { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_17_63:47; - uint64_t leavecs:1; - uint64_t reserved_13_15:3; - uint64_t txnum:5; - uint64_t reserved_5_7:3; - uint64_t totnum:5; -#else - uint64_t totnum:5; - uint64_t reserved_5_7:3; - uint64_t txnum:5; - uint64_t reserved_13_15:3; - uint64_t leavecs:1; - uint64_t reserved_17_63:47; -#endif - } cn30xx; - struct cvmx_mpi_tx_cn30xx cn31xx; - struct cvmx_mpi_tx_cn30xx cn50xx; - struct cvmx_mpi_tx_cn61xx { -#ifdef __BIG_ENDIAN_BITFIELD - uint64_t reserved_21_63:43; - uint64_t csid:1; - uint64_t reserved_17_19:3; - uint64_t leavecs:1; - uint64_t reserved_13_15:3; - uint64_t txnum:5; - uint64_t reserved_5_7:3; - uint64_t totnum:5; -#else - uint64_t totnum:5; - uint64_t reserved_5_7:3; - uint64_t txnum:5; - uint64_t reserved_13_15:3; - uint64_t leavecs:1; - uint64_t reserved_17_19:3; - uint64_t csid:1; - uint64_t reserved_21_63:43; -#endif - } cn61xx; - struct cvmx_mpi_tx_s cn66xx; - struct cvmx_mpi_tx_cn61xx cnf71xx; -}; - -#endif diff --git a/drivers/spi/spi-cavium.h b/drivers/spi/spi-cavium.h new file mode 100644 index 0000000..d41dba5 --- /dev/null +++ b/drivers/spi/spi-cavium.h @@ -0,0 +1,298 @@ +/* MPI register descriptions */ + +#define CVMX_MPI_CFG (CVMX_ADD_IO_SEG(0x0001070000001000ull)) +#define CVMX_MPI_DATX(offset) (CVMX_ADD_IO_SEG(0x0001070000001080ull) + ((offset) & 15) * 8) +#define CVMX_MPI_STS (CVMX_ADD_IO_SEG(0x0001070000001008ull)) +#define CVMX_MPI_TX (CVMX_ADD_IO_SEG(0x0001070000001010ull)) + +union cvmx_mpi_cfg { + uint64_t u64; + struct cvmx_mpi_cfg_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t csena3:1; + uint64_t csena2:1; + uint64_t csena1:1; + uint64_t csena0:1; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t csena:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t csena:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t csena0:1; + uint64_t csena1:1; + uint64_t csena2:1; + uint64_t csena3:1; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } s; + struct cvmx_mpi_cfg_cn30xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t reserved_12_15:4; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t csena:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t csena:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t reserved_12_15:4; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } cn30xx; + struct cvmx_mpi_cfg_cn31xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t reserved_11_15:5; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t csena:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t csena:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t reserved_11_15:5; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } cn31xx; + struct cvmx_mpi_cfg_cn30xx cn50xx; + struct cvmx_mpi_cfg_cn61xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t reserved_14_15:2; + uint64_t csena1:1; + uint64_t csena0:1; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t reserved_6_6:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t reserved_6_6:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t csena0:1; + uint64_t csena1:1; + uint64_t reserved_14_15:2; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } cn61xx; + struct cvmx_mpi_cfg_cn66xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t csena3:1; + uint64_t csena2:1; + uint64_t reserved_12_13:2; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t reserved_6_6:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t reserved_6_6:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t reserved_12_13:2; + uint64_t csena2:1; + uint64_t csena3:1; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } cn66xx; + struct cvmx_mpi_cfg_cn61xx cnf71xx; +}; + +union cvmx_mpi_datx { + uint64_t u64; + struct cvmx_mpi_datx_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_8_63:56; + uint64_t data:8; +#else + uint64_t data:8; + uint64_t reserved_8_63:56; +#endif + } s; + struct cvmx_mpi_datx_s cn30xx; + struct cvmx_mpi_datx_s cn31xx; + struct cvmx_mpi_datx_s cn50xx; + struct cvmx_mpi_datx_s cn61xx; + struct cvmx_mpi_datx_s cn66xx; + struct cvmx_mpi_datx_s cnf71xx; +}; + +union cvmx_mpi_sts { + uint64_t u64; + struct cvmx_mpi_sts_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_13_63:51; + uint64_t rxnum:5; + uint64_t reserved_1_7:7; + uint64_t busy:1; +#else + uint64_t busy:1; + uint64_t reserved_1_7:7; + uint64_t rxnum:5; + uint64_t reserved_13_63:51; +#endif + } s; + struct cvmx_mpi_sts_s cn30xx; + struct cvmx_mpi_sts_s cn31xx; + struct cvmx_mpi_sts_s cn50xx; + struct cvmx_mpi_sts_s cn61xx; + struct cvmx_mpi_sts_s cn66xx; + struct cvmx_mpi_sts_s cnf71xx; +}; + +union cvmx_mpi_tx { + uint64_t u64; + struct cvmx_mpi_tx_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_22_63:42; + uint64_t csid:2; + uint64_t reserved_17_19:3; + uint64_t leavecs:1; + uint64_t reserved_13_15:3; + uint64_t txnum:5; + uint64_t reserved_5_7:3; + uint64_t totnum:5; +#else + uint64_t totnum:5; + uint64_t reserved_5_7:3; + uint64_t txnum:5; + uint64_t reserved_13_15:3; + uint64_t leavecs:1; + uint64_t reserved_17_19:3; + uint64_t csid:2; + uint64_t reserved_22_63:42; +#endif + } s; + struct cvmx_mpi_tx_cn30xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_17_63:47; + uint64_t leavecs:1; + uint64_t reserved_13_15:3; + uint64_t txnum:5; + uint64_t reserved_5_7:3; + uint64_t totnum:5; +#else + uint64_t totnum:5; + uint64_t reserved_5_7:3; + uint64_t txnum:5; + uint64_t reserved_13_15:3; + uint64_t leavecs:1; + uint64_t reserved_17_63:47; +#endif + } cn30xx; + struct cvmx_mpi_tx_cn30xx cn31xx; + struct cvmx_mpi_tx_cn30xx cn50xx; + struct cvmx_mpi_tx_cn61xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_21_63:43; + uint64_t csid:1; + uint64_t reserved_17_19:3; + uint64_t leavecs:1; + uint64_t reserved_13_15:3; + uint64_t txnum:5; + uint64_t reserved_5_7:3; + uint64_t totnum:5; +#else + uint64_t totnum:5; + uint64_t reserved_5_7:3; + uint64_t txnum:5; + uint64_t reserved_13_15:3; + uint64_t leavecs:1; + uint64_t reserved_17_19:3; + uint64_t csid:1; + uint64_t reserved_21_63:43; +#endif + } cn61xx; + struct cvmx_mpi_tx_s cn66xx; + struct cvmx_mpi_tx_cn61xx cnf71xx; +}; diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index 209eddc..2180176 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -15,7 +15,8 @@ #include #include -#include + +#include "spi-cavium.h" #define OCTEON_SPI_MAX_BYTES 9 -- cgit v0.10.2 From 63d49afefc87d1503956185e94fad43140c2ba9e Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Sat, 23 Jul 2016 12:42:54 +0200 Subject: spi: octeon: Split driver into Octeon specific and common parts Separate driver probing from SPI transfer functions. Signed-off-by: Jan Glauber Tested-by: Steven J. Hill Signed-off-by: Mark Brown diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3c74d00..185367e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o +spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o diff --git a/drivers/spi/spi-cavium-octeon.c b/drivers/spi/spi-cavium-octeon.c new file mode 100644 index 0000000..ee4703e --- /dev/null +++ b/drivers/spi/spi-cavium-octeon.c @@ -0,0 +1,104 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011, 2012 Cavium, Inc. + */ + +#include +#include +#include +#include +#include + +#include + +#include "spi-cavium.h" + +static int octeon_spi_probe(struct platform_device *pdev) +{ + struct resource *res_mem; + void __iomem *reg_base; + struct spi_master *master; + struct octeon_spi *p; + int err = -ENOENT; + + master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + p = spi_master_get_devdata(master); + platform_set_drvdata(pdev, master); + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(reg_base)) { + err = PTR_ERR(reg_base); + goto fail; + } + + p->register_base = reg_base; + p->sys_freq = octeon_get_io_clock_rate(); + + p->regs.config = 0; + p->regs.status = 0x08; + p->regs.tx = 0x10; + p->regs.data = 0x80; + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | + SPI_CPOL | + SPI_CS_HIGH | + SPI_LSB_FIRST | + SPI_3WIRE; + + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + + master->dev.of_node = pdev->dev.of_node; + err = devm_spi_register_master(&pdev->dev, master); + if (err) { + dev_err(&pdev->dev, "register master failed: %d\n", err); + goto fail; + } + + dev_info(&pdev->dev, "OCTEON SPI bus driver\n"); + + return 0; +fail: + spi_master_put(master); + return err; +} + +static int octeon_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct octeon_spi *p = spi_master_get_devdata(master); + + /* Clear the CSENA* and put everything in a known state. */ + writeq(0, p->register_base + OCTEON_SPI_CFG(p)); + + return 0; +} + +static const struct of_device_id octeon_spi_match[] = { + { .compatible = "cavium,octeon-3010-spi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_spi_match); + +static struct platform_driver octeon_spi_driver = { + .driver = { + .name = "spi-octeon", + .of_match_table = octeon_spi_match, + }, + .probe = octeon_spi_probe, + .remove = octeon_spi_remove, +}; + +module_platform_driver(octeon_spi_driver); + +MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-cavium.c b/drivers/spi/spi-cavium.c new file mode 100644 index 0000000..5aaf215 --- /dev/null +++ b/drivers/spi/spi-cavium.c @@ -0,0 +1,151 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011, 2012 Cavium, Inc. + */ + +#include +#include +#include +#include + +#include "spi-cavium.h" + +static void octeon_spi_wait_ready(struct octeon_spi *p) +{ + union cvmx_mpi_sts mpi_sts; + unsigned int loops = 0; + + do { + if (loops++) + __delay(500); + mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p)); + } while (mpi_sts.s.busy); +} + +static int octeon_spi_do_transfer(struct octeon_spi *p, + struct spi_message *msg, + struct spi_transfer *xfer, + bool last_xfer) +{ + struct spi_device *spi = msg->spi; + union cvmx_mpi_cfg mpi_cfg; + union cvmx_mpi_tx mpi_tx; + unsigned int clkdiv; + int mode; + bool cpha, cpol; + const u8 *tx_buf; + u8 *rx_buf; + int len; + int i; + + mode = spi->mode; + cpha = mode & SPI_CPHA; + cpol = mode & SPI_CPOL; + + clkdiv = p->sys_freq / (2 * xfer->speed_hz); + + mpi_cfg.u64 = 0; + + mpi_cfg.s.clkdiv = clkdiv; + mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0; + mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0; + mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0; + mpi_cfg.s.idlelo = cpha != cpol; + mpi_cfg.s.cslate = cpha ? 1 : 0; + mpi_cfg.s.enable = 1; + + if (spi->chip_select < 4) + p->cs_enax |= 1ull << (12 + spi->chip_select); + mpi_cfg.u64 |= p->cs_enax; + + if (mpi_cfg.u64 != p->last_cfg) { + p->last_cfg = mpi_cfg.u64; + writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p)); + } + tx_buf = xfer->tx_buf; + rx_buf = xfer->rx_buf; + len = xfer->len; + while (len > OCTEON_SPI_MAX_BYTES) { + for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { + u8 d; + if (tx_buf) + d = *tx_buf++; + else + d = 0; + writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); + } + mpi_tx.u64 = 0; + mpi_tx.s.csid = spi->chip_select; + mpi_tx.s.leavecs = 1; + mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; + mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); + + octeon_spi_wait_ready(p); + if (rx_buf) + for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { + u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); + *rx_buf++ = (u8)v; + } + len -= OCTEON_SPI_MAX_BYTES; + } + + for (i = 0; i < len; i++) { + u8 d; + if (tx_buf) + d = *tx_buf++; + else + d = 0; + writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); + } + + mpi_tx.u64 = 0; + mpi_tx.s.csid = spi->chip_select; + if (last_xfer) + mpi_tx.s.leavecs = xfer->cs_change; + else + mpi_tx.s.leavecs = !xfer->cs_change; + mpi_tx.s.txnum = tx_buf ? len : 0; + mpi_tx.s.totnum = len; + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); + + octeon_spi_wait_ready(p); + if (rx_buf) + for (i = 0; i < len; i++) { + u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); + *rx_buf++ = (u8)v; + } + + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + return xfer->len; +} + +int octeon_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct octeon_spi *p = spi_master_get_devdata(master); + unsigned int total_len = 0; + int status = 0; + struct spi_transfer *xfer; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + bool last_xfer = list_is_last(&xfer->transfer_list, + &msg->transfers); + int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); + if (r < 0) { + status = r; + goto err; + } + total_len += r; + } +err: + msg->status = status; + msg->actual_length = total_len; + spi_finalize_current_message(master); + return status; +} diff --git a/drivers/spi/spi-cavium.h b/drivers/spi/spi-cavium.h index d41dba5..88c5f36 100644 --- a/drivers/spi/spi-cavium.h +++ b/drivers/spi/spi-cavium.h @@ -1,3 +1,32 @@ +#ifndef __SPI_CAVIUM_H +#define __SPI_CAVIUM_H + +#define OCTEON_SPI_MAX_BYTES 9 +#define OCTEON_SPI_MAX_CLOCK_HZ 16000000 + +struct octeon_spi_regs { + int config; + int status; + int tx; + int data; +}; + +struct octeon_spi { + void __iomem *register_base; + u64 last_cfg; + u64 cs_enax; + int sys_freq; + struct octeon_spi_regs regs; +}; + +#define OCTEON_SPI_CFG(x) (x->regs.config) +#define OCTEON_SPI_STS(x) (x->regs.status) +#define OCTEON_SPI_TX(x) (x->regs.tx) +#define OCTEON_SPI_DAT0(x) (x->regs.data) + +int octeon_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg); + /* MPI register descriptions */ #define CVMX_MPI_CFG (CVMX_ADD_IO_SEG(0x0001070000001000ull)) @@ -296,3 +325,5 @@ union cvmx_mpi_tx { struct cvmx_mpi_tx_s cn66xx; struct cvmx_mpi_tx_cn61xx cnf71xx; }; + +#endif /* __SPI_CAVIUM_H */ diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c deleted file mode 100644 index 2180176..0000000 --- a/drivers/spi/spi-octeon.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2011, 2012 Cavium, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "spi-cavium.h" - -#define OCTEON_SPI_MAX_BYTES 9 - -#define OCTEON_SPI_MAX_CLOCK_HZ 16000000 - -struct octeon_spi_regs { - int config; - int status; - int tx; - int data; -}; - -struct octeon_spi { - void __iomem *register_base; - u64 last_cfg; - u64 cs_enax; - int sys_freq; - struct octeon_spi_regs regs; -}; - -#define OCTEON_SPI_CFG(x) (x->regs.config) -#define OCTEON_SPI_STS(x) (x->regs.status) -#define OCTEON_SPI_TX(x) (x->regs.tx) -#define OCTEON_SPI_DAT0(x) (x->regs.data) - -static void octeon_spi_wait_ready(struct octeon_spi *p) -{ - union cvmx_mpi_sts mpi_sts; - unsigned int loops = 0; - - do { - if (loops++) - __delay(500); - mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p)); - } while (mpi_sts.s.busy); -} - -static int octeon_spi_do_transfer(struct octeon_spi *p, - struct spi_message *msg, - struct spi_transfer *xfer, - bool last_xfer) -{ - struct spi_device *spi = msg->spi; - union cvmx_mpi_cfg mpi_cfg; - union cvmx_mpi_tx mpi_tx; - unsigned int clkdiv; - int mode; - bool cpha, cpol; - const u8 *tx_buf; - u8 *rx_buf; - int len; - int i; - - mode = spi->mode; - cpha = mode & SPI_CPHA; - cpol = mode & SPI_CPOL; - - clkdiv = p->sys_freq / (2 * xfer->speed_hz); - - mpi_cfg.u64 = 0; - - mpi_cfg.s.clkdiv = clkdiv; - mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0; - mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0; - mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0; - mpi_cfg.s.idlelo = cpha != cpol; - mpi_cfg.s.cslate = cpha ? 1 : 0; - mpi_cfg.s.enable = 1; - - if (spi->chip_select < 4) - p->cs_enax |= 1ull << (12 + spi->chip_select); - mpi_cfg.u64 |= p->cs_enax; - - if (mpi_cfg.u64 != p->last_cfg) { - p->last_cfg = mpi_cfg.u64; - writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p)); - } - tx_buf = xfer->tx_buf; - rx_buf = xfer->rx_buf; - len = xfer->len; - while (len > OCTEON_SPI_MAX_BYTES) { - for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u8 d; - if (tx_buf) - d = *tx_buf++; - else - d = 0; - writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); - } - mpi_tx.u64 = 0; - mpi_tx.s.csid = spi->chip_select; - mpi_tx.s.leavecs = 1; - mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; - mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; - writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); - - octeon_spi_wait_ready(p); - if (rx_buf) - for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); - *rx_buf++ = (u8)v; - } - len -= OCTEON_SPI_MAX_BYTES; - } - - for (i = 0; i < len; i++) { - u8 d; - if (tx_buf) - d = *tx_buf++; - else - d = 0; - writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); - } - - mpi_tx.u64 = 0; - mpi_tx.s.csid = spi->chip_select; - if (last_xfer) - mpi_tx.s.leavecs = xfer->cs_change; - else - mpi_tx.s.leavecs = !xfer->cs_change; - mpi_tx.s.txnum = tx_buf ? len : 0; - mpi_tx.s.totnum = len; - writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); - - octeon_spi_wait_ready(p); - if (rx_buf) - for (i = 0; i < len; i++) { - u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); - *rx_buf++ = (u8)v; - } - - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); - - return xfer->len; -} - -static int octeon_spi_transfer_one_message(struct spi_master *master, - struct spi_message *msg) -{ - struct octeon_spi *p = spi_master_get_devdata(master); - unsigned int total_len = 0; - int status = 0; - struct spi_transfer *xfer; - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - bool last_xfer = list_is_last(&xfer->transfer_list, - &msg->transfers); - int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); - if (r < 0) { - status = r; - goto err; - } - total_len += r; - } -err: - msg->status = status; - msg->actual_length = total_len; - spi_finalize_current_message(master); - return status; -} - -static int octeon_spi_probe(struct platform_device *pdev) -{ - struct resource *res_mem; - void __iomem *reg_base; - struct spi_master *master; - struct octeon_spi *p; - int err = -ENOENT; - - master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi)); - if (!master) - return -ENOMEM; - p = spi_master_get_devdata(master); - platform_set_drvdata(pdev, master); - - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg_base = devm_ioremap_resource(&pdev->dev, res_mem); - if (IS_ERR(reg_base)) { - err = PTR_ERR(reg_base); - goto fail; - } - - p->register_base = reg_base; - p->sys_freq = octeon_get_io_clock_rate(); - - p->regs.config = 0; - p->regs.status = 0x08; - p->regs.tx = 0x10; - p->regs.data = 0x80; - - master->num_chipselect = 4; - master->mode_bits = SPI_CPHA | - SPI_CPOL | - SPI_CS_HIGH | - SPI_LSB_FIRST | - SPI_3WIRE; - - master->transfer_one_message = octeon_spi_transfer_one_message; - master->bits_per_word_mask = SPI_BPW_MASK(8); - master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; - - master->dev.of_node = pdev->dev.of_node; - err = devm_spi_register_master(&pdev->dev, master); - if (err) { - dev_err(&pdev->dev, "register master failed: %d\n", err); - goto fail; - } - - dev_info(&pdev->dev, "OCTEON SPI bus driver\n"); - - return 0; -fail: - spi_master_put(master); - return err; -} - -static int octeon_spi_remove(struct platform_device *pdev) -{ - struct spi_master *master = platform_get_drvdata(pdev); - struct octeon_spi *p = spi_master_get_devdata(master); - - /* Clear the CSENA* and put everything in a known state. */ - writeq(0, p->register_base + OCTEON_SPI_CFG(p)); - - return 0; -} - -static const struct of_device_id octeon_spi_match[] = { - { .compatible = "cavium,octeon-3010-spi", }, - {}, -}; -MODULE_DEVICE_TABLE(of, octeon_spi_match); - -static struct platform_driver octeon_spi_driver = { - .driver = { - .name = "spi-octeon", - .of_match_table = octeon_spi_match, - }, - .probe = octeon_spi_probe, - .remove = octeon_spi_remove, -}; - -module_platform_driver(octeon_spi_driver); - -MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); -MODULE_AUTHOR("David Daney"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From ef4d96ec4ad947360f48677b6007a4c77953b090 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Jul 2016 23:53:31 +0100 Subject: spi: Split bus and I/O locking The current SPI code attempts to use bus_lock_mutex for two purposes. One is to implement spi_bus_lock() which grants exclusive access to the bus. The other is to serialize access to the physical hardware. This duplicate purpose causes confusion which leads to cases where access is not locked when a caller holds the bus lock mutex. Fix this by splitting out the I/O functionality into a new io_mutex. This means taking both mutexes in the DMA path, replacing the existing mutex with the new I/O one in the message pump (the mutex now always being taken in the message pump) and taking the bus lock mutex in spi_sync(), allowing __spi_sync() to have no mutex handling. While we're at it hoist the mutex further up the message pump before we power up the device so that all power up/down of the block is covered by it and there are no races with in-line pumping of messages. Reported-by: Rich Felker Tested-by: Rich Felker Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c9a8d54..d2e7f13 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1069,7 +1069,6 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); * __spi_pump_messages - function which processes spi message queue * @master: master to process queue for * @in_kthread: true if we are in the context of the message pump thread - * @bus_locked: true if the bus mutex is held when calling this function * * This function checks if there is any spi message in the queue that * needs processing and if so call out to the driver to initialize hardware @@ -1079,8 +1078,7 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); * inside spi_sync(); the queue extraction handling at the top of the * function should deal with this safely. */ -static void __spi_pump_messages(struct spi_master *master, bool in_kthread, - bool bus_locked) +static void __spi_pump_messages(struct spi_master *master, bool in_kthread) { unsigned long flags; bool was_busy = false; @@ -1152,6 +1150,8 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread, master->busy = true; spin_unlock_irqrestore(&master->queue_lock, flags); + mutex_lock(&master->io_mutex); + if (!was_busy && master->auto_runtime_pm) { ret = pm_runtime_get_sync(master->dev.parent); if (ret < 0) { @@ -1176,9 +1176,6 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread, } } - if (!bus_locked) - mutex_lock(&master->bus_lock_mutex); - trace_spi_message_start(master->cur_msg); if (master->prepare_message) { @@ -1208,8 +1205,7 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread, } out: - if (!bus_locked) - mutex_unlock(&master->bus_lock_mutex); + mutex_unlock(&master->io_mutex); /* Prod the scheduler in case transfer_one() was busy waiting */ if (!ret) @@ -1225,7 +1221,7 @@ static void spi_pump_messages(struct kthread_work *work) struct spi_master *master = container_of(work, struct spi_master, pump_messages); - __spi_pump_messages(master, true, master->bus_lock_flag); + __spi_pump_messages(master, true); } static int spi_init_queue(struct spi_master *master) @@ -1887,6 +1883,7 @@ int spi_register_master(struct spi_master *master) spin_lock_init(&master->queue_lock); spin_lock_init(&master->bus_lock_spinlock); mutex_init(&master->bus_lock_mutex); + mutex_init(&master->io_mutex); master->bus_lock_flag = 0; init_completion(&master->xfer_completion); if (!master->max_dma_len) @@ -2767,6 +2764,7 @@ int spi_flash_read(struct spi_device *spi, } mutex_lock(&master->bus_lock_mutex); + mutex_lock(&master->io_mutex); if (master->dma_rx) { rx_dev = master->dma_rx->device->dev; ret = spi_map_buf(master, rx_dev, &msg->rx_sg, @@ -2779,6 +2777,7 @@ int spi_flash_read(struct spi_device *spi, if (msg->cur_msg_mapped) spi_unmap_buf(master, rx_dev, &msg->rx_sg, DMA_FROM_DEVICE); + mutex_unlock(&master->io_mutex); mutex_unlock(&master->bus_lock_mutex); if (master->auto_runtime_pm) @@ -2800,8 +2799,7 @@ static void spi_complete(void *arg) complete(arg); } -static int __spi_sync(struct spi_device *spi, struct spi_message *message, - int bus_locked) +static int __spi_sync(struct spi_device *spi, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); int status; @@ -2819,9 +2817,6 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync); SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync); - if (!bus_locked) - mutex_lock(&master->bus_lock_mutex); - /* If we're not using the legacy transfer method then we will * try to transfer in the calling context so special case. * This code would be less tricky if we could remove the @@ -2839,9 +2834,6 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, status = spi_async_locked(spi, message); } - if (!bus_locked) - mutex_unlock(&master->bus_lock_mutex); - if (status == 0) { /* Push out the messages in the calling context if we * can. @@ -2851,7 +2843,7 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, spi_sync_immediate); SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync_immediate); - __spi_pump_messages(master, false, bus_locked); + __spi_pump_messages(master, false); } wait_for_completion(&done); @@ -2884,7 +2876,13 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, */ int spi_sync(struct spi_device *spi, struct spi_message *message) { - return __spi_sync(spi, message, spi->master->bus_lock_flag); + int ret; + + mutex_lock(&spi->master->bus_lock_mutex); + ret = __spi_sync(spi, message); + mutex_unlock(&spi->master->bus_lock_mutex); + + return ret; } EXPORT_SYMBOL_GPL(spi_sync); @@ -2906,7 +2904,7 @@ EXPORT_SYMBOL_GPL(spi_sync); */ int spi_sync_locked(struct spi_device *spi, struct spi_message *message) { - return __spi_sync(spi, message, 1); + return __spi_sync(spi, message); } EXPORT_SYMBOL_GPL(spi_sync_locked); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 7b53af4..072cb2a 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -312,8 +312,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @flags: other constraints relevant to this driver * @max_transfer_size: function that returns the max transfer size for * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used. + * @io_mutex: mutex for physical bus access * @bus_lock_spinlock: spinlock for SPI bus locking - * @bus_lock_mutex: mutex for SPI bus locking + * @bus_lock_mutex: mutex for exclusion of multiple callers * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use * @setup: updates the device mode and clocking records used by a * device's SPI controller; protocol code may call this. This @@ -446,6 +447,9 @@ struct spi_master { */ size_t (*max_transfer_size)(struct spi_device *spi); + /* I/O mutex */ + struct mutex io_mutex; + /* lock and mutex for SPI bus locking */ spinlock_t bus_lock_spinlock; struct mutex bus_lock_mutex; -- cgit v0.10.2