Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Nagasuresh Relli | 2504 | 99.40% | 1 | 25.00% |
Li Zetao | 8 | 0.32% | 1 | 25.00% |
Yang Yingliang | 5 | 0.20% | 1 | 25.00% |
Uwe Kleine-König | 2 | 0.08% | 1 | 25.00% |
Total | 2519 | 4 |
// SPDX-License-Identifier: (GPL-2.0) /* * Microchip coreQSPI QSPI controller driver * * Copyright (C) 2018-2022 Microchip Technology Inc. and its subsidiaries * * Author: Naga Sureshkumar Relli <nagasuresh.relli@microchip.com> * */ #include <linux/clk.h> #include <linux/err.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/spi/spi-mem.h> /* * 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 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: Virtual address of the QSPI controller registers * @clk: QSPI Operating clock * @data_completion: completion structure * @op_lock: lock access to the device * @txbuf: TX buffer * @rxbuf: RX buffer * @irq: IRQ number * @tx_len: Number of bytes left to transfer * @rx_len: Number of bytes left to receive */ struct mchp_coreqspi { void __iomem *regs; struct clk *clk; struct completion data_completion; struct mutex op_lock; /* lock access to the device */ u8 *txbuf; u8 *rxbuf; int irq; int tx_len; int rx_len; }; static int mchp_coreqspi_set_mode(struct mchp_coreqspi *qspi, const struct spi_mem_op *op) { u32 control = readl_relaxed(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_relaxed(control, qspi->regs + REG_CONTROL); return 0; } static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi) { u32 control, data; if (!qspi->rx_len) return; control = readl_relaxed(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_relaxed(control, qspi->regs + REG_CONTROL); while (qspi->rx_len >= 4) { while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) ; data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); *(u32 *)qspi->rxbuf = data; qspi->rxbuf += 4; qspi->rx_len -= 4; } control &= ~CONTROL_FLAGSX4; writel_relaxed(control, qspi->regs + REG_CONTROL); while (qspi->rx_len--) { while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) ; data = readl_relaxed(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_relaxed(qspi->regs + REG_CONTROL); control |= CONTROL_FLAGSX4; writel_relaxed(control, qspi->regs + REG_CONTROL); while (qspi->tx_len >= 4) { while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) ; data = *(u32 *)qspi->txbuf; qspi->txbuf += 4; qspi->tx_len -= 4; writel_relaxed(data, qspi->regs + REG_X4_TX_DATA); } control &= ~CONTROL_FLAGSX4; writel_relaxed(control, qspi->regs + REG_CONTROL); while (qspi->tx_len--) { while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) ; data = *qspi->txbuf++; writel_relaxed(data, qspi->regs + REG_TX_DATA); } } static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi) { u32 mask = IEN_TXDONE | IEN_RXDONE | IEN_RXAVAILABLE; writel_relaxed(mask, qspi->regs + REG_IEN); } static void mchp_coreqspi_disable_ints(struct mchp_coreqspi *qspi) { writel_relaxed(0, qspi->regs + REG_IEN); } static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id) { struct mchp_coreqspi *qspi = (struct mchp_coreqspi *)dev_id; irqreturn_t ret = IRQ_NONE; int intfield = readl_relaxed(qspi->regs + REG_STATUS) & STATUS_MASK; if (intfield == 0) return ret; if (intfield & IEN_TXDONE) { writel_relaxed(IEN_TXDONE, qspi->regs + REG_STATUS); ret = IRQ_HANDLED; } if (intfield & IEN_RXAVAILABLE) { writel_relaxed(IEN_RXAVAILABLE, qspi->regs + REG_STATUS); mchp_coreqspi_read_op(qspi); ret = IRQ_HANDLED; } if (intfield & IEN_RXDONE) { writel_relaxed(IEN_RXDONE, qspi->regs + REG_STATUS); complete(&qspi->data_completion); ret = IRQ_HANDLED; } return ret; } static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi) { unsigned long clk_hz; u32 control, baud_rate_val = 0; clk_hz = clk_get_rate(qspi->clk); if (!clk_hz) return -EINVAL; baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * spi->max_speed_hz); if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) { dev_err(&spi->dev, "could not configure the clock for spi clock %d Hz & system clock %ld Hz\n", spi->max_speed_hz, clk_hz); return -EINVAL; } control = readl_relaxed(qspi->regs + REG_CONTROL); control |= baud_rate_val << CONTROL_CLKRATE_SHIFT; writel_relaxed(control, qspi->regs + REG_CONTROL); control = readl_relaxed(qspi->regs + REG_CONTROL); if ((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) control |= CONTROL_CLKIDLE; else control &= ~CONTROL_CLKIDLE; writel_relaxed(control, qspi->regs + REG_CONTROL); return 0; } static int mchp_coreqspi_setup_op(struct spi_device *spi_dev) { struct spi_controller *ctlr = spi_dev->controller; struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); u32 control = readl_relaxed(qspi->regs + REG_CONTROL); control |= (CONTROL_MASTER | CONTROL_ENABLE); control &= ~CONTROL_CLKIDLE; writel_relaxed(control, qspi->regs + REG_CONTROL); return 0; } 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_relaxed(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_relaxed(qspi->regs + REG_CONTROL); if (ctrl & CONTROL_MODE12_MASK) frames |= (1 << FRAMES_SHIFT); frames |= FRAMES_FLAGWORD; writel_relaxed(frames, qspi->regs + REG_FRAMES); } static int mchp_qspi_wait_for_ready(struct spi_mem *mem) { struct mchp_coreqspi *qspi = spi_controller_get_devdata (mem->spi->controller); u32 status; int ret; ret = readl_poll_timeout(qspi->regs + REG_STATUS, status, (status & STATUS_READY), 0, TIMEOUT_MS); if (ret) { dev_err(&mem->spi->dev, "Timeout waiting on QSPI ready.\n"); return -ETIMEDOUT; } return ret; } static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct mchp_coreqspi *qspi = spi_controller_get_devdata (mem->spi->controller); u32 address = op->addr.val; u8 opcode = op->cmd.opcode; u8 opaddr[5]; int err, i; mutex_lock(&qspi->op_lock); err = mchp_qspi_wait_for_ready(mem); if (err) goto error; err = mchp_coreqspi_setup_clock(qspi, mem->spi); if (err) goto error; err = mchp_coreqspi_set_mode(qspi, op); if (err) goto error; reinit_completion(&qspi->data_completion); 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_enable_ints(qspi); if (!wait_for_completion_timeout(&qspi->data_completion, msecs_to_jiffies(1000))) err = -ETIMEDOUT; error: mutex_unlock(&qspi->op_lock); mchp_coreqspi_disable_ints(qspi); return err; } static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { if (!spi_mem_default_supports_op(mem, 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_mem *mem, struct spi_mem_op *op) { if (op->data.dir == SPI_MEM_DATA_OUT || op->data.dir == SPI_MEM_DATA_IN) { if (op->data.nbytes > MAX_DATA_CMD_LEN) op->data.nbytes = MAX_DATA_CMD_LEN; } 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 int mchp_coreqspi_probe(struct platform_device *pdev) { struct spi_controller *ctlr; struct mchp_coreqspi *qspi; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; int ret; ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*qspi)); if (!ctlr) return dev_err_probe(&pdev->dev, -ENOMEM, "unable to allocate host for QSPI controller\n"); qspi = spi_controller_get_devdata(ctlr); platform_set_drvdata(pdev, qspi); qspi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(qspi->regs)) return dev_err_probe(&pdev->dev, PTR_ERR(qspi->regs), "failed to map registers\n"); qspi->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(qspi->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(qspi->clk), "could not get clock\n"); init_completion(&qspi->data_completion); mutex_init(&qspi->op_lock); qspi->irq = platform_get_irq(pdev, 0); if (qspi->irq < 0) return qspi->irq; ret = devm_request_irq(&pdev->dev, qspi->irq, mchp_coreqspi_isr, IRQF_SHARED, pdev->name, qspi); if (ret) { dev_err(&pdev->dev, "request_irq failed %d\n", ret); return ret; } ctlr->bits_per_word_mask = SPI_BPW_MASK(8); ctlr->mem_ops = &mchp_coreqspi_mem_ops; ctlr->setup = mchp_coreqspi_setup_op; ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; ctlr->dev.of_node = np; ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) return dev_err_probe(&pdev->dev, ret, "spi_register_controller failed\n"); return 0; } static void mchp_coreqspi_remove(struct platform_device *pdev) { struct mchp_coreqspi *qspi = platform_get_drvdata(pdev); u32 control = readl_relaxed(qspi->regs + REG_CONTROL); mchp_coreqspi_disable_ints(qspi); control &= ~CONTROL_ENABLE; writel_relaxed(control, qspi->regs + REG_CONTROL); } static const struct of_device_id mchp_coreqspi_of_match[] = { { .compatible = "microchip,coreqspi-rtl-v2" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mchp_coreqspi_of_match); static struct platform_driver mchp_coreqspi_driver = { .probe = mchp_coreqspi_probe, .driver = { .name = "microchip,coreqspi", .of_match_table = mchp_coreqspi_of_match, }, .remove_new = mchp_coreqspi_remove, }; module_platform_driver(mchp_coreqspi_driver); MODULE_AUTHOR("Naga Sureshkumar Relli <nagasuresh.relli@microchip.com"); MODULE_DESCRIPTION("Microchip coreQSPI QSPI controller driver"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1