From mboxrd@z Thu Jan 1 00:00:00 1970 From: Bin Meng Date: Tue, 19 Nov 2019 22:36:53 +0800 Subject: [U-Boot] [PATCH v3 084/108] spi: ich: Support hardware sequencing In-Reply-To: <20191021033913.220758-79-sjg@chromium.org> References: <20191021033913.220758-22-sjg@chromium.org> <20191021033913.220758-79-sjg@chromium.org> Message-ID: List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Hi Simon, On Mon, Oct 21, 2019 at 11:40 AM Simon Glass wrote: > > Apollolake (APL) only supports hardware sequencing. Add support for this > into the SPI driver, as an option. > > Signed-off-by: Simon Glass > --- > > 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 > #include > #include > +#include > #include > +#include > #include > > #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