* SH RSPI driver
*
* Copyright (C) 2012, 2013 Renesas Solutions Corp.
+ * Copyright (C) 2014 Glider bvba
*
* Based on spi-sh.c:
* Copyright (C) 2011 Renesas Solutions Corp.
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/sh_dma.h>
#include <linux/spi/spi.h>
#include <linux/spi/rspi.h>
#define RSPI_SPCMD5 0x1a /* Command Register 5 */
#define RSPI_SPCMD6 0x1c /* Command Register 6 */
#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 */
#define RSPI_SPBFCR 0x20 /* Buffer Control Register */
#define QSPI_SPBMUL1 0x20 /* Transfer Data Length Multiplier Setting Register 1 */
#define QSPI_SPBMUL2 0x24 /* Transfer Data Length Multiplier Setting Register 2 */
#define QSPI_SPBMUL3 0x28 /* Transfer Data Length Multiplier Setting Register 3 */
+#define QSPI_SPBMUL(i) (QSPI_SPBMUL0 + (i) * 4)
/* SPCR - Control Register */
#define SPCR_SPRIE 0x80 /* Receive Interrupt Enable */
/* RSPI on SH only */
#define SPCR_TXMD 0x02 /* TX Only Mode (vs. Full Duplex) */
#define SPCR_SPMS 0x01 /* 3-wire Mode (vs. 4-wire) */
-/* QSPI on R-Car M2 only */
+/* QSPI on R-Car Gen2 only */
#define SPCR_WSWAP 0x02 /* Word Swap of read-data for DMAC */
#define SPCR_BSWAP 0x01 /* Byte Swap of read-data for DMAC */
#define SPCMD_LSBF 0x1000 /* LSB First */
#define SPCMD_SPB_MASK 0x0f00 /* Data Length Setting */
#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_20BIT 0x0000
#define SPCMD_SPB_24BIT 0x0100
#define SPBFCR_TXTRG_MASK 0x30 /* Transmit Buffer Data Triggering Number */
#define SPBFCR_RXTRG_MASK 0x07 /* Receive Buffer Data Triggering Number */
-#define DUMMY_DATA 0x00
-
struct rspi_data {
void __iomem *addr;
u32 max_speed_hz;
int rx_irq, tx_irq;
const struct spi_ops *ops;
- /* for dmaengine */
- struct dma_chan *chan_tx;
- struct dma_chan *chan_rx;
-
- unsigned dma_width_16bit:1;
unsigned dma_callbacked:1;
unsigned byte_access:1;
};
int (*set_config_register)(struct rspi_data *rspi, int access_size);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *xfer);
+ u16 mode_bits;
+ u16 flags;
+ u16 fifo_size;
};
/*
rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR);
/* Sets transfer bit rate */
- spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1;
+ spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk),
+ 2 * rspi->max_speed_hz) - 1;
rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR);
/* Disable dummy transmission, set 16-bit word access, 1 frame */
rspi_write8(rspi, 0x00, RSPI_SPCR2);
/* Sets SPCMD */
- rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | rspi->spcmd,
- RSPI_SPCMD0);
+ rspi->spcmd |= SPCMD_SPB_8_TO_16(access_size);
+ rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0);
/* Sets RSPI mode */
rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR);
rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR);
/* Sets transfer bit rate */
- spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1;
+ spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk),
+ 2 * rspi->max_speed_hz) - 1;
rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR);
/* Disable dummy transmission, set byte access */
*/
static int qspi_set_config_register(struct rspi_data *rspi, int access_size)
{
- u16 spcmd;
int spbr;
/* Sets output mode, MOSI signal, and (optionally) loopback */
rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR);
/* Sets transfer bit rate */
- spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz);
+ spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk), 2 * rspi->max_speed_hz);
rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR);
/* Disable dummy transmission, set byte access */
/* Data Length Setting */
if (access_size == 8)
- spcmd = SPCMD_SPB_8BIT;
+ rspi->spcmd |= SPCMD_SPB_8BIT;
else if (access_size == 16)
- spcmd = SPCMD_SPB_16BIT;
+ rspi->spcmd |= SPCMD_SPB_16BIT;
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 */
rspi_write32(rspi, 0, QSPI_SPBMUL0);
rspi_write8(rspi, 0x00, QSPI_SPBFCR);
/* 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);
return 0;
int ret;
rspi->spsr = rspi_read8(rspi, RSPI_SPSR);
+ if (rspi->spsr & wait_mask)
+ return 0;
+
rspi_enable_irq(rspi, enable_bit);
ret = wait_event_timeout(rspi->wait, rspi->spsr & wait_mask, HZ);
if (ret == 0 && !(rspi->spsr & wait_mask))
return 0;
}
+static inline int rspi_wait_for_tx_empty(struct rspi_data *rspi)
+{
+ return rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
+}
+
+static inline int rspi_wait_for_rx_full(struct rspi_data *rspi)
+{
+ return rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE);
+}
+
static int rspi_data_out(struct rspi_data *rspi, u8 data)
{
- if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
+ int error = rspi_wait_for_tx_empty(rspi);
+ if (error < 0) {
dev_err(&rspi->master->dev, "transmit timeout\n");
- return -ETIMEDOUT;
+ return error;
}
rspi_write_data(rspi, data);
return 0;
static int rspi_data_in(struct rspi_data *rspi)
{
+ int error;
u8 data;
- if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) {
+ error = rspi_wait_for_rx_full(rspi);
+ if (error < 0) {
dev_err(&rspi->master->dev, "receive timeout\n");
- return -ETIMEDOUT;
+ return error;
}
data = rspi_read_data(rspi);
return data;
}
-static int rspi_data_out_in(struct rspi_data *rspi, u8 data)
+static int rspi_pio_transfer(struct rspi_data *rspi, const u8 *tx, u8 *rx,
+ unsigned int n)
{
- int ret;
-
- ret = rspi_data_out(rspi, data);
- if (ret < 0)
- return ret;
+ while (n-- > 0) {
+ if (tx) {
+ int ret = rspi_data_out(rspi, *tx++);
+ if (ret < 0)
+ return ret;
+ }
+ if (rx) {
+ int ret = rspi_data_in(rspi);
+ if (ret < 0)
+ return ret;
+ *rx++ = ret;
+ }
+ }
- return rspi_data_in(rspi);
+ return 0;
}
static void rspi_dma_complete(void *arg)
wake_up_interruptible(&rspi->wait);
}
-static int rspi_dma_map_sg(struct scatterlist *sg, const void *buf,
- unsigned len, struct dma_chan *chan,
- enum dma_transfer_direction dir)
+static int rspi_dma_transfer(struct rspi_data *rspi, struct sg_table *tx,
+ struct sg_table *rx)
{
- sg_init_table(sg, 1);
- sg_set_buf(sg, buf, len);
- sg_dma_len(sg) = len;
- return dma_map_sg(chan->device->dev, sg, 1, dir);
-}
+ struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL;
+ u8 irq_mask = 0;
+ unsigned int other_irq = 0;
+ dma_cookie_t cookie;
+ int ret;
-static void rspi_dma_unmap_sg(struct scatterlist *sg, struct dma_chan *chan,
- enum dma_transfer_direction dir)
-{
- dma_unmap_sg(chan->device->dev, sg, 1, dir);
-}
+ /* First prepare and submit the DMA request(s), as this may fail */
+ if (rx) {
+ desc_rx = dmaengine_prep_slave_sg(rspi->master->dma_rx,
+ rx->sgl, rx->nents, DMA_FROM_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_rx) {
+ ret = -EAGAIN;
+ goto no_dma_rx;
+ }
-static void rspi_memory_to_8bit(void *buf, const void *data, unsigned len)
-{
- u16 *dst = buf;
- const u8 *src = data;
+ desc_rx->callback = rspi_dma_complete;
+ desc_rx->callback_param = rspi;
+ cookie = dmaengine_submit(desc_rx);
+ if (dma_submit_error(cookie)) {
+ ret = cookie;
+ goto no_dma_rx;
+ }
- while (len) {
- *dst++ = (u16)(*src++);
- len--;
+ irq_mask |= SPCR_SPRIE;
}
-}
-static void rspi_memory_from_8bit(void *buf, const void *data, unsigned len)
-{
- u8 *dst = buf;
- const u16 *src = data;
-
- while (len) {
- *dst++ = (u8)*src++;
- len--;
- }
-}
+ if (tx) {
+ desc_tx = dmaengine_prep_slave_sg(rspi->master->dma_tx,
+ tx->sgl, tx->nents, DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc_tx) {
+ ret = -EAGAIN;
+ goto no_dma_tx;
+ }
-static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t)
-{
- struct scatterlist sg;
- const void *buf = NULL;
- struct dma_async_tx_descriptor *desc;
- unsigned int len;
- int ret = 0;
-
- if (rspi->dma_width_16bit) {
- void *tmp;
- /*
- * If DMAC bus width is 16-bit, the driver allocates a dummy
- * buffer. And, the driver converts original data into the
- * DMAC data as the following format:
- * original data: 1st byte, 2nd byte ...
- * DMAC data: 1st byte, dummy, 2nd byte, dummy ...
- */
- len = t->len * 2;
- tmp = kmalloc(len, GFP_KERNEL);
- if (!tmp)
- return -ENOMEM;
- rspi_memory_to_8bit(tmp, t->tx_buf, t->len);
- buf = tmp;
- } else {
- len = t->len;
- buf = t->tx_buf;
- }
+ if (rx) {
+ /* No callback */
+ desc_tx->callback = NULL;
+ } else {
+ desc_tx->callback = rspi_dma_complete;
+ desc_tx->callback_param = rspi;
+ }
+ cookie = dmaengine_submit(desc_tx);
+ if (dma_submit_error(cookie)) {
+ ret = cookie;
+ goto no_dma_tx;
+ }
- if (!rspi_dma_map_sg(&sg, buf, len, rspi->chan_tx, DMA_TO_DEVICE)) {
- ret = -EFAULT;
- goto end_nomap;
- }
- desc = dmaengine_prep_slave_sg(rspi->chan_tx, &sg, 1, DMA_TO_DEVICE,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc) {
- ret = -EIO;
- goto end;
+ irq_mask |= SPCR_SPTIE;
}
/*
- * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be
+ * DMAC needs SPxIE, but if SPxIE is set, the IRQ routine will be
* called. So, this driver disables the IRQ while DMA transfer.
*/
- disable_irq(rspi->tx_irq);
+ if (tx)
+ disable_irq(other_irq = rspi->tx_irq);
+ if (rx && rspi->rx_irq != other_irq)
+ disable_irq(rspi->rx_irq);
- rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, RSPI_SPCR);
- rspi_enable_irq(rspi, SPCR_SPTIE);
+ rspi_enable_irq(rspi, irq_mask);
rspi->dma_callbacked = 0;
- desc->callback = rspi_dma_complete;
- desc->callback_param = rspi;
- dmaengine_submit(desc);
- dma_async_issue_pending(rspi->chan_tx);
+ /* Now start DMA */
+ if (rx)
+ dma_async_issue_pending(rspi->master->dma_rx);
+ if (tx)
+ dma_async_issue_pending(rspi->master->dma_tx);
ret = wait_event_interruptible_timeout(rspi->wait,
rspi->dma_callbacked, HZ);
if (ret > 0 && rspi->dma_callbacked)
ret = 0;
- else if (!ret)
+ else if (!ret) {
+ dev_err(&rspi->master->dev, "DMA timeout\n");
ret = -ETIMEDOUT;
- rspi_disable_irq(rspi, SPCR_SPTIE);
+ if (tx)
+ dmaengine_terminate_all(rspi->master->dma_tx);
+ if (rx)
+ dmaengine_terminate_all(rspi->master->dma_rx);
+ }
- enable_irq(rspi->tx_irq);
+ rspi_disable_irq(rspi, irq_mask);
+
+ if (tx)
+ enable_irq(rspi->tx_irq);
+ if (rx && rspi->rx_irq != other_irq)
+ enable_irq(rspi->rx_irq);
-end:
- rspi_dma_unmap_sg(&sg, rspi->chan_tx, DMA_TO_DEVICE);
-end_nomap:
- if (rspi->dma_width_16bit)
- kfree(buf);
+ return ret;
+no_dma_tx:
+ if (rx)
+ dmaengine_terminate_all(rspi->master->dma_rx);
+no_dma_rx:
+ if (ret == -EAGAIN) {
+ pr_warn_once("%s %s: DMA not available, falling back to PIO\n",
+ dev_driver_string(&rspi->master->dev),
+ dev_name(&rspi->master->dev));
+ }
return ret;
}
rspi_write8(rspi, 0, QSPI_SPBFCR);
}
-static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t)
+static bool __rspi_can_dma(const struct rspi_data *rspi,
+ const struct spi_transfer *xfer)
{
- struct scatterlist sg, sg_dummy;
- void *dummy = NULL, *rx_buf = NULL;
- struct dma_async_tx_descriptor *desc, *desc_dummy;
- unsigned int len;
- int ret = 0;
-
- if (rspi->dma_width_16bit) {
- /*
- * If DMAC bus width is 16-bit, the driver allocates a dummy
- * buffer. And, finally the driver converts the DMAC data into
- * actual data as the following format:
- * DMAC data: 1st byte, dummy, 2nd byte, dummy ...
- * actual data: 1st byte, 2nd byte ...
- */
- len = t->len * 2;
- rx_buf = kmalloc(len, GFP_KERNEL);
- if (!rx_buf)
- return -ENOMEM;
- } else {
- len = t->len;
- rx_buf = t->rx_buf;
- }
-
- /* prepare dummy transfer to generate SPI clocks */
- dummy = kzalloc(len, GFP_KERNEL);
- if (!dummy) {
- ret = -ENOMEM;
- goto end_nomap;
- }
- if (!rspi_dma_map_sg(&sg_dummy, dummy, len, rspi->chan_tx,
- DMA_TO_DEVICE)) {
- ret = -EFAULT;
- goto end_nomap;
- }
- desc_dummy = dmaengine_prep_slave_sg(rspi->chan_tx, &sg_dummy, 1,
- DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc_dummy) {
- ret = -EIO;
- goto end_dummy_mapped;
- }
-
- /* prepare receive transfer */
- if (!rspi_dma_map_sg(&sg, rx_buf, len, rspi->chan_rx,
- DMA_FROM_DEVICE)) {
- ret = -EFAULT;
- goto end_dummy_mapped;
-
- }
- desc = dmaengine_prep_slave_sg(rspi->chan_rx, &sg, 1, DMA_FROM_DEVICE,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc) {
- ret = -EIO;
- goto end;
- }
-
- rspi_receive_init(rspi);
-
- /*
- * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be
- * called. So, this driver disables the IRQ while DMA transfer.
- */
- disable_irq(rspi->tx_irq);
- if (rspi->rx_irq != rspi->tx_irq)
- disable_irq(rspi->rx_irq);
-
- rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD, RSPI_SPCR);
- rspi_enable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE);
- rspi->dma_callbacked = 0;
-
- desc->callback = rspi_dma_complete;
- desc->callback_param = rspi;
- dmaengine_submit(desc);
- dma_async_issue_pending(rspi->chan_rx);
-
- desc_dummy->callback = NULL; /* No callback */
- dmaengine_submit(desc_dummy);
- dma_async_issue_pending(rspi->chan_tx);
-
- ret = wait_event_interruptible_timeout(rspi->wait,
- rspi->dma_callbacked, HZ);
- if (ret > 0 && rspi->dma_callbacked)
- ret = 0;
- else if (!ret)
- ret = -ETIMEDOUT;
- rspi_disable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE);
-
- enable_irq(rspi->tx_irq);
- if (rspi->rx_irq != rspi->tx_irq)
- enable_irq(rspi->rx_irq);
-
-end:
- rspi_dma_unmap_sg(&sg, rspi->chan_rx, DMA_FROM_DEVICE);
-end_dummy_mapped:
- rspi_dma_unmap_sg(&sg_dummy, rspi->chan_tx, DMA_TO_DEVICE);
-end_nomap:
- if (rspi->dma_width_16bit) {
- if (!ret)
- rspi_memory_from_8bit(t->rx_buf, rx_buf, t->len);
- kfree(rx_buf);
- }
- kfree(dummy);
-
- return ret;
+ return xfer->len > rspi->ops->fifo_size;
}
-static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t)
+static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi,
+ struct spi_transfer *xfer)
{
- if (t->tx_buf && rspi->chan_tx)
- return 1;
- /* If the module receives data by DMAC, it also needs TX DMAC */
- if (t->rx_buf && rspi->chan_tx && rspi->chan_rx)
- return 1;
+ struct rspi_data *rspi = spi_master_get_devdata(master);
- return 0;
+ return __rspi_can_dma(rspi, xfer);
}
-static int rspi_transfer_out_in(struct rspi_data *rspi,
+static int rspi_common_transfer(struct rspi_data *rspi,
struct spi_transfer *xfer)
{
- int remain = xfer->len, ret;
- const u8 *tx_buf = xfer->tx_buf;
- u8 *rx_buf = xfer->rx_buf;
- u8 spcr, data;
-
- rspi_receive_init(rspi);
-
- spcr = rspi_read8(rspi, RSPI_SPCR);
- if (rx_buf)
- spcr &= ~SPCR_TXMD;
- else
- spcr |= SPCR_TXMD;
- rspi_write8(rspi, spcr, RSPI_SPCR);
+ int ret;
- while (remain > 0) {
- data = tx_buf ? *tx_buf++ : DUMMY_DATA;
- ret = rspi_data_out(rspi, data);
- if (ret < 0)
+ if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
+ /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
+ ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
+ xfer->rx_buf ? &xfer->rx_sg : NULL);
+ if (ret != -EAGAIN)
return ret;
- if (rx_buf) {
- ret = rspi_data_in(rspi);
- if (ret < 0)
- return ret;
- *rx_buf++ = ret;
- }
- remain--;
}
+ ret = rspi_pio_transfer(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len);
+ if (ret < 0)
+ return ret;
+
/* Wait for the last transmission */
- rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
+ rspi_wait_for_tx_empty(rspi);
return 0;
}
struct spi_transfer *xfer)
{
struct rspi_data *rspi = spi_master_get_devdata(master);
- int ret;
-
- if (!rspi_is_dma(rspi, xfer))
- return rspi_transfer_out_in(rspi, xfer);
-
- if (xfer->tx_buf) {
- ret = rspi_send_dma(rspi, xfer);
- if (ret < 0)
- return ret;
- }
- if (xfer->rx_buf)
- return rspi_receive_dma(rspi, xfer);
+ u8 spcr;
- return 0;
-}
-
-static int rspi_rz_transfer_out_in(struct rspi_data *rspi,
- struct spi_transfer *xfer)
-{
- int remain = xfer->len, ret;
- const u8 *tx_buf = xfer->tx_buf;
- u8 *rx_buf = xfer->rx_buf;
- u8 data;
-
- rspi_rz_receive_init(rspi);
-
- while (remain > 0) {
- data = tx_buf ? *tx_buf++ : DUMMY_DATA;
- ret = rspi_data_out_in(rspi, data);
- if (ret < 0)
- return ret;
- if (rx_buf)
- *rx_buf++ = ret;
- remain--;
+ spcr = rspi_read8(rspi, RSPI_SPCR);
+ if (xfer->rx_buf) {
+ rspi_receive_init(rspi);
+ spcr &= ~SPCR_TXMD;
+ } else {
+ spcr |= SPCR_TXMD;
}
+ rspi_write8(rspi, spcr, RSPI_SPCR);
- /* Wait for the last transmission */
- rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
-
- return 0;
+ return rspi_common_transfer(rspi, xfer);
}
static int rspi_rz_transfer_one(struct spi_master *master,
{
struct rspi_data *rspi = spi_master_get_devdata(master);
- return rspi_rz_transfer_out_in(rspi, xfer);
+ rspi_rz_receive_init(rspi);
+
+ return rspi_common_transfer(rspi, xfer);
}
static int qspi_transfer_out_in(struct rspi_data *rspi,
struct spi_transfer *xfer)
{
- int remain = xfer->len, ret;
- const u8 *tx_buf = xfer->tx_buf;
- u8 *rx_buf = xfer->rx_buf;
- u8 data;
-
qspi_receive_init(rspi);
- while (remain > 0) {
- data = tx_buf ? *tx_buf++ : DUMMY_DATA;
- ret = rspi_data_out_in(rspi, data);
- if (ret < 0)
+ return rspi_common_transfer(rspi, xfer);
+}
+
+static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
+{
+ int ret;
+
+ if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
+ ret = rspi_dma_transfer(rspi, &xfer->tx_sg, NULL);
+ if (ret != -EAGAIN)
return ret;
- if (rx_buf)
- *rx_buf++ = ret;
- remain--;
}
+ ret = rspi_pio_transfer(rspi, xfer->tx_buf, NULL, xfer->len);
+ if (ret < 0)
+ return ret;
+
/* Wait for the last transmission */
- rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
+ rspi_wait_for_tx_empty(rspi);
return 0;
}
+static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer)
+{
+ if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
+ int ret = rspi_dma_transfer(rspi, NULL, &xfer->rx_sg);
+ if (ret != -EAGAIN)
+ return ret;
+ }
+
+ return rspi_pio_transfer(rspi, NULL, xfer->rx_buf, xfer->len);
+}
+
static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *xfer)
{
struct rspi_data *rspi = spi_master_get_devdata(master);
- return qspi_transfer_out_in(rspi, xfer);
+ if (spi->mode & SPI_LOOP) {
+ return qspi_transfer_out_in(rspi, xfer);
+ } else if (xfer->tx_nbits > SPI_NBITS_SINGLE) {
+ /* Quad or Dual SPI Write */
+ return qspi_transfer_out(rspi, xfer);
+ } else if (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)
return 0;
}
-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,
- struct spi_message *message)
+ struct spi_message *msg)
{
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);
return 0;
}
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);
+ /* Disable SPI function */
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;
}
-static int rspi_request_dma(struct rspi_data *rspi,
- struct platform_device *pdev)
+static struct dma_chan *rspi_request_dma_chan(struct device *dev,
+ enum dma_transfer_direction dir,
+ unsigned int id,
+ dma_addr_t port_addr)
{
- const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dma_cap_mask_t mask;
+ struct dma_chan *chan;
struct dma_slave_config cfg;
int ret;
- if (!res || !rspi_pd)
- return 0; /* The driver assumes no error. */
-
- rspi->dma_width_16bit = rspi_pd->dma_width_16bit;
-
- /* If the module receives data by DMAC, it also needs TX DMAC */
- if (rspi_pd->dma_rx_id && rspi_pd->dma_tx_id) {
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- rspi->chan_rx = dma_request_channel(mask, shdma_chan_filter,
- (void *)rspi_pd->dma_rx_id);
- if (rspi->chan_rx) {
- cfg.slave_id = rspi_pd->dma_rx_id;
- cfg.direction = DMA_DEV_TO_MEM;
- cfg.dst_addr = 0;
- cfg.src_addr = res->start + RSPI_SPDR;
- ret = dmaengine_slave_config(rspi->chan_rx, &cfg);
- if (!ret)
- dev_info(&pdev->dev, "Use DMA when rx.\n");
- else
- return ret;
- }
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
+ (void *)(unsigned long)id, dev,
+ dir == DMA_MEM_TO_DEV ? "tx" : "rx");
+ if (!chan) {
+ dev_warn(dev, "dma_request_slave_channel_compat failed\n");
+ return NULL;
}
- if (rspi_pd->dma_tx_id) {
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- rspi->chan_tx = dma_request_channel(mask, shdma_chan_filter,
- (void *)rspi_pd->dma_tx_id);
- if (rspi->chan_tx) {
- cfg.slave_id = rspi_pd->dma_tx_id;
- cfg.direction = DMA_MEM_TO_DEV;
- cfg.dst_addr = res->start + RSPI_SPDR;
- cfg.src_addr = 0;
- ret = dmaengine_slave_config(rspi->chan_tx, &cfg);
- if (!ret)
- dev_info(&pdev->dev, "Use DMA when tx\n");
- else
- return ret;
- }
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.slave_id = id;
+ cfg.direction = dir;
+ if (dir == DMA_MEM_TO_DEV) {
+ cfg.dst_addr = port_addr;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ } else {
+ cfg.src_addr = port_addr;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
}
+ ret = dmaengine_slave_config(chan, &cfg);
+ if (ret) {
+ dev_warn(dev, "dmaengine_slave_config failed %d\n", ret);
+ dma_release_channel(chan);
+ return NULL;
+ }
+
+ return chan;
+}
+
+static int rspi_request_dma(struct device *dev, struct spi_master *master,
+ const struct resource *res)
+{
+ const struct rspi_plat_data *rspi_pd = dev_get_platdata(dev);
+ unsigned int dma_tx_id, dma_rx_id;
+
+ if (dev->of_node) {
+ /* In the OF case we will get the slave IDs from the DT */
+ dma_tx_id = 0;
+ dma_rx_id = 0;
+ } else if (rspi_pd && rspi_pd->dma_tx_id && rspi_pd->dma_rx_id) {
+ dma_tx_id = rspi_pd->dma_tx_id;
+ dma_rx_id = rspi_pd->dma_rx_id;
+ } else {
+ /* The driver assumes no error. */
+ return 0;
+ }
+
+ master->dma_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, dma_tx_id,
+ res->start + RSPI_SPDR);
+ if (!master->dma_tx)
+ return -ENODEV;
+
+ master->dma_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, dma_rx_id,
+ res->start + RSPI_SPDR);
+ if (!master->dma_rx) {
+ dma_release_channel(master->dma_tx);
+ master->dma_tx = NULL;
+ return -ENODEV;
+ }
+
+ master->can_dma = rspi_can_dma;
+ dev_info(dev, "DMA available");
return 0;
}
-static void rspi_release_dma(struct rspi_data *rspi)
+static void rspi_release_dma(struct spi_master *master)
{
- if (rspi->chan_tx)
- dma_release_channel(rspi->chan_tx);
- if (rspi->chan_rx)
- dma_release_channel(rspi->chan_rx);
+ if (master->dma_tx)
+ dma_release_channel(master->dma_tx);
+ if (master->dma_rx)
+ dma_release_channel(master->dma_rx);
}
static int rspi_remove(struct platform_device *pdev)
{
struct rspi_data *rspi = platform_get_drvdata(pdev);
- rspi_release_dma(rspi);
- clk_disable_unprepare(rspi->clk);
+ rspi_release_dma(rspi->master);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct spi_ops rspi_ops = {
+ .set_config_register = rspi_set_config_register,
+ .transfer_one = rspi_transfer_one,
+ .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP,
+ .flags = SPI_MASTER_MUST_TX,
+ .fifo_size = 8,
+};
+
+static const struct spi_ops rspi_rz_ops = {
+ .set_config_register = rspi_rz_set_config_register,
+ .transfer_one = rspi_rz_transfer_one,
+ .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP,
+ .flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX,
+ .fifo_size = 8, /* 8 for TX, 32 for RX */
+};
+
+static const struct spi_ops qspi_ops = {
+ .set_config_register = qspi_set_config_register,
+ .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,
+ .flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX,
+ .fifo_size = 32,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rspi_of_match[] = {
+ /* RSPI on legacy SH */
+ { .compatible = "renesas,rspi", .data = &rspi_ops },
+ /* RSPI on RZ/A1H */
+ { .compatible = "renesas,rspi-rz", .data = &rspi_rz_ops },
+ /* QSPI on R-Car Gen2 */
+ { .compatible = "renesas,qspi", .data = &qspi_ops },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rspi_of_match);
+
+static int rspi_parse_dt(struct device *dev, struct spi_master *master)
+{
+ u32 num_cs;
+ int error;
+
+ /* Parse DT properties */
+ error = of_property_read_u32(dev->of_node, "num-cs", &num_cs);
+ if (error) {
+ dev_err(dev, "of_property_read_u32 num-cs failed %d\n", error);
+ return error;
+ }
+
+ master->num_chipselect = num_cs;
return 0;
}
+#else
+#define rspi_of_match NULL
+static inline int rspi_parse_dt(struct device *dev, struct spi_master *master)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_OF */
static int rspi_request_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, const char *suffix,
void *dev_id)
{
- const char *base = dev_name(dev);
- size_t len = strlen(base) + strlen(suffix) + 2;
- char *name = devm_kzalloc(dev, len, GFP_KERNEL);
+ const char *name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
+ dev_name(dev), suffix);
if (!name)
return -ENOMEM;
- snprintf(name, len, "%s:%s", base, suffix);
+
return devm_request_irq(dev, irq, handler, 0, name, dev_id);
}
struct spi_master *master;
struct rspi_data *rspi;
int ret;
- const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev);
+ const struct of_device_id *of_id;
+ const struct rspi_plat_data *rspi_pd;
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;
- }
master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data));
if (master == NULL) {
return -ENOMEM;
}
+ of_id = of_match_device(rspi_of_match, &pdev->dev);
+ if (of_id) {
+ ops = of_id->data;
+ ret = rspi_parse_dt(&pdev->dev, master);
+ if (ret)
+ goto error1;
+ } else {
+ ops = (struct spi_ops *)pdev->id_entry->driver_data;
+ rspi_pd = dev_get_platdata(&pdev->dev);
+ if (rspi_pd && rspi_pd->num_chipselect)
+ master->num_chipselect = rspi_pd->num_chipselect;
+ else
+ master->num_chipselect = 2; /* default */
+ }
+
+ /* ops parameter check */
+ if (!ops->set_config_register) {
+ dev_err(&pdev->dev, "there is no set_config_register\n");
+ ret = -ENODEV;
+ goto error1;
+ }
+
rspi = spi_master_get_devdata(master);
platform_set_drvdata(pdev, rspi);
rspi->ops = ops;
goto error1;
}
- ret = clk_prepare_enable(rspi->clk);
- if (ret < 0) {
- dev_err(&pdev->dev, "unable to prepare/enable clock\n");
- goto error1;
- }
+ pm_runtime_enable(&pdev->dev);
init_waitqueue_head(&rspi->wait);
- if (rspi_pd && rspi_pd->num_chipselect)
- master->num_chipselect = rspi_pd->num_chipselect;
- else
- master->num_chipselect = 2; /* default */
-
master->bus_num = pdev->id;
master->setup = rspi_setup;
+ master->auto_runtime_pm = true;
master->transfer_one = ops->transfer_one;
- master->cleanup = rspi_cleanup;
master->prepare_message = rspi_prepare_message;
master->unprepare_message = rspi_unprepare_message;
- master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP;
+ master->mode_bits = ops->mode_bits;
+ master->flags = ops->flags;
+ master->dev.of_node = pdev->dev.of_node;
ret = platform_get_irq_byname(pdev, "rx");
if (ret < 0) {
goto error2;
}
- ret = rspi_request_dma(rspi, pdev);
- if (ret < 0) {
- dev_err(&pdev->dev, "rspi_request_dma failed.\n");
- goto error3;
- }
+ ret = rspi_request_dma(&pdev->dev, master, res);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "DMA not available, using PIO\n");
ret = devm_spi_register_master(&pdev->dev, master);
if (ret < 0) {
return 0;
error3:
- rspi_release_dma(rspi);
+ rspi_release_dma(master);
error2:
- clk_disable_unprepare(rspi->clk);
+ pm_runtime_disable(&pdev->dev);
error1:
spi_master_put(master);
return ret;
}
-static struct spi_ops rspi_ops = {
- .set_config_register = rspi_set_config_register,
- .transfer_one = rspi_transfer_one,
-};
-
-static struct spi_ops rspi_rz_ops = {
- .set_config_register = rspi_rz_set_config_register,
- .transfer_one = rspi_rz_transfer_one,
-};
-
-static struct spi_ops qspi_ops = {
- .set_config_register = qspi_set_config_register,
- .transfer_one = qspi_transfer_one,
-};
-
static struct platform_device_id spi_driver_ids[] = {
{ "rspi", (kernel_ulong_t)&rspi_ops },
{ "rspi-rz", (kernel_ulong_t)&rspi_rz_ops },
.driver = {
.name = "renesas_spi",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rspi_of_match),
},
};
module_platform_driver(rspi_driver);