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 --- include/linux/spi/mxs-spi.h | 109 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 include/linux/spi/mxs-spi.h (limited to 'include/linux') diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h new file mode 100644 index 000000000000..b7ccd577f758 --- /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(-) (limited to 'include/linux') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 26c95dc87cb2..e80c2b6b2d7c 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 b7ccd577f758..c08aef57c7ce 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(+) (limited to 'include/linux') diff --git a/include/linux/spi/mxs-spi.h b/include/linux/spi/mxs-spi.h index c08aef57c7ce..7dfa1d7d1a78 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(-) (limited to 'include/linux') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index e80c2b6b2d7c..7b85e035a4cb 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 7dfa1d7d1a78..475f69fb896f 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 (limited to 'include/linux') diff --git a/drivers/clk/mxs/Makefile b/drivers/clk/mxs/Makefile index 7bedeec08524..a6a22237e860 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 000000000000..af7bdbf9ebd7 --- /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 7b85e035a4cb..0813340fa29c 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 475f69fb896f..d07f8dc7fdd9 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 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(-) (limited to 'include/linux') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 0813340fa29c..4da996654772 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 7bf826f5af59..0f28afb80310 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 d07f8dc7fdd9..61ae1306db23 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 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 (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/spi/spi-sc18is602.txt b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt new file mode 100644 index 000000000000..02f9033270a2 --- /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 000000000000..a45702865a38 --- /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 5f84b5563c2d..920bb4d22d40 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 3920dcf4c740..7559c984db77 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 000000000000..dd9896423f0b --- /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 000000000000..997b06634152 --- /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(-) (limited to 'include/linux') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 47c6753fb31b..cf47802f00c9 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 fe1d7b283cb6..854b7294f6c6 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