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