From mboxrd@z Thu Jan 1 00:00:00 1970 Delivery-date: Mon, 28 Mar 2022 11:45:15 +0200 Received: from metis.ext.pengutronix.de ([2001:67c:670:201:290:27ff:fe1d:cc33]) by lore.white.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1nYlvz-008rC0-Fq for lore@lore.pengutronix.de; Mon, 28 Mar 2022 11:45:15 +0200 Received: from bombadil.infradead.org ([2607:7c80:54:e::133]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nYlw0-00070V-Jh for lore@pengutronix.de; Mon, 28 Mar 2022 11:45:14 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:From:In-Reply-To:MIME-Version: References:Message-ID:Subject:Cc:To:Date:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=FSIFzzJKeHm93I+mROMOgxsaYk5fxeV6AUY1l916aNg=; b=pAiyydMDG0qRrSVvagfEbtFHpx F28AseSGHvT688RAtc2ySVbGGAZwL0i9M1mFGFqzLPFegRR/+WTya6zLJ/Y10t0Ohrr5y/tfVy/sW kZyoFAecdTA2okVCe36frhNrQM+rY0vaFkW/kw/9O5kNq14HS1nudFwRntYGHweTXq6fMNDRPGMON Tl3q6MMdOWgcWOumJnpQl2Qr+2AqICt3R56OSAF7dKFLI3gXMSH0mwn3kolmsoi8cqQ5t1TIdulQn /0YYcu3o/2oT6SFw6M8ev8Rmczq7ZwXhlPdT0qcujITfZSRQhQ1XfEprAwXCY54CI6guYcSO45S+M 2+072tkg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nYluO-00841X-LB; Mon, 28 Mar 2022 09:43:32 +0000 Received: from metis.ext.pengutronix.de ([2001:67c:670:201:290:27ff:fe1d:cc33]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nYluI-00840x-51 for barebox@lists.infradead.org; Mon, 28 Mar 2022 09:43:28 +0000 Received: from ptx.hi.pengutronix.de ([2001:67c:670:100:1d::c0]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nYluE-0006tK-Fx; Mon, 28 Mar 2022 11:43:22 +0200 Received: from sha by ptx.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1nYluD-0001rL-VH; Mon, 28 Mar 2022 11:43:21 +0200 Date: Mon, 28 Mar 2022 11:43:21 +0200 To: Ahmad Fatoum Cc: barebox@lists.infradead.org Message-ID: <20220328094321.GQ12181@pengutronix.de> References: <20220319072410.1310723-1-ahmad@a3f.at> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20220319072410.1310723-1-ahmad@a3f.at> X-Sent-From: Pengutronix Hildesheim X-URL: http://www.pengutronix.de/ X-IRC: #ptxdist @freenode X-Accept-Language: de,en X-Accept-Content-Type: text/plain X-Uptime: 11:06:33 up 107 days, 17:52, 86 users, load average: 0.14, 0.30, 0.27 User-Agent: Mutt/1.10.1 (2018-07-13) From: Sascha Hauer X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220328_024326_574649_3358771C X-CRM114-Status: GOOD ( 43.54 ) X-BeenThere: barebox@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "barebox" X-SA-Exim-Connect-IP: 2607:7c80:54:e::133 X-SA-Exim-Mail-From: barebox-bounces+lore=pengutronix.de@lists.infradead.org X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on metis.ext.pengutronix.de X-Spam-Level: X-Spam-Status: No, score=-4.7 required=4.0 tests=AWL,BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE,SPF_NONE, T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.2 Subject: Re: [PATCH 1/4] spi: Port SiFive SPI controller driver X-SA-Exim-Version: 4.2.1 (built Wed, 08 May 2019 21:11:16 +0000) X-SA-Exim-Scanned: Yes (on metis.ext.pengutronix.de) On Sat, Mar 19, 2022 at 08:24:07AM +0100, 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 > --- > drivers/spi/Kconfig | 6 + > drivers/spi/Makefile | 1 + > drivers/spi/spi-sifive.c | 576 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 583 insertions(+) > create mode 100644 drivers/spi/spi-sifive.c > > 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..3cb613285603 > --- /dev/null > +++ b/drivers/spi/spi-sifive.c > @@ -0,0 +1,576 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright 2018 SiFive, Inc. > + * Copyright 2019 Bhargav Shah > + * > + * SiFive SPI controller driver (master mode only) > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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); > +} > + > +#define BUILD_WAIT_FOR_BIT(sfx, type, read) \ > + \ > +static inline int wait_for_bit_##sfx(const void *reg, \ > + const type mask, \ > + const bool set, \ > + const unsigned int timeout_ms, \ > + const bool breakable) \ > +{ \ > + type val; \ > + unsigned long start = get_timer(0); \ > + \ > + while (1) { \ > + val = read(reg); \ > + \ > + if (!set) \ > + val = ~val; \ > + \ > + if ((val & mask) == mask) \ > + return 0; \ > + \ > + if (get_timer(start) > timeout_ms) \ > + break; \ > + \ > + if (breakable && ctrlc()) { \ > + puts("Abort\n"); \ > + return -EINTR; \ > + } \ > + \ > + udelay(1); \ > + WATCHDOG_RESET(); \ > + } \ > + \ > + debug("%s: Timeout (reg=%p mask=%x wait_set=%i)\n", __func__, \ > + reg, mask, set); \ > + \ > + return -ETIMEDOUT; \ > +} This is unused and would not be usable in barebox. > + > +static int sifive_spi_transfer_one(struct spi_device *spi_dev, unsigned int bitlen, > + const void *dout, void *din, unsigned long flags) > +{ > + struct sifive_spi *spi = to_sifive_spi(spi_dev->controller); > + const u8 *tx_ptr = dout; > + u8 *rx_ptr = din; > + u32 remaining_len; > + int ret; > + > + if (flags & SPI_XFER_BEGIN) { > + sifive_spi_prep_device(spi, spi_dev); > + sifive_spi_set_cs(spi, spi_dev); > + } It would be easier to move preparation and cs handling out of this function and let the callers handle it. > + > + sifive_spi_prep_transfer(spi, spi_dev, rx_ptr); > + > + remaining_len = bitlen / 8; All callers call sifive_spi_transfer_one() with nbytes * 8, given that we need the length in bytes here we could pass that directly. > + > + while (remaining_len) { > + unsigned int n_words = min(remaining_len, 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; > + } > + > + remaining_len -= n_words; > + } > + > + if (flags & SPI_XFER_END) > + sifive_spi_clear_cs(spi); > + > + return 0; > +} > + > +static int sifive_spi_transfer(struct spi_device *spi_dev, struct spi_message *msg) > +{ > + struct spi_controller *ctlr = spi_dev->controller; > + struct spi_transfer *t, *t_first, *t_last = NULL; > + unsigned long flags; > + int ret = 0; > + > + if (list_empty(&msg->transfers)) > + return 0; > + > + t_first = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list); > + t_last = list_last_entry(&msg->transfers, struct spi_transfer, transfer_list); > + > + msg->actual_length = 0; > + > + 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); > + flags = 0; > + if (t == t_first) > + flags |= SPI_XFER_BEGIN; > + if (t == t_last) > + flags |= SPI_XFER_END; > + ret = sifive_spi_transfer_one(spi_dev, t->len * 8, > + t->tx_buf, t->rx_buf, flags); > + if (ret < 0) > + return ret; > + msg->actual_length += t->len; > + } > + dev_dbg(ctlr->dev, "transfer done actual_length=%i\n", msg->actual_length); > + > + 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); > + unsigned long flags = SPI_XFER_BEGIN; > + u8 opcode = op->cmd.opcode; > + unsigned int pos = 0; > + const void *tx_buf = NULL; > + void *rx_buf = NULL; > + int op_len, i; > + u8 *op_buf; > + int ret; > + > + if (!op->addr.nbytes && !op->dummy.nbytes && !op->data.nbytes) > + flags |= SPI_XFER_END; > + > + spi->fmt_proto = op->cmd.buswidth; > + > + /* send the opcode */ > + ret = sifive_spi_transfer_one(spi_dev, 8, (void *)&opcode, NULL, flags); > + if (ret < 0) { > + dev_err(dev, "failed to xfer opcode\n"); > + return ret; > + } > + > + op_len = op->addr.nbytes + op->dummy.nbytes; > + > + op_buf = malloc(op_len); > + if (!op_buf) > + return -ENOMEM; > + > + /* send the addr + dummy */ > + if (op->addr.nbytes) { > + /* fill address */ > + for (i = 0; i < op->addr.nbytes; i++) > + op_buf[pos + i] = op->addr.val >> > + (8 * (op->addr.nbytes - i - 1)); > + > + pos += op->addr.nbytes; pos was 0 before, so now pos is op->addr.nbytes. You could use op->addr.nbytes directly and drop pos altogether. Sascha -- 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@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox