int cadence_qspi_apb_process_queue(struct struct_cqspi *cadence_qspi, struct spi_device *spi, unsigned int n_trans, struct spi_transfer **spi_xfer) { struct platform_device *pdev = cadence_qspi->pdev; struct cqspi_platform_data *pdata = pdev->dev.platform_data; struct spi_transfer *cmd_xfer = spi_xfer[0]; struct spi_transfer *data_xfer = (n_trans >= 2) ? spi_xfer[1] : NULL; void __iomem *iobase = cadence_qspi->iobase; unsigned int sclk; int ret = 0; struct cqspi_flash_pdata *f_pdata; pr_debug("%s %d\n", __func__, n_trans); if (!cmd_xfer->len) { pr_err("QSPI: SPI transfer length is 0.\n"); return -EINVAL; } /* Switch chip select. */ if (cadence_qspi->current_cs != spi->chip_select) { cadence_qspi->current_cs = spi->chip_select; cadence_qspi_switch_cs(cadence_qspi, spi->chip_select); } /* Setup baudrate divisor and delays */ f_pdata = &(pdata->f_pdata[cadence_qspi->current_cs]); sclk = cmd_xfer->speed_hz ? cmd_xfer->speed_hz : spi->max_speed_hz; cadence_qspi_apb_controller_disable(iobase); cadence_qspi_apb_config_baudrate_div(iobase, pdata->master_ref_clk_hz, sclk); cadence_qspi_apb_delay(cadence_qspi, pdata->master_ref_clk_hz, sclk); cadence_qspi_apb_readdata_capture(iobase, 1, f_pdata->read_delay); cadence_qspi_apb_controller_enable(iobase); /* * Use STIG command to send if the transfer length is less than * 4 or if only one transfer. */ if ((cmd_xfer->len < 4) || (n_trans == 1)) { /* STIG command */ if (data_xfer && data_xfer->rx_buf) { /* STIG read */ ret = cadence_qspi_apb_command_read(iobase, cmd_xfer->len, cmd_xfer->tx_buf, data_xfer->len, data_xfer->rx_buf); } else { /* STIG write */ ret = cadence_qspi_apb_command_write(iobase, cmd_xfer->len, cmd_xfer->tx_buf); } } else if (cmd_xfer->len >= 4 && (n_trans == 2)){ /* Indirect operation */ if (data_xfer->rx_buf) { /* Indirect read */ ret = cadence_qspi_apb_indirect_read_setup(iobase, pdata->qspi_ahb_phy, cmd_xfer->len, cmd_xfer->tx_buf, spi->addr_width); if (pdata->enable_dma) { ret = cadence_qspi_apb_indirect_read_dma( cadence_qspi, data_xfer->len, data_xfer->rx_buf); } else { ret = cadence_qspi_apb_indirect_read_execute( cadence_qspi, data_xfer->len, data_xfer->rx_buf); } } else { /* Indirect write */ ret = cadence_qspi_apb_indirect_write_setup( iobase, pdata->qspi_ahb_phy, cmd_xfer->len, cmd_xfer->tx_buf); if (pdata->enable_dma) { ret = cadence_qspi_apb_indirect_write_dma( cadence_qspi, data_xfer->len, (unsigned char *)data_xfer->tx_buf); } else { ret = cadence_qspi_apb_indirect_write_execute( cadence_qspi, data_xfer->len, data_xfer->tx_buf); } } } else { pr_err("QSPI : Unknown SPI transfer.\n"); return -EINVAL; } return ret; }
static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { struct udevice *bus = dev->parent; struct cadence_spi_platdata *plat = bus->platdata; struct cadence_spi_priv *priv = dev_get_priv(bus); void *base = priv->regbase; u8 *cmd_buf = priv->cmd_buf; size_t data_bytes; int err = 0; u32 mode = CQSPI_STIG_WRITE; if (flags & SPI_XFER_BEGIN) { /* copy command to local buffer */ priv->cmd_len = bitlen / 8; memcpy(cmd_buf, dout, priv->cmd_len); } if (flags == (SPI_XFER_BEGIN | SPI_XFER_END)) { /* if start and end bit are set, the data bytes is 0. */ data_bytes = 0; } else { data_bytes = bitlen / 8; } debug("%s: len=%d [bytes]\n", __func__, data_bytes); /* Set Chip select */ cadence_qspi_apb_chipselect(base, spi_chip_select(dev), CONFIG_CQSPI_DECODER); if ((flags & SPI_XFER_END) || (flags == 0)) { if (priv->cmd_len == 0) { printf("QSPI: Error, command is empty.\n"); return -1; } if (din && data_bytes) { /* read */ /* Use STIG if no address. */ if (!CQSPI_IS_ADDR(priv->cmd_len)) mode = CQSPI_STIG_READ; else mode = CQSPI_INDIRECT_READ; } else if (dout && !(flags & SPI_XFER_BEGIN)) { /* write */ if (!CQSPI_IS_ADDR(priv->cmd_len)) mode = CQSPI_STIG_WRITE; else mode = CQSPI_INDIRECT_WRITE; } switch (mode) { case CQSPI_STIG_READ: err = cadence_qspi_apb_command_read( base, priv->cmd_len, cmd_buf, data_bytes, din); break; case CQSPI_STIG_WRITE: err = cadence_qspi_apb_command_write(base, priv->cmd_len, cmd_buf, data_bytes, dout); break; case CQSPI_INDIRECT_READ: err = cadence_qspi_apb_indirect_read_setup(plat, priv->cmd_len, cmd_buf); if (!err) { err = cadence_qspi_apb_indirect_read_execute (plat, data_bytes, din); } break; case CQSPI_INDIRECT_WRITE: err = cadence_qspi_apb_indirect_write_setup (plat, priv->cmd_len, cmd_buf); if (!err) { err = cadence_qspi_apb_indirect_write_execute (plat, data_bytes, dout); } break; default: err = -1; break; } if (flags & SPI_XFER_END) { /* clear command buffer */ memset(cmd_buf, 0, sizeof(priv->cmd_buf)); priv->cmd_len = 0; } } return err; }