On Fri, Nov 13, 2020 at 8:32 AM SkyLake Huang <skylake.hu...@mediatek.com> wrote: > > From: "SkyLake.Huang" <skylake.hu...@mediatek.com> > > This patch adds support for MTK SPI NOR controller, which you > can see on mt7622 & mt7629. > > This controller is designed only for SPI NOR. We can't adjust > its bus clock dynamically. Set clock in dts instead. > > Signed-off-by: SkyLake.Huang <skylake.hu...@mediatek.com> > --- > drivers/spi/Kconfig | 7 + > drivers/spi/Makefile | 1 + > drivers/spi/mtk_snor.c | 597 +++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 605 insertions(+) > create mode 100644 drivers/spi/mtk_snor.c > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index fae2040af8..670af450c1 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -174,6 +174,13 @@ config MT7621_SPI > the SPI NOR flash on platforms embedding this Ralink / MediaTek > SPI core, like MT7621/7628/7688. > > +config MTK_SNOR > + bool "Mediatek SPI-NOR controller driver" > + depends on SPI_MEM > + help > + Enable the Mediatek SPINOR controller driver. This driver has > + better read/write performance with NOR. > + > config MTK_SNFI_SPI > bool "Mediatek SPI memory controller driver" > depends on SPI_MEM > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index ae4f2958f8..efe92f6b18 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -38,6 +38,7 @@ obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o > obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o > obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o > obj-$(CONFIG_MTK_SNFI_SPI) += mtk_snfi_spi.o > +obj-$(CONFIG_MTK_SNOR) += mtk_snor.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 > diff --git a/drivers/spi/mtk_snor.c b/drivers/spi/mtk_snor.c > new file mode 100644 > index 0000000000..0a92f1c5a8 > --- /dev/null > +++ b/drivers/spi/mtk_snor.c > @@ -0,0 +1,597 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// > +// Mediatek SPI-NOR controller driver > +// > +// Copyright (C) 2020 SkyLake Huang <skylake.hu...@mediatek.com> > +// > +// Some parts are based on drivers/spi/spi-mtk-nor.c of linux version > + > +#include <common.h> > +#include <clk.h> > +#include <cpu_func.h> > +#include <dm.h> > +#include <errno.h> > +#include <spi.h> > +#include <spi-mem.h> > +#include <stdbool.h> > +#include <watchdog.h> > +#include <dm/pinctrl.h> > +#include <dm/device.h> > +#include <asm/dma-mapping.h> > +#include <linux/dma-direction.h> > +#include <linux/completion.h> > +#include <linux/bitops.h> > +#include <linux/io.h> > +#include <linux/iopoll.h> > + > +#define DRIVER_NAME "mtk-spi-nor" > + > +#define MTK_NOR_REG_CMD 0x00 > +#define MTK_NOR_CMD_WRSR BIT(5) > +#define MTK_NOR_CMD_WRITE BIT(4) > +#define MTK_NOR_CMD_PROGRAM BIT(2) > +#define MTK_NOR_CMD_RDSR BIT(1) > +#define MTK_NOR_CMD_READ BIT(0) > +#define MTK_NOR_CMD_MASK GENMASK(5, 0) > + > +#define MTK_NOR_REG_PRG_CNT 0x04 > +#define MTK_NOR_REG_RDSR 0x08 > +#define MTK_NOR_REG_RDATA 0x0c > + > +#define MTK_NOR_REG_RADR0 0x10 > +#define MTK_NOR_REG_RADR(n) (MTK_NOR_REG_RADR0 + 4 * (n)) > +#define MTK_NOR_REG_RADR3 0xc8 > + > +#define MTK_NOR_REG_WDATA 0x1c > + > +#define MTK_NOR_REG_PRGDATA0 0x20 > +#define MTK_NOR_REG_PRGDATA(n) (MTK_NOR_REG_PRGDATA0 + 4 * (n)) > +#define MTK_NOR_REG_PRGDATA_MAX 5 > + > +#define MTK_NOR_REG_SHIFT0 0x38 > +#define MTK_NOR_REG_SHIFT(n) (MTK_NOR_REG_SHIFT0 + 4 * (n)) > +#define MTK_NOR_REG_SHIFT_MAX 9 > + > +#define MTK_NOR_REG_CFG1 0x60 > +#define MTK_NOR_FAST_READ BIT(0) > + > +#define MTK_NOR_REG_CFG2 0x64 > +#define MTK_NOR_WR_CUSTOM_OP_EN BIT(4) > +#define MTK_NOR_WR_BUF_EN BIT(0) > + > +#define MTK_NOR_REG_PP_DATA 0x98 > + > +#define MTK_NOR_REG_IRQ_STAT 0xa8 > +#define MTK_NOR_REG_IRQ_EN 0xac > +#define MTK_NOR_IRQ_DMA BIT(7) > +#define MTK_NOR_IRQ_WRSR BIT(5) > +#define MTK_NOR_IRQ_MASK GENMASK(7, 0) > + > +#define MTK_NOR_REG_CFG3 0xb4 > +#define MTK_NOR_DISABLE_WREN BIT(7) > +#define MTK_NOR_DISABLE_SR_POLL BIT(5) > + > +#define MTK_NOR_REG_WP 0xc4 > +#define MTK_NOR_ENABLE_SF_CMD 0x30 > + > +#define MTK_NOR_REG_BUSCFG 0xcc > +#define MTK_NOR_4B_ADDR BIT(4) > +#define MTK_NOR_QUAD_ADDR BIT(3) > +#define MTK_NOR_QUAD_READ BIT(2) > +#define MTK_NOR_DUAL_ADDR BIT(1) > +#define MTK_NOR_DUAL_READ BIT(0) > +#define MTK_NOR_BUS_MODE_MASK GENMASK(4, 0) > + > +#define MTK_NOR_REG_DMA_CTL 0x718 > +#define MTK_NOR_DMA_START BIT(0) > + > +#define MTK_NOR_REG_DMA_FADR 0x71c > +#define MTK_NOR_REG_DMA_DADR 0x720 > +#define MTK_NOR_REG_DMA_END_DADR 0x724 > + > +#define MTK_NOR_PRG_MAX_SIZE 6 > +// Reading DMA src/dst addresses have to be 16-byte aligned > +#define MTK_NOR_DMA_ALIGN 16 > +#define MTK_NOR_DMA_ALIGN_MASK (MTK_NOR_DMA_ALIGN - 1) > +// and we allocate a bounce buffer if destination address isn't aligned. > +#define MTK_NOR_BOUNCE_BUF_SIZE PAGE_SIZE > + > +// Buffered page program can do one 128-byte transfer > +#define MTK_NOR_PP_SIZE 128 > + > +#define CLK_TO_US(priv, clkcnt) ((clkcnt) * (1000000) / > ((priv)->spi_freq)) > + > +#define MTK_NOR_UNLOCK_ALL 0x0 > + > +struct mtk_snor_priv { > + struct device *dev; > + void __iomem *base; > + u8 *buffer; > + struct clk spi_clk; > + struct clk ctlr_clk; > + unsigned int spi_freq; > + bool wbuf_en; > +}; > + > +static inline dma_addr_t __dma_map_single(void *vaddr, size_t len, > + enum dma_data_direction dir) > +{ > + unsigned long addr = (unsigned long)vaddr; > + > + len = ALIGN(len, ARCH_DMA_MINALIGN); > + if (dir == DMA_FROM_DEVICE) > + invalidate_dcache_range(addr, addr + len); > + else > + flush_dcache_range(addr, addr + len); > + > + return addr; > +} > + > +static inline void __dma_unmap_single(dma_addr_t addr, size_t len, > + enum dma_data_direction dir) > +{ > + len = ALIGN(len, ARCH_DMA_MINALIGN); > + if (dir != DMA_TO_DEVICE) > + invalidate_dcache_range(addr, addr + len); > +} > + > +static inline void mtk_snor_rmw(struct mtk_snor_priv *priv, u32 reg, u32 set, > + u32 clr) > +{ > + u32 val = readl(priv->base + reg); > + > + val &= ~clr; > + val |= set; > + writel(val, priv->base + reg); > +} > + > +static inline int mtk_snor_cmd_exec(struct mtk_snor_priv *priv, u32 cmd, > + ulong clk) > +{ > + unsigned long long delay = CLK_TO_US(priv, clk); > + u32 reg; > + int ret; > + > + writel(cmd, priv->base + MTK_NOR_REG_CMD); > + > + delay = (delay + 1) * 200; > + ret = readl_poll_timeout(priv->base + MTK_NOR_REG_CMD, reg, !(reg & > cmd), > + delay); > + if (ret < 0) > + dev_err(priv->dev, "command %u timeout.\n", cmd); > + return ret; > +} > + > +static void mtk_snor_set_addr(struct mtk_snor_priv *priv, > + const struct spi_mem_op *op) > +{ > + u32 addr = op->addr.val; > + int i; > + > + for (i = 0; i < 3; i++) { > + writeb(addr & 0xff, priv->base + MTK_NOR_REG_RADR(i)); > + addr >>= 8; > + } > + if (op->addr.nbytes == 4) { > + writeb(addr & 0xff, priv->base + MTK_NOR_REG_RADR3); > + mtk_snor_rmw(priv, MTK_NOR_REG_BUSCFG, MTK_NOR_4B_ADDR, 0); > + } else { > + mtk_snor_rmw(priv, MTK_NOR_REG_BUSCFG, 0, MTK_NOR_4B_ADDR); > + } > +} > + > +static bool mtk_snor_match_read(const struct spi_mem_op *op) > +{ > + int dummy = 0; > + > + if (op->dummy.buswidth) > + dummy = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth; > + > + if (op->data.buswidth == 2 || op->data.buswidth == 4) { > + if (op->addr.buswidth == 1) > + return dummy == 8; > + else if (op->addr.buswidth == 2) > + return dummy == 4; > + else if (op->addr.buswidth == 4) > + return dummy == 6; > + } else if ((op->addr.buswidth == 0) && (op->data.buswidth == 1)) { > + return dummy == 0; > + } else if ((op->addr.buswidth == 1) && (op->data.buswidth == 1)) { > + if (op->cmd.opcode == 0x03) > + return dummy == 0; > + else if (op->cmd.opcode == 0x0b) > + return dummy == 8; > + } > + return false; > +}
This looks like flash command handling on the SPI side. Can you try to handle this driver at mtd/spi side as UCLASS_SPI_FLASH?