Commit 8097dbd4 authored by Mark Brown's avatar Mark Brown

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
...@@ -39,6 +39,12 @@ properties: ...@@ -39,6 +39,12 @@ properties:
power-domains: power-domains:
maxItems: 1 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: required:
- compatible - compatible
- reg - reg
...@@ -50,6 +56,9 @@ required: ...@@ -50,6 +56,9 @@ required:
- '#address-cells' - '#address-cells'
- '#size-cells' - '#size-cells'
dependencies:
renesas,csi-no-ss: [ spi-slave ]
unevaluatedProperties: false unevaluatedProperties: false
examples: examples:
......
...@@ -862,7 +862,8 @@ config SPI_RZV2M_CSI ...@@ -862,7 +862,8 @@ config SPI_RZV2M_CSI
tristate "Renesas RZ/V2M CSI controller" tristate "Renesas RZ/V2M CSI controller"
depends on ARCH_RENESAS || COMPILE_TEST depends on ARCH_RENESAS || COMPILE_TEST
help 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 config SPI_QCOM_QSPI
tristate "QTI QSPI controller" tristate "QTI QSPI controller"
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h> #include <linux/property.h>
#include <linux/reset.h> #include <linux/reset.h>
...@@ -38,6 +39,9 @@ ...@@ -38,6 +39,9 @@
#define CSI_MODE_SETUP 0x00000040 #define CSI_MODE_SETUP 0x00000040
/* CSI_CLKSEL */ /* 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_CKP BIT(17)
#define CSI_CLKSEL_DAP BIT(16) #define CSI_CLKSEL_DAP BIT(16)
#define CSI_CLKSEL_MODE (CSI_CLKSEL_CKP|CSI_CLKSEL_DAP) #define CSI_CLKSEL_MODE (CSI_CLKSEL_CKP|CSI_CLKSEL_DAP)
...@@ -82,6 +86,10 @@ ...@@ -82,6 +86,10 @@
#define CSI_MAX_SPI_SCKO (8 * HZ_PER_MHZ) #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 { struct rzv2m_csi_priv {
void __iomem *base; void __iomem *base;
struct clk *csiclk; struct clk *csiclk;
...@@ -99,6 +107,8 @@ struct rzv2m_csi_priv { ...@@ -99,6 +107,8 @@ struct rzv2m_csi_priv {
wait_queue_head_t wait; wait_queue_head_t wait;
u32 errors; u32 errors;
u32 status; u32 status;
bool target_aborted;
bool use_ss_pin;
}; };
static void rzv2m_csi_reg_write_bit(const struct rzv2m_csi_priv *csi, 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) ...@@ -193,6 +203,14 @@ static int rzv2m_csi_read_rxfifo(struct rzv2m_csi_priv *csi)
return 0; 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) static inline void rzv2m_csi_calc_current_transfer(struct rzv2m_csi_priv *csi)
{ {
unsigned int bytes_transferred = max(csi->bytes_received, csi->bytes_sent); 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, ...@@ -279,32 +297,23 @@ static int rzv2m_csi_wait_for_interrupt(struct rzv2m_csi_priv *csi,
rzv2m_csi_enable_irqs(csi, enable_bits); rzv2m_csi_enable_irqs(csi, enable_bits);
ret = wait_event_timeout(csi->wait, if (spi_controller_is_target(csi->controller)) {
((csi->status & wait_mask) == wait_mask) || ret = wait_event_interruptible(csi->wait,
csi->errors, HZ); ((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) == 0 ? -ETIMEDOUT : 0;
}
rzv2m_csi_disable_irqs(csi, enable_bits); rzv2m_csi_disable_irqs(csi, enable_bits);
if (csi->errors) if (csi->errors)
return -EIO; 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; return ret;
} }
...@@ -312,7 +321,7 @@ static inline int rzv2m_csi_wait_for_rx_ready(struct rzv2m_csi_priv *csi) ...@@ -312,7 +321,7 @@ static inline int rzv2m_csi_wait_for_rx_ready(struct rzv2m_csi_priv *csi)
{ {
int ret; int ret;
if (readl(csi->base + CSI_IFIFOL) == csi->bytes_to_transfer) if (readl(csi->base + CSI_IFIFOL) >= csi->bytes_to_transfer)
return 0; return 0;
ret = rzv2m_csi_wait_for_interrupt(csi, CSI_INT_R_TRGR, 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, ...@@ -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) static int rzv2m_csi_setup(struct spi_device *spi)
{ {
struct rzv2m_csi_priv *csi = spi_controller_get_devdata(spi->controller); struct rzv2m_csi_priv *csi = spi_controller_get_devdata(spi->controller);
u32 slave_selection = CSI_CLKSEL_SS_DISABLED;
int ret; int ret;
rzv2m_csi_sw_reset(csi, 0); rzv2m_csi_sw_reset(csi, 0);
...@@ -402,8 +412,17 @@ static int rzv2m_csi_setup(struct spi_device *spi) ...@@ -402,8 +412,17 @@ static int rzv2m_csi_setup(struct spi_device *spi)
rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_DIR, rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_DIR,
!!(spi->mode & SPI_LSB_FIRST)); !!(spi->mode & SPI_LSB_FIRST));
/* Set the operation mode as master */ /* Set the role, 1 for target and 0 for host */
rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_SLAVE, 0); 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 */ /* Give the IP a SW reset */
ret = rzv2m_csi_sw_reset(csi, 1); ret = rzv2m_csi_sw_reset(csi, 1);
...@@ -431,9 +450,13 @@ static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi) ...@@ -431,9 +450,13 @@ static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi)
/* Make sure the TX FIFO is empty */ /* Make sure the TX FIFO is empty */
writel(0, csi->base + CSI_OFIFOL); 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_sent = 0;
csi->bytes_received = 0; csi->bytes_received = 0;
csi->errors = 0; csi->errors = 0;
csi->target_aborted = false;
rzv2m_csi_disable_all_irqs(csi); rzv2m_csi_disable_all_irqs(csi);
rzv2m_csi_clear_all_irqs(csi); rzv2m_csi_clear_all_irqs(csi);
...@@ -452,28 +475,21 @@ static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *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); 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); writel(readl(csi->base + CSI_INT), csi->base + CSI_INT);
csi->status = 0; csi->status = 0;
rzv2m_csi_start_stop_operation(csi, 1, false);
/* TX */ /* TX */
if (csi->txbuf) { if (csi->txbuf) {
ret = rzv2m_csi_fill_txfifo(csi); ret = rzv2m_csi_fill_txfifo(csi);
if (ret) if (ret)
break; break;
ret = rzv2m_csi_wait_for_tx_empty(csi);
if (ret)
break;
if (csi->bytes_sent == csi->buffer_len) if (csi->bytes_sent == csi->buffer_len)
tx_completed = true; tx_completed = true;
} }
rzv2m_csi_start_stop_operation(csi, 1, false);
/* /*
* Make sure the RX FIFO contains the desired number of words. * Make sure the RX FIFO contains the desired number of words.
* We then either flush its content, or we copy it onto * 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) ...@@ -483,31 +499,28 @@ static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi)
if (ret) if (ret)
break; break;
/* RX */ if (!spi_controller_is_target(csi->controller))
if (csi->rxbuf) {
rzv2m_csi_start_stop_operation(csi, 0, false); rzv2m_csi_start_stop_operation(csi, 0, false);
/* RX */
if (csi->rxbuf) {
ret = rzv2m_csi_read_rxfifo(csi); ret = rzv2m_csi_read_rxfifo(csi);
if (ret) if (ret)
break; break;
if (csi->bytes_received == csi->buffer_len) if (csi->bytes_received == csi->buffer_len)
rx_completed = true; 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) { if (csi->errors) {
ret = -EIO; ret = -EIO;
goto pio_quit; break;
} }
} }
rzv2m_csi_start_stop_operation(csi, 0, true); rzv2m_csi_start_stop_operation(csi, 0, true);
pio_quit:
rzv2m_csi_disable_all_irqs(csi); rzv2m_csi_disable_all_irqs(csi);
rzv2m_csi_enable_rx_trigger(csi, false); rzv2m_csi_enable_rx_trigger(csi, false);
rzv2m_csi_clear_all_irqs(csi); rzv2m_csi_clear_all_irqs(csi);
...@@ -529,7 +542,8 @@ static int rzv2m_csi_transfer_one(struct spi_controller *controller, ...@@ -529,7 +542,8 @@ static int rzv2m_csi_transfer_one(struct spi_controller *controller,
rzv2m_csi_setup_operating_mode(csi, transfer); rzv2m_csi_setup_operating_mode(csi, transfer);
rzv2m_csi_setup_clock(csi, transfer->speed_hz); if (!spi_controller_is_target(csi->controller))
rzv2m_csi_setup_clock(csi, transfer->speed_hz);
ret = rzv2m_csi_pio_transfer(csi); ret = rzv2m_csi_pio_transfer(csi);
if (ret) { if (ret) {
...@@ -546,24 +560,48 @@ static int rzv2m_csi_transfer_one(struct spi_controller *controller, ...@@ -546,24 +560,48 @@ static int rzv2m_csi_transfer_one(struct spi_controller *controller,
return ret; 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) static int rzv2m_csi_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node;
struct spi_controller *controller; struct spi_controller *controller;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct rzv2m_csi_priv *csi; struct rzv2m_csi_priv *csi;
struct reset_control *rstc; struct reset_control *rstc;
bool target_mode;
int irq; int irq;
int ret; int ret;
controller = devm_spi_alloc_host(dev, sizeof(*csi)); 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) if (!controller)
return -ENOMEM; return -ENOMEM;
csi = spi_controller_get_devdata(controller); csi = spi_controller_get_devdata(controller);
platform_set_drvdata(pdev, csi); 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->dev = dev;
csi->controller = controller; csi->controller = controller;
csi->target_aborted = false;
csi->base = devm_platform_ioremap_resource(pdev, 0); csi->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(csi->base)) if (IS_ERR(csi->base))
...@@ -589,11 +627,12 @@ static int rzv2m_csi_probe(struct platform_device *pdev) ...@@ -589,11 +627,12 @@ static int rzv2m_csi_probe(struct platform_device *pdev)
init_waitqueue_head(&csi->wait); 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->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);
controller->setup = rzv2m_csi_setup; controller->setup = rzv2m_csi_setup;
controller->transfer_one = rzv2m_csi_transfer_one; controller->transfer_one = rzv2m_csi_transfer_one;
controller->use_gpio_descriptors = true; controller->use_gpio_descriptors = true;
controller->target_abort = rzv2m_csi_target_abort;
device_set_node(&controller->dev, dev_fwnode(dev)); device_set_node(&controller->dev, dev_fwnode(dev));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment