summaryrefslogtreecommitdiff
path: root/drivers/spi/spi_qsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi_qsd.c')
-rw-r--r--drivers/spi/spi_qsd.c104
1 files changed, 91 insertions, 13 deletions
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index da58f19dd6e6..73396072a052 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -33,6 +33,7 @@
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
@@ -52,6 +53,7 @@ static int msm_spi_pm_suspend_runtime(struct device *device);
static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd);
static int get_local_resources(struct msm_spi *dd);
static void put_local_resources(struct msm_spi *dd);
+static void msm_spi_slv_setup(struct msm_spi *dd);
static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
struct platform_device *pdev)
@@ -84,7 +86,10 @@ static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
static inline void msm_spi_register_init(struct msm_spi *dd)
{
- writel_relaxed(0x00000001, dd->base + SPI_SW_RESET);
+ if (dd->pdata->is_slv_ctrl)
+ writel_relaxed(0x00000002, dd->base + SPI_SW_RESET);
+ else
+ writel_relaxed(0x00000001, dd->base + SPI_SW_RESET);
msm_spi_set_state(dd, SPI_OP_STATE_RESET);
writel_relaxed(0x00000000, dd->base + SPI_OPERATIONAL);
writel_relaxed(0x00000000, dd->base + SPI_CONFIG);
@@ -932,6 +937,7 @@ static inline void msm_spi_ack_transfer(struct msm_spi *dd)
static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
{
u32 op, ret = IRQ_NONE;
+ u32 slv;
struct msm_spi *dd = dev_id;
if (pm_runtime_suspended(dd->dev)) {
@@ -945,7 +951,9 @@ static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
}
op = readl_relaxed(dd->base + SPI_OPERATIONAL);
+ slv = readl_relaxed(dd->base + SPI_SLAVE_IRQ_STATUS);
writel_relaxed(op, dd->base + SPI_OPERATIONAL);
+ writel_relaxed(slv, dd->base + SPI_SLAVE_IRQ_STATUS);
/*
* Ensure service flag was cleared before further
* processing of interrupt.
@@ -1234,7 +1242,10 @@ msm_spi_use_dma(struct msm_spi *dd, struct spi_transfer *tr, u8 bpw)
static void
msm_spi_set_transfer_mode(struct msm_spi *dd, u8 bpw, u32 read_count)
{
- if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) {
+ if (dd->pdata->is_slv_ctrl) {
+ dd->tx_mode = SPI_BAM_MODE;
+ dd->rx_mode = SPI_BAM_MODE;
+ } else if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) {
dd->tx_mode = SPI_BAM_MODE;
dd->rx_mode = SPI_BAM_MODE;
} else {
@@ -1353,7 +1364,7 @@ static void get_transfer_length(struct msm_spi *dd)
static int msm_spi_process_transfer(struct msm_spi *dd)
{
u8 bpw;
- u32 max_speed;
+ u32 max_speed = 0;
u32 read_count;
u32 timeout;
u32 spi_ioc;
@@ -1392,7 +1403,7 @@ static int msm_spi_process_transfer(struct msm_spi *dd)
DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word);
- if (dd->spi->mode & SPI_LOOP)
+ if (dd->spi->mode & SPI_LOOP && !dd->pdata->is_slv_ctrl)
int_loopback = 1;
if (msm_spi_set_state(dd, SPI_OP_STATE_RESET))
@@ -1414,8 +1425,11 @@ static int msm_spi_process_transfer(struct msm_spi *dd)
msm_spi_set_qup_io_modes(dd);
msm_spi_set_spi_config(dd, bpw);
msm_spi_set_qup_config(dd, bpw);
- spi_ioc = msm_spi_set_spi_io_control(dd);
+ if (!dd->pdata->is_slv_ctrl)
+ spi_ioc = msm_spi_set_spi_io_control(dd);
msm_spi_set_qup_op_mask(dd);
+ if (dd->pdata->is_slv_ctrl)
+ msm_spi_slv_setup(dd);
/* The output fifo interrupt handler will handle all writes after
the first. Restricting this to one write avoids contention
@@ -1482,12 +1496,20 @@ transfer_end:
dd->rx_mode = SPI_MODE_NONE;
msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- if (!dd->cur_transfer->cs_change)
+ if (!dd->cur_transfer->cs_change && !dd->pdata->is_slv_ctrl)
writel_relaxed(spi_ioc & ~SPI_IO_C_MX_CS_MODE,
dd->base + SPI_IO_CONTROL);
return status;
}
+static int msm_spi_slv_abort(struct spi_master *spi)
+{
+ struct msm_spi *dd = spi_master_get_devdata(spi);
+
+ complete_all(&dd->tx_transfer_complete);
+ complete_all(&dd->rx_transfer_complete);
+ return 0;
+}
static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag)
{
@@ -1753,6 +1775,32 @@ static int msm_spi_unprepare_transfer_hardware(struct spi_master *master)
return 0;
}
+static void msm_spi_slv_setup(struct msm_spi *dd)
+{
+ u32 spi_config = readl_relaxed(dd->base + SPI_CONFIG);
+ u32 qup_config = readl_relaxed(dd->base + QUP_CONFIG);
+ u32 irq_en = GENMASK(6, 0);
+
+ qup_config &= ~QUP_CFG_MODE;
+ qup_config |= QUP_CONFIG_SPI_SLAVE;
+ qup_config |= (SPI_EN_EXT_OUT_FLAG | APP_CLK_ON_EN | CORE_CLK_ON_EN
+ | FIFO_CLK_ON_EN | CORE_EX_CLK_ON_EN);
+ spi_config |= SPI_CFG_SLAVE_OP;
+ writel_relaxed(qup_config, dd->base + QUP_CONFIG);
+ writel_relaxed(spi_config, dd->base + SPI_CONFIG);
+ writel_relaxed(irq_en, (dd->base + SPI_SLAVE_IRQ_EN));
+ if (dd->read_buf && !dd->write_buf) {
+ u32 slv_cfg =
+ readl_relaxed(dd->base + SPI_SLAVE_CONFIG);
+ slv_cfg |= (RX_UNBALANCED_MASK | SPI_S_CGC_EN);
+ writel_relaxed(slv_cfg, (dd->base + SPI_SLAVE_CONFIG));
+ }
+ /*
+ * Ensure Slave setup completes before returning.
+ */
+ mb();
+}
+
static int msm_spi_setup(struct spi_device *spi)
{
struct msm_spi *dd;
@@ -2248,6 +2296,8 @@ struct msm_spi_platform_data *msm_spi_dt_to_pdata(
&pdata->rt_priority, DT_OPT, DT_BOOL, 0},
{"qcom,shared",
&pdata->is_shared, DT_OPT, DT_BOOL, 0},
+ {"qcom,slv-ctrl",
+ &pdata->is_slv_ctrl, DT_OPT, DT_BOOL, 0},
{NULL, NULL, 0, 0, 0},
};
@@ -2451,6 +2501,12 @@ err_clk_get:
return rc;
}
+static const struct of_device_id msm_spi_dt_match[] = {
+ { .compatible = "qcom,spi-qup-v2", },
+ { .compatible = "qcom,qup-v26", },
+ {}
+};
+
static int msm_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
@@ -2481,6 +2537,9 @@ static int msm_spi_probe(struct platform_device *pdev)
dd = spi_master_get_devdata(master);
if (pdev->dev.of_node) {
+ const struct of_device_id *dev_id;
+ enum msm_spi_qup_version ver;
+
dd->qup_ver = SPI_QUP_VERSION_BFAM;
master->dev.of_node = pdev->dev.of_node;
pdata = msm_spi_dt_to_pdata(pdev, dd);
@@ -2495,6 +2554,17 @@ static int msm_spi_probe(struct platform_device *pdev)
"using default bus_num %d\n", pdev->id);
else
master->bus_num = pdev->id = rc;
+
+ dev_id = of_match_device(msm_spi_dt_match, &pdev->dev);
+ if (dev_id)
+ ver = SPI_QUP_VERSION_SPI_SLV;
+ else
+ ver = SPI_QUP_VERSION_BFAM;
+
+ if (ver >= SPI_QUP_VERSION_SPI_SLV)
+ dd->slv_support = true;
+ else
+ dd->slv_support = false;
} else {
pdata = pdev->dev.platform_data;
dd->qup_ver = SPI_QUP_VERSION_NONE;
@@ -2553,6 +2623,21 @@ static int msm_spi_probe(struct platform_device *pdev)
}
spi_dma_mask(&pdev->dev);
+
+ if (pdata && pdata->is_slv_ctrl) {
+ if (!dd->slv_support) {
+ rc = -ENXIO;
+ dev_err(&pdev->dev, "QUP ver %d, no slv support\n",
+ dd->qup_ver);
+ goto err_probe_res;
+ }
+
+ master->slave = true;
+ master->set_cs = NULL;
+ master->setup = NULL;
+ master->slave_abort = msm_spi_slv_abort;
+ }
+
skip_dma_resources:
spin_lock_init(&dd->queue_lock);
@@ -2753,13 +2838,6 @@ static int msm_spi_remove(struct platform_device *pdev)
return 0;
}
-static struct of_device_id msm_spi_dt_match[] = {
- {
- .compatible = "qcom,spi-qup-v2",
- },
- {}
-};
-
static const struct dev_pm_ops msm_spi_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(msm_spi_suspend, msm_spi_resume)
SET_RUNTIME_PM_OPS(msm_spi_pm_suspend_runtime,