diff --git a/MAINTAINERS b/MAINTAINERS index a72ccd0b5854dfe375f394aa75172f7228a682a5..ffa8e7b89c7ac03dae4ad042aaaf2d85dea71d9a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -205,7 +205,7 @@ F: drivers/mmc/mtk-sd.c F: drivers/pinctrl/mediatek/ F: drivers/power/domain/mtk-power-domain.c F: drivers/ram/mediatek/ -F: drivers/spi/mtk_qspi.c +F: drivers/spi/mtk_snfi_spi.c F: drivers/timer/mtk_timer.c F: drivers/watchdog/mtk_wdt.c F: drivers/net/mtk_eth.c diff --git a/configs/mt7629_rfb_defconfig b/configs/mt7629_rfb_defconfig index d6a7c84df35ea53a7563e5572ec8377e547df28e..c0a586f24865b48eef148c0bebd4b9a817d4eb33 100644 --- a/configs/mt7629_rfb_defconfig +++ b/configs/mt7629_rfb_defconfig @@ -64,7 +64,7 @@ CONFIG_DM_SERIAL=y CONFIG_MTK_SERIAL=y CONFIG_SPI=y CONFIG_DM_SPI=y -CONFIG_MTK_QSPI=y +CONFIG_MTK_SNFI_SPI=y CONFIG_SYSRESET=y CONFIG_SPL_SYSRESET=y CONFIG_SYSRESET_WATCHDOG=y diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c index f3d687ae806f6aded5f9f6f210e7b93e39f78c44..350812a04ba982514d55dae3e0b7c0f44070d0e9 100644 --- a/drivers/mmc/mmc_spi.c +++ b/drivers/mmc/mmc_spi.c @@ -84,7 +84,7 @@ static int mmc_spi_sendcmd(struct udevice *dev, cmdo[4] = cmdarg >> 8; cmdo[5] = cmdarg; cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; - ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, 0); + ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, SPI_XFER_BEGIN); if (ret) return ret; @@ -360,6 +360,8 @@ static int dm_mmc_spi_request(struct udevice *dev, struct mmc_cmd *cmd, } done: + dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END); + dm_spi_release_bus(dev); return ret; diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index ec929760eee4768522bed3f564128b4963c717ea..a3920ba520e0eb9d414a0e2f5f913e0b52ab5b1e 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -163,11 +163,15 @@ const struct flash_info spi_nor_ids[] = { { INFO("n25q128a13", 0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) }, { INFO("n25q256a", 0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { INFO("n25q256ax1", 0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO6("mt25qu512a", 0x20bb20, 0x104400, 64 * 1024, 1024, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { INFO("n25q512a", 0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { INFO("n25q512ax3", 0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { INFO("n25q00", 0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { INFO("n25q00a", 0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, { INFO("mt25qu02g", 0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + { INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_4B_OPCODES) }, + { INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_4B_OPCODES) }, #endif #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ /* Spansion/Cypress -- single (large) sector size only, at least diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index cc174dd0363a846d323dcc9cf548bcfb5f3b0595..f459c0a41138fe56fe4092637ad7fd86d898c6a8 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -158,13 +158,14 @@ config MT7621_SPI the SPI NOR flash on platforms embedding this Ralink / MediaTek SPI core, like MT7621/7628/7688. -config MTK_QSPI - bool "Mediatek QSPI driver" - imply SPI_FLASH_BAR +config MTK_SNFI_SPI + bool "Mediatek SPI memory controller driver" + depends on SPI_MEM help - Enable the Mediatek QSPI driver. This driver can be - used to access the SPI NOR flash on platforms embedding this - Mediatek QSPI IP core. + Enable the Mediatek SPI memory controller driver. This driver is + originally based on the MediaTek SNFI IP core. It can only be + used to access SPI memory devices like SPI-NOR or SPI-NAND on + platforms embedding this IP core, like MT7622/M7629. config MVEBU_A3700_SPI bool "Marvell Armada 3700 SPI driver" @@ -232,6 +233,14 @@ config SANDBOX_SPI }; }; +config SPI_SIFIVE + bool "SiFive SPI driver" + help + This driver supports the SiFive SPI IP. If unsure say N. + Enable the SiFive SPI controller driver. + + The SiFive SPI controller driver is found on various SiFive SoCs. + config SPI_SUNXI bool "Allwinner SoC SPI controllers" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index ab84122f0834586abbbd07ea563ff960313c9aab..ae4f2958f8a21121d32cfd07a1aeb2b9de9d9591 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -37,7 +37,7 @@ obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o -obj-$(CONFIG_MTK_QSPI) += mtk_qspi.o +obj-$(CONFIG_MTK_SNFI_SPI) += mtk_snfi_spi.o obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o obj-$(CONFIG_MSCC_BB_SPI) += mscc_bb_spi.o obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o @@ -50,6 +50,7 @@ obj-$(CONFIG_PL022_SPI) += pl022_spi.o obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o +obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o obj-$(CONFIG_SPI_SUNXI) += spi-sunxi.o obj-$(CONFIG_SH_SPI) += sh_spi.o obj-$(CONFIG_SH_QSPI) += sh_qspi.o diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 1598c4f698907cafb1f1667847b7031c191d98c0..41abe1996f2592a8cdfdca15b4e40863da975676 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -150,20 +151,13 @@ static void qspi_write32(u32 flags, u32 *addr, u32 val) static inline int is_controller_busy(const struct fsl_qspi_priv *priv) { u32 val; - const u32 mask = QSPI_SR_BUSY_MASK | QSPI_SR_AHB_ACC_MASK | - QSPI_SR_IP_ACC_MASK; - unsigned int retry = 5; + u32 mask = QSPI_SR_BUSY_MASK | QSPI_SR_AHB_ACC_MASK | + QSPI_SR_IP_ACC_MASK; - do { - val = qspi_read32(priv->flags, &priv->regs->sr); + if (priv->flags & QSPI_FLAG_REGMAP_ENDIAN_BIG) + mask = (u32)cpu_to_be32(mask); - if ((~val & mask) == mask) - return 0; - - udelay(1); - } while (--retry); - - return -ETIMEDOUT; + return readl_poll_timeout(&priv->regs->sr, val, !(val & mask), 1000); } /* QSPI support swapping the flash read/write data diff --git a/drivers/spi/mtk_qspi.c b/drivers/spi/mtk_qspi.c deleted file mode 100644 index b510733e92c3e5844bdb31d2f2d3f74d589ab63a..0000000000000000000000000000000000000000 --- a/drivers/spi/mtk_qspi.c +++ /dev/null @@ -1,359 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2018 MediaTek, Inc. - * Author : Guochun.Mao@mediatek.com - */ - -#include -#include -#include -#include -#include -#include -#include - -/* Register Offset */ -struct mtk_qspi_regs { - u32 cmd; - u32 cnt; - u32 rdsr; - u32 rdata; - u32 radr[3]; - u32 wdata; - u32 prgdata[6]; - u32 shreg[10]; - u32 cfg[2]; - u32 shreg10; - u32 mode_mon; - u32 status[4]; - u32 flash_time; - u32 flash_cfg; - u32 reserved_0[3]; - u32 sf_time; - u32 pp_dw_data; - u32 reserved_1; - u32 delsel_0[2]; - u32 intrstus; - u32 intren; - u32 reserved_2; - u32 cfg3; - u32 reserved_3; - u32 chksum; - u32 aaicmd; - u32 wrprot; - u32 radr3; - u32 dual; - u32 delsel_1[3]; -}; - -struct mtk_qspi_platdata { - fdt_addr_t reg_base; - fdt_addr_t mem_base; -}; - -struct mtk_qspi_priv { - struct mtk_qspi_regs *regs; - unsigned long *mem_base; - u8 op; - u8 tx[3]; /* only record max 3 bytes paras, when it's address. */ - u32 txlen; /* dout buffer length - op code length */ - u8 *rx; - u32 rxlen; -}; - -#define MTK_QSPI_CMD_POLLINGREG_US 500000 -#define MTK_QSPI_WRBUF_SIZE 256 -#define MTK_QSPI_COMMAND_ENABLE 0x30 - -/* NOR flash controller commands */ -#define MTK_QSPI_RD_TRIGGER BIT(0) -#define MTK_QSPI_READSTATUS BIT(1) -#define MTK_QSPI_PRG_CMD BIT(2) -#define MTK_QSPI_WR_TRIGGER BIT(4) -#define MTK_QSPI_WRITESTATUS BIT(5) -#define MTK_QSPI_AUTOINC BIT(7) - -#define MTK_QSPI_MAX_RX_TX_SHIFT 0x6 -#define MTK_QSPI_MAX_SHIFT 0x8 - -#define MTK_QSPI_WR_BUF_ENABLE 0x1 -#define MTK_QSPI_WR_BUF_DISABLE 0x0 - -static int mtk_qspi_execute_cmd(struct mtk_qspi_priv *priv, u8 cmd) -{ - u8 tmp; - u8 val = cmd & ~MTK_QSPI_AUTOINC; - - writeb(cmd, &priv->regs->cmd); - - return readb_poll_timeout(&priv->regs->cmd, tmp, !(val & tmp), - MTK_QSPI_CMD_POLLINGREG_US); -} - -static int mtk_qspi_tx_rx(struct mtk_qspi_priv *priv) -{ - int len = 1 + priv->txlen + priv->rxlen; - int i, ret, idx; - - if (len > MTK_QSPI_MAX_SHIFT) - return -ERR_INVAL; - - writeb(len * 8, &priv->regs->cnt); - - /* start at PRGDATA5, go down to PRGDATA0 */ - idx = MTK_QSPI_MAX_RX_TX_SHIFT - 1; - - /* opcode */ - writeb(priv->op, &priv->regs->prgdata[idx]); - idx--; - - /* program TX data */ - for (i = 0; i < priv->txlen; i++, idx--) - writeb(priv->tx[i], &priv->regs->prgdata[idx]); - - /* clear out rest of TX registers */ - while (idx >= 0) { - writeb(0, &priv->regs->prgdata[idx]); - idx--; - } - - ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_PRG_CMD); - if (ret) - return ret; - - /* restart at first RX byte */ - idx = priv->rxlen - 1; - - /* read out RX data */ - for (i = 0; i < priv->rxlen; i++, idx--) - priv->rx[i] = readb(&priv->regs->shreg[idx]); - - return 0; -} - -static int mtk_qspi_read(struct mtk_qspi_priv *priv, - u32 addr, u8 *buf, u32 len) -{ - memcpy(buf, (u8 *)priv->mem_base + addr, len); - return 0; -} - -static void mtk_qspi_set_addr(struct mtk_qspi_priv *priv, u32 addr) -{ - int i; - - for (i = 0; i < 3; i++) { - writeb(addr & 0xff, &priv->regs->radr[i]); - addr >>= 8; - } -} - -static int mtk_qspi_write_single_byte(struct mtk_qspi_priv *priv, - u32 addr, u32 length, const u8 *data) -{ - int i, ret; - - mtk_qspi_set_addr(priv, addr); - - for (i = 0; i < length; i++) { - writeb(*data++, &priv->regs->wdata); - ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER); - if (ret < 0) - return ret; - } - return 0; -} - -static int mtk_qspi_write_buffer(struct mtk_qspi_priv *priv, u32 addr, - const u8 *buf) -{ - int i, data; - - mtk_qspi_set_addr(priv, addr); - - for (i = 0; i < MTK_QSPI_WRBUF_SIZE; i += 4) { - data = buf[i + 3] << 24 | buf[i + 2] << 16 | - buf[i + 1] << 8 | buf[i]; - writel(data, &priv->regs->pp_dw_data); - } - - return mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER); -} - -static int mtk_qspi_write(struct mtk_qspi_priv *priv, - u32 addr, const u8 *buf, u32 len) -{ - int ret; - - /* setting pre-fetch buffer for page program */ - writel(MTK_QSPI_WR_BUF_ENABLE, &priv->regs->cfg[1]); - while (len >= MTK_QSPI_WRBUF_SIZE) { - ret = mtk_qspi_write_buffer(priv, addr, buf); - if (ret < 0) - return ret; - - len -= MTK_QSPI_WRBUF_SIZE; - addr += MTK_QSPI_WRBUF_SIZE; - buf += MTK_QSPI_WRBUF_SIZE; - } - /* disable pre-fetch buffer for page program */ - writel(MTK_QSPI_WR_BUF_DISABLE, &priv->regs->cfg[1]); - - if (len) - return mtk_qspi_write_single_byte(priv, addr, len, buf); - - return 0; -} - -static int mtk_qspi_claim_bus(struct udevice *dev) -{ - /* nothing to do */ - return 0; -} - -static int mtk_qspi_release_bus(struct udevice *dev) -{ - /* nothing to do */ - return 0; -} - -static int mtk_qspi_transfer(struct mtk_qspi_priv *priv, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) -{ - u32 bytes = DIV_ROUND_UP(bitlen, 8); - u32 addr; - - if (!bytes) - return -ERR_INVAL; - - if (dout) { - if (flags & SPI_XFER_BEGIN) { - /* parse op code and potential paras first */ - priv->op = *(u8 *)dout; - if (bytes > 1) - memcpy(priv->tx, (u8 *)dout + 1, - bytes <= 4 ? bytes - 1 : 3); - priv->txlen = bytes - 1; - } - - if (flags == SPI_XFER_ONCE) { - /* operations without receiving or sending data. - * for example: erase, write flash register or write - * enable... - */ - priv->rx = NULL; - priv->rxlen = 0; - return mtk_qspi_tx_rx(priv); - } - - if (flags & SPI_XFER_END) { - /* here, dout should be data to be written. - * and priv->tx should be filled 3Bytes address. - */ - addr = priv->tx[0] << 16 | priv->tx[1] << 8 | - priv->tx[2]; - return mtk_qspi_write(priv, addr, (u8 *)dout, bytes); - } - } - - if (din) { - if (priv->txlen >= 3) { - /* if run to here, priv->tx[] should be the address - * where read data from, - * and, din is the buf to receive data. - */ - addr = priv->tx[0] << 16 | priv->tx[1] << 8 | - priv->tx[2]; - return mtk_qspi_read(priv, addr, (u8 *)din, bytes); - } - - /* should be reading flash's register */ - priv->rx = (u8 *)din; - priv->rxlen = bytes; - return mtk_qspi_tx_rx(priv); - } - - return 0; -} - -static int mtk_qspi_xfer(struct udevice *dev, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) -{ - struct udevice *bus = dev->parent; - struct mtk_qspi_priv *priv = dev_get_priv(bus); - - return mtk_qspi_transfer(priv, bitlen, dout, din, flags); -} - -static int mtk_qspi_set_speed(struct udevice *bus, uint speed) -{ - /* nothing to do */ - return 0; -} - -static int mtk_qspi_set_mode(struct udevice *bus, uint mode) -{ - /* nothing to do */ - return 0; -} - -static int mtk_qspi_ofdata_to_platdata(struct udevice *bus) -{ - struct resource res_reg, res_mem; - struct mtk_qspi_platdata *plat = bus->platdata; - int ret; - - ret = dev_read_resource_byname(bus, "reg_base", &res_reg); - if (ret) { - debug("can't get reg_base resource(ret = %d)\n", ret); - return -ENOMEM; - } - - ret = dev_read_resource_byname(bus, "mem_base", &res_mem); - if (ret) { - debug("can't get map_base resource(ret = %d)\n", ret); - return -ENOMEM; - } - - plat->mem_base = res_mem.start; - plat->reg_base = res_reg.start; - - return 0; -} - -static int mtk_qspi_probe(struct udevice *bus) -{ - struct mtk_qspi_platdata *plat = dev_get_platdata(bus); - struct mtk_qspi_priv *priv = dev_get_priv(bus); - - priv->regs = (struct mtk_qspi_regs *)plat->reg_base; - priv->mem_base = (unsigned long *)plat->mem_base; - - writel(MTK_QSPI_COMMAND_ENABLE, &priv->regs->wrprot); - - return 0; -} - -static const struct dm_spi_ops mtk_qspi_ops = { - .claim_bus = mtk_qspi_claim_bus, - .release_bus = mtk_qspi_release_bus, - .xfer = mtk_qspi_xfer, - .set_speed = mtk_qspi_set_speed, - .set_mode = mtk_qspi_set_mode, -}; - -static const struct udevice_id mtk_qspi_ids[] = { - { .compatible = "mediatek,mt7629-qspi" }, - { } -}; - -U_BOOT_DRIVER(mtk_qspi) = { - .name = "mtk_qspi", - .id = UCLASS_SPI, - .of_match = mtk_qspi_ids, - .ops = &mtk_qspi_ops, - .ofdata_to_platdata = mtk_qspi_ofdata_to_platdata, - .platdata_auto_alloc_size = sizeof(struct mtk_qspi_platdata), - .priv_auto_alloc_size = sizeof(struct mtk_qspi_priv), - .probe = mtk_qspi_probe, -}; diff --git a/drivers/spi/mtk_snfi_spi.c b/drivers/spi/mtk_snfi_spi.c new file mode 100644 index 0000000000000000000000000000000000000000..2a8947651597c0da8a7150a6d70a3d608fc0d8c2 --- /dev/null +++ b/drivers/spi/mtk_snfi_spi.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SNFI_MAC_CTL 0x500 +#define MAC_XIO_SEL BIT(4) +#define SF_MAC_EN BIT(3) +#define SF_TRIG BIT(2) +#define WIP_READY BIT(1) +#define WIP BIT(0) + +#define SNFI_MAC_OUTL 0x504 +#define SNFI_MAC_INL 0x508 + +#define SNFI_MISC_CTL 0x538 +#define SW_RST BIT(28) +#define FIFO_RD_LTC_SHIFT 25 +#define FIFO_RD_LTC GENMASK(26, 25) +#define LATCH_LAT_SHIFT 8 +#define LATCH_LAT GENMASK(9, 8) +#define CS_DESELECT_CYC_SHIFT 0 +#define CS_DESELECT_CYC GENMASK(4, 0) + +#define SNF_STA_CTL1 0x550 +#define SPI_STATE GENMASK(3, 0) + +#define SNFI_GPRAM_OFFSET 0x800 +#define SNFI_GPRAM_SIZE 0x80 + +#define SNFI_POLL_INTERVAL 500000 +#define SNFI_RST_POLL_INTERVAL 1000000 + +struct mtk_snfi_priv { + void __iomem *base; + + struct clk nfi_clk; + struct clk pad_clk; +}; + +static int mtk_snfi_adjust_op_size(struct spi_slave *slave, + struct spi_mem_op *op) +{ + u32 nbytes; + + /* + * When there is input data, it will be appended after the output + * data in the GPRAM. So the total size of either pure output data + * or the output+input data must not exceed the GPRAM size. + */ + + nbytes = sizeof(op->cmd.opcode) + op->addr.nbytes + + op->dummy.nbytes; + + if (nbytes + op->data.nbytes <= SNFI_GPRAM_SIZE) + return 0; + + if (nbytes >= SNFI_GPRAM_SIZE) + return -ENOTSUPP; + + op->data.nbytes = SNFI_GPRAM_SIZE - nbytes; + + return 0; +} + +static bool mtk_snfi_supports_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 || + op->dummy.buswidth > 1 || op->data.buswidth > 1) + return false; + + return true; +} + +static int mtk_snfi_mac_trigger(struct mtk_snfi_priv *priv, + struct udevice *bus, u32 outlen, u32 inlen) +{ + int ret; + u32 val; + +#ifdef CONFIG_PINCTRL + pinctrl_select_state(bus, "snfi"); +#endif + + writel(SF_MAC_EN, priv->base + SNFI_MAC_CTL); + writel(outlen, priv->base + SNFI_MAC_OUTL); + writel(inlen, priv->base + SNFI_MAC_INL); + + writel(SF_MAC_EN | SF_TRIG, priv->base + SNFI_MAC_CTL); + + ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val, + val & WIP_READY, SNFI_POLL_INTERVAL); + if (ret) { + printf("%s: timed out waiting for WIP_READY\n", __func__); + goto cleanup; + } + + ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val, + !(val & WIP), SNFI_POLL_INTERVAL); + if (ret) + printf("%s: timed out waiting for WIP cleared\n", __func__); + + writel(0, priv->base + SNFI_MAC_CTL); + +cleanup: +#ifdef CONFIG_PINCTRL + pinctrl_select_state(bus, "default"); +#endif + + return ret; +} + +static int mtk_snfi_mac_reset(struct mtk_snfi_priv *priv) +{ + int ret; + u32 val; + + setbits_32(priv->base + SNFI_MISC_CTL, SW_RST); + + ret = readl_poll_timeout(priv->base + SNF_STA_CTL1, val, + !(val & SPI_STATE), SNFI_POLL_INTERVAL); + if (ret) + printf("%s: failed to reset snfi mac\n", __func__); + + writel((2 << FIFO_RD_LTC_SHIFT) | + (10 << CS_DESELECT_CYC_SHIFT), + priv->base + SNFI_MISC_CTL); + + return ret; +} + +static void mtk_snfi_copy_to_gpram(struct mtk_snfi_priv *priv, + const void *data, size_t len) +{ + void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET; + size_t i, n = (len + sizeof(u32) - 1) / sizeof(u32); + const u32 *buff = data; + + /* + * The output data will always be copied to the beginning of + * the GPRAM. Uses word write for better performace. + * + * Trailing bytes in the last word are not cared. + */ + + for (i = 0; i < n; i++) + writel(buff[i], gpram + i * sizeof(u32)); +} + +static void mtk_snfi_copy_from_gpram(struct mtk_snfi_priv *priv, u8 *cache, + void *data, size_t pos, size_t len) +{ + void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET; + u32 *buff = (u32 *)cache; + size_t i, off, end; + + /* Start position in the buffer */ + off = pos & (sizeof(u32) - 1); + + /* End position for copy */ + end = (len + pos + sizeof(u32) - 1) & (~(sizeof(u32) - 1)); + + /* Start position for copy */ + pos &= ~(sizeof(u32) - 1); + + /* + * Read aligned data from GPRAM to buffer first. + * Uses word read for better performace. + */ + i = 0; + while (pos < end) { + buff[i++] = readl(gpram + pos); + pos += sizeof(u32); + } + + /* Copy rx data */ + memcpy(data, cache + off, len); +} + +static int mtk_snfi_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + struct udevice *bus = dev_get_parent(slave->dev); + struct mtk_snfi_priv *priv = dev_get_priv(bus); + u8 gpram_cache[SNFI_GPRAM_SIZE]; + u32 i, len = 0, inlen = 0; + int addr_sh; + int ret; + + WATCHDOG_RESET(); + + ret = mtk_snfi_mac_reset(priv); + if (ret) + return ret; + + /* Put opcode */ + gpram_cache[len++] = op->cmd.opcode; + + /* Put address */ + addr_sh = (op->addr.nbytes - 1) * 8; + while (addr_sh >= 0) { + gpram_cache[len++] = (op->addr.val >> addr_sh) & 0xff; + addr_sh -= 8; + } + + /* Put dummy bytes */ + for (i = 0; i < op->dummy.nbytes; i++) + gpram_cache[len++] = 0; + + /* Put output data */ + if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) { + memcpy(gpram_cache + len, op->data.buf.out, op->data.nbytes); + len += op->data.nbytes; + } + + /* Copy final output data to GPRAM */ + mtk_snfi_copy_to_gpram(priv, gpram_cache, len); + + /* Start one SPI transaction */ + if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN) + inlen = op->data.nbytes; + + ret = mtk_snfi_mac_trigger(priv, bus, len, inlen); + if (ret) + return ret; + + /* Copy input data from GPRAM */ + if (inlen) + mtk_snfi_copy_from_gpram(priv, gpram_cache, op->data.buf.in, + len, inlen); + + return 0; +} + +static int mtk_snfi_spi_probe(struct udevice *bus) +{ + struct mtk_snfi_priv *priv = dev_get_priv(bus); + int ret; + + priv->base = (void __iomem *)devfdt_get_addr(bus); + if (!priv->base) + return -EINVAL; + + ret = clk_get_by_name(bus, "nfi_clk", &priv->nfi_clk); + if (ret < 0) + return ret; + + ret = clk_get_by_name(bus, "pad_clk", &priv->pad_clk); + if (ret < 0) + return ret; + + clk_enable(&priv->nfi_clk); + clk_enable(&priv->pad_clk); + + return 0; +} + +static int mtk_snfi_set_speed(struct udevice *bus, uint speed) +{ + /* + * The SNFI does not have a bus clock divider. + * The bus clock is set in dts (pad_clk, UNIVPLL2_D8 = 50MHz). + */ + + return 0; +} + +static int mtk_snfi_set_mode(struct udevice *bus, uint mode) +{ + /* The SNFI supports only mode 0 */ + + if (mode) + return -EINVAL; + + return 0; +} + +static const struct spi_controller_mem_ops mtk_snfi_mem_ops = { + .adjust_op_size = mtk_snfi_adjust_op_size, + .supports_op = mtk_snfi_supports_op, + .exec_op = mtk_snfi_exec_op, +}; + +static const struct dm_spi_ops mtk_snfi_spi_ops = { + .mem_ops = &mtk_snfi_mem_ops, + .set_speed = mtk_snfi_set_speed, + .set_mode = mtk_snfi_set_mode, +}; + +static const struct udevice_id mtk_snfi_spi_ids[] = { + { .compatible = "mediatek,mtk-snfi-spi" }, + { } +}; + +U_BOOT_DRIVER(mtk_snfi_spi) = { + .name = "mtk_snfi_spi", + .id = UCLASS_SPI, + .of_match = mtk_snfi_spi_ids, + .ops = &mtk_snfi_spi_ops, + .priv_auto_alloc_size = sizeof(struct mtk_snfi_priv), + .probe = mtk_snfi_spi_probe, +}; diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 7aabebeff5fa35a6aef80bb1a412403138fd778e..7788ab995344332a1280b196c52de652dd55de5a 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -430,12 +430,14 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op) if (slave->max_write_size && len > slave->max_write_size) return -EINVAL; - if (op->data.dir == SPI_MEM_DATA_IN && slave->max_read_size) - op->data.nbytes = min(op->data.nbytes, + if (op->data.dir == SPI_MEM_DATA_IN) { + if (slave->max_read_size) + op->data.nbytes = min(op->data.nbytes, slave->max_read_size); - else if (slave->max_write_size) + } else if (slave->max_write_size) { op->data.nbytes = min(op->data.nbytes, slave->max_write_size - len); + } if (!op->data.nbytes) return -EINVAL; diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c new file mode 100644 index 0000000000000000000000000000000000000000..969bd4b75cbd318361c0b14e6fa9a283c373fd1f --- /dev/null +++ b/drivers/spi/spi-sifive.c @@ -0,0 +1,370 @@ +// 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 + +#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) + +struct sifive_spi { + void *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; + u32 num_cs; +}; + +static void sifive_spi_prep_device(struct sifive_spi *spi, + struct dm_spi_slave_platdata *slave) +{ + /* Update the chip select polarity */ + if (slave->mode & SPI_CS_HIGH) + spi->cs_inactive &= ~BIT(slave->cs); + else + spi->cs_inactive |= BIT(slave->cs); + writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF); + + /* Select the correct device */ + writel(slave->cs, spi->regs + SIFIVE_SPI_REG_CSID); +} + +static int sifive_spi_set_cs(struct sifive_spi *spi, + struct dm_spi_slave_platdata *slave) +{ + u32 cs_mode = SIFIVE_SPI_CSMODE_MODE_HOLD; + + if (slave->mode & SPI_CS_HIGH) + cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO; + + writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE); + + return 0; +} + +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, + bool is_rx_xfer, + struct dm_spi_slave_platdata *slave) +{ + 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 (slave->mode & SPI_LSB_FIRST) + cr |= SIFIVE_SPI_FMT_ENDIAN; + + /* Number of wires ? */ + cr &= ~SIFIVE_SPI_FMT_PROTO_MASK; + if ((slave->mode & SPI_TX_QUAD) || (slave->mode & SPI_RX_QUAD)) + cr |= SIFIVE_SPI_FMT_PROTO_QUAD; + else if ((slave->mode & SPI_TX_DUAL) || (slave->mode & SPI_RX_DUAL)) + cr |= SIFIVE_SPI_FMT_PROTO_DUAL; + else + cr |= SIFIVE_SPI_FMT_PROTO_SINGLE; + + /* SPI direction in/out ? */ + cr &= ~SIFIVE_SPI_FMT_DIR; + if (!is_rx_xfer) + 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_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct sifive_spi *spi = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev); + const unsigned char *tx_ptr = dout; + u8 *rx_ptr = din; + u32 remaining_len; + int ret; + + if (flags & SPI_XFER_BEGIN) { + sifive_spi_prep_device(spi, slave); + + ret = sifive_spi_set_cs(spi, slave); + if (ret) + return ret; + } + + sifive_spi_prep_transfer(spi, true, slave); + + remaining_len = bitlen / 8; + + while (remaining_len) { + int n_words, tx_words, rx_words; + + n_words = min(remaining_len, spi->fifo_depth); + + /* Enqueue n_words for transmission */ + if (tx_ptr) { + for (tx_words = 0; tx_words < n_words; ++tx_words) { + sifive_spi_tx(spi, tx_ptr); + sifive_spi_rx(spi, NULL); + tx_ptr++; + } + } + + /* Read out all the data from the RX FIFO */ + if (rx_ptr) { + for (rx_words = 0; rx_words < n_words; ++rx_words) { + sifive_spi_tx(spi, NULL); + sifive_spi_rx(spi, rx_ptr); + rx_ptr++; + } + } + + remaining_len -= n_words; + } + + if (flags & SPI_XFER_END) + sifive_spi_clear_cs(spi); + + return 0; +} + +static int sifive_spi_set_speed(struct udevice *bus, uint speed) +{ + struct sifive_spi *spi = dev_get_priv(bus); + 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); + + return 0; +} + +static int sifive_spi_set_mode(struct udevice *bus, uint mode) +{ + struct sifive_spi *spi = dev_get_priv(bus); + 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); + + return 0; +} + +static int sifive_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct sifive_spi *spi = dev_get_priv(bus); + + if (cs >= spi->num_cs) + return -EINVAL; + + return 0; +} + +static void sifive_spi_init_hw(struct sifive_spi *spi) +{ + 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) { + printf("Could not auto probe CS lines\n"); + return; + } + + spi->num_cs = ilog2(cs_bits) + 1; + if (spi->num_cs > SIFIVE_SPI_MAX_CS) { + printf("Invalid number of spi slaves\n"); + return; + } + + /* Watermark interrupts are disabled by default */ + writel(0, spi->regs + SIFIVE_SPI_REG_IE); + + /* 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 int sifive_spi_probe(struct udevice *bus) +{ + struct sifive_spi *spi = dev_get_priv(bus); + struct clk clkdev; + int ret; + + spi->regs = (void *)(ulong)dev_remap_addr(bus); + if (!spi->regs) + return -ENODEV; + + spi->fifo_depth = dev_read_u32_default(bus, + "sifive,fifo-depth", + SIFIVE_SPI_DEFAULT_DEPTH); + + spi->bits_per_word = dev_read_u32_default(bus, + "sifive,max-bits-per-word", + SIFIVE_SPI_DEFAULT_BITS); + + ret = clk_get_by_index(bus, 0, &clkdev); + if (ret) + return ret; + spi->freq = clk_get_rate(&clkdev); + + /* init the sifive spi hw */ + sifive_spi_init_hw(spi); + + return 0; +} + +static const struct dm_spi_ops sifive_spi_ops = { + .xfer = sifive_spi_xfer, + .set_speed = sifive_spi_set_speed, + .set_mode = sifive_spi_set_mode, + .cs_info = sifive_spi_cs_info, +}; + +static const struct udevice_id sifive_spi_ids[] = { + { .compatible = "sifive,spi0" }, + { } +}; + +U_BOOT_DRIVER(sifive_spi) = { + .name = "sifive_spi", + .id = UCLASS_SPI, + .of_match = sifive_spi_ids, + .ops = &sifive_spi_ops, + .priv_auto_alloc_size = sizeof(struct sifive_spi), + .probe = sifive_spi_probe, +};