
On Fri, Nov 13, 2020 at 8:32 AM SkyLake Huang SkyLake.Huang@mediatek.com wrote:
From: "SkyLake.Huang" skylake.huang@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.huang@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.Huang@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?