summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-rzv2m-csi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-rzv2m-csi.c')
-rw-r--r--drivers/spi/spi-rzv2m-csi.c122
1 files changed, 56 insertions, 66 deletions
diff --git a/drivers/spi/spi-rzv2m-csi.c b/drivers/spi/spi-rzv2m-csi.c
index 14ad65da930d..d0f51b17aa7c 100644
--- a/drivers/spi/spi-rzv2m-csi.c
+++ b/drivers/spi/spi-rzv2m-csi.c
@@ -5,13 +5,17 @@
* Copyright (C) 2023 Renesas Electronics Corporation
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/count_zeros.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
+#include <linux/log2.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/reset.h>
#include <linux/spi/spi.h>
+#include <linux/units.h>
/* Registers */
#define CSI_MODE 0x00 /* CSI mode control */
@@ -36,6 +40,7 @@
/* CSI_CLKSEL */
#define CSI_CLKSEL_CKP BIT(17)
#define CSI_CLKSEL_DAP BIT(16)
+#define CSI_CLKSEL_MODE (CSI_CLKSEL_CKP|CSI_CLKSEL_DAP)
#define CSI_CLKSEL_SLAVE BIT(15)
#define CSI_CLKSEL_CKS GENMASK(14, 1)
@@ -60,17 +65,22 @@
/* CSI_FIFOTRG */
#define CSI_FIFOTRG_R_TRG GENMASK(2, 0)
-#define CSI_FIFO_SIZE_BYTES 32
-#define CSI_FIFO_HALF_SIZE 16
+#define CSI_FIFO_SIZE_BYTES 32U
+#define CSI_FIFO_HALF_SIZE 16U
#define CSI_EN_DIS_TIMEOUT_US 100
-#define CSI_CKS_MAX 0x3FFF
+/*
+ * Clock "csiclk" gets divided by 2 * CSI_CLKSEL_CKS in order to generate the
+ * serial clock (output from master), with CSI_CLKSEL_CKS ranging from 0x1 (that
+ * means "csiclk" is divided by 2) to 0x3FFF ("csiclk" is divided by 32766).
+ */
+#define CSI_CKS_MAX GENMASK(13, 0)
#define UNDERRUN_ERROR BIT(0)
#define OVERFLOW_ERROR BIT(1)
#define TX_TIMEOUT_ERROR BIT(2)
#define RX_TIMEOUT_ERROR BIT(3)
-#define CSI_MAX_SPI_SCKO 8000000
+#define CSI_MAX_SPI_SCKO (8 * HZ_PER_MHZ)
struct rzv2m_csi_priv {
void __iomem *base;
@@ -78,33 +88,19 @@ struct rzv2m_csi_priv {
struct clk *pclk;
struct device *dev;
struct spi_controller *controller;
- const u8 *txbuf;
- u8 *rxbuf;
- int buffer_len;
- int bytes_sent;
- int bytes_received;
- int bytes_to_transfer;
- int words_to_transfer;
- unsigned char bytes_per_word;
+ const void *txbuf;
+ void *rxbuf;
+ unsigned int buffer_len;
+ unsigned int bytes_sent;
+ unsigned int bytes_received;
+ unsigned int bytes_to_transfer;
+ unsigned int words_to_transfer;
+ unsigned int bytes_per_word;
wait_queue_head_t wait;
- u8 errors;
+ u32 errors;
u32 status;
};
-static const unsigned char x_trg[] = {
- 0, 1, 1, 2, 2, 2, 2, 3,
- 3, 3, 3, 3, 3, 3, 3, 4,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 5
-};
-
-static const unsigned char x_trg_words[] = {
- 1, 2, 2, 4, 4, 4, 4, 8,
- 8, 8, 8, 8, 8, 8, 8, 16,
- 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 16, 16, 16, 32
-};
-
static void rzv2m_csi_reg_write_bit(const struct rzv2m_csi_priv *csi,
int reg_offs, int bit_mask, u32 value)
{
@@ -124,13 +120,12 @@ static int rzv2m_csi_sw_reset(struct rzv2m_csi_priv *csi, int assert)
rzv2m_csi_reg_write_bit(csi, CSI_CNT, CSI_CNT_CSIRST, assert);
- if (assert) {
- return readl_poll_timeout(csi->base + CSI_MODE, reg,
- !(reg & CSI_MODE_CSOT), 0,
- CSI_EN_DIS_TIMEOUT_US);
- }
+ if (!assert)
+ return 0;
- return 0;
+ return readl_poll_timeout(csi->base + CSI_MODE, reg,
+ !(reg & CSI_MODE_CSOT), 0,
+ CSI_EN_DIS_TIMEOUT_US);
}
static int rzv2m_csi_start_stop_operation(const struct rzv2m_csi_priv *csi,
@@ -140,28 +135,28 @@ static int rzv2m_csi_start_stop_operation(const struct rzv2m_csi_priv *csi,
rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_CSIE, enable);
- if (!enable && wait)
- return readl_poll_timeout(csi->base + CSI_MODE, reg,
- !(reg & CSI_MODE_CSOT), 0,
- CSI_EN_DIS_TIMEOUT_US);
+ if (enable || !wait)
+ return 0;
- return 0;
+ return readl_poll_timeout(csi->base + CSI_MODE, reg,
+ !(reg & CSI_MODE_CSOT), 0,
+ CSI_EN_DIS_TIMEOUT_US);
}
static int rzv2m_csi_fill_txfifo(struct rzv2m_csi_priv *csi)
{
- int i;
+ unsigned int i;
if (readl(csi->base + CSI_OFIFOL))
return -EIO;
if (csi->bytes_per_word == 2) {
- u16 *buf = (u16 *)csi->txbuf;
+ const u16 *buf = csi->txbuf;
for (i = 0; i < csi->words_to_transfer; i++)
writel(buf[i], csi->base + CSI_OFIFO);
} else {
- u8 *buf = (u8 *)csi->txbuf;
+ const u8 *buf = csi->txbuf;
for (i = 0; i < csi->words_to_transfer; i++)
writel(buf[i], csi->base + CSI_OFIFO);
@@ -175,18 +170,18 @@ static int rzv2m_csi_fill_txfifo(struct rzv2m_csi_priv *csi)
static int rzv2m_csi_read_rxfifo(struct rzv2m_csi_priv *csi)
{
- int i;
+ unsigned int i;
if (readl(csi->base + CSI_IFIFOL) != csi->bytes_to_transfer)
return -EIO;
if (csi->bytes_per_word == 2) {
- u16 *buf = (u16 *)csi->rxbuf;
+ u16 *buf = csi->rxbuf;
for (i = 0; i < csi->words_to_transfer; i++)
buf[i] = (u16)readl(csi->base + CSI_IFIFO);
} else {
- u8 *buf = (u8 *)csi->rxbuf;
+ u8 *buf = csi->rxbuf;
for (i = 0; i < csi->words_to_transfer; i++)
buf[i] = (u8)readl(csi->base + CSI_IFIFO);
@@ -200,9 +195,9 @@ static int rzv2m_csi_read_rxfifo(struct rzv2m_csi_priv *csi)
static inline void rzv2m_csi_calc_current_transfer(struct rzv2m_csi_priv *csi)
{
- int bytes_transferred = max_t(int, csi->bytes_received, csi->bytes_sent);
- int bytes_remaining = csi->buffer_len - bytes_transferred;
- int to_transfer;
+ unsigned int bytes_transferred = max(csi->bytes_received, csi->bytes_sent);
+ unsigned int bytes_remaining = csi->buffer_len - bytes_transferred;
+ unsigned int to_transfer;
if (csi->txbuf)
/*
@@ -210,9 +205,9 @@ static inline void rzv2m_csi_calc_current_transfer(struct rzv2m_csi_priv *csi)
* hard to raise an overflow error (which is only possible
* when IP transmits and receives at the same time).
*/
- to_transfer = min_t(int, CSI_FIFO_HALF_SIZE, bytes_remaining);
+ to_transfer = min(CSI_FIFO_HALF_SIZE, bytes_remaining);
else
- to_transfer = min_t(int, CSI_FIFO_SIZE_BYTES, bytes_remaining);
+ to_transfer = min(CSI_FIFO_SIZE_BYTES, bytes_remaining);
if (csi->bytes_per_word == 2)
to_transfer >>= 1;
@@ -223,7 +218,7 @@ static inline void rzv2m_csi_calc_current_transfer(struct rzv2m_csi_priv *csi)
* less than or equal to the number of bytes we need to transfer.
* This may result in multiple smaller transfers.
*/
- csi->words_to_transfer = x_trg_words[to_transfer - 1];
+ csi->words_to_transfer = rounddown_pow_of_two(to_transfer);
if (csi->bytes_per_word == 2)
csi->bytes_to_transfer = csi->words_to_transfer << 1;
@@ -234,7 +229,7 @@ static inline void rzv2m_csi_calc_current_transfer(struct rzv2m_csi_priv *csi)
static inline void rzv2m_csi_set_rx_fifo_trigger_level(struct rzv2m_csi_priv *csi)
{
rzv2m_csi_reg_write_bit(csi, CSI_FIFOTRG, CSI_FIFOTRG_R_TRG,
- x_trg[csi->words_to_transfer - 1]);
+ ilog2(csi->words_to_transfer));
}
static inline void rzv2m_csi_enable_rx_trigger(struct rzv2m_csi_priv *csi,
@@ -307,7 +302,6 @@ static int rzv2m_csi_wait_for_tx_empty(struct rzv2m_csi_priv *csi)
return 0;
ret = rzv2m_csi_wait_for_interrupt(csi, CSI_INT_TREND, CSI_CNT_TREND_E);
-
if (ret == -ETIMEDOUT)
csi->errors |= TX_TIMEOUT_ERROR;
@@ -323,7 +317,6 @@ static inline int rzv2m_csi_wait_for_rx_ready(struct rzv2m_csi_priv *csi)
ret = rzv2m_csi_wait_for_interrupt(csi, CSI_INT_R_TRGR,
CSI_CNT_R_TRGR_E);
-
if (ret == -ETIMEDOUT)
csi->errors |= RX_TIMEOUT_ERROR;
@@ -332,7 +325,7 @@ static inline int rzv2m_csi_wait_for_rx_ready(struct rzv2m_csi_priv *csi)
static irqreturn_t rzv2m_csi_irq_handler(int irq, void *data)
{
- struct rzv2m_csi_priv *csi = (struct rzv2m_csi_priv *)data;
+ struct rzv2m_csi_priv *csi = data;
csi->status = readl(csi->base + CSI_INT);
rzv2m_csi_disable_irqs(csi, csi->status);
@@ -402,10 +395,8 @@ static int rzv2m_csi_setup(struct spi_device *spi)
writel(CSI_MODE_SETUP, csi->base + CSI_MODE);
/* Setup clock polarity and phase timing */
- rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_CKP,
- !(spi->mode & SPI_CPOL));
- rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_DAP,
- !(spi->mode & SPI_CPHA));
+ rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_MODE,
+ ~spi->mode & SPI_MODE_X_MASK);
/* Setup serial data order */
rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_DIR,
@@ -433,8 +424,8 @@ static int rzv2m_csi_setup(struct spi_device *spi)
static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi)
{
- bool tx_completed = csi->txbuf ? false : true;
- bool rx_completed = csi->rxbuf ? false : true;
+ bool tx_completed = !csi->txbuf;
+ bool rx_completed = !csi->rxbuf;
int ret = 0;
/* Make sure the TX FIFO is empty */
@@ -564,7 +555,7 @@ static int rzv2m_csi_probe(struct platform_device *pdev)
int irq;
int ret;
- controller = devm_spi_alloc_master(dev, sizeof(*csi));
+ controller = devm_spi_alloc_host(dev, sizeof(*csi));
if (!controller)
return -ENOMEM;
@@ -599,12 +590,13 @@ 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->dev.of_node = pdev->dev.of_node;
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;
+ device_set_node(&controller->dev, dev_fwnode(dev));
+
ret = devm_request_irq(dev, irq, rzv2m_csi_irq_handler, 0,
dev_name(dev), csi);
if (ret)
@@ -635,15 +627,13 @@ static int rzv2m_csi_probe(struct platform_device *pdev)
return 0;
}
-static int rzv2m_csi_remove(struct platform_device *pdev)
+static void rzv2m_csi_remove(struct platform_device *pdev)
{
struct rzv2m_csi_priv *csi = platform_get_drvdata(pdev);
spi_unregister_controller(csi->controller);
rzv2m_csi_sw_reset(csi, 1);
clk_disable_unprepare(csi->csiclk);
-
- return 0;
}
static const struct of_device_id rzv2m_csi_match[] = {
@@ -654,7 +644,7 @@ MODULE_DEVICE_TABLE(of, rzv2m_csi_match);
static struct platform_driver rzv2m_csi_drv = {
.probe = rzv2m_csi_probe,
- .remove = rzv2m_csi_remove,
+ .remove_new = rzv2m_csi_remove,
.driver = {
.name = "rzv2m_csi",
.of_match_table = rzv2m_csi_match,