[U-Boot] [PATCH v3 084/108] spi: ich: Support hardware sequencing
Bin Meng
bmeng.cn at gmail.com
Tue Nov 19 14:36:53 UTC 2019
Hi Simon,
On Mon, Oct 21, 2019 at 11:40 AM Simon Glass <sjg at chromium.org> wrote:
>
> Apollolake (APL) only supports hardware sequencing. Add support for this
> into the SPI driver, as an option.
>
> Signed-off-by: Simon Glass <sjg at chromium.org>
> ---
>
> Changes in v3: None
> Changes in v2: None
>
> drivers/spi/ich.c | 205 +++++++++++++++++++++++++++++++++++++++++++++-
> drivers/spi/ich.h | 39 +++++++++
> 2 files changed, 241 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c
> index ae3bff36bba..ae1dc64bde8 100644
> --- a/drivers/spi/ich.c
> +++ b/drivers/spi/ich.c
> @@ -17,7 +17,9 @@
> #include <pci.h>
> #include <pci_ids.h>
> #include <spi.h>
> +#include <spi_flash.h>
> #include <spi-mem.h>
> +#include <asm/fast_spi.h>
> #include <asm/io.h>
>
> #include "ich.h"
> @@ -36,6 +38,7 @@ struct ich_spi_platdata {
> bool lockdown; /* lock down controller settings? */
> ulong mmio_base; /* Base of MMIO registers */
> pci_dev_t bdf; /* PCI address used by of-platdata */
> + bool hwseq; /* Use hardware sequencing (not s/w) */
> };
>
> static u8 ich_readb(struct ich_spi_priv *priv, int reg)
> @@ -244,7 +247,8 @@ static void ich_spi_config_opcode(struct udevice *dev)
> ich_writel(ctlr, SPI_OPMENU_UPPER, ctlr->opmenu + sizeof(u32));
> }
>
> -static int ich_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
> +static int ich_spi_exec_op_swseq(struct spi_slave *slave,
> + const struct spi_mem_op *op)
> {
> struct udevice *bus = dev_get_parent(slave->dev);
> struct ich_spi_platdata *plat = dev_get_platdata(bus);
> @@ -415,6 +419,197 @@ static int ich_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
> return 0;
> }
>
> +/*
> + * Ensure read/write xfer len is not greater than SPIBAR_FDATA_FIFO_SIZE and
> + * that the operation does not cross page boundary.
> + */
> +static uint get_xfer_len(u32 offset, int len, int page_size)
> +{
> + uint xfer_len = min(len, SPIBAR_FDATA_FIFO_SIZE);
> + uint bytes_left = ALIGN(offset, page_size) - offset;
> +
> + if (bytes_left)
> + xfer_len = min(xfer_len, bytes_left);
> +
> + return xfer_len;
> +}
> +
> +/* Fill FDATAn FIFO in preparation for a write transaction */
> +static void fill_xfer_fifo(struct fast_spi_regs *regs, const void *data,
> + uint len)
> +{
> + memcpy(regs->fdata, data, len);
> +}
> +
> +/* Drain FDATAn FIFO after a read transaction populates data */
> +static void drain_xfer_fifo(struct fast_spi_regs *regs, void *dest, uint len)
> +{
> + memcpy(dest, regs->fdata, len);
> +}
> +
> +/* Fire up a transfer using the hardware sequencer */
> +static void start_hwseq_xfer(struct fast_spi_regs *regs, uint hsfsts_cycle,
> + uint offset, uint len)
> +{
> + /* Make sure all W1C status bits get cleared */
> + u32 hsfsts;
> +
> + hsfsts = readl(®s->hsfsts_ctl);
> + hsfsts &= ~(HSFSTS_FCYCLE_MASK | HSFSTS_FDBC_MASK);
> + hsfsts |= HSFSTS_AEL | HSFSTS_FCERR | HSFSTS_FDONE;
> +
> + /* Set up transaction parameters */
> + hsfsts |= hsfsts_cycle << HSFSTS_FCYCLE_SHIFT;
> + hsfsts |= ((len - 1) << HSFSTS_FDBC_SHIFT) & HSFSTS_FDBC_MASK;
> + hsfsts |= HSFSTS_FGO;
> +
> + writel(offset, ®s->faddr);
> + writel(hsfsts, ®s->hsfsts_ctl);
> +}
> +
> +static int wait_for_hwseq_xfer(struct fast_spi_regs *regs, uint offset)
> +{
> + ulong start;
> + u32 hsfsts;
> +
> + start = get_timer(0);
> + do {
> + hsfsts = readl(®s->hsfsts_ctl);
> + if (hsfsts & HSFSTS_FCERR) {
> + debug("SPI transaction error at offset %x HSFSTS = %08x\n",
> + offset, hsfsts);
> + return -EIO;
> + }
> + if (hsfsts & HSFSTS_AEL)
> + return -EPERM;
> +
> + if (hsfsts & HSFSTS_FDONE)
> + return 0;
> + } while (get_timer(start) < SPIBAR_HWSEQ_XFER_TIMEOUT_MS);
> +
> + debug("SPI transaction timeout at offset %x HSFSTS = %08x, timer %d\n",
> + offset, hsfsts, (uint)get_timer(start));
> +
> + return -ETIMEDOUT;
> +}
> +
> +/**
> + * exec_sync_hwseq_xfer() - Execute FAST_SPI flash transfer
Should we just mention this is flash transfer using hardware
sequencer, instead of FAST_SPI?
> + *
> + * This waits until complete or timeout
> + *
> + * @regs: SPI registers
> + * @hsfsts_cycle: Cycle type (enum hsfsts_cycle_t)
> + * @offset: Offset to access
> + * @len: Number of bytes to transfer (can be 0)
> + * @return 9 if OK, -EIO on flash-cycle error (FCERR), -EPERM on access error
0 if OK
> + * (AEL), -ETIMEDOUT on timeout
> + */
> +static int exec_sync_hwseq_xfer(struct fast_spi_regs *regs, uint hsfsts_cycle,
> + uint offset, uint len)
> +{
> + start_hwseq_xfer(regs, hsfsts_cycle, offset, len);
> +
> + return wait_for_hwseq_xfer(regs, offset);
> +}
> +
> +static int ich_spi_exec_op_hwseq(struct spi_slave *slave,
> + const struct spi_mem_op *op)
> +{
> + struct spi_flash *flash = dev_get_uclass_priv(slave->dev);
> + struct udevice *bus = dev_get_parent(slave->dev);
> + struct ich_spi_priv *priv = dev_get_priv(bus);
> + struct fast_spi_regs *regs = priv->base;
> + uint page_size;
> + uint offset;
> + int cycle;
> + uint len;
> + bool out;
> + int ret;
> + u8 *buf;
> +
> + offset = op->addr.val;
> + len = op->data.nbytes;
> +
> + switch (op->cmd.opcode) {
> + case SPINOR_OP_RDID:
> + cycle = HSFSTS_CYCLE_RDID;
> + break;
> + case SPINOR_OP_READ_FAST:
> + cycle = HSFSTS_CYCLE_READ;
> + break;
> + case SPINOR_OP_PP:
> + cycle = HSFSTS_CYCLE_WRITE;
> + break;
> + case SPINOR_OP_WREN:
> + /* Nothing needs to be done */
> + return 0;
> + case SPINOR_OP_WRSR:
> + cycle = HSFSTS_CYCLE_WR_STATUS;
> + break;
> + case SPINOR_OP_RDSR:
> + cycle = HSFSTS_CYCLE_RD_STATUS;
> + break;
> + case SPINOR_OP_WRDI:
> + return 0; /* ignore */
> + case SPINOR_OP_BE_4K:
> + cycle = HSFSTS_CYCLE_4K_ERASE;
> + while (len) {
> + uint xfer_len = 0x1000;
> +
> + ret = exec_sync_hwseq_xfer(regs, cycle, offset, 0);
> + if (ret)
> + return ret;
> + offset += xfer_len;
> + len -= xfer_len;
> + }
> + return 0;
> + default:
> + debug("Unknown cycle %x\n", op->cmd.opcode);
> + return -EINVAL;
> + };
> +
> + out = op->data.dir == SPI_MEM_DATA_OUT;
> + buf = out ? (u8 *)op->data.buf.out : op->data.buf.in;
> + page_size = flash->page_size ? : 256;
> +
> + while (len) {
> + uint xfer_len = get_xfer_len(offset, len, page_size);
> +
> + if (out)
> + fill_xfer_fifo(regs, buf, xfer_len);
> +
> + ret = exec_sync_hwseq_xfer(regs, cycle, offset, xfer_len);
> + if (ret)
> + return ret;
> +
> + if (!out)
> + drain_xfer_fifo(regs, buf, xfer_len);
> +
> + offset += xfer_len;
> + buf += xfer_len;
> + len -= xfer_len;
> + }
> +
> + return 0;
> +}
> +
> +static int ich_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
> +{
> + struct udevice *bus = dev_get_parent(slave->dev);
> + struct ich_spi_platdata *plat = dev_get_platdata(bus);
> + int ret;
> +
> + bootstage_start(BOOTSTAGE_ID_ACCUM_SPI, "fast_spi");
> + if (plat->hwseq)
> + ret = ich_spi_exec_op_hwseq(slave, op);
> + else
> + ret = ich_spi_exec_op_swseq(slave, op);
> + bootstage_accum(BOOTSTAGE_ID_ACCUM_SPI);
> +
> + return ret;
> +}
> +
> static int ich_spi_adjust_size(struct spi_slave *slave, struct spi_mem_op *op)
> {
> unsigned int page_offset;
> @@ -585,9 +780,11 @@ static int ich_spi_child_pre_probe(struct udevice *dev)
>
> /*
> * Yes this controller can only write a small number of bytes at
> - * once! The limit is typically 64 bytes.
> + * once! The limit is typically 64 bytes. For hardware sequencing a
> + * a loop is used to get around this.
> */
> - slave->max_write_size = priv->databytes;
> + if (!plat->hwseq)
> + slave->max_write_size = priv->databytes;
> /*
> * ICH 7 SPI controller only supports array read command
> * and byte program command for SST flash
> @@ -606,10 +803,12 @@ static int ich_spi_ofdata_to_platdata(struct udevice *dev)
> plat->ich_version = dev_get_driver_data(dev);
> plat->lockdown = dev_read_bool(dev, "intel,spi-lock-down");
> pch_get_spi_base(dev->parent, &plat->mmio_base);
> + plat->hwseq = dev_read_u32_default(dev, "intel,hardware-seq", 0);
> #else
> plat->ich_version = ICHV_APL;
> plat->mmio_base = plat->dtplat.early_regs[0];
> plat->bdf = pci_x86_ofplat_get_devfn(plat->dtplat.reg[0]);
> + plat->hwseq = plat->dtplat.intel_hardware_seq;
> #endif
> debug("%s: mmio_base=%lx\n", __func__, plat->mmio_base);
>
> diff --git a/drivers/spi/ich.h b/drivers/spi/ich.h
> index 623b2c547a6..c7cf37b9321 100644
> --- a/drivers/spi/ich.h
> +++ b/drivers/spi/ich.h
> @@ -163,6 +163,45 @@ struct spi_trans {
>
> #define ICH_BOUNDARY 0x1000
>
> +#define HSFSTS_FDBC_SHIFT 24
> +#define HSFSTS_FDBC_MASK (0x3f << HSFSTS_FDBC_SHIFT)
> +#define HSFSTS_WET BIT(21)
> +#define HSFSTS_FCYCLE_SHIFT 17
> +#define HSFSTS_FCYCLE_MASK (0xf << HSFSTS_FCYCLE_SHIFT)
> +
> +/* Supported flash cycle types */
> +enum hsfsts_cycle_t {
> + HSFSTS_CYCLE_READ = 0,
> + HSFSTS_CYCLE_WRITE = 2,
> + HSFSTS_CYCLE_4K_ERASE,
> + HSFSTS_CYCLE_64K_ERASE,
> + HSFSTS_CYCLE_RDSFDP,
> + HSFSTS_CYCLE_RDID,
> + HSFSTS_CYCLE_WR_STATUS,
> + HSFSTS_CYCLE_RD_STATUS,
> +};
> +
> +#define HSFSTS_FGO BIT(16)
> +#define HSFSTS_FLOCKDN BIT(15)
> +#define HSFSTS_FDV BIT(14)
> +#define HSFSTS_FDOPSS BIT(13)
> +#define HSFSTS_WRSDIS BIT(11)
> +#define HSFSTS_SAF_CE BIT(8)
> +#define HSFSTS_SAF_ACTIVE BIT(7)
> +#define HSFSTS_SAF_LE BIT(6)
> +#define HSFSTS_SCIP BIT(5)
> +#define HSFSTS_SAF_DLE BIT(4)
> +#define HSFSTS_SAF_ERROR BIT(3)
> +#define HSFSTS_AEL BIT(2)
> +#define HSFSTS_FCERR BIT(1)
> +#define HSFSTS_FDONE BIT(0)
> +#define HSFSTS_W1C_BITS 0xff
> +
> +/* Maximum bytes of data that can fit in FDATAn (0x10) registers */
> +#define SPIBAR_FDATA_FIFO_SIZE 0x40
> +
> +#define SPIBAR_HWSEQ_XFER_TIMEOUT_MS 5000
> +
> enum ich_version {
> ICHV_7,
> ICHV_9,
> --
Regards,
Bin
More information about the U-Boot
mailing list