xref: /openbmc/linux/drivers/spi/spi-rzv2m-csi.c (revision 2ed2699f)
183c624d8SFabrizio Castro // SPDX-License-Identifier: GPL-2.0-or-later
283c624d8SFabrizio Castro /*
383c624d8SFabrizio Castro  * Renesas RZ/V2M Clocked Serial Interface (CSI) driver
483c624d8SFabrizio Castro  *
583c624d8SFabrizio Castro  * Copyright (C) 2023 Renesas Electronics Corporation
683c624d8SFabrizio Castro  */
783c624d8SFabrizio Castro 
8f572ba79SFabrizio Castro #include <linux/bits.h>
983c624d8SFabrizio Castro #include <linux/clk.h>
1083c624d8SFabrizio Castro #include <linux/count_zeros.h>
1183c624d8SFabrizio Castro #include <linux/interrupt.h>
1283c624d8SFabrizio Castro #include <linux/iopoll.h>
1383c624d8SFabrizio Castro #include <linux/platform_device.h>
1483c624d8SFabrizio Castro #include <linux/reset.h>
1583c624d8SFabrizio Castro #include <linux/spi/spi.h>
1674e27ce8SFabrizio Castro #include <linux/units.h>
1783c624d8SFabrizio Castro 
1883c624d8SFabrizio Castro /* Registers */
1983c624d8SFabrizio Castro #define CSI_MODE		0x00	/* CSI mode control */
2083c624d8SFabrizio Castro #define CSI_CLKSEL		0x04	/* CSI clock select */
2183c624d8SFabrizio Castro #define CSI_CNT			0x08	/* CSI control */
2283c624d8SFabrizio Castro #define CSI_INT			0x0C	/* CSI interrupt status */
2383c624d8SFabrizio Castro #define CSI_IFIFOL		0x10	/* CSI receive FIFO level display */
2483c624d8SFabrizio Castro #define CSI_OFIFOL		0x14	/* CSI transmit FIFO level display */
2583c624d8SFabrizio Castro #define CSI_IFIFO		0x18	/* CSI receive window */
2683c624d8SFabrizio Castro #define CSI_OFIFO		0x1C	/* CSI transmit window */
2783c624d8SFabrizio Castro #define CSI_FIFOTRG		0x20	/* CSI FIFO trigger level */
2883c624d8SFabrizio Castro 
2983c624d8SFabrizio Castro /* CSI_MODE */
3083c624d8SFabrizio Castro #define CSI_MODE_CSIE		BIT(7)
3183c624d8SFabrizio Castro #define CSI_MODE_TRMD		BIT(6)
3283c624d8SFabrizio Castro #define CSI_MODE_CCL		BIT(5)
3383c624d8SFabrizio Castro #define CSI_MODE_DIR		BIT(4)
3483c624d8SFabrizio Castro #define CSI_MODE_CSOT		BIT(0)
3583c624d8SFabrizio Castro 
3683c624d8SFabrizio Castro #define CSI_MODE_SETUP		0x00000040
3783c624d8SFabrizio Castro 
3883c624d8SFabrizio Castro /* CSI_CLKSEL */
3983c624d8SFabrizio Castro #define CSI_CLKSEL_CKP		BIT(17)
4083c624d8SFabrizio Castro #define CSI_CLKSEL_DAP		BIT(16)
4183c624d8SFabrizio Castro #define CSI_CLKSEL_SLAVE	BIT(15)
4283c624d8SFabrizio Castro #define CSI_CLKSEL_CKS		GENMASK(14, 1)
4383c624d8SFabrizio Castro 
4483c624d8SFabrizio Castro /* CSI_CNT */
4583c624d8SFabrizio Castro #define CSI_CNT_CSIRST		BIT(28)
4683c624d8SFabrizio Castro #define CSI_CNT_R_TRGEN		BIT(19)
4783c624d8SFabrizio Castro #define CSI_CNT_UNDER_E		BIT(13)
4883c624d8SFabrizio Castro #define CSI_CNT_OVERF_E		BIT(12)
4983c624d8SFabrizio Castro #define CSI_CNT_TREND_E		BIT(9)
5083c624d8SFabrizio Castro #define CSI_CNT_CSIEND_E	BIT(8)
5183c624d8SFabrizio Castro #define CSI_CNT_T_TRGR_E	BIT(4)
5283c624d8SFabrizio Castro #define CSI_CNT_R_TRGR_E	BIT(0)
5383c624d8SFabrizio Castro 
5483c624d8SFabrizio Castro /* CSI_INT */
5583c624d8SFabrizio Castro #define CSI_INT_UNDER		BIT(13)
5683c624d8SFabrizio Castro #define CSI_INT_OVERF		BIT(12)
5783c624d8SFabrizio Castro #define CSI_INT_TREND		BIT(9)
5883c624d8SFabrizio Castro #define CSI_INT_CSIEND		BIT(8)
5983c624d8SFabrizio Castro #define CSI_INT_T_TRGR		BIT(4)
6083c624d8SFabrizio Castro #define CSI_INT_R_TRGR		BIT(0)
6183c624d8SFabrizio Castro 
6283c624d8SFabrizio Castro /* CSI_FIFOTRG */
6383c624d8SFabrizio Castro #define CSI_FIFOTRG_R_TRG       GENMASK(2, 0)
6483c624d8SFabrizio Castro 
6583c624d8SFabrizio Castro #define CSI_FIFO_SIZE_BYTES	32
6683c624d8SFabrizio Castro #define CSI_FIFO_HALF_SIZE	16
6783c624d8SFabrizio Castro #define CSI_EN_DIS_TIMEOUT_US	100
68aecf9fbdSFabrizio Castro /*
69aecf9fbdSFabrizio Castro  * Clock "csiclk" gets divided by 2 * CSI_CLKSEL_CKS in order to generate the
70aecf9fbdSFabrizio Castro  * serial clock (output from master), with CSI_CLKSEL_CKS ranging from 0x1 (that
71aecf9fbdSFabrizio Castro  * means "csiclk" is divided by 2) to 0x3FFF ("csiclk" is divided by 32766).
72aecf9fbdSFabrizio Castro  */
73aecf9fbdSFabrizio Castro #define CSI_CKS_MAX		GENMASK(13, 0)
7483c624d8SFabrizio Castro 
7583c624d8SFabrizio Castro #define UNDERRUN_ERROR		BIT(0)
7683c624d8SFabrizio Castro #define OVERFLOW_ERROR		BIT(1)
7783c624d8SFabrizio Castro #define TX_TIMEOUT_ERROR	BIT(2)
7883c624d8SFabrizio Castro #define RX_TIMEOUT_ERROR	BIT(3)
7983c624d8SFabrizio Castro 
8074e27ce8SFabrizio Castro #define CSI_MAX_SPI_SCKO	(8 * HZ_PER_MHZ)
8183c624d8SFabrizio Castro 
8283c624d8SFabrizio Castro struct rzv2m_csi_priv {
8383c624d8SFabrizio Castro 	void __iomem *base;
8483c624d8SFabrizio Castro 	struct clk *csiclk;
8583c624d8SFabrizio Castro 	struct clk *pclk;
8683c624d8SFabrizio Castro 	struct device *dev;
8783c624d8SFabrizio Castro 	struct spi_controller *controller;
8883c624d8SFabrizio Castro 	const u8 *txbuf;
8983c624d8SFabrizio Castro 	u8 *rxbuf;
9083c624d8SFabrizio Castro 	int buffer_len;
9183c624d8SFabrizio Castro 	int bytes_sent;
9283c624d8SFabrizio Castro 	int bytes_received;
9383c624d8SFabrizio Castro 	int bytes_to_transfer;
9483c624d8SFabrizio Castro 	int words_to_transfer;
9583c624d8SFabrizio Castro 	unsigned char bytes_per_word;
9683c624d8SFabrizio Castro 	wait_queue_head_t wait;
9783c624d8SFabrizio Castro 	u8 errors;
9883c624d8SFabrizio Castro 	u32 status;
9983c624d8SFabrizio Castro };
10083c624d8SFabrizio Castro 
10183c624d8SFabrizio Castro static const unsigned char x_trg[] = {
10283c624d8SFabrizio Castro 	0, 1, 1, 2, 2, 2, 2, 3,
10383c624d8SFabrizio Castro 	3, 3, 3, 3, 3, 3, 3, 4,
10483c624d8SFabrizio Castro 	4, 4, 4, 4, 4, 4, 4, 4,
10583c624d8SFabrizio Castro 	4, 4, 4, 4, 4, 4, 4, 5
10683c624d8SFabrizio Castro };
10783c624d8SFabrizio Castro 
10883c624d8SFabrizio Castro static const unsigned char x_trg_words[] = {
10983c624d8SFabrizio Castro 	1,  2,  2,  4,  4,  4,  4,  8,
11083c624d8SFabrizio Castro 	8,  8,  8,  8,  8,  8,  8,  16,
11183c624d8SFabrizio Castro 	16, 16, 16, 16, 16, 16, 16, 16,
11283c624d8SFabrizio Castro 	16, 16, 16, 16, 16, 16, 16, 32
11383c624d8SFabrizio Castro };
11483c624d8SFabrizio Castro 
11583c624d8SFabrizio Castro static void rzv2m_csi_reg_write_bit(const struct rzv2m_csi_priv *csi,
11683c624d8SFabrizio Castro 				    int reg_offs, int bit_mask, u32 value)
11783c624d8SFabrizio Castro {
11883c624d8SFabrizio Castro 	int nr_zeros;
11983c624d8SFabrizio Castro 	u32 tmp;
12083c624d8SFabrizio Castro 
12183c624d8SFabrizio Castro 	nr_zeros = count_trailing_zeros(bit_mask);
12283c624d8SFabrizio Castro 	value <<= nr_zeros;
12383c624d8SFabrizio Castro 
12483c624d8SFabrizio Castro 	tmp = (readl(csi->base + reg_offs) & ~bit_mask) | value;
12583c624d8SFabrizio Castro 	writel(tmp, csi->base + reg_offs);
12683c624d8SFabrizio Castro }
12783c624d8SFabrizio Castro 
12883c624d8SFabrizio Castro static int rzv2m_csi_sw_reset(struct rzv2m_csi_priv *csi, int assert)
12983c624d8SFabrizio Castro {
13083c624d8SFabrizio Castro 	u32 reg;
13183c624d8SFabrizio Castro 
13283c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_CNT, CSI_CNT_CSIRST, assert);
13383c624d8SFabrizio Castro 
134*2ed2699fSFabrizio Castro 	if (!assert)
135*2ed2699fSFabrizio Castro 		return 0;
136*2ed2699fSFabrizio Castro 
13783c624d8SFabrizio Castro 	return readl_poll_timeout(csi->base + CSI_MODE, reg,
13883c624d8SFabrizio Castro 				  !(reg & CSI_MODE_CSOT), 0,
13983c624d8SFabrizio Castro 				  CSI_EN_DIS_TIMEOUT_US);
14083c624d8SFabrizio Castro }
14183c624d8SFabrizio Castro 
14283c624d8SFabrizio Castro static int rzv2m_csi_start_stop_operation(const struct rzv2m_csi_priv *csi,
14383c624d8SFabrizio Castro 					  int enable, bool wait)
14483c624d8SFabrizio Castro {
14583c624d8SFabrizio Castro 	u32 reg;
14683c624d8SFabrizio Castro 
14783c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_CSIE, enable);
14883c624d8SFabrizio Castro 
149*2ed2699fSFabrizio Castro 	if (enable || !wait)
150*2ed2699fSFabrizio Castro 		return 0;
151*2ed2699fSFabrizio Castro 
15283c624d8SFabrizio Castro 	return readl_poll_timeout(csi->base + CSI_MODE, reg,
15383c624d8SFabrizio Castro 				  !(reg & CSI_MODE_CSOT), 0,
15483c624d8SFabrizio Castro 				  CSI_EN_DIS_TIMEOUT_US);
15583c624d8SFabrizio Castro }
15683c624d8SFabrizio Castro 
15783c624d8SFabrizio Castro static int rzv2m_csi_fill_txfifo(struct rzv2m_csi_priv *csi)
15883c624d8SFabrizio Castro {
15983c624d8SFabrizio Castro 	int i;
16083c624d8SFabrizio Castro 
16183c624d8SFabrizio Castro 	if (readl(csi->base + CSI_OFIFOL))
16283c624d8SFabrizio Castro 		return -EIO;
16383c624d8SFabrizio Castro 
16483c624d8SFabrizio Castro 	if (csi->bytes_per_word == 2) {
16583c624d8SFabrizio Castro 		u16 *buf = (u16 *)csi->txbuf;
16683c624d8SFabrizio Castro 
16783c624d8SFabrizio Castro 		for (i = 0; i < csi->words_to_transfer; i++)
16883c624d8SFabrizio Castro 			writel(buf[i], csi->base + CSI_OFIFO);
16983c624d8SFabrizio Castro 	} else {
17083c624d8SFabrizio Castro 		u8 *buf = (u8 *)csi->txbuf;
17183c624d8SFabrizio Castro 
17283c624d8SFabrizio Castro 		for (i = 0; i < csi->words_to_transfer; i++)
17383c624d8SFabrizio Castro 			writel(buf[i], csi->base + CSI_OFIFO);
17483c624d8SFabrizio Castro 	}
17583c624d8SFabrizio Castro 
17683c624d8SFabrizio Castro 	csi->txbuf += csi->bytes_to_transfer;
17783c624d8SFabrizio Castro 	csi->bytes_sent += csi->bytes_to_transfer;
17883c624d8SFabrizio Castro 
17983c624d8SFabrizio Castro 	return 0;
18083c624d8SFabrizio Castro }
18183c624d8SFabrizio Castro 
18283c624d8SFabrizio Castro static int rzv2m_csi_read_rxfifo(struct rzv2m_csi_priv *csi)
18383c624d8SFabrizio Castro {
18483c624d8SFabrizio Castro 	int i;
18583c624d8SFabrizio Castro 
18683c624d8SFabrizio Castro 	if (readl(csi->base + CSI_IFIFOL) != csi->bytes_to_transfer)
18783c624d8SFabrizio Castro 		return -EIO;
18883c624d8SFabrizio Castro 
18983c624d8SFabrizio Castro 	if (csi->bytes_per_word == 2) {
19083c624d8SFabrizio Castro 		u16 *buf = (u16 *)csi->rxbuf;
19183c624d8SFabrizio Castro 
19283c624d8SFabrizio Castro 		for (i = 0; i < csi->words_to_transfer; i++)
19383c624d8SFabrizio Castro 			buf[i] = (u16)readl(csi->base + CSI_IFIFO);
19483c624d8SFabrizio Castro 	} else {
19583c624d8SFabrizio Castro 		u8 *buf = (u8 *)csi->rxbuf;
19683c624d8SFabrizio Castro 
19783c624d8SFabrizio Castro 		for (i = 0; i < csi->words_to_transfer; i++)
19883c624d8SFabrizio Castro 			buf[i] = (u8)readl(csi->base + CSI_IFIFO);
19983c624d8SFabrizio Castro 	}
20083c624d8SFabrizio Castro 
20183c624d8SFabrizio Castro 	csi->rxbuf += csi->bytes_to_transfer;
20283c624d8SFabrizio Castro 	csi->bytes_received += csi->bytes_to_transfer;
20383c624d8SFabrizio Castro 
20483c624d8SFabrizio Castro 	return 0;
20583c624d8SFabrizio Castro }
20683c624d8SFabrizio Castro 
20783c624d8SFabrizio Castro static inline void rzv2m_csi_calc_current_transfer(struct rzv2m_csi_priv *csi)
20883c624d8SFabrizio Castro {
20983c624d8SFabrizio Castro 	int bytes_transferred = max_t(int, csi->bytes_received, csi->bytes_sent);
21083c624d8SFabrizio Castro 	int bytes_remaining = csi->buffer_len - bytes_transferred;
21183c624d8SFabrizio Castro 	int to_transfer;
21283c624d8SFabrizio Castro 
21383c624d8SFabrizio Castro 	if (csi->txbuf)
21483c624d8SFabrizio Castro 		/*
21583c624d8SFabrizio Castro 		 * Leaving a little bit of headroom in the FIFOs makes it very
21683c624d8SFabrizio Castro 		 * hard to raise an overflow error (which is only possible
21783c624d8SFabrizio Castro 		 * when IP transmits and receives at the same time).
21883c624d8SFabrizio Castro 		 */
21983c624d8SFabrizio Castro 		to_transfer = min_t(int, CSI_FIFO_HALF_SIZE, bytes_remaining);
22083c624d8SFabrizio Castro 	else
22183c624d8SFabrizio Castro 		to_transfer = min_t(int, CSI_FIFO_SIZE_BYTES, bytes_remaining);
22283c624d8SFabrizio Castro 
22383c624d8SFabrizio Castro 	if (csi->bytes_per_word == 2)
22483c624d8SFabrizio Castro 		to_transfer >>= 1;
22583c624d8SFabrizio Castro 
22683c624d8SFabrizio Castro 	/*
22783c624d8SFabrizio Castro 	 * We can only choose a trigger level from a predefined set of values.
22883c624d8SFabrizio Castro 	 * This will pick a value that is the greatest possible integer that's
22983c624d8SFabrizio Castro 	 * less than or equal to the number of bytes we need to transfer.
23083c624d8SFabrizio Castro 	 * This may result in multiple smaller transfers.
23183c624d8SFabrizio Castro 	 */
23283c624d8SFabrizio Castro 	csi->words_to_transfer = x_trg_words[to_transfer - 1];
23383c624d8SFabrizio Castro 
23483c624d8SFabrizio Castro 	if (csi->bytes_per_word == 2)
23583c624d8SFabrizio Castro 		csi->bytes_to_transfer = csi->words_to_transfer << 1;
23683c624d8SFabrizio Castro 	else
23783c624d8SFabrizio Castro 		csi->bytes_to_transfer = csi->words_to_transfer;
23883c624d8SFabrizio Castro }
23983c624d8SFabrizio Castro 
24083c624d8SFabrizio Castro static inline void rzv2m_csi_set_rx_fifo_trigger_level(struct rzv2m_csi_priv *csi)
24183c624d8SFabrizio Castro {
24283c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_FIFOTRG, CSI_FIFOTRG_R_TRG,
24383c624d8SFabrizio Castro 				x_trg[csi->words_to_transfer - 1]);
24483c624d8SFabrizio Castro }
24583c624d8SFabrizio Castro 
24683c624d8SFabrizio Castro static inline void rzv2m_csi_enable_rx_trigger(struct rzv2m_csi_priv *csi,
24783c624d8SFabrizio Castro 					       bool enable)
24883c624d8SFabrizio Castro {
24983c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_CNT, CSI_CNT_R_TRGEN, enable);
25083c624d8SFabrizio Castro }
25183c624d8SFabrizio Castro 
25283c624d8SFabrizio Castro static void rzv2m_csi_disable_irqs(const struct rzv2m_csi_priv *csi,
25383c624d8SFabrizio Castro 				   u32 enable_bits)
25483c624d8SFabrizio Castro {
25583c624d8SFabrizio Castro 	u32 cnt = readl(csi->base + CSI_CNT);
25683c624d8SFabrizio Castro 
25783c624d8SFabrizio Castro 	writel(cnt & ~enable_bits, csi->base + CSI_CNT);
25883c624d8SFabrizio Castro }
25983c624d8SFabrizio Castro 
26083c624d8SFabrizio Castro static void rzv2m_csi_disable_all_irqs(struct rzv2m_csi_priv *csi)
26183c624d8SFabrizio Castro {
26283c624d8SFabrizio Castro 	rzv2m_csi_disable_irqs(csi, CSI_CNT_R_TRGR_E | CSI_CNT_T_TRGR_E |
26383c624d8SFabrizio Castro 			       CSI_CNT_CSIEND_E | CSI_CNT_TREND_E |
26483c624d8SFabrizio Castro 			       CSI_CNT_OVERF_E | CSI_CNT_UNDER_E);
26583c624d8SFabrizio Castro }
26683c624d8SFabrizio Castro 
26783c624d8SFabrizio Castro static inline void rzv2m_csi_clear_irqs(struct rzv2m_csi_priv *csi, u32 irqs)
26883c624d8SFabrizio Castro {
26983c624d8SFabrizio Castro 	writel(irqs, csi->base + CSI_INT);
27083c624d8SFabrizio Castro }
27183c624d8SFabrizio Castro 
27283c624d8SFabrizio Castro static void rzv2m_csi_clear_all_irqs(struct rzv2m_csi_priv *csi)
27383c624d8SFabrizio Castro {
27483c624d8SFabrizio Castro 	rzv2m_csi_clear_irqs(csi, CSI_INT_UNDER | CSI_INT_OVERF |
27583c624d8SFabrizio Castro 			     CSI_INT_TREND | CSI_INT_CSIEND |  CSI_INT_T_TRGR |
27683c624d8SFabrizio Castro 			     CSI_INT_R_TRGR);
27783c624d8SFabrizio Castro }
27883c624d8SFabrizio Castro 
27983c624d8SFabrizio Castro static void rzv2m_csi_enable_irqs(struct rzv2m_csi_priv *csi, u32 enable_bits)
28083c624d8SFabrizio Castro {
28183c624d8SFabrizio Castro 	u32 cnt = readl(csi->base + CSI_CNT);
28283c624d8SFabrizio Castro 
28383c624d8SFabrizio Castro 	writel(cnt | enable_bits, csi->base + CSI_CNT);
28483c624d8SFabrizio Castro }
28583c624d8SFabrizio Castro 
28683c624d8SFabrizio Castro static int rzv2m_csi_wait_for_interrupt(struct rzv2m_csi_priv *csi,
28783c624d8SFabrizio Castro 					u32 wait_mask, u32 enable_bits)
28883c624d8SFabrizio Castro {
28983c624d8SFabrizio Castro 	int ret;
29083c624d8SFabrizio Castro 
29183c624d8SFabrizio Castro 	rzv2m_csi_enable_irqs(csi, enable_bits);
29283c624d8SFabrizio Castro 
29383c624d8SFabrizio Castro 	ret = wait_event_timeout(csi->wait,
29483c624d8SFabrizio Castro 				 ((csi->status & wait_mask) == wait_mask) ||
29583c624d8SFabrizio Castro 				 csi->errors, HZ);
29683c624d8SFabrizio Castro 
29783c624d8SFabrizio Castro 	rzv2m_csi_disable_irqs(csi, enable_bits);
29883c624d8SFabrizio Castro 
29983c624d8SFabrizio Castro 	if (csi->errors)
30083c624d8SFabrizio Castro 		return -EIO;
30183c624d8SFabrizio Castro 
30283c624d8SFabrizio Castro 	if (!ret)
30383c624d8SFabrizio Castro 		return -ETIMEDOUT;
30483c624d8SFabrizio Castro 
30583c624d8SFabrizio Castro 	return 0;
30683c624d8SFabrizio Castro }
30783c624d8SFabrizio Castro 
30883c624d8SFabrizio Castro static int rzv2m_csi_wait_for_tx_empty(struct rzv2m_csi_priv *csi)
30983c624d8SFabrizio Castro {
31083c624d8SFabrizio Castro 	int ret;
31183c624d8SFabrizio Castro 
31283c624d8SFabrizio Castro 	if (readl(csi->base + CSI_OFIFOL) == 0)
31383c624d8SFabrizio Castro 		return 0;
31483c624d8SFabrizio Castro 
31583c624d8SFabrizio Castro 	ret = rzv2m_csi_wait_for_interrupt(csi, CSI_INT_TREND, CSI_CNT_TREND_E);
31683c624d8SFabrizio Castro 
31783c624d8SFabrizio Castro 	if (ret == -ETIMEDOUT)
31883c624d8SFabrizio Castro 		csi->errors |= TX_TIMEOUT_ERROR;
31983c624d8SFabrizio Castro 
32083c624d8SFabrizio Castro 	return ret;
32183c624d8SFabrizio Castro }
32283c624d8SFabrizio Castro 
32383c624d8SFabrizio Castro static inline int rzv2m_csi_wait_for_rx_ready(struct rzv2m_csi_priv *csi)
32483c624d8SFabrizio Castro {
32583c624d8SFabrizio Castro 	int ret;
32683c624d8SFabrizio Castro 
32783c624d8SFabrizio Castro 	if (readl(csi->base + CSI_IFIFOL) == csi->bytes_to_transfer)
32883c624d8SFabrizio Castro 		return 0;
32983c624d8SFabrizio Castro 
33083c624d8SFabrizio Castro 	ret = rzv2m_csi_wait_for_interrupt(csi, CSI_INT_R_TRGR,
33183c624d8SFabrizio Castro 					   CSI_CNT_R_TRGR_E);
33283c624d8SFabrizio Castro 
33383c624d8SFabrizio Castro 	if (ret == -ETIMEDOUT)
33483c624d8SFabrizio Castro 		csi->errors |= RX_TIMEOUT_ERROR;
33583c624d8SFabrizio Castro 
33683c624d8SFabrizio Castro 	return ret;
33783c624d8SFabrizio Castro }
33883c624d8SFabrizio Castro 
33983c624d8SFabrizio Castro static irqreturn_t rzv2m_csi_irq_handler(int irq, void *data)
34083c624d8SFabrizio Castro {
34183c624d8SFabrizio Castro 	struct rzv2m_csi_priv *csi = (struct rzv2m_csi_priv *)data;
34283c624d8SFabrizio Castro 
34383c624d8SFabrizio Castro 	csi->status = readl(csi->base + CSI_INT);
34483c624d8SFabrizio Castro 	rzv2m_csi_disable_irqs(csi, csi->status);
34583c624d8SFabrizio Castro 
34683c624d8SFabrizio Castro 	if (csi->status & CSI_INT_OVERF)
34783c624d8SFabrizio Castro 		csi->errors |= OVERFLOW_ERROR;
34883c624d8SFabrizio Castro 	if (csi->status & CSI_INT_UNDER)
34983c624d8SFabrizio Castro 		csi->errors |= UNDERRUN_ERROR;
35083c624d8SFabrizio Castro 
35183c624d8SFabrizio Castro 	wake_up(&csi->wait);
35283c624d8SFabrizio Castro 
35383c624d8SFabrizio Castro 	return IRQ_HANDLED;
35483c624d8SFabrizio Castro }
35583c624d8SFabrizio Castro 
35683c624d8SFabrizio Castro static void rzv2m_csi_setup_clock(struct rzv2m_csi_priv *csi, u32 spi_hz)
35783c624d8SFabrizio Castro {
35883c624d8SFabrizio Castro 	unsigned long csiclk_rate = clk_get_rate(csi->csiclk);
35983c624d8SFabrizio Castro 	unsigned long pclk_rate = clk_get_rate(csi->pclk);
36083c624d8SFabrizio Castro 	unsigned long csiclk_rate_limit = pclk_rate >> 1;
36183c624d8SFabrizio Castro 	u32 cks;
36283c624d8SFabrizio Castro 
36383c624d8SFabrizio Castro 	/*
36483c624d8SFabrizio Castro 	 * There is a restriction on the frequency of CSICLK, it has to be <=
36583c624d8SFabrizio Castro 	 * PCLK / 2.
36683c624d8SFabrizio Castro 	 */
36783c624d8SFabrizio Castro 	if (csiclk_rate > csiclk_rate_limit) {
36883c624d8SFabrizio Castro 		clk_set_rate(csi->csiclk, csiclk_rate >> 1);
36983c624d8SFabrizio Castro 		csiclk_rate = clk_get_rate(csi->csiclk);
37083c624d8SFabrizio Castro 	} else if ((csiclk_rate << 1) <= csiclk_rate_limit) {
37183c624d8SFabrizio Castro 		clk_set_rate(csi->csiclk, csiclk_rate << 1);
37283c624d8SFabrizio Castro 		csiclk_rate = clk_get_rate(csi->csiclk);
37383c624d8SFabrizio Castro 	}
37483c624d8SFabrizio Castro 
37583c624d8SFabrizio Castro 	spi_hz = spi_hz > CSI_MAX_SPI_SCKO ? CSI_MAX_SPI_SCKO : spi_hz;
37683c624d8SFabrizio Castro 
37783c624d8SFabrizio Castro 	cks = DIV_ROUND_UP(csiclk_rate, spi_hz << 1);
37883c624d8SFabrizio Castro 	if (cks > CSI_CKS_MAX)
37983c624d8SFabrizio Castro 		cks = CSI_CKS_MAX;
38083c624d8SFabrizio Castro 
38183c624d8SFabrizio Castro 	dev_dbg(csi->dev, "SPI clk rate is %ldHz\n", csiclk_rate / (cks << 1));
38283c624d8SFabrizio Castro 
38383c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_CKS, cks);
38483c624d8SFabrizio Castro }
38583c624d8SFabrizio Castro 
38683c624d8SFabrizio Castro static void rzv2m_csi_setup_operating_mode(struct rzv2m_csi_priv *csi,
38783c624d8SFabrizio Castro 					   struct spi_transfer *t)
38883c624d8SFabrizio Castro {
38983c624d8SFabrizio Castro 	if (t->rx_buf && !t->tx_buf)
39083c624d8SFabrizio Castro 		/* Reception-only mode */
39183c624d8SFabrizio Castro 		rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_TRMD, 0);
39283c624d8SFabrizio Castro 	else
39383c624d8SFabrizio Castro 		/* Send and receive mode */
39483c624d8SFabrizio Castro 		rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_TRMD, 1);
39583c624d8SFabrizio Castro 
39683c624d8SFabrizio Castro 	csi->bytes_per_word = t->bits_per_word / 8;
39783c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_CCL,
39883c624d8SFabrizio Castro 				csi->bytes_per_word == 2);
39983c624d8SFabrizio Castro }
40083c624d8SFabrizio Castro 
40183c624d8SFabrizio Castro static int rzv2m_csi_setup(struct spi_device *spi)
40283c624d8SFabrizio Castro {
40383c624d8SFabrizio Castro 	struct rzv2m_csi_priv *csi = spi_controller_get_devdata(spi->controller);
40483c624d8SFabrizio Castro 	int ret;
40583c624d8SFabrizio Castro 
40683c624d8SFabrizio Castro 	rzv2m_csi_sw_reset(csi, 0);
40783c624d8SFabrizio Castro 
40883c624d8SFabrizio Castro 	writel(CSI_MODE_SETUP, csi->base + CSI_MODE);
40983c624d8SFabrizio Castro 
41083c624d8SFabrizio Castro 	/* Setup clock polarity and phase timing */
41183c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_CKP,
41283c624d8SFabrizio Castro 				!(spi->mode & SPI_CPOL));
41383c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_DAP,
41483c624d8SFabrizio Castro 				!(spi->mode & SPI_CPHA));
41583c624d8SFabrizio Castro 
41683c624d8SFabrizio Castro 	/* Setup serial data order */
41783c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_DIR,
41883c624d8SFabrizio Castro 				!!(spi->mode & SPI_LSB_FIRST));
41983c624d8SFabrizio Castro 
42083c624d8SFabrizio Castro 	/* Set the operation mode as master */
42183c624d8SFabrizio Castro 	rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_SLAVE, 0);
42283c624d8SFabrizio Castro 
42383c624d8SFabrizio Castro 	/* Give the IP a SW reset */
42483c624d8SFabrizio Castro 	ret = rzv2m_csi_sw_reset(csi, 1);
42583c624d8SFabrizio Castro 	if (ret)
42683c624d8SFabrizio Castro 		return ret;
42783c624d8SFabrizio Castro 	rzv2m_csi_sw_reset(csi, 0);
42883c624d8SFabrizio Castro 
42983c624d8SFabrizio Castro 	/*
43083c624d8SFabrizio Castro 	 * We need to enable the communication so that the clock will settle
43183c624d8SFabrizio Castro 	 * for the right polarity before enabling the CS.
43283c624d8SFabrizio Castro 	 */
43383c624d8SFabrizio Castro 	rzv2m_csi_start_stop_operation(csi, 1, false);
43483c624d8SFabrizio Castro 	udelay(10);
43583c624d8SFabrizio Castro 	rzv2m_csi_start_stop_operation(csi, 0, false);
43683c624d8SFabrizio Castro 
43783c624d8SFabrizio Castro 	return 0;
43883c624d8SFabrizio Castro }
43983c624d8SFabrizio Castro 
44083c624d8SFabrizio Castro static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi)
44183c624d8SFabrizio Castro {
44283c624d8SFabrizio Castro 	bool tx_completed = csi->txbuf ? false : true;
44383c624d8SFabrizio Castro 	bool rx_completed = csi->rxbuf ? false : true;
44483c624d8SFabrizio Castro 	int ret = 0;
44583c624d8SFabrizio Castro 
44683c624d8SFabrizio Castro 	/* Make sure the TX FIFO is empty */
44783c624d8SFabrizio Castro 	writel(0, csi->base + CSI_OFIFOL);
44883c624d8SFabrizio Castro 
44983c624d8SFabrizio Castro 	csi->bytes_sent = 0;
45083c624d8SFabrizio Castro 	csi->bytes_received = 0;
45183c624d8SFabrizio Castro 	csi->errors = 0;
45283c624d8SFabrizio Castro 
45383c624d8SFabrizio Castro 	rzv2m_csi_disable_all_irqs(csi);
45483c624d8SFabrizio Castro 	rzv2m_csi_clear_all_irqs(csi);
45583c624d8SFabrizio Castro 	rzv2m_csi_enable_rx_trigger(csi, true);
45683c624d8SFabrizio Castro 
45783c624d8SFabrizio Castro 	while (!tx_completed || !rx_completed) {
45883c624d8SFabrizio Castro 		/*
45983c624d8SFabrizio Castro 		 * Decide how many words we are going to transfer during
46083c624d8SFabrizio Castro 		 * this cycle (for both TX and RX), then set the RX FIFO trigger
46183c624d8SFabrizio Castro 		 * level accordingly. No need to set a trigger level for the
46283c624d8SFabrizio Castro 		 * TX FIFO, as this IP comes with an interrupt that fires when
46383c624d8SFabrizio Castro 		 * the TX FIFO is empty.
46483c624d8SFabrizio Castro 		 */
46583c624d8SFabrizio Castro 		rzv2m_csi_calc_current_transfer(csi);
46683c624d8SFabrizio Castro 		rzv2m_csi_set_rx_fifo_trigger_level(csi);
46783c624d8SFabrizio Castro 
46883c624d8SFabrizio Castro 		rzv2m_csi_enable_irqs(csi, CSI_INT_OVERF | CSI_INT_UNDER);
46983c624d8SFabrizio Castro 
47083c624d8SFabrizio Castro 		/* Make sure the RX FIFO is empty */
47183c624d8SFabrizio Castro 		writel(0, csi->base + CSI_IFIFOL);
47283c624d8SFabrizio Castro 
47383c624d8SFabrizio Castro 		writel(readl(csi->base + CSI_INT), csi->base + CSI_INT);
47483c624d8SFabrizio Castro 		csi->status = 0;
47583c624d8SFabrizio Castro 
47683c624d8SFabrizio Castro 		rzv2m_csi_start_stop_operation(csi, 1, false);
47783c624d8SFabrizio Castro 
47883c624d8SFabrizio Castro 		/* TX */
47983c624d8SFabrizio Castro 		if (csi->txbuf) {
48083c624d8SFabrizio Castro 			ret = rzv2m_csi_fill_txfifo(csi);
48183c624d8SFabrizio Castro 			if (ret)
48283c624d8SFabrizio Castro 				break;
48383c624d8SFabrizio Castro 
48483c624d8SFabrizio Castro 			ret = rzv2m_csi_wait_for_tx_empty(csi);
48583c624d8SFabrizio Castro 			if (ret)
48683c624d8SFabrizio Castro 				break;
48783c624d8SFabrizio Castro 
48883c624d8SFabrizio Castro 			if (csi->bytes_sent == csi->buffer_len)
48983c624d8SFabrizio Castro 				tx_completed = true;
49083c624d8SFabrizio Castro 		}
49183c624d8SFabrizio Castro 
49283c624d8SFabrizio Castro 		/*
49383c624d8SFabrizio Castro 		 * Make sure the RX FIFO contains the desired number of words.
49483c624d8SFabrizio Castro 		 * We then either flush its content, or we copy it onto
49583c624d8SFabrizio Castro 		 * csi->rxbuf.
49683c624d8SFabrizio Castro 		 */
49783c624d8SFabrizio Castro 		ret = rzv2m_csi_wait_for_rx_ready(csi);
49883c624d8SFabrizio Castro 		if (ret)
49983c624d8SFabrizio Castro 			break;
50083c624d8SFabrizio Castro 
50183c624d8SFabrizio Castro 		/* RX */
50283c624d8SFabrizio Castro 		if (csi->rxbuf) {
50383c624d8SFabrizio Castro 			rzv2m_csi_start_stop_operation(csi, 0, false);
50483c624d8SFabrizio Castro 
50583c624d8SFabrizio Castro 			ret = rzv2m_csi_read_rxfifo(csi);
50683c624d8SFabrizio Castro 			if (ret)
50783c624d8SFabrizio Castro 				break;
50883c624d8SFabrizio Castro 
50983c624d8SFabrizio Castro 			if (csi->bytes_received == csi->buffer_len)
51083c624d8SFabrizio Castro 				rx_completed = true;
51183c624d8SFabrizio Castro 		}
51283c624d8SFabrizio Castro 
51383c624d8SFabrizio Castro 		ret = rzv2m_csi_start_stop_operation(csi, 0, true);
51483c624d8SFabrizio Castro 		if (ret)
51583c624d8SFabrizio Castro 			goto pio_quit;
51683c624d8SFabrizio Castro 
51783c624d8SFabrizio Castro 		if (csi->errors) {
51883c624d8SFabrizio Castro 			ret = -EIO;
51983c624d8SFabrizio Castro 			goto pio_quit;
52083c624d8SFabrizio Castro 		}
52183c624d8SFabrizio Castro 	}
52283c624d8SFabrizio Castro 
52383c624d8SFabrizio Castro 	rzv2m_csi_start_stop_operation(csi, 0, true);
52483c624d8SFabrizio Castro 
52583c624d8SFabrizio Castro pio_quit:
52683c624d8SFabrizio Castro 	rzv2m_csi_disable_all_irqs(csi);
52783c624d8SFabrizio Castro 	rzv2m_csi_enable_rx_trigger(csi, false);
52883c624d8SFabrizio Castro 	rzv2m_csi_clear_all_irqs(csi);
52983c624d8SFabrizio Castro 
53083c624d8SFabrizio Castro 	return ret;
53183c624d8SFabrizio Castro }
53283c624d8SFabrizio Castro 
53383c624d8SFabrizio Castro static int rzv2m_csi_transfer_one(struct spi_controller *controller,
53483c624d8SFabrizio Castro 				  struct spi_device *spi,
53583c624d8SFabrizio Castro 				  struct spi_transfer *transfer)
53683c624d8SFabrizio Castro {
53783c624d8SFabrizio Castro 	struct rzv2m_csi_priv *csi = spi_controller_get_devdata(controller);
53883c624d8SFabrizio Castro 	struct device *dev = csi->dev;
53983c624d8SFabrizio Castro 	int ret;
54083c624d8SFabrizio Castro 
54183c624d8SFabrizio Castro 	csi->txbuf = transfer->tx_buf;
54283c624d8SFabrizio Castro 	csi->rxbuf = transfer->rx_buf;
54383c624d8SFabrizio Castro 	csi->buffer_len = transfer->len;
54483c624d8SFabrizio Castro 
54583c624d8SFabrizio Castro 	rzv2m_csi_setup_operating_mode(csi, transfer);
54683c624d8SFabrizio Castro 
54783c624d8SFabrizio Castro 	rzv2m_csi_setup_clock(csi, transfer->speed_hz);
54883c624d8SFabrizio Castro 
54983c624d8SFabrizio Castro 	ret = rzv2m_csi_pio_transfer(csi);
55083c624d8SFabrizio Castro 	if (ret) {
55183c624d8SFabrizio Castro 		if (csi->errors & UNDERRUN_ERROR)
55283c624d8SFabrizio Castro 			dev_err(dev, "Underrun error\n");
55383c624d8SFabrizio Castro 		if (csi->errors & OVERFLOW_ERROR)
55483c624d8SFabrizio Castro 			dev_err(dev, "Overflow error\n");
55583c624d8SFabrizio Castro 		if (csi->errors & TX_TIMEOUT_ERROR)
55683c624d8SFabrizio Castro 			dev_err(dev, "TX timeout error\n");
55783c624d8SFabrizio Castro 		if (csi->errors & RX_TIMEOUT_ERROR)
55883c624d8SFabrizio Castro 			dev_err(dev, "RX timeout error\n");
55983c624d8SFabrizio Castro 	}
56083c624d8SFabrizio Castro 
56183c624d8SFabrizio Castro 	return ret;
56283c624d8SFabrizio Castro }
56383c624d8SFabrizio Castro 
56483c624d8SFabrizio Castro static int rzv2m_csi_probe(struct platform_device *pdev)
56583c624d8SFabrizio Castro {
56683c624d8SFabrizio Castro 	struct spi_controller *controller;
56783c624d8SFabrizio Castro 	struct device *dev = &pdev->dev;
56883c624d8SFabrizio Castro 	struct rzv2m_csi_priv *csi;
56983c624d8SFabrizio Castro 	struct reset_control *rstc;
57083c624d8SFabrizio Castro 	int irq;
57183c624d8SFabrizio Castro 	int ret;
57283c624d8SFabrizio Castro 
57383c624d8SFabrizio Castro 	controller = devm_spi_alloc_master(dev, sizeof(*csi));
57483c624d8SFabrizio Castro 	if (!controller)
57583c624d8SFabrizio Castro 		return -ENOMEM;
57683c624d8SFabrizio Castro 
57783c624d8SFabrizio Castro 	csi = spi_controller_get_devdata(controller);
57883c624d8SFabrizio Castro 	platform_set_drvdata(pdev, csi);
57983c624d8SFabrizio Castro 
58083c624d8SFabrizio Castro 	csi->dev = dev;
58183c624d8SFabrizio Castro 	csi->controller = controller;
58283c624d8SFabrizio Castro 
58383c624d8SFabrizio Castro 	csi->base = devm_platform_ioremap_resource(pdev, 0);
58483c624d8SFabrizio Castro 	if (IS_ERR(csi->base))
58583c624d8SFabrizio Castro 		return PTR_ERR(csi->base);
58683c624d8SFabrizio Castro 
58783c624d8SFabrizio Castro 	irq = platform_get_irq(pdev, 0);
58883c624d8SFabrizio Castro 	if (irq < 0)
58983c624d8SFabrizio Castro 		return irq;
59083c624d8SFabrizio Castro 
59183c624d8SFabrizio Castro 	csi->csiclk = devm_clk_get(dev, "csiclk");
59283c624d8SFabrizio Castro 	if (IS_ERR(csi->csiclk))
59383c624d8SFabrizio Castro 		return dev_err_probe(dev, PTR_ERR(csi->csiclk),
59483c624d8SFabrizio Castro 				     "could not get csiclk\n");
59583c624d8SFabrizio Castro 
59683c624d8SFabrizio Castro 	csi->pclk = devm_clk_get(dev, "pclk");
59783c624d8SFabrizio Castro 	if (IS_ERR(csi->pclk))
59883c624d8SFabrizio Castro 		return dev_err_probe(dev, PTR_ERR(csi->pclk),
59983c624d8SFabrizio Castro 				     "could not get pclk\n");
60083c624d8SFabrizio Castro 
60183c624d8SFabrizio Castro 	rstc = devm_reset_control_get_shared(dev, NULL);
60283c624d8SFabrizio Castro 	if (IS_ERR(rstc))
60383c624d8SFabrizio Castro 		return dev_err_probe(dev, PTR_ERR(rstc), "Missing reset ctrl\n");
60483c624d8SFabrizio Castro 
60583c624d8SFabrizio Castro 	init_waitqueue_head(&csi->wait);
60683c624d8SFabrizio Castro 
60783c624d8SFabrizio Castro 	controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
60883c624d8SFabrizio Castro 	controller->dev.of_node = pdev->dev.of_node;
60983c624d8SFabrizio Castro 	controller->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);
61083c624d8SFabrizio Castro 	controller->setup = rzv2m_csi_setup;
61183c624d8SFabrizio Castro 	controller->transfer_one = rzv2m_csi_transfer_one;
61283c624d8SFabrizio Castro 	controller->use_gpio_descriptors = true;
61383c624d8SFabrizio Castro 
61483c624d8SFabrizio Castro 	ret = devm_request_irq(dev, irq, rzv2m_csi_irq_handler, 0,
61583c624d8SFabrizio Castro 			       dev_name(dev), csi);
61683c624d8SFabrizio Castro 	if (ret)
61783c624d8SFabrizio Castro 		return dev_err_probe(dev, ret, "cannot request IRQ\n");
61883c624d8SFabrizio Castro 
61983c624d8SFabrizio Castro 	/*
62083c624d8SFabrizio Castro 	 * The reset also affects other HW that is not under the control
62183c624d8SFabrizio Castro 	 * of Linux. Therefore, all we can do is make sure the reset is
62283c624d8SFabrizio Castro 	 * deasserted.
62383c624d8SFabrizio Castro 	 */
62483c624d8SFabrizio Castro 	reset_control_deassert(rstc);
62583c624d8SFabrizio Castro 
62683c624d8SFabrizio Castro 	/* Make sure the IP is in SW reset state */
62783c624d8SFabrizio Castro 	ret = rzv2m_csi_sw_reset(csi, 1);
62883c624d8SFabrizio Castro 	if (ret)
62983c624d8SFabrizio Castro 		return ret;
63083c624d8SFabrizio Castro 
63183c624d8SFabrizio Castro 	ret = clk_prepare_enable(csi->csiclk);
63283c624d8SFabrizio Castro 	if (ret)
63383c624d8SFabrizio Castro 		return dev_err_probe(dev, ret, "could not enable csiclk\n");
63483c624d8SFabrizio Castro 
63583c624d8SFabrizio Castro 	ret = spi_register_controller(controller);
63683c624d8SFabrizio Castro 	if (ret) {
63783c624d8SFabrizio Castro 		clk_disable_unprepare(csi->csiclk);
63883c624d8SFabrizio Castro 		return dev_err_probe(dev, ret, "register controller failed\n");
63983c624d8SFabrizio Castro 	}
64083c624d8SFabrizio Castro 
64183c624d8SFabrizio Castro 	return 0;
64283c624d8SFabrizio Castro }
64383c624d8SFabrizio Castro 
64493033314SUwe Kleine-König static void rzv2m_csi_remove(struct platform_device *pdev)
64583c624d8SFabrizio Castro {
64683c624d8SFabrizio Castro 	struct rzv2m_csi_priv *csi = platform_get_drvdata(pdev);
64783c624d8SFabrizio Castro 
64883c624d8SFabrizio Castro 	spi_unregister_controller(csi->controller);
64983c624d8SFabrizio Castro 	rzv2m_csi_sw_reset(csi, 1);
65083c624d8SFabrizio Castro 	clk_disable_unprepare(csi->csiclk);
65183c624d8SFabrizio Castro }
65283c624d8SFabrizio Castro 
65383c624d8SFabrizio Castro static const struct of_device_id rzv2m_csi_match[] = {
65483c624d8SFabrizio Castro 	{ .compatible = "renesas,rzv2m-csi" },
65583c624d8SFabrizio Castro 	{ /* sentinel */ }
65683c624d8SFabrizio Castro };
65783c624d8SFabrizio Castro MODULE_DEVICE_TABLE(of, rzv2m_csi_match);
65883c624d8SFabrizio Castro 
65983c624d8SFabrizio Castro static struct platform_driver rzv2m_csi_drv = {
66083c624d8SFabrizio Castro 	.probe = rzv2m_csi_probe,
66193033314SUwe Kleine-König 	.remove_new = rzv2m_csi_remove,
66283c624d8SFabrizio Castro 	.driver = {
66383c624d8SFabrizio Castro 		.name = "rzv2m_csi",
66483c624d8SFabrizio Castro 		.of_match_table = rzv2m_csi_match,
66583c624d8SFabrizio Castro 	},
66683c624d8SFabrizio Castro };
66783c624d8SFabrizio Castro module_platform_driver(rzv2m_csi_drv);
66883c624d8SFabrizio Castro 
66983c624d8SFabrizio Castro MODULE_LICENSE("GPL");
67083c624d8SFabrizio Castro MODULE_AUTHOR("Fabrizio Castro <castro.fabrizio.jz@renesas.com>");
67183c624d8SFabrizio Castro MODULE_DESCRIPTION("Clocked Serial Interface Driver");
672