diff options
Diffstat (limited to 'drivers/spi/spi_qsd.c')
-rw-r--r-- | drivers/spi/spi_qsd.c | 104 |
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, |