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