| From 2dff0ab0360e79e4594fa6b940004d9943bc109b Mon Sep 17 00:00:00 2001 |
| From: Hiep Cao Minh <cm-hiep@jinso.co.jp> |
| Date: Tue, 3 Sep 2013 13:10:26 +0900 |
| Subject: spi: rcar: add Renesas QSPI support on RSPI |
| |
| The R8A7790 has QSPI module which is very similar to RSPI. |
| This patch adds into RSPI module together to supports QSPI module. |
| |
| Signed-off-by: Hiep Cao Minh <cm-hiep@jinso.co.jp> |
| Signed-off-by: Mark Brown <broonie@linaro.org> |
| (cherry picked from commit 5ce0ba88650f2606244a761d92e2b725f4ab3583) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/spi/Kconfig | 2 +- |
| drivers/spi/spi-rspi.c | 181 ++++++++++++++++++++++++++++++++++++----------- |
| include/linux/spi/rspi.h | 2 + |
| 3 files changed, 144 insertions(+), 41 deletions(-) |
| |
| diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig |
| index 0c19ed6a62f4..f9f44767c542 100644 |
| --- a/drivers/spi/Kconfig |
| +++ b/drivers/spi/Kconfig |
| @@ -341,7 +341,7 @@ config SPI_PXA2XX_PCI |
| |
| config SPI_RSPI |
| tristate "Renesas RSPI controller" |
| - depends on SUPERH && SH_DMAE_BASE |
| + depends on (SUPERH || ARCH_SHMOBILE) && SH_DMAE_BASE |
| help |
| SPI driver for Renesas RSPI blocks. |
| |
| diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c |
| index 49ae72a93087..7675a6b25653 100644 |
| --- a/drivers/spi/spi-rspi.c |
| +++ b/drivers/spi/spi-rspi.c |
| @@ -59,6 +59,14 @@ |
| #define RSPI_SPCMD6 0x1c |
| #define RSPI_SPCMD7 0x1e |
| |
| +/*qspi only */ |
| +#define QSPI_SPBFCR 0x18 |
| +#define QSPI_SPBDCR 0x1a |
| +#define QSPI_SPBMUL0 0x1c |
| +#define QSPI_SPBMUL1 0x20 |
| +#define QSPI_SPBMUL2 0x24 |
| +#define QSPI_SPBMUL3 0x28 |
| + |
| /* SPCR */ |
| #define SPCR_SPRIE 0x80 |
| #define SPCR_SPE 0x40 |
| @@ -126,6 +134,8 @@ |
| #define SPCMD_LSBF 0x1000 |
| #define SPCMD_SPB_MASK 0x0f00 |
| #define SPCMD_SPB_8_TO_16(bit) (((bit - 1) << 8) & SPCMD_SPB_MASK) |
| +#define SPCMD_SPB_8BIT 0x0000 /* qspi only */ |
| +#define SPCMD_SPB_16BIT 0x0100 |
| #define SPCMD_SPB_20BIT 0x0000 |
| #define SPCMD_SPB_24BIT 0x0100 |
| #define SPCMD_SPB_32BIT 0x0200 |
| @@ -135,6 +145,10 @@ |
| #define SPCMD_CPOL 0x0002 |
| #define SPCMD_CPHA 0x0001 |
| |
| +/* SPBFCR */ |
| +#define SPBFCR_TXRST 0x80 /* qspi only */ |
| +#define SPBFCR_RXRST 0x40 /* qspi only */ |
| + |
| struct rspi_data { |
| void __iomem *addr; |
| u32 max_speed_hz; |
| @@ -145,6 +159,7 @@ struct rspi_data { |
| spinlock_t lock; |
| struct clk *clk; |
| unsigned char spsr; |
| + const struct spi_ops *ops; |
| |
| /* for dmaengine */ |
| struct dma_chan *chan_tx; |
| @@ -165,6 +180,11 @@ static void rspi_write16(struct rspi_data *rspi, u16 data, u16 offset) |
| iowrite16(data, rspi->addr + offset); |
| } |
| |
| +static void rspi_write32(struct rspi_data *rspi, u32 data, u16 offset) |
| +{ |
| + iowrite32(data, rspi->addr + offset); |
| +} |
| + |
| static u8 rspi_read8(struct rspi_data *rspi, u16 offset) |
| { |
| return ioread8(rspi->addr + offset); |
| @@ -175,17 +195,98 @@ static u16 rspi_read16(struct rspi_data *rspi, u16 offset) |
| return ioread16(rspi->addr + offset); |
| } |
| |
| -static unsigned char rspi_calc_spbr(struct rspi_data *rspi) |
| +/* optional functions */ |
| +struct spi_ops { |
| + int (*set_config_register)(struct rspi_data *rspi, int access_size); |
| +}; |
| + |
| +/* |
| + * functions for RSPI |
| + */ |
| +static int rspi_set_config_register(struct rspi_data *rspi, int access_size) |
| { |
| - int tmp; |
| - unsigned char spbr; |
| + int spbr; |
| + |
| + /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ |
| + rspi_write8(rspi, 0x00, RSPI_SPPCR); |
| |
| - tmp = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; |
| - spbr = clamp(tmp, 0, 255); |
| + /* Sets transfer bit rate */ |
| + spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; |
| + rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); |
| + |
| + /* Sets number of frames to be used: 1 frame */ |
| + rspi_write8(rspi, 0x00, RSPI_SPDCR); |
| |
| - return spbr; |
| + /* Sets RSPCK, SSL, next-access delay value */ |
| + rspi_write8(rspi, 0x00, RSPI_SPCKD); |
| + rspi_write8(rspi, 0x00, RSPI_SSLND); |
| + rspi_write8(rspi, 0x00, RSPI_SPND); |
| + |
| + /* Sets parity, interrupt mask */ |
| + rspi_write8(rspi, 0x00, RSPI_SPCR2); |
| + |
| + /* Sets SPCMD */ |
| + rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | SPCMD_SSLKP, |
| + RSPI_SPCMD0); |
| + |
| + /* Sets RSPI mode */ |
| + rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); |
| + |
| + return 0; |
| } |
| |
| +/* |
| + * functions for QSPI |
| + */ |
| +static int qspi_set_config_register(struct rspi_data *rspi, int access_size) |
| +{ |
| + u16 spcmd; |
| + int spbr; |
| + |
| + /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ |
| + rspi_write8(rspi, 0x00, RSPI_SPPCR); |
| + |
| + /* Sets transfer bit rate */ |
| + spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz); |
| + rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); |
| + |
| + /* Sets number of frames to be used: 1 frame */ |
| + rspi_write8(rspi, 0x00, RSPI_SPDCR); |
| + |
| + /* Sets RSPCK, SSL, next-access delay value */ |
| + rspi_write8(rspi, 0x00, RSPI_SPCKD); |
| + rspi_write8(rspi, 0x00, RSPI_SSLND); |
| + rspi_write8(rspi, 0x00, RSPI_SPND); |
| + |
| + /* Data Length Setting */ |
| + if (access_size == 8) |
| + spcmd = SPCMD_SPB_8BIT; |
| + else if (access_size == 16) |
| + spcmd = SPCMD_SPB_16BIT; |
| + else if (access_size == 32) |
| + spcmd = SPCMD_SPB_32BIT; |
| + |
| + spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | SPCMD_SSLKP | SPCMD_SPNDEN; |
| + |
| + /* Resets transfer data length */ |
| + rspi_write32(rspi, 0, QSPI_SPBMUL0); |
| + |
| + /* Resets transmit and receive buffer */ |
| + rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR); |
| + /* Sets buffer to allow normal operation */ |
| + rspi_write8(rspi, 0x00, QSPI_SPBFCR); |
| + |
| + /* Sets SPCMD */ |
| + rspi_write16(rspi, spcmd, RSPI_SPCMD0); |
| + |
| + /* Enables SPI function in a master mode */ |
| + rspi_write8(rspi, SPCR_SPE | SPCR_MSTR, RSPI_SPCR); |
| + |
| + return 0; |
| +} |
| + |
| +#define set_config_register(spi, n) spi->ops->set_config_register(spi, n) |
| + |
| static void rspi_enable_irq(struct rspi_data *rspi, u8 enable) |
| { |
| rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | enable, RSPI_SPCR); |
| @@ -220,35 +321,6 @@ static void rspi_negate_ssl(struct rspi_data *rspi) |
| rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); |
| } |
| |
| -static int rspi_set_config_register(struct rspi_data *rspi, int access_size) |
| -{ |
| - /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ |
| - rspi_write8(rspi, 0x00, RSPI_SPPCR); |
| - |
| - /* Sets transfer bit rate */ |
| - rspi_write8(rspi, rspi_calc_spbr(rspi), RSPI_SPBR); |
| - |
| - /* Sets number of frames to be used: 1 frame */ |
| - rspi_write8(rspi, 0x00, RSPI_SPDCR); |
| - |
| - /* Sets RSPCK, SSL, next-access delay value */ |
| - rspi_write8(rspi, 0x00, RSPI_SPCKD); |
| - rspi_write8(rspi, 0x00, RSPI_SSLND); |
| - rspi_write8(rspi, 0x00, RSPI_SPND); |
| - |
| - /* Sets parity, interrupt mask */ |
| - rspi_write8(rspi, 0x00, RSPI_SPCR2); |
| - |
| - /* Sets SPCMD */ |
| - rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | SPCMD_SSLKP, |
| - RSPI_SPCMD0); |
| - |
| - /* Sets RSPI mode */ |
| - rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); |
| - |
| - return 0; |
| -} |
| - |
| static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, |
| struct spi_transfer *t) |
| { |
| @@ -616,7 +688,7 @@ static int rspi_setup(struct spi_device *spi) |
| spi->bits_per_word = 8; |
| rspi->max_speed_hz = spi->max_speed_hz; |
| |
| - rspi_set_config_register(rspi, 8); |
| + set_config_register(rspi, 8); |
| |
| return 0; |
| } |
| @@ -745,7 +817,16 @@ static int rspi_probe(struct platform_device *pdev) |
| struct rspi_data *rspi; |
| int ret, irq; |
| char clk_name[16]; |
| + struct rspi_plat_data *rspi_pd = pdev->dev.platform_data; |
| + const struct spi_ops *ops; |
| + const struct platform_device_id *id_entry = pdev->id_entry; |
| |
| + ops = (struct spi_ops *)id_entry->driver_data; |
| + /* ops parameter check */ |
| + if (!ops->set_config_register) { |
| + dev_err(&pdev->dev, "there is no set_config_register\n"); |
| + return -ENODEV; |
| + } |
| /* get base addr */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (unlikely(res == NULL)) { |
| @@ -767,7 +848,7 @@ static int rspi_probe(struct platform_device *pdev) |
| |
| rspi = spi_master_get_devdata(master); |
| platform_set_drvdata(pdev, rspi); |
| - |
| + rspi->ops = ops; |
| rspi->master = master; |
| rspi->addr = ioremap(res->start, resource_size(res)); |
| if (rspi->addr == NULL) { |
| @@ -776,7 +857,7 @@ static int rspi_probe(struct platform_device *pdev) |
| goto error1; |
| } |
| |
| - snprintf(clk_name, sizeof(clk_name), "rspi%d", pdev->id); |
| + snprintf(clk_name, sizeof(clk_name), "%s%d", id_entry->name, pdev->id); |
| rspi->clk = clk_get(&pdev->dev, clk_name); |
| if (IS_ERR(rspi->clk)) { |
| dev_err(&pdev->dev, "cannot get clock\n"); |
| @@ -790,7 +871,10 @@ static int rspi_probe(struct platform_device *pdev) |
| INIT_WORK(&rspi->ws, rspi_work); |
| init_waitqueue_head(&rspi->wait); |
| |
| - master->num_chipselect = 2; |
| + master->num_chipselect = rspi_pd->num_chipselect; |
| + if (!master->num_chipselect) |
| + master->num_chipselect = 2; /* default */ |
| + |
| master->bus_num = pdev->id; |
| master->setup = rspi_setup; |
| master->transfer = rspi_transfer; |
| @@ -832,11 +916,28 @@ error1: |
| return ret; |
| } |
| |
| +static struct spi_ops rspi_ops = { |
| + .set_config_register = rspi_set_config_register, |
| +}; |
| + |
| +static struct spi_ops qspi_ops = { |
| + .set_config_register = qspi_set_config_register, |
| +}; |
| + |
| +static struct platform_device_id spi_driver_ids[] = { |
| + { "rspi", (kernel_ulong_t)&rspi_ops }, |
| + { "qspi", (kernel_ulong_t)&qspi_ops }, |
| + {}, |
| +}; |
| + |
| +MODULE_DEVICE_TABLE(platform, spi_driver_ids); |
| + |
| static struct platform_driver rspi_driver = { |
| .probe = rspi_probe, |
| .remove = rspi_remove, |
| + .id_table = spi_driver_ids, |
| .driver = { |
| - .name = "rspi", |
| + .name = "renesas_spi", |
| .owner = THIS_MODULE, |
| }, |
| }; |
| diff --git a/include/linux/spi/rspi.h b/include/linux/spi/rspi.h |
| index 900f0e328235..a25bd6f65e7f 100644 |
| --- a/include/linux/spi/rspi.h |
| +++ b/include/linux/spi/rspi.h |
| @@ -26,6 +26,8 @@ struct rspi_plat_data { |
| unsigned int dma_rx_id; |
| |
| unsigned dma_width_16bit:1; /* DMAC read/write width = 16-bit */ |
| + |
| + u16 num_chipselect; |
| }; |
| |
| #endif |
| -- |
| 1.8.5.rc3 |
| |