From 8be3d3b2ac87955455c7ce852e0d489cb04fff59 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:06 +0200 Subject: mmc: spi: Move SSP register definitions into separate file Move the definitions into separate file so separate SPI driver can be implemented. The SSP controller in MXS can act both as a MMC host and as a SPI host. Based on previous attempt by: Fabio Estevam Signed-off-by: Fabio Estevam Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/mmc/host/mxs-mmc.c | 87 ++--------------------------------- include/linux/spi/mxs-spi.h | 109 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 84 deletions(-) create mode 100644 include/linux/spi/mxs-spi.h diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index a51f9309ffb..26c95dc87cb 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -45,87 +45,10 @@ #include #include #include +#include #define DRIVER_NAME "mxs-mmc" -/* card detect polling timeout */ -#define MXS_MMC_DETECT_TIMEOUT (HZ/2) - -#define ssp_is_old(host) ((host)->devid == IMX23_MMC) - -/* SSP registers */ -#define HW_SSP_CTRL0 0x000 -#define BM_SSP_CTRL0_RUN (1 << 29) -#define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) -#define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) -#define BM_SSP_CTRL0_READ (1 << 25) -#define BM_SSP_CTRL0_DATA_XFER (1 << 24) -#define BP_SSP_CTRL0_BUS_WIDTH (22) -#define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22) -#define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) -#define BM_SSP_CTRL0_LONG_RESP (1 << 19) -#define BM_SSP_CTRL0_GET_RESP (1 << 17) -#define BM_SSP_CTRL0_ENABLE (1 << 16) -#define BP_SSP_CTRL0_XFER_COUNT (0) -#define BM_SSP_CTRL0_XFER_COUNT (0xffff) -#define HW_SSP_CMD0 0x010 -#define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25) -#define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22) -#define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21) -#define BM_SSP_CMD0_APPEND_8CYC (1 << 20) -#define BP_SSP_CMD0_BLOCK_SIZE (16) -#define BM_SSP_CMD0_BLOCK_SIZE (0xf << 16) -#define BP_SSP_CMD0_BLOCK_COUNT (8) -#define BM_SSP_CMD0_BLOCK_COUNT (0xff << 8) -#define BP_SSP_CMD0_CMD (0) -#define BM_SSP_CMD0_CMD (0xff) -#define HW_SSP_CMD1 0x020 -#define HW_SSP_XFER_SIZE 0x030 -#define HW_SSP_BLOCK_SIZE 0x040 -#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT (4) -#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4) -#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE (0) -#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE (0xf) -#define HW_SSP_TIMING(h) (ssp_is_old(h) ? 0x050 : 0x070) -#define BP_SSP_TIMING_TIMEOUT (16) -#define BM_SSP_TIMING_TIMEOUT (0xffff << 16) -#define BP_SSP_TIMING_CLOCK_DIVIDE (8) -#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) -#define BP_SSP_TIMING_CLOCK_RATE (0) -#define BM_SSP_TIMING_CLOCK_RATE (0xff) -#define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080) -#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) -#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) -#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29) -#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN (1 << 28) -#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ (1 << 27) -#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN (1 << 26) -#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ (1 << 25) -#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN (1 << 24) -#define BM_SSP_CTRL1_DATA_CRC_IRQ (1 << 23) -#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN (1 << 22) -#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ (1 << 21) -#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN (1 << 20) -#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ (1 << 17) -#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN (1 << 16) -#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) -#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) -#define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) -#define BM_SSP_CTRL1_POLARITY (1 << 9) -#define BP_SSP_CTRL1_WORD_LENGTH (4) -#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) -#define BP_SSP_CTRL1_SSP_MODE (0) -#define BM_SSP_CTRL1_SSP_MODE (0xf) -#define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0) -#define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0) -#define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0) -#define HW_SSP_SDRESP3(h) (ssp_is_old(h) ? 0x0b0 : 0x0d0) -#define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100) -#define BM_SSP_STATUS_CARD_DETECT (1 << 28) -#define BM_SSP_STATUS_SDIO_IRQ (1 << 17) - -#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) - #define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \ BM_SSP_CTRL1_RESP_ERR_IRQ | \ BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \ @@ -135,12 +58,8 @@ BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \ BM_SSP_CTRL1_FIFO_OVERRUN_IRQ) -#define SSP_PIO_NUM 3 - -enum mxs_mmc_id { - IMX23_MMC, - IMX28_MMC, -}; +/* card detect polling timeout */ +#define MXS_MMC_DETECT_TIMEOUT (HZ/2) struct mxs_mmc_host { struct mmc_host *mmc; diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h new file mode 100644 index 00000000000..b7ccd577f75 --- /dev/null +++ b/include/linux/spi/mxs-spi.h @@ -0,0 +1,109 @@ +/* + * include/linux/spi/mxs-spi.h + * + * Freescale i.MX233/i.MX28 SPI controller register definition + * + * Copyright 2008 Embedded Alley Solutions, Inc. + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __LINUX_SPI_MXS_SPI_H__ +#define __LINUX_SPI_MXS_SPI_H__ + +#define ssp_is_old(host) ((host)->devid == IMX23_MMC) + +/* SSP registers */ +#define HW_SSP_CTRL0 0x000 +#define BM_SSP_CTRL0_RUN (1 << 29) +#define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) +#define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) +#define BM_SSP_CTRL0_READ (1 << 25) +#define BM_SSP_CTRL0_DATA_XFER (1 << 24) +#define BP_SSP_CTRL0_BUS_WIDTH 22 +#define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22) +#define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) +#define BM_SSP_CTRL0_LONG_RESP (1 << 19) +#define BM_SSP_CTRL0_GET_RESP (1 << 17) +#define BM_SSP_CTRL0_ENABLE (1 << 16) +#define BP_SSP_CTRL0_XFER_COUNT 0 +#define BM_SSP_CTRL0_XFER_COUNT 0xffff +#define HW_SSP_CMD0 0x010 +#define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25) +#define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22) +#define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21) +#define BM_SSP_CMD0_APPEND_8CYC (1 << 20) +#define BP_SSP_CMD0_BLOCK_SIZE 16 +#define BM_SSP_CMD0_BLOCK_SIZE (0xf << 16) +#define BP_SSP_CMD0_BLOCK_COUNT 8 +#define BM_SSP_CMD0_BLOCK_COUNT (0xff << 8) +#define BP_SSP_CMD0_CMD 0 +#define BM_SSP_CMD0_CMD 0xff +#define HW_SSP_CMD1 0x020 +#define HW_SSP_XFER_SIZE 0x030 +#define HW_SSP_BLOCK_SIZE 0x040 +#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT 4 +#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4) +#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE 0 +#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE 0xf +#define HW_SSP_TIMING(h) (ssp_is_old(h) ? 0x050 : 0x070) +#define BP_SSP_TIMING_TIMEOUT 16 +#define BM_SSP_TIMING_TIMEOUT (0xffff << 16) +#define BP_SSP_TIMING_CLOCK_DIVIDE 8 +#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) +#define BP_SSP_TIMING_CLOCK_RATE 0 +#define BM_SSP_TIMING_CLOCK_RATE 0xff +#define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080) +#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) +#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) +#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29) +#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN (1 << 28) +#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ (1 << 27) +#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN (1 << 26) +#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ (1 << 25) +#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN (1 << 24) +#define BM_SSP_CTRL1_DATA_CRC_IRQ (1 << 23) +#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN (1 << 22) +#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ (1 << 21) +#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN (1 << 20) +#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ (1 << 17) +#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN (1 << 16) +#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) +#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) +#define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) +#define BM_SSP_CTRL1_POLARITY (1 << 9) +#define BP_SSP_CTRL1_WORD_LENGTH 4 +#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) +#define BP_SSP_CTRL1_SSP_MODE 0 +#define BM_SSP_CTRL1_SSP_MODE 0xf +#define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0) +#define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0) +#define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0) +#define HW_SSP_SDRESP3(h) (ssp_is_old(h) ? 0x0b0 : 0x0d0) +#define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100) +#define BM_SSP_STATUS_CARD_DETECT (1 << 28) +#define BM_SSP_STATUS_SDIO_IRQ (1 << 17) + +#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) + +#define SSP_PIO_NUM 3 + +enum mxs_mmc_id { + IMX23_MMC, + IMX28_MMC, +}; + +#endif /* __LINUX_SPI_MXS_SPI_H__ */ -- cgit v1.2.3 From 600a991f98d94c97f72870f85df687c517c3af53 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:07 +0200 Subject: mmc: spi: Rename IMX2[38]_MMC to IMX2[38]_SSP Since the SSP controller can act as both SPI and MMC host, renaming the enum to properly reflect the naming seems appropriate. Based on previous attempt by: Fabio Estevam Signed-off-by: Fabio Estevam Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/mmc/host/mxs-mmc.c | 18 +++++++++--------- include/linux/spi/mxs-spi.h | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 26c95dc87cb..e80c2b6b2d7 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -78,7 +78,7 @@ struct mxs_mmc_host { enum dma_transfer_direction slave_dirn; u32 ssp_pio_words[SSP_PIO_NUM]; - enum mxs_mmc_id devid; + enum mxs_ssp_id devid; unsigned char bus_width; spinlock_t lock; int sdio_irq_en; @@ -601,22 +601,22 @@ static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param) return true; } -static struct platform_device_id mxs_mmc_ids[] = { +static struct platform_device_id mxs_ssp_ids[] = { { .name = "imx23-mmc", - .driver_data = IMX23_MMC, + .driver_data = IMX23_SSP, }, { .name = "imx28-mmc", - .driver_data = IMX28_MMC, + .driver_data = IMX28_SSP, }, { /* sentinel */ } }; -MODULE_DEVICE_TABLE(platform, mxs_mmc_ids); +MODULE_DEVICE_TABLE(platform, mxs_ssp_ids); static const struct of_device_id mxs_mmc_dt_ids[] = { - { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_MMC, }, - { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_MMC, }, + { .compatible = "fsl,imx23-mmc", .data = (void *) IMX23_SSP, }, + { .compatible = "fsl,imx28-mmc", .data = (void *) IMX28_SSP, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_mmc_dt_ids); @@ -655,7 +655,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) } if (np) { - host->devid = (enum mxs_mmc_id) of_id->data; + host->devid = (enum mxs_ssp_id) of_id->data; /* * TODO: This is a temporary solution and should be changed * to use generic DMA binding later when the helpers get in. @@ -829,7 +829,7 @@ static const struct dev_pm_ops mxs_mmc_pm_ops = { static struct platform_driver mxs_mmc_driver = { .probe = mxs_mmc_probe, .remove = mxs_mmc_remove, - .id_table = mxs_mmc_ids, + .id_table = mxs_ssp_ids, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index b7ccd577f75..c08aef57c7c 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -24,7 +24,7 @@ #ifndef __LINUX_SPI_MXS_SPI_H__ #define __LINUX_SPI_MXS_SPI_H__ -#define ssp_is_old(host) ((host)->devid == IMX23_MMC) +#define ssp_is_old(host) ((host)->devid == IMX23_SSP) /* SSP registers */ #define HW_SSP_CTRL0 0x000 @@ -101,9 +101,9 @@ #define SSP_PIO_NUM 3 -enum mxs_mmc_id { - IMX23_MMC, - IMX28_MMC, +enum mxs_ssp_id { + IMX23_SSP, + IMX28_SSP, }; #endif /* __LINUX_SPI_MXS_SPI_H__ */ -- cgit v1.2.3 From f83b73806579c666fe6f1a4ed4800092c89e81db Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:08 +0200 Subject: mmc: spi: Add necessary bits into mxs-spi.h Add missing register bits and registers into mxs-spi.h . These will be used by the SPI driver. Based on previous attempt by: Fabio Estevam Signed-off-by: Fabio Estevam Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- include/linux/spi/mxs-spi.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index c08aef57c7c..7dfa1d7d1a7 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -30,12 +30,14 @@ #define HW_SSP_CTRL0 0x000 #define BM_SSP_CTRL0_RUN (1 << 29) #define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) +#define BM_SSP_CTRL0_LOCK_CS (1 << 27) #define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) #define BM_SSP_CTRL0_READ (1 << 25) #define BM_SSP_CTRL0_DATA_XFER (1 << 24) #define BP_SSP_CTRL0_BUS_WIDTH 22 #define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22) #define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) +#define BM_SSP_CTRL0_WAIT_FOR_CMD (1 << 20) #define BM_SSP_CTRL0_LONG_RESP (1 << 19) #define BM_SSP_CTRL0_GET_RESP (1 << 17) #define BM_SSP_CTRL0_ENABLE (1 << 16) @@ -64,8 +66,12 @@ #define BM_SSP_TIMING_TIMEOUT (0xffff << 16) #define BP_SSP_TIMING_CLOCK_DIVIDE 8 #define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) +#define BF_SSP_TIMING_CLOCK_DIVIDE(v) \ + (((v) << 8) & BM_SSP_TIMING_CLOCK_DIVIDE) #define BP_SSP_TIMING_CLOCK_RATE 0 #define BM_SSP_TIMING_CLOCK_RATE 0xff +#define BF_SSP_TIMING_CLOCK_RATE(v) \ + (((v) << 0) & BM_SSP_TIMING_CLOCK_RATE) #define HW_SSP_CTRL1(h) (ssp_is_old(h) ? 0x060 : 0x080) #define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) #define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) @@ -84,11 +90,26 @@ #define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) #define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) #define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) +#define BM_SSP_CTRL1_PHASE (1 << 10) #define BM_SSP_CTRL1_POLARITY (1 << 9) #define BP_SSP_CTRL1_WORD_LENGTH 4 #define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) +#define BF_SSP_CTRL1_WORD_LENGTH(v) \ + (((v) << 4) & BM_SSP_CTRL1_WORD_LENGTH) +#define BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS 0x3 +#define BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS 0x7 +#define BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS 0xF #define BP_SSP_CTRL1_SSP_MODE 0 #define BM_SSP_CTRL1_SSP_MODE 0xf +#define BF_SSP_CTRL1_SSP_MODE(v) \ + (((v) << 0) & BM_SSP_CTRL1_SSP_MODE) +#define BV_SSP_CTRL1_SSP_MODE__SPI 0x0 +#define BV_SSP_CTRL1_SSP_MODE__SSI 0x1 +#define BV_SSP_CTRL1_SSP_MODE__SD_MMC 0x3 +#define BV_SSP_CTRL1_SSP_MODE__MS 0x4 + +#define HW_SSP_DATA(h) (ssp_is_old(h) ? 0x070 : 0x090) + #define HW_SSP_SDRESP0(h) (ssp_is_old(h) ? 0x080 : 0x0a0) #define HW_SSP_SDRESP1(h) (ssp_is_old(h) ? 0x090 : 0x0b0) #define HW_SSP_SDRESP2(h) (ssp_is_old(h) ? 0x0a0 : 0x0c0) @@ -96,6 +117,7 @@ #define HW_SSP_STATUS(h) (ssp_is_old(h) ? 0x0c0 : 0x100) #define BM_SSP_STATUS_CARD_DETECT (1 << 28) #define BM_SSP_STATUS_SDIO_IRQ (1 << 17) +#define BM_SSP_STATUS_FIFO_EMPTY (1 << 5) #define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) -- cgit v1.2.3 From 829c1bf40b926a86e545733f6252262add3abe39 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:09 +0200 Subject: mmc: spi: Pull out parts shared between MMC and SPI Abstract out the common part of private data shared between MMC and SPI. These shall later allow to use common clock configuration function. Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/mmc/host/mxs-mmc.c | 107 ++++++++++++++++++++++++-------------------- include/linux/spi/mxs-spi.h | 8 ++++ 2 files changed, 67 insertions(+), 48 deletions(-) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index e80c2b6b2d7..7b85e035a4c 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -62,23 +62,20 @@ #define MXS_MMC_DETECT_TIMEOUT (HZ/2) struct mxs_mmc_host { + struct mxs_ssp ssp; + struct mmc_host *mmc; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; - void __iomem *base; int dma_channel; - struct clk *clk; - unsigned int clk_rate; - struct dma_chan *dmach; struct mxs_dma_data dma_data; unsigned int dma_dir; enum dma_transfer_direction slave_dirn; u32 ssp_pio_words[SSP_PIO_NUM]; - enum mxs_ssp_id devid; unsigned char bus_width; spinlock_t lock; int sdio_irq_en; @@ -105,16 +102,18 @@ static int mxs_mmc_get_ro(struct mmc_host *mmc) static int mxs_mmc_get_cd(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; - return !(readl(host->base + HW_SSP_STATUS(host)) & + return !(readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_CARD_DETECT); } static void mxs_mmc_reset(struct mxs_mmc_host *host) { + struct mxs_ssp *ssp = &host->ssp; u32 ctrl0, ctrl1; - stmp_reset_block(host->base); + stmp_reset_block(ssp->base); ctrl0 = BM_SSP_CTRL0_IGNORE_CRC; ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) | @@ -130,15 +129,15 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host) writel(BF_SSP(0xffff, TIMING_TIMEOUT) | BF_SSP(2, TIMING_CLOCK_DIVIDE) | BF_SSP(0, TIMING_CLOCK_RATE), - host->base + HW_SSP_TIMING(host)); + ssp->base + HW_SSP_TIMING(ssp)); if (host->sdio_irq_en) { ctrl0 |= BM_SSP_CTRL0_SDIO_IRQ_CHECK; ctrl1 |= BM_SSP_CTRL1_SDIO_IRQ_EN; } - writel(ctrl0, host->base + HW_SSP_CTRL0); - writel(ctrl1, host->base + HW_SSP_CTRL1(host)); + writel(ctrl0, ssp->base + HW_SSP_CTRL0); + writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp)); } static void mxs_mmc_start_cmd(struct mxs_mmc_host *host, @@ -149,15 +148,16 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host) struct mmc_command *cmd = host->cmd; struct mmc_data *data = host->data; struct mmc_request *mrq = host->mrq; + struct mxs_ssp *ssp = &host->ssp; if (mmc_resp_type(cmd) & MMC_RSP_PRESENT) { if (mmc_resp_type(cmd) & MMC_RSP_136) { - cmd->resp[3] = readl(host->base + HW_SSP_SDRESP0(host)); - cmd->resp[2] = readl(host->base + HW_SSP_SDRESP1(host)); - cmd->resp[1] = readl(host->base + HW_SSP_SDRESP2(host)); - cmd->resp[0] = readl(host->base + HW_SSP_SDRESP3(host)); + cmd->resp[3] = readl(ssp->base + HW_SSP_SDRESP0(ssp)); + cmd->resp[2] = readl(ssp->base + HW_SSP_SDRESP1(ssp)); + cmd->resp[1] = readl(ssp->base + HW_SSP_SDRESP2(ssp)); + cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP3(ssp)); } else { - cmd->resp[0] = readl(host->base + HW_SSP_SDRESP0(host)); + cmd->resp[0] = readl(ssp->base + HW_SSP_SDRESP0(ssp)); } } @@ -196,13 +196,14 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id) struct mxs_mmc_host *host = dev_id; struct mmc_command *cmd = host->cmd; struct mmc_data *data = host->data; + struct mxs_ssp *ssp = &host->ssp; u32 stat; spin_lock(&host->lock); - stat = readl(host->base + HW_SSP_CTRL1(host)); + stat = readl(ssp->base + HW_SSP_CTRL1(ssp)); writel(stat & MXS_MMC_IRQ_BITS, - host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR); + ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR); if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN)) mmc_signal_sdio_irq(host->mmc); @@ -366,6 +367,8 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) unsigned int data_size = 0, log2_blksz; unsigned int blocks = data->blocks; + struct mxs_ssp *ssp = &host->ssp; + u32 ignore_crc, get_resp, long_resp, read; u32 ctrl0, cmd0, cmd1, val; @@ -408,15 +411,15 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) blocks = 1; /* xfer count, block size and count need to be set differently */ - if (ssp_is_old(host)) { + if (ssp_is_old(ssp)) { ctrl0 |= BF_SSP(data_size, CTRL0_XFER_COUNT); cmd0 |= BF_SSP(log2_blksz, CMD0_BLOCK_SIZE) | BF_SSP(blocks - 1, CMD0_BLOCK_COUNT); } else { - writel(data_size, host->base + HW_SSP_XFER_SIZE); + writel(data_size, ssp->base + HW_SSP_XFER_SIZE); writel(BF_SSP(log2_blksz, BLOCK_SIZE_BLOCK_SIZE) | BF_SSP(blocks - 1, BLOCK_SIZE_BLOCK_COUNT), - host->base + HW_SSP_BLOCK_SIZE); + ssp->base + HW_SSP_BLOCK_SIZE); } if ((cmd->opcode == MMC_STOP_TRANSMISSION) || @@ -431,11 +434,11 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) } /* set the timeout count */ - timeout = mxs_ns_to_ssp_ticks(host->clk_rate, data->timeout_ns); - val = readl(host->base + HW_SSP_TIMING(host)); + timeout = mxs_ns_to_ssp_ticks(ssp->clk_rate, data->timeout_ns); + val = readl(ssp->base + HW_SSP_TIMING(ssp)); val &= ~(BM_SSP_TIMING_TIMEOUT); val |= BF_SSP(timeout, TIMING_TIMEOUT); - writel(val, host->base + HW_SSP_TIMING(host)); + writel(val, ssp->base + HW_SSP_TIMING(ssp)); /* pio */ host->ssp_pio_words[0] = ctrl0; @@ -500,11 +503,12 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate) { + struct mxs_ssp *ssp = &host->ssp; unsigned int ssp_clk, ssp_sck; u32 clock_divide, clock_rate; u32 val; - ssp_clk = clk_get_rate(host->clk); + ssp_clk = clk_get_rate(ssp->clk); for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) { clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide); @@ -521,13 +525,13 @@ static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate) ssp_sck = ssp_clk / clock_divide / (1 + clock_rate); - val = readl(host->base + HW_SSP_TIMING(host)); + val = readl(ssp->base + HW_SSP_TIMING(ssp)); val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE); val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE); val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE); - writel(val, host->base + HW_SSP_TIMING(host)); + writel(val, ssp->base + HW_SSP_TIMING(ssp)); - host->clk_rate = ssp_sck; + ssp->clk_rate = ssp_sck; dev_dbg(mmc_dev(host->mmc), "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n", @@ -552,6 +556,7 @@ static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; unsigned long flags; spin_lock_irqsave(&host->lock, flags); @@ -560,19 +565,19 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) if (enable) { writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, - host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); writel(BM_SSP_CTRL1_SDIO_IRQ_EN, - host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET); + ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_SET); - if (readl(host->base + HW_SSP_STATUS(host)) & + if (readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_SDIO_IRQ) mmc_signal_sdio_irq(host->mmc); } else { writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, - host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); writel(BM_SSP_CTRL1_SDIO_IRQ_EN, - host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR); + ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR); } spin_unlock_irqrestore(&host->lock, flags); @@ -635,6 +640,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) dma_cap_mask_t mask; struct regulator *reg_vmmc; enum of_gpio_flags flags; + struct mxs_ssp *ssp; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -648,14 +654,16 @@ static int mxs_mmc_probe(struct platform_device *pdev) return -ENOMEM; host = mmc_priv(mmc); - host->base = devm_request_and_ioremap(&pdev->dev, iores); - if (!host->base) { + ssp = &host->ssp; + ssp->dev = &pdev->dev; + ssp->base = devm_request_and_ioremap(&pdev->dev, iores); + if (!ssp->base) { ret = -EADDRNOTAVAIL; goto out_mmc_free; } if (np) { - host->devid = (enum mxs_ssp_id) of_id->data; + ssp->devid = (enum mxs_ssp_id) of_id->data; /* * TODO: This is a temporary solution and should be changed * to use generic DMA binding later when the helpers get in. @@ -668,7 +676,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) goto out_mmc_free; } } else { - host->devid = pdev->id_entry->driver_data; + ssp->devid = pdev->id_entry->driver_data; host->dma_channel = dmares->start; } @@ -691,12 +699,12 @@ static int mxs_mmc_probe(struct platform_device *pdev) goto out_mmc_free; } - host->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); + ssp->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(ssp->clk)) { + ret = PTR_ERR(ssp->clk); goto out_mmc_free; } - clk_prepare_enable(host->clk); + clk_prepare_enable(ssp->clk); mxs_mmc_reset(host); @@ -741,8 +749,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->max_segs = 52; mmc->max_blk_size = 1 << 0xf; - mmc->max_blk_count = (ssp_is_old(host)) ? 0xff : 0xffffff; - mmc->max_req_size = (ssp_is_old(host)) ? 0xffff : 0xffffffff; + mmc->max_blk_count = (ssp_is_old(ssp)) ? 0xff : 0xffffff; + mmc->max_req_size = (ssp_is_old(ssp)) ? 0xffff : 0xffffffff; mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev); platform_set_drvdata(pdev, mmc); @@ -766,8 +774,8 @@ out_free_dma: if (host->dmach) dma_release_channel(host->dmach); out_clk_put: - clk_disable_unprepare(host->clk); - clk_put(host->clk); + clk_disable_unprepare(ssp->clk); + clk_put(ssp->clk); out_mmc_free: mmc_free_host(mmc); return ret; @@ -777,6 +785,7 @@ static int mxs_mmc_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; mmc_remove_host(mmc); @@ -785,8 +794,8 @@ static int mxs_mmc_remove(struct platform_device *pdev) if (host->dmach) dma_release_channel(host->dmach); - clk_disable_unprepare(host->clk); - clk_put(host->clk); + clk_disable_unprepare(ssp->clk); + clk_put(ssp->clk); mmc_free_host(mmc); @@ -798,11 +807,12 @@ static int mxs_mmc_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; int ret = 0; ret = mmc_suspend_host(mmc); - clk_disable_unprepare(host->clk); + clk_disable_unprepare(ssp->clk); return ret; } @@ -811,9 +821,10 @@ static int mxs_mmc_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxs_mmc_host *host = mmc_priv(mmc); + struct mxs_ssp *ssp = &host->ssp; int ret = 0; - clk_prepare_enable(host->clk); + clk_prepare_enable(ssp->clk); ret = mmc_resume_host(mmc); diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index 7dfa1d7d1a7..475f69fb896 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -128,4 +128,12 @@ enum mxs_ssp_id { IMX28_SSP, }; +struct mxs_ssp { + struct device *dev; + void __iomem *base; + struct clk *clk; + unsigned int clk_rate; + enum mxs_ssp_id devid; +}; + #endif /* __LINUX_SPI_MXS_SPI_H__ */ -- cgit v1.2.3 From 1308239858c33feeeb67003d08c754ee181f33cf Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:10 +0200 Subject: mmc: spi: Pull out the SSP clock configuration function Pull out the MMC clock configuration function and make it into SSP clock configuration function, so it can be used by the SPI driver too. Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/clk/mxs/Makefile | 2 +- drivers/clk/mxs/clk-ssp.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/mxs-mmc.c | 39 +--------------------------- include/linux/spi/mxs-spi.h | 2 ++ 4 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 drivers/clk/mxs/clk-ssp.c diff --git a/drivers/clk/mxs/Makefile b/drivers/clk/mxs/Makefile index 7bedeec0852..a6a22237e86 100644 --- a/drivers/clk/mxs/Makefile +++ b/drivers/clk/mxs/Makefile @@ -2,7 +2,7 @@ # Makefile for mxs specific clk # -obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o +obj-y += clk.o clk-pll.o clk-ref.o clk-div.o clk-frac.o clk-ssp.o obj-$(CONFIG_SOC_IMX23) += clk-imx23.o obj-$(CONFIG_SOC_IMX28) += clk-imx28.o diff --git a/drivers/clk/mxs/clk-ssp.c b/drivers/clk/mxs/clk-ssp.c new file mode 100644 index 00000000000..af7bdbf9ebd --- /dev/null +++ b/drivers/clk/mxs/clk-ssp.c @@ -0,0 +1,62 @@ +/* + * Copyright 2012 DENX Software Engineering, GmbH + * + * Pulled from code: + * Portions copyright (C) 2003 Russell King, PXA MMCI Driver + * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver + * + * Copyright 2008 Embedded Alley Solutions, Inc. + * Copyright 2009-2011 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include + +void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate) +{ + unsigned int ssp_clk, ssp_sck; + u32 clock_divide, clock_rate; + u32 val; + + ssp_clk = clk_get_rate(ssp->clk); + + for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) { + clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide); + clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0; + if (clock_rate <= 255) + break; + } + + if (clock_divide > 254) { + dev_err(ssp->dev, + "%s: cannot set clock to %d\n", __func__, rate); + return; + } + + ssp_sck = ssp_clk / clock_divide / (1 + clock_rate); + + val = readl(ssp->base + HW_SSP_TIMING(ssp)); + val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE); + val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE); + val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE); + writel(val, ssp->base + HW_SSP_TIMING(ssp)); + + ssp->clk_rate = ssp_sck; + + dev_dbg(ssp->dev, + "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n", + __func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate); +} +EXPORT_SYMBOL_GPL(mxs_ssp_set_clk_rate); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 7b85e035a4c..0813340fa29 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -501,43 +501,6 @@ static void mxs_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) mxs_mmc_start_cmd(host, mrq->cmd); } -static void mxs_mmc_set_clk_rate(struct mxs_mmc_host *host, unsigned int rate) -{ - struct mxs_ssp *ssp = &host->ssp; - unsigned int ssp_clk, ssp_sck; - u32 clock_divide, clock_rate; - u32 val; - - ssp_clk = clk_get_rate(ssp->clk); - - for (clock_divide = 2; clock_divide <= 254; clock_divide += 2) { - clock_rate = DIV_ROUND_UP(ssp_clk, rate * clock_divide); - clock_rate = (clock_rate > 0) ? clock_rate - 1 : 0; - if (clock_rate <= 255) - break; - } - - if (clock_divide > 254) { - dev_err(mmc_dev(host->mmc), - "%s: cannot set clock to %d\n", __func__, rate); - return; - } - - ssp_sck = ssp_clk / clock_divide / (1 + clock_rate); - - val = readl(ssp->base + HW_SSP_TIMING(ssp)); - val &= ~(BM_SSP_TIMING_CLOCK_DIVIDE | BM_SSP_TIMING_CLOCK_RATE); - val |= BF_SSP(clock_divide, TIMING_CLOCK_DIVIDE); - val |= BF_SSP(clock_rate, TIMING_CLOCK_RATE); - writel(val, ssp->base + HW_SSP_TIMING(ssp)); - - ssp->clk_rate = ssp_sck; - - dev_dbg(mmc_dev(host->mmc), - "%s: clock_divide %d, clock_rate %d, ssp_clk %d, rate_actual %d, rate_requested %d\n", - __func__, clock_divide, clock_rate, ssp_clk, ssp_sck, rate); -} - static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mxs_mmc_host *host = mmc_priv(mmc); @@ -550,7 +513,7 @@ static void mxs_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->bus_width = 0; if (ios->clock) - mxs_mmc_set_clk_rate(host, ios->clock); + mxs_ssp_set_clk_rate(&host->ssp, ios->clock); } static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index 475f69fb896..d07f8dc7fdd 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -136,4 +136,6 @@ struct mxs_ssp { enum mxs_ssp_id devid; }; +void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate); + #endif /* __LINUX_SPI_MXS_SPI_H__ */ -- cgit v1.2.3 From 646781d3325cdcc648b8eeb3b7cda393bcb78659 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:11 +0200 Subject: spi/mxs: Add SPI driver for mx233/mx28 This is slightly reworked version of the SPI driver. Support for DT has been added and it's been converted to queued API. Based on previous attempt by: Fabio Estevam Signed-off-by: Fabio Estevam Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-mxs.c | 431 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 439 insertions(+) create mode 100644 drivers/spi/spi-mxs.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5f84b5563c2..6992b30b4e4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -364,6 +364,13 @@ config SPI_STMP3XXX help SPI driver for Freescale STMP37xx/378x SoC SSP interface +config SPI_MXS + tristate "Freescale MXS SPI controller" + depends on ARCH_MXS + select STMP_DEVICE + help + SPI driver for Freescale MXS devices. + config SPI_TEGRA tristate "Nvidia Tegra SPI controller" depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3920dcf4c74..3b72d87b330 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o +obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c new file mode 100644 index 00000000000..7bf826f5af5 --- /dev/null +++ b/drivers/spi/spi-mxs.c @@ -0,0 +1,431 @@ +/* + * Freescale MXS SPI master driver + * + * Copyright 2012 DENX Software Engineering, GmbH. + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * Rework and transition to new API by: + * Marek Vasut + * + * Based on previous attempt by: + * Fabio Estevam + * + * Based on code from U-Boot bootloader by: + * Marek Vasut + * + * Based on spi-stmp.c, which is: + * Author: Dmitry Pervushin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "mxs-spi" + +#define SSP_TIMEOUT 1000 /* 1000 ms */ + +struct mxs_spi { + struct mxs_ssp ssp; +}; + +static int mxs_spi_setup_transfer(struct spi_device *dev, + struct spi_transfer *t) +{ + struct mxs_spi *spi = spi_master_get_devdata(dev->master); + struct mxs_ssp *ssp = &spi->ssp; + uint8_t bits_per_word; + uint32_t hz = 0; + + bits_per_word = dev->bits_per_word; + if (t && t->bits_per_word) + bits_per_word = t->bits_per_word; + + if (bits_per_word != 8) { + dev_err(&dev->dev, "%s, unsupported bits_per_word=%d\n", + __func__, bits_per_word); + return -EINVAL; + } + + hz = dev->max_speed_hz; + if (t && t->speed_hz) + hz = min(hz, t->speed_hz); + if (hz == 0) { + dev_err(&dev->dev, "Cannot continue with zero clock\n"); + return -EINVAL; + } + + mxs_ssp_set_clk_rate(ssp, hz); + + writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) | + BF_SSP_CTRL1_WORD_LENGTH + (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | + ((dev->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | + ((dev->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), + ssp->base + HW_SSP_CTRL1(ssp)); + + writel(0x0, ssp->base + HW_SSP_CMD0); + writel(0x0, ssp->base + HW_SSP_CMD1); + + return 0; +} + +static int mxs_spi_setup(struct spi_device *dev) +{ + int err = 0; + + if (!dev->bits_per_word) + dev->bits_per_word = 8; + + if (dev->mode & ~(SPI_CPOL | SPI_CPHA)) + return -EINVAL; + + err = mxs_spi_setup_transfer(dev, NULL); + if (err) { + dev_err(&dev->dev, + "Failed to setup transfer, error = %d\n", err); + } + + return err; +} + +static uint32_t mxs_spi_cs_to_reg(unsigned cs) +{ + uint32_t select = 0; + + /* + * i.MX28 Datasheet: 17.10.1: HW_SSP_CTRL0 + * + * The bits BM_SSP_CTRL0_WAIT_FOR_CMD and BM_SSP_CTRL0_WAIT_FOR_IRQ + * in HW_SSP_CTRL0 register do have multiple usage, please refer to + * the datasheet for further details. In SPI mode, they are used to + * toggle the chip-select lines (nCS pins). + */ + if (cs & 1) + select |= BM_SSP_CTRL0_WAIT_FOR_CMD; + if (cs & 2) + select |= BM_SSP_CTRL0_WAIT_FOR_IRQ; + + return select; +} + +static void mxs_spi_set_cs(struct mxs_spi *spi, unsigned cs) +{ + const uint32_t mask = + BM_SSP_CTRL0_WAIT_FOR_CMD | BM_SSP_CTRL0_WAIT_FOR_IRQ; + uint32_t select; + struct mxs_ssp *ssp = &spi->ssp; + + writel(mask, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + select = mxs_spi_cs_to_reg(cs); + writel(select, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); +} + +static inline void mxs_spi_enable(struct mxs_spi *spi) +{ + struct mxs_ssp *ssp = &spi->ssp; + + writel(BM_SSP_CTRL0_LOCK_CS, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + writel(BM_SSP_CTRL0_IGNORE_CRC, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); +} + +static inline void mxs_spi_disable(struct mxs_spi *spi) +{ + struct mxs_ssp *ssp = &spi->ssp; + + writel(BM_SSP_CTRL0_LOCK_CS, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + writel(BM_SSP_CTRL0_IGNORE_CRC, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); +} + +static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); + struct mxs_ssp *ssp = &spi->ssp; + uint32_t reg; + + while (1) { + reg = readl_relaxed(ssp->base + offset); + + if (set && ((reg & mask) == mask)) + break; + + if (!set && ((~reg & mask) == mask)) + break; + + udelay(1); + + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + return 0; +} + +static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, + unsigned char *buf, int len, + int *first, int *last, int write) +{ + struct mxs_ssp *ssp = &spi->ssp; + + if (*first) + mxs_spi_enable(spi); + + mxs_spi_set_cs(spi, cs); + + while (len--) { + if (*last && len == 0) + mxs_spi_disable(spi); + + if (ssp->devid == IMX23_SSP) { + writel(BM_SSP_CTRL0_XFER_COUNT, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + writel(1, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + } else { + writel(1, ssp->base + HW_SSP_XFER_SIZE); + } + + if (write) + writel(BM_SSP_CTRL0_READ, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); + else + writel(BM_SSP_CTRL0_READ, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + + writel(BM_SSP_CTRL0_RUN, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + + if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 1)) + return -ETIMEDOUT; + + if (write) + writel(*buf, ssp->base + HW_SSP_DATA(ssp)); + + writel(BM_SSP_CTRL0_DATA_XFER, + ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); + + if (!write) { + if (mxs_ssp_wait(spi, HW_SSP_STATUS(ssp), + BM_SSP_STATUS_FIFO_EMPTY, 0)) + return -ETIMEDOUT; + + *buf = (readl(ssp->base + HW_SSP_DATA(ssp)) & 0xff); + } + + if (mxs_ssp_wait(spi, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN, 0)) + return -ETIMEDOUT; + + buf++; + } + + if (len <= 0) + return 0; + + return -ETIMEDOUT; +} + +static int mxs_spi_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct mxs_spi *spi = spi_master_get_devdata(master); + struct mxs_ssp *ssp = &spi->ssp; + int first, last; + struct spi_transfer *t, *tmp_t; + int status = 0; + int cs; + + first = last = 0; + + cs = m->spi->chip_select; + + list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { + + status = mxs_spi_setup_transfer(m->spi, t); + if (status) + break; + + if (&t->transfer_list == m->transfers.next) + first = 1; + if (&t->transfer_list == m->transfers.prev) + last = 1; + if (t->rx_buf && t->tx_buf) { + dev_err(ssp->dev, + "Cannot send and receive simultaneously\n"); + status = -EINVAL; + break; + } + + if (t->tx_buf) + status = mxs_spi_txrx_pio(spi, cs, (void *)t->tx_buf, + t->len, &first, &last, 1); + if (t->rx_buf) + status = mxs_spi_txrx_pio(spi, cs, t->rx_buf, + t->len, &first, &last, 0); + + m->actual_length += t->len; + if (status) + break; + + first = last = 0; + } + + m->status = 0; + spi_finalize_current_message(master); + + return status; +} + +static const struct of_device_id mxs_spi_dt_ids[] = { + { .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, }, + { .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_spi_dt_ids); + +static int __devinit mxs_spi_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(mxs_spi_dt_ids, &pdev->dev); + struct device_node *np = pdev->dev.of_node; + struct spi_master *master; + struct mxs_spi *spi; + struct mxs_ssp *ssp; + struct resource *iores; + struct pinctrl *pinctrl; + struct clk *clk; + void __iomem *base; + int devid; + int ret = 0; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -EINVAL; + + base = devm_request_and_ioremap(&pdev->dev, iores); + if (!base) + return -EADDRNOTAVAIL; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (np) + devid = (enum mxs_ssp_id) of_id->data; + else + devid = pdev->id_entry->driver_data; + + master = spi_alloc_master(&pdev->dev, sizeof(*spi)); + if (!master) + return -ENOMEM; + + master->transfer_one_message = mxs_spi_transfer_one; + master->setup = mxs_spi_setup; + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->num_chipselect = 3; + master->dev.of_node = np; + master->flags = SPI_MASTER_HALF_DUPLEX; + + spi = spi_master_get_devdata(master); + ssp = &spi->ssp; + ssp->dev = &pdev->dev; + ssp->clk = clk; + ssp->base = base; + ssp->devid = devid; + + clk_prepare_enable(ssp->clk); + ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; + + stmp_reset_block(ssp->base); + + platform_set_drvdata(pdev, master); + + ret = spi_register_master(master); + if (ret) { + dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); + goto out_master_free; + } + + return 0; + +out_master_free: + platform_set_drvdata(pdev, NULL); + clk_disable_unprepare(ssp->clk); + spi_master_put(master); + return ret; +} + +static int __devexit mxs_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master; + struct mxs_spi *spi; + struct mxs_ssp *ssp; + + master = platform_get_drvdata(pdev); + spi = spi_master_get_devdata(master); + ssp = &spi->ssp; + + spi_unregister_master(master); + + platform_set_drvdata(pdev, NULL); + + clk_disable_unprepare(ssp->clk); + + spi_master_put(master); + + return 0; +} + +static struct platform_driver mxs_spi_driver = { + .probe = mxs_spi_probe, + .remove = __devexit_p(mxs_spi_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = mxs_spi_dt_ids, + }, +}; + +module_platform_driver(mxs_spi_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("MXS SPI master driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mxs-spi"); -- cgit v1.2.3 From 65defb9b3ba67c1d6f88ac62c24644eb23a7b676 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:12 +0200 Subject: mmc: spi: Pull out common DMA parts from MXS MMC These parts will be used by the MXS SPI driver too. Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/mmc/host/mxs-mmc.c | 88 ++++++++++++++++++++++----------------------- drivers/spi/spi-mxs.c | 1 - include/linux/spi/mxs-spi.h | 9 +++++ 3 files changed, 51 insertions(+), 47 deletions(-) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 0813340fa29..4da99665477 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -69,13 +68,6 @@ struct mxs_mmc_host { struct mmc_command *cmd; struct mmc_data *data; - int dma_channel; - struct dma_chan *dmach; - struct mxs_dma_data dma_data; - unsigned int dma_dir; - enum dma_transfer_direction slave_dirn; - u32 ssp_pio_words[SSP_PIO_NUM]; - unsigned char bus_width; spinlock_t lock; int sdio_irq_en; @@ -163,7 +155,7 @@ static void mxs_mmc_request_done(struct mxs_mmc_host *host) if (data) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); + data->sg_len, ssp->dma_dir); /* * If there was an error on any block, we mark all * data blocks as being in error. @@ -232,6 +224,7 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id) static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( struct mxs_mmc_host *host, unsigned long flags) { + struct mxs_ssp *ssp = &host->ssp; struct dma_async_tx_descriptor *desc; struct mmc_data *data = host->data; struct scatterlist * sgl; @@ -240,24 +233,24 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( if (data) { /* data */ dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); + data->sg_len, ssp->dma_dir); sgl = data->sg; sg_len = data->sg_len; } else { /* pio */ - sgl = (struct scatterlist *) host->ssp_pio_words; + sgl = (struct scatterlist *) ssp->ssp_pio_words; sg_len = SSP_PIO_NUM; } - desc = dmaengine_prep_slave_sg(host->dmach, - sgl, sg_len, host->slave_dirn, flags); + desc = dmaengine_prep_slave_sg(ssp->dmach, + sgl, sg_len, ssp->slave_dirn, flags); if (desc) { desc->callback = mxs_mmc_dma_irq_callback; desc->callback_param = host; } else { if (data) dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); + data->sg_len, ssp->dma_dir); } return desc; @@ -265,6 +258,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( static void mxs_mmc_bc(struct mxs_mmc_host *host) { + struct mxs_ssp *ssp = &host->ssp; struct mmc_command *cmd = host->cmd; struct dma_async_tx_descriptor *desc; u32 ctrl0, cmd0, cmd1; @@ -278,17 +272,17 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host) cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN; } - host->ssp_pio_words[0] = ctrl0; - host->ssp_pio_words[1] = cmd0; - host->ssp_pio_words[2] = cmd1; - host->dma_dir = DMA_NONE; - host->slave_dirn = DMA_TRANS_NONE; + ssp->ssp_pio_words[0] = ctrl0; + ssp->ssp_pio_words[1] = cmd0; + ssp->ssp_pio_words[2] = cmd1; + ssp->dma_dir = DMA_NONE; + ssp->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK); if (!desc) goto out; dmaengine_submit(desc); - dma_async_issue_pending(host->dmach); + dma_async_issue_pending(ssp->dmach); return; out: @@ -298,6 +292,7 @@ out: static void mxs_mmc_ac(struct mxs_mmc_host *host) { + struct mxs_ssp *ssp = &host->ssp; struct mmc_command *cmd = host->cmd; struct dma_async_tx_descriptor *desc; u32 ignore_crc, get_resp, long_resp; @@ -319,17 +314,17 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host) cmd0 |= BM_SSP_CMD0_CONT_CLKING_EN | BM_SSP_CMD0_SLOW_CLKING_EN; } - host->ssp_pio_words[0] = ctrl0; - host->ssp_pio_words[1] = cmd0; - host->ssp_pio_words[2] = cmd1; - host->dma_dir = DMA_NONE; - host->slave_dirn = DMA_TRANS_NONE; + ssp->ssp_pio_words[0] = ctrl0; + ssp->ssp_pio_words[1] = cmd0; + ssp->ssp_pio_words[2] = cmd1; + ssp->dma_dir = DMA_NONE; + ssp->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK); if (!desc) goto out; dmaengine_submit(desc); - dma_async_issue_pending(host->dmach); + dma_async_issue_pending(ssp->dmach); return; out: @@ -441,11 +436,11 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) writel(val, ssp->base + HW_SSP_TIMING(ssp)); /* pio */ - host->ssp_pio_words[0] = ctrl0; - host->ssp_pio_words[1] = cmd0; - host->ssp_pio_words[2] = cmd1; - host->dma_dir = DMA_NONE; - host->slave_dirn = DMA_TRANS_NONE; + ssp->ssp_pio_words[0] = ctrl0; + ssp->ssp_pio_words[1] = cmd0; + ssp->ssp_pio_words[2] = cmd1; + ssp->dma_dir = DMA_NONE; + ssp->slave_dirn = DMA_TRANS_NONE; desc = mxs_mmc_prep_dma(host, 0); if (!desc) goto out; @@ -453,14 +448,14 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) /* append data sg */ WARN_ON(host->data != NULL); host->data = data; - host->dma_dir = dma_data_dir; - host->slave_dirn = slave_dirn; + ssp->dma_dir = dma_data_dir; + ssp->slave_dirn = slave_dirn; desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) goto out; dmaengine_submit(desc); - dma_async_issue_pending(host->dmach); + dma_async_issue_pending(ssp->dmach); return; out: dev_warn(mmc_dev(host->mmc), @@ -557,14 +552,15 @@ static const struct mmc_host_ops mxs_mmc_ops = { static bool mxs_mmc_dma_filter(struct dma_chan *chan, void *param) { struct mxs_mmc_host *host = param; + struct mxs_ssp *ssp = &host->ssp; if (!mxs_dma_is_apbh(chan)) return false; - if (chan->chan_id != host->dma_channel) + if (chan->chan_id != ssp->dma_channel) return false; - chan->private = &host->dma_data; + chan->private = &ssp->dma_data; return true; } @@ -632,7 +628,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) * to use generic DMA binding later when the helpers get in. */ ret = of_property_read_u32(np, "fsl,ssp-dma-channel", - &host->dma_channel); + &ssp->dma_channel); if (ret) { dev_err(mmc_dev(host->mmc), "failed to get dma channel\n"); @@ -640,7 +636,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) } } else { ssp->devid = pdev->id_entry->driver_data; - host->dma_channel = dmares->start; + ssp->dma_channel = dmares->start; } host->mmc = mmc; @@ -673,9 +669,9 @@ static int mxs_mmc_probe(struct platform_device *pdev) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - host->dma_data.chan_irq = irq_dma; - host->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host); - if (!host->dmach) { + ssp->dma_data.chan_irq = irq_dma; + ssp->dmach = dma_request_channel(mask, mxs_mmc_dma_filter, host); + if (!ssp->dmach) { dev_err(mmc_dev(host->mmc), "%s: failed to request dma\n", __func__); goto out_clk_put; @@ -714,7 +710,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->max_blk_size = 1 << 0xf; mmc->max_blk_count = (ssp_is_old(ssp)) ? 0xff : 0xffffff; mmc->max_req_size = (ssp_is_old(ssp)) ? 0xffff : 0xffffffff; - mmc->max_seg_size = dma_get_max_seg_size(host->dmach->device->dev); + mmc->max_seg_size = dma_get_max_seg_size(ssp->dmach->device->dev); platform_set_drvdata(pdev, mmc); @@ -734,8 +730,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) return 0; out_free_dma: - if (host->dmach) - dma_release_channel(host->dmach); + if (ssp->dmach) + dma_release_channel(ssp->dmach); out_clk_put: clk_disable_unprepare(ssp->clk); clk_put(ssp->clk); @@ -754,8 +750,8 @@ static int mxs_mmc_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - if (host->dmach) - dma_release_channel(host->dmach); + if (ssp->dmach) + dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); clk_put(ssp->clk); diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 7bf826f5af5..0f28afb8031 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index d07f8dc7fdd..61ae1306db2 100644 --- a/include/linux/spi/mxs-spi.h +++ b/include/linux/spi/mxs-spi.h @@ -24,6 +24,8 @@ #ifndef __LINUX_SPI_MXS_SPI_H__ #define __LINUX_SPI_MXS_SPI_H__ +#include + #define ssp_is_old(host) ((host)->devid == IMX23_SSP) /* SSP registers */ @@ -134,6 +136,13 @@ struct mxs_ssp { struct clk *clk; unsigned int clk_rate; enum mxs_ssp_id devid; + + int dma_channel; + struct dma_chan *dmach; + struct mxs_dma_data dma_data; + unsigned int dma_dir; + enum dma_transfer_direction slave_dirn; + u32 ssp_pio_words[SSP_PIO_NUM]; }; void mxs_ssp_set_clk_rate(struct mxs_ssp *ssp, unsigned int rate); -- cgit v1.2.3 From 474afc042fb9db8f88b68243f78a38cb764692fc Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:13 +0200 Subject: spi/mxs: Add DMA support into SPI driver Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 216 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 0f28afb8031..130a4368835 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -55,8 +55,12 @@ #define SSP_TIMEOUT 1000 /* 1000 ms */ +#define SG_NUM 4 +#define SG_MAXLEN 0xff00 + struct mxs_spi { struct mxs_ssp ssp; + struct completion c; }; static int mxs_spi_setup_transfer(struct spi_device *dev, @@ -194,6 +198,115 @@ static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) return 0; } +static void mxs_ssp_dma_irq_callback(void *param) +{ + struct mxs_spi *spi = param; + complete(&spi->c); +} + +static irqreturn_t mxs_ssp_irq_handler(int irq, void *dev_id) +{ + struct mxs_ssp *ssp = dev_id; + dev_err(ssp->dev, "%s[%i] CTRL1=%08x STATUS=%08x\n", + __func__, __LINE__, + readl(ssp->base + HW_SSP_CTRL1(ssp)), + readl(ssp->base + HW_SSP_STATUS(ssp))); + return IRQ_HANDLED; +} + +static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, + unsigned char *buf, int len, + int *first, int *last, int write) +{ + struct mxs_ssp *ssp = &spi->ssp; + struct dma_async_tx_descriptor *desc; + struct scatterlist sg[SG_NUM]; + int sg_count; + uint32_t pio = BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); + int ret; + + if (len > SG_NUM * SG_MAXLEN) { + dev_err(ssp->dev, "Data chunk too big for DMA\n"); + return -EINVAL; + } + + init_completion(&spi->c); + + if (*first) + pio |= BM_SSP_CTRL0_LOCK_CS; + if (*last) + pio |= BM_SSP_CTRL0_IGNORE_CRC; + if (!write) + pio |= BM_SSP_CTRL0_READ; + + if (ssp->devid == IMX23_SSP) + pio |= len; + else + writel(len, ssp->base + HW_SSP_XFER_SIZE); + + /* Queue the PIO register write transfer. */ + desc = dmaengine_prep_slave_sg(ssp->dmach, + (struct scatterlist *)&pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(ssp->dev, + "Failed to get PIO reg. write descriptor.\n"); + return -EINVAL; + } + + /* Queue the DMA data transfer. */ + sg_init_table(sg, (len / SG_MAXLEN) + 1); + sg_count = 0; + while (len) { + sg_set_buf(&sg[sg_count++], buf, min(len, SG_MAXLEN)); + len -= min(len, SG_MAXLEN); + buf += min(len, SG_MAXLEN); + } + dma_map_sg(ssp->dev, sg, sg_count, + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + desc = dmaengine_prep_slave_sg(ssp->dmach, sg, sg_count, + write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!desc) { + dev_err(ssp->dev, + "Failed to get DMA data write descriptor.\n"); + ret = -EINVAL; + goto err; + } + + /* + * The last descriptor must have this callback, + * to finish the DMA transaction. + */ + desc->callback = mxs_ssp_dma_irq_callback; + desc->callback_param = spi; + + /* Start the transfer. */ + dmaengine_submit(desc); + dma_async_issue_pending(ssp->dmach); + + ret = wait_for_completion_timeout(&spi->c, + msecs_to_jiffies(SSP_TIMEOUT)); + + if (!ret) { + dev_err(ssp->dev, "DMA transfer timeout\n"); + ret = -ETIMEDOUT; + goto err; + } + + ret = 0; + +err: + for (--sg_count; sg_count >= 0; sg_count--) { + dma_unmap_sg(ssp->dev, &sg[sg_count], 1, + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } + + return ret; +} + static int mxs_spi_txrx_pio(struct mxs_spi *spi, int cs, unsigned char *buf, int len, int *first, int *last, int write) @@ -281,19 +394,49 @@ static int mxs_spi_transfer_one(struct spi_master *master, first = 1; if (&t->transfer_list == m->transfers.prev) last = 1; - if (t->rx_buf && t->tx_buf) { + if ((t->rx_buf && t->tx_buf) || (t->rx_dma && t->tx_dma)) { dev_err(ssp->dev, "Cannot send and receive simultaneously\n"); status = -EINVAL; break; } - if (t->tx_buf) - status = mxs_spi_txrx_pio(spi, cs, (void *)t->tx_buf, - t->len, &first, &last, 1); - if (t->rx_buf) - status = mxs_spi_txrx_pio(spi, cs, t->rx_buf, - t->len, &first, &last, 0); + /* + * Small blocks can be transfered via PIO. + * Measured by empiric means: + * + * dd if=/dev/mtdblock0 of=/dev/null bs=1024k count=1 + * + * DMA only: 2.164808 seconds, 473.0KB/s + * Combined: 1.676276 seconds, 610.9KB/s + */ + if (t->len <= 256) { + writel(BM_SSP_CTRL1_DMA_ENABLE, + ssp->base + HW_SSP_CTRL1(ssp) + + STMP_OFFSET_REG_CLR); + + if (t->tx_buf) + status = mxs_spi_txrx_pio(spi, cs, + (void *)t->tx_buf, + t->len, &first, &last, 1); + if (t->rx_buf) + status = mxs_spi_txrx_pio(spi, cs, + t->rx_buf, t->len, + &first, &last, 0); + } else { + writel(BM_SSP_CTRL1_DMA_ENABLE, + ssp->base + HW_SSP_CTRL1(ssp) + + STMP_OFFSET_REG_SET); + + if (t->tx_buf) + status = mxs_spi_txrx_dma(spi, cs, + (void *)t->tx_buf, t->len, + &first, &last, 1); + if (t->rx_buf) + status = mxs_spi_txrx_dma(spi, cs, + t->rx_buf, t->len, + &first, &last, 0); + } m->actual_length += t->len; if (status) @@ -308,6 +451,21 @@ static int mxs_spi_transfer_one(struct spi_master *master, return status; } +static bool mxs_ssp_dma_filter(struct dma_chan *chan, void *param) +{ + struct mxs_ssp *ssp = param; + + if (!mxs_dma_is_apbh(chan)) + return false; + + if (chan->chan_id != ssp->dma_channel) + return false; + + chan->private = &ssp->dma_data; + + return true; +} + static const struct of_device_id mxs_spi_dt_ids[] = { { .compatible = "fsl,imx23-spi", .data = (void *) IMX23_SSP, }, { .compatible = "fsl,imx28-spi", .data = (void *) IMX28_SSP, }, @@ -323,15 +481,18 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) struct spi_master *master; struct mxs_spi *spi; struct mxs_ssp *ssp; - struct resource *iores; + struct resource *iores, *dmares; struct pinctrl *pinctrl; struct clk *clk; void __iomem *base; - int devid; - int ret = 0; + int devid, dma_channel; + int ret = 0, irq_err, irq_dma; + dma_cap_mask_t mask; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) + irq_err = platform_get_irq(pdev, 0); + irq_dma = platform_get_irq(pdev, 1); + if (!iores || irq_err < 0 || irq_dma < 0) return -EINVAL; base = devm_request_and_ioremap(&pdev->dev, iores); @@ -346,10 +507,26 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - if (np) + if (np) { devid = (enum mxs_ssp_id) of_id->data; - else + /* + * TODO: This is a temporary solution and should be changed + * to use generic DMA binding later when the helpers get in. + */ + ret = of_property_read_u32(np, "fsl,ssp-dma-channel", + &dma_channel); + if (ret) { + dev_err(&pdev->dev, + "Failed to get DMA channel\n"); + return -EINVAL; + } + } else { + dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmares) + return -EINVAL; devid = pdev->id_entry->driver_data; + dma_channel = dmares->start; + } master = spi_alloc_master(&pdev->dev, sizeof(*spi)); if (!master) @@ -368,8 +545,28 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) ssp->clk = clk; ssp->base = base; ssp->devid = devid; + ssp->dma_channel = dma_channel; + + ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0, + DRIVER_NAME, ssp); + if (ret) + goto out_master_free; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + ssp->dma_data.chan_irq = irq_dma; + ssp->dmach = dma_request_channel(mask, mxs_ssp_dma_filter, ssp); + if (!ssp->dmach) { + dev_err(ssp->dev, "Failed to request DMA\n"); + goto out_master_free; + } + /* + * Crank up the clock to 120MHz, this will be further divided onto a + * proper speed. + */ clk_prepare_enable(ssp->clk); + clk_set_rate(ssp->clk, 120 * 1000 * 1000); ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; stmp_reset_block(ssp->base); @@ -379,14 +576,16 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) ret = spi_register_master(master); if (ret) { dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret); - goto out_master_free; + goto out_free_dma; } return 0; -out_master_free: +out_free_dma: platform_set_drvdata(pdev, NULL); + dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); +out_master_free: spi_master_put(master); return ret; } @@ -405,6 +604,8 @@ static int __devexit mxs_spi_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); + dma_release_channel(ssp->dmach); + clk_disable_unprepare(ssp->clk); spi_master_put(master); -- cgit v1.2.3 From 4ad1e5b5d1dde19f09e728ba8b5164968f9dabeb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 3 Aug 2012 17:26:14 +0200 Subject: spi/mxs: Add SSP/SPI device tree documentation Signed-off-by: Marek Vasut Acked-by: Chris Ball Acked-by: Shawn Guo Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/mxs-spi.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/mxs-spi.txt diff --git a/Documentation/devicetree/bindings/spi/mxs-spi.txt b/Documentation/devicetree/bindings/spi/mxs-spi.txt new file mode 100644 index 00000000000..c36296f5ad2 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/mxs-spi.txt @@ -0,0 +1,18 @@ +* Freescale MX233/MX28 SSP/SPI + +Required properties: +- compatible: Should be "fsl,-spi", where soc is "imx23" or "imx28" +- reg: Offset and length of the register set for the device +- interrupts: Should contain SSP interrupts (error irq first, dma irq second) +- fsl,ssp-dma-channel: APBX DMA channel for the SSP + +Example: + +ssp0: ssp@80010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx28-spi"; + reg = <0x80010000 0x2000>; + interrupts = <96 82>; + fsl,ssp-dma-channel = <0>; +}; -- cgit v1.2.3 From ba59a807883382f6ce5ad884312d91d5748e90ee Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Mon, 23 Jul 2012 13:16:55 +0200 Subject: spi: Refactor spi-orion to use SPI framework queue. Replace the deprecated master->transfer with transfer_one_message() and allow the SPI subsystem handle all the queuing of messages. Signed-off-by: Andrew Lunn Acked-by: Linus Walleij Acked-by: Sebastian Hesselbarth Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 209 ++++++++++++++---------------------------------- 1 file changed, 61 insertions(+), 148 deletions(-) diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 9b0caddce50..b17c09cf0a0 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -36,12 +36,6 @@ #define ORION_SPI_CLK_PRESCALE_MASK 0x1F struct orion_spi { - struct work_struct work; - - /* Lock access to transfer list. */ - spinlock_t lock; - - struct list_head msg_queue; struct spi_master *master; void __iomem *base; unsigned int max_speed; @@ -49,8 +43,6 @@ struct orion_spi { struct clk *clk; }; -static struct workqueue_struct *orion_spi_wq; - static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) { return orion_spi->base + reg; @@ -277,73 +269,78 @@ out: } -static void orion_spi_work(struct work_struct *work) +static int orion_spi_transfer_one_message(struct spi_master *master, + struct spi_message *m) { - struct orion_spi *orion_spi = - container_of(work, struct orion_spi, work); - - spin_lock_irq(&orion_spi->lock); - while (!list_empty(&orion_spi->msg_queue)) { - struct spi_message *m; - struct spi_device *spi; - struct spi_transfer *t = NULL; - int par_override = 0; - int status = 0; - int cs_active = 0; - - m = container_of(orion_spi->msg_queue.next, struct spi_message, - queue); + struct orion_spi *orion_spi = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + struct spi_transfer *t = NULL; + int par_override = 0; + int status = 0; + int cs_active = 0; - list_del_init(&m->queue); - spin_unlock_irq(&orion_spi->lock); + /* Load defaults */ + status = orion_spi_setup_transfer(spi, NULL); - spi = m->spi; + if (status < 0) + goto msg_done; - /* Load defaults */ - status = orion_spi_setup_transfer(spi, NULL); + list_for_each_entry(t, &m->transfers, transfer_list) { + /* make sure buffer length is even when working in 16 + * bit mode*/ + if ((t->bits_per_word == 16) && (t->len & 1)) { + dev_err(&spi->dev, + "message rejected : " + "odd data length %d while in 16 bit mode\n", + t->len); + status = -EIO; + goto msg_done; + } - if (status < 0) + if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { + dev_err(&spi->dev, + "message rejected : " + "device min speed (%d Hz) exceeds " + "required transfer speed (%d Hz)\n", + orion_spi->min_speed, t->speed_hz); + status = -EIO; goto msg_done; + } - list_for_each_entry(t, &m->transfers, transfer_list) { - if (par_override || t->speed_hz || t->bits_per_word) { - par_override = 1; - status = orion_spi_setup_transfer(spi, t); - if (status < 0) - break; - if (!t->speed_hz && !t->bits_per_word) - par_override = 0; - } - - if (!cs_active) { - orion_spi_set_cs(orion_spi, 1); - cs_active = 1; - } - - if (t->len) - m->actual_length += - orion_spi_write_read(spi, t); - - if (t->delay_usecs) - udelay(t->delay_usecs); - - if (t->cs_change) { - orion_spi_set_cs(orion_spi, 0); - cs_active = 0; - } + if (par_override || t->speed_hz || t->bits_per_word) { + par_override = 1; + status = orion_spi_setup_transfer(spi, t); + if (status < 0) + break; + if (!t->speed_hz && !t->bits_per_word) + par_override = 0; } -msg_done: - if (cs_active) - orion_spi_set_cs(orion_spi, 0); + if (!cs_active) { + orion_spi_set_cs(orion_spi, 1); + cs_active = 1; + } - m->status = status; - m->complete(m->context); + if (t->len) + m->actual_length += orion_spi_write_read(spi, t); - spin_lock_irq(&orion_spi->lock); + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (t->cs_change) { + orion_spi_set_cs(orion_spi, 0); + cs_active = 0; + } } - spin_unlock_irq(&orion_spi->lock); +msg_done: + if (cs_active) + orion_spi_set_cs(orion_spi, 0); + + m->status = status; + spi_finalize_current_message(master); + + return 0; } static int __init orion_spi_reset(struct orion_spi *orion_spi) @@ -376,75 +373,6 @@ static int orion_spi_setup(struct spi_device *spi) return 0; } -static int orion_spi_transfer(struct spi_device *spi, struct spi_message *m) -{ - struct orion_spi *orion_spi; - struct spi_transfer *t = NULL; - unsigned long flags; - - m->actual_length = 0; - m->status = 0; - - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers) || !m->complete) - return -EINVAL; - - orion_spi = spi_master_get_devdata(spi->master); - - list_for_each_entry(t, &m->transfers, transfer_list) { - unsigned int bits_per_word = spi->bits_per_word; - - if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { - dev_err(&spi->dev, - "message rejected : " - "invalid transfer data buffers\n"); - goto msg_rejected; - } - - if (t->bits_per_word) - bits_per_word = t->bits_per_word; - - if ((bits_per_word != 8) && (bits_per_word != 16)) { - dev_err(&spi->dev, - "message rejected : " - "invalid transfer bits_per_word (%d bits)\n", - bits_per_word); - goto msg_rejected; - } - /*make sure buffer length is even when working in 16 bit mode*/ - if ((t->bits_per_word == 16) && (t->len & 1)) { - dev_err(&spi->dev, - "message rejected : " - "odd data length (%d) while in 16 bit mode\n", - t->len); - goto msg_rejected; - } - - if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { - dev_err(&spi->dev, - "message rejected : " - "device min speed (%d Hz) exceeds " - "required transfer speed (%d Hz)\n", - orion_spi->min_speed, t->speed_hz); - goto msg_rejected; - } - } - - - spin_lock_irqsave(&orion_spi->lock, flags); - list_add_tail(&m->queue, &orion_spi->msg_queue); - queue_work(orion_spi_wq, &orion_spi->work); - spin_unlock_irqrestore(&orion_spi->lock, flags); - - return 0; -msg_rejected: - /* Message rejected and not queued */ - m->status = -EINVAL; - if (m->complete) - m->complete(m->context); - return -EINVAL; -} - static int __init orion_spi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -474,7 +402,7 @@ static int __init orion_spi_probe(struct platform_device *pdev) master->mode_bits = 0; master->setup = orion_spi_setup; - master->transfer = orion_spi_transfer; + master->transfer_one_message = orion_spi_transfer_one_message; master->num_chipselect = ORION_NUM_CHIPSELECTS; dev_set_drvdata(&pdev->dev, master); @@ -507,11 +435,6 @@ static int __init orion_spi_probe(struct platform_device *pdev) } spi->base = ioremap(r->start, SZ_1K); - INIT_WORK(&spi->work, orion_spi_work); - - spin_lock_init(&spi->lock); - INIT_LIST_HEAD(&spi->msg_queue); - if (orion_spi_reset(spi) < 0) goto out_rel_mem; @@ -536,14 +459,12 @@ out: static int __exit orion_spi_remove(struct platform_device *pdev) { struct spi_master *master; - struct orion_spi *spi; struct resource *r; + struct orion_spi *spi; master = dev_get_drvdata(&pdev->dev); spi = spi_master_get_devdata(master); - cancel_work_sync(&spi->work); - clk_disable_unprepare(spi->clk); clk_put(spi->clk); @@ -574,21 +495,13 @@ static struct platform_driver orion_spi_driver = { static int __init orion_spi_init(void) { - orion_spi_wq = create_singlethread_workqueue( - orion_spi_driver.driver.name); - if (orion_spi_wq == NULL) - return -ENOMEM; - return platform_driver_probe(&orion_spi_driver, orion_spi_probe); } module_init(orion_spi_init); static void __exit orion_spi_exit(void) { - flush_workqueue(orion_spi_wq); platform_driver_unregister(&orion_spi_driver); - - destroy_workqueue(orion_spi_wq); } module_exit(orion_spi_exit); -- cgit v1.2.3 From 89f98dc5eaf78a5a6920c54ee8ef8992d35f8c48 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 15 Aug 2012 09:30:28 +0200 Subject: spi: spi-altera: Use of_match_ptr Instead of having to define the match table to NULL if CONFIG_OF isn't set, use the of_match_ptr() macro which will do this for us. Signed-off-by: Tobias Klauser Acked-by: Rob Herring Signed-off-by: Mark Brown --- drivers/spi/spi-altera.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index c00d00e96ee..f1fec2a19d1 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -307,8 +307,6 @@ static const struct of_device_id altera_spi_match[] = { {}, }; MODULE_DEVICE_TABLE(of, altera_spi_match); -#else /* CONFIG_OF */ -#define altera_spi_match NULL #endif /* CONFIG_OF */ static struct platform_driver altera_spi_driver = { @@ -318,7 +316,7 @@ static struct platform_driver altera_spi_driver = { .name = DRV_NAME, .owner = THIS_MODULE, .pm = NULL, - .of_match_table = altera_spi_match, + .of_match_table = of_match_ptr(altera_spi_match), }, }; module_platform_driver(altera_spi_driver); -- cgit v1.2.3 From a8e8f1398e80335b923fe747eab2b7c40d14ae31 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 16 Aug 2012 20:49:30 +0530 Subject: spi: omap2-mcspi: Remove the call to platform_set_drvdata(pdev, NULL) Remove the call of platform_set_drvdata(pdev, NULL) as they are not needed anymore. Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index bc4778175e3..84aeaf0e5a0 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1240,7 +1240,6 @@ dma_chnl_free: kfree(mcspi->dma_channels); free_master: kfree(master); - platform_set_drvdata(pdev, NULL); return status; } @@ -1259,7 +1258,6 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev) spi_unregister_master(master); kfree(dma_channels); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From b2af045c70aa60f7231b22a363d74a1ab5a6297b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 22 Aug 2012 13:42:47 +0200 Subject: drivers/spi/spi-s3c24xx.c: fix error return code Initialize return variable before exiting on an error path. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // ( if@p1 (\(ret < 0\|ret != 0\)) { ... return ret; } | ret@p1 = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 8ee7d790ce4..a2a080b7f42 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -611,6 +611,7 @@ static int __devinit s3c24xx_spi_probe(struct platform_device *pdev) if (!pdata->set_cs) { if (pdata->pin_cs < 0) { dev_err(&pdev->dev, "No chipselect pin\n"); + err = -EINVAL; goto err_register; } -- cgit v1.2.3 From af4e944dbda418034d1b08628b10e753369fbdc7 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 22 Aug 2012 11:35:13 +0530 Subject: spi: omap2-mcspi: Remove the macro MOD_REG_BIT Remove the macro MOD_REG_BIT instead make the bit field modifications directly. This deletes a branch operation in cases where the the set is predecided. While at it optimise two sequential bit clear in one step. Acked-by: Felipe Balbi Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 84aeaf0e5a0..cd87f90588e 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -140,13 +140,6 @@ struct omap2_mcspi_cs { u32 chconf0; }; -#define MOD_REG_BIT(val, mask, set) do { \ - if (set) \ - val |= mask; \ - else \ - val &= ~mask; \ -} while (0) - static inline void mcspi_write_reg(struct spi_master *master, int idx, u32 val) { @@ -205,7 +198,11 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi, else rw = OMAP2_MCSPI_CHCONF_DMAW; - MOD_REG_BIT(l, rw, enable); + if (enable) + l |= rw; + else + l &= ~rw; + mcspi_write_chconf0(spi, l); } @@ -224,7 +221,11 @@ static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) u32 l; l = mcspi_cached_chconf0(spi); - MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); + if (cs_active) + l |= OMAP2_MCSPI_CHCONF_FORCE; + else + l &= ~OMAP2_MCSPI_CHCONF_FORCE; + mcspi_write_chconf0(spi, l); } @@ -239,9 +240,8 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master) * to single-channel master mode */ l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL); - MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0); - MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); - MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1); + l &= ~(OMAP2_MCSPI_MODULCTRL_STEST | OMAP2_MCSPI_MODULCTRL_MS); + l |= OMAP2_MCSPI_MODULCTRL_SINGLE; mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); ctx->modulctrl = l; @@ -1285,9 +1285,9 @@ static int omap2_mcspi_resume(struct device *dev) * We need to toggle CS state for OMAP take this * change in account. */ - MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 1); + cs->chconf0 |= OMAP2_MCSPI_CHCONF_FORCE; __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); - MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0); + cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE; __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } } -- cgit v1.2.3 From 034d3dc9886054384d2a64f2c954ecbb11ce88c8 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 22 Aug 2012 11:35:12 +0530 Subject: spi: omap2-mcspi: Call pm_runtime_* functions directly Call the pm_runtime functions directly making room for possible pm optimisations. Also the runtime functions aren't just about enabling and disabling of clocks though it does enable clocks also. Acked-by: Felipe Balbi Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index cd87f90588e..9340974404c 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -260,16 +260,6 @@ static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) list_for_each_entry(cs, &ctx->cs, node) __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } -static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) -{ - pm_runtime_mark_last_busy(mcspi->dev); - pm_runtime_put_autosuspend(mcspi->dev); -} - -static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) -{ - return pm_runtime_get_sync(mcspi->dev); -} static int omap2_prepare_transfer(struct spi_master *master) { @@ -848,12 +838,13 @@ static int omap2_mcspi_setup(struct spi_device *spi) return ret; } - ret = omap2_mcspi_enable_clocks(mcspi); + ret = pm_runtime_get_sync(mcspi->dev); if (ret < 0) return ret; ret = omap2_mcspi_setup_transfer(spi, NULL); - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_mark_last_busy(mcspi->dev); + pm_runtime_put_autosuspend(mcspi->dev); return ret; } @@ -1067,7 +1058,7 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) struct omap2_mcspi_regs *ctx = &mcspi->ctx; int ret = 0; - ret = omap2_mcspi_enable_clocks(mcspi); + ret = pm_runtime_get_sync(mcspi->dev); if (ret < 0) return ret; @@ -1076,7 +1067,8 @@ static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) ctx->wakeupenable = OMAP2_MCSPI_WAKEUPENABLE_WKEN; omap2_mcspi_set_master_mode(master); - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_mark_last_busy(mcspi->dev); + pm_runtime_put_autosuspend(mcspi->dev); return 0; } @@ -1253,7 +1245,8 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev) mcspi = spi_master_get_devdata(master); dma_channels = mcspi->dma_channels; - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_mark_last_busy(mcspi->dev); + pm_runtime_put_autosuspend(mcspi->dev); pm_runtime_disable(&pdev->dev); spi_unregister_master(master); @@ -1278,7 +1271,7 @@ static int omap2_mcspi_resume(struct device *dev) struct omap2_mcspi_regs *ctx = &mcspi->ctx; struct omap2_mcspi_cs *cs; - omap2_mcspi_enable_clocks(mcspi); + pm_runtime_get_sync(mcspi->dev); list_for_each_entry(cs, &ctx->cs, node) { if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) { /* @@ -1291,7 +1284,8 @@ static int omap2_mcspi_resume(struct device *dev) __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } } - omap2_mcspi_disable_clocks(mcspi); + pm_runtime_mark_last_busy(mcspi->dev); + pm_runtime_put_autosuspend(mcspi->dev); return 0; } #else -- cgit v1.2.3 From a93a2029de7c7f15eb33331cd0f8114892eaa0f3 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Wed, 22 Aug 2012 11:35:14 +0530 Subject: spi: omap2-mcspi: At remove dont use the runtime_autosuspend calls At remove we shouldnt be using the autosuspend timeout as we are calling pm_runtime_disable immediately after. Acked-by: Felipe Balbi Signed-off-by: Shubhrajyoti D Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 9340974404c..164c15d6a1b 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1245,8 +1245,7 @@ static int __devexit omap2_mcspi_remove(struct platform_device *pdev) mcspi = spi_master_get_devdata(master); dma_channels = mcspi->dma_channels; - pm_runtime_mark_last_busy(mcspi->dev); - pm_runtime_put_autosuspend(mcspi->dev); + pm_runtime_put_sync(mcspi->dev); pm_runtime_disable(&pdev->dev); spi_unregister_master(master); -- cgit v1.2.3 From 495e3ff365425f9b69884ddde36047184f37c2bd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 19 Aug 2012 10:44:17 -0700 Subject: spi/pl022: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. An extra call to spi_master_put is unnecessary and results in an access to free memory. Drop it. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index aab518ec2bb..47c6753fb31 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2171,7 +2171,6 @@ pl022_remove(struct amba_device *adev) amba_release_regions(adev); tasklet_disable(&pl022->pump_transfers); spi_unregister_master(pl022->master); - spi_master_put(pl022->master); amba_set_drvdata(adev, NULL); return 0; } -- cgit v1.2.3 From 866c0f25d2dc896e647970cf5e371b5083a6e5d5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:29:21 -0700 Subject: spi/mpc52xx: Fix error handling in probe function The call to spi_master_put() is needed to free device memory. It must be called after spi_alloc_master, and must only be called after the device memory is no longer used. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index cb3a3830b0a..a749589677b 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -454,7 +454,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) GFP_KERNEL); if (!ms->gpio_cs) { rc = -ENOMEM; - goto err_alloc; + goto err_alloc_gpio; } for (i = 0; i < ms->gpio_cs_count; i++) { @@ -514,12 +514,13 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) err_register: dev_err(&ms->master->dev, "initialization failed\n"); - spi_master_put(master); err_gpio: while (i-- > 0) gpio_free(ms->gpio_cs[i]); kfree(ms->gpio_cs); + err_alloc_gpio: + spi_master_put(master); err_alloc: err_init: iounmap(regs); -- cgit v1.2.3 From f95e1028e3317a084af955aa0b1c25a6c9213869 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:29:22 -0700 Subject: spi/mpc52xx: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. An extra call to spi_master_put is unnecessary and results in an access to free memory. At the same time, since the call to spi_unregister_master results in device memory being freed, it must no longer be accessed afterwards. To fix both problems, call spi_master_get to get an extra reference to the device, and call spi_master_put only after the last access to device data. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index a749589677b..04541065021 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -529,7 +529,7 @@ static int __devinit mpc52xx_spi_probe(struct platform_device *op) static int __devexit mpc52xx_spi_remove(struct platform_device *op) { - struct spi_master *master = dev_get_drvdata(&op->dev); + struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); struct mpc52xx_spi *ms = spi_master_get_devdata(master); int i; @@ -541,8 +541,8 @@ static int __devexit mpc52xx_spi_remove(struct platform_device *op) kfree(ms->gpio_cs); spi_unregister_master(master); - spi_master_put(master); iounmap(ms->regs); + spi_master_put(master); return 0; } -- cgit v1.2.3 From c8c87c656f95d764eb2b460b46b0144794108da4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:29:23 -0700 Subject: spi/mpc52xx-psc: Avoid access to freed memory in device remove function The call to spi_unregister_master() in the device remove function frees device memory, and with it any device local data. However, device local data is still accessed after the call to spi_unregister_master(). Acquire a reference to the SPI master device and release it after cleanup is complete to solve the problem. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx-psc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index 66047156d90..bd47d262d53 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -481,7 +481,7 @@ static int __devinit mpc52xx_psc_spi_of_probe(struct platform_device *op) static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op) { - struct spi_master *master = dev_get_drvdata(&op->dev); + struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); flush_workqueue(mps->workqueue); @@ -490,6 +490,7 @@ static int __devexit mpc52xx_psc_spi_of_remove(struct platform_device *op) free_irq(mps->irq, mps); if (mps->psc) iounmap(mps->psc); + spi_master_put(master); return 0; } -- cgit v1.2.3 From 21879213652aca6e3fe250fc8e02c68e71c5e18a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:29:24 -0700 Subject: spi/mpc512x-psc: Avoid access to freed memory in device remove function The call to spi_unregister_master() in the device remove function frees device memory, and with it any device local data. However, device local data is still accessed after the call to spi_unregister_master(). Acquire a reference to the SPI master device and release it after cleanup is complete to solve the problem. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-mpc512x-psc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 4c63f772780..0a1e39e94d0 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -494,7 +494,7 @@ free_master: static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); + struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); flush_workqueue(mps->workqueue); @@ -503,6 +503,7 @@ static int __devexit mpc512x_psc_spi_do_remove(struct device *dev) free_irq(mps->irq, mps); if (mps->psc) iounmap(mps->psc); + spi_master_put(master); return 0; } -- cgit v1.2.3 From 3ce8859e2e72713d3619285cab609d05c3591fc4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 18 Aug 2012 09:06:27 -0700 Subject: spi: Master driver for NXP SC18IS602/603 This driver adds support for NXP SC18IS602/603 I2C to SPI bus bridge. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-sc18is602.txt | 23 ++ Documentation/spi/spi-sc18is602 | 36 ++ drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-sc18is602.c | 364 +++++++++++++++++++++ include/linux/platform_data/sc18is602.h | 19 ++ 6 files changed, 449 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-sc18is602.txt create mode 100644 Documentation/spi/spi-sc18is602 create mode 100644 drivers/spi/spi-sc18is602.c create mode 100644 include/linux/platform_data/sc18is602.h diff --git a/Documentation/devicetree/bindings/spi/spi-sc18is602.txt b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt new file mode 100644 index 00000000000..02f9033270a --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt @@ -0,0 +1,23 @@ +NXP SC18IS602/SCIS603 + +Required properties: + - compatible : Should be one of + "nxp,sc18is602" + "nxp,sc18is602b" + "nxp,sc18is603" + - reg: I2C bus address + +Optional properties: + - clock-frequency : external oscillator clock frequency. If not + specified, the SC18IS602 default frequency (7372000) will be used. + +The clock-frequency property is relevant and needed only if the chip has an +external oscillator (SC18IS603). + +Example: + + sc18is603@28 { + compatible = "nxp,sc18is603"; + reg = <0x28>; + clock-frequency = <14744000>; + } diff --git a/Documentation/spi/spi-sc18is602 b/Documentation/spi/spi-sc18is602 new file mode 100644 index 00000000000..a45702865a3 --- /dev/null +++ b/Documentation/spi/spi-sc18is602 @@ -0,0 +1,36 @@ +Kernel driver spi-sc18is602 +=========================== + +Supported chips: + * NXP SI18IS602/602B/603 + Datasheet: http://www.nxp.com/documents/data_sheet/SC18IS602_602B_603.pdf + +Author: + Guenter Roeck + + +Description +----------- + +This driver provides connects a NXP SC18IS602/603 I2C-bus to SPI bridge to the +kernel's SPI core subsystem. + +The driver does not probe for supported chips, since the SI18IS602/603 does not +support Chip ID registers. You will have to instantiate the devices explicitly. +Please see Documentation/i2c/instantiating-devices for details. + + +Usage Notes +----------- + +This driver requires the I2C adapter driver to support raw I2C messages. I2C +adapter drivers which can only handle the SMBus protocol are not supported. + +The maximum SPI message size supported by SC18IS602/603 is 200 bytes. Attempts +to initiate longer transfers will fail with -EINVAL. EEPROM read operations and +similar large accesses have to be split into multiple chunks of no more than +200 bytes per SPI message (128 bytes of data per message is recommended). This +means that programs such as "cp" or "od", which automatically use large block +sizes to access a device, can not be used directly to read data from EEPROM. +Programs such as dd, where the block size can be specified, should be used +instead. diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5f84b5563c2..920bb4d22d4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -325,6 +325,12 @@ config SPI_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. +config SPI_SC18IS602 + tristate "NXP SC18IS602/602B/603 I2C to SPI bridge" + depends on I2C + help + SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge. + config SPI_SH_MSIOF tristate "SuperH MSIOF SPI controller" depends on SUPERH && HAVE_CLK diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3920dcf4c74..7559c984db7 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o +obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o obj-$(CONFIG_SPI_SH) += spi-sh.o obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c new file mode 100644 index 00000000000..dd9896423f0 --- /dev/null +++ b/drivers/spi/spi-sc18is602.c @@ -0,0 +1,364 @@ +/* + * NXP SC18IS602/603 SPI driver + * + * Copyright (C) Guenter Roeck + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum chips { sc18is602, sc18is602b, sc18is603 }; + +#define SC18IS602_BUFSIZ 200 +#define SC18IS602_CLOCK 7372000 + +#define SC18IS602_MODE_CPHA BIT(2) +#define SC18IS602_MODE_CPOL BIT(3) +#define SC18IS602_MODE_LSB_FIRST BIT(5) +#define SC18IS602_MODE_CLOCK_DIV_4 0x0 +#define SC18IS602_MODE_CLOCK_DIV_16 0x1 +#define SC18IS602_MODE_CLOCK_DIV_64 0x2 +#define SC18IS602_MODE_CLOCK_DIV_128 0x3 + +struct sc18is602 { + struct spi_master *master; + struct device *dev; + u8 ctrl; + u32 freq; + u32 speed; + + /* I2C data */ + struct i2c_client *client; + enum chips id; + u8 buffer[SC18IS602_BUFSIZ + 1]; + int tlen; /* Data queued for tx in buffer */ + int rindex; /* Receive data index in buffer */ +}; + +static int sc18is602_wait_ready(struct sc18is602 *hw, int len) +{ + int i, err; + int usecs = 1000000 * len / hw->speed + 1; + u8 dummy[1]; + + for (i = 0; i < 10; i++) { + err = i2c_master_recv(hw->client, dummy, 1); + if (err >= 0) + return 0; + usleep_range(usecs, usecs * 2); + } + return -ETIMEDOUT; +} + +static int sc18is602_txrx(struct sc18is602 *hw, struct spi_message *msg, + struct spi_transfer *t, bool do_transfer) +{ + unsigned int len = t->len; + int ret; + + if (hw->tlen == 0) { + /* First byte (I2C command) is chip select */ + hw->buffer[0] = 1 << msg->spi->chip_select; + hw->tlen = 1; + hw->rindex = 0; + } + /* + * We can not immediately send data to the chip, since each I2C message + * resembles a full SPI message (from CS active to CS inactive). + * Enqueue messages up to the first read or until do_transfer is true. + */ + if (t->tx_buf) { + memcpy(&hw->buffer[hw->tlen], t->tx_buf, len); + hw->tlen += len; + if (t->rx_buf) + do_transfer = true; + else + hw->rindex = hw->tlen - 1; + } else if (t->rx_buf) { + /* + * For receive-only transfers we still need to perform a dummy + * write to receive data from the SPI chip. + * Read data starts at the end of transmit data (minus 1 to + * account for CS). + */ + hw->rindex = hw->tlen - 1; + memset(&hw->buffer[hw->tlen], 0, len); + hw->tlen += len; + do_transfer = true; + } + + if (do_transfer && hw->tlen > 1) { + ret = sc18is602_wait_ready(hw, SC18IS602_BUFSIZ); + if (ret < 0) + return ret; + ret = i2c_master_send(hw->client, hw->buffer, hw->tlen); + if (ret < 0) + return ret; + if (ret != hw->tlen) + return -EIO; + + if (t->rx_buf) { + int rlen = hw->rindex + len; + + ret = sc18is602_wait_ready(hw, hw->tlen); + if (ret < 0) + return ret; + ret = i2c_master_recv(hw->client, hw->buffer, rlen); + if (ret < 0) + return ret; + if (ret != rlen) + return -EIO; + memcpy(t->rx_buf, &hw->buffer[hw->rindex], len); + } + hw->tlen = 0; + } + return len; +} + +static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode) +{ + u8 ctrl = 0; + int ret; + + if (mode & SPI_CPHA) + ctrl |= SC18IS602_MODE_CPHA; + if (mode & SPI_CPOL) + ctrl |= SC18IS602_MODE_CPOL; + if (mode & SPI_LSB_FIRST) + ctrl |= SC18IS602_MODE_LSB_FIRST; + + /* Find the closest clock speed */ + if (hz >= hw->freq / 4) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_4; + hw->speed = hw->freq / 4; + } else if (hz >= hw->freq / 16) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_16; + hw->speed = hw->freq / 16; + } else if (hz >= hw->freq / 64) { + ctrl |= SC18IS602_MODE_CLOCK_DIV_64; + hw->speed = hw->freq / 64; + } else { + ctrl |= SC18IS602_MODE_CLOCK_DIV_128; + hw->speed = hw->freq / 128; + } + + /* + * Don't do anything if the control value did not change. The initial + * value of 0xff for hw->ctrl ensures that the correct mode will be set + * with the first call to this function. + */ + if (ctrl == hw->ctrl) + return 0; + + ret = i2c_smbus_write_byte_data(hw->client, 0xf0, ctrl); + if (ret < 0) + return ret; + + hw->ctrl = ctrl; + + return 0; +} + +static int sc18is602_check_transfer(struct spi_device *spi, + struct spi_transfer *t, int tlen) +{ + int bpw; + uint32_t hz; + + if (t && t->len + tlen > SC18IS602_BUFSIZ) + return -EINVAL; + + bpw = spi->bits_per_word; + if (t && t->bits_per_word) + bpw = t->bits_per_word; + if (bpw != 8) + return -EINVAL; + + hz = spi->max_speed_hz; + if (t && t->speed_hz) + hz = t->speed_hz; + if (hz == 0) + return -EINVAL; + + return 0; +} + +static int sc18is602_transfer_one(struct spi_master *master, + struct spi_message *m) +{ + struct sc18is602 *hw = spi_master_get_devdata(master); + struct spi_device *spi = m->spi; + struct spi_transfer *t; + int status = 0; + + /* SC18IS602 does not support CS2 */ + if (hw->id == sc18is602 && spi->chip_select == 2) { + status = -ENXIO; + goto error; + } + + hw->tlen = 0; + list_for_each_entry(t, &m->transfers, transfer_list) { + u32 hz = t->speed_hz ? : spi->max_speed_hz; + bool do_transfer; + + status = sc18is602_check_transfer(spi, t, hw->tlen); + if (status < 0) + break; + + status = sc18is602_setup_transfer(hw, hz, spi->mode); + if (status < 0) + break; + + do_transfer = t->cs_change || list_is_last(&t->transfer_list, + &m->transfers); + + if (t->len) { + status = sc18is602_txrx(hw, m, t, do_transfer); + if (status < 0) + break; + m->actual_length += status; + } + status = 0; + + if (t->delay_usecs) + udelay(t->delay_usecs); + } +error: + m->status = status; + spi_finalize_current_message(master); + + return status; +} + +static int sc18is602_setup(struct spi_device *spi) +{ + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST)) + return -EINVAL; + + return sc18is602_check_transfer(spi, NULL, 0); +} + +static int sc18is602_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct sc18is602_platform_data *pdata = dev_get_platdata(dev); + struct sc18is602 *hw; + struct spi_master *master; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + master = spi_alloc_master(dev, sizeof(struct sc18is602)); + if (!master) + return -ENOMEM; + + hw = spi_master_get_devdata(master); + i2c_set_clientdata(client, hw); + + hw->master = master; + hw->client = client; + hw->dev = dev; + hw->ctrl = 0xff; + + hw->id = id->driver_data; + + switch (hw->id) { + case sc18is602: + case sc18is602b: + master->num_chipselect = 4; + hw->freq = SC18IS602_CLOCK; + break; + case sc18is603: + master->num_chipselect = 2; + if (pdata) { + hw->freq = pdata->clock_frequency; + } else { + const __be32 *val; + int len; + + val = of_get_property(np, "clock-frequency", &len); + if (val && len >= sizeof(__be32)) + hw->freq = be32_to_cpup(val); + } + if (!hw->freq) + hw->freq = SC18IS602_CLOCK; + break; + } + master->bus_num = client->adapter->nr; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST; + master->setup = sc18is602_setup; + master->transfer_one_message = sc18is602_transfer_one; + master->dev.of_node = np; + + error = spi_register_master(master); + if (error) + goto error_reg; + + return 0; + +error_reg: + spi_master_put(master); + return error; +} + +static int sc18is602_remove(struct i2c_client *client) +{ + struct sc18is602 *hw = i2c_get_clientdata(client); + struct spi_master *master = hw->master; + + spi_unregister_master(master); + + return 0; +} + +static const struct i2c_device_id sc18is602_id[] = { + { "sc18is602", sc18is602 }, + { "sc18is602b", sc18is602b }, + { "sc18is603", sc18is603 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sc18is602_id); + +static struct i2c_driver sc18is602_driver = { + .driver = { + .name = "sc18is602", + }, + .probe = sc18is602_probe, + .remove = sc18is602_remove, + .id_table = sc18is602_id, +}; + +module_i2c_driver(sc18is602_driver); + +MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver"); +MODULE_AUTHOR("Guenter Roeck"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/sc18is602.h b/include/linux/platform_data/sc18is602.h new file mode 100644 index 00000000000..997b0663415 --- /dev/null +++ b/include/linux/platform_data/sc18is602.h @@ -0,0 +1,19 @@ +/* + * Platform data for NXP SC18IS602/603 + * + * Copyright (C) 2012 Guenter Roeck + * + * 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. + * + * For further information, see the Documentation/spi/sc18is602 file. + */ + +/** + * struct sc18is602_platform_data - sc18is602 info + * @clock_frequency SC18IS603 oscillator frequency + */ +struct sc18is602_platform_data { + u32 clock_frequency; +}; -- cgit v1.2.3 From f6f46de1063c8829713cd9d5b960dd8cb66cde8b Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 22 Aug 2012 15:49:17 +0200 Subject: spi/pl022: Add chip select handling via GPIO This patch adds the ability for the driver to control the chip select directly. This enables independence from cs_control callbacks. Configurable via platform_data, to be extended as DT in the following patch. Based on the initial patch by Alexandre Pereira da Silva Signed-off-by: Roland Stigge Acked-by: Alexandre Pereira da Silva Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 48 ++++++++++++++++++++++++++++++++-------------- include/linux/amba/pl022.h | 2 ++ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 47c6753fb31..cf47802f00c 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -40,6 +40,7 @@ #include #include #include +#include /* * This macro is used to define some register default values. @@ -356,6 +357,8 @@ struct vendor_data { * @sgt_rx: scattertable for the RX transfer * @sgt_tx: scattertable for the TX transfer * @dummypage: a dummy page used for driving data on the bus with DMA + * @cur_cs: current chip select (gpio) + * @chipselects: list of chipselects (gpios) */ struct pl022 { struct amba_device *adev; @@ -389,6 +392,8 @@ struct pl022 { char *dummypage; bool dma_running; #endif + int cur_cs; + int *chipselects; }; /** @@ -433,6 +438,14 @@ static void null_cs_control(u32 command) pr_debug("pl022: dummy chip select control, CS=0x%x\n", command); } +static void pl022_cs_control(struct pl022 *pl022, u32 command) +{ + if (gpio_is_valid(pl022->cur_cs)) + gpio_set_value(pl022->cur_cs, command); + else + pl022->cur_chip->cs_control(command); +} + /** * giveback - current spi_message is over, schedule next message and call * callback of this message. Assumes that caller already @@ -479,7 +492,7 @@ static void giveback(struct pl022 *pl022) if (next_msg && next_msg->spi != pl022->cur_msg->spi) next_msg = NULL; if (!next_msg || pl022->cur_msg->state == STATE_ERROR) - pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); else pl022->next_msg_cs_active = true; @@ -818,8 +831,7 @@ static void dma_callback(void *data) /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) - pl022->cur_chip-> - cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ msg->state = next_transfer(pl022); @@ -1252,8 +1264,7 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) - pl022->cur_chip-> - cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ msg->state = next_transfer(pl022); tasklet_schedule(&pl022->pump_transfers); @@ -1338,7 +1349,7 @@ static void pump_transfers(unsigned long data) /* Reselect chip select only if cs_change was requested */ if (previous->cs_change) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } else { /* STATE_START */ message->state = STATE_RUNNING; @@ -1377,7 +1388,7 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022) /* Enable target chip, if not already active */ if (!pl022->next_msg_cs_active) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); if (set_up_next_transfer(pl022, pl022->cur_transfer)) { /* Error path */ @@ -1429,12 +1440,12 @@ static void do_polling_transfer(struct pl022 *pl022) if (previous->delay_usecs) udelay(previous->delay_usecs); if (previous->cs_change) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } else { /* STATE_START */ message->state = STATE_RUNNING; if (!pl022->next_msg_cs_active) - pl022->cur_chip->cs_control(SSP_CHIP_SELECT); + pl022_cs_control(pl022, SSP_CHIP_SELECT); } /* Configuration Changing Per Transfer */ @@ -1466,7 +1477,7 @@ static void do_polling_transfer(struct pl022 *pl022) /* Update total byte transferred */ message->actual_length += pl022->cur_transfer->len; if (pl022->cur_transfer->cs_change) - pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); + pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ message->state = next_transfer(pl022); } @@ -1495,6 +1506,7 @@ static int pl022_transfer_one_message(struct spi_master *master, /* Setup the SPI using the per chip configuration */ pl022->cur_chip = spi_get_ctldata(msg->spi); + pl022->cur_cs = pl022->chipselects[msg->spi->chip_select]; restore_state(pl022); flush(pl022); @@ -1840,8 +1852,9 @@ static int pl022_setup(struct spi_device *spi) chip->xfer_type = chip_info->com_mode; if (!chip_info->cs_control) { chip->cs_control = null_cs_control; - dev_warn(&spi->dev, - "chip select function is NULL for this chip\n"); + if (!gpio_is_valid(pl022->chipselects[spi->chip_select])) + dev_warn(&spi->dev, + "invalid chip select\n"); } else chip->cs_control = chip_info->cs_control; @@ -1993,7 +2006,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) struct pl022_ssp_controller *platform_info = adev->dev.platform_data; struct spi_master *master; struct pl022 *pl022 = NULL; /*Data for this driver */ - int status = 0; + int status = 0, i; dev_info(&adev->dev, "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); @@ -2004,7 +2017,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) } /* Allocate master with space for data */ - master = spi_alloc_master(dev, sizeof(struct pl022)); + master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) * + platform_info->num_chipselect); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); status = -ENOMEM; @@ -2016,6 +2030,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->master_info = platform_info; pl022->adev = adev; pl022->vendor = id->data; + /* Point chipselects to allocated memory beyond the main struct */ + pl022->chipselects = (int *) pl022 + sizeof(struct pl022); /* * Bus Number Which has been Assigned to this SSP controller @@ -2030,6 +2046,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware; master->rt = platform_info->rt; + if (platform_info->num_chipselect && platform_info->chipselects) + for (i = 0; i < platform_info->num_chipselect; i++) + pl022->chipselects[i] = platform_info->chipselects[i]; + /* * Supports mode 0-3, loopback, and active low CS. Transfers are * always MS bit first on the original pl022. diff --git a/include/linux/amba/pl022.h b/include/linux/amba/pl022.h index fe1d7b283cb..854b7294f6c 100644 --- a/include/linux/amba/pl022.h +++ b/include/linux/amba/pl022.h @@ -244,6 +244,7 @@ struct dma_chan; * indicates no delay and the device will be suspended immediately. * @rt: indicates the controller should run the message pump with realtime * priority to minimise the transfer latency on the bus. + * @chipselects: list of chip select gpios */ struct pl022_ssp_controller { u16 bus_id; @@ -254,6 +255,7 @@ struct pl022_ssp_controller { void *dma_tx_param; int autosuspend_delay; bool rt; + int *chipselects; }; /** -- cgit v1.2.3 From 6d3952a7dfa80919842bbe01ac7f693d40a1eb84 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 22 Aug 2012 15:49:18 +0200 Subject: spi/pl022: Add devicetree support This patch adds device tree support to the spi-pl022 driver. Based on the initial patch by Alexandre Pereira da Silva Signed-off-by: Roland Stigge Acked-by: Alexandre Pereira da Silva Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi_pl022.txt | 15 ++++ drivers/spi/spi-pl022.c | 79 +++++++++++++++++++--- 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi_pl022.txt b/Documentation/devicetree/bindings/spi/spi_pl022.txt index 306ec3ff3c0..877f832c30a 100644 --- a/Documentation/devicetree/bindings/spi/spi_pl022.txt +++ b/Documentation/devicetree/bindings/spi/spi_pl022.txt @@ -6,7 +6,22 @@ Required properties: - interrupts : Should contain SPI controller interrupt Optional properties: +- num-cs : total number of chipselects - cs-gpios : should specify GPIOs used for chipselects. The gpios will be referred to as reg = in the SPI child nodes. If unspecified, a single SPI device without a chip select can be used. +SPI slave nodes must be children of the SPI master node and can +contain the following properties. + +- pl022,interface : interface type: + 0: SPI + 1: Texas Instruments Synchronous Serial Frame Format + 2: Microwire (Half Duplex) +- pl022,com-mode : polling, interrupt or dma +- pl022,rx-level-trig : Rx FIFO watermark level +- pl022,tx-level-trig : Tx FIFO watermark level +- pl022,ctrl-len : Microwire interface: Control length +- pl022,wait-state : Microwire interface: Wait state +- pl022,duplex : Microwire interface: Full/Half duplex + diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index cf47802f00c..959f2acff2d 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -41,6 +41,7 @@ #include #include #include +#include /* * This macro is used to define some register default values. @@ -1778,12 +1779,14 @@ static const struct pl022_config_chip pl022_default_chip_info = { static int pl022_setup(struct spi_device *spi) { struct pl022_config_chip const *chip_info; + struct pl022_config_chip chip_info_dt; struct chip_data *chip; struct ssp_clock_params clk_freq = { .cpsdvsr = 0, .scr = 0}; int status = 0; struct pl022 *pl022 = spi_master_get_devdata(spi->master); unsigned int bits = spi->bits_per_word; u32 tmp; + struct device_node *np = spi->dev.of_node; if (!spi->max_speed_hz) return -EINVAL; @@ -1806,10 +1809,32 @@ static int pl022_setup(struct spi_device *spi) chip_info = spi->controller_data; if (chip_info == NULL) { - chip_info = &pl022_default_chip_info; - /* spi_board_info.controller_data not is supplied */ - dev_dbg(&spi->dev, - "using default controller_data settings\n"); + if (np) { + chip_info_dt = pl022_default_chip_info; + + chip_info_dt.hierarchy = SSP_MASTER; + of_property_read_u32(np, "pl022,interface", + &chip_info_dt.iface); + of_property_read_u32(np, "pl022,com-mode", + &chip_info_dt.com_mode); + of_property_read_u32(np, "pl022,rx-level-trig", + &chip_info_dt.rx_lev_trig); + of_property_read_u32(np, "pl022,tx-level-trig", + &chip_info_dt.tx_lev_trig); + of_property_read_u32(np, "pl022,ctrl-len", + &chip_info_dt.ctrl_len); + of_property_read_u32(np, "pl022,wait-state", + &chip_info_dt.wait_state); + of_property_read_u32(np, "pl022,duplex", + &chip_info_dt.duplex); + + chip_info = &chip_info_dt; + } else { + chip_info = &pl022_default_chip_info; + /* spi_board_info.controller_data not is supplied */ + dev_dbg(&spi->dev, + "using default controller_data settings\n"); + } } else dev_dbg(&spi->dev, "using user supplied controller_data settings\n"); @@ -2006,7 +2031,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) struct pl022_ssp_controller *platform_info = adev->dev.platform_data; struct spi_master *master; struct pl022 *pl022 = NULL; /*Data for this driver */ - int status = 0, i; + struct device_node *np = adev->dev.of_node; + int status = 0, i, num_cs; dev_info(&adev->dev, "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); @@ -2016,9 +2042,19 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) goto err_no_pdata; } + if (platform_info->num_chipselect) { + num_cs = platform_info->num_chipselect; + } else if (IS_ENABLED(CONFIG_OF)) { + of_property_read_u32(np, "num-cs", &num_cs); + } else { + dev_err(&adev->dev, "probe: no chip select defined\n"); + status = -ENODEV; + goto err_no_pdata; + } + /* Allocate master with space for data */ master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) * - platform_info->num_chipselect); + num_cs); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); status = -ENOMEM; @@ -2038,17 +2074,41 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) * on this board */ master->bus_num = platform_info->bus_id; - master->num_chipselect = platform_info->num_chipselect; + master->num_chipselect = num_cs; master->cleanup = pl022_cleanup; master->setup = pl022_setup; master->prepare_transfer_hardware = pl022_prepare_transfer_hardware; master->transfer_one_message = pl022_transfer_one_message; master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware; master->rt = platform_info->rt; + master->dev.of_node = dev->of_node; - if (platform_info->num_chipselect && platform_info->chipselects) - for (i = 0; i < platform_info->num_chipselect; i++) + if (platform_info->num_chipselect && platform_info->chipselects) { + for (i = 0; i < num_cs; i++) pl022->chipselects[i] = platform_info->chipselects[i]; + } else if (IS_ENABLED(CONFIG_OF)) { + for (i = 0; i < num_cs; i++) { + int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); + + if (cs_gpio == -EPROBE_DEFER) { + status = -EPROBE_DEFER; + goto err_no_gpio; + } + + pl022->chipselects[i] = cs_gpio; + + if (gpio_is_valid(cs_gpio)) { + if (gpio_request(cs_gpio, "ssp-pl022")) + dev_err(&adev->dev, + "could not request %d gpio\n", + cs_gpio); + else if (gpio_direction_output(cs_gpio, 1)) + dev_err(&adev->dev, + "could set gpio %d as output\n", + cs_gpio); + } + } + } /* * Supports mode 0-3, loopback, and active low CS. Transfers are @@ -2158,6 +2218,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) err_no_ioremap: amba_release_regions(adev); err_no_ioregion: + err_no_gpio: spi_master_put(master); err_no_master: err_no_pdata: -- cgit v1.2.3 From 41962f90c605983fb04c23bab9c060c9f49ee4c3 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Wed, 22 Aug 2012 15:49:19 +0200 Subject: spi/dt: DT bindings documentation: "num-cs" property for SPI controllers Several SPI controller drivers have defined differently named properties for the number of chip selects. Now adding "num-cs" as a reference name for new bindings. Signed-off-by: Roland Stigge Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-bus.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index e782add2e45..d2c33d0f533 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -21,6 +21,9 @@ 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 + SPI slave nodes must be children of the SPI master node and can contain the following properties. - reg - (required) chip select address of device. -- cgit v1.2.3 From 58ed90de3ef58a19c035355a4a0cd6dfef6d6b0c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 22 Aug 2012 17:28:55 -0700 Subject: spi/sc18is602: Return -EINVAL for probe failures due to I2C function mismatch If the I2C bus master driver does not support the required functionality, the driver returns -ENODEV. This causes a silent probe failure without error message. Since the device has to be explicitly instantiated, and the user should know the correct bus, this event really reflects an error condition. Replace error return value with -EINVAL to trigger an error message showing that the probe function failed. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-sc18is602.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index dd9896423f0..9eda21d739c 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -275,7 +275,7 @@ static int sc18is602_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) - return -ENODEV; + return -EINVAL; master = spi_alloc_master(dev, sizeof(struct sc18is602)); if (!master) -- cgit v1.2.3 From c895db0fb2c9e77fd94ba7995e2a01652a91910c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 24 Aug 2012 04:34:18 +0200 Subject: mxs/spi: Restart the block after unsuccessful transfer Restart the SSP block in case the SSP transfer failed in any way. The block hung in some cases otherwise. Signed-off-by: Marek Vasut Cc: Chris Ball Cc: Shawn Guo Cc: Mark Brown Cc: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 130a4368835..447f917fe66 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -439,8 +439,10 @@ static int mxs_spi_transfer_one(struct spi_master *master, } m->actual_length += t->len; - if (status) + if (status) { + stmp_reset_block(ssp->base); break; + } first = last = 0; } -- cgit v1.2.3 From 41682e03d4fdc947dbd22725d70f222cc7746852 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 24 Aug 2012 04:56:27 +0200 Subject: mxs/spi: Fix misuse of init_completion The init_completion() call does reinit not only the variable carrying the flag that the completion finished, but also initialized the waitqueue associated with the completion. On the contrary, the INIT_COMPLETION() call only reinits the flag. In case there was anything still stuck in the waitqueue, subsequent call to init_completion() would be able to create possible race condition. This patch uses the proper function and moves init_completion() into .probe() call of the driver, to be issued only once. Note that such scenario is impossible, since two threads can never enter the mxs_spi_txrx_dma(), since whole this section is protected by mutex in SPI core. This by no means allows this issue to exit though. Signed-off-by: Marek Vasut Cc: Chris Ball Cc: Shawn Guo Cc: Mark Brown Cc: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 447f917fe66..4e7801dd571 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -230,7 +230,7 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, return -EINVAL; } - init_completion(&spi->c); + INIT_COMPLETION(spi->c); if (*first) pio |= BM_SSP_CTRL0_LOCK_CS; @@ -549,6 +549,8 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) ssp->devid = devid; ssp->dma_channel = dma_channel; + init_completion(&spi->c); + ret = devm_request_irq(&pdev->dev, irq_err, mxs_ssp_irq_handler, 0, DRIVER_NAME, ssp); if (ret) -- cgit v1.2.3 From 154390dcf826dc97b9d338305db14582c32cb58a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 23 Aug 2012 20:08:47 -0700 Subject: spi/stmp: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. The remove function accesses the freed memory after the call to spi_unregister_master(), _and_ it calls spi_master_put on the freed memory. Acquire a reference to the SPI master device and release it after cleanup is complete (with the existing spi_master_put) to solve the problem. Also, the device subsystem ensures that the remove function is only called once, and resets device driver data to NULL. Remove the respective check and drop the unnecessaary call to platform_set_drvdata(). Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-stmp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/spi/spi-stmp.c b/drivers/spi/spi-stmp.c index 58e38528532..911e904b3c8 100644 --- a/drivers/spi/spi-stmp.c +++ b/drivers/spi/spi-stmp.c @@ -594,9 +594,7 @@ static int __devexit stmp_spi_remove(struct platform_device *dev) struct stmp_spi *ss; struct spi_master *master; - master = platform_get_drvdata(dev); - if (master == NULL) - goto out0; + master = spi_master_get(platform_get_drvdata(dev)); ss = spi_master_get_devdata(master); spi_unregister_master(master); @@ -609,8 +607,6 @@ static int __devexit stmp_spi_remove(struct platform_device *dev) destroy_workqueue(ss->workqueue); iounmap(ss->regs); spi_master_put(master); - platform_set_drvdata(dev, NULL); -out0: return 0; } -- cgit v1.2.3 From c68025bf0363a7b44be2de512a88a57d57e63d81 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 23 Aug 2012 20:08:48 -0700 Subject: spi/tegra: Fix device remove function The call to spi_unregister_master() in the device remove function frees device memory, and with it any device local data. However, device local data is still accessed after the call to spi_unregister_master(). Acquire a reference to the SPI master device and release it after cleanup is complete to solve the problem. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-tegra.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index ef52c1c6f5c..e28445d8d27 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -652,7 +652,7 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) struct spi_tegra_data *tspi; struct resource *r; - master = dev_get_drvdata(&pdev->dev); + master = spi_master_get(dev_get_drvdata(&pdev->dev)); tspi = spi_master_get_devdata(master); spi_unregister_master(master); @@ -668,6 +668,8 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) clk_put(tspi->clk); iounmap(tspi->base); + spi_master_put(master); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, resource_size(r)); -- cgit v1.2.3 From 63002e84a80c02b0be0f73932b955755903baabf Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 23 Aug 2012 20:08:49 -0700 Subject: spi/topcliff-pch: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. An extra call to spi_master_put is unnecessary and results in an access to free memory. Drop it. Also, the device subsystem resets device driver data to NULL after the call to the remove function returns, so there is no need to do it here. Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown --- drivers/spi/spi-topcliff-pch.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index cd56dcf4632..159dafd2f90 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1536,8 +1536,6 @@ static int __devexit pch_spi_pd_remove(struct platform_device *plat_dev) pci_iounmap(board_dat->pdev, data->io_remap_addr); spi_unregister_master(data->master); - spi_master_put(data->master); - platform_set_drvdata(plat_dev, NULL); return 0; } -- cgit v1.2.3 From 9a2a52452575a4edf5b91e341d7d87fe2be3474c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 16 Aug 2012 20:14:25 -0700 Subject: spi/s3c64xx: Drop extra calls to spi_master_get in suspend/remove functions Suspend and resume functions call spi_master_get() without matching spi_master_put(). The extra references are unnecessary and cause subsequent module unload attempts to fail. Drop the calls. Signed-off-by: Guenter Roeck Acked-by: Kukjin Kim Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index cfa2c35dfee..0087139090e 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1409,7 +1409,7 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int s3c64xx_spi_suspend(struct device *dev) { - struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); spi_master_suspend(master); @@ -1428,7 +1428,7 @@ static int s3c64xx_spi_suspend(struct device *dev) static int s3c64xx_spi_resume(struct device *dev) { - struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); struct s3c64xx_spi_info *sci = sdd->cntrlr_info; @@ -1452,7 +1452,7 @@ static int s3c64xx_spi_resume(struct device *dev) #ifdef CONFIG_PM_RUNTIME static int s3c64xx_spi_runtime_suspend(struct device *dev) { - struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); clk_disable(sdd->clk); @@ -1463,7 +1463,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev) static int s3c64xx_spi_runtime_resume(struct device *dev) { - struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); + struct spi_master *master = dev_get_drvdata(dev); struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); clk_enable(sdd->src_clk); -- cgit v1.2.3 From 7d520d28dd5287d14b5ec6cf4405a1220ca57d42 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 24 Aug 2012 11:03:02 -0700 Subject: spi/mxs: Fix device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which results in device memory being freed. The remove function accesses the freed memory after the call to spi_unregister_master(), _and_ it calls spi_master_put on the freed memory. Acquire a reference to the SPI master device and release it after cleanup is complete (with the existing spi_master_put) to solve the problem. Also, the device subsystem ensures that the remove function is only called once, and resets device driver data to NULL. Remove the unnecessaary calls to platform_set_drvdata(). Signed-off-by: Guenter Roeck Reviewed-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 4e7801dd571..10d34ebe9ca 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -586,7 +586,6 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) return 0; out_free_dma: - platform_set_drvdata(pdev, NULL); dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); out_master_free: @@ -600,14 +599,12 @@ static int __devexit mxs_spi_remove(struct platform_device *pdev) struct mxs_spi *spi; struct mxs_ssp *ssp; - master = platform_get_drvdata(pdev); + master = spi_master_get(platform_get_drvdata(pdev)); spi = spi_master_get_devdata(master); ssp = &spi->ssp; spi_unregister_master(master); - platform_set_drvdata(pdev, NULL); - dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); -- cgit v1.2.3 From 161c2dd3ca143f0dca50d72292bf651eabe2aa23 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 5 Sep 2012 11:04:35 +0200 Subject: spi: spi-gpio: store chipselect information in private structure The spi-gpio driver currently assumes the chipselect gpio number is stored in ->controller_data of the device's static board information. In devicetree environments, this information is unavailable and has to be derived from the DT node. This patch moves the gpio storage to the controller's private data so the DT bindings can easily build upon the driver. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 0b56cfc71fa..ff7263ce12c 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -46,6 +46,7 @@ struct spi_gpio { struct spi_bitbang bitbang; struct spi_gpio_platform_data pdata; struct platform_device *pdev; + int cs_gpios[0]; }; /*----------------------------------------------------------------------*/ @@ -89,15 +90,21 @@ struct spi_gpio { /*----------------------------------------------------------------------*/ -static inline const struct spi_gpio_platform_data * __pure -spi_to_pdata(const struct spi_device *spi) +static inline struct spi_gpio * __pure +spi_to_spi_gpio(const struct spi_device *spi) { const struct spi_bitbang *bang; - const struct spi_gpio *spi_gpio; + struct spi_gpio *spi_gpio; bang = spi_master_get_devdata(spi->master); spi_gpio = container_of(bang, struct spi_gpio, bitbang); - return &spi_gpio->pdata; + return spi_gpio; +} + +static inline struct spi_gpio_platform_data * __pure +spi_to_pdata(const struct spi_device *spi) +{ + return &spi_to_spi_gpio(spi)->pdata; } /* this is #defined to avoid unused-variable warnings when inlining */ @@ -210,7 +217,8 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, static void spi_gpio_chipselect(struct spi_device *spi, int is_active) { - unsigned long cs = (unsigned long) spi->controller_data; + struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + unsigned int cs = spi_gpio->cs_gpios[spi->chip_select]; /* set initial clock polarity */ if (is_active) @@ -224,8 +232,9 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active) static int spi_gpio_setup(struct spi_device *spi) { - unsigned long cs = (unsigned long) spi->controller_data; - int status = 0; + unsigned int cs = (unsigned int) spi->controller_data; + int status = 0; + struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); if (spi->bits_per_word > 32) return -EINVAL; @@ -239,8 +248,11 @@ static int spi_gpio_setup(struct spi_device *spi) !(spi->mode & SPI_CS_HIGH)); } } - if (!status) + if (!status) { status = spi_bitbang_setup(spi); + spi_gpio->cs_gpios[spi->chip_select] = cs; + } + if (status) { if (!spi->controller_state && cs != SPI_GPIO_NO_CHIPSELECT) gpio_free(cs); @@ -250,7 +262,8 @@ static int spi_gpio_setup(struct spi_device *spi) static void spi_gpio_cleanup(struct spi_device *spi) { - unsigned long cs = (unsigned long) spi->controller_data; + struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + unsigned int cs = spi_gpio->cs_gpios[spi->chip_select]; if (cs != SPI_GPIO_NO_CHIPSELECT) gpio_free(cs); @@ -331,7 +344,8 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) if (status < 0) return status; - master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio); + master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) + + (sizeof(int) * SPI_N_CHIPSEL)); if (!master) { status = -ENOMEM; goto gpio_free; -- cgit v1.2.3 From 38ab18caa0ad9c844ba60f9618c5de6d6954da3e Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 5 Sep 2012 11:04:36 +0200 Subject: spi: spi-gpio: Add DT bindings This patch adds DT bindings to the spi-gpio driver and some documentation about how to use it. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-gpio.txt | 29 +++++++ drivers/spi/spi-gpio.c | 99 +++++++++++++++++++++- 2 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/spi/spi-gpio.txt diff --git a/Documentation/devicetree/bindings/spi/spi-gpio.txt b/Documentation/devicetree/bindings/spi/spi-gpio.txt new file mode 100644 index 00000000000..8a824be1575 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-gpio.txt @@ -0,0 +1,29 @@ +SPI-GPIO devicetree bindings + +Required properties: + + - compatible: should be set to "spi-gpio" + - #address-cells: should be set to <0x1> + - ranges + - gpio-sck: GPIO spec for the SCK line to use + - gpio-miso: GPIO spec for the MISO line to use + - gpio-mosi: GPIO spec for the MOSI line to use + - cs-gpios: GPIOs to use for chipselect lines + - num-chipselects: number of chipselect lines + +Example: + + spi { + compatible = "spi-gpio"; + #address-cells = <0x1>; + ranges; + + gpio-sck = <&gpio 95 0>; + gpio-miso = <&gpio 98 0>; + gpio-mosi = <&gpio 97 0>; + cs-gpios = <&gpio 125 0>; + num-chipselects = <1>; + + /* clients */ + }; + diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index ff7263ce12c..aed16159584 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -232,13 +234,27 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active) static int spi_gpio_setup(struct spi_device *spi) { - unsigned int cs = (unsigned int) spi->controller_data; + unsigned int cs; int status = 0; struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + struct device_node *np = spi->master->dev.of_node; if (spi->bits_per_word > 32) return -EINVAL; + if (np) { + /* + * In DT environments, the CS GPIOs have already been + * initialized from the "cs-gpios" property of the node. + */ + cs = spi_gpio->cs_gpios[spi->chip_select]; + } else { + /* + * ... otherwise, take it from spi->controller_data + */ + cs = (unsigned int) spi->controller_data; + } + if (!spi->controller_state) { if (cs != SPI_GPIO_NO_CHIPSELECT) { status = gpio_request(cs, dev_name(&spi->dev)); @@ -250,6 +266,7 @@ static int spi_gpio_setup(struct spi_device *spi) } if (!status) { status = spi_bitbang_setup(spi); + /* in case it was initialized from static board data */ spi_gpio->cs_gpios[spi->chip_select] = cs; } @@ -326,6 +343,55 @@ done: return value; } +#ifdef CONFIG_OF +static struct of_device_id spi_gpio_dt_ids[] = { + { .compatible = "spi-gpio" }, + {} +}; +MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids); + +static int spi_gpio_probe_dt(struct platform_device *pdev) +{ + int ret; + u32 tmp; + struct spi_gpio_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_device(spi_gpio_dt_ids, &pdev->dev); + + if (!of_id) + return 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->sck = of_get_named_gpio(np, "gpio-sck", 0); + pdata->miso = of_get_named_gpio(np, "gpio-miso", 0); + pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0); + + ret = of_property_read_u32(np, "num-chipselects", &tmp); + if (ret < 0) { + dev_err(&pdev->dev, "num-chipselects property not found\n"); + goto error_free; + } + + pdata->num_chipselect = tmp; + pdev->dev.platform_data = pdata; + + return 1; + +error_free: + devm_kfree(&pdev->dev, pdata); + return ret; +} +#else +static inline int spi_probe_dt(struct platform_device *) +{ + return 0; +} +#endif + static int __devinit spi_gpio_probe(struct platform_device *pdev) { int status; @@ -333,6 +399,13 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) struct spi_gpio *spi_gpio; struct spi_gpio_platform_data *pdata; u16 master_flags = 0; + bool use_of = 0; + + status = spi_gpio_probe_dt(pdev); + if (status < 0) + return status; + if (status > 0) + use_of = 1; pdata = pdev->dev.platform_data; #ifdef GENERIC_BITBANG @@ -362,6 +435,23 @@ static int __devinit spi_gpio_probe(struct platform_device *pdev) master->num_chipselect = SPI_N_CHIPSEL; master->setup = spi_gpio_setup; master->cleanup = spi_gpio_cleanup; +#ifdef CONFIG_OF + master->dev.of_node = pdev->dev.of_node; + + if (use_of) { + int i; + struct device_node *np = pdev->dev.of_node; + + /* + * In DT environments, take the CS GPIO from the "cs-gpios" + * property of the node. + */ + + for (i = 0; i < SPI_N_CHIPSEL; i++) + spi_gpio->cs_gpios[i] = + of_get_named_gpio(np, "cs-gpios", i); + } +#endif spi_gpio->bitbang.master = spi_master_get(master); spi_gpio->bitbang.chipselect = spi_gpio_chipselect; @@ -422,8 +512,11 @@ static int __devexit spi_gpio_remove(struct platform_device *pdev) MODULE_ALIAS("platform:" DRIVER_NAME); static struct platform_driver spi_gpio_driver = { - .driver.name = DRIVER_NAME, - .driver.owner = THIS_MODULE, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spi_gpio_dt_ids), + }, .probe = spi_gpio_probe, .remove = __devexit_p(spi_gpio_remove), }; -- cgit v1.2.3 From 010b481834b2b60f7d8543263a63e69396019f7b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 4 Sep 2012 04:40:15 +0200 Subject: mxs/spi: Fix issues when doing long continuous transfer When doing long continuous transfer, eg. from SPI flash via /dev/mtd, the driver dies. This is caused by a bug in the DMA chaining. Rework the DMA transfer code so that this issue does not happen any longer. This involves proper allocation of correct amount of sg-list members. Also, this means proper creation of DMA descriptors. There is actually an important catch to this, the data transfer descriptors must be interleaved with PIO register write descriptor, otherwise the transfer stalls. This can be done in one descriptor, but due to the limitation of the DMA API, it's not possible. It turns out that in order for the SPI DMA to properly support continuous transfers longer than 65280 bytes, there are some very important parts that were left out from the documentation about about the PIO transfer that is used. Firstly, the XFER_SIZE register is not written with the whole length of a transfer, but is written by each and every chained descriptor with the length of the descriptors data buffer. Next, unlike the demo code supplied by FSL, which only writes one PIO word per descriptor, this does not apply if the descriptors are chained, since the XFER_SIZE register must be written. Therefore, it is essential to use four PIO words, CTRL0, CMD0, CMD1, XFER_SIZE. CMD0 and CMD1 are written with zero, since they don't apply. The DMA programs the PIO words in an incrementing order, so four PIO words. Finally, unlike the demo code supplied by FSL, the SSP_CTRL0_IGNORE_CRC must not be set during the whole transfer, but it must be set only on the last descriptor in the chain. Lastly, this code lends code from drivers/mtd/nand/omap2.c, which solves trouble when the buffer supplied to the DMA transfer was vmalloc()'d. So with this patch, it's safe to use /dev/mtdblockX interface again. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 141 +++++++++++++++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 53 deletions(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 10d34ebe9ca..bcba098e97c 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -53,9 +53,9 @@ #define DRIVER_NAME "mxs-spi" -#define SSP_TIMEOUT 1000 /* 1000 ms */ +/* Use 10S timeout for very long transfers, it should suffice. */ +#define SSP_TIMEOUT 10000 -#define SG_NUM 4 #define SG_MAXLEN 0xff00 struct mxs_spi { @@ -219,61 +219,94 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, int *first, int *last, int write) { struct mxs_ssp *ssp = &spi->ssp; - struct dma_async_tx_descriptor *desc; - struct scatterlist sg[SG_NUM]; + struct dma_async_tx_descriptor *desc = NULL; + const bool vmalloced_buf = is_vmalloc_addr(buf); + const int desc_len = vmalloced_buf ? PAGE_SIZE : SG_MAXLEN; + const int sgs = DIV_ROUND_UP(len, desc_len); int sg_count; - uint32_t pio = BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); - int ret; - - if (len > SG_NUM * SG_MAXLEN) { - dev_err(ssp->dev, "Data chunk too big for DMA\n"); + int min, ret; + uint32_t ctrl0; + struct page *vm_page; + void *sg_buf; + struct { + uint32_t pio[4]; + struct scatterlist sg; + } *dma_xfer; + + if (!len) return -EINVAL; - } + + dma_xfer = kzalloc(sizeof(*dma_xfer) * sgs, GFP_KERNEL); + if (!dma_xfer) + return -ENOMEM; INIT_COMPLETION(spi->c); + ctrl0 = readl(ssp->base + HW_SSP_CTRL0); + ctrl0 |= BM_SSP_CTRL0_DATA_XFER | mxs_spi_cs_to_reg(cs); + if (*first) - pio |= BM_SSP_CTRL0_LOCK_CS; - if (*last) - pio |= BM_SSP_CTRL0_IGNORE_CRC; + ctrl0 |= BM_SSP_CTRL0_LOCK_CS; if (!write) - pio |= BM_SSP_CTRL0_READ; - - if (ssp->devid == IMX23_SSP) - pio |= len; - else - writel(len, ssp->base + HW_SSP_XFER_SIZE); - - /* Queue the PIO register write transfer. */ - desc = dmaengine_prep_slave_sg(ssp->dmach, - (struct scatterlist *)&pio, - 1, DMA_TRANS_NONE, 0); - if (!desc) { - dev_err(ssp->dev, - "Failed to get PIO reg. write descriptor.\n"); - return -EINVAL; - } + ctrl0 |= BM_SSP_CTRL0_READ; /* Queue the DMA data transfer. */ - sg_init_table(sg, (len / SG_MAXLEN) + 1); - sg_count = 0; - while (len) { - sg_set_buf(&sg[sg_count++], buf, min(len, SG_MAXLEN)); - len -= min(len, SG_MAXLEN); - buf += min(len, SG_MAXLEN); - } - dma_map_sg(ssp->dev, sg, sg_count, - write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - desc = dmaengine_prep_slave_sg(ssp->dmach, sg, sg_count, - write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - - if (!desc) { - dev_err(ssp->dev, - "Failed to get DMA data write descriptor.\n"); - ret = -EINVAL; - goto err; + for (sg_count = 0; sg_count < sgs; sg_count++) { + min = min(len, desc_len); + + /* Prepare the transfer descriptor. */ + if ((sg_count + 1 == sgs) && *last) + ctrl0 |= BM_SSP_CTRL0_IGNORE_CRC; + + if (ssp->devid == IMX23_SSP) + ctrl0 |= min; + + dma_xfer[sg_count].pio[0] = ctrl0; + dma_xfer[sg_count].pio[3] = min; + + if (vmalloced_buf) { + vm_page = vmalloc_to_page(buf); + if (!vm_page) { + ret = -ENOMEM; + goto err_vmalloc; + } + sg_buf = page_address(vm_page) + + ((size_t)buf & ~PAGE_MASK); + } else { + sg_buf = buf; + } + + sg_init_one(&dma_xfer[sg_count].sg, sg_buf, min); + ret = dma_map_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, + write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + len -= min; + buf += min; + + /* Queue the PIO register write transfer. */ + desc = dmaengine_prep_slave_sg(ssp->dmach, + (struct scatterlist *)dma_xfer[sg_count].pio, + (ssp->devid == IMX23_SSP) ? 1 : 4, + DMA_TRANS_NONE, + sg_count ? DMA_PREP_INTERRUPT : 0); + if (!desc) { + dev_err(ssp->dev, + "Failed to get PIO reg. write descriptor.\n"); + ret = -EINVAL; + goto err_mapped; + } + + desc = dmaengine_prep_slave_sg(ssp->dmach, + &dma_xfer[sg_count].sg, 1, + write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!desc) { + dev_err(ssp->dev, + "Failed to get DMA data write descriptor.\n"); + ret = -EINVAL; + goto err_mapped; + } } /* @@ -289,21 +322,23 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, int cs, ret = wait_for_completion_timeout(&spi->c, msecs_to_jiffies(SSP_TIMEOUT)); - if (!ret) { dev_err(ssp->dev, "DMA transfer timeout\n"); ret = -ETIMEDOUT; - goto err; + goto err_vmalloc; } ret = 0; -err: - for (--sg_count; sg_count >= 0; sg_count--) { - dma_unmap_sg(ssp->dev, &sg[sg_count], 1, +err_vmalloc: + while (--sg_count >= 0) { +err_mapped: + dma_unmap_sg(ssp->dev, &dma_xfer[sg_count].sg, 1, write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); } + kfree(dma_xfer); + return ret; } -- cgit v1.2.3 From 204e706fa2ac1a62ff6423039945eb567c6c7efc Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 4 Sep 2012 04:40:16 +0200 Subject: mxs/spi: Increment the transfer length only if transfer succeeded The transfer function incremented (struct spi_message)->actual_length unconditionally, even if the transfer failed. Rectify this by incrementing this only if transfer succeeded. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index bcba098e97c..138c8523dbb 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -473,12 +473,12 @@ static int mxs_spi_transfer_one(struct spi_master *master, &first, &last, 0); } - m->actual_length += t->len; if (status) { stmp_reset_block(ssp->base); break; } + m->actual_length += t->len; first = last = 0; } -- cgit v1.2.3 From 727c10e3e54c4404f6842d246b15fe3703d33556 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 4 Sep 2012 04:40:17 +0200 Subject: mxs/spi: Decrement the DMA/PIO border This driver checks the length of transfer to be made and based on this information, either chooses to transfer data via DMA or PIO. Decrement this border further to gain better performace eg. during SPI flash writes. Empiric measurement shows that this gives extra 3kB/s write speed with a M25P80 flash clocked at 40MHz. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 138c8523dbb..21e1dcad391 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -445,7 +445,7 @@ static int mxs_spi_transfer_one(struct spi_master *master, * DMA only: 2.164808 seconds, 473.0KB/s * Combined: 1.676276 seconds, 610.9KB/s */ - if (t->len <= 256) { + if (t->len < 32) { writel(BM_SSP_CTRL1_DMA_ENABLE, ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR); -- cgit v1.2.3 From f13639dc6043eb67e308aa5cf96717a86c10f8b9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 4 Sep 2012 04:40:18 +0200 Subject: mxs/spi: Rework the mxs_ssp_timeout to be more readable Rework the mxs_ssp_timeout() function to make it a bit more readable and hopefully less error prone. Also, have only one successful exit from the function and one failing exit instead of two. Finally, discard the udelay() from this function altogether, as this tightloop is quick enough it's pointless. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 21e1dcad391..556e5ef907f 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -177,25 +177,23 @@ static inline void mxs_spi_disable(struct mxs_spi *spi) static int mxs_ssp_wait(struct mxs_spi *spi, int offset, int mask, bool set) { - unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); + const unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); struct mxs_ssp *ssp = &spi->ssp; uint32_t reg; - while (1) { + do { reg = readl_relaxed(ssp->base + offset); - if (set && ((reg & mask) == mask)) - break; + if (!set) + reg = ~reg; - if (!set && ((~reg & mask) == mask)) - break; + reg &= mask; - udelay(1); + if (reg == mask) + return 0; + } while (time_before(jiffies, timeout)); - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - } - return 0; + return -ETIMEDOUT; } static void mxs_ssp_dma_irq_callback(void *param) -- cgit v1.2.3 From 38e271cde5ae62b0b94ff8e8e7e1e1395da7dbf5 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 4 Sep 2012 16:58:35 +0530 Subject: spi: spi-tle62x0: Use module_spi_driver macro module_spi_driver eliminates module_init and module_exit calls and makes the code simpler. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- drivers/spi/spi-tle62x0.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c index 0ce5c12aab5..24421024dea 100644 --- a/drivers/spi/spi-tle62x0.c +++ b/drivers/spi/spi-tle62x0.c @@ -316,18 +316,7 @@ static struct spi_driver tle62x0_driver = { .remove = __devexit_p(tle62x0_remove), }; -static __init int tle62x0_init(void) -{ - return spi_register_driver(&tle62x0_driver); -} - -static __exit void tle62x0_exit(void) -{ - spi_unregister_driver(&tle62x0_driver); -} - -module_init(tle62x0_init); -module_exit(tle62x0_exit); +module_spi_driver(tle62x0_driver); MODULE_AUTHOR("Ben Dooks "); MODULE_DESCRIPTION("TLE62x0 SPI driver"); -- cgit v1.2.3 From b4b8482690d97ea5421acf71e9e397fe0c5a25b8 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Mon, 3 Sep 2012 10:14:29 +0200 Subject: spi/pl022: Fix chipselects pointer computation The new chip select handling via GPIO introduced a pointer computation bug: (int *) pl022 + sizeof(struct pl022) doesn't point to the data immediately after the actual struct pl022 (as was intended) but to a multiple of bytes after it because of the (int *) type. Replacing the kludgy pointer arithmetic with managed memory allocation for the chip selects. Reported-by: Shiraz Hashim Signed-off-by: Roland Stigge Reviewed-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 959f2acff2d..827ad5152d8 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2053,8 +2053,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) } /* Allocate master with space for data */ - master = spi_alloc_master(dev, sizeof(struct pl022) + sizeof(int) * - num_cs); + master = spi_alloc_master(dev, sizeof(struct pl022)); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); status = -ENOMEM; @@ -2066,8 +2065,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->master_info = platform_info; pl022->adev = adev; pl022->vendor = id->data; - /* Point chipselects to allocated memory beyond the main struct */ - pl022->chipselects = (int *) pl022 + sizeof(struct pl022); + pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int), + GFP_KERNEL); /* * Bus Number Which has been Assigned to this SSP controller -- cgit v1.2.3 From 78bfee0e1e2e22c3062b41be8db618e7484c8e35 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 1 Sep 2012 18:33:08 +0200 Subject: spi: spi-sh-hspi: drop frees of devm_ alloc'd data devm free functions should not have to be explicitly used. A semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ @@ ( * devm_kfree(...); | * devm_free_irq(...); | * devm_iounmap(...); | * devm_release_region(...); | * devm_release_mem_region(...); ) // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- drivers/spi/spi-sh-hspi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 934138c7b3d..796c077ef43 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -283,7 +283,7 @@ static int __devinit hspi_probe(struct platform_device *pdev) ret = spi_register_master(master); if (ret < 0) { dev_err(&pdev->dev, "spi_register_master error.\n"); - goto error2; + goto error1; } pm_runtime_enable(&pdev->dev); @@ -292,8 +292,6 @@ static int __devinit hspi_probe(struct platform_device *pdev) return 0; - error2: - devm_iounmap(hspi->dev, hspi->addr); error1: clk_put(clk); error0: @@ -310,7 +308,6 @@ static int __devexit hspi_remove(struct platform_device *pdev) clk_put(hspi->clk); spi_unregister_master(hspi->master); - devm_iounmap(hspi->dev, hspi->addr); return 0; } -- cgit v1.2.3 From e64d07a2dae569fc3c938adac777562a1d6f151e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 22 Aug 2012 22:38:35 +0200 Subject: spi/mxs: Make the SPI block clock speed configurable via DT Add "clock-frequency" property, which allows configuring the SPI block's base speed. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/mxs-spi.txt | 4 ++++ drivers/spi/spi-mxs.c | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/mxs-spi.txt b/Documentation/devicetree/bindings/spi/mxs-spi.txt index c36296f5ad2..e2e13957c2a 100644 --- a/Documentation/devicetree/bindings/spi/mxs-spi.txt +++ b/Documentation/devicetree/bindings/spi/mxs-spi.txt @@ -6,6 +6,10 @@ Required properties: - interrupts: Should contain SSP interrupts (error irq first, dma irq second) - fsl,ssp-dma-channel: APBX DMA channel for the SSP +Optional properties: +- clock-frequency : Input clock frequency to the SPI block in Hz. + Default is 160000000 Hz. + Example: ssp0: ssp@80010000 { diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 556e5ef907f..edf1360ab09 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -520,10 +520,17 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) struct pinctrl *pinctrl; struct clk *clk; void __iomem *base; - int devid, dma_channel; + int devid, dma_channel, clk_freq; int ret = 0, irq_err, irq_dma; dma_cap_mask_t mask; + /* + * Default clock speed for the SPI core. 160MHz seems to + * work reasonably well with most SPI flashes, so use this + * as a default. Override with "clock-frequency" DT prop. + */ + const int clk_freq_default = 160000000; + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_err = platform_get_irq(pdev, 0); irq_dma = platform_get_irq(pdev, 1); @@ -555,12 +562,18 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) "Failed to get DMA channel\n"); return -EINVAL; } + + ret = of_property_read_u32(np, "clock-frequency", + &clk_freq); + if (ret) + clk_freq = clk_freq_default; } else { dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!dmares) return -EINVAL; devid = pdev->id_entry->driver_data; dma_channel = dmares->start; + clk_freq = clk_freq_default; } master = spi_alloc_master(&pdev->dev, sizeof(*spi)); @@ -598,12 +611,8 @@ static int __devinit mxs_spi_probe(struct platform_device *pdev) goto out_master_free; } - /* - * Crank up the clock to 120MHz, this will be further divided onto a - * proper speed. - */ clk_prepare_enable(ssp->clk); - clk_set_rate(ssp->clk, 120 * 1000 * 1000); + clk_set_rate(ssp->clk, clk_freq); ssp->clk_rate = clk_get_rate(ssp->clk) / 1000; stmp_reset_block(ssp->base); -- cgit v1.2.3 From ac2cb30b4792340d932545f41a8335da2632027d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 7 Sep 2012 08:23:06 +0800 Subject: spi/gpio: Fix stub for spi_gpio_probe_dt() The gpio_ was missing from the name. Add a name for the parameter while we're at it since GCC warns. Reported-by: Stephen Rothwell Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index aed16159584..a2b50c516b3 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -386,7 +386,7 @@ error_free: return ret; } #else -static inline int spi_probe_dt(struct platform_device *) +static inline int spi_gpio_probe_dt(struct platform_device *pdev) { return 0; } -- cgit v1.2.3 From d7b4394e780b02511c8a7a499380cdd56316c770 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Tue, 11 Sep 2012 12:13:20 +0530 Subject: spi: omap2-mcspi: Cleanup the omap2_mcspi_txrx_dma function Currently in omap2_mcspi_txrx_dma the tx and the rx support is interleaved. Make the rx related code in omap2_mcspi_rx_dma and the tx related code omap2_mcspi_tx_dma and call the functions. While at it remove the braces in the if statements which has only one line. Also fix ["foo * bar" to "foo *bar"] warn for the rx and tx variables. Only a cleanup no functional change. Signed-off-by: Shubhrajyoti D Tested-by: Felipe Balbi Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 256 ++++++++++++++++++++++++------------------ 1 file changed, 144 insertions(+), 112 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 164c15d6a1b..5560b708e62 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -315,49 +315,27 @@ static void omap2_mcspi_tx_callback(void *data) omap2_mcspi_set_dma_req(spi, 0, 0); } -static unsigned -omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) +static void omap2_mcspi_tx_dma(struct spi_device *spi, + struct spi_transfer *xfer, + struct dma_slave_config cfg) { struct omap2_mcspi *mcspi; - struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi_dma *mcspi_dma; unsigned int count; - int word_len, element_count; - int elements = 0; - u32 l; u8 * rx; const u8 * tx; void __iomem *chstat_reg; - struct dma_slave_config cfg; - enum dma_slave_buswidth width; - unsigned es; + struct omap2_mcspi_cs *cs = spi->controller_state; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - l = mcspi_cached_chconf0(spi); + count = xfer->len; + rx = xfer->rx_buf; + tx = xfer->tx_buf; chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; - if (cs->word_len <= 8) { - width = DMA_SLAVE_BUSWIDTH_1_BYTE; - es = 1; - } else if (cs->word_len <= 16) { - width = DMA_SLAVE_BUSWIDTH_2_BYTES; - es = 2; - } else { - width = DMA_SLAVE_BUSWIDTH_4_BYTES; - es = 4; - } - - memset(&cfg, 0, sizeof(cfg)); - cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; - cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; - cfg.src_addr_width = width; - cfg.dst_addr_width = width; - cfg.src_maxburst = 1; - cfg.dst_maxburst = 1; - - if (xfer->tx_buf && mcspi_dma->dma_tx) { + if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; struct scatterlist sg; @@ -368,7 +346,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) 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); + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_tx_callback; tx->callback_param = spi; @@ -377,8 +355,50 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) /* FIXME: fall back to PIO? */ } } + dma_async_issue_pending(mcspi_dma->dma_tx); + omap2_mcspi_set_dma_req(spi, 0, 1); - if (xfer->rx_buf && mcspi_dma->dma_rx) { + wait_for_completion(&mcspi_dma->dma_tx_completion); + dma_unmap_single(mcspi->dev, xfer->tx_dma, count, + DMA_TO_DEVICE); + + /* for TX_ONLY mode, be sure all words have shifted out */ + if (rx == NULL) { + if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS) < 0) + dev_err(&spi->dev, "TXS timed out\n"); + else if (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0) + dev_err(&spi->dev, "EOT timed out\n"); + } +} + +static unsigned +omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, + struct dma_slave_config cfg, + unsigned es) +{ + struct omap2_mcspi *mcspi; + struct omap2_mcspi_dma *mcspi_dma; + unsigned int count; + u32 l; + int elements = 0; + int word_len, element_count; + struct omap2_mcspi_cs *cs = spi->controller_state; + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + count = xfer->len; + word_len = cs->word_len; + l = mcspi_cached_chconf0(spi); + + if (word_len <= 8) + element_count = count; + else if (word_len <= 16) + element_count = count >> 1; + else /* word_len <= 32 */ + element_count = count >> 2; + + if (mcspi_dma->dma_rx) { struct dma_async_tx_descriptor *tx; struct scatterlist sg; size_t len = xfer->len - es; @@ -393,108 +413,120 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) sg_dma_len(&sg) = len; tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_rx_callback; tx->callback_param = spi; dmaengine_submit(tx); } else { - /* FIXME: fall back to PIO? */ - } - } - - count = xfer->len; - word_len = cs->word_len; - - rx = xfer->rx_buf; - tx = xfer->tx_buf; - - if (word_len <= 8) { - element_count = count; - } else if (word_len <= 16) { - element_count = count >> 1; - } else /* word_len <= 32 */ { - element_count = count >> 2; - } - - if (tx != NULL) { - dma_async_issue_pending(mcspi_dma->dma_tx); - omap2_mcspi_set_dma_req(spi, 0, 1); - } - - if (rx != NULL) { - dma_async_issue_pending(mcspi_dma->dma_rx); - omap2_mcspi_set_dma_req(spi, 1, 1); - } - - if (tx != NULL) { - wait_for_completion(&mcspi_dma->dma_tx_completion); - dma_unmap_single(mcspi->dev, xfer->tx_dma, count, - DMA_TO_DEVICE); - - /* for TX_ONLY mode, be sure all words have shifted out */ - if (rx == NULL) { - if (mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_TXS) < 0) - dev_err(&spi->dev, "TXS timed out\n"); - else if (mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_EOT) < 0) - dev_err(&spi->dev, "EOT timed out\n"); + /* FIXME: fall back to PIO? */ } } - if (rx != NULL) { - wait_for_completion(&mcspi_dma->dma_rx_completion); - dma_unmap_single(mcspi->dev, xfer->rx_dma, count, - DMA_FROM_DEVICE); - omap2_mcspi_set_enable(spi, 0); + dma_async_issue_pending(mcspi_dma->dma_rx); + omap2_mcspi_set_dma_req(spi, 1, 1); - elements = element_count - 1; + wait_for_completion(&mcspi_dma->dma_rx_completion); + dma_unmap_single(mcspi->dev, xfer->rx_dma, count, + DMA_FROM_DEVICE); + omap2_mcspi_set_enable(spi, 0); - if (l & OMAP2_MCSPI_CHCONF_TURBO) { - elements--; + elements = element_count - 1; - if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) - & OMAP2_MCSPI_CHSTAT_RXS)) { - u32 w; - - w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); - if (word_len <= 8) - ((u8 *)xfer->rx_buf)[elements++] = w; - else if (word_len <= 16) - ((u16 *)xfer->rx_buf)[elements++] = w; - else /* word_len <= 32 */ - ((u32 *)xfer->rx_buf)[elements++] = w; - } else { - dev_err(&spi->dev, - "DMA RX penultimate word empty"); - count -= (word_len <= 8) ? 2 : - (word_len <= 16) ? 4 : - /* word_len <= 32 */ 8; - omap2_mcspi_set_enable(spi, 1); - return count; - } - } + if (l & OMAP2_MCSPI_CHCONF_TURBO) { + elements--; if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) - & OMAP2_MCSPI_CHSTAT_RXS)) { + & OMAP2_MCSPI_CHSTAT_RXS)) { u32 w; w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); if (word_len <= 8) - ((u8 *)xfer->rx_buf)[elements] = w; + ((u8 *)xfer->rx_buf)[elements++] = w; else if (word_len <= 16) - ((u16 *)xfer->rx_buf)[elements] = w; + ((u16 *)xfer->rx_buf)[elements++] = w; else /* word_len <= 32 */ - ((u32 *)xfer->rx_buf)[elements] = w; + ((u32 *)xfer->rx_buf)[elements++] = w; } else { - dev_err(&spi->dev, "DMA RX last word empty"); - count -= (word_len <= 8) ? 1 : - (word_len <= 16) ? 2 : - /* word_len <= 32 */ 4; + dev_err(&spi->dev, "DMA RX penultimate word empty"); + count -= (word_len <= 8) ? 2 : + (word_len <= 16) ? 4 : + /* word_len <= 32 */ 8; + omap2_mcspi_set_enable(spi, 1); + return count; } - omap2_mcspi_set_enable(spi, 1); } + if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) + & OMAP2_MCSPI_CHSTAT_RXS)) { + u32 w; + + w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); + if (word_len <= 8) + ((u8 *)xfer->rx_buf)[elements] = w; + else if (word_len <= 16) + ((u16 *)xfer->rx_buf)[elements] = w; + else /* word_len <= 32 */ + ((u32 *)xfer->rx_buf)[elements] = w; + } else { + dev_err(&spi->dev, "DMA RX last word empty"); + count -= (word_len <= 8) ? 1 : + (word_len <= 16) ? 2 : + /* word_len <= 32 */ 4; + } + omap2_mcspi_set_enable(spi, 1); + return count; +} + +static unsigned +omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct omap2_mcspi *mcspi; + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi_dma *mcspi_dma; + unsigned int count; + u32 l; + u8 *rx; + const u8 *tx; + struct dma_slave_config cfg; + enum dma_slave_buswidth width; + unsigned es; + + mcspi = spi_master_get_devdata(spi->master); + mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + l = mcspi_cached_chconf0(spi); + + + if (cs->word_len <= 8) { + width = DMA_SLAVE_BUSWIDTH_1_BYTE; + es = 1; + } else if (cs->word_len <= 16) { + width = DMA_SLAVE_BUSWIDTH_2_BYTES; + es = 2; + } else { + width = DMA_SLAVE_BUSWIDTH_4_BYTES; + es = 4; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; + cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; + cfg.src_addr_width = width; + cfg.dst_addr_width = width; + cfg.src_maxburst = 1; + cfg.dst_maxburst = 1; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + + count = xfer->len; + + if (tx != NULL) + omap2_mcspi_tx_dma(spi, xfer, cfg); + + if (rx != NULL) + return omap2_mcspi_rx_dma(spi, xfer, cfg, es); + return count; } -- cgit v1.2.3 From 4f5e1b370845a0a5789ff7271004ca49e5baa46f Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 19 Sep 2012 14:23:46 +0200 Subject: spi/pl022: adopt pinctrl support Amend the PL022 pin controller to optionally take a pin control handle and set the state of the pins to "default" on boot and runtime resume, and to "sleep" at runtime suspend. This way we will dynamically save power on the SPI busses, for example some electronic designs may be able to ground the pins when unused instead of pull-up. Some pin controllers may want to set the pins as wake-up sources when sleeping. Effect on platforms using the PL022 driver: - If the platform does not use pin control - no semantic effect, the pinctrl stubs will kick in and resolve the situation. - Platforms using this driver and have pin control but no function defined for the PL022 need to either supply a "default" function in their map or enable pinctrl dummies so the driver is satisfied. - Platforms using this driver with hogs for setting up the PL022 pin control - stop using hogs to take the pl022 pin control handle, let the driver handle this. I'be looked at some platforms that may be affected: - SPEAr: appears to define the proper functions in their device trees and not hogging them, so things should be smooth, the driver will simply start to take its pins. - Ux500: the proper function is defined and will be taken properly by the driver. New sleep states introduced by a separate patch to ux500 but no regression, since the default state is sufficient. - U300: old hog deleted as part of this patch. - LPC32xx: does not appear to be using pinctrl. - ARM Integrator IMPD1, RealView & Versatile: does not use pinctrl. Tested-by: Roland Stigge Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- arch/arm/mach-u300/core.c | 3 --- drivers/spi/spi-pl022.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-u300/core.c b/arch/arm/mach-u300/core.c index 03acf1883ec..281292ebac2 100644 --- a/arch/arm/mach-u300/core.c +++ b/arch/arm/mach-u300/core.c @@ -1604,9 +1604,6 @@ static struct u300_mux_hog u300_mux_hogs[] = { { .dev = &uart0_device.dev, }, - { - .dev = &pl022_device.dev, - }, { .dev = &mmcsd_device.dev, }, diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 827ad5152d8..b6cfb7b3a59 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -42,6 +42,7 @@ #include #include #include +#include /* * This macro is used to define some register default values. @@ -367,6 +368,10 @@ struct pl022 { resource_size_t phybase; void __iomem *virtbase; struct clk *clk; + /* Two optional pin states - default & sleep */ + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; struct spi_master *master; struct pl022_ssp_controller *master_info; /* Message per-transfer pump */ @@ -2068,6 +2073,28 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int), GFP_KERNEL); + pl022->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pl022->pinctrl)) { + status = PTR_ERR(pl022->pinctrl); + goto err_no_pinctrl; + } + + pl022->pins_default = pinctrl_lookup_state(pl022->pinctrl, + PINCTRL_STATE_DEFAULT); + /* enable pins to be muxed in and configured */ + if (!IS_ERR(pl022->pins_default)) { + status = pinctrl_select_state(pl022->pinctrl, + pl022->pins_default); + if (status) + dev_err(dev, "could not set default pins\n"); + } else + dev_err(dev, "could not get default pinstate\n"); + + pl022->pins_sleep = pinctrl_lookup_state(pl022->pinctrl, + PINCTRL_STATE_SLEEP); + if (IS_ERR(pl022->pins_sleep)) + dev_dbg(dev, "could not get sleep pinstate\n"); + /* * Bus Number Which has been Assigned to this SSP controller * on this board @@ -2218,6 +2245,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) amba_release_regions(adev); err_no_ioregion: err_no_gpio: + err_no_pinctrl: spi_master_put(master); err_no_master: err_no_pdata: @@ -2291,15 +2319,33 @@ static int pl022_resume(struct device *dev) static int pl022_runtime_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); + int status = 0; clk_disable(pl022->clk); + /* Optionally let pins go into sleep states */ + if (!IS_ERR(pl022->pins_sleep)) { + status = pinctrl_select_state(pl022->pinctrl, + pl022->pins_sleep); + if (status) + dev_err(dev, "could not set pins to sleep state\n"); + } + return 0; } static int pl022_runtime_resume(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); + int status = 0; + + /* Optionaly enable pins to be muxed in and configured */ + if (!IS_ERR(pl022->pins_default)) { + status = pinctrl_select_state(pl022->pinctrl, + pl022->pins_default); + if (status) + dev_err(dev, "could not set default pins\n"); + } clk_enable(pl022->clk); -- cgit v1.2.3 From ec155afa82cb10d97b6c10d6ede5b0ffa321e816 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 18 Sep 2012 08:01:25 -0400 Subject: spi: omap2-mcspi: add pinctrl support Adds pinctrl support to support OMAP platforms that boot from DT and rely on pinctrl support to set pinmuxes. Signed-off-by: Matt Porter Acked-by: Shubhrajyoti D Acked-by: Tony Lindgren Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 5560b708e62..9926f2784bb 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include @@ -1148,6 +1150,7 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) static int bus_num = 1; struct device_node *node = pdev->dev.of_node; const struct of_device_id *match; + struct pinctrl *pinctrl; master = spi_alloc_master(&pdev->dev, sizeof *mcspi); if (master == NULL) { @@ -1243,6 +1246,11 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev) if (status < 0) goto dma_chnl_free; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, + "pins are not configured from the driver\n"); + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_enable(&pdev->dev); -- cgit v1.2.3 From 5bee3b94d945851d6cb624a5e6808d9a815ebca0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 13 Sep 2012 16:31:30 +0200 Subject: spi/s3c64xx: Don't free controller_data on non-dt platforms When s3c64xx-spi is instantiated from device tree an instance of struct s3c64xx_spi_csinfo is dynamically allocated in the driver. For non-dt platform it is passed from board code through spi_register_board_info(). On error path in s3c64xx_spi_setup() function there is an attempt to free this data struct s3c64xx_spi_csinfo object as it would have been allocated in the driver for both, dt and non-dt based platforms. This leads to following bug when gpio request fails: spi spi1.0: Failed to get /CS gpio [21]: -16 kernel BUG at mm/slub.c:3478! Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM Modules linked in: CPU: 0 Not tainted (3.6.0-rc5-00092-g9b0b493-dirty #6111) PC is at kfree+0x148/0x158 LR is at s3c64xx_spi_setup+0xac/0x290 pc : [] lr : [] psr: 40000013 sp : ee043e10 ip : c032883c fp : c0481f7c r10: ee0abd80 r9 : 00000063 r8 : 00000000 r7 : ee129e78 r6 : ee104a00 r5 : fffffff0 r4 : c047bc64 r3 : 40000400 r2 : c047bc64 r1 : c04def60 r0 : 0004047b Flags: nZcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c5387d Table: 4000404a DAC: 00000015 Process swapper/0 (pid: 1, stack limit = 0xee0422f0) Stack: (0xee043e10 to 0xee044000) ... [] (kfree+0x148/0x158) from [] (s3c64xx_spi_setup+0xac/0x290) [] (s3c64xx_spi_setup+0xac/0x290) from [] (spi_setup+0x34/0x4c) [] (spi_setup+0x34/0x4c) from [] (spi_add_device+0x98/0x128) [] (spi_add_device+0x98/0x128) from [] (spi_new_device+0x74/0xa8) [] (spi_new_device+0x74/0xa8) from [] (spi_match_master_to_boardinfo+0x24/0x44) [] (spi_match_master_to_boardinfo+0x24/0x44) from [] (spi_register_master+0xf4/0x2a8) [] (spi_register_master+0xf4/0x2a8) from [] (s3c64xx_spi_probe+0x34c/0x42c) [] (s3c64xx_spi_probe+0x34c/0x42c) from [] (platform_drv_probe+0x18/0x1c) There should be no attempt to kfree controller_data when it was externally provided through the board code. Fix this by freeing controller_data only when dev->of_node is not null. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Kukjin Kim Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 0087139090e..c6ad4e1c3dc 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -976,7 +976,8 @@ err_msgq: spi_set_ctldata(spi, NULL); err_gpio_req: - kfree(cs); + if (spi->dev.of_node) + kfree(cs); return err; } -- cgit v1.2.3 From 39a6ac11df6579df0361922f05c43f0fac8daa37 Mon Sep 17 00:00:00 2001 From: Roland Stigge Date: Tue, 18 Sep 2012 15:53:53 +0200 Subject: spi/pl022: Devicetree support w/o platform data Even with devicetree support, we needed platform data to provide some data, leading to mixed device tree and platform data. This patch makes it possible to provide all that information via device tree. Now, the data must be provided via platform data _or_ device tree completely. Only in case of DMA where a callback specification is necessary (dma_filter()), platform data is the only option. Signed-off-by: Roland Stigge Acked-by: Arnd Bergmann Acked-by: Linus Walleij Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi_pl022.txt | 7 ++++ drivers/spi/spi-pl022.c | 39 +++++++++++++++++++--- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi_pl022.txt b/Documentation/devicetree/bindings/spi/spi_pl022.txt index 877f832c30a..f158fd31cfd 100644 --- a/Documentation/devicetree/bindings/spi/spi_pl022.txt +++ b/Documentation/devicetree/bindings/spi/spi_pl022.txt @@ -10,6 +10,13 @@ Optional properties: - cs-gpios : should specify GPIOs used for chipselects. The gpios will be referred to as reg = in the SPI child nodes. If unspecified, a single SPI device without a chip select can be used. +- pl022,autosuspend-delay : delay in ms following transfer completion before + the runtime power management system suspends the + device. A setting of 0 indicates no delay and the + device will be suspended immediately +- pl022,rt : indicates the controller should run the message pump with realtime + priority to minimise the transfer latency on the bus (boolean) + SPI slave nodes must be children of the SPI master node and can contain the following properties. diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index b6cfb7b3a59..3f2f36c79ab 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2029,6 +2029,34 @@ static void pl022_cleanup(struct spi_device *spi) kfree(chip); } +static struct pl022_ssp_controller * +pl022_platform_data_dt_get(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct pl022_ssp_controller *pd; + u32 tmp; + + if (!np) { + dev_err(dev, "no dt node defined\n"); + return NULL; + } + + pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL); + if (!pd) { + dev_err(dev, "cannot allocate platform data memory\n"); + return NULL; + } + + pd->bus_id = -1; + of_property_read_u32(np, "num-cs", &tmp); + pd->num_chipselect = tmp; + of_property_read_u32(np, "pl022,autosuspend-delay", + &pd->autosuspend_delay); + pd->rt = of_property_read_bool(np, "pl022,rt"); + + return pd; +} + static int __devinit pl022_probe(struct amba_device *adev, const struct amba_id *id) { @@ -2041,18 +2069,19 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) dev_info(&adev->dev, "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); - if (platform_info == NULL) { - dev_err(&adev->dev, "probe - no platform data supplied\n"); + if (!platform_info && IS_ENABLED(CONFIG_OF)) + platform_info = pl022_platform_data_dt_get(dev); + + if (!platform_info) { + dev_err(dev, "probe: no platform data defined\n"); status = -ENODEV; goto err_no_pdata; } if (platform_info->num_chipselect) { num_cs = platform_info->num_chipselect; - } else if (IS_ENABLED(CONFIG_OF)) { - of_property_read_u32(np, "num-cs", &num_cs); } else { - dev_err(&adev->dev, "probe: no chip select defined\n"); + dev_err(dev, "probe: no chip select defined\n"); status = -ENODEV; goto err_no_pdata; } -- cgit v1.2.3 From aeef9915b9a40d257ec94f9900a3fb4a15da2ab7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 26 Sep 2012 16:48:36 +0200 Subject: spi/pl022: use more managed resources This switches the PL022 SPI driver to use devm_* managed resources for IRQ, clocks, ioremap and GPIO. Prior to this, the GPIOs would even leak. Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index f8568b43660..15737bcee9f 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1,7 +1,7 @@ /* * A driver for the ARM PL022 PrimeCell SSP/SPI bus master. * - * Copyright (C) 2008-2009 ST-Ericsson AB + * Copyright (C) 2008-2012 ST-Ericsson AB * Copyright (C) 2006 STMicroelectronics Pvt. Ltd. * * Author: Linus Walleij @@ -2074,24 +2074,21 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) if (!platform_info) { dev_err(dev, "probe: no platform data defined\n"); - status = -ENODEV; - goto err_no_pdata; + return -ENODEV; } if (platform_info->num_chipselect) { num_cs = platform_info->num_chipselect; } else { dev_err(dev, "probe: no chip select defined\n"); - status = -ENODEV; - goto err_no_pdata; + return -ENODEV; } /* Allocate master with space for data */ master = spi_alloc_master(dev, sizeof(struct pl022)); if (master == NULL) { dev_err(&adev->dev, "probe - cannot alloc SPI master\n"); - status = -ENOMEM; - goto err_no_master; + return -ENOMEM; } pl022 = spi_master_get_devdata(master); @@ -2153,7 +2150,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->chipselects[i] = cs_gpio; if (gpio_is_valid(cs_gpio)) { - if (gpio_request(cs_gpio, "ssp-pl022")) + if (devm_gpio_request(dev, cs_gpio, "ssp-pl022")) dev_err(&adev->dev, "could not request %d gpio\n", cs_gpio); @@ -2180,7 +2177,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) goto err_no_ioregion; pl022->phybase = adev->res.start; - pl022->virtbase = ioremap(adev->res.start, resource_size(&adev->res)); + pl022->virtbase = devm_ioremap(dev, adev->res.start, + resource_size(&adev->res)); if (pl022->virtbase == NULL) { status = -ENOMEM; goto err_no_ioremap; @@ -2190,7 +2188,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_resume(dev); - pl022->clk = clk_get(&adev->dev, NULL); + pl022->clk = devm_clk_get(&adev->dev, NULL); if (IS_ERR(pl022->clk)) { status = PTR_ERR(pl022->clk); dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n"); @@ -2218,8 +2216,8 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) SSP_CR1(pl022->virtbase)); load_ssp_default_config(pl022); - status = request_irq(adev->irq[0], pl022_interrupt_handler, 0, "pl022", - pl022); + status = devm_request_irq(dev, adev->irq[0], pl022_interrupt_handler, + 0, "pl022", pl022); if (status < 0) { dev_err(&adev->dev, "probe - cannot get IRQ (%d)\n", status); goto err_no_irq; @@ -2259,24 +2257,18 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id) err_spi_register: if (platform_info->enable_dma) pl022_dma_remove(pl022); - - free_irq(adev->irq[0], pl022); err_no_irq: clk_disable(pl022->clk); err_no_clk_en: clk_unprepare(pl022->clk); err_clk_prep: - clk_put(pl022->clk); err_no_clk: - iounmap(pl022->virtbase); err_no_ioremap: amba_release_regions(adev); err_no_ioregion: err_no_gpio: err_no_pinctrl: spi_master_put(master); - err_no_master: - err_no_pdata: return status; } @@ -2298,12 +2290,9 @@ pl022_remove(struct amba_device *adev) if (pl022->master_info->enable_dma) pl022_dma_remove(pl022); - free_irq(adev->irq[0], pl022); clk_disable(pl022->clk); clk_unprepare(pl022->clk); - clk_put(pl022->clk); pm_runtime_disable(&adev->dev); - iounmap(pl022->virtbase); amba_release_regions(adev); tasklet_disable(&pl022->pump_transfers); spi_unregister_master(pl022->master); -- cgit v1.2.3 From ada7aec7eec0ca2d2a9d2208548813d5dda8108e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 26 Sep 2012 18:06:22 +0200 Subject: spi/pl022: get/put resources on suspend/resume This factors out the resource handling in runtime suspend/resume and also calls it from the ordinary suspend and resume hooks. The semantics require that ordinary PM op suspend is called with runtime PM in resumed mode, so that ordinary suspend can assume that it will e.g. decrease the clock reference counter to 0, runtime resume having previously increased it to 1. Cc: Vipul Kumar Samar Cc: Viresh Kumar Acked-by: Ulf Hansson Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 66 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 15737bcee9f..919464102d3 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2300,6 +2300,45 @@ pl022_remove(struct amba_device *adev) return 0; } +#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME) +/* + * These two functions are used from both suspend/resume and + * the runtime counterparts to handle external resources like + * clocks, pins and regulators when going to sleep. + */ +static void pl022_suspend_resources(struct pl022 *pl022) +{ + int ret; + + clk_disable(pl022->clk); + + /* Optionally let pins go into sleep states */ + if (!IS_ERR(pl022->pins_sleep)) { + ret = pinctrl_select_state(pl022->pinctrl, + pl022->pins_sleep); + if (ret) + dev_err(&pl022->adev->dev, + "could not set pins to sleep state\n"); + } +} + +static void pl022_resume_resources(struct pl022 *pl022) +{ + int ret; + + /* Optionaly enable pins to be muxed in and configured */ + if (!IS_ERR(pl022->pins_default)) { + ret = pinctrl_select_state(pl022->pinctrl, + pl022->pins_default); + if (ret) + dev_err(&pl022->adev->dev, + "could not set default pins\n"); + } + + clk_enable(pl022->clk); +} +#endif + #ifdef CONFIG_SUSPEND static int pl022_suspend(struct device *dev) { @@ -2311,6 +2350,7 @@ static int pl022_suspend(struct device *dev) dev_warn(dev, "cannot suspend master\n"); return ret; } + pl022_suspend_resources(pl022); dev_dbg(dev, "suspended\n"); return 0; @@ -2321,6 +2361,8 @@ static int pl022_resume(struct device *dev) struct pl022 *pl022 = dev_get_drvdata(dev); int ret; + pl022_resume_resources(pl022); + /* Start the queue running */ ret = spi_master_resume(pl022->master); if (ret) @@ -2336,36 +2378,16 @@ static int pl022_resume(struct device *dev) static int pl022_runtime_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - int status = 0; - - clk_disable(pl022->clk); - - /* Optionally let pins go into sleep states */ - if (!IS_ERR(pl022->pins_sleep)) { - status = pinctrl_select_state(pl022->pinctrl, - pl022->pins_sleep); - if (status) - dev_err(dev, "could not set pins to sleep state\n"); - } + pl022_suspend_resources(pl022); return 0; } static int pl022_runtime_resume(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - int status = 0; - - /* Optionaly enable pins to be muxed in and configured */ - if (!IS_ERR(pl022->pins_default)) { - status = pinctrl_select_state(pl022->pinctrl, - pl022->pins_default); - if (status) - dev_err(dev, "could not set default pins\n"); - } - - clk_enable(pl022->clk); + pl022_resume_resources(pl022); return 0; } #endif -- cgit v1.2.3 From c09b890b763df3ccd79a2c34c2f1abeb73179caf Mon Sep 17 00:00:00 2001 From: Knut Wohlrab Date: Tue, 25 Sep 2012 13:21:57 +0200 Subject: spi/imx: set the inactive state of the clock according to the clock polarity There are SPI devices which need a SPI clock with active low polarity and high inactive state. Add the setting of the inactive state ECSPIx_CONFIGREG:SCLK CTL according to the clock polarity ECSPIx_CONFIGREG:SCLK POL: DT without "spi-cpol" = 0 = clock active high polarity = inactive state low DT with "spi-cpol" = 1 = clock active low polarity = inactive state high Signed-off-by: Knut Wohlrab Signed-off-by: Dirk Behme Acked-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index e834ff8c018..d64655b70b5 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -197,6 +197,7 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, #define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4)) #define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8)) #define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12)) +#define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs) + 20)) #define MX51_ECSPI_INT 0x10 #define MX51_ECSPI_INT_TEEN (1 << 0) @@ -287,9 +288,10 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, if (config->mode & SPI_CPHA) cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs); - if (config->mode & SPI_CPOL) + if (config->mode & SPI_CPOL) { cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs); - + cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs); + } if (config->mode & SPI_CS_HIGH) cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs); -- cgit v1.2.3 From 536a53a300d0d40152796eefb0a9e6e36ca37f7d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 28 Sep 2012 10:15:48 -0600 Subject: spi: remove completely broken Tegra driver The current SPI driver has many issues. Examples are: * Segfaulting on most transfers due to expecting all transfers to have both RX and TX buffers. * Hanging on TX transfers since the whole driver flow is driven by RX DMA completion, but the HW is only told to enable RX for RX transfers. * Use of clk_disable_unprepare() from atomic context. * Once those and other minor issues are fixed, the driver still doesn't actually work. * The driver also implements a deprecated API to the SPI core. For this reason, simply remove the driver completely. This has two advantages: 1) This will remove the last use of Tegra's , which will allow that file to be removed, which is required for single zImage work. 2) The downstream driver is significaly different from the current code. I believe a patch to re-add the downstream driver (with appropriate cleanup) will be much simpler to review if it's a new file rather than randomly interspered with essentially unrelated existing code. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 6 - drivers/spi/Makefile | 1 - drivers/spi/spi-tegra.c | 702 ------------------------------------------------ 3 files changed, 709 deletions(-) delete mode 100644 drivers/spi/spi-tegra.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 63a79437aca..ecc31a1f73f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -377,12 +377,6 @@ config SPI_MXS help SPI driver for Freescale MXS devices. -config SPI_TEGRA - tristate "Nvidia Tegra SPI controller" - depends on ARCH_TEGRA && (TEGRA_SYSTEM_DMA || TEGRA20_APB_DMA) - help - SPI driver for NVidia Tegra SoCs - config SPI_TI_SSP tristate "TI Sequencer Serial Port - SPI Support" depends on MFD_TI_SSP diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 938705644bb..22fd3a7251b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -59,7 +59,6 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o obj-$(CONFIG_SPI_STMP3XXX) += spi-stmp.o -obj-$(CONFIG_SPI_TEGRA) += spi-tegra.o obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c deleted file mode 100644 index e28445d8d27..00000000000 --- a/drivers/spi/spi-tegra.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * Driver for Nvidia TEGRA spi controller. - * - * Copyright (C) 2010 Google, Inc. - * - * Author: - * Erik Gilling - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 -#include -#include -#include - -#include -#include - -#include - -#define SLINK_COMMAND 0x000 -#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) -#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) -#define SLINK_BOTH_EN (1 << 10) -#define SLINK_CS_SW (1 << 11) -#define SLINK_CS_VALUE (1 << 12) -#define SLINK_CS_POLARITY (1 << 13) -#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16) -#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16) -#define SLINK_IDLE_SDA_PULL_LOW (2 << 16) -#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16) -#define SLINK_IDLE_SDA_MASK (3 << 16) -#define SLINK_CS_POLARITY1 (1 << 20) -#define SLINK_CK_SDA (1 << 21) -#define SLINK_CS_POLARITY2 (1 << 22) -#define SLINK_CS_POLARITY3 (1 << 23) -#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24) -#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24) -#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24) -#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24) -#define SLINK_IDLE_SCLK_MASK (3 << 24) -#define SLINK_M_S (1 << 28) -#define SLINK_WAIT (1 << 29) -#define SLINK_GO (1 << 30) -#define SLINK_ENB (1 << 31) - -#define SLINK_COMMAND2 0x004 -#define SLINK_LSBFE (1 << 0) -#define SLINK_SSOE (1 << 1) -#define SLINK_SPIE (1 << 4) -#define SLINK_BIDIROE (1 << 6) -#define SLINK_MODFEN (1 << 7) -#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8) -#define SLINK_CS_ACTIVE_BETWEEN (1 << 17) -#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18) -#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20) -#define SLINK_FIFO_REFILLS_0 (0 << 22) -#define SLINK_FIFO_REFILLS_1 (1 << 22) -#define SLINK_FIFO_REFILLS_2 (2 << 22) -#define SLINK_FIFO_REFILLS_3 (3 << 22) -#define SLINK_FIFO_REFILLS_MASK (3 << 22) -#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26) -#define SLINK_SPC0 (1 << 29) -#define SLINK_TXEN (1 << 30) -#define SLINK_RXEN (1 << 31) - -#define SLINK_STATUS 0x008 -#define SLINK_COUNT(val) (((val) >> 0) & 0x1f) -#define SLINK_WORD(val) (((val) >> 5) & 0x1f) -#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff) -#define SLINK_MODF (1 << 16) -#define SLINK_RX_UNF (1 << 18) -#define SLINK_TX_OVF (1 << 19) -#define SLINK_TX_FULL (1 << 20) -#define SLINK_TX_EMPTY (1 << 21) -#define SLINK_RX_FULL (1 << 22) -#define SLINK_RX_EMPTY (1 << 23) -#define SLINK_TX_UNF (1 << 24) -#define SLINK_RX_OVF (1 << 25) -#define SLINK_TX_FLUSH (1 << 26) -#define SLINK_RX_FLUSH (1 << 27) -#define SLINK_SCLK (1 << 28) -#define SLINK_ERR (1 << 29) -#define SLINK_RDY (1 << 30) -#define SLINK_BSY (1 << 31) - -#define SLINK_MAS_DATA 0x010 -#define SLINK_SLAVE_DATA 0x014 - -#define SLINK_DMA_CTL 0x018 -#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0) -#define SLINK_TX_TRIG_1 (0 << 16) -#define SLINK_TX_TRIG_4 (1 << 16) -#define SLINK_TX_TRIG_8 (2 << 16) -#define SLINK_TX_TRIG_16 (3 << 16) -#define SLINK_TX_TRIG_MASK (3 << 16) -#define SLINK_RX_TRIG_1 (0 << 18) -#define SLINK_RX_TRIG_4 (1 << 18) -#define SLINK_RX_TRIG_8 (2 << 18) -#define SLINK_RX_TRIG_16 (3 << 18) -#define SLINK_RX_TRIG_MASK (3 << 18) -#define SLINK_PACKED (1 << 20) -#define SLINK_PACK_SIZE_4 (0 << 21) -#define SLINK_PACK_SIZE_8 (1 << 21) -#define SLINK_PACK_SIZE_16 (2 << 21) -#define SLINK_PACK_SIZE_32 (3 << 21) -#define SLINK_PACK_SIZE_MASK (3 << 21) -#define SLINK_IE_TXC (1 << 26) -#define SLINK_IE_RXC (1 << 27) -#define SLINK_DMA_EN (1 << 31) - -#define SLINK_STATUS2 0x01c -#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0) -#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f) >> 16) - -#define SLINK_TX_FIFO 0x100 -#define SLINK_RX_FIFO 0x180 - -static const unsigned long spi_tegra_req_sels[] = { - TEGRA_DMA_REQ_SEL_SL2B1, - TEGRA_DMA_REQ_SEL_SL2B2, - TEGRA_DMA_REQ_SEL_SL2B3, - TEGRA_DMA_REQ_SEL_SL2B4, -}; - -#define BB_LEN 32 - -struct spi_tegra_data { - struct spi_master *master; - struct platform_device *pdev; - spinlock_t lock; - - struct clk *clk; - void __iomem *base; - unsigned long phys; - - u32 cur_speed; - - struct list_head queue; - struct spi_transfer *cur; - unsigned cur_pos; - unsigned cur_len; - unsigned cur_bytes_per_word; - - /* The tegra spi controller has a bug which causes the first word - * in PIO transactions to be garbage. Since packed DMA transactions - * require transfers to be 4 byte aligned we need a bounce buffer - * for the generic case. - */ - int dma_req_len; -#if defined(CONFIG_TEGRA_SYSTEM_DMA) - struct tegra_dma_req rx_dma_req; - struct tegra_dma_channel *rx_dma; -#else - struct dma_chan *rx_dma; - struct dma_slave_config sconfig; - struct dma_async_tx_descriptor *rx_dma_desc; - dma_cookie_t rx_cookie; -#endif - u32 *rx_bb; - dma_addr_t rx_bb_phys; -}; - -#if !defined(CONFIG_TEGRA_SYSTEM_DMA) -static void tegra_spi_rx_dma_complete(void *args); -#endif - -static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi, - unsigned long reg) -{ - return readl(tspi->base + reg); -} - -static inline void spi_tegra_writel(struct spi_tegra_data *tspi, - unsigned long val, - unsigned long reg) -{ - writel(val, tspi->base + reg); -} - -static void spi_tegra_go(struct spi_tegra_data *tspi) -{ - unsigned long val; - - wmb(); - - val = spi_tegra_readl(tspi, SLINK_DMA_CTL); - val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN; - val |= SLINK_DMA_BLOCK_SIZE(tspi->dma_req_len / 4 - 1); - spi_tegra_writel(tspi, val, SLINK_DMA_CTL); -#if defined(CONFIG_TEGRA_SYSTEM_DMA) - tspi->rx_dma_req.size = tspi->dma_req_len; - tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req); -#else - tspi->rx_dma_desc = dmaengine_prep_slave_single(tspi->rx_dma, - tspi->rx_bb_phys, tspi->dma_req_len, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); - if (!tspi->rx_dma_desc) { - dev_err(&tspi->pdev->dev, "dmaengine slave prep failed\n"); - return; - } - tspi->rx_dma_desc->callback = tegra_spi_rx_dma_complete; - tspi->rx_dma_desc->callback_param = tspi; - tspi->rx_cookie = dmaengine_submit(tspi->rx_dma_desc); - dma_async_issue_pending(tspi->rx_dma); -#endif - - val |= SLINK_DMA_EN; - spi_tegra_writel(tspi, val, SLINK_DMA_CTL); -} - -static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi, - struct spi_transfer *t) -{ - unsigned len = min(t->len - tspi->cur_pos, BB_LEN * - tspi->cur_bytes_per_word); - u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_pos; - int i, j; - unsigned long val; - - val = spi_tegra_readl(tspi, SLINK_COMMAND); - val &= ~SLINK_WORD_SIZE(~0); - val |= SLINK_WORD_SIZE(len / tspi->cur_bytes_per_word - 1); - spi_tegra_writel(tspi, val, SLINK_COMMAND); - - for (i = 0; i < len; i += tspi->cur_bytes_per_word) { - val = 0; - for (j = 0; j < tspi->cur_bytes_per_word; j++) - val |= tx_buf[i + j] << j * 8; - - spi_tegra_writel(tspi, val, SLINK_TX_FIFO); - } - - tspi->dma_req_len = len / tspi->cur_bytes_per_word * 4; - - return len; -} - -static unsigned spi_tegra_drain_rx_fifo(struct spi_tegra_data *tspi, - struct spi_transfer *t) -{ - unsigned len = tspi->cur_len; - u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_pos; - int i, j; - unsigned long val; - - for (i = 0; i < len; i += tspi->cur_bytes_per_word) { - val = tspi->rx_bb[i / tspi->cur_bytes_per_word]; - for (j = 0; j < tspi->cur_bytes_per_word; j++) - rx_buf[i + j] = (val >> (j * 8)) & 0xff; - } - - return len; -} - -static void spi_tegra_start_transfer(struct spi_device *spi, - struct spi_transfer *t) -{ - struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); - u32 speed; - u8 bits_per_word; - unsigned long val; - - speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; - bits_per_word = t->bits_per_word ? t->bits_per_word : - spi->bits_per_word; - - tspi->cur_bytes_per_word = (bits_per_word - 1) / 8 + 1; - - if (speed != tspi->cur_speed) - clk_set_rate(tspi->clk, speed); - - if (tspi->cur_speed == 0) - clk_prepare_enable(tspi->clk); - - tspi->cur_speed = speed; - - val = spi_tegra_readl(tspi, SLINK_COMMAND2); - val &= ~SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN; - if (t->rx_buf) - val |= SLINK_RXEN; - if (t->tx_buf) - val |= SLINK_TXEN; - val |= SLINK_SS_EN_CS(spi->chip_select); - val |= SLINK_SPIE; - spi_tegra_writel(tspi, val, SLINK_COMMAND2); - - val = spi_tegra_readl(tspi, SLINK_COMMAND); - val &= ~SLINK_BIT_LENGTH(~0); - val |= SLINK_BIT_LENGTH(bits_per_word - 1); - - /* FIXME: should probably control CS manually so that we can be sure - * it does not go low between transfer and to support delay_usecs - * correctly. - */ - val &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA & ~SLINK_CS_SW; - - if (spi->mode & SPI_CPHA) - val |= SLINK_CK_SDA; - - if (spi->mode & SPI_CPOL) - val |= SLINK_IDLE_SCLK_DRIVE_HIGH; - else - val |= SLINK_IDLE_SCLK_DRIVE_LOW; - - val |= SLINK_M_S; - - spi_tegra_writel(tspi, val, SLINK_COMMAND); - - spi_tegra_writel(tspi, SLINK_RX_FLUSH | SLINK_TX_FLUSH, SLINK_STATUS); - - tspi->cur = t; - tspi->cur_pos = 0; - tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, t); - - spi_tegra_go(tspi); -} - -static void spi_tegra_start_message(struct spi_device *spi, - struct spi_message *m) -{ - struct spi_transfer *t; - - m->actual_length = 0; - m->status = 0; - - t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list); - spi_tegra_start_transfer(spi, t); -} - -static void handle_spi_rx_dma_complete(struct spi_tegra_data *tspi) -{ - unsigned long flags; - struct spi_message *m; - struct spi_device *spi; - int timeout = 0; - unsigned long val; - - /* the SPI controller may come back with both the BSY and RDY bits - * set. In this case we need to wait for the BSY bit to clear so - * that we are sure the DMA is finished. 1000 reads was empirically - * determined to be long enough. - */ - while (timeout++ < 1000) { - if (!(spi_tegra_readl(tspi, SLINK_STATUS) & SLINK_BSY)) - break; - } - - spin_lock_irqsave(&tspi->lock, flags); - - val = spi_tegra_readl(tspi, SLINK_STATUS); - val |= SLINK_RDY; - spi_tegra_writel(tspi, val, SLINK_STATUS); - - m = list_first_entry(&tspi->queue, struct spi_message, queue); - - if (timeout >= 1000) - m->status = -EIO; - - spi = m->state; - - tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur); - m->actual_length += tspi->cur_pos; - - if (tspi->cur_pos < tspi->cur->len) { - tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, tspi->cur); - spi_tegra_go(tspi); - } else if (!list_is_last(&tspi->cur->transfer_list, - &m->transfers)) { - tspi->cur = list_first_entry(&tspi->cur->transfer_list, - struct spi_transfer, - transfer_list); - spi_tegra_start_transfer(spi, tspi->cur); - } else { - list_del(&m->queue); - - m->complete(m->context); - - if (!list_empty(&tspi->queue)) { - m = list_first_entry(&tspi->queue, struct spi_message, - queue); - spi = m->state; - spi_tegra_start_message(spi, m); - } else { - clk_disable_unprepare(tspi->clk); - tspi->cur_speed = 0; - } - } - - spin_unlock_irqrestore(&tspi->lock, flags); -} -#if defined(CONFIG_TEGRA_SYSTEM_DMA) -static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req) -{ - struct spi_tegra_data *tspi = req->dev; - handle_spi_rx_dma_complete(tspi); -} -#else -static void tegra_spi_rx_dma_complete(void *args) -{ - struct spi_tegra_data *tspi = args; - handle_spi_rx_dma_complete(tspi); -} -#endif - -static int spi_tegra_setup(struct spi_device *spi) -{ - struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); - unsigned long cs_bit; - unsigned long val; - unsigned long flags; - - dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", - spi->bits_per_word, - spi->mode & SPI_CPOL ? "" : "~", - spi->mode & SPI_CPHA ? "" : "~", - spi->max_speed_hz); - - - switch (spi->chip_select) { - case 0: - cs_bit = SLINK_CS_POLARITY; - break; - - case 1: - cs_bit = SLINK_CS_POLARITY1; - break; - - case 2: - cs_bit = SLINK_CS_POLARITY2; - break; - - case 4: - cs_bit = SLINK_CS_POLARITY3; - break; - - default: - return -EINVAL; - } - - spin_lock_irqsave(&tspi->lock, flags); - - val = spi_tegra_readl(tspi, SLINK_COMMAND); - if (spi->mode & SPI_CS_HIGH) - val |= cs_bit; - else - val &= ~cs_bit; - spi_tegra_writel(tspi, val, SLINK_COMMAND); - - spin_unlock_irqrestore(&tspi->lock, flags); - - return 0; -} - -static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m) -{ - struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); - struct spi_transfer *t; - unsigned long flags; - int was_empty; - - if (list_empty(&m->transfers) || !m->complete) - return -EINVAL; - - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->bits_per_word < 0 || t->bits_per_word > 32) - return -EINVAL; - - if (t->len == 0) - return -EINVAL; - - if (!t->rx_buf && !t->tx_buf) - return -EINVAL; - } - - m->state = spi; - - spin_lock_irqsave(&tspi->lock, flags); - was_empty = list_empty(&tspi->queue); - list_add_tail(&m->queue, &tspi->queue); - - if (was_empty) - spi_tegra_start_message(spi, m); - - spin_unlock_irqrestore(&tspi->lock, flags); - - return 0; -} - -static int __devinit spi_tegra_probe(struct platform_device *pdev) -{ - struct spi_master *master; - struct spi_tegra_data *tspi; - struct resource *r; - int ret; -#if !defined(CONFIG_TEGRA_SYSTEM_DMA) - dma_cap_mask_t mask; -#endif - - master = spi_alloc_master(&pdev->dev, sizeof *tspi); - if (master == NULL) { - dev_err(&pdev->dev, "master allocation failed\n"); - return -ENOMEM; - } - - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - - master->bus_num = pdev->id; - - master->setup = spi_tegra_setup; - master->transfer = spi_tegra_transfer; - master->num_chipselect = 4; - - dev_set_drvdata(&pdev->dev, master); - tspi = spi_master_get_devdata(master); - tspi->master = master; - tspi->pdev = pdev; - spin_lock_init(&tspi->lock); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - ret = -ENODEV; - goto err0; - } - - if (!request_mem_region(r->start, resource_size(r), - dev_name(&pdev->dev))) { - ret = -EBUSY; - goto err0; - } - - tspi->phys = r->start; - tspi->base = ioremap(r->start, resource_size(r)); - if (!tspi->base) { - dev_err(&pdev->dev, "can't ioremap iomem\n"); - ret = -ENOMEM; - goto err1; - } - - tspi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(tspi->clk)) { - dev_err(&pdev->dev, "can not get clock\n"); - ret = PTR_ERR(tspi->clk); - goto err2; - } - - INIT_LIST_HEAD(&tspi->queue); - -#if defined(CONFIG_TEGRA_SYSTEM_DMA) - tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); - if (!tspi->rx_dma) { - dev_err(&pdev->dev, "can not allocate rx dma channel\n"); - ret = -ENODEV; - goto err3; - } -#else - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - tspi->rx_dma = dma_request_channel(mask, NULL, NULL); - if (!tspi->rx_dma) { - dev_err(&pdev->dev, "can not allocate rx dma channel\n"); - ret = -ENODEV; - goto err3; - } - -#endif - - tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN, - &tspi->rx_bb_phys, GFP_KERNEL); - if (!tspi->rx_bb) { - dev_err(&pdev->dev, "can not allocate rx bounce buffer\n"); - ret = -ENOMEM; - goto err4; - } - -#if defined(CONFIG_TEGRA_SYSTEM_DMA) - tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete; - tspi->rx_dma_req.to_memory = 1; - tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys; - tspi->rx_dma_req.dest_bus_width = 32; - tspi->rx_dma_req.source_addr = tspi->phys + SLINK_RX_FIFO; - tspi->rx_dma_req.source_bus_width = 32; - tspi->rx_dma_req.source_wrap = 4; - tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; - tspi->rx_dma_req.dev = tspi; -#else - /* Dmaengine Dma slave config */ - tspi->sconfig.src_addr = tspi->phys + SLINK_RX_FIFO; - tspi->sconfig.dst_addr = tspi->phys + SLINK_RX_FIFO; - tspi->sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - tspi->sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - tspi->sconfig.slave_id = spi_tegra_req_sels[pdev->id]; - tspi->sconfig.src_maxburst = 1; - tspi->sconfig.dst_maxburst = 1; - ret = dmaengine_device_control(tspi->rx_dma, - DMA_SLAVE_CONFIG, (unsigned long) &tspi->sconfig); - if (ret < 0) { - dev_err(&pdev->dev, "can not do slave configure for dma %d\n", - ret); - goto err4; - } -#endif - - master->dev.of_node = pdev->dev.of_node; - ret = spi_register_master(master); - - if (ret < 0) - goto err5; - - return ret; - -err5: - dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, - tspi->rx_bb, tspi->rx_bb_phys); -err4: -#if defined(CONFIG_TEGRA_SYSTEM_DMA) - tegra_dma_free_channel(tspi->rx_dma); -#else - dma_release_channel(tspi->rx_dma); -#endif -err3: - clk_put(tspi->clk); -err2: - iounmap(tspi->base); -err1: - release_mem_region(r->start, resource_size(r)); -err0: - spi_master_put(master); - return ret; -} - -static int __devexit spi_tegra_remove(struct platform_device *pdev) -{ - struct spi_master *master; - struct spi_tegra_data *tspi; - struct resource *r; - - master = spi_master_get(dev_get_drvdata(&pdev->dev)); - tspi = spi_master_get_devdata(master); - - spi_unregister_master(master); -#if defined(CONFIG_TEGRA_SYSTEM_DMA) - tegra_dma_free_channel(tspi->rx_dma); -#else - dma_release_channel(tspi->rx_dma); -#endif - - dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, - tspi->rx_bb, tspi->rx_bb_phys); - - clk_put(tspi->clk); - iounmap(tspi->base); - - spi_master_put(master); - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - - return 0; -} - -MODULE_ALIAS("platform:spi_tegra"); - -#ifdef CONFIG_OF -static struct of_device_id spi_tegra_of_match_table[] __devinitdata = { - { .compatible = "nvidia,tegra20-spi", }, - {} -}; -MODULE_DEVICE_TABLE(of, spi_tegra_of_match_table); -#else /* CONFIG_OF */ -#define spi_tegra_of_match_table NULL -#endif /* CONFIG_OF */ - -static struct platform_driver spi_tegra_driver = { - .driver = { - .name = "spi_tegra", - .owner = THIS_MODULE, - .of_match_table = spi_tegra_of_match_table, - }, - .probe = spi_tegra_probe, - .remove = __devexit_p(spi_tegra_remove), -}; -module_platform_driver(spi_tegra_driver); - -MODULE_LICENSE("GPL"); -- cgit v1.2.3