Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Fabrizio Castro | 3018 | 99.90% | 11 | 84.62% |
Uwe Kleine-König | 2 | 0.07% | 1 | 7.69% |
Yang Yingliang | 1 | 0.03% | 1 | 7.69% |
Total | 3021 | 13 |
// SPDX-License-Identifier: GPL-2.0-or-later /* * Renesas RZ/V2M Clocked Serial Interface (CSI) driver * * Copyright (C) 2023 Renesas Electronics Corporation */ #include <linux/bits.h> #include <linux/clk.h> #include <linux/count_zeros.h> #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/log2.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/reset.h> #include <linux/spi/spi.h> #include <linux/units.h> /* Registers */ #define CSI_MODE 0x00 /* CSI mode control */ #define CSI_CLKSEL 0x04 /* CSI clock select */ #define CSI_CNT 0x08 /* CSI control */ #define CSI_INT 0x0C /* CSI interrupt status */ #define CSI_IFIFOL 0x10 /* CSI receive FIFO level display */ #define CSI_OFIFOL 0x14 /* CSI transmit FIFO level display */ #define CSI_IFIFO 0x18 /* CSI receive window */ #define CSI_OFIFO 0x1C /* CSI transmit window */ #define CSI_FIFOTRG 0x20 /* CSI FIFO trigger level */ /* CSI_MODE */ #define CSI_MODE_CSIE BIT(7) #define CSI_MODE_TRMD BIT(6) #define CSI_MODE_CCL BIT(5) #define CSI_MODE_DIR BIT(4) #define CSI_MODE_CSOT BIT(0) #define CSI_MODE_SETUP 0x00000040 /* CSI_CLKSEL */ #define CSI_CLKSEL_SS_ENA BIT(19) #define CSI_CLKSEL_SS_POL BIT(18) #define CSI_CLKSEL_SS (CSI_CLKSEL_SS_ENA | CSI_CLKSEL_SS_POL) #define CSI_CLKSEL_CKP BIT(17) #define CSI_CLKSEL_DAP BIT(16) #define CSI_CLKSEL_MODE (CSI_CLKSEL_CKP|CSI_CLKSEL_DAP) #define CSI_CLKSEL_SLAVE BIT(15) #define CSI_CLKSEL_CKS GENMASK(14, 1) /* CSI_CNT */ #define CSI_CNT_CSIRST BIT(28) #define CSI_CNT_R_TRGEN BIT(19) #define CSI_CNT_UNDER_E BIT(13) #define CSI_CNT_OVERF_E BIT(12) #define CSI_CNT_TREND_E BIT(9) #define CSI_CNT_CSIEND_E BIT(8) #define CSI_CNT_T_TRGR_E BIT(4) #define CSI_CNT_R_TRGR_E BIT(0) /* CSI_INT */ #define CSI_INT_UNDER BIT(13) #define CSI_INT_OVERF BIT(12) #define CSI_INT_TREND BIT(9) #define CSI_INT_CSIEND BIT(8) #define CSI_INT_T_TRGR BIT(4) #define CSI_INT_R_TRGR BIT(0) /* CSI_FIFOTRG */ #define CSI_FIFOTRG_R_TRG GENMASK(2, 0) #define CSI_FIFO_SIZE_BYTES 32U #define CSI_FIFO_HALF_SIZE 16U #define CSI_EN_DIS_TIMEOUT_US 100 /* * Clock "csiclk" gets divided by 2 * CSI_CLKSEL_CKS in order to generate the * serial clock (output from master), with CSI_CLKSEL_CKS ranging from 0x1 (that * means "csiclk" is divided by 2) to 0x3FFF ("csiclk" is divided by 32766). */ #define CSI_CKS_MAX GENMASK(13, 0) #define UNDERRUN_ERROR BIT(0) #define OVERFLOW_ERROR BIT(1) #define TX_TIMEOUT_ERROR BIT(2) #define RX_TIMEOUT_ERROR BIT(3) #define CSI_MAX_SPI_SCKO (8 * HZ_PER_MHZ) #define CSI_CLKSEL_SS_DISABLED 0 #define CSI_CLKSEL_SS_ENABLED_ACTIVE_LOW BIT(1) #define CSI_CLKSEL_SS_ENABLED_ACTIVE_HIGH GENMASK(1, 0) struct rzv2m_csi_priv { void __iomem *base; struct clk *csiclk; struct clk *pclk; struct device *dev; struct spi_controller *controller; const void *txbuf; void *rxbuf; unsigned int buffer_len; unsigned int bytes_sent; unsigned int bytes_received; unsigned int bytes_to_transfer; unsigned int words_to_transfer; unsigned int bytes_per_word; wait_queue_head_t wait; u32 errors; u32 status; bool target_aborted; bool use_ss_pin; }; static void rzv2m_csi_reg_write_bit(const struct rzv2m_csi_priv *csi, int reg_offs, int bit_mask, u32 value) { int nr_zeros; u32 tmp; nr_zeros = count_trailing_zeros(bit_mask); value <<= nr_zeros; tmp = (readl(csi->base + reg_offs) & ~bit_mask) | value; writel(tmp, csi->base + reg_offs); } static int rzv2m_csi_sw_reset(struct rzv2m_csi_priv *csi, int assert) { u32 reg; rzv2m_csi_reg_write_bit(csi, CSI_CNT, CSI_CNT_CSIRST, assert); if (!assert) return 0; return readl_poll_timeout(csi->base + CSI_MODE, reg, !(reg & CSI_MODE_CSOT), 0, CSI_EN_DIS_TIMEOUT_US); } static int rzv2m_csi_start_stop_operation(const struct rzv2m_csi_priv *csi, int enable, bool wait) { u32 reg; rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_CSIE, enable); if (enable || !wait) return 0; return readl_poll_timeout(csi->base + CSI_MODE, reg, !(reg & CSI_MODE_CSOT), 0, CSI_EN_DIS_TIMEOUT_US); } static int rzv2m_csi_fill_txfifo(struct rzv2m_csi_priv *csi) { unsigned int i; if (readl(csi->base + CSI_OFIFOL)) return -EIO; if (csi->bytes_per_word == 2) { const u16 *buf = csi->txbuf; for (i = 0; i < csi->words_to_transfer; i++) writel(buf[i], csi->base + CSI_OFIFO); } else { const u8 *buf = csi->txbuf; for (i = 0; i < csi->words_to_transfer; i++) writel(buf[i], csi->base + CSI_OFIFO); } csi->txbuf += csi->bytes_to_transfer; csi->bytes_sent += csi->bytes_to_transfer; return 0; } static int rzv2m_csi_read_rxfifo(struct rzv2m_csi_priv *csi) { unsigned int i; if (readl(csi->base + CSI_IFIFOL) != csi->bytes_to_transfer) return -EIO; if (csi->bytes_per_word == 2) { u16 *buf = csi->rxbuf; for (i = 0; i < csi->words_to_transfer; i++) buf[i] = (u16)readl(csi->base + CSI_IFIFO); } else { u8 *buf = csi->rxbuf; for (i = 0; i < csi->words_to_transfer; i++) buf[i] = (u8)readl(csi->base + CSI_IFIFO); } csi->rxbuf += csi->bytes_to_transfer; csi->bytes_received += csi->bytes_to_transfer; return 0; } static inline void rzv2m_csi_empty_rxfifo(struct rzv2m_csi_priv *csi) { unsigned int i; for (i = 0; i < csi->words_to_transfer; i++) readl(csi->base + CSI_IFIFO); } static inline void rzv2m_csi_calc_current_transfer(struct rzv2m_csi_priv *csi) { unsigned int bytes_transferred = max(csi->bytes_received, csi->bytes_sent); unsigned int bytes_remaining = csi->buffer_len - bytes_transferred; unsigned int to_transfer; if (csi->txbuf) /* * Leaving a little bit of headroom in the FIFOs makes it very * hard to raise an overflow error (which is only possible * when IP transmits and receives at the same time). */ to_transfer = min(CSI_FIFO_HALF_SIZE, bytes_remaining); else to_transfer = min(CSI_FIFO_SIZE_BYTES, bytes_remaining); if (csi->bytes_per_word == 2) to_transfer >>= 1; /* * We can only choose a trigger level from a predefined set of values. * This will pick a value that is the greatest possible integer that's * less than or equal to the number of bytes we need to transfer. * This may result in multiple smaller transfers. */ csi->words_to_transfer = rounddown_pow_of_two(to_transfer); if (csi->bytes_per_word == 2) csi->bytes_to_transfer = csi->words_to_transfer << 1; else csi->bytes_to_transfer = csi->words_to_transfer; } static inline void rzv2m_csi_set_rx_fifo_trigger_level(struct rzv2m_csi_priv *csi) { rzv2m_csi_reg_write_bit(csi, CSI_FIFOTRG, CSI_FIFOTRG_R_TRG, ilog2(csi->words_to_transfer)); } static inline void rzv2m_csi_enable_rx_trigger(struct rzv2m_csi_priv *csi, bool enable) { rzv2m_csi_reg_write_bit(csi, CSI_CNT, CSI_CNT_R_TRGEN, enable); } static void rzv2m_csi_disable_irqs(const struct rzv2m_csi_priv *csi, u32 enable_bits) { u32 cnt = readl(csi->base + CSI_CNT); writel(cnt & ~enable_bits, csi->base + CSI_CNT); } static void rzv2m_csi_disable_all_irqs(struct rzv2m_csi_priv *csi) { rzv2m_csi_disable_irqs(csi, CSI_CNT_R_TRGR_E | CSI_CNT_T_TRGR_E | CSI_CNT_CSIEND_E | CSI_CNT_TREND_E | CSI_CNT_OVERF_E | CSI_CNT_UNDER_E); } static inline void rzv2m_csi_clear_irqs(struct rzv2m_csi_priv *csi, u32 irqs) { writel(irqs, csi->base + CSI_INT); } static void rzv2m_csi_clear_all_irqs(struct rzv2m_csi_priv *csi) { rzv2m_csi_clear_irqs(csi, CSI_INT_UNDER | CSI_INT_OVERF | CSI_INT_TREND | CSI_INT_CSIEND | CSI_INT_T_TRGR | CSI_INT_R_TRGR); } static void rzv2m_csi_enable_irqs(struct rzv2m_csi_priv *csi, u32 enable_bits) { u32 cnt = readl(csi->base + CSI_CNT); writel(cnt | enable_bits, csi->base + CSI_CNT); } static int rzv2m_csi_wait_for_interrupt(struct rzv2m_csi_priv *csi, u32 wait_mask, u32 enable_bits) { int ret; rzv2m_csi_enable_irqs(csi, enable_bits); if (spi_controller_is_target(csi->controller)) { ret = wait_event_interruptible(csi->wait, ((csi->status & wait_mask) == wait_mask) || csi->errors || csi->target_aborted); if (ret || csi->target_aborted) ret = -EINTR; } else { ret = wait_event_timeout(csi->wait, ((csi->status & wait_mask) == wait_mask) || csi->errors, HZ) == 0 ? -ETIMEDOUT : 0; } rzv2m_csi_disable_irqs(csi, enable_bits); if (csi->errors) return -EIO; return ret; } static inline int rzv2m_csi_wait_for_rx_ready(struct rzv2m_csi_priv *csi) { int ret; if (readl(csi->base + CSI_IFIFOL) >= csi->bytes_to_transfer) return 0; ret = rzv2m_csi_wait_for_interrupt(csi, CSI_INT_R_TRGR, CSI_CNT_R_TRGR_E); if (ret == -ETIMEDOUT) csi->errors |= RX_TIMEOUT_ERROR; return ret; } static irqreturn_t rzv2m_csi_irq_handler(int irq, void *data) { struct rzv2m_csi_priv *csi = data; csi->status = readl(csi->base + CSI_INT); rzv2m_csi_disable_irqs(csi, csi->status); if (csi->status & CSI_INT_OVERF) csi->errors |= OVERFLOW_ERROR; if (csi->status & CSI_INT_UNDER) csi->errors |= UNDERRUN_ERROR; wake_up(&csi->wait); return IRQ_HANDLED; } static void rzv2m_csi_setup_clock(struct rzv2m_csi_priv *csi, u32 spi_hz) { unsigned long csiclk_rate = clk_get_rate(csi->csiclk); unsigned long pclk_rate = clk_get_rate(csi->pclk); unsigned long csiclk_rate_limit = pclk_rate >> 1; u32 cks; /* * There is a restriction on the frequency of CSICLK, it has to be <= * PCLK / 2. */ if (csiclk_rate > csiclk_rate_limit) { clk_set_rate(csi->csiclk, csiclk_rate >> 1); csiclk_rate = clk_get_rate(csi->csiclk); } else if ((csiclk_rate << 1) <= csiclk_rate_limit) { clk_set_rate(csi->csiclk, csiclk_rate << 1); csiclk_rate = clk_get_rate(csi->csiclk); } spi_hz = spi_hz > CSI_MAX_SPI_SCKO ? CSI_MAX_SPI_SCKO : spi_hz; cks = DIV_ROUND_UP(csiclk_rate, spi_hz << 1); if (cks > CSI_CKS_MAX) cks = CSI_CKS_MAX; dev_dbg(csi->dev, "SPI clk rate is %ldHz\n", csiclk_rate / (cks << 1)); rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_CKS, cks); } static void rzv2m_csi_setup_operating_mode(struct rzv2m_csi_priv *csi, struct spi_transfer *t) { if (t->rx_buf && !t->tx_buf) /* Reception-only mode */ rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_TRMD, 0); else /* Send and receive mode */ rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_TRMD, 1); csi->bytes_per_word = t->bits_per_word / 8; rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_CCL, csi->bytes_per_word == 2); } static int rzv2m_csi_setup(struct spi_device *spi) { struct rzv2m_csi_priv *csi = spi_controller_get_devdata(spi->controller); u32 slave_selection = CSI_CLKSEL_SS_DISABLED; int ret; rzv2m_csi_sw_reset(csi, 0); writel(CSI_MODE_SETUP, csi->base + CSI_MODE); /* Setup clock polarity and phase timing */ rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_MODE, ~spi->mode & SPI_MODE_X_MASK); /* Setup serial data order */ rzv2m_csi_reg_write_bit(csi, CSI_MODE, CSI_MODE_DIR, !!(spi->mode & SPI_LSB_FIRST)); /* Set the role, 1 for target and 0 for host */ rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_SLAVE, !!spi_controller_is_target(csi->controller)); if (csi->use_ss_pin) slave_selection = spi->mode & SPI_CS_HIGH ? CSI_CLKSEL_SS_ENABLED_ACTIVE_HIGH : CSI_CLKSEL_SS_ENABLED_ACTIVE_LOW; /* Configure the slave selection (SS) pin */ rzv2m_csi_reg_write_bit(csi, CSI_CLKSEL, CSI_CLKSEL_SS, slave_selection); /* Give the IP a SW reset */ ret = rzv2m_csi_sw_reset(csi, 1); if (ret) return ret; rzv2m_csi_sw_reset(csi, 0); /* * We need to enable the communication so that the clock will settle * for the right polarity before enabling the CS. */ rzv2m_csi_start_stop_operation(csi, 1, false); udelay(10); rzv2m_csi_start_stop_operation(csi, 0, false); return 0; } static int rzv2m_csi_pio_transfer(struct rzv2m_csi_priv *csi) { bool tx_completed = !csi->txbuf; bool rx_completed = !csi->rxbuf; int ret = 0; /* Make sure the TX FIFO is empty */ writel(0, csi->base + CSI_OFIFOL); /* Make sure the RX FIFO is empty */ writel(0, csi->base + CSI_IFIFOL); csi->bytes_sent = 0; csi->bytes_received = 0; csi->errors = 0; csi->target_aborted = false; rzv2m_csi_disable_all_irqs(csi); rzv2m_csi_clear_all_irqs(csi); rzv2m_csi_enable_rx_trigger(csi, true); while (!tx_completed || !rx_completed) { /* * Decide how many words we are going to transfer during * this cycle (for both TX and RX), then set the RX FIFO trigger * level accordingly. No need to set a trigger level for the * TX FIFO, as this IP comes with an interrupt that fires when * the TX FIFO is empty. */ rzv2m_csi_calc_current_transfer(csi); rzv2m_csi_set_rx_fifo_trigger_level(csi); rzv2m_csi_enable_irqs(csi, CSI_INT_OVERF | CSI_INT_UNDER); writel(readl(csi->base + CSI_INT), csi->base + CSI_INT); csi->status = 0; /* TX */ if (csi->txbuf) { ret = rzv2m_csi_fill_txfifo(csi); if (ret) break; if (csi->bytes_sent == csi->buffer_len) tx_completed = true; } rzv2m_csi_start_stop_operation(csi, 1, false); /* * Make sure the RX FIFO contains the desired number of words. * We then either flush its content, or we copy it onto * csi->rxbuf. */ ret = rzv2m_csi_wait_for_rx_ready(csi); if (ret) break; if (!spi_controller_is_target(csi->controller)) rzv2m_csi_start_stop_operation(csi, 0, false); /* RX */ if (csi->rxbuf) { ret = rzv2m_csi_read_rxfifo(csi); if (ret) break; if (csi->bytes_received == csi->buffer_len) rx_completed = true; } else { rzv2m_csi_empty_rxfifo(csi); } if (csi->errors) { ret = -EIO; break; } } rzv2m_csi_start_stop_operation(csi, 0, true); rzv2m_csi_disable_all_irqs(csi); rzv2m_csi_enable_rx_trigger(csi, false); rzv2m_csi_clear_all_irqs(csi); return ret; } static int rzv2m_csi_transfer_one(struct spi_controller *controller, struct spi_device *spi, struct spi_transfer *transfer) { struct rzv2m_csi_priv *csi = spi_controller_get_devdata(controller); struct device *dev = csi->dev; int ret; csi->txbuf = transfer->tx_buf; csi->rxbuf = transfer->rx_buf; csi->buffer_len = transfer->len; rzv2m_csi_setup_operating_mode(csi, transfer); if (!spi_controller_is_target(csi->controller)) rzv2m_csi_setup_clock(csi, transfer->speed_hz); ret = rzv2m_csi_pio_transfer(csi); if (ret) { if (csi->errors & UNDERRUN_ERROR) dev_err(dev, "Underrun error\n"); if (csi->errors & OVERFLOW_ERROR) dev_err(dev, "Overflow error\n"); if (csi->errors & TX_TIMEOUT_ERROR) dev_err(dev, "TX timeout error\n"); if (csi->errors & RX_TIMEOUT_ERROR) dev_err(dev, "RX timeout error\n"); } return ret; } static int rzv2m_csi_target_abort(struct spi_controller *ctlr) { struct rzv2m_csi_priv *csi = spi_controller_get_devdata(ctlr); csi->target_aborted = true; wake_up(&csi->wait); return 0; } static int rzv2m_csi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct spi_controller *controller; struct device *dev = &pdev->dev; struct rzv2m_csi_priv *csi; struct reset_control *rstc; bool target_mode; int irq; int ret; target_mode = of_property_read_bool(np, "spi-slave"); if (target_mode) controller = devm_spi_alloc_target(dev, sizeof(*csi)); else controller = devm_spi_alloc_host(dev, sizeof(*csi)); if (!controller) return -ENOMEM; csi = spi_controller_get_devdata(controller); platform_set_drvdata(pdev, csi); csi->use_ss_pin = false; if (spi_controller_is_target(controller) && !of_property_read_bool(np, "renesas,csi-no-ss")) csi->use_ss_pin = true; csi->dev = dev; csi->controller = controller; csi->target_aborted = false; csi->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(csi->base)) return PTR_ERR(csi->base); irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; csi->csiclk = devm_clk_get(dev, "csiclk"); if (IS_ERR(csi->csiclk)) return dev_err_probe(dev, PTR_ERR(csi->csiclk), "could not get csiclk\n"); csi->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(csi->pclk)) return dev_err_probe(dev, PTR_ERR(csi->pclk), "could not get pclk\n"); rstc = devm_reset_control_get_shared(dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), "Missing reset ctrl\n"); init_waitqueue_head(&csi->wait); controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH; controller->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8); controller->setup = rzv2m_csi_setup; controller->transfer_one = rzv2m_csi_transfer_one; controller->use_gpio_descriptors = true; controller->target_abort = rzv2m_csi_target_abort; device_set_node(&controller->dev, dev_fwnode(dev)); ret = devm_request_irq(dev, irq, rzv2m_csi_irq_handler, 0, dev_name(dev), csi); if (ret) return dev_err_probe(dev, ret, "cannot request IRQ\n"); /* * The reset also affects other HW that is not under the control * of Linux. Therefore, all we can do is make sure the reset is * deasserted. */ reset_control_deassert(rstc); /* Make sure the IP is in SW reset state */ ret = rzv2m_csi_sw_reset(csi, 1); if (ret) return ret; ret = clk_prepare_enable(csi->csiclk); if (ret) return dev_err_probe(dev, ret, "could not enable csiclk\n"); ret = spi_register_controller(controller); if (ret) { clk_disable_unprepare(csi->csiclk); return dev_err_probe(dev, ret, "register controller failed\n"); } return 0; } static void rzv2m_csi_remove(struct platform_device *pdev) { struct rzv2m_csi_priv *csi = platform_get_drvdata(pdev); spi_unregister_controller(csi->controller); rzv2m_csi_sw_reset(csi, 1); clk_disable_unprepare(csi->csiclk); } static const struct of_device_id rzv2m_csi_match[] = { { .compatible = "renesas,rzv2m-csi" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzv2m_csi_match); static struct platform_driver rzv2m_csi_drv = { .probe = rzv2m_csi_probe, .remove_new = rzv2m_csi_remove, .driver = { .name = "rzv2m_csi", .of_match_table = rzv2m_csi_match, }, }; module_platform_driver(rzv2m_csi_drv); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Fabrizio Castro <castro.fabrizio.jz@renesas.com>"); MODULE_DESCRIPTION("Clocked Serial Interface Driver");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1