|
@@ -59,6 +59,14 @@
|
|
|
#define RSPI_SPCMD6 0x1c
|
|
|
#define RSPI_SPCMD7 0x1e
|
|
|
|
|
|
+/*qspi only */
|
|
|
+#define QSPI_SPBFCR 0x18
|
|
|
+#define QSPI_SPBDCR 0x1a
|
|
|
+#define QSPI_SPBMUL0 0x1c
|
|
|
+#define QSPI_SPBMUL1 0x20
|
|
|
+#define QSPI_SPBMUL2 0x24
|
|
|
+#define QSPI_SPBMUL3 0x28
|
|
|
+
|
|
|
/* SPCR */
|
|
|
#define SPCR_SPRIE 0x80
|
|
|
#define SPCR_SPE 0x40
|
|
@@ -126,6 +134,8 @@
|
|
|
#define SPCMD_LSBF 0x1000
|
|
|
#define SPCMD_SPB_MASK 0x0f00
|
|
|
#define SPCMD_SPB_8_TO_16(bit) (((bit - 1) << 8) & SPCMD_SPB_MASK)
|
|
|
+#define SPCMD_SPB_8BIT 0x0000 /* qspi only */
|
|
|
+#define SPCMD_SPB_16BIT 0x0100
|
|
|
#define SPCMD_SPB_20BIT 0x0000
|
|
|
#define SPCMD_SPB_24BIT 0x0100
|
|
|
#define SPCMD_SPB_32BIT 0x0200
|
|
@@ -135,6 +145,10 @@
|
|
|
#define SPCMD_CPOL 0x0002
|
|
|
#define SPCMD_CPHA 0x0001
|
|
|
|
|
|
+/* SPBFCR */
|
|
|
+#define SPBFCR_TXRST 0x80 /* qspi only */
|
|
|
+#define SPBFCR_RXRST 0x40 /* qspi only */
|
|
|
+
|
|
|
struct rspi_data {
|
|
|
void __iomem *addr;
|
|
|
u32 max_speed_hz;
|
|
@@ -145,6 +159,7 @@ struct rspi_data {
|
|
|
spinlock_t lock;
|
|
|
struct clk *clk;
|
|
|
unsigned char spsr;
|
|
|
+ const struct spi_ops *ops;
|
|
|
|
|
|
/* for dmaengine */
|
|
|
struct dma_chan *chan_tx;
|
|
@@ -165,6 +180,11 @@ static void rspi_write16(struct rspi_data *rspi, u16 data, u16 offset)
|
|
|
iowrite16(data, rspi->addr + offset);
|
|
|
}
|
|
|
|
|
|
+static void rspi_write32(struct rspi_data *rspi, u32 data, u16 offset)
|
|
|
+{
|
|
|
+ iowrite32(data, rspi->addr + offset);
|
|
|
+}
|
|
|
+
|
|
|
static u8 rspi_read8(struct rspi_data *rspi, u16 offset)
|
|
|
{
|
|
|
return ioread8(rspi->addr + offset);
|
|
@@ -175,17 +195,103 @@ static u16 rspi_read16(struct rspi_data *rspi, u16 offset)
|
|
|
return ioread16(rspi->addr + offset);
|
|
|
}
|
|
|
|
|
|
-static unsigned char rspi_calc_spbr(struct rspi_data *rspi)
|
|
|
+/* optional functions */
|
|
|
+struct spi_ops {
|
|
|
+ int (*set_config_register)(struct rspi_data *rspi, int access_size);
|
|
|
+ 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);
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * functions for RSPI
|
|
|
+ */
|
|
|
+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 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);
|
|
|
+
|
|
|
+ /* Sets RSPCK, SSL, next-access delay value */
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SPCKD);
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SSLND);
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SPND);
|
|
|
+
|
|
|
+ /* Sets parity, interrupt mask */
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SPCR2);
|
|
|
+
|
|
|
+ /* Sets SPCMD */
|
|
|
+ rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | SPCMD_SSLKP,
|
|
|
+ RSPI_SPCMD0);
|
|
|
+
|
|
|
+ /* Sets RSPI mode */
|
|
|
+ rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * functions for QSPI
|
|
|
+ */
|
|
|
+static int qspi_set_config_register(struct rspi_data *rspi, int access_size)
|
|
|
{
|
|
|
- int tmp;
|
|
|
- unsigned char spbr;
|
|
|
+ u16 spcmd;
|
|
|
+ int spbr;
|
|
|
+
|
|
|
+ /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SPPCR);
|
|
|
+
|
|
|
+ /* Sets transfer bit rate */
|
|
|
+ spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz);
|
|
|
+ rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR);
|
|
|
+
|
|
|
+ /* Sets number of frames to be used: 1 frame */
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SPDCR);
|
|
|
+
|
|
|
+ /* Sets RSPCK, SSL, next-access delay value */
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SPCKD);
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SSLND);
|
|
|
+ rspi_write8(rspi, 0x00, RSPI_SPND);
|
|
|
+
|
|
|
+ /* Data Length Setting */
|
|
|
+ if (access_size == 8)
|
|
|
+ spcmd = SPCMD_SPB_8BIT;
|
|
|
+ else if (access_size == 16)
|
|
|
+ spcmd = SPCMD_SPB_16BIT;
|
|
|
+ else if (access_size == 32)
|
|
|
+ spcmd = SPCMD_SPB_32BIT;
|
|
|
+
|
|
|
+ spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | SPCMD_SSLKP | SPCMD_SPNDEN;
|
|
|
+
|
|
|
+ /* Resets transfer data length */
|
|
|
+ rspi_write32(rspi, 0, QSPI_SPBMUL0);
|
|
|
+
|
|
|
+ /* Resets transmit and receive buffer */
|
|
|
+ rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR);
|
|
|
+ /* Sets buffer to allow normal operation */
|
|
|
+ rspi_write8(rspi, 0x00, QSPI_SPBFCR);
|
|
|
+
|
|
|
+ /* Sets SPCMD */
|
|
|
+ rspi_write16(rspi, spcmd, RSPI_SPCMD0);
|
|
|
|
|
|
- tmp = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1;
|
|
|
- spbr = clamp(tmp, 0, 255);
|
|
|
+ /* Enables SPI function in a master mode */
|
|
|
+ rspi_write8(rspi, SPCR_SPE | SPCR_MSTR, RSPI_SPCR);
|
|
|
|
|
|
- return spbr;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
+#define set_config_register(spi, n) spi->ops->set_config_register(spi, n)
|
|
|
+
|
|
|
static void rspi_enable_irq(struct rspi_data *rspi, u8 enable)
|
|
|
{
|
|
|
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | enable, RSPI_SPCR);
|
|
@@ -220,54 +326,60 @@ static void rspi_negate_ssl(struct rspi_data *rspi)
|
|
|
rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR);
|
|
|
}
|
|
|
|
|
|
-static int rspi_set_config_register(struct rspi_data *rspi, int access_size)
|
|
|
+static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
+ struct spi_transfer *t)
|
|
|
{
|
|
|
- /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */
|
|
|
- rspi_write8(rspi, 0x00, RSPI_SPPCR);
|
|
|
-
|
|
|
- /* Sets transfer bit rate */
|
|
|
- rspi_write8(rspi, rspi_calc_spbr(rspi), RSPI_SPBR);
|
|
|
-
|
|
|
- /* Sets number of frames to be used: 1 frame */
|
|
|
- rspi_write8(rspi, 0x00, RSPI_SPDCR);
|
|
|
+ int remain = t->len;
|
|
|
+ u8 *data;
|
|
|
|
|
|
- /* Sets RSPCK, SSL, next-access delay value */
|
|
|
- rspi_write8(rspi, 0x00, RSPI_SPCKD);
|
|
|
- rspi_write8(rspi, 0x00, RSPI_SSLND);
|
|
|
- rspi_write8(rspi, 0x00, RSPI_SPND);
|
|
|
+ data = (u8 *)t->tx_buf;
|
|
|
+ while (remain > 0) {
|
|
|
+ rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD,
|
|
|
+ RSPI_SPCR);
|
|
|
|
|
|
- /* Sets parity, interrupt mask */
|
|
|
- rspi_write8(rspi, 0x00, RSPI_SPCR2);
|
|
|
+ if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) {
|
|
|
+ dev_err(&rspi->master->dev,
|
|
|
+ "%s: tx empty timeout\n", __func__);
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
|
|
|
- /* Sets SPCMD */
|
|
|
- rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | SPCMD_SSLKP,
|
|
|
- RSPI_SPCMD0);
|
|
|
+ rspi_write16(rspi, *data, RSPI_SPDR);
|
|
|
+ data++;
|
|
|
+ remain--;
|
|
|
+ }
|
|
|
|
|
|
- /* Sets RSPI mode */
|
|
|
- rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR);
|
|
|
+ /* Waiting for the last transmition */
|
|
|
+ rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
+static int qspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
struct spi_transfer *t)
|
|
|
{
|
|
|
int remain = t->len;
|
|
|
u8 *data;
|
|
|
|
|
|
+ rspi_write8(rspi, SPBFCR_TXRST, QSPI_SPBFCR);
|
|
|
+ rspi_write8(rspi, 0x00, QSPI_SPBFCR);
|
|
|
+
|
|
|
data = (u8 *)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;
|
|
|
}
|
|
|
+ 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);
|
|
|
|
|
|
- rspi_write16(rspi, *data, RSPI_SPDR);
|
|
|
- data++;
|
|
|
remain--;
|
|
|
}
|
|
|
|
|
@@ -277,6 +389,8 @@ static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#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;
|
|
@@ -442,6 +556,51 @@ static int rspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void qspi_receive_init(struct rspi_data *rspi)
|
|
|
+{
|
|
|
+ unsigned char spsr;
|
|
|
+
|
|
|
+ spsr = rspi_read8(rspi, RSPI_SPSR);
|
|
|
+ if (spsr & SPSR_SPRF)
|
|
|
+ rspi_read8(rspi, RSPI_SPDR); /* dummy read */
|
|
|
+ rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR);
|
|
|
+ rspi_write8(rspi, 0x00, 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 = (u8 *)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, 0x00, 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;
|
|
@@ -581,7 +740,7 @@ static void rspi_work(struct work_struct *work)
|
|
|
if (rspi_is_dma(rspi, t))
|
|
|
ret = rspi_send_dma(rspi, t);
|
|
|
else
|
|
|
- ret = rspi_send_pio(rspi, mesg, t);
|
|
|
+ ret = send_pio(rspi, mesg, t);
|
|
|
if (ret < 0)
|
|
|
goto error;
|
|
|
}
|
|
@@ -589,7 +748,7 @@ static void rspi_work(struct work_struct *work)
|
|
|
if (rspi_is_dma(rspi, t))
|
|
|
ret = rspi_receive_dma(rspi, t);
|
|
|
else
|
|
|
- ret = rspi_receive_pio(rspi, mesg, t);
|
|
|
+ ret = receive_pio(rspi, mesg, t);
|
|
|
if (ret < 0)
|
|
|
goto error;
|
|
|
}
|
|
@@ -616,7 +775,7 @@ static int rspi_setup(struct spi_device *spi)
|
|
|
spi->bits_per_word = 8;
|
|
|
rspi->max_speed_hz = spi->max_speed_hz;
|
|
|
|
|
|
- rspi_set_config_register(rspi, 8);
|
|
|
+ set_config_register(rspi, 8);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -745,7 +904,16 @@ static int rspi_probe(struct platform_device *pdev)
|
|
|
struct rspi_data *rspi;
|
|
|
int ret, irq;
|
|
|
char clk_name[16];
|
|
|
-
|
|
|
+ struct rspi_plat_data *rspi_pd = pdev->dev.platform_data;
|
|
|
+ const struct spi_ops *ops;
|
|
|
+ const struct platform_device_id *id_entry = pdev->id_entry;
|
|
|
+
|
|
|
+ ops = (struct spi_ops *)id_entry->driver_data;
|
|
|
+ /* ops parameter check */
|
|
|
+ if (!ops->set_config_register) {
|
|
|
+ dev_err(&pdev->dev, "there is no set_config_register\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
/* get base addr */
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
if (unlikely(res == NULL)) {
|
|
@@ -767,7 +935,7 @@ static int rspi_probe(struct platform_device *pdev)
|
|
|
|
|
|
rspi = spi_master_get_devdata(master);
|
|
|
platform_set_drvdata(pdev, rspi);
|
|
|
-
|
|
|
+ rspi->ops = ops;
|
|
|
rspi->master = master;
|
|
|
rspi->addr = ioremap(res->start, resource_size(res));
|
|
|
if (rspi->addr == NULL) {
|
|
@@ -776,7 +944,7 @@ static int rspi_probe(struct platform_device *pdev)
|
|
|
goto error1;
|
|
|
}
|
|
|
|
|
|
- snprintf(clk_name, sizeof(clk_name), "rspi%d", pdev->id);
|
|
|
+ snprintf(clk_name, sizeof(clk_name), "%s%d", id_entry->name, pdev->id);
|
|
|
rspi->clk = clk_get(&pdev->dev, clk_name);
|
|
|
if (IS_ERR(rspi->clk)) {
|
|
|
dev_err(&pdev->dev, "cannot get clock\n");
|
|
@@ -790,7 +958,10 @@ static int rspi_probe(struct platform_device *pdev)
|
|
|
INIT_WORK(&rspi->ws, rspi_work);
|
|
|
init_waitqueue_head(&rspi->wait);
|
|
|
|
|
|
- master->num_chipselect = 2;
|
|
|
+ master->num_chipselect = rspi_pd->num_chipselect;
|
|
|
+ if (!master->num_chipselect)
|
|
|
+ master->num_chipselect = 2; /* default */
|
|
|
+
|
|
|
master->bus_num = pdev->id;
|
|
|
master->setup = rspi_setup;
|
|
|
master->transfer = rspi_transfer;
|
|
@@ -832,11 +1003,32 @@ error1:
|
|
|
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 },
|
|
|
+ { "qspi", (kernel_ulong_t)&qspi_ops },
|
|
|
+ {},
|
|
|
+};
|
|
|
+
|
|
|
+MODULE_DEVICE_TABLE(platform, spi_driver_ids);
|
|
|
+
|
|
|
static struct platform_driver rspi_driver = {
|
|
|
.probe = rspi_probe,
|
|
|
.remove = rspi_remove,
|
|
|
+ .id_table = spi_driver_ids,
|
|
|
.driver = {
|
|
|
- .name = "rspi",
|
|
|
+ .name = "renesas_spi",
|
|
|
.owner = THIS_MODULE,
|
|
|
},
|
|
|
};
|