162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Mediatek SPI NOR controller driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2020 Chuanhong Guo <gch981213@gmail.com> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bits.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/completion.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/iopoll.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/spi/spi.h> 2062306a36Sopenharmony_ci#include <linux/spi/spi-mem.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define DRIVER_NAME "mtk-spi-nor" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MTK_NOR_REG_CMD 0x00 2662306a36Sopenharmony_ci#define MTK_NOR_CMD_WRITE BIT(4) 2762306a36Sopenharmony_ci#define MTK_NOR_CMD_PROGRAM BIT(2) 2862306a36Sopenharmony_ci#define MTK_NOR_CMD_READ BIT(0) 2962306a36Sopenharmony_ci#define MTK_NOR_CMD_MASK GENMASK(5, 0) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MTK_NOR_REG_PRG_CNT 0x04 3262306a36Sopenharmony_ci#define MTK_NOR_PRG_CNT_MAX 56 3362306a36Sopenharmony_ci#define MTK_NOR_REG_RDATA 0x0c 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MTK_NOR_REG_RADR0 0x10 3662306a36Sopenharmony_ci#define MTK_NOR_REG_RADR(n) (MTK_NOR_REG_RADR0 + 4 * (n)) 3762306a36Sopenharmony_ci#define MTK_NOR_REG_RADR3 0xc8 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define MTK_NOR_REG_WDATA 0x1c 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define MTK_NOR_REG_PRGDATA0 0x20 4262306a36Sopenharmony_ci#define MTK_NOR_REG_PRGDATA(n) (MTK_NOR_REG_PRGDATA0 + 4 * (n)) 4362306a36Sopenharmony_ci#define MTK_NOR_REG_PRGDATA_MAX 5 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define MTK_NOR_REG_SHIFT0 0x38 4662306a36Sopenharmony_ci#define MTK_NOR_REG_SHIFT(n) (MTK_NOR_REG_SHIFT0 + 4 * (n)) 4762306a36Sopenharmony_ci#define MTK_NOR_REG_SHIFT_MAX 9 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define MTK_NOR_REG_CFG1 0x60 5062306a36Sopenharmony_ci#define MTK_NOR_FAST_READ BIT(0) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define MTK_NOR_REG_CFG2 0x64 5362306a36Sopenharmony_ci#define MTK_NOR_WR_CUSTOM_OP_EN BIT(4) 5462306a36Sopenharmony_ci#define MTK_NOR_WR_BUF_EN BIT(0) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define MTK_NOR_REG_PP_DATA 0x98 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define MTK_NOR_REG_IRQ_STAT 0xa8 5962306a36Sopenharmony_ci#define MTK_NOR_REG_IRQ_EN 0xac 6062306a36Sopenharmony_ci#define MTK_NOR_IRQ_DMA BIT(7) 6162306a36Sopenharmony_ci#define MTK_NOR_IRQ_MASK GENMASK(7, 0) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define MTK_NOR_REG_CFG3 0xb4 6462306a36Sopenharmony_ci#define MTK_NOR_DISABLE_WREN BIT(7) 6562306a36Sopenharmony_ci#define MTK_NOR_DISABLE_SR_POLL BIT(5) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define MTK_NOR_REG_WP 0xc4 6862306a36Sopenharmony_ci#define MTK_NOR_ENABLE_SF_CMD 0x30 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define MTK_NOR_REG_BUSCFG 0xcc 7162306a36Sopenharmony_ci#define MTK_NOR_4B_ADDR BIT(4) 7262306a36Sopenharmony_ci#define MTK_NOR_QUAD_ADDR BIT(3) 7362306a36Sopenharmony_ci#define MTK_NOR_QUAD_READ BIT(2) 7462306a36Sopenharmony_ci#define MTK_NOR_DUAL_ADDR BIT(1) 7562306a36Sopenharmony_ci#define MTK_NOR_DUAL_READ BIT(0) 7662306a36Sopenharmony_ci#define MTK_NOR_BUS_MODE_MASK GENMASK(4, 0) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define MTK_NOR_REG_DMA_CTL 0x718 7962306a36Sopenharmony_ci#define MTK_NOR_DMA_START BIT(0) 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define MTK_NOR_REG_DMA_FADR 0x71c 8262306a36Sopenharmony_ci#define MTK_NOR_REG_DMA_DADR 0x720 8362306a36Sopenharmony_ci#define MTK_NOR_REG_DMA_END_DADR 0x724 8462306a36Sopenharmony_ci#define MTK_NOR_REG_CG_DIS 0x728 8562306a36Sopenharmony_ci#define MTK_NOR_SFC_SW_RST BIT(2) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define MTK_NOR_REG_DMA_DADR_HB 0x738 8862306a36Sopenharmony_ci#define MTK_NOR_REG_DMA_END_DADR_HB 0x73c 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define MTK_NOR_PRG_MAX_SIZE 6 9162306a36Sopenharmony_ci// Reading DMA src/dst addresses have to be 16-byte aligned 9262306a36Sopenharmony_ci#define MTK_NOR_DMA_ALIGN 16 9362306a36Sopenharmony_ci#define MTK_NOR_DMA_ALIGN_MASK (MTK_NOR_DMA_ALIGN - 1) 9462306a36Sopenharmony_ci// and we allocate a bounce buffer if destination address isn't aligned. 9562306a36Sopenharmony_ci#define MTK_NOR_BOUNCE_BUF_SIZE PAGE_SIZE 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci// Buffered page program can do one 128-byte transfer 9862306a36Sopenharmony_ci#define MTK_NOR_PP_SIZE 128 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define CLK_TO_US(sp, clkcnt) DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct mtk_nor_caps { 10362306a36Sopenharmony_ci u8 dma_bits; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* extra_dummy_bit is adding for the IP of new SoCs. 10662306a36Sopenharmony_ci * Some new SoCs modify the timing of fetching registers' values 10762306a36Sopenharmony_ci * and IDs of nor flash, they need a extra_dummy_bit which can add 10862306a36Sopenharmony_ci * more clock cycles for fetching data. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci u8 extra_dummy_bit; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct mtk_nor { 11462306a36Sopenharmony_ci struct spi_controller *ctlr; 11562306a36Sopenharmony_ci struct device *dev; 11662306a36Sopenharmony_ci void __iomem *base; 11762306a36Sopenharmony_ci u8 *buffer; 11862306a36Sopenharmony_ci dma_addr_t buffer_dma; 11962306a36Sopenharmony_ci struct clk *spi_clk; 12062306a36Sopenharmony_ci struct clk *ctlr_clk; 12162306a36Sopenharmony_ci struct clk *axi_clk; 12262306a36Sopenharmony_ci struct clk *axi_s_clk; 12362306a36Sopenharmony_ci unsigned int spi_freq; 12462306a36Sopenharmony_ci bool wbuf_en; 12562306a36Sopenharmony_ci bool has_irq; 12662306a36Sopenharmony_ci bool high_dma; 12762306a36Sopenharmony_ci struct completion op_done; 12862306a36Sopenharmony_ci const struct mtk_nor_caps *caps; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic inline void mtk_nor_rmw(struct mtk_nor *sp, u32 reg, u32 set, u32 clr) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u32 val = readl(sp->base + reg); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci val &= ~clr; 13662306a36Sopenharmony_ci val |= set; 13762306a36Sopenharmony_ci writel(val, sp->base + reg); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic inline int mtk_nor_cmd_exec(struct mtk_nor *sp, u32 cmd, ulong clk) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci ulong delay = CLK_TO_US(sp, clk); 14362306a36Sopenharmony_ci u32 reg; 14462306a36Sopenharmony_ci int ret; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci writel(cmd, sp->base + MTK_NOR_REG_CMD); 14762306a36Sopenharmony_ci ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CMD, reg, !(reg & cmd), 14862306a36Sopenharmony_ci delay / 3, (delay + 1) * 200); 14962306a36Sopenharmony_ci if (ret < 0) 15062306a36Sopenharmony_ci dev_err(sp->dev, "command %u timeout.\n", cmd); 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void mtk_nor_reset(struct mtk_nor *sp) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, 0, MTK_NOR_SFC_SW_RST); 15762306a36Sopenharmony_ci mb(); /* flush previous writes */ 15862306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, MTK_NOR_SFC_SW_RST, 0); 15962306a36Sopenharmony_ci mb(); /* flush previous writes */ 16062306a36Sopenharmony_ci writel(MTK_NOR_ENABLE_SF_CMD, sp->base + MTK_NOR_REG_WP); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void mtk_nor_set_addr(struct mtk_nor *sp, const struct spi_mem_op *op) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci u32 addr = op->addr.val; 16662306a36Sopenharmony_ci int i; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 16962306a36Sopenharmony_ci writeb(addr & 0xff, sp->base + MTK_NOR_REG_RADR(i)); 17062306a36Sopenharmony_ci addr >>= 8; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci if (op->addr.nbytes == 4) { 17362306a36Sopenharmony_ci writeb(addr & 0xff, sp->base + MTK_NOR_REG_RADR3); 17462306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, MTK_NOR_4B_ADDR, 0); 17562306a36Sopenharmony_ci } else { 17662306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, 0, MTK_NOR_4B_ADDR); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic bool need_bounce(struct mtk_nor *sp, const struct spi_mem_op *op) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci return ((uintptr_t)op->data.buf.in & MTK_NOR_DMA_ALIGN_MASK); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic bool mtk_nor_match_read(const struct spi_mem_op *op) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int dummy = 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (op->dummy.nbytes) 19062306a36Sopenharmony_ci dummy = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if ((op->data.buswidth == 2) || (op->data.buswidth == 4)) { 19362306a36Sopenharmony_ci if (op->addr.buswidth == 1) 19462306a36Sopenharmony_ci return dummy == 8; 19562306a36Sopenharmony_ci else if (op->addr.buswidth == 2) 19662306a36Sopenharmony_ci return dummy == 4; 19762306a36Sopenharmony_ci else if (op->addr.buswidth == 4) 19862306a36Sopenharmony_ci return dummy == 6; 19962306a36Sopenharmony_ci } else if ((op->addr.buswidth == 1) && (op->data.buswidth == 1)) { 20062306a36Sopenharmony_ci if (op->cmd.opcode == 0x03) 20162306a36Sopenharmony_ci return dummy == 0; 20262306a36Sopenharmony_ci else if (op->cmd.opcode == 0x0b) 20362306a36Sopenharmony_ci return dummy == 8; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci return false; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic bool mtk_nor_match_prg(const struct spi_mem_op *op) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci int tx_len, rx_len, prg_len, prg_left; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci // prg mode is spi-only. 21362306a36Sopenharmony_ci if ((op->cmd.buswidth > 1) || (op->addr.buswidth > 1) || 21462306a36Sopenharmony_ci (op->dummy.buswidth > 1) || (op->data.buswidth > 1)) 21562306a36Sopenharmony_ci return false; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci tx_len = op->cmd.nbytes + op->addr.nbytes; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT) { 22062306a36Sopenharmony_ci // count dummy bytes only if we need to write data after it 22162306a36Sopenharmony_ci tx_len += op->dummy.nbytes; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci // leave at least one byte for data 22462306a36Sopenharmony_ci if (tx_len > MTK_NOR_REG_PRGDATA_MAX) 22562306a36Sopenharmony_ci return false; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci // if there's no addr, meaning adjust_op_size is impossible, 22862306a36Sopenharmony_ci // check data length as well. 22962306a36Sopenharmony_ci if ((!op->addr.nbytes) && 23062306a36Sopenharmony_ci (tx_len + op->data.nbytes > MTK_NOR_REG_PRGDATA_MAX + 1)) 23162306a36Sopenharmony_ci return false; 23262306a36Sopenharmony_ci } else if (op->data.dir == SPI_MEM_DATA_IN) { 23362306a36Sopenharmony_ci if (tx_len > MTK_NOR_REG_PRGDATA_MAX + 1) 23462306a36Sopenharmony_ci return false; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci rx_len = op->data.nbytes; 23762306a36Sopenharmony_ci prg_left = MTK_NOR_PRG_CNT_MAX / 8 - tx_len - op->dummy.nbytes; 23862306a36Sopenharmony_ci if (prg_left > MTK_NOR_REG_SHIFT_MAX + 1) 23962306a36Sopenharmony_ci prg_left = MTK_NOR_REG_SHIFT_MAX + 1; 24062306a36Sopenharmony_ci if (rx_len > prg_left) { 24162306a36Sopenharmony_ci if (!op->addr.nbytes) 24262306a36Sopenharmony_ci return false; 24362306a36Sopenharmony_ci rx_len = prg_left; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci prg_len = tx_len + op->dummy.nbytes + rx_len; 24762306a36Sopenharmony_ci if (prg_len > MTK_NOR_PRG_CNT_MAX / 8) 24862306a36Sopenharmony_ci return false; 24962306a36Sopenharmony_ci } else { 25062306a36Sopenharmony_ci prg_len = tx_len + op->dummy.nbytes; 25162306a36Sopenharmony_ci if (prg_len > MTK_NOR_PRG_CNT_MAX / 8) 25262306a36Sopenharmony_ci return false; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci return true; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void mtk_nor_adj_prg_size(struct spi_mem_op *op) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int tx_len, tx_left, prg_left; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci tx_len = op->cmd.nbytes + op->addr.nbytes; 26262306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT) { 26362306a36Sopenharmony_ci tx_len += op->dummy.nbytes; 26462306a36Sopenharmony_ci tx_left = MTK_NOR_REG_PRGDATA_MAX + 1 - tx_len; 26562306a36Sopenharmony_ci if (op->data.nbytes > tx_left) 26662306a36Sopenharmony_ci op->data.nbytes = tx_left; 26762306a36Sopenharmony_ci } else if (op->data.dir == SPI_MEM_DATA_IN) { 26862306a36Sopenharmony_ci prg_left = MTK_NOR_PRG_CNT_MAX / 8 - tx_len - op->dummy.nbytes; 26962306a36Sopenharmony_ci if (prg_left > MTK_NOR_REG_SHIFT_MAX + 1) 27062306a36Sopenharmony_ci prg_left = MTK_NOR_REG_SHIFT_MAX + 1; 27162306a36Sopenharmony_ci if (op->data.nbytes > prg_left) 27262306a36Sopenharmony_ci op->data.nbytes = prg_left; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (!op->data.nbytes) 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) { 28462306a36Sopenharmony_ci if ((op->data.dir == SPI_MEM_DATA_IN) && 28562306a36Sopenharmony_ci mtk_nor_match_read(op)) { 28662306a36Sopenharmony_ci // limit size to prevent timeout calculation overflow 28762306a36Sopenharmony_ci if (op->data.nbytes > 0x400000) 28862306a36Sopenharmony_ci op->data.nbytes = 0x400000; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if ((op->addr.val & MTK_NOR_DMA_ALIGN_MASK) || 29162306a36Sopenharmony_ci (op->data.nbytes < MTK_NOR_DMA_ALIGN)) 29262306a36Sopenharmony_ci op->data.nbytes = 1; 29362306a36Sopenharmony_ci else if (!need_bounce(sp, op)) 29462306a36Sopenharmony_ci op->data.nbytes &= ~MTK_NOR_DMA_ALIGN_MASK; 29562306a36Sopenharmony_ci else if (op->data.nbytes > MTK_NOR_BOUNCE_BUF_SIZE) 29662306a36Sopenharmony_ci op->data.nbytes = MTK_NOR_BOUNCE_BUF_SIZE; 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci } else if (op->data.dir == SPI_MEM_DATA_OUT) { 29962306a36Sopenharmony_ci if (op->data.nbytes >= MTK_NOR_PP_SIZE) 30062306a36Sopenharmony_ci op->data.nbytes = MTK_NOR_PP_SIZE; 30162306a36Sopenharmony_ci else 30262306a36Sopenharmony_ci op->data.nbytes = 1; 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci mtk_nor_adj_prg_size(op); 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic bool mtk_nor_supports_op(struct spi_mem *mem, 31262306a36Sopenharmony_ci const struct spi_mem_op *op) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci if (!spi_mem_default_supports_op(mem, op)) 31562306a36Sopenharmony_ci return false; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (op->cmd.buswidth != 1) 31862306a36Sopenharmony_ci return false; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) { 32162306a36Sopenharmony_ci switch (op->data.dir) { 32262306a36Sopenharmony_ci case SPI_MEM_DATA_IN: 32362306a36Sopenharmony_ci if (mtk_nor_match_read(op)) 32462306a36Sopenharmony_ci return true; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case SPI_MEM_DATA_OUT: 32762306a36Sopenharmony_ci if ((op->addr.buswidth == 1) && 32862306a36Sopenharmony_ci (op->dummy.nbytes == 0) && 32962306a36Sopenharmony_ci (op->data.buswidth == 1)) 33062306a36Sopenharmony_ci return true; 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci default: 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return mtk_nor_match_prg(op); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void mtk_nor_setup_bus(struct mtk_nor *sp, const struct spi_mem_op *op) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci u32 reg = 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (op->addr.nbytes == 4) 34562306a36Sopenharmony_ci reg |= MTK_NOR_4B_ADDR; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (op->data.buswidth == 4) { 34862306a36Sopenharmony_ci reg |= MTK_NOR_QUAD_READ; 34962306a36Sopenharmony_ci writeb(op->cmd.opcode, sp->base + MTK_NOR_REG_PRGDATA(4)); 35062306a36Sopenharmony_ci if (op->addr.buswidth == 4) 35162306a36Sopenharmony_ci reg |= MTK_NOR_QUAD_ADDR; 35262306a36Sopenharmony_ci } else if (op->data.buswidth == 2) { 35362306a36Sopenharmony_ci reg |= MTK_NOR_DUAL_READ; 35462306a36Sopenharmony_ci writeb(op->cmd.opcode, sp->base + MTK_NOR_REG_PRGDATA(3)); 35562306a36Sopenharmony_ci if (op->addr.buswidth == 2) 35662306a36Sopenharmony_ci reg |= MTK_NOR_DUAL_ADDR; 35762306a36Sopenharmony_ci } else { 35862306a36Sopenharmony_ci if (op->cmd.opcode == 0x0b) 35962306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_CFG1, MTK_NOR_FAST_READ, 0); 36062306a36Sopenharmony_ci else 36162306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_CFG1, 0, MTK_NOR_FAST_READ); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, reg, MTK_NOR_BUS_MODE_MASK); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int mtk_nor_dma_exec(struct mtk_nor *sp, u32 from, unsigned int length, 36762306a36Sopenharmony_ci dma_addr_t dma_addr) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci int ret = 0; 37062306a36Sopenharmony_ci u32 delay, timeout; 37162306a36Sopenharmony_ci u32 reg; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci writel(from, sp->base + MTK_NOR_REG_DMA_FADR); 37462306a36Sopenharmony_ci writel(dma_addr, sp->base + MTK_NOR_REG_DMA_DADR); 37562306a36Sopenharmony_ci writel(dma_addr + length, sp->base + MTK_NOR_REG_DMA_END_DADR); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (sp->high_dma) { 37862306a36Sopenharmony_ci writel(upper_32_bits(dma_addr), 37962306a36Sopenharmony_ci sp->base + MTK_NOR_REG_DMA_DADR_HB); 38062306a36Sopenharmony_ci writel(upper_32_bits(dma_addr + length), 38162306a36Sopenharmony_ci sp->base + MTK_NOR_REG_DMA_END_DADR_HB); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (sp->has_irq) { 38562306a36Sopenharmony_ci reinit_completion(&sp->op_done); 38662306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_IRQ_EN, MTK_NOR_IRQ_DMA, 0); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_DMA_CTL, MTK_NOR_DMA_START, 0); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci delay = CLK_TO_US(sp, (length + 5) * BITS_PER_BYTE); 39262306a36Sopenharmony_ci timeout = (delay + 1) * 100; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (sp->has_irq) { 39562306a36Sopenharmony_ci if (!wait_for_completion_timeout(&sp->op_done, 39662306a36Sopenharmony_ci usecs_to_jiffies(max(timeout, 10000U)))) 39762306a36Sopenharmony_ci ret = -ETIMEDOUT; 39862306a36Sopenharmony_ci } else { 39962306a36Sopenharmony_ci ret = readl_poll_timeout(sp->base + MTK_NOR_REG_DMA_CTL, reg, 40062306a36Sopenharmony_ci !(reg & MTK_NOR_DMA_START), delay / 3, 40162306a36Sopenharmony_ci timeout); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (ret < 0) 40562306a36Sopenharmony_ci dev_err(sp->dev, "dma read timeout.\n"); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return ret; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int mtk_nor_read_bounce(struct mtk_nor *sp, const struct spi_mem_op *op) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci unsigned int rdlen; 41362306a36Sopenharmony_ci int ret; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (op->data.nbytes & MTK_NOR_DMA_ALIGN_MASK) 41662306a36Sopenharmony_ci rdlen = (op->data.nbytes + MTK_NOR_DMA_ALIGN) & ~MTK_NOR_DMA_ALIGN_MASK; 41762306a36Sopenharmony_ci else 41862306a36Sopenharmony_ci rdlen = op->data.nbytes; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ret = mtk_nor_dma_exec(sp, op->addr.val, rdlen, sp->buffer_dma); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!ret) 42362306a36Sopenharmony_ci memcpy(op->data.buf.in, sp->buffer, op->data.nbytes); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return ret; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int mtk_nor_read_dma(struct mtk_nor *sp, const struct spi_mem_op *op) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci int ret; 43162306a36Sopenharmony_ci dma_addr_t dma_addr; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (need_bounce(sp, op)) 43462306a36Sopenharmony_ci return mtk_nor_read_bounce(sp, op); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci dma_addr = dma_map_single(sp->dev, op->data.buf.in, 43762306a36Sopenharmony_ci op->data.nbytes, DMA_FROM_DEVICE); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (dma_mapping_error(sp->dev, dma_addr)) 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ret = mtk_nor_dma_exec(sp, op->addr.val, op->data.nbytes, dma_addr); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci dma_unmap_single(sp->dev, dma_addr, op->data.nbytes, DMA_FROM_DEVICE); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int mtk_nor_read_pio(struct mtk_nor *sp, const struct spi_mem_op *op) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci u8 *buf = op->data.buf.in; 45262306a36Sopenharmony_ci int ret; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci ret = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_READ, 6 * BITS_PER_BYTE); 45562306a36Sopenharmony_ci if (!ret) 45662306a36Sopenharmony_ci buf[0] = readb(sp->base + MTK_NOR_REG_RDATA); 45762306a36Sopenharmony_ci return ret; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int mtk_nor_setup_write_buffer(struct mtk_nor *sp, bool on) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci int ret; 46362306a36Sopenharmony_ci u32 val; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!(sp->wbuf_en ^ on)) 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci val = readl(sp->base + MTK_NOR_REG_CFG2); 46962306a36Sopenharmony_ci if (on) { 47062306a36Sopenharmony_ci writel(val | MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); 47162306a36Sopenharmony_ci ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, 47262306a36Sopenharmony_ci val & MTK_NOR_WR_BUF_EN, 0, 10000); 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci writel(val & ~MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); 47562306a36Sopenharmony_ci ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, 47662306a36Sopenharmony_ci !(val & MTK_NOR_WR_BUF_EN), 0, 10000); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (!ret) 48062306a36Sopenharmony_ci sp->wbuf_en = on; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return ret; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int mtk_nor_pp_buffered(struct mtk_nor *sp, const struct spi_mem_op *op) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci const u8 *buf = op->data.buf.out; 48862306a36Sopenharmony_ci u32 val; 48962306a36Sopenharmony_ci int ret, i; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ret = mtk_nor_setup_write_buffer(sp, true); 49262306a36Sopenharmony_ci if (ret < 0) 49362306a36Sopenharmony_ci return ret; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci for (i = 0; i < op->data.nbytes; i += 4) { 49662306a36Sopenharmony_ci val = buf[i + 3] << 24 | buf[i + 2] << 16 | buf[i + 1] << 8 | 49762306a36Sopenharmony_ci buf[i]; 49862306a36Sopenharmony_ci writel(val, sp->base + MTK_NOR_REG_PP_DATA); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci return mtk_nor_cmd_exec(sp, MTK_NOR_CMD_WRITE, 50162306a36Sopenharmony_ci (op->data.nbytes + 5) * BITS_PER_BYTE); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int mtk_nor_pp_unbuffered(struct mtk_nor *sp, 50562306a36Sopenharmony_ci const struct spi_mem_op *op) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci const u8 *buf = op->data.buf.out; 50862306a36Sopenharmony_ci int ret; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ret = mtk_nor_setup_write_buffer(sp, false); 51162306a36Sopenharmony_ci if (ret < 0) 51262306a36Sopenharmony_ci return ret; 51362306a36Sopenharmony_ci writeb(buf[0], sp->base + MTK_NOR_REG_WDATA); 51462306a36Sopenharmony_ci return mtk_nor_cmd_exec(sp, MTK_NOR_CMD_WRITE, 6 * BITS_PER_BYTE); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int mtk_nor_spi_mem_prg(struct mtk_nor *sp, const struct spi_mem_op *op) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci int rx_len = 0; 52062306a36Sopenharmony_ci int reg_offset = MTK_NOR_REG_PRGDATA_MAX; 52162306a36Sopenharmony_ci int tx_len, prg_len; 52262306a36Sopenharmony_ci int i, ret; 52362306a36Sopenharmony_ci void __iomem *reg; 52462306a36Sopenharmony_ci u8 bufbyte; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci tx_len = op->cmd.nbytes + op->addr.nbytes; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci // count dummy bytes only if we need to write data after it 52962306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT) 53062306a36Sopenharmony_ci tx_len += op->dummy.nbytes + op->data.nbytes; 53162306a36Sopenharmony_ci else if (op->data.dir == SPI_MEM_DATA_IN) 53262306a36Sopenharmony_ci rx_len = op->data.nbytes; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci prg_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes + 53562306a36Sopenharmony_ci op->data.nbytes; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci // an invalid op may reach here if the caller calls exec_op without 53862306a36Sopenharmony_ci // adjust_op_size. return -EINVAL instead of -ENOTSUPP so that 53962306a36Sopenharmony_ci // spi-mem won't try this op again with generic spi transfers. 54062306a36Sopenharmony_ci if ((tx_len > MTK_NOR_REG_PRGDATA_MAX + 1) || 54162306a36Sopenharmony_ci (rx_len > MTK_NOR_REG_SHIFT_MAX + 1) || 54262306a36Sopenharmony_ci (prg_len > MTK_NOR_PRG_CNT_MAX / 8)) 54362306a36Sopenharmony_ci return -EINVAL; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci // fill tx data 54662306a36Sopenharmony_ci for (i = op->cmd.nbytes; i > 0; i--, reg_offset--) { 54762306a36Sopenharmony_ci reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); 54862306a36Sopenharmony_ci bufbyte = (op->cmd.opcode >> ((i - 1) * BITS_PER_BYTE)) & 0xff; 54962306a36Sopenharmony_ci writeb(bufbyte, reg); 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci for (i = op->addr.nbytes; i > 0; i--, reg_offset--) { 55362306a36Sopenharmony_ci reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); 55462306a36Sopenharmony_ci bufbyte = (op->addr.val >> ((i - 1) * BITS_PER_BYTE)) & 0xff; 55562306a36Sopenharmony_ci writeb(bufbyte, reg); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT) { 55962306a36Sopenharmony_ci for (i = 0; i < op->dummy.nbytes; i++, reg_offset--) { 56062306a36Sopenharmony_ci reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); 56162306a36Sopenharmony_ci writeb(0, reg); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci for (i = 0; i < op->data.nbytes; i++, reg_offset--) { 56562306a36Sopenharmony_ci reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); 56662306a36Sopenharmony_ci writeb(((const u8 *)(op->data.buf.out))[i], reg); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci for (; reg_offset >= 0; reg_offset--) { 57162306a36Sopenharmony_ci reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); 57262306a36Sopenharmony_ci writeb(0, reg); 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci // trigger op 57662306a36Sopenharmony_ci if (rx_len) 57762306a36Sopenharmony_ci writel(prg_len * BITS_PER_BYTE + sp->caps->extra_dummy_bit, 57862306a36Sopenharmony_ci sp->base + MTK_NOR_REG_PRG_CNT); 57962306a36Sopenharmony_ci else 58062306a36Sopenharmony_ci writel(prg_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ret = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_PROGRAM, 58362306a36Sopenharmony_ci prg_len * BITS_PER_BYTE); 58462306a36Sopenharmony_ci if (ret) 58562306a36Sopenharmony_ci return ret; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci // fetch read data 58862306a36Sopenharmony_ci reg_offset = 0; 58962306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_IN) { 59062306a36Sopenharmony_ci for (i = op->data.nbytes - 1; i >= 0; i--, reg_offset++) { 59162306a36Sopenharmony_ci reg = sp->base + MTK_NOR_REG_SHIFT(reg_offset); 59262306a36Sopenharmony_ci ((u8 *)(op->data.buf.in))[i] = readb(reg); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master); 60262306a36Sopenharmony_ci int ret; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if ((op->data.nbytes == 0) || 60562306a36Sopenharmony_ci ((op->addr.nbytes != 3) && (op->addr.nbytes != 4))) 60662306a36Sopenharmony_ci return mtk_nor_spi_mem_prg(sp, op); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (op->data.dir == SPI_MEM_DATA_OUT) { 60962306a36Sopenharmony_ci mtk_nor_set_addr(sp, op); 61062306a36Sopenharmony_ci writeb(op->cmd.opcode, sp->base + MTK_NOR_REG_PRGDATA0); 61162306a36Sopenharmony_ci if (op->data.nbytes == MTK_NOR_PP_SIZE) 61262306a36Sopenharmony_ci return mtk_nor_pp_buffered(sp, op); 61362306a36Sopenharmony_ci return mtk_nor_pp_unbuffered(sp, op); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op)) { 61762306a36Sopenharmony_ci ret = mtk_nor_setup_write_buffer(sp, false); 61862306a36Sopenharmony_ci if (ret < 0) 61962306a36Sopenharmony_ci return ret; 62062306a36Sopenharmony_ci mtk_nor_setup_bus(sp, op); 62162306a36Sopenharmony_ci if (op->data.nbytes == 1) { 62262306a36Sopenharmony_ci mtk_nor_set_addr(sp, op); 62362306a36Sopenharmony_ci return mtk_nor_read_pio(sp, op); 62462306a36Sopenharmony_ci } else { 62562306a36Sopenharmony_ci ret = mtk_nor_read_dma(sp, op); 62662306a36Sopenharmony_ci if (unlikely(ret)) { 62762306a36Sopenharmony_ci /* Handle rare bus glitch */ 62862306a36Sopenharmony_ci mtk_nor_reset(sp); 62962306a36Sopenharmony_ci mtk_nor_setup_bus(sp, op); 63062306a36Sopenharmony_ci return mtk_nor_read_dma(sp, op); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return ret; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci return mtk_nor_spi_mem_prg(sp, op); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic int mtk_nor_setup(struct spi_device *spi) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct mtk_nor *sp = spi_controller_get_devdata(spi->master); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (spi->max_speed_hz && (spi->max_speed_hz < sp->spi_freq)) { 64562306a36Sopenharmony_ci dev_err(&spi->dev, "spi clock should be %u Hz.\n", 64662306a36Sopenharmony_ci sp->spi_freq); 64762306a36Sopenharmony_ci return -EINVAL; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci spi->max_speed_hz = sp->spi_freq; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic int mtk_nor_transfer_one_message(struct spi_controller *master, 65562306a36Sopenharmony_ci struct spi_message *m) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct mtk_nor *sp = spi_controller_get_devdata(master); 65862306a36Sopenharmony_ci struct spi_transfer *t = NULL; 65962306a36Sopenharmony_ci unsigned long trx_len = 0; 66062306a36Sopenharmony_ci int stat = 0; 66162306a36Sopenharmony_ci int reg_offset = MTK_NOR_REG_PRGDATA_MAX; 66262306a36Sopenharmony_ci void __iomem *reg; 66362306a36Sopenharmony_ci const u8 *txbuf; 66462306a36Sopenharmony_ci u8 *rxbuf; 66562306a36Sopenharmony_ci int i; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci list_for_each_entry(t, &m->transfers, transfer_list) { 66862306a36Sopenharmony_ci txbuf = t->tx_buf; 66962306a36Sopenharmony_ci for (i = 0; i < t->len; i++, reg_offset--) { 67062306a36Sopenharmony_ci reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); 67162306a36Sopenharmony_ci if (txbuf) 67262306a36Sopenharmony_ci writeb(txbuf[i], reg); 67362306a36Sopenharmony_ci else 67462306a36Sopenharmony_ci writeb(0, reg); 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci trx_len += t->len; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci writel(trx_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci stat = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_PROGRAM, 68262306a36Sopenharmony_ci trx_len * BITS_PER_BYTE); 68362306a36Sopenharmony_ci if (stat < 0) 68462306a36Sopenharmony_ci goto msg_done; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci reg_offset = trx_len - 1; 68762306a36Sopenharmony_ci list_for_each_entry(t, &m->transfers, transfer_list) { 68862306a36Sopenharmony_ci rxbuf = t->rx_buf; 68962306a36Sopenharmony_ci for (i = 0; i < t->len; i++, reg_offset--) { 69062306a36Sopenharmony_ci reg = sp->base + MTK_NOR_REG_SHIFT(reg_offset); 69162306a36Sopenharmony_ci if (rxbuf) 69262306a36Sopenharmony_ci rxbuf[i] = readb(reg); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci m->actual_length = trx_len; 69762306a36Sopenharmony_cimsg_done: 69862306a36Sopenharmony_ci m->status = stat; 69962306a36Sopenharmony_ci spi_finalize_current_message(master); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic void mtk_nor_disable_clk(struct mtk_nor *sp) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci clk_disable_unprepare(sp->spi_clk); 70762306a36Sopenharmony_ci clk_disable_unprepare(sp->ctlr_clk); 70862306a36Sopenharmony_ci clk_disable_unprepare(sp->axi_clk); 70962306a36Sopenharmony_ci clk_disable_unprepare(sp->axi_s_clk); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int mtk_nor_enable_clk(struct mtk_nor *sp) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci int ret; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci ret = clk_prepare_enable(sp->spi_clk); 71762306a36Sopenharmony_ci if (ret) 71862306a36Sopenharmony_ci return ret; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci ret = clk_prepare_enable(sp->ctlr_clk); 72162306a36Sopenharmony_ci if (ret) { 72262306a36Sopenharmony_ci clk_disable_unprepare(sp->spi_clk); 72362306a36Sopenharmony_ci return ret; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ret = clk_prepare_enable(sp->axi_clk); 72762306a36Sopenharmony_ci if (ret) { 72862306a36Sopenharmony_ci clk_disable_unprepare(sp->spi_clk); 72962306a36Sopenharmony_ci clk_disable_unprepare(sp->ctlr_clk); 73062306a36Sopenharmony_ci return ret; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = clk_prepare_enable(sp->axi_s_clk); 73462306a36Sopenharmony_ci if (ret) { 73562306a36Sopenharmony_ci clk_disable_unprepare(sp->spi_clk); 73662306a36Sopenharmony_ci clk_disable_unprepare(sp->ctlr_clk); 73762306a36Sopenharmony_ci clk_disable_unprepare(sp->axi_clk); 73862306a36Sopenharmony_ci return ret; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return 0; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic void mtk_nor_init(struct mtk_nor *sp) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci writel(0, sp->base + MTK_NOR_REG_IRQ_EN); 74762306a36Sopenharmony_ci writel(MTK_NOR_IRQ_MASK, sp->base + MTK_NOR_REG_IRQ_STAT); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci writel(MTK_NOR_ENABLE_SF_CMD, sp->base + MTK_NOR_REG_WP); 75062306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_CFG2, MTK_NOR_WR_CUSTOM_OP_EN, 0); 75162306a36Sopenharmony_ci mtk_nor_rmw(sp, MTK_NOR_REG_CFG3, 75262306a36Sopenharmony_ci MTK_NOR_DISABLE_WREN | MTK_NOR_DISABLE_SR_POLL, 0); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic irqreturn_t mtk_nor_irq_handler(int irq, void *data) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct mtk_nor *sp = data; 75862306a36Sopenharmony_ci u32 irq_status, irq_enabled; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci irq_status = readl(sp->base + MTK_NOR_REG_IRQ_STAT); 76162306a36Sopenharmony_ci irq_enabled = readl(sp->base + MTK_NOR_REG_IRQ_EN); 76262306a36Sopenharmony_ci // write status back to clear interrupt 76362306a36Sopenharmony_ci writel(irq_status, sp->base + MTK_NOR_REG_IRQ_STAT); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (!(irq_status & irq_enabled)) 76662306a36Sopenharmony_ci return IRQ_NONE; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (irq_status & MTK_NOR_IRQ_DMA) { 76962306a36Sopenharmony_ci complete(&sp->op_done); 77062306a36Sopenharmony_ci writel(0, sp->base + MTK_NOR_REG_IRQ_EN); 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return IRQ_HANDLED; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic size_t mtk_max_msg_size(struct spi_device *spi) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci return MTK_NOR_PRG_MAX_SIZE; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic const struct spi_controller_mem_ops mtk_nor_mem_ops = { 78262306a36Sopenharmony_ci .adjust_op_size = mtk_nor_adjust_op_size, 78362306a36Sopenharmony_ci .supports_op = mtk_nor_supports_op, 78462306a36Sopenharmony_ci .exec_op = mtk_nor_exec_op 78562306a36Sopenharmony_ci}; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic const struct mtk_nor_caps mtk_nor_caps_mt8173 = { 78862306a36Sopenharmony_ci .dma_bits = 32, 78962306a36Sopenharmony_ci .extra_dummy_bit = 0, 79062306a36Sopenharmony_ci}; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic const struct mtk_nor_caps mtk_nor_caps_mt8186 = { 79362306a36Sopenharmony_ci .dma_bits = 32, 79462306a36Sopenharmony_ci .extra_dummy_bit = 1, 79562306a36Sopenharmony_ci}; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic const struct mtk_nor_caps mtk_nor_caps_mt8192 = { 79862306a36Sopenharmony_ci .dma_bits = 36, 79962306a36Sopenharmony_ci .extra_dummy_bit = 0, 80062306a36Sopenharmony_ci}; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic const struct of_device_id mtk_nor_match[] = { 80362306a36Sopenharmony_ci { .compatible = "mediatek,mt8173-nor", .data = &mtk_nor_caps_mt8173 }, 80462306a36Sopenharmony_ci { .compatible = "mediatek,mt8186-nor", .data = &mtk_nor_caps_mt8186 }, 80562306a36Sopenharmony_ci { .compatible = "mediatek,mt8192-nor", .data = &mtk_nor_caps_mt8192 }, 80662306a36Sopenharmony_ci { /* sentinel */ } 80762306a36Sopenharmony_ci}; 80862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_nor_match); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic int mtk_nor_probe(struct platform_device *pdev) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct spi_controller *ctlr; 81362306a36Sopenharmony_ci struct mtk_nor *sp; 81462306a36Sopenharmony_ci struct mtk_nor_caps *caps; 81562306a36Sopenharmony_ci void __iomem *base; 81662306a36Sopenharmony_ci struct clk *spi_clk, *ctlr_clk, *axi_clk, *axi_s_clk; 81762306a36Sopenharmony_ci int ret, irq; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 82062306a36Sopenharmony_ci if (IS_ERR(base)) 82162306a36Sopenharmony_ci return PTR_ERR(base); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci spi_clk = devm_clk_get(&pdev->dev, "spi"); 82462306a36Sopenharmony_ci if (IS_ERR(spi_clk)) 82562306a36Sopenharmony_ci return PTR_ERR(spi_clk); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci ctlr_clk = devm_clk_get(&pdev->dev, "sf"); 82862306a36Sopenharmony_ci if (IS_ERR(ctlr_clk)) 82962306a36Sopenharmony_ci return PTR_ERR(ctlr_clk); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci axi_clk = devm_clk_get_optional(&pdev->dev, "axi"); 83262306a36Sopenharmony_ci if (IS_ERR(axi_clk)) 83362306a36Sopenharmony_ci return PTR_ERR(axi_clk); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci axi_s_clk = devm_clk_get_optional(&pdev->dev, "axi_s"); 83662306a36Sopenharmony_ci if (IS_ERR(axi_s_clk)) 83762306a36Sopenharmony_ci return PTR_ERR(axi_s_clk); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci caps = (struct mtk_nor_caps *)of_device_get_match_data(&pdev->dev); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(caps->dma_bits)); 84262306a36Sopenharmony_ci if (ret) { 84362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to set dma mask(%u)\n", caps->dma_bits); 84462306a36Sopenharmony_ci return ret; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*sp)); 84862306a36Sopenharmony_ci if (!ctlr) { 84962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate spi controller\n"); 85062306a36Sopenharmony_ci return -ENOMEM; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci ctlr->bits_per_word_mask = SPI_BPW_MASK(8); 85462306a36Sopenharmony_ci ctlr->dev.of_node = pdev->dev.of_node; 85562306a36Sopenharmony_ci ctlr->max_message_size = mtk_max_msg_size; 85662306a36Sopenharmony_ci ctlr->mem_ops = &mtk_nor_mem_ops; 85762306a36Sopenharmony_ci ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; 85862306a36Sopenharmony_ci ctlr->num_chipselect = 1; 85962306a36Sopenharmony_ci ctlr->setup = mtk_nor_setup; 86062306a36Sopenharmony_ci ctlr->transfer_one_message = mtk_nor_transfer_one_message; 86162306a36Sopenharmony_ci ctlr->auto_runtime_pm = true; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, ctlr); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci sp = spi_controller_get_devdata(ctlr); 86662306a36Sopenharmony_ci sp->base = base; 86762306a36Sopenharmony_ci sp->has_irq = false; 86862306a36Sopenharmony_ci sp->wbuf_en = false; 86962306a36Sopenharmony_ci sp->ctlr = ctlr; 87062306a36Sopenharmony_ci sp->dev = &pdev->dev; 87162306a36Sopenharmony_ci sp->spi_clk = spi_clk; 87262306a36Sopenharmony_ci sp->ctlr_clk = ctlr_clk; 87362306a36Sopenharmony_ci sp->axi_clk = axi_clk; 87462306a36Sopenharmony_ci sp->axi_s_clk = axi_s_clk; 87562306a36Sopenharmony_ci sp->caps = caps; 87662306a36Sopenharmony_ci sp->high_dma = caps->dma_bits > 32; 87762306a36Sopenharmony_ci sp->buffer = dmam_alloc_coherent(&pdev->dev, 87862306a36Sopenharmony_ci MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN, 87962306a36Sopenharmony_ci &sp->buffer_dma, GFP_KERNEL); 88062306a36Sopenharmony_ci if (!sp->buffer) 88162306a36Sopenharmony_ci return -ENOMEM; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if ((uintptr_t)sp->buffer & MTK_NOR_DMA_ALIGN_MASK) { 88462306a36Sopenharmony_ci dev_err(sp->dev, "misaligned allocation of internal buffer.\n"); 88562306a36Sopenharmony_ci return -ENOMEM; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci ret = mtk_nor_enable_clk(sp); 88962306a36Sopenharmony_ci if (ret < 0) 89062306a36Sopenharmony_ci return ret; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci sp->spi_freq = clk_get_rate(sp->spi_clk); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci mtk_nor_init(sp); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci irq = platform_get_irq_optional(pdev, 0); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (irq < 0) { 89962306a36Sopenharmony_ci dev_warn(sp->dev, "IRQ not available."); 90062306a36Sopenharmony_ci } else { 90162306a36Sopenharmony_ci ret = devm_request_irq(sp->dev, irq, mtk_nor_irq_handler, 0, 90262306a36Sopenharmony_ci pdev->name, sp); 90362306a36Sopenharmony_ci if (ret < 0) { 90462306a36Sopenharmony_ci dev_warn(sp->dev, "failed to request IRQ."); 90562306a36Sopenharmony_ci } else { 90662306a36Sopenharmony_ci init_completion(&sp->op_done); 90762306a36Sopenharmony_ci sp->has_irq = true; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, -1); 91262306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 91362306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 91462306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 91562306a36Sopenharmony_ci pm_runtime_get_noresume(&pdev->dev); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci ret = devm_spi_register_controller(&pdev->dev, ctlr); 91862306a36Sopenharmony_ci if (ret < 0) 91962306a36Sopenharmony_ci goto err_probe; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 92262306a36Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci dev_info(&pdev->dev, "spi frequency: %d Hz\n", sp->spi_freq); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cierr_probe: 92962306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 93062306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 93162306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci mtk_nor_disable_clk(sp); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return ret; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic void mtk_nor_remove(struct platform_device *pdev) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev); 94162306a36Sopenharmony_ci struct mtk_nor *sp = spi_controller_get_devdata(ctlr); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 94462306a36Sopenharmony_ci pm_runtime_set_suspended(&pdev->dev); 94562306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci mtk_nor_disable_clk(sp); 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic int __maybe_unused mtk_nor_runtime_suspend(struct device *dev) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci struct spi_controller *ctlr = dev_get_drvdata(dev); 95362306a36Sopenharmony_ci struct mtk_nor *sp = spi_controller_get_devdata(ctlr); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci mtk_nor_disable_clk(sp); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci return 0; 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int __maybe_unused mtk_nor_runtime_resume(struct device *dev) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct spi_controller *ctlr = dev_get_drvdata(dev); 96362306a36Sopenharmony_ci struct mtk_nor *sp = spi_controller_get_devdata(ctlr); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return mtk_nor_enable_clk(sp); 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic int __maybe_unused mtk_nor_suspend(struct device *dev) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci return pm_runtime_force_suspend(dev); 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic int __maybe_unused mtk_nor_resume(struct device *dev) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci struct spi_controller *ctlr = dev_get_drvdata(dev); 97662306a36Sopenharmony_ci struct mtk_nor *sp = spi_controller_get_devdata(ctlr); 97762306a36Sopenharmony_ci int ret; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci ret = pm_runtime_force_resume(dev); 98062306a36Sopenharmony_ci if (ret) 98162306a36Sopenharmony_ci return ret; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci mtk_nor_init(sp); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return 0; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic const struct dev_pm_ops mtk_nor_pm_ops = { 98962306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(mtk_nor_runtime_suspend, 99062306a36Sopenharmony_ci mtk_nor_runtime_resume, NULL) 99162306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(mtk_nor_suspend, mtk_nor_resume) 99262306a36Sopenharmony_ci}; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic struct platform_driver mtk_nor_driver = { 99562306a36Sopenharmony_ci .driver = { 99662306a36Sopenharmony_ci .name = DRIVER_NAME, 99762306a36Sopenharmony_ci .of_match_table = mtk_nor_match, 99862306a36Sopenharmony_ci .pm = &mtk_nor_pm_ops, 99962306a36Sopenharmony_ci }, 100062306a36Sopenharmony_ci .probe = mtk_nor_probe, 100162306a36Sopenharmony_ci .remove_new = mtk_nor_remove, 100262306a36Sopenharmony_ci}; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cimodule_platform_driver(mtk_nor_driver); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ciMODULE_DESCRIPTION("Mediatek SPI NOR controller driver"); 100762306a36Sopenharmony_ciMODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>"); 100862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 100962306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 1010