18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR MIT) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Amlogic Meson Nand Flash Controller Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2018 Amlogic, inc. 68c2ecf20Sopenharmony_ci * Author: Liang Yang <liang.yang@amlogic.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_device.h> 228c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define NFC_REG_CMD 0x00 258c2ecf20Sopenharmony_ci#define NFC_CMD_IDLE (0xc << 14) 268c2ecf20Sopenharmony_ci#define NFC_CMD_CLE (0x5 << 14) 278c2ecf20Sopenharmony_ci#define NFC_CMD_ALE (0x6 << 14) 288c2ecf20Sopenharmony_ci#define NFC_CMD_ADL ((0 << 16) | (3 << 20)) 298c2ecf20Sopenharmony_ci#define NFC_CMD_ADH ((1 << 16) | (3 << 20)) 308c2ecf20Sopenharmony_ci#define NFC_CMD_AIL ((2 << 16) | (3 << 20)) 318c2ecf20Sopenharmony_ci#define NFC_CMD_AIH ((3 << 16) | (3 << 20)) 328c2ecf20Sopenharmony_ci#define NFC_CMD_SEED ((8 << 16) | (3 << 20)) 338c2ecf20Sopenharmony_ci#define NFC_CMD_M2N ((0 << 17) | (2 << 20)) 348c2ecf20Sopenharmony_ci#define NFC_CMD_N2M ((1 << 17) | (2 << 20)) 358c2ecf20Sopenharmony_ci#define NFC_CMD_RB BIT(20) 368c2ecf20Sopenharmony_ci#define NFC_CMD_SCRAMBLER_ENABLE BIT(19) 378c2ecf20Sopenharmony_ci#define NFC_CMD_SCRAMBLER_DISABLE 0 388c2ecf20Sopenharmony_ci#define NFC_CMD_SHORTMODE_DISABLE 0 398c2ecf20Sopenharmony_ci#define NFC_CMD_RB_INT BIT(14) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0)) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define NFC_REG_CFG 0x04 448c2ecf20Sopenharmony_ci#define NFC_REG_DADR 0x08 458c2ecf20Sopenharmony_ci#define NFC_REG_IADR 0x0c 468c2ecf20Sopenharmony_ci#define NFC_REG_BUF 0x10 478c2ecf20Sopenharmony_ci#define NFC_REG_INFO 0x14 488c2ecf20Sopenharmony_ci#define NFC_REG_DC 0x18 498c2ecf20Sopenharmony_ci#define NFC_REG_ADR 0x1c 508c2ecf20Sopenharmony_ci#define NFC_REG_DL 0x20 518c2ecf20Sopenharmony_ci#define NFC_REG_DH 0x24 528c2ecf20Sopenharmony_ci#define NFC_REG_CADR 0x28 538c2ecf20Sopenharmony_ci#define NFC_REG_SADR 0x2c 548c2ecf20Sopenharmony_ci#define NFC_REG_PINS 0x30 558c2ecf20Sopenharmony_ci#define NFC_REG_VER 0x38 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define NFC_RB_IRQ_EN BIT(21) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \ 608c2ecf20Sopenharmony_ci ( \ 618c2ecf20Sopenharmony_ci (cmd_dir) | \ 628c2ecf20Sopenharmony_ci ((ran) << 19) | \ 638c2ecf20Sopenharmony_ci ((bch) << 14) | \ 648c2ecf20Sopenharmony_ci ((short_mode) << 13) | \ 658c2ecf20Sopenharmony_ci (((page_size) & 0x7f) << 6) | \ 668c2ecf20Sopenharmony_ci ((pages) & 0x3f) \ 678c2ecf20Sopenharmony_ci ) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define GENCMDDADDRL(adl, addr) ((adl) | ((addr) & 0xffff)) 708c2ecf20Sopenharmony_ci#define GENCMDDADDRH(adh, addr) ((adh) | (((addr) >> 16) & 0xffff)) 718c2ecf20Sopenharmony_ci#define GENCMDIADDRL(ail, addr) ((ail) | ((addr) & 0xffff)) 728c2ecf20Sopenharmony_ci#define GENCMDIADDRH(aih, addr) ((aih) | (((addr) >> 16) & 0xffff)) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define DMA_DIR(dir) ((dir) ? NFC_CMD_N2M : NFC_CMD_M2N) 758c2ecf20Sopenharmony_ci#define DMA_ADDR_ALIGN 8 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define ECC_CHECK_RETURN_FF (-1) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define NAND_CE0 (0xe << 10) 808c2ecf20Sopenharmony_ci#define NAND_CE1 (0xd << 10) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define DMA_BUSY_TIMEOUT 0x100000 838c2ecf20Sopenharmony_ci#define CMD_FIFO_EMPTY_TIMEOUT 1000 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define MAX_CE_NUM 2 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* eMMC clock register, misc control */ 888c2ecf20Sopenharmony_ci#define CLK_SELECT_NAND BIT(31) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define NFC_CLK_CYCLE 6 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* nand flash controller delay 3 ns */ 938c2ecf20Sopenharmony_ci#define NFC_DEFAULT_DELAY 3000 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define ROW_ADDER(page, index) (((page) >> (8 * (index))) & 0xff) 968c2ecf20Sopenharmony_ci#define MAX_CYCLE_ADDRS 5 978c2ecf20Sopenharmony_ci#define DIRREAD 1 988c2ecf20Sopenharmony_ci#define DIRWRITE 0 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define ECC_PARITY_BCH8_512B 14 1018c2ecf20Sopenharmony_ci#define ECC_COMPLETE BIT(31) 1028c2ecf20Sopenharmony_ci#define ECC_ERR_CNT(x) (((x) >> 24) & GENMASK(5, 0)) 1038c2ecf20Sopenharmony_ci#define ECC_ZERO_CNT(x) (((x) >> 16) & GENMASK(5, 0)) 1048c2ecf20Sopenharmony_ci#define ECC_UNCORRECTABLE 0x3f 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define PER_INFO_BYTE 8 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistruct meson_nfc_nand_chip { 1098c2ecf20Sopenharmony_ci struct list_head node; 1108c2ecf20Sopenharmony_ci struct nand_chip nand; 1118c2ecf20Sopenharmony_ci unsigned long clk_rate; 1128c2ecf20Sopenharmony_ci unsigned long level1_divider; 1138c2ecf20Sopenharmony_ci u32 bus_timing; 1148c2ecf20Sopenharmony_ci u32 twb; 1158c2ecf20Sopenharmony_ci u32 tadl; 1168c2ecf20Sopenharmony_ci u32 tbers_max; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci u32 bch_mode; 1198c2ecf20Sopenharmony_ci u8 *data_buf; 1208c2ecf20Sopenharmony_ci __le64 *info_buf; 1218c2ecf20Sopenharmony_ci u32 nsels; 1228c2ecf20Sopenharmony_ci u8 sels[]; 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct meson_nand_ecc { 1268c2ecf20Sopenharmony_ci u32 bch; 1278c2ecf20Sopenharmony_ci u32 strength; 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistruct meson_nfc_data { 1318c2ecf20Sopenharmony_ci const struct nand_ecc_caps *ecc_caps; 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistruct meson_nfc_param { 1358c2ecf20Sopenharmony_ci u32 chip_select; 1368c2ecf20Sopenharmony_ci u32 rb_select; 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistruct nand_rw_cmd { 1408c2ecf20Sopenharmony_ci u32 cmd0; 1418c2ecf20Sopenharmony_ci u32 addrs[MAX_CYCLE_ADDRS]; 1428c2ecf20Sopenharmony_ci u32 cmd1; 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistruct nand_timing { 1468c2ecf20Sopenharmony_ci u32 twb; 1478c2ecf20Sopenharmony_ci u32 tadl; 1488c2ecf20Sopenharmony_ci u32 tbers_max; 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistruct meson_nfc { 1528c2ecf20Sopenharmony_ci struct nand_controller controller; 1538c2ecf20Sopenharmony_ci struct clk *core_clk; 1548c2ecf20Sopenharmony_ci struct clk *device_clk; 1558c2ecf20Sopenharmony_ci struct clk *phase_tx; 1568c2ecf20Sopenharmony_ci struct clk *phase_rx; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci unsigned long clk_rate; 1598c2ecf20Sopenharmony_ci u32 bus_timing; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci struct device *dev; 1628c2ecf20Sopenharmony_ci void __iomem *reg_base; 1638c2ecf20Sopenharmony_ci struct regmap *reg_clk; 1648c2ecf20Sopenharmony_ci struct completion completion; 1658c2ecf20Sopenharmony_ci struct list_head chips; 1668c2ecf20Sopenharmony_ci const struct meson_nfc_data *data; 1678c2ecf20Sopenharmony_ci struct meson_nfc_param param; 1688c2ecf20Sopenharmony_ci struct nand_timing timing; 1698c2ecf20Sopenharmony_ci union { 1708c2ecf20Sopenharmony_ci int cmd[32]; 1718c2ecf20Sopenharmony_ci struct nand_rw_cmd rw; 1728c2ecf20Sopenharmony_ci } cmdfifo; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci dma_addr_t daddr; 1758c2ecf20Sopenharmony_ci dma_addr_t iaddr; 1768c2ecf20Sopenharmony_ci u32 info_bytes; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci unsigned long assigned_cs; 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cienum { 1828c2ecf20Sopenharmony_ci NFC_ECC_BCH8_1K = 2, 1838c2ecf20Sopenharmony_ci NFC_ECC_BCH24_1K, 1848c2ecf20Sopenharmony_ci NFC_ECC_BCH30_1K, 1858c2ecf20Sopenharmony_ci NFC_ECC_BCH40_1K, 1868c2ecf20Sopenharmony_ci NFC_ECC_BCH50_1K, 1878c2ecf20Sopenharmony_ci NFC_ECC_BCH60_1K, 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci#define MESON_ECC_DATA(b, s) { .bch = (b), .strength = (s)} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic struct meson_nand_ecc meson_ecc[] = { 1938c2ecf20Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8), 1948c2ecf20Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24), 1958c2ecf20Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30), 1968c2ecf20Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40), 1978c2ecf20Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50), 1988c2ecf20Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60), 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int meson_nand_calc_ecc_bytes(int step_size, int strength) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci int ecc_bytes; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (step_size == 512 && strength == 8) 2068c2ecf20Sopenharmony_ci return ECC_PARITY_BCH8_512B; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8); 2098c2ecf20Sopenharmony_ci ecc_bytes = ALIGN(ecc_bytes, 2); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return ecc_bytes; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciNAND_ECC_CAPS_SINGLE(meson_gxl_ecc_caps, 2158c2ecf20Sopenharmony_ci meson_nand_calc_ecc_bytes, 1024, 8, 24, 30, 40, 50, 60); 2168c2ecf20Sopenharmony_ciNAND_ECC_CAPS_SINGLE(meson_axg_ecc_caps, 2178c2ecf20Sopenharmony_ci meson_nand_calc_ecc_bytes, 1024, 8); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci return container_of(nand, struct meson_nfc_nand_chip, nand); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void meson_nfc_select_chip(struct nand_chip *nand, int chip) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 2278c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 2288c2ecf20Sopenharmony_ci int ret, value; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (chip < 0 || WARN_ON_ONCE(chip >= meson_chip->nsels)) 2318c2ecf20Sopenharmony_ci return; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0; 2348c2ecf20Sopenharmony_ci nfc->param.rb_select = nfc->param.chip_select; 2358c2ecf20Sopenharmony_ci nfc->timing.twb = meson_chip->twb; 2368c2ecf20Sopenharmony_ci nfc->timing.tadl = meson_chip->tadl; 2378c2ecf20Sopenharmony_ci nfc->timing.tbers_max = meson_chip->tbers_max; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (nfc->clk_rate != meson_chip->clk_rate) { 2408c2ecf20Sopenharmony_ci ret = clk_set_rate(nfc->device_clk, meson_chip->clk_rate); 2418c2ecf20Sopenharmony_ci if (ret) { 2428c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to set clock rate\n"); 2438c2ecf20Sopenharmony_ci return; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci nfc->clk_rate = meson_chip->clk_rate; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci if (nfc->bus_timing != meson_chip->bus_timing) { 2488c2ecf20Sopenharmony_ci value = (NFC_CLK_CYCLE - 1) | (meson_chip->bus_timing << 5); 2498c2ecf20Sopenharmony_ci writel(value, nfc->reg_base + NFC_REG_CFG); 2508c2ecf20Sopenharmony_ci writel((1 << 31), nfc->reg_base + NFC_REG_CMD); 2518c2ecf20Sopenharmony_ci nfc->bus_timing = meson_chip->bus_timing; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff), 2588c2ecf20Sopenharmony_ci nfc->reg_base + NFC_REG_CMD); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)), 2648c2ecf20Sopenharmony_ci nfc->reg_base + NFC_REG_CMD); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir, 2688c2ecf20Sopenharmony_ci int scrambler) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 2718c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); 2728c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 2738c2ecf20Sopenharmony_ci u32 bch = meson_chip->bch_mode, cmd; 2748c2ecf20Sopenharmony_ci int len = mtd->writesize, pagesize, pages; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci pagesize = nand->ecc.size; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (raw) { 2798c2ecf20Sopenharmony_ci len = mtd->writesize + mtd->oobsize; 2808c2ecf20Sopenharmony_ci cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir); 2818c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci pages = len / nand->ecc.size; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci cmd = CMDRWGEN(DMA_DIR(dir), scrambler, bch, 2888c2ecf20Sopenharmony_ci NFC_CMD_SHORTMODE_DISABLE, pagesize, pages); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic void meson_nfc_drain_cmd(struct meson_nfc *nfc) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci /* 2968c2ecf20Sopenharmony_ci * Insert two commands to make sure all valid commands are finished. 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * The Nand flash controller is designed as two stages pipleline - 2998c2ecf20Sopenharmony_ci * a) fetch and b) excute. 3008c2ecf20Sopenharmony_ci * There might be cases when the driver see command queue is empty, 3018c2ecf20Sopenharmony_ci * but the Nand flash controller still has two commands buffered, 3028c2ecf20Sopenharmony_ci * one is fetched into NFC request queue (ready to run), and another 3038c2ecf20Sopenharmony_ci * is actively executing. So pushing 2 "IDLE" commands guarantees that 3048c2ecf20Sopenharmony_ci * the pipeline is emptied. 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci meson_nfc_cmd_idle(nfc, 0); 3078c2ecf20Sopenharmony_ci meson_nfc_cmd_idle(nfc, 0); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc, 3118c2ecf20Sopenharmony_ci unsigned int timeout_ms) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci u32 cmd_size = 0; 3148c2ecf20Sopenharmony_ci int ret; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* wait cmd fifo is empty */ 3178c2ecf20Sopenharmony_ci ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size, 3188c2ecf20Sopenharmony_ci !NFC_CMD_GET_SIZE(cmd_size), 3198c2ecf20Sopenharmony_ci 10, timeout_ms * 1000); 3208c2ecf20Sopenharmony_ci if (ret) 3218c2ecf20Sopenharmony_ci dev_err(nfc->dev, "wait for empty CMD FIFO time out\n"); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return ret; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int meson_nfc_wait_dma_finish(struct meson_nfc *nfc) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci meson_nfc_drain_cmd(nfc); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 3368c2ecf20Sopenharmony_ci int len; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return meson_chip->data_buf + len; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 3468c2ecf20Sopenharmony_ci int len, temp; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci temp = nand->ecc.size + nand->ecc.bytes; 3498c2ecf20Sopenharmony_ci len = (temp + 2) * i; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return meson_chip->data_buf + len; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void meson_nfc_get_data_oob(struct nand_chip *nand, 3558c2ecf20Sopenharmony_ci u8 *buf, u8 *oobbuf) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci int i, oob_len = 0; 3588c2ecf20Sopenharmony_ci u8 *dsrc, *osrc; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci oob_len = nand->ecc.bytes + 2; 3618c2ecf20Sopenharmony_ci for (i = 0; i < nand->ecc.steps; i++) { 3628c2ecf20Sopenharmony_ci if (buf) { 3638c2ecf20Sopenharmony_ci dsrc = meson_nfc_data_ptr(nand, i); 3648c2ecf20Sopenharmony_ci memcpy(buf, dsrc, nand->ecc.size); 3658c2ecf20Sopenharmony_ci buf += nand->ecc.size; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci osrc = meson_nfc_oob_ptr(nand, i); 3688c2ecf20Sopenharmony_ci memcpy(oobbuf, osrc, oob_len); 3698c2ecf20Sopenharmony_ci oobbuf += oob_len; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic void meson_nfc_set_data_oob(struct nand_chip *nand, 3748c2ecf20Sopenharmony_ci const u8 *buf, u8 *oobbuf) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci int i, oob_len = 0; 3778c2ecf20Sopenharmony_ci u8 *dsrc, *osrc; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci oob_len = nand->ecc.bytes + 2; 3808c2ecf20Sopenharmony_ci for (i = 0; i < nand->ecc.steps; i++) { 3818c2ecf20Sopenharmony_ci if (buf) { 3828c2ecf20Sopenharmony_ci dsrc = meson_nfc_data_ptr(nand, i); 3838c2ecf20Sopenharmony_ci memcpy(dsrc, buf, nand->ecc.size); 3848c2ecf20Sopenharmony_ci buf += nand->ecc.size; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci osrc = meson_nfc_oob_ptr(nand, i); 3878c2ecf20Sopenharmony_ci memcpy(osrc, oobbuf, oob_len); 3888c2ecf20Sopenharmony_ci oobbuf += oob_len; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci u32 cmd, cfg; 3958c2ecf20Sopenharmony_ci int ret = 0; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci meson_nfc_cmd_idle(nfc, nfc->timing.twb); 3988c2ecf20Sopenharmony_ci meson_nfc_drain_cmd(nfc); 3998c2ecf20Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci cfg = readl(nfc->reg_base + NFC_REG_CFG); 4028c2ecf20Sopenharmony_ci cfg |= NFC_RB_IRQ_EN; 4038c2ecf20Sopenharmony_ci writel(cfg, nfc->reg_base + NFC_REG_CFG); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci reinit_completion(&nfc->completion); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* use the max erase time as the maximum clock for waiting R/B */ 4088c2ecf20Sopenharmony_ci cmd = NFC_CMD_RB | NFC_CMD_RB_INT 4098c2ecf20Sopenharmony_ci | nfc->param.chip_select | nfc->timing.tbers_max; 4108c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&nfc->completion, 4138c2ecf20Sopenharmony_ci msecs_to_jiffies(timeout_ms)); 4148c2ecf20Sopenharmony_ci if (ret == 0) 4158c2ecf20Sopenharmony_ci ret = -1; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 4238c2ecf20Sopenharmony_ci __le64 *info; 4248c2ecf20Sopenharmony_ci int i, count; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) { 4278c2ecf20Sopenharmony_ci info = &meson_chip->info_buf[i]; 4288c2ecf20Sopenharmony_ci *info |= oob_buf[count]; 4298c2ecf20Sopenharmony_ci *info |= oob_buf[count + 1] << 8; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 4368c2ecf20Sopenharmony_ci __le64 *info; 4378c2ecf20Sopenharmony_ci int i, count; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) { 4408c2ecf20Sopenharmony_ci info = &meson_chip->info_buf[i]; 4418c2ecf20Sopenharmony_ci oob_buf[count] = *info; 4428c2ecf20Sopenharmony_ci oob_buf[count + 1] = *info >> 8; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips, 4478c2ecf20Sopenharmony_ci u64 *correct_bitmap) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 4508c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 4518c2ecf20Sopenharmony_ci __le64 *info; 4528c2ecf20Sopenharmony_ci int ret = 0, i; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci for (i = 0; i < nand->ecc.steps; i++) { 4558c2ecf20Sopenharmony_ci info = &meson_chip->info_buf[i]; 4568c2ecf20Sopenharmony_ci if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) { 4578c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += ECC_ERR_CNT(*info); 4588c2ecf20Sopenharmony_ci *bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info)); 4598c2ecf20Sopenharmony_ci *correct_bitmap |= BIT_ULL(i); 4608c2ecf20Sopenharmony_ci continue; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci if ((nand->options & NAND_NEED_SCRAMBLING) && 4638c2ecf20Sopenharmony_ci ECC_ZERO_CNT(*info) < nand->ecc.strength) { 4648c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += ECC_ZERO_CNT(*info); 4658c2ecf20Sopenharmony_ci *bitflips = max_t(u32, *bitflips, 4668c2ecf20Sopenharmony_ci ECC_ZERO_CNT(*info)); 4678c2ecf20Sopenharmony_ci ret = ECC_CHECK_RETURN_FF; 4688c2ecf20Sopenharmony_ci } else { 4698c2ecf20Sopenharmony_ci ret = -EBADMSG; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf, 4768c2ecf20Sopenharmony_ci int datalen, void *infobuf, int infolen, 4778c2ecf20Sopenharmony_ci enum dma_data_direction dir) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 4808c2ecf20Sopenharmony_ci u32 cmd; 4818c2ecf20Sopenharmony_ci int ret = 0; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci nfc->daddr = dma_map_single(nfc->dev, databuf, datalen, dir); 4848c2ecf20Sopenharmony_ci ret = dma_mapping_error(nfc->dev, nfc->daddr); 4858c2ecf20Sopenharmony_ci if (ret) { 4868c2ecf20Sopenharmony_ci dev_err(nfc->dev, "DMA mapping error\n"); 4878c2ecf20Sopenharmony_ci return ret; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr); 4908c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr); 4938c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (infobuf) { 4968c2ecf20Sopenharmony_ci nfc->iaddr = dma_map_single(nfc->dev, infobuf, infolen, dir); 4978c2ecf20Sopenharmony_ci ret = dma_mapping_error(nfc->dev, nfc->iaddr); 4988c2ecf20Sopenharmony_ci if (ret) { 4998c2ecf20Sopenharmony_ci dev_err(nfc->dev, "DMA mapping error\n"); 5008c2ecf20Sopenharmony_ci dma_unmap_single(nfc->dev, 5018c2ecf20Sopenharmony_ci nfc->daddr, datalen, dir); 5028c2ecf20Sopenharmony_ci return ret; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci nfc->info_bytes = infolen; 5058c2ecf20Sopenharmony_ci cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr); 5068c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr); 5098c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return ret; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void meson_nfc_dma_buffer_release(struct nand_chip *nand, 5168c2ecf20Sopenharmony_ci int datalen, int infolen, 5178c2ecf20Sopenharmony_ci enum dma_data_direction dir) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci dma_unmap_single(nfc->dev, nfc->daddr, datalen, dir); 5228c2ecf20Sopenharmony_ci if (infolen) { 5238c2ecf20Sopenharmony_ci dma_unmap_single(nfc->dev, nfc->iaddr, infolen, dir); 5248c2ecf20Sopenharmony_ci nfc->info_bytes = 0; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 5318c2ecf20Sopenharmony_ci int ret = 0; 5328c2ecf20Sopenharmony_ci u32 cmd; 5338c2ecf20Sopenharmony_ci u8 *info; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci info = kzalloc(PER_INFO_BYTE, GFP_KERNEL); 5368c2ecf20Sopenharmony_ci if (!info) 5378c2ecf20Sopenharmony_ci return -ENOMEM; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci ret = meson_nfc_dma_buffer_setup(nand, buf, len, info, 5408c2ecf20Sopenharmony_ci PER_INFO_BYTE, DMA_FROM_DEVICE); 5418c2ecf20Sopenharmony_ci if (ret) 5428c2ecf20Sopenharmony_ci goto out; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci cmd = NFC_CMD_N2M | (len & GENMASK(13, 0)); 5458c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci meson_nfc_drain_cmd(nfc); 5488c2ecf20Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, 1000); 5498c2ecf20Sopenharmony_ci meson_nfc_dma_buffer_release(nand, len, PER_INFO_BYTE, DMA_FROM_DEVICE); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ciout: 5528c2ecf20Sopenharmony_ci kfree(info); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return ret; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 5608c2ecf20Sopenharmony_ci int ret = 0; 5618c2ecf20Sopenharmony_ci u32 cmd; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL, 5648c2ecf20Sopenharmony_ci 0, DMA_TO_DEVICE); 5658c2ecf20Sopenharmony_ci if (ret) 5668c2ecf20Sopenharmony_ci return ret; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci cmd = NFC_CMD_M2N | (len & GENMASK(13, 0)); 5698c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci meson_nfc_drain_cmd(nfc); 5728c2ecf20Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, 1000); 5738c2ecf20Sopenharmony_ci meson_nfc_dma_buffer_release(nand, len, 0, DMA_TO_DEVICE); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return ret; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand, 5798c2ecf20Sopenharmony_ci int page, bool in) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci const struct nand_sdr_timings *sdr = 5828c2ecf20Sopenharmony_ci nand_get_sdr_timings(nand_get_interface_config(nand)); 5838c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 5848c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 5858c2ecf20Sopenharmony_ci u32 *addrs = nfc->cmdfifo.rw.addrs; 5868c2ecf20Sopenharmony_ci u32 cs = nfc->param.chip_select; 5878c2ecf20Sopenharmony_ci u32 cmd0, cmd_num, row_start; 5888c2ecf20Sopenharmony_ci int ret = 0, i; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci cmd_num = sizeof(struct nand_rw_cmd) / sizeof(int); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN; 5938c2ecf20Sopenharmony_ci nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci addrs[0] = cs | NFC_CMD_ALE | 0; 5968c2ecf20Sopenharmony_ci if (mtd->writesize <= 512) { 5978c2ecf20Sopenharmony_ci cmd_num--; 5988c2ecf20Sopenharmony_ci row_start = 1; 5998c2ecf20Sopenharmony_ci } else { 6008c2ecf20Sopenharmony_ci addrs[1] = cs | NFC_CMD_ALE | 0; 6018c2ecf20Sopenharmony_ci row_start = 2; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci addrs[row_start] = cs | NFC_CMD_ALE | ROW_ADDER(page, 0); 6058c2ecf20Sopenharmony_ci addrs[row_start + 1] = cs | NFC_CMD_ALE | ROW_ADDER(page, 1); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (nand->options & NAND_ROW_ADDR_3) 6088c2ecf20Sopenharmony_ci addrs[row_start + 2] = 6098c2ecf20Sopenharmony_ci cs | NFC_CMD_ALE | ROW_ADDER(page, 2); 6108c2ecf20Sopenharmony_ci else 6118c2ecf20Sopenharmony_ci cmd_num--; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* subtract cmd1 */ 6148c2ecf20Sopenharmony_ci cmd_num--; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci for (i = 0; i < cmd_num; i++) 6178c2ecf20Sopenharmony_ci writel_relaxed(nfc->cmdfifo.cmd[i], 6188c2ecf20Sopenharmony_ci nfc->reg_base + NFC_REG_CMD); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (in) { 6218c2ecf20Sopenharmony_ci nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART; 6228c2ecf20Sopenharmony_ci writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD); 6238c2ecf20Sopenharmony_ci meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max)); 6248c2ecf20Sopenharmony_ci } else { 6258c2ecf20Sopenharmony_ci meson_nfc_cmd_idle(nfc, nfc->timing.tadl); 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci return ret; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic int meson_nfc_write_page_sub(struct nand_chip *nand, 6328c2ecf20Sopenharmony_ci int page, int raw) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci const struct nand_sdr_timings *sdr = 6358c2ecf20Sopenharmony_ci nand_get_sdr_timings(nand_get_interface_config(nand)); 6368c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 6378c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 6388c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 6398c2ecf20Sopenharmony_ci int data_len, info_len; 6408c2ecf20Sopenharmony_ci u32 cmd; 6418c2ecf20Sopenharmony_ci int ret; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci meson_nfc_select_chip(nand, nand->cur_cs); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci data_len = mtd->writesize + mtd->oobsize; 6468c2ecf20Sopenharmony_ci info_len = nand->ecc.steps * PER_INFO_BYTE; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRWRITE); 6498c2ecf20Sopenharmony_ci if (ret) 6508c2ecf20Sopenharmony_ci return ret; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf, 6538c2ecf20Sopenharmony_ci data_len, meson_chip->info_buf, 6548c2ecf20Sopenharmony_ci info_len, DMA_TO_DEVICE); 6558c2ecf20Sopenharmony_ci if (ret) 6568c2ecf20Sopenharmony_ci return ret; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (nand->options & NAND_NEED_SCRAMBLING) { 6598c2ecf20Sopenharmony_ci meson_nfc_cmd_seed(nfc, page); 6608c2ecf20Sopenharmony_ci meson_nfc_cmd_access(nand, raw, DIRWRITE, 6618c2ecf20Sopenharmony_ci NFC_CMD_SCRAMBLER_ENABLE); 6628c2ecf20Sopenharmony_ci } else { 6638c2ecf20Sopenharmony_ci meson_nfc_cmd_access(nand, raw, DIRWRITE, 6648c2ecf20Sopenharmony_ci NFC_CMD_SCRAMBLER_DISABLE); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG; 6688c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 6698c2ecf20Sopenharmony_ci meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max)); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return ret; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf, 6778c2ecf20Sopenharmony_ci int oob_required, int page) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci u8 *oob_buf = nand->oob_poi; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci meson_nfc_set_data_oob(nand, buf, oob_buf); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return meson_nfc_write_page_sub(nand, page, 1); 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int meson_nfc_write_page_hwecc(struct nand_chip *nand, 6878c2ecf20Sopenharmony_ci const u8 *buf, int oob_required, int page) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 6908c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 6918c2ecf20Sopenharmony_ci u8 *oob_buf = nand->oob_poi; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci memcpy(meson_chip->data_buf, buf, mtd->writesize); 6948c2ecf20Sopenharmony_ci memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE); 6958c2ecf20Sopenharmony_ci meson_nfc_set_user_byte(nand, oob_buf); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci return meson_nfc_write_page_sub(nand, page, 0); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc, 7018c2ecf20Sopenharmony_ci struct nand_chip *nand, int raw) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 7048c2ecf20Sopenharmony_ci __le64 *info; 7058c2ecf20Sopenharmony_ci u32 neccpages; 7068c2ecf20Sopenharmony_ci int ret; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci neccpages = raw ? 1 : nand->ecc.steps; 7098c2ecf20Sopenharmony_ci info = &meson_chip->info_buf[neccpages - 1]; 7108c2ecf20Sopenharmony_ci do { 7118c2ecf20Sopenharmony_ci usleep_range(10, 15); 7128c2ecf20Sopenharmony_ci /* info is updated by nfc dma engine*/ 7138c2ecf20Sopenharmony_ci smp_rmb(); 7148c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(nfc->dev, nfc->iaddr, nfc->info_bytes, 7158c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 7168c2ecf20Sopenharmony_ci ret = *info & ECC_COMPLETE; 7178c2ecf20Sopenharmony_ci } while (!ret); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int meson_nfc_read_page_sub(struct nand_chip *nand, 7218c2ecf20Sopenharmony_ci int page, int raw) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 7248c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 7258c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 7268c2ecf20Sopenharmony_ci int data_len, info_len; 7278c2ecf20Sopenharmony_ci int ret; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci meson_nfc_select_chip(nand, nand->cur_cs); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci data_len = mtd->writesize + mtd->oobsize; 7328c2ecf20Sopenharmony_ci info_len = nand->ecc.steps * PER_INFO_BYTE; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD); 7358c2ecf20Sopenharmony_ci if (ret) 7368c2ecf20Sopenharmony_ci return ret; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf, 7398c2ecf20Sopenharmony_ci data_len, meson_chip->info_buf, 7408c2ecf20Sopenharmony_ci info_len, DMA_FROM_DEVICE); 7418c2ecf20Sopenharmony_ci if (ret) 7428c2ecf20Sopenharmony_ci return ret; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (nand->options & NAND_NEED_SCRAMBLING) { 7458c2ecf20Sopenharmony_ci meson_nfc_cmd_seed(nfc, page); 7468c2ecf20Sopenharmony_ci meson_nfc_cmd_access(nand, raw, DIRREAD, 7478c2ecf20Sopenharmony_ci NFC_CMD_SCRAMBLER_ENABLE); 7488c2ecf20Sopenharmony_ci } else { 7498c2ecf20Sopenharmony_ci meson_nfc_cmd_access(nand, raw, DIRREAD, 7508c2ecf20Sopenharmony_ci NFC_CMD_SCRAMBLER_DISABLE); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ret = meson_nfc_wait_dma_finish(nfc); 7548c2ecf20Sopenharmony_ci meson_nfc_check_ecc_pages_valid(nfc, nand, raw); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci return ret; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf, 7628c2ecf20Sopenharmony_ci int oob_required, int page) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci u8 *oob_buf = nand->oob_poi; 7658c2ecf20Sopenharmony_ci int ret; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci ret = meson_nfc_read_page_sub(nand, page, 1); 7688c2ecf20Sopenharmony_ci if (ret) 7698c2ecf20Sopenharmony_ci return ret; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci meson_nfc_get_data_oob(nand, buf, oob_buf); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci return 0; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf, 7778c2ecf20Sopenharmony_ci int oob_required, int page) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 7808c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 7818c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 7828c2ecf20Sopenharmony_ci u64 correct_bitmap = 0; 7838c2ecf20Sopenharmony_ci u32 bitflips = 0; 7848c2ecf20Sopenharmony_ci u8 *oob_buf = nand->oob_poi; 7858c2ecf20Sopenharmony_ci int ret, i; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci ret = meson_nfc_read_page_sub(nand, page, 0); 7888c2ecf20Sopenharmony_ci if (ret) 7898c2ecf20Sopenharmony_ci return ret; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci meson_nfc_get_user_byte(nand, oob_buf); 7928c2ecf20Sopenharmony_ci ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap); 7938c2ecf20Sopenharmony_ci if (ret == ECC_CHECK_RETURN_FF) { 7948c2ecf20Sopenharmony_ci if (buf) 7958c2ecf20Sopenharmony_ci memset(buf, 0xff, mtd->writesize); 7968c2ecf20Sopenharmony_ci memset(oob_buf, 0xff, mtd->oobsize); 7978c2ecf20Sopenharmony_ci } else if (ret < 0) { 7988c2ecf20Sopenharmony_ci if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) { 7998c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 8008c2ecf20Sopenharmony_ci return bitflips; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci ret = meson_nfc_read_page_raw(nand, buf, 0, page); 8038c2ecf20Sopenharmony_ci if (ret) 8048c2ecf20Sopenharmony_ci return ret; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci for (i = 0; i < nand->ecc.steps ; i++) { 8078c2ecf20Sopenharmony_ci u8 *data = buf + i * ecc->size; 8088c2ecf20Sopenharmony_ci u8 *oob = nand->oob_poi + i * (ecc->bytes + 2); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (correct_bitmap & BIT_ULL(i)) 8118c2ecf20Sopenharmony_ci continue; 8128c2ecf20Sopenharmony_ci ret = nand_check_erased_ecc_chunk(data, ecc->size, 8138c2ecf20Sopenharmony_ci oob, ecc->bytes + 2, 8148c2ecf20Sopenharmony_ci NULL, 0, 8158c2ecf20Sopenharmony_ci ecc->strength); 8168c2ecf20Sopenharmony_ci if (ret < 0) { 8178c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 8188c2ecf20Sopenharmony_ci } else { 8198c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += ret; 8208c2ecf20Sopenharmony_ci bitflips = max_t(u32, bitflips, ret); 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci } else if (buf && buf != meson_chip->data_buf) { 8248c2ecf20Sopenharmony_ci memcpy(buf, meson_chip->data_buf, mtd->writesize); 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return bitflips; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic int meson_nfc_read_oob_raw(struct nand_chip *nand, int page) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci return meson_nfc_read_page_raw(nand, NULL, 1, page); 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic int meson_nfc_read_oob(struct nand_chip *nand, int page) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci return meson_nfc_read_page_hwecc(nand, NULL, 1, page); 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic bool meson_nfc_is_buffer_dma_safe(const void *buffer) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci if ((uintptr_t)buffer % DMA_ADDR_ALIGN) 8438c2ecf20Sopenharmony_ci return false; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (virt_addr_valid(buffer) && (!object_is_on_stack(buffer))) 8468c2ecf20Sopenharmony_ci return true; 8478c2ecf20Sopenharmony_ci return false; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic void * 8518c2ecf20Sopenharmony_cimeson_nand_op_get_dma_safe_input_buf(const struct nand_op_instr *instr) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR)) 8548c2ecf20Sopenharmony_ci return NULL; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (meson_nfc_is_buffer_dma_safe(instr->ctx.data.buf.in)) 8578c2ecf20Sopenharmony_ci return instr->ctx.data.buf.in; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci return kzalloc(instr->ctx.data.len, GFP_KERNEL); 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic void 8638c2ecf20Sopenharmony_cimeson_nand_op_put_dma_safe_input_buf(const struct nand_op_instr *instr, 8648c2ecf20Sopenharmony_ci void *buf) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR) || 8678c2ecf20Sopenharmony_ci WARN_ON(!buf)) 8688c2ecf20Sopenharmony_ci return; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (buf == instr->ctx.data.buf.in) 8718c2ecf20Sopenharmony_ci return; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci memcpy(instr->ctx.data.buf.in, buf, instr->ctx.data.len); 8748c2ecf20Sopenharmony_ci kfree(buf); 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic void * 8788c2ecf20Sopenharmony_cimeson_nand_op_get_dma_safe_output_buf(const struct nand_op_instr *instr) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR)) 8818c2ecf20Sopenharmony_ci return NULL; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (meson_nfc_is_buffer_dma_safe(instr->ctx.data.buf.out)) 8848c2ecf20Sopenharmony_ci return (void *)instr->ctx.data.buf.out; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci return kmemdup(instr->ctx.data.buf.out, 8878c2ecf20Sopenharmony_ci instr->ctx.data.len, GFP_KERNEL); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void 8918c2ecf20Sopenharmony_cimeson_nand_op_put_dma_safe_output_buf(const struct nand_op_instr *instr, 8928c2ecf20Sopenharmony_ci const void *buf) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR) || 8958c2ecf20Sopenharmony_ci WARN_ON(!buf)) 8968c2ecf20Sopenharmony_ci return; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (buf != instr->ctx.data.buf.out) 8998c2ecf20Sopenharmony_ci kfree(buf); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int meson_nfc_exec_op(struct nand_chip *nand, 9038c2ecf20Sopenharmony_ci const struct nand_operation *op, bool check_only) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 9068c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 9078c2ecf20Sopenharmony_ci const struct nand_op_instr *instr = NULL; 9088c2ecf20Sopenharmony_ci void *buf; 9098c2ecf20Sopenharmony_ci u32 op_id, delay_idle, cmd; 9108c2ecf20Sopenharmony_ci int i; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (check_only) 9138c2ecf20Sopenharmony_ci return 0; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci meson_nfc_select_chip(nand, op->cs); 9168c2ecf20Sopenharmony_ci for (op_id = 0; op_id < op->ninstrs; op_id++) { 9178c2ecf20Sopenharmony_ci instr = &op->instrs[op_id]; 9188c2ecf20Sopenharmony_ci delay_idle = DIV_ROUND_UP(PSEC_TO_NSEC(instr->delay_ns), 9198c2ecf20Sopenharmony_ci meson_chip->level1_divider * 9208c2ecf20Sopenharmony_ci NFC_CLK_CYCLE); 9218c2ecf20Sopenharmony_ci switch (instr->type) { 9228c2ecf20Sopenharmony_ci case NAND_OP_CMD_INSTR: 9238c2ecf20Sopenharmony_ci cmd = nfc->param.chip_select | NFC_CMD_CLE; 9248c2ecf20Sopenharmony_ci cmd |= instr->ctx.cmd.opcode & 0xff; 9258c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 9268c2ecf20Sopenharmony_ci meson_nfc_cmd_idle(nfc, delay_idle); 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 9308c2ecf20Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) { 9318c2ecf20Sopenharmony_ci cmd = nfc->param.chip_select | NFC_CMD_ALE; 9328c2ecf20Sopenharmony_ci cmd |= instr->ctx.addr.addrs[i] & 0xff; 9338c2ecf20Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci meson_nfc_cmd_idle(nfc, delay_idle); 9368c2ecf20Sopenharmony_ci break; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 9398c2ecf20Sopenharmony_ci buf = meson_nand_op_get_dma_safe_input_buf(instr); 9408c2ecf20Sopenharmony_ci if (!buf) 9418c2ecf20Sopenharmony_ci return -ENOMEM; 9428c2ecf20Sopenharmony_ci meson_nfc_read_buf(nand, buf, instr->ctx.data.len); 9438c2ecf20Sopenharmony_ci meson_nand_op_put_dma_safe_input_buf(instr, buf); 9448c2ecf20Sopenharmony_ci break; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 9478c2ecf20Sopenharmony_ci buf = meson_nand_op_get_dma_safe_output_buf(instr); 9488c2ecf20Sopenharmony_ci if (!buf) 9498c2ecf20Sopenharmony_ci return -ENOMEM; 9508c2ecf20Sopenharmony_ci meson_nfc_write_buf(nand, buf, instr->ctx.data.len); 9518c2ecf20Sopenharmony_ci meson_nand_op_put_dma_safe_output_buf(instr, buf); 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 9558c2ecf20Sopenharmony_ci meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms); 9568c2ecf20Sopenharmony_ci if (instr->delay_ns) 9578c2ecf20Sopenharmony_ci meson_nfc_cmd_idle(nfc, delay_idle); 9588c2ecf20Sopenharmony_ci break; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, 1000); 9628c2ecf20Sopenharmony_ci return 0; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic int meson_ooblayout_ecc(struct mtd_info *mtd, int section, 9668c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (section >= nand->ecc.steps) 9718c2ecf20Sopenharmony_ci return -ERANGE; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci oobregion->offset = 2 + (section * (2 + nand->ecc.bytes)); 9748c2ecf20Sopenharmony_ci oobregion->length = nand->ecc.bytes; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic int meson_ooblayout_free(struct mtd_info *mtd, int section, 9808c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (section >= nand->ecc.steps) 9858c2ecf20Sopenharmony_ci return -ERANGE; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci oobregion->offset = section * (2 + nand->ecc.bytes); 9888c2ecf20Sopenharmony_ci oobregion->length = 2; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops meson_ooblayout_ops = { 9948c2ecf20Sopenharmony_ci .ecc = meson_ooblayout_ecc, 9958c2ecf20Sopenharmony_ci .free = meson_ooblayout_free, 9968c2ecf20Sopenharmony_ci}; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic int meson_nfc_clk_init(struct meson_nfc *nfc) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci int ret; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* request core clock */ 10038c2ecf20Sopenharmony_ci nfc->core_clk = devm_clk_get(nfc->dev, "core"); 10048c2ecf20Sopenharmony_ci if (IS_ERR(nfc->core_clk)) { 10058c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to get core clock\n"); 10068c2ecf20Sopenharmony_ci return PTR_ERR(nfc->core_clk); 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci nfc->device_clk = devm_clk_get(nfc->dev, "device"); 10108c2ecf20Sopenharmony_ci if (IS_ERR(nfc->device_clk)) { 10118c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to get device clock\n"); 10128c2ecf20Sopenharmony_ci return PTR_ERR(nfc->device_clk); 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci nfc->phase_tx = devm_clk_get(nfc->dev, "tx"); 10168c2ecf20Sopenharmony_ci if (IS_ERR(nfc->phase_tx)) { 10178c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to get TX clk\n"); 10188c2ecf20Sopenharmony_ci return PTR_ERR(nfc->phase_tx); 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci nfc->phase_rx = devm_clk_get(nfc->dev, "rx"); 10228c2ecf20Sopenharmony_ci if (IS_ERR(nfc->phase_rx)) { 10238c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to get RX clk\n"); 10248c2ecf20Sopenharmony_ci return PTR_ERR(nfc->phase_rx); 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ 10288c2ecf20Sopenharmony_ci regmap_update_bits(nfc->reg_clk, 10298c2ecf20Sopenharmony_ci 0, CLK_SELECT_NAND, CLK_SELECT_NAND); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->core_clk); 10328c2ecf20Sopenharmony_ci if (ret) { 10338c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to enable core clock\n"); 10348c2ecf20Sopenharmony_ci return ret; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->device_clk); 10388c2ecf20Sopenharmony_ci if (ret) { 10398c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to enable device clock\n"); 10408c2ecf20Sopenharmony_ci goto err_device_clk; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->phase_tx); 10448c2ecf20Sopenharmony_ci if (ret) { 10458c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to enable TX clock\n"); 10468c2ecf20Sopenharmony_ci goto err_phase_tx; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->phase_rx); 10508c2ecf20Sopenharmony_ci if (ret) { 10518c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to enable RX clock\n"); 10528c2ecf20Sopenharmony_ci goto err_phase_rx; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci ret = clk_set_rate(nfc->device_clk, 24000000); 10568c2ecf20Sopenharmony_ci if (ret) 10578c2ecf20Sopenharmony_ci goto err_disable_rx; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci return 0; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cierr_disable_rx: 10628c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->phase_rx); 10638c2ecf20Sopenharmony_cierr_phase_rx: 10648c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->phase_tx); 10658c2ecf20Sopenharmony_cierr_phase_tx: 10668c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->device_clk); 10678c2ecf20Sopenharmony_cierr_device_clk: 10688c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->core_clk); 10698c2ecf20Sopenharmony_ci return ret; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic void meson_nfc_disable_clk(struct meson_nfc *nfc) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->phase_rx); 10758c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->phase_tx); 10768c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->device_clk); 10778c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->core_clk); 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void meson_nfc_free_buffer(struct nand_chip *nand) 10818c2ecf20Sopenharmony_ci{ 10828c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci kfree(meson_chip->info_buf); 10858c2ecf20Sopenharmony_ci kfree(meson_chip->data_buf); 10868c2ecf20Sopenharmony_ci} 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_cistatic int meson_chip_buffer_init(struct nand_chip *nand) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 10918c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 10928c2ecf20Sopenharmony_ci u32 page_bytes, info_bytes, nsectors; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci nsectors = mtd->writesize / nand->ecc.size; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci page_bytes = mtd->writesize + mtd->oobsize; 10978c2ecf20Sopenharmony_ci info_bytes = nsectors * PER_INFO_BYTE; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci meson_chip->data_buf = kmalloc(page_bytes, GFP_KERNEL); 11008c2ecf20Sopenharmony_ci if (!meson_chip->data_buf) 11018c2ecf20Sopenharmony_ci return -ENOMEM; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci meson_chip->info_buf = kmalloc(info_bytes, GFP_KERNEL); 11048c2ecf20Sopenharmony_ci if (!meson_chip->info_buf) { 11058c2ecf20Sopenharmony_ci kfree(meson_chip->data_buf); 11068c2ecf20Sopenharmony_ci return -ENOMEM; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci return 0; 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic 11138c2ecf20Sopenharmony_ciint meson_nfc_setup_interface(struct nand_chip *nand, int csline, 11148c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 11178c2ecf20Sopenharmony_ci const struct nand_sdr_timings *timings; 11188c2ecf20Sopenharmony_ci u32 div, bt_min, bt_max, tbers_clocks; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci timings = nand_get_sdr_timings(conf); 11218c2ecf20Sopenharmony_ci if (IS_ERR(timings)) 11228c2ecf20Sopenharmony_ci return -ENOTSUPP; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci div = DIV_ROUND_UP((timings->tRC_min / 1000), NFC_CLK_CYCLE); 11288c2ecf20Sopenharmony_ci bt_min = (timings->tREA_max + NFC_DEFAULT_DELAY) / div; 11298c2ecf20Sopenharmony_ci bt_max = (NFC_DEFAULT_DELAY + timings->tRHOH_min + 11308c2ecf20Sopenharmony_ci timings->tRC_min / 2) / div; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci meson_chip->twb = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tWB_max), 11338c2ecf20Sopenharmony_ci div * NFC_CLK_CYCLE); 11348c2ecf20Sopenharmony_ci meson_chip->tadl = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tADL_min), 11358c2ecf20Sopenharmony_ci div * NFC_CLK_CYCLE); 11368c2ecf20Sopenharmony_ci tbers_clocks = DIV_ROUND_UP_ULL(PSEC_TO_NSEC(timings->tBERS_max), 11378c2ecf20Sopenharmony_ci div * NFC_CLK_CYCLE); 11388c2ecf20Sopenharmony_ci meson_chip->tbers_max = ilog2(tbers_clocks); 11398c2ecf20Sopenharmony_ci if (!is_power_of_2(tbers_clocks)) 11408c2ecf20Sopenharmony_ci meson_chip->tbers_max++; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci bt_min = DIV_ROUND_UP(bt_min, 1000); 11438c2ecf20Sopenharmony_ci bt_max = DIV_ROUND_UP(bt_max, 1000); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (bt_max < bt_min) 11468c2ecf20Sopenharmony_ci return -EINVAL; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci meson_chip->level1_divider = div; 11498c2ecf20Sopenharmony_ci meson_chip->clk_rate = 1000000000 / meson_chip->level1_divider; 11508c2ecf20Sopenharmony_ci meson_chip->bus_timing = (bt_min + bt_max) / 2 + 1; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return 0; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic int meson_nand_bch_mode(struct nand_chip *nand) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 11588c2ecf20Sopenharmony_ci int i; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci if (nand->ecc.strength > 60 || nand->ecc.strength < 8) 11618c2ecf20Sopenharmony_ci return -EINVAL; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(meson_ecc); i++) { 11648c2ecf20Sopenharmony_ci if (meson_ecc[i].strength == nand->ecc.strength) { 11658c2ecf20Sopenharmony_ci meson_chip->bch_mode = meson_ecc[i].bch; 11668c2ecf20Sopenharmony_ci return 0; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci return -EINVAL; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic void meson_nand_detach_chip(struct nand_chip *nand) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci meson_nfc_free_buffer(nand); 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic int meson_nand_attach_chip(struct nand_chip *nand) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 11818c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 11828c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 11838c2ecf20Sopenharmony_ci int ret; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci if (!mtd->name) { 11868c2ecf20Sopenharmony_ci mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL, 11878c2ecf20Sopenharmony_ci "%s:nand%d", 11888c2ecf20Sopenharmony_ci dev_name(nfc->dev), 11898c2ecf20Sopenharmony_ci meson_chip->sels[0]); 11908c2ecf20Sopenharmony_ci if (!mtd->name) 11918c2ecf20Sopenharmony_ci return -ENOMEM; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if (nand->bbt_options & NAND_BBT_USE_FLASH) 11958c2ecf20Sopenharmony_ci nand->bbt_options |= NAND_BBT_NO_OOB; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci nand->options |= NAND_NO_SUBPAGE_WRITE; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci ret = nand_ecc_choose_conf(nand, nfc->data->ecc_caps, 12008c2ecf20Sopenharmony_ci mtd->oobsize - 2); 12018c2ecf20Sopenharmony_ci if (ret) { 12028c2ecf20Sopenharmony_ci dev_err(nfc->dev, "failed to ECC init\n"); 12038c2ecf20Sopenharmony_ci return -EINVAL; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &meson_ooblayout_ops); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci ret = meson_nand_bch_mode(nand); 12098c2ecf20Sopenharmony_ci if (ret) 12108c2ecf20Sopenharmony_ci return -EINVAL; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 12138c2ecf20Sopenharmony_ci nand->ecc.write_page_raw = meson_nfc_write_page_raw; 12148c2ecf20Sopenharmony_ci nand->ecc.write_page = meson_nfc_write_page_hwecc; 12158c2ecf20Sopenharmony_ci nand->ecc.write_oob_raw = nand_write_oob_std; 12168c2ecf20Sopenharmony_ci nand->ecc.write_oob = nand_write_oob_std; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci nand->ecc.read_page_raw = meson_nfc_read_page_raw; 12198c2ecf20Sopenharmony_ci nand->ecc.read_page = meson_nfc_read_page_hwecc; 12208c2ecf20Sopenharmony_ci nand->ecc.read_oob_raw = meson_nfc_read_oob_raw; 12218c2ecf20Sopenharmony_ci nand->ecc.read_oob = meson_nfc_read_oob; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (nand->options & NAND_BUSWIDTH_16) { 12248c2ecf20Sopenharmony_ci dev_err(nfc->dev, "16bits bus width not supported"); 12258c2ecf20Sopenharmony_ci return -EINVAL; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci ret = meson_chip_buffer_init(nand); 12288c2ecf20Sopenharmony_ci if (ret) 12298c2ecf20Sopenharmony_ci return -ENOMEM; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return ret; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic const struct nand_controller_ops meson_nand_controller_ops = { 12358c2ecf20Sopenharmony_ci .attach_chip = meson_nand_attach_chip, 12368c2ecf20Sopenharmony_ci .detach_chip = meson_nand_detach_chip, 12378c2ecf20Sopenharmony_ci .setup_interface = meson_nfc_setup_interface, 12388c2ecf20Sopenharmony_ci .exec_op = meson_nfc_exec_op, 12398c2ecf20Sopenharmony_ci}; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic int 12428c2ecf20Sopenharmony_cimeson_nfc_nand_chip_init(struct device *dev, 12438c2ecf20Sopenharmony_ci struct meson_nfc *nfc, struct device_node *np) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip; 12468c2ecf20Sopenharmony_ci struct nand_chip *nand; 12478c2ecf20Sopenharmony_ci struct mtd_info *mtd; 12488c2ecf20Sopenharmony_ci int ret, i; 12498c2ecf20Sopenharmony_ci u32 tmp, nsels; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32)); 12528c2ecf20Sopenharmony_ci if (!nsels || nsels > MAX_CE_NUM) { 12538c2ecf20Sopenharmony_ci dev_err(dev, "invalid register property size\n"); 12548c2ecf20Sopenharmony_ci return -EINVAL; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci meson_chip = devm_kzalloc(dev, struct_size(meson_chip, sels, nsels), 12588c2ecf20Sopenharmony_ci GFP_KERNEL); 12598c2ecf20Sopenharmony_ci if (!meson_chip) 12608c2ecf20Sopenharmony_ci return -ENOMEM; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci meson_chip->nsels = nsels; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci for (i = 0; i < nsels; i++) { 12658c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(np, "reg", i, &tmp); 12668c2ecf20Sopenharmony_ci if (ret) { 12678c2ecf20Sopenharmony_ci dev_err(dev, "could not retrieve register property: %d\n", 12688c2ecf20Sopenharmony_ci ret); 12698c2ecf20Sopenharmony_ci return ret; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (test_and_set_bit(tmp, &nfc->assigned_cs)) { 12738c2ecf20Sopenharmony_ci dev_err(dev, "CS %d already assigned\n", tmp); 12748c2ecf20Sopenharmony_ci return -EINVAL; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci nand = &meson_chip->nand; 12798c2ecf20Sopenharmony_ci nand->controller = &nfc->controller; 12808c2ecf20Sopenharmony_ci nand->controller->ops = &meson_nand_controller_ops; 12818c2ecf20Sopenharmony_ci nand_set_flash_node(nand, np); 12828c2ecf20Sopenharmony_ci nand_set_controller_data(nand, nfc); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci nand->options |= NAND_USES_DMA; 12858c2ecf20Sopenharmony_ci mtd = nand_to_mtd(nand); 12868c2ecf20Sopenharmony_ci mtd->owner = THIS_MODULE; 12878c2ecf20Sopenharmony_ci mtd->dev.parent = dev; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci ret = nand_scan(nand, nsels); 12908c2ecf20Sopenharmony_ci if (ret) 12918c2ecf20Sopenharmony_ci return ret; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 12948c2ecf20Sopenharmony_ci if (ret) { 12958c2ecf20Sopenharmony_ci dev_err(dev, "failed to register MTD device: %d\n", ret); 12968c2ecf20Sopenharmony_ci nand_cleanup(nand); 12978c2ecf20Sopenharmony_ci return ret; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci list_add_tail(&meson_chip->node, &nfc->chips); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci return 0; 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_cistatic int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip; 13088c2ecf20Sopenharmony_ci struct mtd_info *mtd; 13098c2ecf20Sopenharmony_ci int ret; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci while (!list_empty(&nfc->chips)) { 13128c2ecf20Sopenharmony_ci meson_chip = list_first_entry(&nfc->chips, 13138c2ecf20Sopenharmony_ci struct meson_nfc_nand_chip, node); 13148c2ecf20Sopenharmony_ci mtd = nand_to_mtd(&meson_chip->nand); 13158c2ecf20Sopenharmony_ci ret = mtd_device_unregister(mtd); 13168c2ecf20Sopenharmony_ci if (ret) 13178c2ecf20Sopenharmony_ci return ret; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci nand_cleanup(&meson_chip->nand); 13208c2ecf20Sopenharmony_ci list_del(&meson_chip->node); 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci return 0; 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_cistatic int meson_nfc_nand_chips_init(struct device *dev, 13278c2ecf20Sopenharmony_ci struct meson_nfc *nfc) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 13308c2ecf20Sopenharmony_ci struct device_node *nand_np; 13318c2ecf20Sopenharmony_ci int ret; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci for_each_child_of_node(np, nand_np) { 13348c2ecf20Sopenharmony_ci ret = meson_nfc_nand_chip_init(dev, nfc, nand_np); 13358c2ecf20Sopenharmony_ci if (ret) { 13368c2ecf20Sopenharmony_ci meson_nfc_nand_chip_cleanup(nfc); 13378c2ecf20Sopenharmony_ci of_node_put(nand_np); 13388c2ecf20Sopenharmony_ci return ret; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci return 0; 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic irqreturn_t meson_nfc_irq(int irq, void *id) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci struct meson_nfc *nfc = id; 13488c2ecf20Sopenharmony_ci u32 cfg; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci cfg = readl(nfc->reg_base + NFC_REG_CFG); 13518c2ecf20Sopenharmony_ci if (!(cfg & NFC_RB_IRQ_EN)) 13528c2ecf20Sopenharmony_ci return IRQ_NONE; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci cfg &= ~(NFC_RB_IRQ_EN); 13558c2ecf20Sopenharmony_ci writel(cfg, nfc->reg_base + NFC_REG_CFG); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci complete(&nfc->completion); 13588c2ecf20Sopenharmony_ci return IRQ_HANDLED; 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic const struct meson_nfc_data meson_gxl_data = { 13628c2ecf20Sopenharmony_ci .ecc_caps = &meson_gxl_ecc_caps, 13638c2ecf20Sopenharmony_ci}; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_cistatic const struct meson_nfc_data meson_axg_data = { 13668c2ecf20Sopenharmony_ci .ecc_caps = &meson_axg_ecc_caps, 13678c2ecf20Sopenharmony_ci}; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic const struct of_device_id meson_nfc_id_table[] = { 13708c2ecf20Sopenharmony_ci { 13718c2ecf20Sopenharmony_ci .compatible = "amlogic,meson-gxl-nfc", 13728c2ecf20Sopenharmony_ci .data = &meson_gxl_data, 13738c2ecf20Sopenharmony_ci }, { 13748c2ecf20Sopenharmony_ci .compatible = "amlogic,meson-axg-nfc", 13758c2ecf20Sopenharmony_ci .data = &meson_axg_data, 13768c2ecf20Sopenharmony_ci }, 13778c2ecf20Sopenharmony_ci {} 13788c2ecf20Sopenharmony_ci}; 13798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_nfc_id_table); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_cistatic int meson_nfc_probe(struct platform_device *pdev) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 13848c2ecf20Sopenharmony_ci struct meson_nfc *nfc; 13858c2ecf20Sopenharmony_ci struct resource *res; 13868c2ecf20Sopenharmony_ci int ret, irq; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); 13898c2ecf20Sopenharmony_ci if (!nfc) 13908c2ecf20Sopenharmony_ci return -ENOMEM; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci nfc->data = of_device_get_match_data(&pdev->dev); 13938c2ecf20Sopenharmony_ci if (!nfc->data) 13948c2ecf20Sopenharmony_ci return -ENODEV; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci nand_controller_init(&nfc->controller); 13978c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nfc->chips); 13988c2ecf20Sopenharmony_ci init_completion(&nfc->completion); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci nfc->dev = dev; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14038c2ecf20Sopenharmony_ci nfc->reg_base = devm_ioremap_resource(dev, res); 14048c2ecf20Sopenharmony_ci if (IS_ERR(nfc->reg_base)) 14058c2ecf20Sopenharmony_ci return PTR_ERR(nfc->reg_base); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci nfc->reg_clk = 14088c2ecf20Sopenharmony_ci syscon_regmap_lookup_by_phandle(dev->of_node, 14098c2ecf20Sopenharmony_ci "amlogic,mmc-syscon"); 14108c2ecf20Sopenharmony_ci if (IS_ERR(nfc->reg_clk)) { 14118c2ecf20Sopenharmony_ci dev_err(dev, "Failed to lookup clock base\n"); 14128c2ecf20Sopenharmony_ci return PTR_ERR(nfc->reg_clk); 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 14168c2ecf20Sopenharmony_ci if (irq < 0) 14178c2ecf20Sopenharmony_ci return -EINVAL; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci ret = meson_nfc_clk_init(nfc); 14208c2ecf20Sopenharmony_ci if (ret) { 14218c2ecf20Sopenharmony_ci dev_err(dev, "failed to initialize NAND clock\n"); 14228c2ecf20Sopenharmony_ci return ret; 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci writel(0, nfc->reg_base + NFC_REG_CFG); 14268c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc); 14278c2ecf20Sopenharmony_ci if (ret) { 14288c2ecf20Sopenharmony_ci dev_err(dev, "failed to request NFC IRQ\n"); 14298c2ecf20Sopenharmony_ci ret = -EINVAL; 14308c2ecf20Sopenharmony_ci goto err_clk; 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 14348c2ecf20Sopenharmony_ci if (ret) { 14358c2ecf20Sopenharmony_ci dev_err(dev, "failed to set DMA mask\n"); 14368c2ecf20Sopenharmony_ci goto err_clk; 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, nfc); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci ret = meson_nfc_nand_chips_init(dev, nfc); 14428c2ecf20Sopenharmony_ci if (ret) { 14438c2ecf20Sopenharmony_ci dev_err(dev, "failed to init NAND chips\n"); 14448c2ecf20Sopenharmony_ci goto err_clk; 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci return 0; 14488c2ecf20Sopenharmony_cierr_clk: 14498c2ecf20Sopenharmony_ci meson_nfc_disable_clk(nfc); 14508c2ecf20Sopenharmony_ci return ret; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cistatic int meson_nfc_remove(struct platform_device *pdev) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct meson_nfc *nfc = platform_get_drvdata(pdev); 14568c2ecf20Sopenharmony_ci int ret; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci ret = meson_nfc_nand_chip_cleanup(nfc); 14598c2ecf20Sopenharmony_ci if (ret) 14608c2ecf20Sopenharmony_ci return ret; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci meson_nfc_disable_clk(nfc); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, NULL); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci return 0; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic struct platform_driver meson_nfc_driver = { 14708c2ecf20Sopenharmony_ci .probe = meson_nfc_probe, 14718c2ecf20Sopenharmony_ci .remove = meson_nfc_remove, 14728c2ecf20Sopenharmony_ci .driver = { 14738c2ecf20Sopenharmony_ci .name = "meson-nand", 14748c2ecf20Sopenharmony_ci .of_match_table = meson_nfc_id_table, 14758c2ecf20Sopenharmony_ci }, 14768c2ecf20Sopenharmony_ci}; 14778c2ecf20Sopenharmony_cimodule_platform_driver(meson_nfc_driver); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL"); 14808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liang Yang <liang.yang@amlogic.com>"); 14818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic's Meson NAND Flash Controller driver"); 1482