| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2022 Microchip Technology Inc.
- * Padmarao Begari <padmarao.begari@microchip.com>
- * Naga Sureshkumar Relli <nagasuresh.relli@microchip.com>
- */
- #include <common.h>
- #include <clk.h>
- #include <dm.h>
- #include <log.h>
- #include <malloc.h>
- #include <spi.h>
- #include <spi-mem.h>
- #include <asm/io.h>
- #include <linux/bitops.h>
- #include <linux/delay.h>
- #include <linux/types.h>
- #include <linux/sizes.h>
- DECLARE_GLOBAL_DATA_PTR;
- /*
- * QSPI Control register mask defines
- */
- #define CONTROL_ENABLE BIT(0)
- #define CONTROL_MASTER BIT(1)
- #define CONTROL_XIP BIT(2)
- #define CONTROL_XIPADDR BIT(3)
- #define CONTROL_CLKIDLE BIT(10)
- #define CONTROL_SAMPLE_MASK GENMASK(12, 11)
- #define CONTROL_MODE0 BIT(13)
- #define CONTROL_MODE12_MASK GENMASK(15, 14)
- #define CONTROL_MODE12_EX_RO BIT(14)
- #define CONTROL_MODE12_EX_RW BIT(15)
- #define CONTROL_MODE12_FULL GENMASK(15, 14)
- #define CONTROL_FLAGSX4 BIT(16)
- #define CONTROL_CLKRATE_MASK GENMASK(27, 24)
- #define CONTROL_CLKRATE_SHIFT 24
- /*
- * QSPI Frames register mask defines
- */
- #define FRAMES_TOTALBYTES_MASK GENMASK(15, 0)
- #define FRAMES_CMDBYTES_MASK GENMASK(24, 16)
- #define FRAMES_CMDBYTES_SHIFT 16
- #define FRAMES_SHIFT 25
- #define FRAMES_IDLE_MASK GENMASK(29, 26)
- #define FRAMES_IDLE_SHIFT 26
- #define FRAMES_FLAGBYTE BIT(30)
- #define FRAMES_FLAGWORD BIT(31)
- /*
- * QSPI Interrupt Enable register mask defines
- */
- #define IEN_TXDONE BIT(0)
- #define IEN_RXDONE BIT(1)
- #define IEN_RXAVAILABLE BIT(2)
- #define IEN_TXAVAILABLE BIT(3)
- #define IEN_RXFIFOEMPTY BIT(4)
- #define IEN_TXFIFOFULL BIT(5)
- /*
- * QSPI Status register mask defines
- */
- #define STATUS_TXDONE BIT(0)
- #define STATUS_RXDONE BIT(1)
- #define STATUS_RXAVAILABLE BIT(2)
- #define STATUS_TXAVAILABLE BIT(3)
- #define STATUS_RXFIFOEMPTY BIT(4)
- #define STATUS_TXFIFOFULL BIT(5)
- #define STATUS_READY BIT(7)
- #define STATUS_FLAGSX4 BIT(8)
- #define STATUS_MASK GENMASK(8, 0)
- #define BYTESUPPER_MASK GENMASK(31, 16)
- #define BYTESLOWER_MASK GENMASK(15, 0)
- #define MAX_DIVIDER 16
- #define MIN_DIVIDER 0
- #define MAX_DATA_CMD_LEN 256
- /* QSPI ready time out value */
- #define TIMEOUT_MS (1000 * 500)
- /*
- * QSPI Register offsets.
- */
- #define REG_CONTROL (0x00)
- #define REG_FRAMES (0x04)
- #define REG_IEN (0x0c)
- #define REG_STATUS (0x10)
- #define REG_DIRECT_ACCESS (0x14)
- #define REG_UPPER_ACCESS (0x18)
- #define REG_RX_DATA (0x40)
- #define REG_TX_DATA (0x44)
- #define REG_X4_RX_DATA (0x48)
- #define REG_X4_TX_DATA (0x4c)
- #define REG_FRAMESUP (0x50)
- /**
- * struct mchp_coreqspi - Defines qspi driver instance
- * @regs: Address of the QSPI controller registers
- * @freq: QSPI Input frequency
- * @txbuf: TX buffer
- * @rxbuf: RX buffer
- * @tx_len: Number of bytes left to transfer
- * @rx_len: Number of bytes left to receive
- */
- struct mchp_coreqspi {
- void __iomem *regs;
- u32 freq;
- u8 *txbuf;
- u8 *rxbuf;
- int tx_len;
- int rx_len;
- };
- static void mchp_coreqspi_init_hw(struct mchp_coreqspi *qspi)
- {
- u32 control;
- control = CONTROL_CLKIDLE | CONTROL_ENABLE;
- writel(control, qspi->regs + REG_CONTROL);
- writel(0, qspi->regs + REG_IEN);
- }
- static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi)
- {
- u32 control, data;
- if (!qspi->rx_len)
- return;
- control = readl(qspi->regs + REG_CONTROL);
- /*
- * Read 4-bytes from the SPI FIFO in single transaction and then read
- * the reamaining data byte wise.
- */
- control |= CONTROL_FLAGSX4;
- writel(control, qspi->regs + REG_CONTROL);
- while (qspi->rx_len >= 4) {
- while (readl(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
- ;
- data = readl(qspi->regs + REG_X4_RX_DATA);
- *(u32 *)qspi->rxbuf = data;
- qspi->rxbuf += 4;
- qspi->rx_len -= 4;
- }
- control &= ~CONTROL_FLAGSX4;
- writel(control, qspi->regs + REG_CONTROL);
- while (qspi->rx_len--) {
- while (readl(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
- ;
- data = readl(qspi->regs + REG_RX_DATA);
- *qspi->rxbuf++ = (data & 0xFF);
- }
- }
- static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
- {
- u32 control, data;
- control = readl(qspi->regs + REG_CONTROL);
- control |= CONTROL_FLAGSX4;
- writel(control, qspi->regs + REG_CONTROL);
- while (qspi->tx_len >= 4) {
- while (readl(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
- ;
- data = *(u32 *)qspi->txbuf;
- qspi->txbuf += 4;
- qspi->tx_len -= 4;
- writel(data, qspi->regs + REG_X4_TX_DATA);
- }
- control &= ~CONTROL_FLAGSX4;
- writel(control, qspi->regs + REG_CONTROL);
- while (qspi->tx_len--) {
- while (readl(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
- ;
- data = *qspi->txbuf++;
- writel(data, qspi->regs + REG_TX_DATA);
- }
- }
- static inline void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi,
- const struct spi_mem_op *op)
- {
- u32 idle_cycles = 0;
- int total_bytes, cmd_bytes, frames, ctrl;
- cmd_bytes = op->cmd.nbytes + op->addr.nbytes;
- total_bytes = cmd_bytes + op->data.nbytes;
- /*
- * As per the coreQSPI IP spec,the number of command and data bytes are
- * controlled by the frames register for each SPI sequence. This supports
- * the SPI flash memory read and writes sequences as below. so configure
- * the cmd and total bytes accordingly.
- * ---------------------------------------------------------------------
- * TOTAL BYTES | CMD BYTES | What happens |
- * ______________________________________________________________________
- * | | |
- * 1 | 1 | The SPI core will transmit a single byte |
- * | | and receive data is discarded |
- * | | |
- * 1 | 0 | The SPI core will transmit a single byte |
- * | | and return a single byte |
- * | | |
- * 10 | 4 | The SPI core will transmit 4 command |
- * | | bytes discarding the receive data and |
- * | | transmits 6 dummy bytes returning the 6 |
- * | | received bytes and return a single byte |
- * | | |
- * 10 | 10 | The SPI core will transmit 10 command |
- * | | |
- * 10 | 0 | The SPI core will transmit 10 command |
- * | | bytes and returning 10 received bytes |
- * ______________________________________________________________________
- */
- if (!(op->data.dir == SPI_MEM_DATA_IN))
- cmd_bytes = total_bytes;
- frames = total_bytes & BYTESUPPER_MASK;
- writel(frames, qspi->regs + REG_FRAMESUP);
- frames = total_bytes & BYTESLOWER_MASK;
- frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT;
- if (op->dummy.buswidth)
- idle_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
- frames |= idle_cycles << FRAMES_IDLE_SHIFT;
- ctrl = readl(qspi->regs + REG_CONTROL);
- if (ctrl & CONTROL_MODE12_MASK)
- frames |= (1 << FRAMES_SHIFT);
- frames |= FRAMES_FLAGWORD;
- writel(frames, qspi->regs + REG_FRAMES);
- }
- static int mchp_coreqspi_wait_for_ready(struct spi_slave *slave)
- {
- struct mchp_coreqspi *qspi = dev_get_priv(slave->dev->parent);
- unsigned long count = 0;
- while (1) {
- if (readl(qspi->regs + REG_STATUS) & STATUS_READY)
- return 0;
- udelay(1);
- count += 1;
- if (count == TIMEOUT_MS)
- return -ETIMEDOUT;
- }
- }
- static int mchp_coreqspi_set_operate_mode(struct mchp_coreqspi *qspi,
- const struct spi_mem_op *op)
- {
- u32 control = readl(qspi->regs + REG_CONTROL);
- /*
- * The operating mode can be configured based on the command that needs
- * to be send.
- * bits[15:14]: Sets whether multiple bit SPI operates in normal,
- * extended or full modes.
- * 00: Normal (single DQ0 TX and single DQ1 RX lines)
- * 01: Extended RO (command and address bytes on DQ0 only)
- * 10: Extended RW (command byte on DQ0 only)
- * 11: Full. (command and address are on all DQ lines)
- * bit[13]: Sets whether multiple bit SPI uses 2 or 4 bits of data
- * 0: 2-bits (BSPI)
- * 1: 4-bits (QSPI)
- */
- if (op->data.buswidth == 4 || op->data.buswidth == 2) {
- control &= ~CONTROL_MODE12_MASK;
- if (op->cmd.buswidth == 1 && (op->addr.buswidth == 1 ||
- op->addr.buswidth == 0))
- control |= CONTROL_MODE12_EX_RO;
- else if (op->cmd.buswidth == 1)
- control |= CONTROL_MODE12_EX_RW;
- else
- control |= CONTROL_MODE12_FULL;
- control |= CONTROL_MODE0;
- } else {
- control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0);
- }
- writel(control, qspi->regs + REG_CONTROL);
- return 0;
- }
- static int mchp_coreqspi_exec_op(struct spi_slave *slave,
- const struct spi_mem_op *op)
- {
- struct mchp_coreqspi *qspi = dev_get_priv(slave->dev->parent);
- u32 address = op->addr.val;
- u8 opcode = op->cmd.opcode;
- u8 opaddr[5];
- int err = 0, i;
- err = mchp_coreqspi_wait_for_ready(slave);
- if (err)
- return err;
- err = mchp_coreqspi_set_operate_mode(qspi, op);
- if (err)
- return err;
- mchp_coreqspi_config_op(qspi, op);
- if (op->cmd.opcode) {
- qspi->txbuf = &opcode;
- qspi->rxbuf = NULL;
- qspi->tx_len = op->cmd.nbytes;
- qspi->rx_len = 0;
- mchp_coreqspi_write_op(qspi, false);
- }
- qspi->txbuf = &opaddr[0];
- if (op->addr.nbytes) {
- for (i = 0; i < op->addr.nbytes; i++)
- qspi->txbuf[i] = address >> (8 * (op->addr.nbytes - i - 1));
- qspi->rxbuf = NULL;
- qspi->tx_len = op->addr.nbytes;
- qspi->rx_len = 0;
- mchp_coreqspi_write_op(qspi, false);
- }
- if (op->data.nbytes) {
- if (op->data.dir == SPI_MEM_DATA_OUT) {
- qspi->txbuf = (u8 *)op->data.buf.out;
- qspi->rxbuf = NULL;
- qspi->rx_len = 0;
- qspi->tx_len = op->data.nbytes;
- mchp_coreqspi_write_op(qspi, true);
- } else {
- qspi->txbuf = NULL;
- qspi->rxbuf = (u8 *)op->data.buf.in;
- qspi->rx_len = op->data.nbytes;
- qspi->tx_len = 0;
- mchp_coreqspi_read_op(qspi);
- }
- }
- return 0;
- }
- static bool mchp_coreqspi_supports_op(struct spi_slave *slave,
- const struct spi_mem_op *op)
- {
- if (!spi_mem_default_supports_op(slave, op))
- return false;
- if ((op->data.buswidth == 4 || op->data.buswidth == 2) &&
- (op->cmd.buswidth == 1 && (op->addr.buswidth == 1 ||
- op->addr.buswidth == 0))) {
- /*
- * If the command and address are on DQ0 only, then this
- * controller doesn't support sending data on dual and
- * quad lines. but it supports reading data on dual and
- * quad lines with same configuration as command and
- * address on DQ0.
- * i.e. The control register[15:13] :EX_RO(read only) is
- * meant only for the command and address are on DQ0 but
- * not to write data, it is just to read.
- * Ex: 0x34h is Quad Load Program Data which is not
- * supported. Then the spi-mem layer will iterate over
- * each command and it will chose the supported one.
- */
- if (op->data.dir == SPI_MEM_DATA_OUT)
- return false;
- }
- return true;
- }
- static int mchp_coreqspi_adjust_op_size(struct spi_slave *slave,
- struct spi_mem_op *op)
- {
- if (op->data.dir == SPI_MEM_DATA_OUT) {
- if (op->data.nbytes > MAX_DATA_CMD_LEN)
- op->data.nbytes = MAX_DATA_CMD_LEN;
- }
- return 0;
- }
- static int mchp_coreqspi_set_speed(struct udevice *dev, uint speed)
- {
- struct mchp_coreqspi *qspi = dev_get_priv(dev);
- u32 control, baud_rate_val = 0;
- if (speed > (qspi->freq / 2))
- speed = qspi->freq / 2;
- baud_rate_val = DIV_ROUND_UP(qspi->freq, 2 * speed);
- if (baud_rate_val >= MAX_DIVIDER || baud_rate_val <= MIN_DIVIDER)
- return -EINVAL;
- control = readl(qspi->regs + REG_CONTROL);
- control &= ~CONTROL_CLKRATE_MASK;
- control |= baud_rate_val << CONTROL_CLKRATE_SHIFT;
- writel(control, qspi->regs + REG_CONTROL);
- return 0;
- }
- static int mchp_coreqspi_set_mode(struct udevice *dev, uint mode)
- {
- struct mchp_coreqspi *qspi = dev_get_priv(dev);
- u32 control;
- control = readl(qspi->regs + REG_CONTROL);
- if ((mode & SPI_CPOL) && (mode & SPI_CPHA))
- control |= CONTROL_CLKIDLE;
- else
- control &= ~CONTROL_CLKIDLE;
- writel(control, qspi->regs + REG_CONTROL);
- return 0;
- }
- static int mchp_coreqspi_claim_bus(struct udevice *dev)
- {
- return 0;
- }
- static int mchp_coreqspi_release_bus(struct udevice *dev)
- {
- return 0;
- }
- static int mchp_coreqspi_probe(struct udevice *dev)
- {
- struct mchp_coreqspi *qspi = dev_get_priv(dev);
- struct clk clk;
- ulong clk_rate;
- int ret;
- ret = clk_get_by_index(dev, 0, &clk);
- if (ret)
- return -EINVAL;
- ret = clk_enable(&clk);
- if (ret)
- return ret;
- clk_rate = clk_get_rate(&clk);
- if (!clk_rate)
- return -EINVAL;
- qspi->freq = clk_rate;
- qspi->regs = dev_read_addr_ptr(dev);
- if (!qspi->regs)
- return -EINVAL;
- /* Init the mpfs qspi hw */
- mchp_coreqspi_init_hw(qspi);
- return 0;
- }
- static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = {
- .adjust_op_size = mchp_coreqspi_adjust_op_size,
- .supports_op = mchp_coreqspi_supports_op,
- .exec_op = mchp_coreqspi_exec_op,
- };
- static const struct dm_spi_ops mchp_coreqspi_ops = {
- .claim_bus = mchp_coreqspi_claim_bus,
- .release_bus = mchp_coreqspi_release_bus,
- .set_speed = mchp_coreqspi_set_speed,
- .set_mode = mchp_coreqspi_set_mode,
- .mem_ops = &mchp_coreqspi_mem_ops,
- };
- static const struct udevice_id mchp_coreqspi_ids[] = {
- { .compatible = "microchip,mpfs-coreqspi-rtl-v2" },
- { .compatible = "microchip,mpfs-qspi" },
- { }
- };
- U_BOOT_DRIVER(mchp_coreqspi) = {
- .name = "mchp_coreqspi",
- .id = UCLASS_SPI,
- .of_match = mchp_coreqspi_ids,
- .ops = &mchp_coreqspi_ops,
- .priv_auto = sizeof(struct mchp_coreqspi),
- .probe = mchp_coreqspi_probe,
- };
|