// SPDX-License-Identifier: GPL-2.0
//
// Mediatek SPI NOR controller driver
//
// Copyright (C) 2020 Chuanhong Guo <gch981213@gmail.com>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/string.h>
#define DRIVER_NAME "mtk-spi-nor"
#define MTK_NOR_REG_CMD 0x00
#define MTK_NOR_CMD_WRITE BIT(4)
#define MTK_NOR_CMD_PROGRAM BIT(2)
#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_PRG_CNT_MAX 56
#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_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_REG_CG_DIS 0x728
#define MTK_NOR_SFC_SW_RST BIT(2)
#define MTK_NOR_REG_DMA_DADR_HB 0x738
#define MTK_NOR_REG_DMA_END_DADR_HB 0x73c
#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(sp, clkcnt) DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000)
struct mtk_nor_caps {
u8 dma_bits;
/* extra_dummy_bit is adding for the IP of new SoCs.
* Some new SoCs modify the timing of fetching registers' values
* and IDs of nor flash, they need a extra_dummy_bit which can add
* more clock cycles for fetching data.
*/
u8 extra_dummy_bit;
};
struct mtk_nor {
struct spi_controller *ctlr;
struct device *dev;
void __iomem *base;
u8 *buffer;
dma_addr_t buffer_dma;
struct clk *spi_clk;
struct clk *ctlr_clk;
struct clk *axi_clk;
struct clk *axi_s_clk;
unsigned int spi_freq;
bool wbuf_en;
bool has_irq;
bool high_dma;
struct completion op_done;
const struct mtk_nor_caps *caps;
};
static inline void mtk_nor_rmw(struct mtk_nor *sp, u32 reg, u32 set, u32 clr)
{
u32 val = readl(sp->base + reg);
val &= ~clr;
val |= set;
writel(val, sp->base + reg);
}
static inline int mtk_nor_cmd_exec(struct mtk_nor *sp, u32 cmd, ulong clk)
{
ulong delay = CLK_TO_US(sp, clk);
u32 reg;
int ret;
writel(cmd, sp->base + MTK_NOR_REG_CMD);
ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CMD, reg, !(reg & cmd),
delay / 3, (delay + 1) * 200);
if (ret < 0)
dev_err(sp->dev, "command %u timeout.\n", cmd);
return ret;
}
static void mtk_nor_reset(struct mtk_nor *sp)
{
mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, 0, MTK_NOR_SFC_SW_RST);
mb(); /* flush previous writes */
mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, MTK_NOR_SFC_SW_RST, 0);
mb(); /* flush previous writes */
writel(MTK_NOR_ENABLE_SF_CMD, sp->base + MTK_NOR_REG_WP);
}
static void mtk_nor_set_addr(struct mtk_nor *sp, const struct spi_mem_op *op)
{
u32 addr = op->addr.val;
int i;
for (i = 0; i < 3; i++) {
writeb(addr & 0xff, sp->base + MTK_NOR_REG_RADR(i));
addr >>= 8;
}
if (op->addr.nbytes == 4) {
writeb(addr & 0xff, sp->base + MTK_NOR_REG_RADR3);
mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, MTK_NOR_4B_ADDR, 0);
} else {
mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, 0, MTK_NOR_4B_ADDR);
}
}
static bool need_bounce(struct mtk_nor *sp, const struct spi_mem_op *op)
{
return ((