[PATCH 1/3] spi/qspi: Add memory mapped read support.
Sourav Poddar
sourav.poddar at ti.com
Wed Oct 9 08:24:42 PDT 2013
Qspi controller also supports memory mapped read. Patch
adds support for the same.
In memory mapped read, controller need to be switched to a
memory mapped port using a control module register and a qspi
specific register or just a qspi register.
Then the read need to be happened from the memory mapped
address space.
Signed-off-by: Sourav Poddar <sourav.poddar at ti.com>
---
drivers/spi/spi-ti-qspi.c | 140 ++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 125 insertions(+), 15 deletions(-)
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c
index 0b71270..2722840 100644
--- a/drivers/spi/spi-ti-qspi.c
+++ b/drivers/spi/spi-ti-qspi.c
@@ -46,6 +46,8 @@ struct ti_qspi {
struct spi_master *master;
void __iomem *base;
+ void __iomem *ctrl_base;
+ void __iomem *mmap_base;
struct clk *fclk;
struct device *dev;
@@ -54,6 +56,9 @@ struct ti_qspi {
u32 spi_max_frequency;
u32 cmd;
u32 dc;
+
+ bool memory_mapped;
+ bool ctrl_mod;
};
#define QSPI_PID (0x0)
@@ -109,6 +114,23 @@ struct ti_qspi {
#define QSPI_CSPOL(n) (1 << (1 + n * 8))
#define QSPI_CKPOL(n) (1 << (n * 8))
+#define MM_SWITCH 0x01
+#define MEM_CS 0x100
+#define MEM_CS_DIS 0xfffff0ff
+
+#define QSPI_CMD_RD (0x3 << 0)
+#define QSPI_CMD_DUAL_RD (0x3b << 0)
+#define QSPI_CMD_QUAD_RD (0x6b << 0)
+#define QSPI_CMD_READ_FAST (0x0b << 0)
+#define QSPI_SETUP0_A_BYTES (0x3 << 8)
+#define QSPI_SETUP0_NO_BITS (0x0 << 10)
+#define QSPI_SETUP0_8_BITS (0x1 << 10)
+#define QSPI_SETUP0_RD_NORMAL (0x0 << 12)
+#define QSPI_SETUP0_RD_DUAL (0x1 << 12)
+#define QSPI_SETUP0_RD_QUAD (0x3 << 12)
+#define QSPI_CMD_WRITE (0x2 << 16)
+#define QSPI_NUM_DUMMY_BITS (0x0 << 24)
+
#define QSPI_FRAME 4096
#define QSPI_AUTOSUSPEND_TIMEOUT 2000
@@ -125,12 +147,37 @@ static inline void ti_qspi_write(struct ti_qspi *qspi,
writel(val, qspi->base + reg);
}
+void enable_qspi_memory_mapped(struct ti_qspi *qspi)
+{
+ u32 val;
+
+ ti_qspi_write(qspi, MM_SWITCH, QSPI_SPI_SWITCH_REG);
+ if (qspi->ctrl_mod) {
+ val = readl(qspi->ctrl_base);
+ val |= MEM_CS;
+ writel(val, qspi->ctrl_base);
+ }
+}
+
+void disable_qspi_memory_mapped(struct ti_qspi *qspi)
+{
+ u32 val;
+
+ ti_qspi_write(qspi, ~MM_SWITCH, QSPI_SPI_SWITCH_REG);
+ if (qspi->ctrl_mod) {
+ val = readl(qspi->ctrl_base);
+ val |= MEM_CS_DIS;
+ writel(val, qspi->ctrl_base);
+ }
+}
+
static int ti_qspi_setup(struct spi_device *spi)
{
struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg;
int clk_div = 0, ret;
- u32 clk_ctrl_reg, clk_rate, clk_mask;
+ u32 clk_ctrl_reg, clk_rate, clk_mask, memval = 0;
+ qspi->dc = 0;
if (spi->master->busy) {
dev_dbg(qspi->dev, "master busy doing other trasnfers\n");
@@ -178,6 +225,37 @@ static int ti_qspi_setup(struct spi_device *spi)
ti_qspi_write(qspi, clk_mask, QSPI_SPI_CLOCK_CNTRL_REG);
ctx_reg->clkctrl = clk_mask;
+ if (spi->mode & SPI_CPHA)
+ qspi->dc |= QSPI_CKPHA(spi->chip_select);
+ if (spi->mode & SPI_CPOL)
+ qspi->dc |= QSPI_CKPOL(spi->chip_select);
+ if (spi->mode & SPI_CS_HIGH)
+ qspi->dc |= QSPI_CSPOL(spi->chip_select);
+
+ ti_qspi_write(qspi, qspi->dc, QSPI_SPI_DC_REG);
+
+ if (qspi->memory_mapped) {
+ switch (spi->mode) {
+ case SPI_TX_DUAL:
+ memval |= (QSPI_CMD_DUAL_RD | QSPI_SETUP0_A_BYTES |
+ QSPI_SETUP0_8_BITS | QSPI_SETUP0_RD_DUAL |
+ QSPI_CMD_WRITE | QSPI_NUM_DUMMY_BITS);
+ break;
+ case SPI_TX_QUAD:
+ memval |= (QSPI_CMD_QUAD_RD | QSPI_SETUP0_A_BYTES |
+ QSPI_SETUP0_8_BITS | QSPI_SETUP0_RD_QUAD |
+ QSPI_CMD_WRITE | QSPI_NUM_DUMMY_BITS);
+ break;
+ default:
+ memval |= (QSPI_CMD_RD | QSPI_SETUP0_A_BYTES |
+ QSPI_SETUP0_NO_BITS | QSPI_SETUP0_RD_NORMAL |
+ QSPI_CMD_WRITE | QSPI_NUM_DUMMY_BITS);
+ break;
+ }
+ ti_qspi_write(qspi, memval, QSPI_SPI_SETUP0_REG);
+ spi->mode |= SPI_RX_MMAP;
+ }
+
pm_runtime_mark_last_busy(qspi->dev);
ret = pm_runtime_put_autosuspend(qspi->dev);
if (ret < 0) {
@@ -340,16 +418,7 @@ static int ti_qspi_start_transfer_one(struct spi_master *master,
struct spi_transfer *t;
int status = 0, ret;
int frame_length;
-
- /* setup device control reg */
- qspi->dc = 0;
-
- if (spi->mode & SPI_CPHA)
- qspi->dc |= QSPI_CKPHA(spi->chip_select);
- if (spi->mode & SPI_CPOL)
- qspi->dc |= QSPI_CKPOL(spi->chip_select);
- if (spi->mode & SPI_CS_HIGH)
- qspi->dc |= QSPI_CSPOL(spi->chip_select);
+ size_t from = 0;
frame_length = (m->frame_length << 3) / spi->bits_per_word;
@@ -362,11 +431,21 @@ static int ti_qspi_start_transfer_one(struct spi_master *master,
qspi->cmd |= QSPI_WC_CMD_INT_EN;
ti_qspi_write(qspi, QSPI_WC_INT_EN, QSPI_INTR_ENABLE_SET_REG);
- ti_qspi_write(qspi, qspi->dc, QSPI_SPI_DC_REG);
mutex_lock(&qspi->list_lock);
list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->memory_map) {
+ if (t->tx_buf) {
+ from = t->len;
+ continue;
+ }
+ enable_qspi_memory_mapped(qspi);
+ memcpy(t->rx_buf, qspi->mmap_base + from, t->len);
+ disable_qspi_memory_mapped(qspi);
+ goto out;
+ }
+
qspi->cmd |= QSPI_WLEN(t->bits_per_word);
ret = qspi_transfer_msg(qspi, t);
@@ -379,6 +458,7 @@ static int ti_qspi_start_transfer_one(struct spi_master *master,
m->actual_length += t->len;
}
+out:
mutex_unlock(&qspi->list_lock);
m->status = status;
@@ -437,7 +517,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
{
struct ti_qspi *qspi;
struct spi_master *master;
- struct resource *r;
+ struct resource *r, *res_ctrl, *res_mmap;
struct device_node *np = pdev->dev.of_node;
u32 max_freq;
int ret = 0, num_cs, irq;
@@ -446,7 +526,8 @@ static int ti_qspi_probe(struct platform_device *pdev)
if (!master)
return -ENOMEM;
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
+ SPI_RX_MMAP;
master->bus_num = -1;
master->flags = SPI_MASTER_HALF_DUPLEX;
@@ -465,7 +546,16 @@ static int ti_qspi_probe(struct platform_device *pdev)
qspi->master = master;
qspi->dev = &pdev->dev;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_base");
+ if (r == NULL) {
+ dev_err(&pdev->dev, "missing platform resources data\n");
+ return -ENODEV;
+ }
+
+ res_mmap = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "qspi_mmap");
+ res_ctrl = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "qspi_ctrlmod");
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -481,6 +571,23 @@ static int ti_qspi_probe(struct platform_device *pdev)
goto free_master;
}
+ if (res_ctrl) {
+ qspi->ctrl_mod = true;
+ qspi->ctrl_base = devm_ioremap_resource(&pdev->dev, res_ctrl);
+ if (IS_ERR(qspi->ctrl_base)) {
+ ret = PTR_ERR(qspi->ctrl_base);
+ goto free_master;
+ }
+ }
+
+ if (res_mmap) {
+ qspi->mmap_base = devm_ioremap_resource(&pdev->dev, res_mmap);
+ if (IS_ERR(qspi->mmap_base)) {
+ ret = PTR_ERR(qspi->mmap_base);
+ goto free_master;
+ }
+ }
+
ret = devm_request_irq(&pdev->dev, irq, ti_qspi_isr, 0,
dev_name(&pdev->dev), qspi);
if (ret < 0) {
@@ -504,6 +611,9 @@ static int ti_qspi_probe(struct platform_device *pdev)
if (!of_property_read_u32(np, "spi-max-frequency", &max_freq))
qspi->spi_max_frequency = max_freq;
+ if (of_property_read_bool(np, "mmap_read"))
+ qspi->memory_mapped = true;
+
ret = devm_spi_register_master(&pdev->dev, master);
if (ret)
goto free_master;
--
1.7.1
More information about the linux-mtd
mailing list