diff options
Diffstat (limited to 'drivers/spi/spi-cadence.c')
| -rw-r--r-- | drivers/spi/spi-cadence.c | 254 |
1 files changed, 167 insertions, 87 deletions
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index de8fe3c5becb..47054da630d0 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Cadence SPI controller driver (master and slave mode) + * Cadence SPI controller driver (host and target mode) * * Copyright (C) 2008 - 2014 Xilinx, Inc. * @@ -18,6 +18,7 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/spi/spi.h> /* Name of this driver */ @@ -59,10 +60,10 @@ CDNS_SPI_CR_BAUD_DIV_4) /* - * SPI Configuration Register - Baud rate and slave select + * SPI Configuration Register - Baud rate and target select * * These are the values used in the calculation of baud rate divisor and - * setting the slave select. + * setting the target select. */ #define CDNS_SPI_BAUD_DIV_MAX 7 /* Baud rate divisor maximum */ @@ -108,9 +109,11 @@ * @rxbuf: Pointer to the RX buffer * @tx_bytes: Number of bytes left to transfer * @rx_bytes: Number of bytes requested + * @n_bytes: Number of bytes per word * @dev_busy: Device busy flag * @is_decoded_cs: Flag for decoder property set or not * @tx_fifo_depth: Depth of the TX FIFO + * @rstc: Optional reset control for SPI controller */ struct cdns_spi { void __iomem *regs; @@ -118,13 +121,22 @@ struct cdns_spi { struct clk *pclk; unsigned int clk_rate; u32 speed_hz; - const u8 *txbuf; - u8 *rxbuf; + const void *txbuf; + void *rxbuf; int tx_bytes; int rx_bytes; + u8 n_bytes; u8 dev_busy; u32 is_decoded_cs; unsigned int tx_fifo_depth; + struct reset_control *rstc; +}; + +enum cdns_spi_frame_n_bytes { + CDNS_SPI_N_BYTES_NULL = 0, + CDNS_SPI_N_BYTES_U8 = 1, + CDNS_SPI_N_BYTES_U16 = 2, + CDNS_SPI_N_BYTES_U32 = 4 }; /* Macros for the SPI controller read/write */ @@ -141,20 +153,20 @@ static inline void cdns_spi_write(struct cdns_spi *xspi, u32 offset, u32 val) /** * cdns_spi_init_hw - Initialize the hardware and configure the SPI controller * @xspi: Pointer to the cdns_spi structure - * @is_slave: Flag to indicate slave or master mode - * * On reset the SPI controller is configured to slave or master mode. - * In master mode baud rate divisor is set to 4, threshold value for TX FIFO + * @is_target: Flag to indicate target or host mode + * * On reset the SPI controller is configured to target or host mode. + * In host mode baud rate divisor is set to 4, threshold value for TX FIFO * not full interrupt is set to 1 and size of the word to be transferred as 8 bit. * * This function initializes the SPI controller to disable and clear all the - * interrupts, enable manual slave select and manual start, deselect all the + * interrupts, enable manual target select and manual start, deselect all the * chip select lines, and enable the SPI controller. */ -static void cdns_spi_init_hw(struct cdns_spi *xspi, bool is_slave) +static void cdns_spi_init_hw(struct cdns_spi *xspi, bool is_target) { u32 ctrl_reg = 0; - if (!is_slave) + if (!is_target) ctrl_reg |= CDNS_SPI_CR_DEFAULT; if (xspi->is_decoded_cs) @@ -185,10 +197,10 @@ static void cdns_spi_chipselect(struct spi_device *spi, bool is_high) ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR); if (is_high) { - /* Deselect the slave */ + /* Deselect the target */ ctrl_reg |= CDNS_SPI_CR_SSCTRL; } else { - /* Select the slave */ + /* Select the target */ ctrl_reg &= ~CDNS_SPI_CR_SSCTRL; if (!(xspi->is_decoded_cs)) ctrl_reg |= ((~(CDNS_SPI_SS0 << spi_get_chipselect(spi, 0))) << @@ -227,7 +239,7 @@ static void cdns_spi_config_clock_mode(struct spi_device *spi) /* * Just writing the CR register does not seem to apply the clock * setting changes. This is problematic when changing the clock - * polarity as it will cause the SPI slave to see spurious clock + * polarity as it will cause the SPI target to see spurious clock * transitions. To workaround the issue toggle the ER register. */ cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE); @@ -302,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi, return 0; } +static u8 cdns_spi_n_bytes(struct spi_transfer *transfer) +{ + if (transfer->bits_per_word <= 8) + return CDNS_SPI_N_BYTES_U8; + else if (transfer->bits_per_word <= 16) + return CDNS_SPI_N_BYTES_U16; + else + return CDNS_SPI_N_BYTES_U32; +} + +static inline void cdns_spi_reader(struct cdns_spi *xspi) +{ + u32 rxw = 0; + + if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) { + pr_err("%s: rxbuf address is not aligned for %d bytes\n", + __func__, xspi->n_bytes); + return; + } + + rxw = cdns_spi_read(xspi, CDNS_SPI_RXD); + if (xspi->rxbuf) { + switch (xspi->n_bytes) { + case CDNS_SPI_N_BYTES_U8: + *(u8 *)xspi->rxbuf = rxw; + break; + case CDNS_SPI_N_BYTES_U16: + *(u16 *)xspi->rxbuf = rxw; + break; + case CDNS_SPI_N_BYTES_U32: + *(u32 *)xspi->rxbuf = rxw; + break; + default: + pr_err("%s invalid n_bytes %d\n", __func__, + xspi->n_bytes); + return; + } + xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes; + } +} + +static inline void cdns_spi_writer(struct cdns_spi *xspi) +{ + u32 txw = 0; + + if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) { + pr_err("%s: txbuf address is not aligned for %d bytes\n", + __func__, xspi->n_bytes); + return; + } + + if (xspi->txbuf) { + switch (xspi->n_bytes) { + case CDNS_SPI_N_BYTES_U8: + txw = *(u8 *)xspi->txbuf; + break; + case CDNS_SPI_N_BYTES_U16: + txw = *(u16 *)xspi->txbuf; + break; + case CDNS_SPI_N_BYTES_U32: + txw = *(u32 *)xspi->txbuf; + break; + default: + pr_err("%s invalid n_bytes %d\n", __func__, + xspi->n_bytes); + return; + } + cdns_spi_write(xspi, CDNS_SPI_TXD, txw); + xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes; + } +} + /** * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO * @xspi: Pointer to the cdns_spi structure @@ -317,29 +401,15 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) xspi->rx_bytes -= nrx; while (ntx || nrx) { - /* When xspi in busy condition, bytes may send failed, - * then spi control did't work thoroughly, add one byte delay - */ - if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) - udelay(10); + if (nrx) { + cdns_spi_reader(xspi); + nrx--; + } if (ntx) { - if (xspi->txbuf) - cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); - else - cdns_spi_write(xspi, CDNS_SPI_TXD, 0); - + cdns_spi_writer(xspi); ntx--; } - - if (nrx) { - u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); - - if (xspi->rxbuf) - *xspi->rxbuf++ = data; - - nrx--; - } } } @@ -392,6 +462,11 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) if (xspi->tx_bytes) { cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt); } else { + /* Fixed delay due to controller limitation with + * RX_NEMPTY incorrect status + * Xilinx AR:65885 contains more details + */ + udelay(10); cdns_spi_process_fifo(xspi, 0, trans_cnt); cdns_spi_write(xspi, CDNS_SPI_IDR, CDNS_SPI_IXR_DEFAULT); @@ -406,7 +481,7 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) static int cdns_prepare_message(struct spi_controller *ctlr, struct spi_message *msg) { - if (!spi_controller_is_slave(ctlr)) + if (!spi_controller_is_target(ctlr)) cdns_spi_config_clock_mode(msg->spi); return 0; } @@ -418,9 +493,9 @@ static int cdns_prepare_message(struct spi_controller *ctlr, * @transfer: Pointer to the spi_transfer structure which provides * information about next transfer parameters * - * This function in master mode fills the TX FIFO, starts the SPI transfer and + * This function in host mode fills the TX FIFO, starts the SPI transfer and * returns a positive transfer count so that core will wait for completion. - * This function in slave mode fills the TX FIFO and wait for transfer trigger. + * This function in target mode fills the TX FIFO and wait for transfer trigger. * * Return: Number of bytes transferred in the last transfer */ @@ -435,18 +510,27 @@ static int cdns_transfer_one(struct spi_controller *ctlr, xspi->tx_bytes = transfer->len; xspi->rx_bytes = transfer->len; - if (!spi_controller_is_slave(ctlr)) { + if (!spi_controller_is_target(ctlr)) { cdns_spi_setup_transfer(spi, transfer); } else { /* Set TX empty threshold to half of FIFO depth - * only if TX bytes are more than half FIFO depth. + * only if TX bytes are more than FIFO depth. */ if (xspi->tx_bytes > xspi->tx_fifo_depth) cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1); } + /* When xspi in busy condition, bytes may send failed, + * then spi control didn't work thoroughly, add one byte delay + */ + if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) + udelay(10); + + xspi->n_bytes = cdns_spi_n_bytes(transfer); + xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); + xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); + cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); - spi_transfer_delay_exec(transfer); cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); return transfer->len; @@ -457,7 +541,7 @@ static int cdns_transfer_one(struct spi_controller *ctlr, * @ctlr: Pointer to the spi_controller structure which provides * information about the controller. * - * This function enables SPI master controller. + * This function enables SPI host controller. * * Return: 0 always */ @@ -475,7 +559,7 @@ static int cdns_prepare_transfer_hardware(struct spi_controller *ctlr) * @ctlr: Pointer to the spi_controller structure which provides * information about the controller. * - * This function disables the SPI master controller when no slave selected. + * This function disables the SPI host controller when no target selected. * This function flush out if any pending data in FIFO. * * Return: 0 always @@ -486,15 +570,15 @@ static int cdns_unprepare_transfer_hardware(struct spi_controller *ctlr) u32 ctrl_reg; unsigned int cnt = xspi->tx_fifo_depth; - if (spi_controller_is_slave(ctlr)) { + if (spi_controller_is_target(ctlr)) { while (cnt--) cdns_spi_read(xspi, CDNS_SPI_RXD); } - /* Disable the SPI if slave is deselected */ + /* Disable the SPI if target is deselected */ ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR); ctrl_reg = (ctrl_reg & CDNS_SPI_CR_SSCTRL) >> CDNS_SPI_SS_SHIFT; - if (ctrl_reg == CDNS_SPI_NOSS || spi_controller_is_slave(ctlr)) + if (ctrl_reg == CDNS_SPI_NOSS || spi_controller_is_target(ctlr)) cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE); /* Reset to default */ @@ -521,14 +605,14 @@ static void cdns_spi_detect_fifo_depth(struct cdns_spi *xspi) } /** - * cdns_slave_abort - Abort slave transfer + * cdns_target_abort - Abort target transfer * @ctlr: Pointer to the spi_controller structure * - * This function abort slave transfer if there any transfer timeout. + * This function abort target transfer if there any transfer timeout. * * Return: 0 always */ -static int cdns_slave_abort(struct spi_controller *ctlr) +static int cdns_target_abort(struct spi_controller *ctlr) { struct cdns_spi *xspi = spi_controller_get_devdata(ctlr); u32 intr_status; @@ -555,13 +639,13 @@ static int cdns_spi_probe(struct platform_device *pdev) struct spi_controller *ctlr; struct cdns_spi *xspi; u32 num_cs; - bool slave; + bool target; - slave = of_property_read_bool(pdev->dev.of_node, "spi-slave"); - if (slave) - ctlr = spi_alloc_slave(&pdev->dev, sizeof(*xspi)); + target = of_property_read_bool(pdev->dev.of_node, "spi-slave"); + if (target) + ctlr = spi_alloc_target(&pdev->dev, sizeof(*xspi)); else - ctlr = spi_alloc_master(&pdev->dev, sizeof(*xspi)); + ctlr = spi_alloc_host(&pdev->dev, sizeof(*xspi)); if (!ctlr) return -ENOMEM; @@ -576,33 +660,31 @@ static int cdns_spi_probe(struct platform_device *pdev) goto remove_ctlr; } - xspi->pclk = devm_clk_get(&pdev->dev, "pclk"); + xspi->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); if (IS_ERR(xspi->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); ret = PTR_ERR(xspi->pclk); goto remove_ctlr; } - ret = clk_prepare_enable(xspi->pclk); - if (ret) { - dev_err(&pdev->dev, "Unable to enable APB clock.\n"); + xspi->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi"); + if (IS_ERR(xspi->rstc)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(xspi->rstc), + "Cannot get SPI reset.\n"); goto remove_ctlr; } - if (!spi_controller_is_slave(ctlr)) { - xspi->ref_clk = devm_clk_get(&pdev->dev, "ref_clk"); - if (IS_ERR(xspi->ref_clk)) { - dev_err(&pdev->dev, "ref_clk clock not found.\n"); - ret = PTR_ERR(xspi->ref_clk); - goto clk_dis_apb; - } + reset_control_assert(xspi->rstc); + reset_control_deassert(xspi->rstc); - ret = clk_prepare_enable(xspi->ref_clk); - if (ret) { - dev_err(&pdev->dev, "Unable to enable device clock.\n"); - goto clk_dis_apb; - } + xspi->ref_clk = devm_clk_get_enabled(&pdev->dev, "ref_clk"); + if (IS_ERR(xspi->ref_clk)) { + dev_err(&pdev->dev, "ref_clk clock not found.\n"); + ret = PTR_ERR(xspi->ref_clk); + goto remove_ctlr; + } + if (!spi_controller_is_target(ctlr)) { pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_get_noresume(&pdev->dev); @@ -624,11 +706,11 @@ static int cdns_spi_probe(struct platform_device *pdev) cdns_spi_detect_fifo_depth(xspi); /* SPI controller initializations */ - cdns_spi_init_hw(xspi, spi_controller_is_slave(ctlr)); + cdns_spi_init_hw(xspi, spi_controller_is_target(ctlr)); irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - ret = -ENXIO; + if (irq < 0) { + ret = irq; goto clk_dis_all; } @@ -648,7 +730,10 @@ static int cdns_spi_probe(struct platform_device *pdev) ctlr->mode_bits = SPI_CPOL | SPI_CPHA; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); - if (!spi_controller_is_slave(ctlr)) { + if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6")) + ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32); + + if (!spi_controller_is_target(ctlr)) { ctlr->mode_bits |= SPI_CS_HIGH; ctlr->set_cs = cdns_spi_chipselect; ctlr->auto_runtime_pm = true; @@ -656,11 +741,10 @@ static int cdns_spi_probe(struct platform_device *pdev) /* Set to default valid value */ ctlr->max_speed_hz = xspi->clk_rate / 4; xspi->speed_hz = ctlr->max_speed_hz; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); } else { ctlr->mode_bits |= SPI_NO_CS; - ctlr->slave_abort = cdns_slave_abort; + ctlr->target_abort = cdns_target_abort; } ret = spi_register_controller(ctlr); if (ret) { @@ -671,13 +755,10 @@ static int cdns_spi_probe(struct platform_device *pdev) return ret; clk_dis_all: - if (!spi_controller_is_slave(ctlr)) { - pm_runtime_set_suspended(&pdev->dev); + if (!spi_controller_is_target(ctlr)) { pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(xspi->ref_clk); + pm_runtime_set_suspended(&pdev->dev); } -clk_dis_apb: - clk_disable_unprepare(xspi->pclk); remove_ctlr: spi_controller_put(ctlr); return ret; @@ -690,8 +771,6 @@ remove_ctlr: * This function is called if a device is physically removed from the system or * if the driver module is being unloaded. It frees all resources allocated to * the device. - * - * Return: 0 on success and error value on error */ static void cdns_spi_remove(struct platform_device *pdev) { @@ -700,10 +779,10 @@ static void cdns_spi_remove(struct platform_device *pdev) cdns_spi_write(xspi, CDNS_SPI_ER, CDNS_SPI_ER_DISABLE); - clk_disable_unprepare(xspi->ref_clk); - clk_disable_unprepare(xspi->pclk); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_disable(&pdev->dev); + if (!spi_controller_is_target(ctlr)) { + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + } spi_unregister_controller(ctlr); } @@ -737,7 +816,7 @@ static int __maybe_unused cdns_spi_resume(struct device *dev) struct spi_controller *ctlr = dev_get_drvdata(dev); struct cdns_spi *xspi = spi_controller_get_devdata(ctlr); - cdns_spi_init_hw(xspi, spi_controller_is_slave(ctlr)); + cdns_spi_init_hw(xspi, spi_controller_is_target(ctlr)); return spi_controller_resume(ctlr); } @@ -797,6 +876,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = { static const struct of_device_id cdns_spi_of_match[] = { { .compatible = "xlnx,zynq-spi-r1p6" }, + { .compatible = "cix,sky1-spi-r1p6" }, { .compatible = "cdns,spi-r1p6" }, { /* end of table */ } }; @@ -805,7 +885,7 @@ MODULE_DEVICE_TABLE(of, cdns_spi_of_match); /* cdns_spi_driver - This structure defines the SPI subsystem platform driver */ static struct platform_driver cdns_spi_driver = { .probe = cdns_spi_probe, - .remove_new = cdns_spi_remove, + .remove = cdns_spi_remove, .driver = { .name = CDNS_SPI_NAME, .of_match_table = cdns_spi_of_match, |
