Commit 880c6d11 authored by Geert Uytterhoeven's avatar Geert Uytterhoeven Committed by Mark Brown

spi: rspi: Add support for Quad and Dual SPI Transfers on QSPI

Add support for Quad and Dual SPI Transfers on the Renesas Quad Serial
Peripheral Interface, as found in R-Car Gen2 SoCs like R-Car H2 (r8a7790)
and R-Car M2 (r8a7791):
  - Add unidirectional transfer methods for Quad/Dual SPI Transfers.
  - Program the sequencer to handle SPI messages with multiple transfer
    modes when Quad or Dual transfers are enabled for an SPI slave.
    Up to 4 transfer modes per SPI message are supported by the hardware.
  - Advertise the availability of Quad and Dual SPI modes on QSPI.
Signed-off-by: default avatarGeert Uytterhoeven <geert+renesas@linux-m68k.org>
Signed-off-by: default avatarMark Brown <broonie@linaro.org>
parent 426ef76d
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* SH RSPI driver * SH RSPI driver
* *
* Copyright (C) 2012, 2013 Renesas Solutions Corp. * Copyright (C) 2012, 2013 Renesas Solutions Corp.
* Copyright (C) 2014 Glider bvba
* *
* Based on spi-sh.c: * Based on spi-sh.c:
* Copyright (C) 2011 Renesas Solutions Corp. * Copyright (C) 2011 Renesas Solutions Corp.
...@@ -57,6 +58,10 @@ ...@@ -57,6 +58,10 @@
#define RSPI_SPCMD5 0x1a /* Command Register 5 */ #define RSPI_SPCMD5 0x1a /* Command Register 5 */
#define RSPI_SPCMD6 0x1c /* Command Register 6 */ #define RSPI_SPCMD6 0x1c /* Command Register 6 */
#define RSPI_SPCMD7 0x1e /* Command Register 7 */ #define RSPI_SPCMD7 0x1e /* Command Register 7 */
#define RSPI_SPCMD(i) (RSPI_SPCMD0 + (i) * 2)
#define RSPI_NUM_SPCMD 8
#define RSPI_RZ_NUM_SPCMD 4
#define QSPI_NUM_SPCMD 4
/* RSPI on RZ only */ /* RSPI on RZ only */
#define RSPI_SPBFCR 0x20 /* Buffer Control Register */ #define RSPI_SPBFCR 0x20 /* Buffer Control Register */
...@@ -69,6 +74,7 @@ ...@@ -69,6 +74,7 @@
#define QSPI_SPBMUL1 0x20 /* Transfer Data Length Multiplier Setting Register 1 */ #define QSPI_SPBMUL1 0x20 /* Transfer Data Length Multiplier Setting Register 1 */
#define QSPI_SPBMUL2 0x24 /* Transfer Data Length Multiplier Setting Register 2 */ #define QSPI_SPBMUL2 0x24 /* Transfer Data Length Multiplier Setting Register 2 */
#define QSPI_SPBMUL3 0x28 /* Transfer Data Length Multiplier Setting Register 3 */ #define QSPI_SPBMUL3 0x28 /* Transfer Data Length Multiplier Setting Register 3 */
#define QSPI_SPBMUL(i) (QSPI_SPBMUL0 + (i) * 4)
/* SPCR - Control Register */ /* SPCR - Control Register */
#define SPCR_SPRIE 0x80 /* Receive Interrupt Enable */ #define SPCR_SPRIE 0x80 /* Receive Interrupt Enable */
...@@ -152,7 +158,7 @@ ...@@ -152,7 +158,7 @@
#define SPCMD_LSBF 0x1000 /* LSB First */ #define SPCMD_LSBF 0x1000 /* LSB First */
#define SPCMD_SPB_MASK 0x0f00 /* Data Length Setting */ #define SPCMD_SPB_MASK 0x0f00 /* Data Length Setting */
#define SPCMD_SPB_8_TO_16(bit) (((bit - 1) << 8) & SPCMD_SPB_MASK) #define SPCMD_SPB_8_TO_16(bit) (((bit - 1) << 8) & SPCMD_SPB_MASK)
#define SPCMD_SPB_8BIT 0x0000 /* qspi only */ #define SPCMD_SPB_8BIT 0x0000 /* QSPI only */
#define SPCMD_SPB_16BIT 0x0100 #define SPCMD_SPB_16BIT 0x0100
#define SPCMD_SPB_20BIT 0x0000 #define SPCMD_SPB_20BIT 0x0000
#define SPCMD_SPB_24BIT 0x0100 #define SPCMD_SPB_24BIT 0x0100
...@@ -245,6 +251,7 @@ struct spi_ops { ...@@ -245,6 +251,7 @@ struct spi_ops {
int (*set_config_register)(struct rspi_data *rspi, int access_size); int (*set_config_register)(struct rspi_data *rspi, int access_size);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi, int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *xfer); struct spi_transfer *xfer);
u16 mode_bits;
}; };
/* /*
...@@ -274,8 +281,8 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) ...@@ -274,8 +281,8 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size)
rspi_write8(rspi, 0x00, RSPI_SPCR2); rspi_write8(rspi, 0x00, RSPI_SPCR2);
/* Sets SPCMD */ /* Sets SPCMD */
rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | rspi->spcmd, rspi->spcmd |= SPCMD_SPB_8_TO_16(access_size);
RSPI_SPCMD0); rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0);
/* Sets RSPI mode */ /* Sets RSPI mode */
rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR);
...@@ -321,7 +328,6 @@ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) ...@@ -321,7 +328,6 @@ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size)
*/ */
static int qspi_set_config_register(struct rspi_data *rspi, int access_size) static int qspi_set_config_register(struct rspi_data *rspi, int access_size)
{ {
u16 spcmd;
int spbr; int spbr;
/* Sets output mode, MOSI signal, and (optionally) loopback */ /* Sets output mode, MOSI signal, and (optionally) loopback */
...@@ -342,13 +348,13 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) ...@@ -342,13 +348,13 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size)
/* Data Length Setting */ /* Data Length Setting */
if (access_size == 8) if (access_size == 8)
spcmd = SPCMD_SPB_8BIT; rspi->spcmd |= SPCMD_SPB_8BIT;
else if (access_size == 16) else if (access_size == 16)
spcmd = SPCMD_SPB_16BIT; rspi->spcmd |= SPCMD_SPB_16BIT;
else else
spcmd = SPCMD_SPB_32BIT; rspi->spcmd |= SPCMD_SPB_32BIT;
spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | rspi->spcmd | SPCMD_SPNDEN; rspi->spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | SPCMD_SPNDEN;
/* Resets transfer data length */ /* Resets transfer data length */
rspi_write32(rspi, 0, QSPI_SPBMUL0); rspi_write32(rspi, 0, QSPI_SPBMUL0);
...@@ -359,9 +365,9 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) ...@@ -359,9 +365,9 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size)
rspi_write8(rspi, 0x00, QSPI_SPBFCR); rspi_write8(rspi, 0x00, QSPI_SPBFCR);
/* Sets SPCMD */ /* Sets SPCMD */
rspi_write16(rspi, spcmd, RSPI_SPCMD0); rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0);
/* Enables SPI function in a master mode */ /* Enables SPI function in master mode */
rspi_write8(rspi, SPCR_SPE | SPCR_MSTR, RSPI_SPCR); rspi_write8(rspi, SPCR_SPE | SPCR_MSTR, RSPI_SPCR);
return 0; return 0;
...@@ -811,12 +817,55 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, ...@@ -811,12 +817,55 @@ static int qspi_transfer_out_in(struct rspi_data *rspi,
return 0; return 0;
} }
static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
{
const u8 *buf = xfer->tx_buf;
unsigned int i;
int ret;
for (i = 0; i < xfer->len; i++) {
ret = rspi_data_out(rspi, *buf++);
if (ret < 0)
return ret;
}
/* Wait for the last transmission */
rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
return 0;
}
static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer)
{
u8 *buf = xfer->rx_buf;
unsigned int i;
int ret;
for (i = 0; i < xfer->len; i++) {
ret = rspi_data_in(rspi);
if (ret < 0)
return ret;
*buf++ = ret;
}
return 0;
}
static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *xfer) struct spi_transfer *xfer)
{ {
struct rspi_data *rspi = spi_master_get_devdata(master); struct rspi_data *rspi = spi_master_get_devdata(master);
return qspi_transfer_out_in(rspi, xfer); if (xfer->tx_buf && xfer->tx_nbits > SPI_NBITS_SINGLE) {
/* Quad or Dual SPI Write */
return qspi_transfer_out(rspi, xfer);
} else if (xfer->rx_buf && xfer->rx_nbits > SPI_NBITS_SINGLE) {
/* Quad or Dual SPI Read */
return qspi_transfer_in(rspi, xfer);
} else {
/* Single SPI Transfer */
return qspi_transfer_out_in(rspi, xfer);
}
} }
static int rspi_setup(struct spi_device *spi) static int rspi_setup(struct spi_device *spi)
...@@ -845,21 +894,101 @@ static void rspi_cleanup(struct spi_device *spi) ...@@ -845,21 +894,101 @@ static void rspi_cleanup(struct spi_device *spi)
{ {
} }
static u16 qspi_transfer_mode(const struct spi_transfer *xfer)
{
if (xfer->tx_buf)
switch (xfer->tx_nbits) {
case SPI_NBITS_QUAD:
return SPCMD_SPIMOD_QUAD;
case SPI_NBITS_DUAL:
return SPCMD_SPIMOD_DUAL;
default:
return 0;
}
if (xfer->rx_buf)
switch (xfer->rx_nbits) {
case SPI_NBITS_QUAD:
return SPCMD_SPIMOD_QUAD | SPCMD_SPRW;
case SPI_NBITS_DUAL:
return SPCMD_SPIMOD_DUAL | SPCMD_SPRW;
default:
return 0;
}
return 0;
}
static int qspi_setup_sequencer(struct rspi_data *rspi,
const struct spi_message *msg)
{
const struct spi_transfer *xfer;
unsigned int i = 0, len = 0;
u16 current_mode = 0xffff, mode;
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
mode = qspi_transfer_mode(xfer);
if (mode == current_mode) {
len += xfer->len;
continue;
}
/* Transfer mode change */
if (i) {
/* Set transfer data length of previous transfer */
rspi_write32(rspi, len, QSPI_SPBMUL(i - 1));
}
if (i >= QSPI_NUM_SPCMD) {
dev_err(&msg->spi->dev,
"Too many different transfer modes");
return -EINVAL;
}
/* Program transfer mode for this transfer */
rspi_write16(rspi, rspi->spcmd | mode, RSPI_SPCMD(i));
current_mode = mode;
len = xfer->len;
i++;
}
if (i) {
/* Set final transfer data length and sequence length */
rspi_write32(rspi, len, QSPI_SPBMUL(i - 1));
rspi_write8(rspi, i - 1, RSPI_SPSCR);
}
return 0;
}
static int rspi_prepare_message(struct spi_master *master, static int rspi_prepare_message(struct spi_master *master,
struct spi_message *message) struct spi_message *msg)
{ {
struct rspi_data *rspi = spi_master_get_devdata(master); struct rspi_data *rspi = spi_master_get_devdata(master);
int ret;
if (msg->spi->mode &
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)) {
/* Setup sequencer for messages with multiple transfer modes */
ret = qspi_setup_sequencer(rspi, msg);
if (ret < 0)
return ret;
}
/* Enable SPI function in master mode */
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR); rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR);
return 0; return 0;
} }
static int rspi_unprepare_message(struct spi_master *master, static int rspi_unprepare_message(struct spi_master *master,
struct spi_message *message) struct spi_message *msg)
{ {
struct rspi_data *rspi = spi_master_get_devdata(master); struct rspi_data *rspi = spi_master_get_devdata(master);
/* Disable SPI function */
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR);
/* Reset sequencer for Single SPI Transfers */
rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0);
rspi_write8(rspi, 0, RSPI_SPSCR);
return 0; return 0;
} }
...@@ -989,16 +1118,21 @@ static int rspi_remove(struct platform_device *pdev) ...@@ -989,16 +1118,21 @@ static int rspi_remove(struct platform_device *pdev)
static const struct spi_ops rspi_ops = { static const struct spi_ops rspi_ops = {
.set_config_register = rspi_set_config_register, .set_config_register = rspi_set_config_register,
.transfer_one = rspi_transfer_one, .transfer_one = rspi_transfer_one,
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP,
}; };
static const struct spi_ops rspi_rz_ops = { static const struct spi_ops rspi_rz_ops = {
.set_config_register = rspi_rz_set_config_register, .set_config_register = rspi_rz_set_config_register,
.transfer_one = rspi_rz_transfer_one, .transfer_one = rspi_rz_transfer_one,
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP,
}; };
static const struct spi_ops qspi_ops = { static const struct spi_ops qspi_ops = {
.set_config_register = qspi_set_config_register, .set_config_register = qspi_set_config_register,
.transfer_one = qspi_transfer_one, .transfer_one = qspi_transfer_one,
.mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP |
SPI_TX_DUAL | SPI_TX_QUAD |
SPI_RX_DUAL | SPI_RX_QUAD,
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
...@@ -1120,7 +1254,7 @@ static int rspi_probe(struct platform_device *pdev) ...@@ -1120,7 +1254,7 @@ static int rspi_probe(struct platform_device *pdev)
master->cleanup = rspi_cleanup; master->cleanup = rspi_cleanup;
master->prepare_message = rspi_prepare_message; master->prepare_message = rspi_prepare_message;
master->unprepare_message = rspi_unprepare_message; master->unprepare_message = rspi_unprepare_message;
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP; master->mode_bits = ops->mode_bits;
master->dev.of_node = pdev->dev.of_node; master->dev.of_node = pdev->dev.of_node;
ret = platform_get_irq_byname(pdev, "rx"); ret = platform_get_irq_byname(pdev, "rx");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment