|
@@ -1,7 +1,8 @@
|
|
|
/*
|
|
|
* SH RSPI driver
|
|
|
*
|
|
|
- * Copyright (C) 2012 Renesas Solutions Corp.
|
|
|
+ * Copyright (C) 2012, 2013 Renesas Solutions Corp.
|
|
|
+ * Copyright (C) 2014 Glider bvba
|
|
|
*
|
|
|
* Based on spi-sh.c:
|
|
|
* Copyright (C) 2011 Renesas Solutions Corp.
|
|
@@ -25,14 +26,14 @@
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/sched.h>
|
|
|
#include <linux/errno.h>
|
|
|
-#include <linux/list.h>
|
|
|
-#include <linux/workqueue.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/platform_device.h>
|
|
|
#include <linux/io.h>
|
|
|
#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>
|
|
@@ -49,7 +50,7 @@
|
|
|
#define RSPI_SPCKD 0x0c /* Clock Delay Register */
|
|
|
#define RSPI_SSLND 0x0d /* Slave Select Negation Delay Register */
|
|
|
#define RSPI_SPND 0x0e /* Next-Access Delay Register */
|
|
|
-#define RSPI_SPCR2 0x0f /* Control Register 2 */
|
|
|
+#define RSPI_SPCR2 0x0f /* Control Register 2 (SH only) */
|
|
|
#define RSPI_SPCMD0 0x10 /* Command Register 0 */
|
|
|
#define RSPI_SPCMD1 0x12 /* Command Register 1 */
|
|
|
#define RSPI_SPCMD2 0x14 /* Command Register 2 */
|
|
@@ -58,16 +59,23 @@
|
|
|
#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 RSPI_SPBFDR 0x22 /* Buffer Data Count Setting Register */
|
|
|
|
|
|
-/*qspi only */
|
|
|
+/* QSPI only */
|
|
|
#define QSPI_SPBFCR 0x18 /* Buffer Control Register */
|
|
|
#define QSPI_SPBDCR 0x1a /* Buffer Data Count Register */
|
|
|
#define QSPI_SPBMUL0 0x1c /* Transfer Data Length Multiplier Setting Register 0 */
|
|
|
#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 */
|
|
@@ -104,7 +112,7 @@
|
|
|
#define SPSR_PERF 0x08 /* Parity Error Flag */
|
|
|
#define SPSR_MODF 0x04 /* Mode Fault Error Flag */
|
|
|
#define SPSR_IDLNF 0x02 /* RSPI Idle Flag */
|
|
|
-#define SPSR_OVRF 0x01 /* Overrun Error Flag */
|
|
|
+#define SPSR_OVRF 0x01 /* Overrun Error Flag (RSPI only) */
|
|
|
|
|
|
/* SPSCR - Sequence Control Register */
|
|
|
#define SPSCR_SPSLN_MASK 0x07 /* Sequence Length Specification */
|
|
@@ -121,13 +129,13 @@
|
|
|
#define SPDCR_SPLWORD SPDCR_SPLW1
|
|
|
#define SPDCR_SPLBYTE SPDCR_SPLW0
|
|
|
#define SPDCR_SPLW 0x20 /* Access Width Specification (SH) */
|
|
|
-#define SPDCR_SPRDTD 0x10 /* Receive Transmit Data Select */
|
|
|
+#define SPDCR_SPRDTD 0x10 /* Receive Transmit Data Select (SH) */
|
|
|
#define SPDCR_SLSEL1 0x08
|
|
|
#define SPDCR_SLSEL0 0x04
|
|
|
-#define SPDCR_SLSEL_MASK 0x0c /* SSL1 Output Select */
|
|
|
+#define SPDCR_SLSEL_MASK 0x0c /* SSL1 Output Select (SH) */
|
|
|
#define SPDCR_SPFC1 0x02
|
|
|
#define SPDCR_SPFC0 0x01
|
|
|
-#define SPDCR_SPFC_MASK 0x03 /* Frame Count Setting (1-4) */
|
|
|
+#define SPDCR_SPFC_MASK 0x03 /* Frame Count Setting (1-4) (SH) */
|
|
|
|
|
|
/* SPCKD - Clock Delay Register */
|
|
|
#define SPCKD_SCKDL_MASK 0x07 /* Clock Delay Setting (1-8) */
|
|
@@ -151,7 +159,7 @@
|
|
|
#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
|
|
@@ -170,8 +178,8 @@
|
|
|
#define SPCMD_CPHA 0x0001 /* Clock Phase Setting */
|
|
|
|
|
|
/* SPBFCR - Buffer Control Register */
|
|
|
-#define SPBFCR_TXRST 0x80 /* Transmit Buffer Data Reset (qspi only) */
|
|
|
-#define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset (qspi only) */
|
|
|
+#define SPBFCR_TXRST 0x80 /* Transmit Buffer Data Reset */
|
|
|
+#define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset */
|
|
|
#define SPBFCR_TXTRG_MASK 0x30 /* Transmit Buffer Data Triggering Number */
|
|
|
#define SPBFCR_RXTRG_MASK 0x07 /* Receive Buffer Data Triggering Number */
|
|
|
|
|
@@ -181,22 +189,21 @@ struct rspi_data {
|
|
|
void __iomem *addr;
|
|
|
u32 max_speed_hz;
|
|
|
struct spi_master *master;
|
|
|
- struct list_head queue;
|
|
|
- struct work_struct ws;
|
|
|
wait_queue_head_t wait;
|
|
|
- spinlock_t lock;
|
|
|
struct clk *clk;
|
|
|
- u8 spsr;
|
|
|
u16 spcmd;
|
|
|
+ u8 spsr;
|
|
|
+ u8 sppcr;
|
|
|
+ int rx_irq, tx_irq;
|
|
|
const struct spi_ops *ops;
|
|
|
|
|
|
/* for dmaengine */
|
|
|
struct dma_chan *chan_tx;
|
|
|
struct dma_chan *chan_rx;
|
|
|
- int irq;
|
|
|
|
|
|
unsigned dma_width_16bit:1;
|
|
|
unsigned dma_callbacked:1;
|
|
|
+ unsigned byte_access:1;
|
|
|
};
|
|
|
|
|
|
static void rspi_write8(const struct rspi_data *rspi, u8 data, u16 offset)
|
|
@@ -224,34 +231,47 @@ static u16 rspi_read16(const struct rspi_data *rspi, u16 offset)
|
|
|
return ioread16(rspi->addr + offset);
|
|
|
}
|
|
|
|
|
|
+static void rspi_write_data(const struct rspi_data *rspi, u16 data)
|
|
|
+{
|
|
|
+ if (rspi->byte_access)
|
|
|
+ rspi_write8(rspi, data, RSPI_SPDR);
|
|
|
+ else /* 16 bit */
|
|
|
+ rspi_write16(rspi, data, RSPI_SPDR);
|
|
|
+}
|
|
|
+
|
|
|
+static u16 rspi_read_data(const struct rspi_data *rspi)
|
|
|
+{
|
|
|
+ if (rspi->byte_access)
|
|
|
+ return rspi_read8(rspi, RSPI_SPDR);
|
|
|
+ else /* 16 bit */
|
|
|
+ return rspi_read16(rspi, RSPI_SPDR);
|
|
|
+}
|
|
|
+
|
|
|
/* optional functions */
|
|
|
struct spi_ops {
|
|
|
- int (*set_config_register)(const struct rspi_data *rspi,
|
|
|
- int access_size);
|
|
|
- int (*send_pio)(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
- struct spi_transfer *t);
|
|
|
- int (*receive_pio)(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
- struct spi_transfer *t);
|
|
|
-
|
|
|
+ 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;
|
|
|
};
|
|
|
|
|
|
/*
|
|
|
- * functions for RSPI
|
|
|
+ * functions for RSPI on legacy SH
|
|
|
*/
|
|
|
-static int rspi_set_config_register(const struct rspi_data *rspi,
|
|
|
- int access_size)
|
|
|
+static int rspi_set_config_register(struct rspi_data *rspi, int access_size)
|
|
|
{
|
|
|
int spbr;
|
|
|
|
|
|
- /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */
|
|
|
- rspi_write8(rspi, 0x00, RSPI_SPPCR);
|
|
|
+ /* 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) - 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);
|
|
|
+ /* Disable dummy transmission, set 16-bit word access, 1 frame */
|
|
|
+ rspi_write8(rspi, 0, RSPI_SPDCR);
|
|
|
+ rspi->byte_access = 0;
|
|
|
|
|
|
/* Sets RSPCK, SSL, next-access delay value */
|
|
|
rspi_write8(rspi, 0x00, RSPI_SPCKD);
|
|
@@ -262,8 +282,41 @@ static int rspi_set_config_register(const struct rspi_data *rspi,
|
|
|
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);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * functions for RSPI on RZ
|
|
|
+ */
|
|
|
+static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size)
|
|
|
+{
|
|
|
+ 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) - 1;
|
|
|
+ rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR);
|
|
|
+
|
|
|
+ /* Disable dummy transmission, set byte access */
|
|
|
+ rspi_write8(rspi, SPDCR_SPLBYTE, RSPI_SPDCR);
|
|
|
+ rspi->byte_access = 1;
|
|
|
+
|
|
|
+ /* 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 SPCMD */
|
|
|
+ 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);
|
|
@@ -274,21 +327,20 @@ static int rspi_set_config_register(const struct rspi_data *rspi,
|
|
|
/*
|
|
|
* functions for QSPI
|
|
|
*/
|
|
|
-static int qspi_set_config_register(const struct rspi_data *rspi,
|
|
|
- int access_size)
|
|
|
+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 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);
|
|
|
rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR);
|
|
|
|
|
|
- /* Sets number of frames to be used: 1 frame */
|
|
|
- rspi_write8(rspi, 0x00, RSPI_SPDCR);
|
|
|
+ /* Disable dummy transmission, set byte access */
|
|
|
+ rspi_write8(rspi, 0, RSPI_SPDCR);
|
|
|
+ rspi->byte_access = 1;
|
|
|
|
|
|
/* Sets RSPCK, SSL, next-access delay value */
|
|
|
rspi_write8(rspi, 0x00, RSPI_SPCKD);
|
|
@@ -297,13 +349,13 @@ static int qspi_set_config_register(const struct rspi_data *rspi,
|
|
|
|
|
|
/* 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);
|
|
@@ -314,9 +366,9 @@ static int qspi_set_config_register(const struct rspi_data *rspi,
|
|
|
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;
|
|
@@ -340,6 +392,9 @@ static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask,
|
|
|
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))
|
|
@@ -348,78 +403,39 @@ static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void rspi_assert_ssl(const struct rspi_data *rspi)
|
|
|
-{
|
|
|
- rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR);
|
|
|
-}
|
|
|
-
|
|
|
-static void rspi_negate_ssl(const struct rspi_data *rspi)
|
|
|
+static int rspi_data_out(struct rspi_data *rspi, u8 data)
|
|
|
{
|
|
|
- rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR);
|
|
|
+ if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
|
|
|
+ dev_err(&rspi->master->dev, "transmit timeout\n");
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+ rspi_write_data(rspi, data);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
- struct spi_transfer *t)
|
|
|
+static int rspi_data_in(struct rspi_data *rspi)
|
|
|
{
|
|
|
- int remain = t->len;
|
|
|
- const u8 *data = t->tx_buf;
|
|
|
- while (remain > 0) {
|
|
|
- rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD,
|
|
|
- RSPI_SPCR);
|
|
|
-
|
|
|
- if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
|
|
|
- dev_err(&rspi->master->dev,
|
|
|
- "%s: tx empty timeout\n", __func__);
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
+ u8 data;
|
|
|
|
|
|
- rspi_write16(rspi, *data, RSPI_SPDR);
|
|
|
- data++;
|
|
|
- remain--;
|
|
|
+ if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) {
|
|
|
+ dev_err(&rspi->master->dev, "receive timeout\n");
|
|
|
+ return -ETIMEDOUT;
|
|
|
}
|
|
|
-
|
|
|
- /* Waiting for the last transmission */
|
|
|
- rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
|
|
|
-
|
|
|
- return 0;
|
|
|
+ data = rspi_read_data(rspi);
|
|
|
+ return data;
|
|
|
}
|
|
|
|
|
|
-static int qspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
- struct spi_transfer *t)
|
|
|
+static int rspi_data_out_in(struct rspi_data *rspi, u8 data)
|
|
|
{
|
|
|
- int remain = t->len;
|
|
|
- const u8 *data = t->tx_buf;
|
|
|
-
|
|
|
- rspi_write8(rspi, SPBFCR_TXRST, QSPI_SPBFCR);
|
|
|
- rspi_write8(rspi, 0x00, QSPI_SPBFCR);
|
|
|
-
|
|
|
- while (remain > 0) {
|
|
|
-
|
|
|
- if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
|
|
|
- dev_err(&rspi->master->dev,
|
|
|
- "%s: tx empty timeout\n", __func__);
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
- rspi_write8(rspi, *data++, RSPI_SPDR);
|
|
|
-
|
|
|
- if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) {
|
|
|
- dev_err(&rspi->master->dev,
|
|
|
- "%s: receive timeout\n", __func__);
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
- rspi_read8(rspi, RSPI_SPDR);
|
|
|
-
|
|
|
- remain--;
|
|
|
- }
|
|
|
+ int ret;
|
|
|
|
|
|
- /* Waiting for the last transmission */
|
|
|
- rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
|
|
|
+ ret = rspi_data_out(rspi, data);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
|
|
|
- return 0;
|
|
|
+ return rspi_data_in(rspi);
|
|
|
}
|
|
|
|
|
|
-#define send_pio(spi, mesg, t) spi->ops->send_pio(spi, mesg, t)
|
|
|
-
|
|
|
static void rspi_dma_complete(void *arg)
|
|
|
{
|
|
|
struct rspi_data *rspi = arg;
|
|
@@ -471,7 +487,7 @@ 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 len;
|
|
|
+ unsigned int len;
|
|
|
int ret = 0;
|
|
|
|
|
|
if (rspi->dma_width_16bit) {
|
|
@@ -509,7 +525,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t)
|
|
|
* 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->irq);
|
|
|
+ disable_irq(rspi->tx_irq);
|
|
|
|
|
|
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, RSPI_SPCR);
|
|
|
rspi_enable_irq(rspi, SPCR_SPTIE);
|
|
@@ -528,7 +544,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t)
|
|
|
ret = -ETIMEDOUT;
|
|
|
rspi_disable_irq(rspi, SPCR_SPTIE);
|
|
|
|
|
|
- enable_irq(rspi->irq);
|
|
|
+ enable_irq(rspi->tx_irq);
|
|
|
|
|
|
end:
|
|
|
rspi_dma_unmap_sg(&sg, rspi->chan_tx, DMA_TO_DEVICE);
|
|
@@ -545,46 +561,17 @@ static void rspi_receive_init(const struct rspi_data *rspi)
|
|
|
|
|
|
spsr = rspi_read8(rspi, RSPI_SPSR);
|
|
|
if (spsr & SPSR_SPRF)
|
|
|
- rspi_read16(rspi, RSPI_SPDR); /* dummy read */
|
|
|
+ rspi_read_data(rspi); /* dummy read */
|
|
|
if (spsr & SPSR_OVRF)
|
|
|
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPSR) & ~SPSR_OVRF,
|
|
|
RSPI_SPSR);
|
|
|
}
|
|
|
|
|
|
-static int rspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
- struct spi_transfer *t)
|
|
|
+static void rspi_rz_receive_init(const struct rspi_data *rspi)
|
|
|
{
|
|
|
- int remain = t->len;
|
|
|
- u8 *data;
|
|
|
-
|
|
|
rspi_receive_init(rspi);
|
|
|
-
|
|
|
- data = t->rx_buf;
|
|
|
- while (remain > 0) {
|
|
|
- rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD,
|
|
|
- RSPI_SPCR);
|
|
|
-
|
|
|
- if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
|
|
|
- dev_err(&rspi->master->dev,
|
|
|
- "%s: tx empty timeout\n", __func__);
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
- /* dummy write for generate clock */
|
|
|
- rspi_write16(rspi, DUMMY_DATA, RSPI_SPDR);
|
|
|
-
|
|
|
- if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) {
|
|
|
- dev_err(&rspi->master->dev,
|
|
|
- "%s: receive timeout\n", __func__);
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
- /* SPDR allows 16 or 32-bit access only */
|
|
|
- *data = (u8)rspi_read16(rspi, RSPI_SPDR);
|
|
|
-
|
|
|
- data++;
|
|
|
- remain--;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
+ rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, RSPI_SPBFCR);
|
|
|
+ rspi_write8(rspi, 0, RSPI_SPBFCR);
|
|
|
}
|
|
|
|
|
|
static void qspi_receive_init(const struct rspi_data *rspi)
|
|
@@ -593,51 +580,17 @@ static void qspi_receive_init(const struct rspi_data *rspi)
|
|
|
|
|
|
spsr = rspi_read8(rspi, RSPI_SPSR);
|
|
|
if (spsr & SPSR_SPRF)
|
|
|
- rspi_read8(rspi, RSPI_SPDR); /* dummy read */
|
|
|
+ rspi_read_data(rspi); /* dummy read */
|
|
|
rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR);
|
|
|
- rspi_write8(rspi, 0x00, QSPI_SPBFCR);
|
|
|
+ rspi_write8(rspi, 0, QSPI_SPBFCR);
|
|
|
}
|
|
|
|
|
|
-static int qspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
- struct spi_transfer *t)
|
|
|
-{
|
|
|
- int remain = t->len;
|
|
|
- u8 *data;
|
|
|
-
|
|
|
- qspi_receive_init(rspi);
|
|
|
-
|
|
|
- data = t->rx_buf;
|
|
|
- while (remain > 0) {
|
|
|
-
|
|
|
- if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
|
|
|
- dev_err(&rspi->master->dev,
|
|
|
- "%s: tx empty timeout\n", __func__);
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
- /* dummy write for generate clock */
|
|
|
- rspi_write8(rspi, DUMMY_DATA, RSPI_SPDR);
|
|
|
-
|
|
|
- if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) {
|
|
|
- dev_err(&rspi->master->dev,
|
|
|
- "%s: receive timeout\n", __func__);
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
- /* SPDR allows 8, 16 or 32-bit access */
|
|
|
- *data++ = rspi_read8(rspi, RSPI_SPDR);
|
|
|
- remain--;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-#define receive_pio(spi, mesg, t) spi->ops->receive_pio(spi, mesg, t)
|
|
|
-
|
|
|
static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t)
|
|
|
{
|
|
|
struct scatterlist sg, sg_dummy;
|
|
|
void *dummy = NULL, *rx_buf = NULL;
|
|
|
struct dma_async_tx_descriptor *desc, *desc_dummy;
|
|
|
- unsigned len;
|
|
|
+ unsigned int len;
|
|
|
int ret = 0;
|
|
|
|
|
|
if (rspi->dma_width_16bit) {
|
|
@@ -695,7 +648,9 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t)
|
|
|
* 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->irq);
|
|
|
+ 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);
|
|
@@ -718,7 +673,9 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t)
|
|
|
ret = -ETIMEDOUT;
|
|
|
rspi_disable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE);
|
|
|
|
|
|
- enable_irq(rspi->irq);
|
|
|
+ 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);
|
|
@@ -746,56 +703,175 @@ static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void rspi_work(struct work_struct *work)
|
|
|
+static int rspi_transfer_out_in(struct rspi_data *rspi,
|
|
|
+ struct spi_transfer *xfer)
|
|
|
{
|
|
|
- struct rspi_data *rspi = container_of(work, struct rspi_data, ws);
|
|
|
- struct spi_message *mesg;
|
|
|
- struct spi_transfer *t;
|
|
|
- unsigned long flags;
|
|
|
- int ret;
|
|
|
+ int remain = xfer->len, ret;
|
|
|
+ const u8 *tx_buf = xfer->tx_buf;
|
|
|
+ u8 *rx_buf = xfer->rx_buf;
|
|
|
+ u8 spcr, data;
|
|
|
|
|
|
- while (1) {
|
|
|
- spin_lock_irqsave(&rspi->lock, flags);
|
|
|
- if (list_empty(&rspi->queue)) {
|
|
|
- spin_unlock_irqrestore(&rspi->lock, flags);
|
|
|
- break;
|
|
|
- }
|
|
|
- mesg = list_entry(rspi->queue.next, struct spi_message, queue);
|
|
|
- list_del_init(&mesg->queue);
|
|
|
- spin_unlock_irqrestore(&rspi->lock, flags);
|
|
|
-
|
|
|
- rspi_assert_ssl(rspi);
|
|
|
-
|
|
|
- list_for_each_entry(t, &mesg->transfers, transfer_list) {
|
|
|
- if (t->tx_buf) {
|
|
|
- if (rspi_is_dma(rspi, t))
|
|
|
- ret = rspi_send_dma(rspi, t);
|
|
|
- else
|
|
|
- ret = send_pio(rspi, mesg, t);
|
|
|
- if (ret < 0)
|
|
|
- goto error;
|
|
|
- }
|
|
|
- if (t->rx_buf) {
|
|
|
- if (rspi_is_dma(rspi, t))
|
|
|
- ret = rspi_receive_dma(rspi, t);
|
|
|
- else
|
|
|
- ret = receive_pio(rspi, mesg, t);
|
|
|
- if (ret < 0)
|
|
|
- goto error;
|
|
|
- }
|
|
|
- mesg->actual_length += t->len;
|
|
|
+ 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);
|
|
|
+
|
|
|
+ while (remain > 0) {
|
|
|
+ data = tx_buf ? *tx_buf++ : DUMMY_DATA;
|
|
|
+ ret = rspi_data_out(rspi, data);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ if (rx_buf) {
|
|
|
+ ret = rspi_data_in(rspi);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ *rx_buf++ = ret;
|
|
|
}
|
|
|
- rspi_negate_ssl(rspi);
|
|
|
+ remain--;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Wait for the last transmission */
|
|
|
+ rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi,
|
|
|
+ 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);
|
|
|
+
|
|
|
+ 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--;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Wait for the last transmission */
|
|
|
+ rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int rspi_rz_transfer_one(struct spi_master *master,
|
|
|
+ struct spi_device *spi,
|
|
|
+ struct spi_transfer *xfer)
|
|
|
+{
|
|
|
+ struct rspi_data *rspi = spi_master_get_devdata(master);
|
|
|
+
|
|
|
+ return rspi_rz_transfer_out_in(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;
|
|
|
|
|
|
- mesg->status = 0;
|
|
|
- mesg->complete(mesg->context);
|
|
|
+ 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 ret;
|
|
|
+ if (rx_buf)
|
|
|
+ *rx_buf++ = ret;
|
|
|
+ remain--;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Wait for the last transmission */
|
|
|
+ rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
|
|
|
+
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
- return;
|
|
|
+ /* Wait for the last transmission */
|
|
|
+ rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
|
|
|
|
|
|
-error:
|
|
|
- mesg->status = ret;
|
|
|
- mesg->complete(mesg->context);
|
|
|
+ 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,
|
|
|
+ struct spi_transfer *xfer)
|
|
|
+{
|
|
|
+ struct rspi_data *rspi = spi_master_get_devdata(master);
|
|
|
+
|
|
|
+ if (spi->mode & SPI_LOOP) {
|
|
|
+ return qspi_transfer_out_in(rspi, xfer);
|
|
|
+ } else 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)
|
|
@@ -810,32 +886,115 @@ static int rspi_setup(struct spi_device *spi)
|
|
|
if (spi->mode & SPI_CPHA)
|
|
|
rspi->spcmd |= SPCMD_CPHA;
|
|
|
|
|
|
+ /* CMOS output mode and MOSI signal from previous transfer */
|
|
|
+ rspi->sppcr = 0;
|
|
|
+ if (spi->mode & SPI_LOOP)
|
|
|
+ rspi->sppcr |= SPPCR_SPLP;
|
|
|
+
|
|
|
set_config_register(rspi, 8);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int rspi_transfer(struct spi_device *spi, struct spi_message *mesg)
|
|
|
+static u16 qspi_transfer_mode(const struct spi_transfer *xfer)
|
|
|
{
|
|
|
- struct rspi_data *rspi = spi_master_get_devdata(spi->master);
|
|
|
- unsigned long flags;
|
|
|
+ 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;
|
|
|
+}
|
|
|
|
|
|
- mesg->actual_length = 0;
|
|
|
- mesg->status = -EINPROGRESS;
|
|
|
+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));
|
|
|
+ }
|
|
|
|
|
|
- spin_lock_irqsave(&rspi->lock, flags);
|
|
|
- list_add_tail(&mesg->queue, &rspi->queue);
|
|
|
- schedule_work(&rspi->ws);
|
|
|
- spin_unlock_irqrestore(&rspi->lock, flags);
|
|
|
+ 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 void rspi_cleanup(struct spi_device *spi)
|
|
|
+static int rspi_prepare_message(struct spi_master *master,
|
|
|
+ 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 irqreturn_t rspi_irq(int irq, void *_sr)
|
|
|
+static int rspi_unprepare_message(struct spi_master *master,
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t rspi_irq_mux(int irq, void *_sr)
|
|
|
{
|
|
|
struct rspi_data *rspi = _sr;
|
|
|
u8 spsr;
|
|
@@ -857,6 +1016,36 @@ static irqreturn_t rspi_irq(int irq, void *_sr)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static irqreturn_t rspi_irq_rx(int irq, void *_sr)
|
|
|
+{
|
|
|
+ struct rspi_data *rspi = _sr;
|
|
|
+ u8 spsr;
|
|
|
+
|
|
|
+ rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR);
|
|
|
+ if (spsr & SPSR_SPRF) {
|
|
|
+ rspi_disable_irq(rspi, SPCR_SPRIE);
|
|
|
+ wake_up(&rspi->wait);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t rspi_irq_tx(int irq, void *_sr)
|
|
|
+{
|
|
|
+ struct rspi_data *rspi = _sr;
|
|
|
+ u8 spsr;
|
|
|
+
|
|
|
+ rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR);
|
|
|
+ if (spsr & SPSR_SPTEF) {
|
|
|
+ rspi_disable_irq(rspi, SPCR_SPTIE);
|
|
|
+ wake_up(&rspi->wait);
|
|
|
+ return IRQ_HANDLED;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int rspi_request_dma(struct rspi_data *rspi,
|
|
|
struct platform_device *pdev)
|
|
|
{
|
|
@@ -923,34 +1112,89 @@ static int rspi_remove(struct platform_device *pdev)
|
|
|
struct rspi_data *rspi = platform_get_drvdata(pdev);
|
|
|
|
|
|
rspi_release_dma(rspi);
|
|
|
- clk_disable(rspi->clk);
|
|
|
+ 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,
|
|
|
+};
|
|
|
+
|
|
|
+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,
|
|
|
+};
|
|
|
+
|
|
|
+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,
|
|
|
+};
|
|
|
+
|
|
|
+#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);
|
|
|
+ if (!name)
|
|
|
+ return -ENOMEM;
|
|
|
+ snprintf(name, len, "%s:%s", base, suffix);
|
|
|
+ return devm_request_irq(dev, irq, handler, 0, name, dev_id);
|
|
|
+}
|
|
|
+
|
|
|
static int rspi_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
struct resource *res;
|
|
|
struct spi_master *master;
|
|
|
struct rspi_data *rspi;
|
|
|
- int ret, irq;
|
|
|
- char clk_name[16];
|
|
|
- const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev);
|
|
|
+ int ret;
|
|
|
+ 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;
|
|
|
- }
|
|
|
-
|
|
|
- irq = platform_get_irq(pdev, 0);
|
|
|
- if (irq < 0) {
|
|
|
- dev_err(&pdev->dev, "platform_get_irq error\n");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
|
|
|
master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data));
|
|
|
if (master == NULL) {
|
|
@@ -958,6 +1202,28 @@ static int rspi_probe(struct platform_device *pdev)
|
|
|
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;
|
|
@@ -970,39 +1236,61 @@ static int rspi_probe(struct platform_device *pdev)
|
|
|
goto error1;
|
|
|
}
|
|
|
|
|
|
- snprintf(clk_name, sizeof(clk_name), "%s%d", id_entry->name, pdev->id);
|
|
|
- rspi->clk = devm_clk_get(&pdev->dev, clk_name);
|
|
|
+ rspi->clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
if (IS_ERR(rspi->clk)) {
|
|
|
dev_err(&pdev->dev, "cannot get clock\n");
|
|
|
ret = PTR_ERR(rspi->clk);
|
|
|
goto error1;
|
|
|
}
|
|
|
- clk_enable(rspi->clk);
|
|
|
|
|
|
- INIT_LIST_HEAD(&rspi->queue);
|
|
|
- spin_lock_init(&rspi->lock);
|
|
|
- INIT_WORK(&rspi->ws, rspi_work);
|
|
|
- init_waitqueue_head(&rspi->wait);
|
|
|
+ pm_runtime_enable(&pdev->dev);
|
|
|
|
|
|
- if (rspi_pd && rspi_pd->num_chipselect)
|
|
|
- master->num_chipselect = rspi_pd->num_chipselect;
|
|
|
- else
|
|
|
- master->num_chipselect = 2; /* default */
|
|
|
+ init_waitqueue_head(&rspi->wait);
|
|
|
|
|
|
master->bus_num = pdev->id;
|
|
|
master->setup = rspi_setup;
|
|
|
- master->transfer = rspi_transfer;
|
|
|
- master->cleanup = rspi_cleanup;
|
|
|
- master->mode_bits = SPI_CPHA | SPI_CPOL;
|
|
|
+ master->auto_runtime_pm = true;
|
|
|
+ master->transfer_one = ops->transfer_one;
|
|
|
+ master->prepare_message = rspi_prepare_message;
|
|
|
+ master->unprepare_message = rspi_unprepare_message;
|
|
|
+ master->mode_bits = ops->mode_bits;
|
|
|
+ master->dev.of_node = pdev->dev.of_node;
|
|
|
+
|
|
|
+ ret = platform_get_irq_byname(pdev, "rx");
|
|
|
+ if (ret < 0) {
|
|
|
+ ret = platform_get_irq_byname(pdev, "mux");
|
|
|
+ if (ret < 0)
|
|
|
+ ret = platform_get_irq(pdev, 0);
|
|
|
+ if (ret >= 0)
|
|
|
+ rspi->rx_irq = rspi->tx_irq = ret;
|
|
|
+ } else {
|
|
|
+ rspi->rx_irq = ret;
|
|
|
+ ret = platform_get_irq_byname(pdev, "tx");
|
|
|
+ if (ret >= 0)
|
|
|
+ rspi->tx_irq = ret;
|
|
|
+ }
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(&pdev->dev, "platform_get_irq error\n");
|
|
|
+ goto error2;
|
|
|
+ }
|
|
|
|
|
|
- ret = devm_request_irq(&pdev->dev, irq, rspi_irq, 0,
|
|
|
- dev_name(&pdev->dev), rspi);
|
|
|
+ if (rspi->rx_irq == rspi->tx_irq) {
|
|
|
+ /* Single multiplexed interrupt */
|
|
|
+ ret = rspi_request_irq(&pdev->dev, rspi->rx_irq, rspi_irq_mux,
|
|
|
+ "mux", rspi);
|
|
|
+ } else {
|
|
|
+ /* Multi-interrupt mode, only SPRI and SPTI are used */
|
|
|
+ ret = rspi_request_irq(&pdev->dev, rspi->rx_irq, rspi_irq_rx,
|
|
|
+ "rx", rspi);
|
|
|
+ if (!ret)
|
|
|
+ ret = rspi_request_irq(&pdev->dev, rspi->tx_irq,
|
|
|
+ rspi_irq_tx, "tx", rspi);
|
|
|
+ }
|
|
|
if (ret < 0) {
|
|
|
dev_err(&pdev->dev, "request_irq error\n");
|
|
|
goto error2;
|
|
|
}
|
|
|
|
|
|
- rspi->irq = irq;
|
|
|
ret = rspi_request_dma(rspi, pdev);
|
|
|
if (ret < 0) {
|
|
|
dev_err(&pdev->dev, "rspi_request_dma failed.\n");
|
|
@@ -1022,27 +1310,16 @@ static int rspi_probe(struct platform_device *pdev)
|
|
|
error3:
|
|
|
rspi_release_dma(rspi);
|
|
|
error2:
|
|
|
- clk_disable(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,
|
|
|
- .send_pio = rspi_send_pio,
|
|
|
- .receive_pio = rspi_receive_pio,
|
|
|
-};
|
|
|
-
|
|
|
-static struct spi_ops qspi_ops = {
|
|
|
- .set_config_register = qspi_set_config_register,
|
|
|
- .send_pio = qspi_send_pio,
|
|
|
- .receive_pio = qspi_receive_pio,
|
|
|
-};
|
|
|
-
|
|
|
static struct platform_device_id spi_driver_ids[] = {
|
|
|
{ "rspi", (kernel_ulong_t)&rspi_ops },
|
|
|
+ { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops },
|
|
|
{ "qspi", (kernel_ulong_t)&qspi_ops },
|
|
|
{},
|
|
|
};
|
|
@@ -1056,6 +1333,7 @@ static struct platform_driver rspi_driver = {
|
|
|
.driver = {
|
|
|
.name = "renesas_spi",
|
|
|
.owner = THIS_MODULE,
|
|
|
+ .of_match_table = of_match_ptr(rspi_of_match),
|
|
|
},
|
|
|
};
|
|
|
module_platform_driver(rspi_driver);
|