Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 200011ca authored by Dilip Kota's avatar Dilip Kota
Browse files

spi: spi_qsd: Add slave support for QUP core



The QUP core can be programmed to operate as an SPI slave. This
change modifies the existing SPI master driver and adds the slave
functionality.

Change-Id: I73189339956e0fd52449391737fb1e616a6e0bb2
Signed-off-by: default avatarDilip Kota <dkota@codeaurora.org>
parent aedf62a2
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
Qualcomm Serial Peripheral Interface (SPI)

Required properties:
- compatible : Should be "qcom,spi-qup-v2".
- compatible : Should be "qcom,spi-qup-v2". Should "qcom,qup-v26" for
		controllers that support spi slave mode.
- reg : Offset and length of the register regions for the device
- reg-names : Register region names referenced in reg above.
	Required register resource entries are:
@@ -72,6 +73,7 @@ the following properties.
  clock phase (CPHA) mode
- spi-cs-high : (optional) Empty property indicating device requires
  chip select active high
- qcom,slv-ctrl : Set this flag to configure QUP as SPI slave controller.

Example:
	aliases {
+91 −13
Original line number Diff line number Diff line
@@ -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,6 +86,9 @@ static inline int msm_spi_configure_gsbi(struct msm_spi *dd,

static inline void msm_spi_register_init(struct msm_spi *dd)
{
	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);
@@ -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);
	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,
+29 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@
#define QUP_MX_WRITE_CNT_CURRENT      0x0154

#define QUP_CONFIG_SPI_MODE           0x0100
#define QUP_CONFIG_SPI_SLAVE          0x0400
#endif

#define GSBI_CTRL_REG                 0x0
@@ -72,17 +73,26 @@
#define SPI_OUTPUT_FIFO               QSD_REG(0x0100) QUP_REG(0x0110)
#define SPI_INPUT_FIFO                QSD_REG(0x0200) QUP_REG(0x0218)
#define SPI_STATE                     QSD_REG(SPI_OPERATIONAL) QUP_REG(0x0004)
#define SPI_SLAVE_IRQ_STATUS		(0x0330)
#define SPI_SLAVE_IRQ_EN		(0x0334)
#define SPI_SLAVE_CONFIG		(0x0338)

/* QUP_CONFIG fields */
#define SPI_CFG_N                     0x0000001F
#define SPI_NO_INPUT                  0x00000080
#define SPI_NO_OUTPUT                 0x00000040
#define SPI_EN_EXT_OUT_FLAG           0x00010000
#define QUP_CFG_MODE                  0x00000F00
#define APP_CLK_ON_EN			BIT(12)
#define CORE_CLK_ON_EN			BIT(13)
#define FIFO_CLK_ON_EN			BIT(14)
#define CORE_EX_CLK_ON_EN		BIT(15)

/* SPI_CONFIG fields */
#define SPI_CFG_LOOPBACK              0x00000100
#define SPI_CFG_INPUT_FIRST           0x00000200
#define SPI_CFG_HS_MODE               0x00000400
#define SPI_CFG_SLAVE_OP              0x00000020

/* SPI_IO_CONTROL fields */
#define SPI_IO_C_FORCE_CS             0x00000800
@@ -128,6 +138,23 @@

#define SPI_OP_STATE_CLEAR_BITS       0x2

/* SPI SLAVE IRQ_STATUS/EN fields */
#define CS_N_ASSERT			BIT(0)
#define CS_N_DEASSERT			BIT(1)
#define CS_N_ETXT			BIT(2)
#define TX_UNDERFLOW			BIT(3)
#define RX_OVERFLOW_WAIT_EOT		BIT(4)
#define RX_OVERFLOW_NO_EOT		BIT(5)
#define CS_N_ERXT			BIT(6)

/* SPI_SLAVE_CONFIG Fields */
#define RX_N_SHIFT			BIT(0)
#define PAUSE_ON_ERR_DIS		BIT(1)
#define SPI_S_CGC_EN			BIT(2)
#define RX_UNBALANCED_MASK		BIT(3)
#define SLAVE_DIS_RESET_ST		BIT(4)
#define SLAVE_AUTO_PAUSE_EOT		BIT(7)

#define SPI_PINCTRL_STATE_DEFAULT "spi_default"
#define SPI_PINCTRL_STATE_SLEEP "spi_sleep"

@@ -177,6 +204,7 @@ enum msm_spi_state {
enum msm_spi_qup_version {
	SPI_QUP_VERSION_NONE    = 0x0,
	SPI_QUP_VERSION_BFAM    = 0x2,
	SPI_QUP_VERSION_SPI_SLV = 0x26,
};

enum msm_spi_pipe_direction {
@@ -376,6 +404,7 @@ struct msm_spi {
	struct pinctrl_state	*pins_sleep;
	bool			is_init_complete;
	bool			pack_words;
	bool			slv_support;
};

/* Forward declaration */
+2 −0
Original line number Diff line number Diff line
@@ -48,9 +48,11 @@ struct msm_spi_platform_data {
	u32  infinite_mode;
	bool ver_reg_exists;
	bool use_bam;
	bool slv_test;
	u32  bam_consumer_pipe_index;
	u32  bam_producer_pipe_index;
	bool rt_priority;
	bool use_pinctrl;
	bool is_shared;
	bool is_slv_ctrl;
};