summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/spi/spi-cadence-xspi.c52
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) {