Commit f477b7fb authored by wangyuhang's avatar wangyuhang Committed by Mark Brown

spi: DUAL and QUAD support

fix the previous patch some mistake below:
1. DT in slave node, use "spi-tx-nbits = <1/2/4>" in place of using
   "spi-tx-dual, spi-tx-quad" directly, same to rx. So correct the
   previous way to get the property in @of_register_spi_devices().
2. Change the value of transfer bit macro(SPI_NBITS_SINGLE, SPI_NBITS_DUAL
   SPI_NBITS_QUAD) to 0x01, 0x02 and 0x04 to match the actual wires.
3. Add the following check
   (1)keep the tx_nbits and rx_nbits in spi_transfer is not beyond the
      single, dual and quad.
   (2)keep tx_nbits and rx_nbits are contained by @spi_device->mode
      example: if @spi_device->mode = DUAL, then tx/rx_nbits can not be set
               to QUAD(SPI_NBITS_QUAD)
   (3)if "@spi_device->mode & SPI_3WIRE", then tx/rx_nbits should be in
      single(SPI_NBITS_SINGLE)
Signed-off-by: default avatarwangyuhang <wangyuhang2014@gmail.com>
Signed-off-by: default avatarMark Brown <broonie@linaro.org>
parent 56ede94a
...@@ -869,6 +869,51 @@ static void of_register_spi_devices(struct spi_master *master) ...@@ -869,6 +869,51 @@ static void of_register_spi_devices(struct spi_master *master)
if (of_find_property(nc, "spi-3wire", NULL)) if (of_find_property(nc, "spi-3wire", NULL))
spi->mode |= SPI_3WIRE; spi->mode |= SPI_3WIRE;
/* Device DUAL/QUAD mode */
prop = of_get_property(nc, "spi-tx-nbits", &len);
if (!prop || len < sizeof(*prop)) {
dev_err(&master->dev, "%s has no 'spi-tx-nbits' property\n",
nc->full_name);
spi_dev_put(spi);
continue;
}
switch (be32_to_cpup(prop)) {
case SPI_NBITS_SINGLE:
break;
case SPI_NBITS_DUAL:
spi->mode |= SPI_TX_DUAL;
break;
case SPI_NBITS_QUAD:
spi->mode |= SPI_TX_QUAD;
break;
default:
dev_err(&master->dev, "spi-tx-nbits value is not supported\n");
spi_dev_put(spi);
continue;
}
prop = of_get_property(nc, "spi-rx-nbits", &len);
if (!prop || len < sizeof(*prop)) {
dev_err(&master->dev, "%s has no 'spi-rx-nbits' property\n",
nc->full_name);
spi_dev_put(spi);
continue;
}
switch (be32_to_cpup(prop)) {
case SPI_NBITS_SINGLE:
break;
case SPI_NBITS_DUAL:
spi->mode |= SPI_RX_DUAL;
break;
case SPI_NBITS_QUAD:
spi->mode |= SPI_RX_QUAD;
break;
default:
dev_err(&master->dev, "spi-rx-nbits value is not supported\n");
spi_dev_put(spi);
continue;
}
/* Device speed */ /* Device speed */
prop = of_get_property(nc, "spi-max-frequency", &len); prop = of_get_property(nc, "spi-max-frequency", &len);
if (!prop || len < sizeof(*prop)) { if (!prop || len < sizeof(*prop)) {
...@@ -1316,6 +1361,19 @@ int spi_setup(struct spi_device *spi) ...@@ -1316,6 +1361,19 @@ int spi_setup(struct spi_device *spi)
unsigned bad_bits; unsigned bad_bits;
int status = 0; int status = 0;
/* check mode to prevent that DUAL and QUAD set at the same time
*/
if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) ||
((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) {
dev_err(&spi->dev,
"setup: can not select dual and quad at the same time\n");
return -EINVAL;
}
/* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden
*/
if ((spi->mode & SPI_3WIRE) && (spi->mode &
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)))
return -EINVAL;
/* help drivers fail *cleanly* when they need options /* help drivers fail *cleanly* when they need options
* that aren't supported with their current master * that aren't supported with their current master
*/ */
...@@ -1378,6 +1436,8 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) ...@@ -1378,6 +1436,8 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
/** /**
* Set transfer bits_per_word and max speed as spi device default if * Set transfer bits_per_word and max speed as spi device default if
* it is not set for this transfer. * it is not set for this transfer.
* Set transfer tx_nbits and rx_nbits as single transfer default
* (SPI_NBITS_SINGLE) if it is not set for this transfer.
*/ */
list_for_each_entry(xfer, &message->transfers, transfer_list) { list_for_each_entry(xfer, &message->transfers, transfer_list) {
if (!xfer->bits_per_word) if (!xfer->bits_per_word)
...@@ -1403,6 +1463,42 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) ...@@ -1403,6 +1463,42 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
return -EINVAL; return -EINVAL;
if (xfer->speed_hz && master->max_speed_hz && if (xfer->speed_hz && master->max_speed_hz &&
xfer->speed_hz > master->max_speed_hz) xfer->speed_hz > master->max_speed_hz)
if (xfer->tx_buf && !xfer->tx_nbits)
xfer->tx_nbits = SPI_NBITS_SINGLE;
if (xfer->rx_buf && !xfer->rx_nbits)
xfer->rx_nbits = SPI_NBITS_SINGLE;
/* check transfer tx/rx_nbits:
* 1. keep the value is not out of single, dual and quad
* 2. keep tx/rx_nbits is contained by mode in spi_device
* 3. if SPI_3WIRE, tx/rx_nbits should be in single
*/
if (xfer->tx_nbits != SPI_NBITS_SINGLE &&
xfer->tx_nbits != SPI_NBITS_DUAL &&
xfer->tx_nbits != SPI_NBITS_QUAD)
return -EINVAL;
if ((xfer->tx_nbits == SPI_NBITS_DUAL) &&
!(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
return -EINVAL;
if ((xfer->tx_nbits == SPI_NBITS_QUAD) &&
!(spi->mode & SPI_TX_QUAD))
return -EINVAL;
if ((spi->mode & SPI_3WIRE) &&
(xfer->tx_nbits != SPI_NBITS_SINGLE))
return -EINVAL;
/* check transfer rx_nbits */
if (xfer->rx_nbits != SPI_NBITS_SINGLE &&
xfer->rx_nbits != SPI_NBITS_DUAL &&
xfer->rx_nbits != SPI_NBITS_QUAD)
return -EINVAL;
if ((xfer->rx_nbits == SPI_NBITS_DUAL) &&
!(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
return -EINVAL;
if ((xfer->rx_nbits == SPI_NBITS_QUAD) &&
!(spi->mode & SPI_RX_QUAD))
return -EINVAL;
if ((spi->mode & SPI_3WIRE) &&
(xfer->rx_nbits != SPI_NBITS_SINGLE))
return -EINVAL; return -EINVAL;
} }
......
...@@ -74,7 +74,7 @@ struct spi_device { ...@@ -74,7 +74,7 @@ struct spi_device {
struct spi_master *master; struct spi_master *master;
u32 max_speed_hz; u32 max_speed_hz;
u8 chip_select; u8 chip_select;
u8 mode; u16 mode;
#define SPI_CPHA 0x01 /* clock phase */ #define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */ #define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */ #define SPI_MODE_0 (0|0) /* (original MicroWire) */
...@@ -87,6 +87,10 @@ struct spi_device { ...@@ -87,6 +87,10 @@ struct spi_device {
#define SPI_LOOP 0x20 /* loopback mode */ #define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */ #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */ #define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
u8 bits_per_word; u8 bits_per_word;
int irq; int irq;
void *controller_state; void *controller_state;
...@@ -454,6 +458,10 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); ...@@ -454,6 +458,10 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
* @rx_buf: data to be read (dma-safe memory), or NULL * @rx_buf: data to be read (dma-safe memory), or NULL
* @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped * @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped
* @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped * @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped
* @tx_nbits: number of bits used for writting. If 0 the default
* (SPI_NBITS_SINGLE) is used.
* @rx_nbits: number of bits used for reading. If 0 the default
* (SPI_NBITS_SINGLE) is used.
* @len: size of rx and tx buffers (in bytes) * @len: size of rx and tx buffers (in bytes)
* @speed_hz: Select a speed other than the device default for this * @speed_hz: Select a speed other than the device default for this
* transfer. If 0 the default (from @spi_device) is used. * transfer. If 0 the default (from @spi_device) is used.
...@@ -508,6 +516,11 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); ...@@ -508,6 +516,11 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum);
* by the results of previous messages and where the whole transaction * by the results of previous messages and where the whole transaction
* ends when the chipselect goes intactive. * ends when the chipselect goes intactive.
* *
* When SPI can transfer in 1x,2x or 4x. It can get this tranfer information
* from device through @tx_nbits and @rx_nbits. In Bi-direction, these
* two should both be set. User can set transfer mode with SPI_NBITS_SINGLE(1x)
* SPI_NBITS_DUAL(2x) and SPI_NBITS_QUAD(4x) to support these three transfer.
*
* The code that submits an spi_message (and its spi_transfers) * The code that submits an spi_message (and its spi_transfers)
* to the lower layers is responsible for managing its memory. * to the lower layers is responsible for managing its memory.
* Zero-initialize every field you don't set up explicitly, to * Zero-initialize every field you don't set up explicitly, to
...@@ -528,6 +541,11 @@ struct spi_transfer { ...@@ -528,6 +541,11 @@ struct spi_transfer {
dma_addr_t rx_dma; dma_addr_t rx_dma;
unsigned cs_change:1; unsigned cs_change:1;
u8 tx_nbits;
u8 rx_nbits;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word; u8 bits_per_word;
u16 delay_usecs; u16 delay_usecs;
u32 speed_hz; u32 speed_hz;
...@@ -875,7 +893,7 @@ struct spi_board_info { ...@@ -875,7 +893,7 @@ struct spi_board_info {
/* mode becomes spi_device.mode, and is essential for chips /* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong. * where the default of SPI_CS_HIGH = 0 is wrong.
*/ */
u8 mode; u16 mode;
/* ... may need additional spi_device chip config data here. /* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff * avoid stuff protocol drivers can set; but include stuff
......
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