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

Commit 7ef35aef authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "spi: spi_qsd: Add slave support for QUP core"

parents 3495b7c4 200011ca
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;
};