Unverified Commit 8097dbd4 authored by Mark Brown's avatar Mark Brown
Browse files

spi: Add RZ/V2M CSI target support

Merge series from Fabrizio Castro <fabrizio.castro.jz@renesas.com>:

The CSI IP found inside the Renesas RZ/V2M SoC supports both SPI host
and target.  This series extends the CSI dt-bindings and driver to
add SPI target support.
parents 9aaa25df a4f7ef6d
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -39,6 +39,12 @@ properties:
  power-domains:
    maxItems: 1

  renesas,csi-no-ss:
    type: boolean
    description:
      The CSI Slave Selection (SS) pin won't be used to enable transmission and
      reception. Only available when in target mode.

required:
  - compatible
  - reg
@@ -50,6 +56,9 @@ required:
  - '#address-cells'
  - '#size-cells'

dependencies:
  renesas,csi-no-ss: [ spi-slave ]

unevaluatedProperties: false

examples:
+2 −1
Original line number Diff line number Diff line
@@ -862,7 +862,8 @@ config SPI_RZV2M_CSI
	tristate "Renesas RZ/V2M CSI controller"
	depends on ARCH_RENESAS || COMPILE_TEST
	help
	  SPI driver for Renesas RZ/V2M Clocked Serial Interface (CSI)
	  SPI driver for Renesas RZ/V2M Clocked Serial Interface (CSI).
	  CSI supports both SPI host and SPI target roles.

config SPI_QCOM_QSPI
	tristate "QTI QSPI controller"
+83 −44
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/log2.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/reset.h>
@@ -38,6 +39,9 @@
#define CSI_MODE_SETUP		0x00000040

/* CSI_CLKSEL */
#define CSI_CLKSEL_SS_ENA	BIT(19)
#define CSI_CLKSEL_SS_POL	BIT(18)
#define CSI_CLKSEL_SS		(CSI_CLKSEL_SS_ENA | CSI_CLKSEL_SS_POL)
#define CSI_CLKSEL_CKP		BIT(17)
#define CSI_CLKSEL_DAP		BIT(16)
#define CSI_CLKSEL_MODE		(CSI_CLKSEL_CKP|CSI_CLKSEL_DAP)
@@ -82,6 +86,10 @@

#define CSI_MAX_SPI_SCKO	(8 * HZ_PER_MHZ)

#define CSI_CLKSEL_SS_DISABLED			0
#define CSI_CLKSEL_SS_ENABLED_ACTIVE_LOW	BIT(1)
#define CSI_CLKSEL_SS_ENABLED_ACTIVE_HIGH	GENMASK(1, 0)

struct rzv2m_csi_priv {
	void __iomem *base;
	struct clk *csiclk;
@@ -99,6 +107,8 @@ struct rzv2m_csi_priv {
	wait_queue_head_t wait;
	u32 errors;
	u32 status;
	bool target_aborted;
	bool use_ss_pin;
};

static void rzv2m_csi_reg_write_bit(const struct rzv2m_csi_priv *csi,
@@ -193,6 +203,14 @@ static int rzv2m_csi_read_rxfifo(struct rzv2m_csi_priv *csi)
	return 0;
}

static inline void rzv2m_csi_empty_rxfifo(struct rzv2m_csi_priv *csi)
{
	unsigned int i;

	for (i = 0; i < csi->words_to_transfer; i++)
		readl(csi->base + CSI_IFIFO);
}

static inline void rzv2m_csi_calc_current_transfer(struct rzv2m_csi_priv *csi)
{
	unsigned int bytes_transferred = max(csi->bytes_received, csi->bytes_sent);
@@ -279,32 +297,23 @@ static int rzv2m_csi_wait_for_interrupt(struct rzv2m_csi_priv *csi,

	rzv2m_csi_enable_irqs(csi, enable_bits);

	if (spi_controller_is_target(csi->controller)) {
		ret = wait_event_interruptible(csi->wait,
				((csi->status & wait_mask) == wait_mask) ||
				csi->errors || csi->target_aborted);
		if (ret || csi->target_aborted)
			ret = -EINTR;
	} else {
		ret = wait_event_timeout(csi->wait,
				((csi->status & wait_mask) == wait_mask) ||
				 csi->errors, HZ);
				csi->errors, HZ) == 0 ? -ETIMEDOUT : 0;
	}

	rzv2m_csi_disable_irqs(csi, enable_bits);

	if (csi->errors)
		return -EIO;

	if (!ret)
		return -ETIMEDOUT;

	return 0;
}

static int rzv2m_csi_wait_for_tx_empty(struct rzv2m_csi_priv *csi)
{
	int ret;

	if (readl(csi->base + CSI_OFIFOL) == 0)
		return 0;

	ret = rzv2m_csi_wait_for_interrupt(csi, CSI_INT_TREND, CSI_CNT_TREND_E);
	if (ret == -ETIMEDOUT)
		csi->errors |= TX_TIMEOUT_ERROR;

	return ret;
}

@@ -312,7 +321,7 @@ static inline int rzv2m_csi_wait_for_rx_ready(struct rzv2m_csi_priv *csi)
{
	int ret;

	if (readl(csi->base + CSI_IFIFOL) == csi->bytes_to_transfer)
	if (readl(csi->base + CSI_IFIFOL) >= csi->bytes_to_transfer)
		return 0;

	ret = rzv2m_csi_wait_for_interrupt(csi, CSI_INT_R_TRGR,
@@ -388,6 +397,7 @@ static void rzv2m_csi_setup_operating_mode(struct rzv2m_csi_priv *csi,
static int rzv2m_csi_setup(struct spi_device *spi)
{
	struct rzv2m_csi_priv *csi = spi_controller_get_devdata(spi->controller);
	u32 slave_selection = CSI_CLKSEL_SS_DISABLED;
	int ret;

	rzv2m_csi_sw_reset(csi, 0);
@@ -402,8 +412,17 @@ static int rzv2m_csi_setup(struct spi_device *spi)
	rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_DIR,
				!!(spi->mode & SPI_LSB_FIRST));

	/* Set the operation mode as master */
	rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_SLAVE, 0);
	/* Set the role, 1 for target and 0 for host */
	rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_SLAVE,
				!!spi_controller_is_target(csi->controller));

	if (csi->use_ss_pin)
		slave_selection = spi->mode & SPI_CS_HIGH ?
			CSI_CLKSEL_SS_ENABLED_ACTIVE_HIGH :
			CSI_CLKSEL_SS_ENABLED_ACTIVE_LOW;

	/* Configure the slave selection (SS) pin */
	rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_SS, slave_selection);

	/* Give the IP a SW reset */
	ret = rzv2m_csi_sw_reset(csi, 1);
@@ -431,9 +450,13 @@ static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi)
	/* Make sure the TX FIFO is empty */
	writel(0, csi->base + CSI_OFIFOL);

	/* Make sure the RX FIFO is empty */
	writel(0, csi->base + CSI_IFIFOL);

	csi->bytes_sent = 0;
	csi->bytes_received = 0;
	csi->errors = 0;
	csi->target_aborted = false;

	rzv2m_csi_disable_all_irqs(csi);
	rzv2m_csi_clear_all_irqs(csi);
@@ -452,28 +475,21 @@ static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi)

		rzv2m_csi_enable_irqs(csi, CSI_INT_OVERF | CSI_INT_UNDER);

		/* Make sure the RX FIFO is empty */
		writel(0, csi->base + CSI_IFIFOL);

		writel(readl(csi->base + CSI_INT), csi->base + CSI_INT);
		csi->status = 0;

		rzv2m_csi_start_stop_operation(csi, 1, false);

		/* TX */
		if (csi->txbuf) {
			ret = rzv2m_csi_fill_txfifo(csi);
			if (ret)
				break;

			ret = rzv2m_csi_wait_for_tx_empty(csi);
			if (ret)
				break;

			if (csi->bytes_sent == csi->buffer_len)
				tx_completed = true;
		}

		rzv2m_csi_start_stop_operation(csi, 1, false);

		/*
		 * Make sure the RX FIFO contains the desired number of words.
		 * We then either flush its content, or we copy it onto
@@ -483,31 +499,28 @@ static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi)
		if (ret)
			break;

		/* RX */
		if (csi->rxbuf) {
		if (!spi_controller_is_target(csi->controller))
			rzv2m_csi_start_stop_operation(csi, 0, false);

		/* RX */
		if (csi->rxbuf) {
			ret = rzv2m_csi_read_rxfifo(csi);
			if (ret)
				break;

			if (csi->bytes_received == csi->buffer_len)
				rx_completed = true;
		} else {
			rzv2m_csi_empty_rxfifo(csi);
		}

		ret = rzv2m_csi_start_stop_operation(csi, 0, true);
		if (ret)
			goto pio_quit;

		if (csi->errors) {
			ret = -EIO;
			goto pio_quit;
			break;
		}
	}

	rzv2m_csi_start_stop_operation(csi, 0, true);

pio_quit:
	rzv2m_csi_disable_all_irqs(csi);
	rzv2m_csi_enable_rx_trigger(csi, false);
	rzv2m_csi_clear_all_irqs(csi);
@@ -529,6 +542,7 @@ static int rzv2m_csi_transfer_one(struct spi_controller *controller,

	rzv2m_csi_setup_operating_mode(csi, transfer);

	if (!spi_controller_is_target(csi->controller))
		rzv2m_csi_setup_clock(csi, transfer->speed_hz);

	ret = rzv2m_csi_pio_transfer(csi);
@@ -546,24 +560,48 @@ static int rzv2m_csi_transfer_one(struct spi_controller *controller,
	return ret;
}

static int rzv2m_csi_target_abort(struct spi_controller *ctlr)
{
	struct rzv2m_csi_priv *csi = spi_controller_get_devdata(ctlr);

	csi->target_aborted = true;
	wake_up(&csi->wait);

	return 0;
}

static int rzv2m_csi_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct spi_controller *controller;
	struct device *dev = &pdev->dev;
	struct rzv2m_csi_priv *csi;
	struct reset_control *rstc;
	bool target_mode;
	int irq;
	int ret;

	target_mode = of_property_read_bool(np, "spi-slave");

	if (target_mode)
		controller = devm_spi_alloc_target(dev, sizeof(*csi));
	else
		controller = devm_spi_alloc_host(dev, sizeof(*csi));

	if (!controller)
		return -ENOMEM;

	csi = spi_controller_get_devdata(controller);
	platform_set_drvdata(pdev, csi);

	csi->use_ss_pin = false;
	if (spi_controller_is_target(controller) &&
	    !of_property_read_bool(np, "renesas,csi-no-ss"))
		csi->use_ss_pin = true;

	csi->dev = dev;
	csi->controller = controller;
	csi->target_aborted = false;

	csi->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(csi->base))
@@ -589,11 +627,12 @@ static int rzv2m_csi_probe(struct platform_device *pdev)

	init_waitqueue_head(&csi->wait);

	controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
	controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH;
	controller->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);
	controller->setup = rzv2m_csi_setup;
	controller->transfer_one = rzv2m_csi_transfer_one;
	controller->use_gpio_descriptors = true;
	controller->target_abort = rzv2m_csi_target_abort;

	device_set_node(&controller->dev, dev_fwnode(dev));