From 05aec357871f892eea91d8b808f96a6091dd5310 Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Wed, 15 Apr 2015 17:43:52 +0200 Subject: [PATCH 01/14] spi: Add SPI driver for Mikrotik RB4xx series boards This driver mediates access between the connected CPLD and other devices on the bus. The m25p80-compatible boot flash and (some models) MMC use regular SPI, bitbanged as required by the SoC. However the SPI-connected CPLD has a two-wire mode, in which two bits are transferred per SPI clock cycle. The second bit is transmitted with the SoC's CS2 pin. Signed-off-by: Bert Vermeulen Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 6 ++ drivers/spi/Makefile | 1 + drivers/spi/spi-rb4xx.c | 210 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 drivers/spi/spi-rb4xx.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ab8dfbef6f1b..8b1beaf6c9e5 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -429,6 +429,12 @@ config SPI_ROCKCHIP The main usecase of this controller is to use spi flash as boot device. +config SPI_RB4XX + tristate "Mikrotik RB4XX SPI master" + depends on SPI_MASTER && ATH79 + help + SPI controller driver for the Mikrotik RB4xx series boards. + config SPI_RSPI tristate "Renesas RSPI/QSPI controller" depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index d8cbf654976b..0218f39de7d2 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o +obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c new file mode 100644 index 000000000000..9b449d4f9871 --- /dev/null +++ b/drivers/spi/spi-rb4xx.c @@ -0,0 +1,210 @@ +/* + * SPI controller driver for the Mikrotik RB4xx boards + * + * Copyright (C) 2010 Gabor Juhos + * Copyright (C) 2015 Bert Vermeulen + * + * This file was based on the patches for Linux 2.6.27.39 published by + * MikroTik for their RouterBoard 4xx series devices. + * + * This program 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. + * + */ + +#include +#include +#include +#include +#include + +#include + +struct rb4xx_spi { + void __iomem *base; + struct clk *clk; +}; + +static inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg) +{ + return __raw_readl(rbspi->base + reg); +} + +static inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value) +{ + __raw_writel(value, rbspi->base + reg); +} + +static inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value) +{ + u32 regval; + + regval = spi_ioc; + if (value & BIT(0)) + regval |= AR71XX_SPI_IOC_DO; + + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval); + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK); +} + +static void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte) +{ + int i; + + for (i = 7; i >= 0; i--) + do_spi_clk(rbspi, spi_ioc, byte >> i); +} + +/* The CS2 pin is used to clock in a second bit per clock cycle. */ +static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc, + u8 value) +{ + u32 regval; + + regval = spi_ioc; + if (value & BIT(1)) + regval |= AR71XX_SPI_IOC_DO; + if (value & BIT(0)) + regval |= AR71XX_SPI_IOC_CS2; + + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval); + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK); +} + +/* Two bits at a time, msb first */ +static void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte) +{ + do_spi_clk_two(rbspi, spi_ioc, byte >> 6); + do_spi_clk_two(rbspi, spi_ioc, byte >> 4); + do_spi_clk_two(rbspi, spi_ioc, byte >> 2); + do_spi_clk_two(rbspi, spi_ioc, byte >> 0); +} + +static void rb4xx_set_cs(struct spi_device *spi, bool enable) +{ + struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master); + + /* + * Setting CS is done along with bitbanging the actual values, + * since it's all on the same hardware register. However the + * CPLD needs CS deselected after every command. + */ + if (!enable) + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, + AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1); +} + +static int rb4xx_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *t) +{ + struct rb4xx_spi *rbspi = spi_master_get_devdata(master); + int i; + u32 spi_ioc; + u8 *rx_buf; + const u8 *tx_buf; + + /* + * Prime the SPI register with the SPI device selected. The m25p80 boot + * flash and CPLD share the CS0 pin. This works because the CPLD's + * command set was designed to almost not clash with that of the + * boot flash. + */ + if (spi->chip_select == 2) + /* MMC */ + spi_ioc = AR71XX_SPI_IOC_CS0; + else + /* Boot flash and CPLD */ + spi_ioc = AR71XX_SPI_IOC_CS1; + + tx_buf = t->tx_buf; + rx_buf = t->rx_buf; + for (i = 0; i < t->len; ++i) { + if (t->tx_nbits == SPI_NBITS_DUAL) + /* CPLD can use two-wire transfers */ + do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]); + else + do_spi_byte(rbspi, spi_ioc, tx_buf[i]); + if (!rx_buf) + continue; + rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS); + } + spi_finalize_current_transfer(master); + + return 0; +} + +static int rb4xx_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct clk *ahb_clk; + struct rb4xx_spi *rbspi; + struct resource *r; + int err; + void __iomem *spi_base; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spi_base = devm_ioremap_resource(&pdev->dev, r); + if (!spi_base) + return PTR_ERR(spi_base); + + master = spi_alloc_master(&pdev->dev, sizeof(*rbspi)); + if (!master) + return -ENOMEM; + + ahb_clk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(ahb_clk)) + return PTR_ERR(ahb_clk); + + master->bus_num = 0; + master->num_chipselect = 3; + master->mode_bits = SPI_TX_DUAL; + master->bits_per_word_mask = BIT(7); + master->flags = SPI_MASTER_MUST_TX; + master->transfer_one = rb4xx_transfer_one; + master->set_cs = rb4xx_set_cs; + + err = devm_spi_register_master(&pdev->dev, master); + if (err) { + dev_err(&pdev->dev, "failed to register SPI master\n"); + return err; + } + + err = clk_prepare_enable(ahb_clk); + if (err) + return err; + + rbspi = spi_master_get_devdata(master); + rbspi->base = spi_base; + rbspi->clk = ahb_clk; + platform_set_drvdata(pdev, rbspi); + + /* Enable SPI */ + rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); + + return 0; +} + +static int rb4xx_spi_remove(struct platform_device *pdev) +{ + struct rb4xx_spi *rbspi = platform_get_drvdata(pdev); + + clk_disable_unprepare(rbspi->clk); + + return 0; +} + +static struct platform_driver rb4xx_spi_drv = { + .probe = rb4xx_spi_probe, + .remove = rb4xx_spi_remove, + .driver = { + .name = "rb4xx-spi", + }, +}; + +module_platform_driver(rb4xx_spi_drv); + +MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver"); +MODULE_AUTHOR("Gabor Juhos "); +MODULE_AUTHOR("Bert Vermeulen "); +MODULE_LICENSE("GPL v2"); From 4a1ae8be4563d29ddd36f46759191f4e867ed954 Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Mon, 20 Apr 2015 15:53:25 +0200 Subject: [PATCH 02/14] spi: rb4xx: Fix set_cs logic. As it turns out, the set_cs() enable parameter refers to the logic level on the CS pin, not the state of chip selection. This broke functionality of the LEDs behind the CPLD, or at least delayed the commands until another one came in to toggle CS. Signed-off-by: Bert Vermeulen Signed-off-by: Mark Brown --- drivers/spi/spi-rb4xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 9b449d4f9871..50f49f38e73c 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -90,7 +90,7 @@ static void rb4xx_set_cs(struct spi_device *spi, bool enable) * since it's all on the same hardware register. However the * CPLD needs CS deselected after every command. */ - if (!enable) + if (enable) rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1); } From b2b3024ca5853b3ea6390a3388e4761fc16e0655 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 30 Apr 2015 10:06:54 +0800 Subject: [PATCH 03/14] spi: rb4xx: Fix checking return value of devm_ioremap_resource() devm_ioremap_resource() returns ERR_PTR on failure. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-rb4xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 50f49f38e73c..3641d0e20135 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -145,7 +145,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); spi_base = devm_ioremap_resource(&pdev->dev, r); - if (!spi_base) + if (IS_ERR(spi_base)) return PTR_ERR(spi_base); master = spi_alloc_master(&pdev->dev, sizeof(*rbspi)); From 8634dafac6f58c91ab2461a68e8d7ea18ec0486b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:44:05 +0900 Subject: [PATCH 04/14] spi: rspi: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index f6bac9e77d06..70a7a7b168fd 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1300,7 +1300,7 @@ static int rspi_probe(struct platform_device *pdev) return ret; } -static struct platform_device_id spi_driver_ids[] = { +static const struct platform_device_id spi_driver_ids[] = { { "rspi", (kernel_ulong_t)&rspi_ops }, { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops }, { "qspi", (kernel_ulong_t)&qspi_ops }, From 23f6d39ec0a7c33630e80ba6607dffe7788a4c42 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:44:06 +0900 Subject: [PATCH 05/14] spi: s3c64xx: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index b1c6731fbf27..2a8c513c4d07 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1347,7 +1347,7 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = { .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, }; -static struct platform_device_id s3c64xx_spi_driver_ids[] = { +static const struct platform_device_id s3c64xx_spi_driver_ids[] = { { .name = "s3c2443-spi", .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config, From 3789c85206dd4efed58c83d1c29e865f22514410 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:44:07 +0900 Subject: [PATCH 06/14] spi: sh-msiof: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index bcc7c635d8e7..d3370a612d84 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1263,7 +1263,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id spi_driver_ids[] = { +static const struct platform_device_id spi_driver_ids[] = { { "spi_sh_msiof", (kernel_ulong_t)&sh_data }, { "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data }, From 6310372dc34bae740db280d3354b2e470cd31284 Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Thu, 30 Apr 2015 11:12:12 +0900 Subject: [PATCH 07/14] spi: rspi: Re-do the returning value of rspi_dma_check_then_transfer To reduce indentation and complexity of code, insteeds of returning zero the function rspi_dma_check_then_transfer should return rspi_dma_transfer directly after checking error. Signed-off-by: Hiep Cao Minh Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 70a7a7b168fd..568ea41625f5 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -665,15 +665,12 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi, static int rspi_dma_check_then_transfer(struct rspi_data *rspi, struct spi_transfer *xfer) { - if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { - /* rx_buf can be NULL on RSPI on SH in TX-only Mode */ - int ret = rspi_dma_transfer(rspi, &xfer->tx_sg, - xfer->rx_buf ? &xfer->rx_sg : NULL); - if (ret != -EAGAIN) - return 0; - } + if (!rspi->master->can_dma || !__rspi_can_dma(rspi, xfer)) + return -EAGAIN; - return -EAGAIN; + /* rx_buf can be NULL on RSPI on SH in TX-only Mode */ + return rspi_dma_transfer(rspi, &xfer->tx_sg, + xfer->rx_buf ? &xfer->rx_sg : NULL); } static int rspi_common_transfer(struct rspi_data *rspi, From a91bbe7d3fbc448dda9822467561e838cea005f8 Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Fri, 22 May 2015 18:59:36 +0900 Subject: [PATCH 08/14] spi: rspi: modify the name of "qspi_trigger_transfer_out_int" function The name of "qspi_trigger_transfer_out_int" function should be "qspi_trigger_transfer_out_in" without "t". Signed-off-by: Hiep Cao Minh Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 568ea41625f5..45007236f992 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -721,7 +721,7 @@ static int rspi_rz_transfer_one(struct spi_master *master, return rspi_common_transfer(rspi, xfer); } -static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx, +static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx, u8 *rx, unsigned int len) { int i, n, ret; @@ -768,7 +768,7 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, if (ret != -EAGAIN) return ret; - ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf, + ret = qspi_trigger_transfer_out_in(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); if (ret < 0) return ret; From cc2e9328ed09cfd07c2b18e7d9d1826f30df9ec0 Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Fri, 22 May 2015 18:59:37 +0900 Subject: [PATCH 09/14] spi: rspi: Re-do the returning value of qspi_transfer_out_in To reduce complexity of code, drop "ret" then qspi_transfer_out_in function should return the value of "qspi_trigger_transfer_out_in" directly. Signed-off-by: Hiep Cao Minh Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 45007236f992..f9189a0c8cec 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -768,12 +768,8 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, if (ret != -EAGAIN) return ret; - ret = qspi_trigger_transfer_out_in(rspi, xfer->tx_buf, + return qspi_trigger_transfer_out_in(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); - if (ret < 0) - return ret; - - return 0; } static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) From 03fbf488cece461468d3abb795f5e5f055e00040 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 4 Jun 2015 16:55:10 +0300 Subject: [PATCH 10/14] spi: pxa2xx: Differentiate Intel LPSS types Intel LPSS SPI properties differ between between platforms. Now private registers offset 0x400 or 0x800 is autodetected but there is need to support also other offset and handle a few other differences. Prepare for that by splitting the LPSS_SSP type into compatible hardware types and set it now based on PCI or ACPI ID. That type will be used to set properties that differ between current and upcoming platforms. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 8 +++---- drivers/spi/spi-pxa2xx.c | 44 ++++++++++++++++++++++++------------ include/linux/pxa2xx_ssp.h | 3 ++- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index fa7399e84bbb..3cfd4357489a 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -62,7 +62,7 @@ static struct pxa_spi_info spi_info_configs[] = { .max_clk_rate = 3686400, }, [PORT_BYT] = { - .type = LPSS_SSP, + .type = LPSS_BYT_SSP, .port_id = 0, .num_chipselect = 1, .max_clk_rate = 50000000, @@ -70,7 +70,7 @@ static struct pxa_spi_info spi_info_configs[] = { .rx_param = &byt_rx_param, }, [PORT_BSW0] = { - .type = LPSS_SSP, + .type = LPSS_BYT_SSP, .port_id = 0, .num_chipselect = 1, .max_clk_rate = 50000000, @@ -78,7 +78,7 @@ static struct pxa_spi_info spi_info_configs[] = { .rx_param = &bsw0_rx_param, }, [PORT_BSW1] = { - .type = LPSS_SSP, + .type = LPSS_BYT_SSP, .port_id = 1, .num_chipselect = 1, .max_clk_rate = 50000000, @@ -86,7 +86,7 @@ static struct pxa_spi_info spi_info_configs[] = { .rx_param = &bsw1_rx_param, }, [PORT_BSW2] = { - .type = LPSS_SSP, + .type = LPSS_BYT_SSP, .port_id = 2, .num_chipselect = 1, .max_clk_rate = 50000000, diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e3223ac75a7c..a85b7496a3cd 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -74,7 +74,13 @@ MODULE_ALIAS("platform:pxa2xx-spi"); static bool is_lpss_ssp(const struct driver_data *drv_data) { - return drv_data->ssp_type == LPSS_SSP; + switch (drv_data->ssp_type) { + case LPSS_LPT_SSP: + case LPSS_BYT_SSP: + return true; + default: + return false; + } } static bool is_quark_x1000_ssp(const struct driver_data *drv_data) @@ -1085,7 +1091,8 @@ static int setup(struct spi_device *spi) tx_hi_thres = 0; rx_thres = RX_THRESH_QUARK_X1000_DFLT; break; - case LPSS_SSP: + case LPSS_LPT_SSP: + case LPSS_BYT_SSP: tx_thres = LPSS_TX_LOTHRESH_DFLT; tx_hi_thres = LPSS_TX_HITHRESH_DFLT; rx_thres = LPSS_RX_THRESH_DFLT; @@ -1242,6 +1249,18 @@ static void cleanup(struct spi_device *spi) } #ifdef CONFIG_ACPI + +static struct acpi_device_id pxa2xx_spi_acpi_match[] = { + { "INT33C0", LPSS_LPT_SSP }, + { "INT33C1", LPSS_LPT_SSP }, + { "INT3430", LPSS_LPT_SSP }, + { "INT3431", LPSS_LPT_SSP }, + { "80860F0E", LPSS_BYT_SSP }, + { "8086228E", LPSS_BYT_SSP }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); + static struct pxa2xx_spi_master * pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) { @@ -1249,12 +1268,19 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) struct acpi_device *adev; struct ssp_device *ssp; struct resource *res; - int devid; + const struct acpi_device_id *id; + int devid, type; if (!ACPI_HANDLE(&pdev->dev) || acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) return NULL; + id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (id) + type = (int)id->driver_data; + else + return NULL; + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; @@ -1272,7 +1298,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); - ssp->type = LPSS_SSP; + ssp->type = type; ssp->pdev = pdev; ssp->port_id = -1; @@ -1285,16 +1311,6 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) return pdata; } -static struct acpi_device_id pxa2xx_spi_acpi_match[] = { - { "INT33C0", 0 }, - { "INT33C1", 0 }, - { "INT3430", 0 }, - { "INT3431", 0 }, - { "80860F0E", 0 }, - { "8086228E", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); #else static inline struct pxa2xx_spi_master * pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index dab545bb66b3..95a4b3bd7a5c 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -194,8 +194,9 @@ enum pxa_ssp_type { PXA168_SSP, PXA910_SSP, CE4100_SSP, - LPSS_SSP, QUARK_X1000_SSP, + LPSS_LPT_SSP, + LPSS_BYT_SSP, }; struct ssp_device { From dccf7369652f3934456345aab6a92fa905177886 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 4 Jun 2015 16:55:11 +0300 Subject: [PATCH 11/14] spi: pxa2xx: Prepare for new Intel LPSS SPI type Some of the Intel LPSS SPI properties will be different in upcoming platforms compared to existing Lynxpoint and BayTrail/Braswell. LPSS SPI private registers will be at different offset and there will be changes in individual registers and default FIFO thresholds too. Add configuration for these differences and use them in runtime based on LPSS SSP type. With this change private registers offset autodetection becomes needless. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 107 ++++++++++++++++++++----------------- include/linux/pxa2xx_ssp.h | 2 +- 2 files changed, 60 insertions(+), 49 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index a85b7496a3cd..3fec31dbf972 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -60,18 +60,51 @@ MODULE_ALIAS("platform:pxa2xx-spi"); | QUARK_X1000_SSCR1_TFT \ | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) -#define LPSS_RX_THRESH_DFLT 64 -#define LPSS_TX_LOTHRESH_DFLT 160 -#define LPSS_TX_HITHRESH_DFLT 224 - -/* Offset from drv_data->lpss_base */ -#define GENERAL_REG 0x08 #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) -#define SSP_REG 0x0c -#define SPI_CS_CONTROL 0x18 #define SPI_CS_CONTROL_SW_MODE BIT(0) #define SPI_CS_CONTROL_CS_HIGH BIT(1) +struct lpss_config { + /* LPSS offset from drv_data->ioaddr */ + unsigned offset; + /* Register offsets from drv_data->lpss_base or -1 */ + int reg_general; + int reg_ssp; + int reg_cs_ctrl; + /* FIFO thresholds */ + u32 rx_threshold; + u32 tx_threshold_lo; + u32 tx_threshold_hi; +}; + +/* Keep these sorted with enum pxa_ssp_type */ +static const struct lpss_config lpss_platforms[] = { + { /* LPSS_LPT_SSP */ + .offset = 0x800, + .reg_general = 0x08, + .reg_ssp = 0x0c, + .reg_cs_ctrl = 0x18, + .rx_threshold = 64, + .tx_threshold_lo = 160, + .tx_threshold_hi = 224, + }, + { /* LPSS_BYT_SSP */ + .offset = 0x400, + .reg_general = 0x08, + .reg_ssp = 0x0c, + .reg_cs_ctrl = 0x18, + .rx_threshold = 64, + .tx_threshold_lo = 160, + .tx_threshold_hi = 224, + }, +}; + +static inline const struct lpss_config +*lpss_get_config(const struct driver_data *drv_data) +{ + return &lpss_platforms[drv_data->ssp_type - LPSS_LPT_SSP]; +} + static bool is_lpss_ssp(const struct driver_data *drv_data) { switch (drv_data->ssp_type) { @@ -198,63 +231,39 @@ static void __lpss_ssp_write_priv(struct driver_data *drv_data, */ static void lpss_ssp_setup(struct driver_data *drv_data) { - unsigned offset = 0x400; - u32 value, orig; + const struct lpss_config *config; + u32 value; - /* - * Perform auto-detection of the LPSS SSP private registers. They - * can be either at 1k or 2k offset from the base address. - */ - orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); - - /* Test SPI_CS_CONTROL_SW_MODE bit enabling */ - value = orig | SPI_CS_CONTROL_SW_MODE; - writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL); - value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); - if (value != (orig | SPI_CS_CONTROL_SW_MODE)) { - offset = 0x800; - goto detection_done; - } - - orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); - - /* Test SPI_CS_CONTROL_SW_MODE bit disabling */ - value = orig & ~SPI_CS_CONTROL_SW_MODE; - writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL); - value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); - if (value != (orig & ~SPI_CS_CONTROL_SW_MODE)) { - offset = 0x800; - goto detection_done; - } - -detection_done: - /* Now set the LPSS base */ - drv_data->lpss_base = drv_data->ioaddr + offset; + config = lpss_get_config(drv_data); + drv_data->lpss_base = drv_data->ioaddr + config->offset; /* Enable software chip select control */ value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH; - __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); + __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value); /* Enable multiblock DMA transfers */ if (drv_data->master_info->enable_dma) { - __lpss_ssp_write_priv(drv_data, SSP_REG, 1); + __lpss_ssp_write_priv(drv_data, config->reg_ssp, 1); - value = __lpss_ssp_read_priv(drv_data, GENERAL_REG); + value = __lpss_ssp_read_priv(drv_data, config->reg_general); value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; - __lpss_ssp_write_priv(drv_data, GENERAL_REG, value); + __lpss_ssp_write_priv(drv_data, config->reg_general, value); } } static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) { + const struct lpss_config *config; u32 value; - value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL); + config = lpss_get_config(drv_data); + + value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); if (enable) value &= ~SPI_CS_CONTROL_CS_HIGH; else value |= SPI_CS_CONTROL_CS_HIGH; - __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); + __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value); } static void cs_assert(struct driver_data *drv_data) @@ -1081,6 +1090,7 @@ static int setup(struct spi_device *spi) { struct pxa2xx_spi_chip *chip_info = NULL; struct chip_data *chip; + const struct lpss_config *config; struct driver_data *drv_data = spi_master_get_devdata(spi->master); unsigned int clk_div; uint tx_thres, tx_hi_thres, rx_thres; @@ -1093,9 +1103,10 @@ static int setup(struct spi_device *spi) break; case LPSS_LPT_SSP: case LPSS_BYT_SSP: - tx_thres = LPSS_TX_LOTHRESH_DFLT; - tx_hi_thres = LPSS_TX_HITHRESH_DFLT; - rx_thres = LPSS_RX_THRESH_DFLT; + config = lpss_get_config(drv_data); + tx_thres = config->tx_threshold_lo; + tx_hi_thres = config->tx_threshold_hi; + rx_thres = config->rx_threshold; break; default: tx_thres = TX_THRESH_DFLT; diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 95a4b3bd7a5c..0485bab061fd 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -195,7 +195,7 @@ enum pxa_ssp_type { PXA910_SSP, CE4100_SSP, QUARK_X1000_SSP, - LPSS_LPT_SSP, + LPSS_LPT_SSP, /* Keep LPSS types sorted with lpss_platforms[] */ LPSS_BYT_SSP, }; From 82ba2c2ab3e9b9c62327f9441594668b20a1dba2 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 4 Jun 2015 16:55:12 +0300 Subject: [PATCH 12/14] spi: pxa2xx: Make LPSS SPI general register optional General register located in LPSS SPI private register space is not found in upcoming Intel LPSS platforms. Access it conditionally depending is it defined in configuration. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 3fec31dbf972..f97cd42fbc22 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -245,9 +245,13 @@ static void lpss_ssp_setup(struct driver_data *drv_data) if (drv_data->master_info->enable_dma) { __lpss_ssp_write_priv(drv_data, config->reg_ssp, 1); - value = __lpss_ssp_read_priv(drv_data, config->reg_general); - value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; - __lpss_ssp_write_priv(drv_data, config->reg_general, value); + if (config->reg_general >= 0) { + value = __lpss_ssp_read_priv(drv_data, + config->reg_general); + value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; + __lpss_ssp_write_priv(drv_data, + config->reg_general, value); + } } } From 6356437e65c2de610ab2bde24211426ae9322934 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 8 Jun 2015 22:32:37 +0200 Subject: [PATCH 13/14] spi: spi-pxa2xx: remove legacy PXA DMA bits Generic DMA support was already implemented by commit cd7bed003404 ("spi/pxa2xx: break out the private DMA API usage into a separate file") which moved all the legacy PXA DMA implementation code into its own file. With generic DMA available for PXA, we can now just trash this file. Signed-off-by: Daniel Mack Acked-by: Mark Brown [respin after pxa dmaengine support upstream] Signed-off-by: Robert Jarzmik Acked-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 9 +- drivers/spi/Makefile | 1 - drivers/spi/spi-pxa2xx-pxadma.c | 487 -------------------------------- drivers/spi/spi-pxa2xx.h | 6 +- 4 files changed, 2 insertions(+), 501 deletions(-) delete mode 100644 drivers/spi/spi-pxa2xx-pxadma.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 198f96b7fb45..c82a3d128cc6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -393,16 +393,9 @@ config SPI_PPC4xx help This selects a driver for the PPC4xx SPI Controller. -config SPI_PXA2XX_PXADMA - bool "PXA2xx SSP legacy PXA DMA API support" - depends on SPI_PXA2XX && ARCH_PXA - help - Enable PXA private legacy DMA API support. Note that this is - deprecated in favor of generic DMA engine API. - config SPI_PXA2XX_DMA def_bool y - depends on SPI_PXA2XX && !SPI_PXA2XX_PXADMA + depends on SPI_PXA2XX config SPI_PXA2XX tristate "PXA2xx SSP SPI master" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index d8cbf654976b..11df16016d8f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,7 +60,6 @@ obj-$(CONFIG_SPI_ORION) += spi-orion.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o spi-pxa2xx-platform-objs := spi-pxa2xx.o -spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c deleted file mode 100644 index 2e0796a0003f..000000000000 --- a/drivers/spi/spi-pxa2xx-pxadma.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * PXA2xx SPI private DMA support. - * - * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include "spi-pxa2xx.h" - -#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) -#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) - -bool pxa2xx_spi_dma_is_possible(size_t len) -{ - /* Try to map dma buffer and do a dma transfer if successful, but - * only if the length is non-zero and less than MAX_DMA_LEN. - * - * Zero-length non-descriptor DMA is illegal on PXA2xx; force use - * of PIO instead. Care is needed above because the transfer may - * have have been passed with buffers that are already dma mapped. - * A zero-length transfer in PIO mode will not try to write/read - * to/from the buffers - * - * REVISIT large transfers are exactly where we most want to be - * using DMA. If this happens much, split those transfers into - * multiple DMA segments rather than forcing PIO. - */ - return len > 0 && len <= MAX_DMA_LEN; -} - -int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - struct device *dev = &msg->spi->dev; - - if (!drv_data->cur_chip->enable_dma) - return 0; - - if (msg->is_dma_mapped) - return drv_data->rx_dma && drv_data->tx_dma; - - if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) - return 0; - - /* Modify setup if rx buffer is null */ - if (drv_data->rx == NULL) { - *drv_data->null_dma_buf = 0; - drv_data->rx = drv_data->null_dma_buf; - drv_data->rx_map_len = 4; - } else - drv_data->rx_map_len = drv_data->len; - - - /* Modify setup if tx buffer is null */ - if (drv_data->tx == NULL) { - *drv_data->null_dma_buf = 0; - drv_data->tx = drv_data->null_dma_buf; - drv_data->tx_map_len = 4; - } else - drv_data->tx_map_len = drv_data->len; - - /* Stream map the tx buffer. Always do DMA_TO_DEVICE first - * so we flush the cache *before* invalidating it, in case - * the tx and rx buffers overlap. - */ - drv_data->tx_dma = dma_map_single(dev, drv_data->tx, - drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(dev, drv_data->tx_dma)) - return 0; - - /* Stream map the rx buffer */ - drv_data->rx_dma = dma_map_single(dev, drv_data->rx, - drv_data->rx_map_len, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, drv_data->rx_dma)) { - dma_unmap_single(dev, drv_data->tx_dma, - drv_data->tx_map_len, DMA_TO_DEVICE); - return 0; - } - - return 1; -} - -static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data) -{ - struct device *dev; - - if (!drv_data->dma_mapped) - return; - - if (!drv_data->cur_msg->is_dma_mapped) { - dev = &drv_data->cur_msg->spi->dev; - dma_unmap_single(dev, drv_data->rx_dma, - drv_data->rx_map_len, DMA_FROM_DEVICE); - dma_unmap_single(dev, drv_data->tx_dma, - drv_data->tx_map_len, DMA_TO_DEVICE); - } - - drv_data->dma_mapped = 0; -} - -static int wait_ssp_rx_stall(struct driver_data *drv_data) -{ - unsigned long limit = loops_per_jiffy << 1; - - while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit) - cpu_relax(); - - return limit; -} - -static int wait_dma_channel_stop(int channel) -{ - unsigned long limit = loops_per_jiffy << 1; - - while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit) - cpu_relax(); - - return limit; -} - -static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data, - const char *msg) -{ - /* Stop and reset */ - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - write_SSSR_CS(drv_data, drv_data->clear_sr); - pxa2xx_spi_write(drv_data, SSCR1, - pxa2xx_spi_read(drv_data, SSCR1) - & ~drv_data->dma_cr1); - if (!pxa25x_ssp_comp(drv_data)) - pxa2xx_spi_write(drv_data, SSTO, 0); - pxa2xx_spi_flush(drv_data); - pxa2xx_spi_write(drv_data, SSCR0, - pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); - - pxa2xx_spi_unmap_dma_buffers(drv_data); - - dev_err(&drv_data->pdev->dev, "%s\n", msg); - - drv_data->cur_msg->state = ERROR_STATE; - tasklet_schedule(&drv_data->pump_transfers); -} - -static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - - /* Clear and disable interrupts on SSP and DMA channels*/ - pxa2xx_spi_write(drv_data, SSCR1, - pxa2xx_spi_read(drv_data, SSCR1) - & ~drv_data->dma_cr1); - write_SSSR_CS(drv_data, drv_data->clear_sr); - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - - if (wait_dma_channel_stop(drv_data->rx_channel) == 0) - dev_err(&drv_data->pdev->dev, - "dma_handler: dma rx channel stop failed\n"); - - if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) - dev_err(&drv_data->pdev->dev, - "dma_transfer: ssp rx stall failed\n"); - - pxa2xx_spi_unmap_dma_buffers(drv_data); - - /* update the buffer pointer for the amount completed in dma */ - drv_data->rx += drv_data->len - - (DCMD(drv_data->rx_channel) & DCMD_LENGTH); - - /* read trailing data from fifo, it does not matter how many - * bytes are in the fifo just read until buffer is full - * or fifo is empty, which ever occurs first */ - drv_data->read(drv_data); - - /* return count of what was actually read */ - msg->actual_length += drv_data->len - - (drv_data->rx_end - drv_data->rx); - - /* Transfer delays and chip select release are - * handled in pump_transfers or giveback - */ - - /* Move to next transfer */ - msg->state = pxa2xx_spi_next_transfer(drv_data); - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); -} - -void pxa2xx_spi_dma_handler(int channel, void *data) -{ - struct driver_data *drv_data = data; - u32 irq_status = DCSR(channel) & DMA_INT_MASK; - - if (irq_status & DCSR_BUSERR) { - - if (channel == drv_data->tx_channel) - pxa2xx_spi_dma_error_stop(drv_data, - "dma_handler: bad bus address on tx channel"); - else - pxa2xx_spi_dma_error_stop(drv_data, - "dma_handler: bad bus address on rx channel"); - return; - } - - /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */ - if ((channel == drv_data->tx_channel) - && (irq_status & DCSR_ENDINTR) - && (drv_data->ssp_type == PXA25x_SSP)) { - - /* Wait for rx to stall */ - if (wait_ssp_rx_stall(drv_data) == 0) - dev_err(&drv_data->pdev->dev, - "dma_handler: ssp rx stall failed\n"); - - /* finish this transfer, start the next */ - pxa2xx_spi_dma_transfer_complete(drv_data); - } -} - -irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) -{ - u32 irq_status; - - irq_status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr; - if (irq_status & SSSR_ROR) { - pxa2xx_spi_dma_error_stop(drv_data, - "dma_transfer: fifo overrun"); - return IRQ_HANDLED; - } - - /* Check for false positive timeout */ - if ((irq_status & SSSR_TINT) - && (DCSR(drv_data->tx_channel) & DCSR_RUN)) { - pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT); - return IRQ_HANDLED; - } - - if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) { - - /* Clear and disable timeout interrupt, do the rest in - * dma_transfer_complete */ - if (!pxa25x_ssp_comp(drv_data)) - pxa2xx_spi_write(drv_data, SSTO, 0); - - /* finish this transfer, start the next */ - pxa2xx_spi_dma_transfer_complete(drv_data); - - return IRQ_HANDLED; - } - - /* Opps problem detected */ - return IRQ_NONE; -} - -int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst) -{ - u32 dma_width; - - switch (drv_data->n_bytes) { - case 1: - dma_width = DCMD_WIDTH1; - break; - case 2: - dma_width = DCMD_WIDTH2; - break; - default: - dma_width = DCMD_WIDTH4; - break; - } - - /* Setup rx DMA Channel */ - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - DSADR(drv_data->rx_channel) = drv_data->ssdr_physical; - DTADR(drv_data->rx_channel) = drv_data->rx_dma; - if (drv_data->rx == drv_data->null_dma_buf) - /* No target address increment */ - DCMD(drv_data->rx_channel) = DCMD_FLOWSRC - | dma_width - | dma_burst - | drv_data->len; - else - DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR - | DCMD_FLOWSRC - | dma_width - | dma_burst - | drv_data->len; - - /* Setup tx DMA Channel */ - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - DSADR(drv_data->tx_channel) = drv_data->tx_dma; - DTADR(drv_data->tx_channel) = drv_data->ssdr_physical; - if (drv_data->tx == drv_data->null_dma_buf) - /* No source address increment */ - DCMD(drv_data->tx_channel) = DCMD_FLOWTRG - | dma_width - | dma_burst - | drv_data->len; - else - DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR - | DCMD_FLOWTRG - | dma_width - | dma_burst - | drv_data->len; - - /* Enable dma end irqs on SSP to detect end of transfer */ - if (drv_data->ssp_type == PXA25x_SSP) - DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN; - - return 0; -} - -void pxa2xx_spi_dma_start(struct driver_data *drv_data) -{ - DCSR(drv_data->rx_channel) |= DCSR_RUN; - DCSR(drv_data->tx_channel) |= DCSR_RUN; -} - -int pxa2xx_spi_dma_setup(struct driver_data *drv_data) -{ - struct device *dev = &drv_data->pdev->dev; - struct ssp_device *ssp = drv_data->ssp; - - /* Get two DMA channels (rx and tx) */ - drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx", - DMA_PRIO_HIGH, - pxa2xx_spi_dma_handler, - drv_data); - if (drv_data->rx_channel < 0) { - dev_err(dev, "problem (%d) requesting rx channel\n", - drv_data->rx_channel); - return -ENODEV; - } - drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx", - DMA_PRIO_MEDIUM, - pxa2xx_spi_dma_handler, - drv_data); - if (drv_data->tx_channel < 0) { - dev_err(dev, "problem (%d) requesting tx channel\n", - drv_data->tx_channel); - pxa_free_dma(drv_data->rx_channel); - return -ENODEV; - } - - DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel; - DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel; - - return 0; -} - -void pxa2xx_spi_dma_release(struct driver_data *drv_data) -{ - struct ssp_device *ssp = drv_data->ssp; - - DRCMR(ssp->drcmr_rx) = 0; - DRCMR(ssp->drcmr_tx) = 0; - - if (drv_data->tx_channel != 0) - pxa_free_dma(drv_data->tx_channel); - if (drv_data->rx_channel != 0) - pxa_free_dma(drv_data->rx_channel); -} - -void pxa2xx_spi_dma_resume(struct driver_data *drv_data) -{ - if (drv_data->rx_channel != -1) - DRCMR(drv_data->ssp->drcmr_rx) = - DRCMR_MAPVLD | drv_data->rx_channel; - if (drv_data->tx_channel != -1) - DRCMR(drv_data->ssp->drcmr_tx) = - DRCMR_MAPVLD | drv_data->tx_channel; -} - -int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, - struct spi_device *spi, - u8 bits_per_word, u32 *burst_code, - u32 *threshold) -{ - struct pxa2xx_spi_chip *chip_info = - (struct pxa2xx_spi_chip *)spi->controller_data; - int bytes_per_word; - int burst_bytes; - int thresh_words; - int req_burst_size; - int retval = 0; - - /* Set the threshold (in registers) to equal the same amount of data - * as represented by burst size (in bytes). The computation below - * is (burst_size rounded up to nearest 8 byte, word or long word) - * divided by (bytes/register); the tx threshold is the inverse of - * the rx, so that there will always be enough data in the rx fifo - * to satisfy a burst, and there will always be enough space in the - * tx fifo to accept a burst (a tx burst will overwrite the fifo if - * there is not enough space), there must always remain enough empty - * space in the rx fifo for any data loaded to the tx fifo. - * Whenever burst_size (in bytes) equals bits/word, the fifo threshold - * will be 8, or half the fifo; - * The threshold can only be set to 2, 4 or 8, but not 16, because - * to burst 16 to the tx fifo, the fifo would have to be empty; - * however, the minimum fifo trigger level is 1, and the tx will - * request service when the fifo is at this level, with only 15 spaces. - */ - - /* find bytes/word */ - if (bits_per_word <= 8) - bytes_per_word = 1; - else if (bits_per_word <= 16) - bytes_per_word = 2; - else - bytes_per_word = 4; - - /* use struct pxa2xx_spi_chip->dma_burst_size if available */ - if (chip_info) - req_burst_size = chip_info->dma_burst_size; - else { - switch (chip->dma_burst_size) { - default: - /* if the default burst size is not set, - * do it now */ - chip->dma_burst_size = DCMD_BURST8; - case DCMD_BURST8: - req_burst_size = 8; - break; - case DCMD_BURST16: - req_burst_size = 16; - break; - case DCMD_BURST32: - req_burst_size = 32; - break; - } - } - if (req_burst_size <= 8) { - *burst_code = DCMD_BURST8; - burst_bytes = 8; - } else if (req_burst_size <= 16) { - if (bytes_per_word == 1) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST8; - burst_bytes = 8; - retval = 1; - } else { - *burst_code = DCMD_BURST16; - burst_bytes = 16; - } - } else { - if (bytes_per_word == 1) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST8; - burst_bytes = 8; - retval = 1; - } else if (bytes_per_word == 2) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST16; - burst_bytes = 16; - retval = 1; - } else { - *burst_code = DCMD_BURST32; - burst_bytes = 32; - } - } - - thresh_words = burst_bytes / bytes_per_word; - - /* thresh_words will be between 2 and 8 */ - *threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT) - | (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT); - - return retval; -} diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 85a58c906869..9f01e9c9aa75 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -162,11 +162,7 @@ extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data); /* * Select the right DMA implementation. */ -#if defined(CONFIG_SPI_PXA2XX_PXADMA) -#define SPI_PXA2XX_USE_DMA 1 -#define MAX_DMA_LEN 8191 -#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE) -#elif defined(CONFIG_SPI_PXA2XX_DMA) +#if defined(CONFIG_SPI_PXA2XX_DMA) #define SPI_PXA2XX_USE_DMA 1 #define MAX_DMA_LEN SZ_64K #define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) From 8422ddf762c6168a261b5ba07eeaa2f1238ba8c2 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:22:14 +0200 Subject: [PATCH 14/14] spi: pxa2xx: Constify ACPI device ids Constify the ACPI device ID array, it doesn't need to be writable at runtime. Signed-off-by: Mathias Krause Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index f97cd42fbc22..7293d6d875c5 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1265,7 +1265,7 @@ static void cleanup(struct spi_device *spi) #ifdef CONFIG_ACPI -static struct acpi_device_id pxa2xx_spi_acpi_match[] = { +static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { { "INT33C0", LPSS_LPT_SSP }, { "INT33C1", LPSS_LPT_SSP }, { "INT3430", LPSS_LPT_SSP },