diff options
| -rw-r--r-- | drivers/spi/spi-cadence-xspi.c | 52 |
1 files changed, 42 insertions, 10 deletions
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 39c868a5b171..3687853e4ce4 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -268,6 +268,7 @@ #define MRVL_XFER_FUNC_START BIT(0) #define MRVL_XFER_QWORD_COUNT 32 #define MRVL_XFER_QWORD_BYTECOUNT 8 +#define MRVL_XFER_MAX_LEN (MRVL_XFER_QWORD_COUNT * MRVL_XFER_QWORD_BYTECOUNT) #define MRVL_XSPI_POLL_TIMEOUT_US 1000 #define MRVL_XSPI_POLL_DELAY_US 10 @@ -332,6 +333,7 @@ struct cdns_xspi_dev { int irq; int cur_cs; unsigned int sdmasize; + unsigned int sdma_xfer_len; struct completion cmd_complete; struct completion auto_cmd_complete; @@ -346,7 +348,7 @@ struct cdns_xspi_dev { u8 hw_num_banks; const struct cdns_xspi_driver_data *driver_data; - void (*sdma_handler)(struct cdns_xspi_dev *cdns_xspi); + int (*sdma_handler)(struct cdns_xspi_dev *cdns_xspi); void (*set_interrupts_handler)(struct cdns_xspi_dev *cdns_xspi, bool enabled); bool xfer_in_progress; @@ -496,7 +498,7 @@ static inline void cdns_xspi_sdma_write(struct cdns_xspi_dev *cdns_xspi, size_t iowrite8_rep(dst, (const u8 *)buf + offset, len); } -static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) +static int cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) { u32 sdma_size, sdma_trd_info; u8 sdma_dir; @@ -505,6 +507,13 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG); sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info); + if (sdma_size > cdns_xspi->sdma_xfer_len) { + dev_err(cdns_xspi->dev, + "SDMA size %u exceeds the requested length %u\n", + sdma_size, cdns_xspi->sdma_xfer_len); + return -EINVAL; + } + switch (sdma_dir) { case CDNS_XSPI_SDMA_DIR_READ: cdns_xspi_sdma_read(cdns_xspi, sdma_size); @@ -514,6 +523,8 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) cdns_xspi_sdma_write(cdns_xspi, sdma_size); break; } + + return 0; } static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi, @@ -559,6 +570,7 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi, cdns_xspi->in_buffer = op->data.buf.in; cdns_xspi->out_buffer = op->data.buf.out; + cdns_xspi->sdma_xfer_len = op->data.nbytes; cdns_xspi_trigger_command(cdns_xspi, cmd_regs); @@ -567,7 +579,11 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi, cdns_xspi->set_interrupts_handler(cdns_xspi, false); return -EIO; } - cdns_xspi->sdma_handler(cdns_xspi); + ret = cdns_xspi->sdma_handler(cdns_xspi); + if (ret) { + cdns_xspi->set_interrupts_handler(cdns_xspi, false); + return ret; + } } wait_for_completion(&cdns_xspi->cmd_complete); @@ -950,7 +966,7 @@ static void m_iowriteq(void __iomem *addr, const void *buf, int len) } } -static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) +static int marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) { u32 sdma_size, sdma_trd_info; u8 sdma_dir; @@ -959,6 +975,13 @@ static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG); sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info); + if (sdma_size > cdns_xspi->sdma_xfer_len) { + dev_err(cdns_xspi->dev, + "SDMA size %u exceeds the requested length %u\n", + sdma_size, cdns_xspi->sdma_xfer_len); + return -EINVAL; + } + switch (sdma_dir) { case CDNS_XSPI_SDMA_DIR_READ: m_ioreadq(cdns_xspi->sdmabase, @@ -970,6 +993,8 @@ static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) cdns_xspi->out_buffer, sdma_size); break; } + + return 0; } static const struct spi_controller_mem_ops marvell_xspi_mem_ops = { @@ -1083,7 +1108,7 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller, struct spi_device *spi = m->spi; struct spi_transfer *t = NULL; - const unsigned int max_len = MRVL_XFER_QWORD_BYTECOUNT * MRVL_XFER_QWORD_COUNT; + const unsigned int max_len = MRVL_XFER_MAX_LEN; int current_transfer_len; int cs = spi_get_chipselect(spi, 0); int cs_change = 0; @@ -1106,13 +1131,16 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller, list_for_each_entry(t, &m->transfers, transfer_list) { u8 *txd = (u8 *) t->tx_buf; u8 *rxd = (u8 *) t->rx_buf; - u8 data[10]; + u8 data[MRVL_XFER_MAX_LEN] = {0}; u32 cmd_regs[6]; if (!txd) txd = data; - cdns_xspi->in_buffer = txd + 1; + if (rxd) + cdns_xspi->in_buffer = rxd; + else + cdns_xspi->in_buffer = data; cdns_xspi->out_buffer = txd + 1; while (t->len) { @@ -1131,14 +1159,18 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller, cdns_xspi_prepare_transfer(cs, 1, current_transfer_len - 1, cmd_regs); cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + cdns_xspi->sdma_xfer_len = current_transfer_len - 1; if (!cdns_xspi_is_sdma_ready(cdns_xspi, true)) return -EIO; - cdns_xspi->sdma_handler(cdns_xspi); + if (cdns_xspi->sdma_handler(cdns_xspi)) + return -EIO; if (!cdns_xspi_is_stig_ready(cdns_xspi, true)) return -EIO; - cdns_xspi->in_buffer += current_transfer_len; - cdns_xspi->out_buffer += current_transfer_len; + if (rxd) + cdns_xspi->in_buffer += current_transfer_len; + if (t->tx_buf) + cdns_xspi->out_buffer += current_transfer_len; } if (rxd) { |
