On Tue, Apr 26, 2022 at 08:46:03AM +0200, Ahmad Fatoum wrote: > Import the U-Boot v2022.04-rc2 driver to enable access to SPI flash and > SD over SPI on the HiFive Unleashed. Tested with QEMU. > > Signed-off-by: Ahmad Fatoum <ahmad@xxxxxx> > --- > v1 -> v2: > - remove unused wait_for_bit (Sascha) > - move prep and CS handling out of transfer_one into callers (Sascha) > - Use nbytes instead of bitlen as code is bytewise anyway (Sascha) > - remove superfluous pos variable (Sascha) > --- > drivers/spi/Kconfig | 6 + > drivers/spi/Makefile | 1 + > drivers/spi/spi-sifive.c | 521 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 528 insertions(+) > create mode 100644 drivers/spi/spi-sifive.c Applied, thanks Sascha > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index 7df7561718c2..8935feb97b99 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -124,6 +124,12 @@ config SPI_NXP_FLEXSPI > This controller does not support generic SPI messages and only > supports the high-level SPI memory interface. > > +config SPI_SIFIVE > + tristate "SiFive SPI controller" > + depends on SOC_SIFIVE || COMPILE_TEST > + help > + This exposes the SPI controller IP from SiFive. > + > endif > > endmenu > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index 64c8e2645a94..3455eea86988 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -17,3 +17,4 @@ obj-$(CONFIG_DRIVER_SPI_DSPI) += dspi_spi.o > obj-$(CONFIG_SPI_ZYNQ_QSPI) += zynq_qspi.o > obj-$(CONFIG_SPI_NXP_FLEXSPI) += spi-nxp-fspi.o > obj-$(CONFIG_DRIVER_SPI_STM32) += stm32_spi.o > +obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o > diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c > new file mode 100644 > index 000000000000..713bcc0c3bd8 > --- /dev/null > +++ b/drivers/spi/spi-sifive.c > @@ -0,0 +1,521 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright 2018 SiFive, Inc. > + * Copyright 2019 Bhargav Shah <bhargavshah1988@xxxxxxxxx> > + * > + * SiFive SPI controller driver (master mode only) > + */ > + > +#include <common.h> > +#include <linux/clk.h> > +#include <driver.h> > +#include <init.h> > +#include <errno.h> > +#include <linux/reset.h> > +#include <spi/spi.h> > +#include <linux/spi/spi-mem.h> > +#include <linux/bitops.h> > +#include <clock.h> > +#include <gpio.h> > +#include <of_gpio.h> > +#include <linux/bitfield.h> > +#include <linux/iopoll.h> > +#include <linux/log2.h> > + > +#define SIFIVE_SPI_MAX_CS 32 > + > +#define SIFIVE_SPI_DEFAULT_DEPTH 8 > +#define SIFIVE_SPI_DEFAULT_BITS 8 > + > +/* register offsets */ > +#define SIFIVE_SPI_REG_SCKDIV 0x00 /* Serial clock divisor */ > +#define SIFIVE_SPI_REG_SCKMODE 0x04 /* Serial clock mode */ > +#define SIFIVE_SPI_REG_CSID 0x10 /* Chip select ID */ > +#define SIFIVE_SPI_REG_CSDEF 0x14 /* Chip select default */ > +#define SIFIVE_SPI_REG_CSMODE 0x18 /* Chip select mode */ > +#define SIFIVE_SPI_REG_DELAY0 0x28 /* Delay control 0 */ > +#define SIFIVE_SPI_REG_DELAY1 0x2c /* Delay control 1 */ > +#define SIFIVE_SPI_REG_FMT 0x40 /* Frame format */ > +#define SIFIVE_SPI_REG_TXDATA 0x48 /* Tx FIFO data */ > +#define SIFIVE_SPI_REG_RXDATA 0x4c /* Rx FIFO data */ > +#define SIFIVE_SPI_REG_TXMARK 0x50 /* Tx FIFO watermark */ > +#define SIFIVE_SPI_REG_RXMARK 0x54 /* Rx FIFO watermark */ > +#define SIFIVE_SPI_REG_FCTRL 0x60 /* SPI flash interface control */ > +#define SIFIVE_SPI_REG_FFMT 0x64 /* SPI flash instruction format */ > +#define SIFIVE_SPI_REG_IE 0x70 /* Interrupt Enable Register */ > +#define SIFIVE_SPI_REG_IP 0x74 /* Interrupt Pendings Register */ > + > +/* sckdiv bits */ > +#define SIFIVE_SPI_SCKDIV_DIV_MASK 0xfffU > + > +/* sckmode bits */ > +#define SIFIVE_SPI_SCKMODE_PHA BIT(0) > +#define SIFIVE_SPI_SCKMODE_POL BIT(1) > +#define SIFIVE_SPI_SCKMODE_MODE_MASK (SIFIVE_SPI_SCKMODE_PHA | \ > + SIFIVE_SPI_SCKMODE_POL) > + > +/* csmode bits */ > +#define SIFIVE_SPI_CSMODE_MODE_AUTO 0U > +#define SIFIVE_SPI_CSMODE_MODE_HOLD 2U > +#define SIFIVE_SPI_CSMODE_MODE_OFF 3U > + > +/* delay0 bits */ > +#define SIFIVE_SPI_DELAY0_CSSCK(x) ((u32)(x)) > +#define SIFIVE_SPI_DELAY0_CSSCK_MASK 0xffU > +#define SIFIVE_SPI_DELAY0_SCKCS(x) ((u32)(x) << 16) > +#define SIFIVE_SPI_DELAY0_SCKCS_MASK (0xffU << 16) > + > +/* delay1 bits */ > +#define SIFIVE_SPI_DELAY1_INTERCS(x) ((u32)(x)) > +#define SIFIVE_SPI_DELAY1_INTERCS_MASK 0xffU > +#define SIFIVE_SPI_DELAY1_INTERXFR(x) ((u32)(x) << 16) > +#define SIFIVE_SPI_DELAY1_INTERXFR_MASK (0xffU << 16) > + > +/* fmt bits */ > +#define SIFIVE_SPI_FMT_PROTO_SINGLE 0U > +#define SIFIVE_SPI_FMT_PROTO_DUAL 1U > +#define SIFIVE_SPI_FMT_PROTO_QUAD 2U > +#define SIFIVE_SPI_FMT_PROTO_MASK 3U > +#define SIFIVE_SPI_FMT_ENDIAN BIT(2) > +#define SIFIVE_SPI_FMT_DIR BIT(3) > +#define SIFIVE_SPI_FMT_LEN(x) ((u32)(x) << 16) > +#define SIFIVE_SPI_FMT_LEN_MASK (0xfU << 16) > + > +/* txdata bits */ > +#define SIFIVE_SPI_TXDATA_DATA_MASK 0xffU > +#define SIFIVE_SPI_TXDATA_FULL BIT(31) > + > +/* rxdata bits */ > +#define SIFIVE_SPI_RXDATA_DATA_MASK 0xffU > +#define SIFIVE_SPI_RXDATA_EMPTY BIT(31) > + > +/* ie and ip bits */ > +#define SIFIVE_SPI_IP_TXWM BIT(0) > +#define SIFIVE_SPI_IP_RXWM BIT(1) > + > +/* format protocol */ > +#define SIFIVE_SPI_PROTO_QUAD 4 /* 4 lines I/O protocol transfer */ > +#define SIFIVE_SPI_PROTO_DUAL 2 /* 2 lines I/O protocol transfer */ > +#define SIFIVE_SPI_PROTO_SINGLE 1 /* 1 line I/O protocol transfer */ > + > +#define SPI_XFER_BEGIN 0x01 /* Assert CS before transfer */ > +#define SPI_XFER_END 0x02 /* Deassert CS after transfer */ > + > +struct sifive_spi { > + struct spi_controller ctlr; > + void __iomem *regs; /* base address of the registers */ > + u32 fifo_depth; > + u32 bits_per_word; > + u32 cs_inactive; /* Level of the CS pins when inactive*/ > + u32 freq; > + u8 fmt_proto; > +}; > + > +static inline struct sifive_spi *to_sifive_spi(struct spi_controller *ctlr) > +{ > + return container_of(ctlr, struct sifive_spi, ctlr); > +} > + > +static void sifive_spi_prep_device(struct sifive_spi *spi, > + struct spi_device *spi_dev) > +{ > + /* Update the chip select polarity */ > + if (spi_dev->mode & SPI_CS_HIGH) > + spi->cs_inactive &= ~BIT(spi_dev->chip_select); > + else > + spi->cs_inactive |= BIT(spi_dev->chip_select); > + writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF); > + > + /* Select the correct device */ > + writel(spi_dev->chip_select, spi->regs + SIFIVE_SPI_REG_CSID); > +} > + > +static void sifive_spi_set_cs(struct sifive_spi *spi, > + struct spi_device *spi_dev) > +{ > + u32 cs_mode = SIFIVE_SPI_CSMODE_MODE_HOLD; > + > + if (spi_dev->mode & SPI_CS_HIGH) > + cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO; > + > + writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE); > +} > + > +static void sifive_spi_clear_cs(struct sifive_spi *spi) > +{ > + writel(SIFIVE_SPI_CSMODE_MODE_AUTO, spi->regs + SIFIVE_SPI_REG_CSMODE); > +} > + > +static void sifive_spi_prep_transfer(struct sifive_spi *spi, > + struct spi_device *spi_dev, > + u8 *rx_ptr) > +{ > + u32 cr; > + > + /* Modify the SPI protocol mode */ > + cr = readl(spi->regs + SIFIVE_SPI_REG_FMT); > + > + /* Bits per word ? */ > + cr &= ~SIFIVE_SPI_FMT_LEN_MASK; > + cr |= SIFIVE_SPI_FMT_LEN(spi->bits_per_word); > + > + /* LSB first? */ > + cr &= ~SIFIVE_SPI_FMT_ENDIAN; > + if (spi_dev->mode & SPI_LSB_FIRST) > + cr |= SIFIVE_SPI_FMT_ENDIAN; > + > + /* Number of wires ? */ > + cr &= ~SIFIVE_SPI_FMT_PROTO_MASK; > + switch (spi->fmt_proto) { > + case SIFIVE_SPI_PROTO_QUAD: > + cr |= SIFIVE_SPI_FMT_PROTO_QUAD; > + break; > + case SIFIVE_SPI_PROTO_DUAL: > + cr |= SIFIVE_SPI_FMT_PROTO_DUAL; > + break; > + default: > + cr |= SIFIVE_SPI_FMT_PROTO_SINGLE; > + break; > + } > + > + /* SPI direction in/out ? */ > + cr &= ~SIFIVE_SPI_FMT_DIR; > + if (!rx_ptr) > + cr |= SIFIVE_SPI_FMT_DIR; > + > + writel(cr, spi->regs + SIFIVE_SPI_REG_FMT); > +} > + > +static void sifive_spi_rx(struct sifive_spi *spi, u8 *rx_ptr) > +{ > + u32 data; > + > + do { > + data = readl(spi->regs + SIFIVE_SPI_REG_RXDATA); > + } while (data & SIFIVE_SPI_RXDATA_EMPTY); > + > + if (rx_ptr) > + *rx_ptr = data & SIFIVE_SPI_RXDATA_DATA_MASK; > +} > + > +static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr) > +{ > + u32 data; > + u8 tx_data = (tx_ptr) ? *tx_ptr & SIFIVE_SPI_TXDATA_DATA_MASK : > + SIFIVE_SPI_TXDATA_DATA_MASK; > + > + do { > + data = readl(spi->regs + SIFIVE_SPI_REG_TXDATA); > + } while (data & SIFIVE_SPI_TXDATA_FULL); > + > + writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA); > +} > + > +static int sifive_spi_wait(struct sifive_spi *spi, u32 mask) > +{ > + u32 val; > + > + return readl_poll_timeout(spi->regs + SIFIVE_SPI_REG_IP, val, > + (val & mask) == mask, 100 * USEC_PER_MSEC); > +} > + > +static int sifive_spi_transfer_one(struct spi_device *spi_dev, unsigned int nbytes, > + const void *dout, void *din) > +{ > + struct sifive_spi *spi = to_sifive_spi(spi_dev->controller); > + const u8 *tx_ptr = dout; > + u8 *rx_ptr = din; > + int ret; > + > + sifive_spi_prep_transfer(spi, spi_dev, rx_ptr); > + > + while (nbytes) { > + unsigned int n_words = min(nbytes, spi->fifo_depth); > + unsigned int tx_words, rx_words; > + > + /* Enqueue n_words for transmission */ > + for (tx_words = 0; tx_words < n_words; tx_words++) { > + if (!tx_ptr) > + sifive_spi_tx(spi, NULL); > + else > + sifive_spi_tx(spi, tx_ptr++); > + } > + > + if (rx_ptr) { > + /* Wait for transmission + reception to complete */ > + writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_RXMARK); > + ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_RXWM); > + if (ret) > + return ret; > + > + /* Read out all the data from the RX FIFO */ > + for (rx_words = 0; rx_words < n_words; rx_words++) > + sifive_spi_rx(spi, rx_ptr++); > + } else { > + /* Wait for transmission to complete */ > + ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_TXWM); > + if (ret) > + return ret; > + } > + > + nbytes -= n_words; > + } > + > + return 0; > +} > + > +static int sifive_spi_transfer(struct spi_device *spi_dev, struct spi_message *msg) > +{ > + struct spi_controller *ctlr = spi_dev->controller; > + struct sifive_spi *spi = to_sifive_spi(ctlr); > + struct spi_transfer *t; > + int ret = 0; > + > + if (list_empty(&msg->transfers)) > + return 0; > + > + msg->actual_length = 0; > + > + sifive_spi_prep_device(spi, spi_dev); > + sifive_spi_set_cs(spi, spi_dev); > + > + dev_dbg(ctlr->dev, "transfer start actual_length=%i\n", msg->actual_length); > + list_for_each_entry(t, &msg->transfers, transfer_list) { > + dev_dbg(ctlr->dev, " xfer %p: len %u tx %p rx %p\n", > + t, t->len, t->tx_buf, t->rx_buf); > + > + ret = sifive_spi_transfer_one(spi_dev, t->len, > + t->tx_buf, t->rx_buf); > + if (ret < 0) > + goto out; > + msg->actual_length += t->len; > + } > + dev_dbg(ctlr->dev, "transfer done actual_length=%i\n", msg->actual_length); > + > +out: > + sifive_spi_clear_cs(spi); > + return ret; > +} > + > +static int sifive_spi_exec_op(struct spi_mem *mem, > + const struct spi_mem_op *op) > +{ > + struct spi_device *spi_dev = mem->spi; > + struct device_d *dev = &spi_dev->dev; > + struct sifive_spi *spi = spi_controller_get_devdata(spi_dev->controller); > + u8 opcode = op->cmd.opcode; > + int ret; > + > + spi->fmt_proto = op->cmd.buswidth; > + > + sifive_spi_prep_device(spi, spi_dev); > + sifive_spi_set_cs(spi, spi_dev); > + > + /* send the opcode */ > + ret = sifive_spi_transfer_one(spi_dev, 1, &opcode, NULL); > + if (ret < 0) { > + dev_err(dev, "failed to xfer opcode\n"); > + goto out; > + } > + > + if (!op->addr.nbytes && !op->data.nbytes) > + goto out; > + > + /* send the addr + dummy */ > + if (op->addr.nbytes) { > + int i, op_len = op->addr.nbytes + op->dummy.nbytes; > + u8 *op_buf; > + > + op_buf = malloc(op_len); > + if (!op_buf) { > + ret = -ENOMEM; > + goto out; > + } > + > + /* fill address */ > + for (i = 0; i < op->addr.nbytes; i++) > + op_buf[i] = op->addr.val >> > + (8 * (op->addr.nbytes - i - 1)); > + > + /* fill dummy */ > + memset(op_buf + op->addr.nbytes, 0xff, op->dummy.nbytes); > + > + spi->fmt_proto = op->addr.buswidth; > + > + ret = sifive_spi_transfer_one(spi_dev, op_len, op_buf, NULL); > + free(op_buf); > + if (ret < 0) { > + dev_err(dev, "failed to xfer addr + dummy\n"); > + goto out; > + } > + } > + > + /* send/received the data */ > + if (op->data.nbytes) { > + const void *tx_buf = NULL; > + void *rx_buf = NULL; > + > + if (op->data.dir == SPI_MEM_DATA_IN) > + rx_buf = op->data.buf.in; > + else > + tx_buf = op->data.buf.out; > + > + spi->fmt_proto = op->data.buswidth; > + > + ret = sifive_spi_transfer_one(spi_dev, op->data.nbytes, > + tx_buf, rx_buf); > + if (ret) { > + dev_err(dev, "failed to xfer data\n"); > + goto out; > + } > + } > + > +out: > + sifive_spi_clear_cs(spi); > + return ret; > +} > + > +static void sifive_spi_set_speed(struct sifive_spi *spi, uint speed) > +{ > + u32 scale; > + > + if (speed > spi->freq) > + speed = spi->freq; > + > + /* Cofigure max speed */ > + scale = (DIV_ROUND_UP(spi->freq >> 1, speed) - 1) > + & SIFIVE_SPI_SCKDIV_DIV_MASK; > + writel(scale, spi->regs + SIFIVE_SPI_REG_SCKDIV); > +} > + > +static void sifive_spi_set_mode(struct sifive_spi *spi, uint mode) > +{ > + u32 cr; > + > + /* Switch clock mode bits */ > + cr = readl(spi->regs + SIFIVE_SPI_REG_SCKMODE) & > + ~SIFIVE_SPI_SCKMODE_MODE_MASK; > + if (mode & SPI_CPHA) > + cr |= SIFIVE_SPI_SCKMODE_PHA; > + if (mode & SPI_CPOL) > + cr |= SIFIVE_SPI_SCKMODE_POL; > + > + writel(cr, spi->regs + SIFIVE_SPI_REG_SCKMODE); > +} > + > +static int sifive_spi_setup(struct spi_device *spi_dev) > +{ > + struct sifive_spi *spi = to_sifive_spi(spi_dev->controller); > + > + sifive_spi_set_mode(spi, spi_dev->mode); > + sifive_spi_set_speed(spi, spi_dev->max_speed_hz); > + > + return 0; > +} > + > +static void sifive_spi_init_hw(struct sifive_spi *spi) > +{ > + struct device_d *dev = spi->ctlr.dev; > + u32 cs_bits; > + > + /* probe the number of CS lines */ > + spi->cs_inactive = readl(spi->regs + SIFIVE_SPI_REG_CSDEF); > + writel(0xffffffffU, spi->regs + SIFIVE_SPI_REG_CSDEF); > + cs_bits = readl(spi->regs + SIFIVE_SPI_REG_CSDEF); > + writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF); > + if (!cs_bits) { > + dev_warn(dev, "Could not auto probe CS lines\n"); > + return; > + } > + > + spi->ctlr.num_chipselect = ilog2(cs_bits) + 1; > + if (spi->ctlr.num_chipselect > SIFIVE_SPI_MAX_CS) { > + dev_warn(dev, "Invalid number of spi slaves\n"); > + return; > + } > + > + /* Watermark interrupts are disabled by default */ > + writel(0, spi->regs + SIFIVE_SPI_REG_IE); > + > + /* Default watermark FIFO threshold values */ > + writel(1, spi->regs + SIFIVE_SPI_REG_TXMARK); > + writel(0, spi->regs + SIFIVE_SPI_REG_RXMARK); > + > + /* Set CS/SCK Delays and Inactive Time to defaults */ > + writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1), > + spi->regs + SIFIVE_SPI_REG_DELAY0); > + writel(SIFIVE_SPI_DELAY1_INTERCS(1) | SIFIVE_SPI_DELAY1_INTERXFR(0), > + spi->regs + SIFIVE_SPI_REG_DELAY1); > + > + /* Exit specialized memory-mapped SPI flash mode */ > + writel(0, spi->regs + SIFIVE_SPI_REG_FCTRL); > +} > + > +static const struct spi_controller_mem_ops sifive_spi_mem_ops = { > + .exec_op = sifive_spi_exec_op, > +}; > + > +static void sifive_spi_dt_probe(struct sifive_spi *spi) > +{ > + struct device_node *node = spi->ctlr.dev->device_node; > + > + spi->fifo_depth = SIFIVE_SPI_DEFAULT_DEPTH; > + of_property_read_u32(node, "sifive,fifo-depth", &spi->fifo_depth); > + > + spi->bits_per_word = SIFIVE_SPI_DEFAULT_BITS; > + of_property_read_u32(node, "sifive,max-bits-per-word", &spi->bits_per_word); > +} > + > +static int sifive_spi_probe(struct device_d *dev) > +{ > + struct sifive_spi *spi; > + struct resource *iores; > + struct spi_controller *ctlr; > + struct clk *clkdev; > + > + iores = dev_request_mem_resource(dev, 0); > + if (IS_ERR(iores)) > + return PTR_ERR(iores); > + > + spi = xzalloc(sizeof(*spi)); > + > + spi->regs = IOMEM(iores->start); > + if (!spi->regs) > + return -ENODEV; > + > + ctlr = &spi->ctlr; > + ctlr->dev = dev; > + > + ctlr->setup = sifive_spi_setup; > + ctlr->transfer = sifive_spi_transfer; > + ctlr->mem_ops = &sifive_spi_mem_ops; > + > + ctlr->bus_num = -1; > + > + spi_controller_set_devdata(ctlr, spi); > + > + sifive_spi_dt_probe(spi); > + > + clkdev = clk_get(dev, NULL); > + if (IS_ERR(clkdev)) > + return PTR_ERR(clkdev); > + > + spi->freq = clk_get_rate(clkdev); > + > + /* init the sifive spi hw */ > + sifive_spi_init_hw(spi); > + > + return spi_register_master(ctlr); > +} > + > +static const struct of_device_id sifive_spi_ids[] = { > + { .compatible = "sifive,spi0" }, > + { /* sentinel */ } > +}; > + > +static struct driver_d sifive_spi_driver = { > + .name = "sifive_spi", > + .probe = sifive_spi_probe, > + .of_compatible = sifive_spi_ids, > +}; > +coredevice_platform_driver(sifive_spi_driver); > -- > 2.34.1 > > > _______________________________________________ > barebox mailing list > barebox@xxxxxxxxxxxxxxxxxxx > http://lists.infradead.org/mailman/listinfo/barebox > -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox