162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR MIT) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Amlogic Meson Nand Flash Controller Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2018 Amlogic, inc. 662306a36Sopenharmony_ci * Author: Liang Yang <liang.yang@amlogic.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/platform_device.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/clk-provider.h> 1462306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 1562306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1662306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/iopoll.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define NFC_REG_CMD 0x00 2562306a36Sopenharmony_ci#define NFC_CMD_IDLE (0xc << 14) 2662306a36Sopenharmony_ci#define NFC_CMD_CLE (0x5 << 14) 2762306a36Sopenharmony_ci#define NFC_CMD_ALE (0x6 << 14) 2862306a36Sopenharmony_ci#define NFC_CMD_ADL ((0 << 16) | (3 << 20)) 2962306a36Sopenharmony_ci#define NFC_CMD_ADH ((1 << 16) | (3 << 20)) 3062306a36Sopenharmony_ci#define NFC_CMD_AIL ((2 << 16) | (3 << 20)) 3162306a36Sopenharmony_ci#define NFC_CMD_AIH ((3 << 16) | (3 << 20)) 3262306a36Sopenharmony_ci#define NFC_CMD_SEED ((8 << 16) | (3 << 20)) 3362306a36Sopenharmony_ci#define NFC_CMD_M2N ((0 << 17) | (2 << 20)) 3462306a36Sopenharmony_ci#define NFC_CMD_N2M ((1 << 17) | (2 << 20)) 3562306a36Sopenharmony_ci#define NFC_CMD_RB BIT(20) 3662306a36Sopenharmony_ci#define NFC_CMD_SCRAMBLER_ENABLE BIT(19) 3762306a36Sopenharmony_ci#define NFC_CMD_SCRAMBLER_DISABLE 0 3862306a36Sopenharmony_ci#define NFC_CMD_SHORTMODE_DISABLE 0 3962306a36Sopenharmony_ci#define NFC_CMD_RB_INT BIT(14) 4062306a36Sopenharmony_ci#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16)) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0)) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define NFC_REG_CFG 0x04 4562306a36Sopenharmony_ci#define NFC_REG_DADR 0x08 4662306a36Sopenharmony_ci#define NFC_REG_IADR 0x0c 4762306a36Sopenharmony_ci#define NFC_REG_BUF 0x10 4862306a36Sopenharmony_ci#define NFC_REG_INFO 0x14 4962306a36Sopenharmony_ci#define NFC_REG_DC 0x18 5062306a36Sopenharmony_ci#define NFC_REG_ADR 0x1c 5162306a36Sopenharmony_ci#define NFC_REG_DL 0x20 5262306a36Sopenharmony_ci#define NFC_REG_DH 0x24 5362306a36Sopenharmony_ci#define NFC_REG_CADR 0x28 5462306a36Sopenharmony_ci#define NFC_REG_SADR 0x2c 5562306a36Sopenharmony_ci#define NFC_REG_PINS 0x30 5662306a36Sopenharmony_ci#define NFC_REG_VER 0x38 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define NFC_RB_IRQ_EN BIT(21) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define CLK_DIV_SHIFT 0 6162306a36Sopenharmony_ci#define CLK_DIV_WIDTH 6 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \ 6462306a36Sopenharmony_ci ( \ 6562306a36Sopenharmony_ci (cmd_dir) | \ 6662306a36Sopenharmony_ci (ran) | \ 6762306a36Sopenharmony_ci ((bch) << 14) | \ 6862306a36Sopenharmony_ci ((short_mode) << 13) | \ 6962306a36Sopenharmony_ci (((page_size) & 0x7f) << 6) | \ 7062306a36Sopenharmony_ci ((pages) & 0x3f) \ 7162306a36Sopenharmony_ci ) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define GENCMDDADDRL(adl, addr) ((adl) | ((addr) & 0xffff)) 7462306a36Sopenharmony_ci#define GENCMDDADDRH(adh, addr) ((adh) | (((addr) >> 16) & 0xffff)) 7562306a36Sopenharmony_ci#define GENCMDIADDRL(ail, addr) ((ail) | ((addr) & 0xffff)) 7662306a36Sopenharmony_ci#define GENCMDIADDRH(aih, addr) ((aih) | (((addr) >> 16) & 0xffff)) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define DMA_DIR(dir) ((dir) ? NFC_CMD_N2M : NFC_CMD_M2N) 7962306a36Sopenharmony_ci#define DMA_ADDR_ALIGN 8 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define ECC_CHECK_RETURN_FF (-1) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define NAND_CE0 (0xe << 10) 8462306a36Sopenharmony_ci#define NAND_CE1 (0xd << 10) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define DMA_BUSY_TIMEOUT 0x100000 8762306a36Sopenharmony_ci#define CMD_FIFO_EMPTY_TIMEOUT 1000 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define MAX_CE_NUM 2 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* eMMC clock register, misc control */ 9262306a36Sopenharmony_ci#define CLK_SELECT_NAND BIT(31) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define NFC_CLK_CYCLE 6 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* nand flash controller delay 3 ns */ 9762306a36Sopenharmony_ci#define NFC_DEFAULT_DELAY 3000 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define ROW_ADDER(page, index) (((page) >> (8 * (index))) & 0xff) 10062306a36Sopenharmony_ci#define MAX_CYCLE_ADDRS 5 10162306a36Sopenharmony_ci#define DIRREAD 1 10262306a36Sopenharmony_ci#define DIRWRITE 0 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define ECC_PARITY_BCH8_512B 14 10562306a36Sopenharmony_ci#define ECC_COMPLETE BIT(31) 10662306a36Sopenharmony_ci#define ECC_ERR_CNT(x) (((x) >> 24) & GENMASK(5, 0)) 10762306a36Sopenharmony_ci#define ECC_ZERO_CNT(x) (((x) >> 16) & GENMASK(5, 0)) 10862306a36Sopenharmony_ci#define ECC_UNCORRECTABLE 0x3f 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define PER_INFO_BYTE 8 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define NFC_CMD_RAW_LEN GENMASK(13, 0) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define NFC_COLUMN_ADDR_0 0 11562306a36Sopenharmony_ci#define NFC_COLUMN_ADDR_1 0 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct meson_nfc_nand_chip { 11862306a36Sopenharmony_ci struct list_head node; 11962306a36Sopenharmony_ci struct nand_chip nand; 12062306a36Sopenharmony_ci unsigned long clk_rate; 12162306a36Sopenharmony_ci unsigned long level1_divider; 12262306a36Sopenharmony_ci u32 bus_timing; 12362306a36Sopenharmony_ci u32 twb; 12462306a36Sopenharmony_ci u32 tadl; 12562306a36Sopenharmony_ci u32 tbers_max; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci u32 bch_mode; 12862306a36Sopenharmony_ci u8 *data_buf; 12962306a36Sopenharmony_ci __le64 *info_buf; 13062306a36Sopenharmony_ci u32 nsels; 13162306a36Sopenharmony_ci u8 sels[]; 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistruct meson_nand_ecc { 13562306a36Sopenharmony_ci u32 bch; 13662306a36Sopenharmony_ci u32 strength; 13762306a36Sopenharmony_ci u32 size; 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct meson_nfc_data { 14162306a36Sopenharmony_ci const struct nand_ecc_caps *ecc_caps; 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct meson_nfc_param { 14562306a36Sopenharmony_ci u32 chip_select; 14662306a36Sopenharmony_ci u32 rb_select; 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistruct nand_rw_cmd { 15062306a36Sopenharmony_ci u32 cmd0; 15162306a36Sopenharmony_ci u32 addrs[MAX_CYCLE_ADDRS]; 15262306a36Sopenharmony_ci u32 cmd1; 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistruct nand_timing { 15662306a36Sopenharmony_ci u32 twb; 15762306a36Sopenharmony_ci u32 tadl; 15862306a36Sopenharmony_ci u32 tbers_max; 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistruct meson_nfc { 16262306a36Sopenharmony_ci struct nand_controller controller; 16362306a36Sopenharmony_ci struct clk *core_clk; 16462306a36Sopenharmony_ci struct clk *device_clk; 16562306a36Sopenharmony_ci struct clk *nand_clk; 16662306a36Sopenharmony_ci struct clk_divider nand_divider; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci unsigned long clk_rate; 16962306a36Sopenharmony_ci u32 bus_timing; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci struct device *dev; 17262306a36Sopenharmony_ci void __iomem *reg_base; 17362306a36Sopenharmony_ci void __iomem *reg_clk; 17462306a36Sopenharmony_ci struct completion completion; 17562306a36Sopenharmony_ci struct list_head chips; 17662306a36Sopenharmony_ci const struct meson_nfc_data *data; 17762306a36Sopenharmony_ci struct meson_nfc_param param; 17862306a36Sopenharmony_ci struct nand_timing timing; 17962306a36Sopenharmony_ci union { 18062306a36Sopenharmony_ci int cmd[32]; 18162306a36Sopenharmony_ci struct nand_rw_cmd rw; 18262306a36Sopenharmony_ci } cmdfifo; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci dma_addr_t daddr; 18562306a36Sopenharmony_ci dma_addr_t iaddr; 18662306a36Sopenharmony_ci u32 info_bytes; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci unsigned long assigned_cs; 18962306a36Sopenharmony_ci bool no_rb_pin; 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cienum { 19362306a36Sopenharmony_ci NFC_ECC_BCH8_512 = 1, 19462306a36Sopenharmony_ci NFC_ECC_BCH8_1K, 19562306a36Sopenharmony_ci NFC_ECC_BCH24_1K, 19662306a36Sopenharmony_ci NFC_ECC_BCH30_1K, 19762306a36Sopenharmony_ci NFC_ECC_BCH40_1K, 19862306a36Sopenharmony_ci NFC_ECC_BCH50_1K, 19962306a36Sopenharmony_ci NFC_ECC_BCH60_1K, 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci#define MESON_ECC_DATA(b, s, sz) { .bch = (b), .strength = (s), .size = (sz) } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct meson_nand_ecc meson_ecc[] = { 20562306a36Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH8_512, 8, 512), 20662306a36Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 1024), 20762306a36Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24, 1024), 20862306a36Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30, 1024), 20962306a36Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40, 1024), 21062306a36Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50, 1024), 21162306a36Sopenharmony_ci MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60, 1024), 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int meson_nand_calc_ecc_bytes(int step_size, int strength) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci int ecc_bytes; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (step_size == 512 && strength == 8) 21962306a36Sopenharmony_ci return ECC_PARITY_BCH8_512B; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8); 22262306a36Sopenharmony_ci ecc_bytes = ALIGN(ecc_bytes, 2); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return ecc_bytes; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciNAND_ECC_CAPS_SINGLE(meson_gxl_ecc_caps, 22862306a36Sopenharmony_ci meson_nand_calc_ecc_bytes, 1024, 8, 24, 30, 40, 50, 60); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const int axg_stepinfo_strengths[] = { 8 }; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic const struct nand_ecc_step_info axg_stepinfo[] = { 23362306a36Sopenharmony_ci { 23462306a36Sopenharmony_ci .stepsize = 1024, 23562306a36Sopenharmony_ci .strengths = axg_stepinfo_strengths, 23662306a36Sopenharmony_ci .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths) 23762306a36Sopenharmony_ci }, 23862306a36Sopenharmony_ci { 23962306a36Sopenharmony_ci .stepsize = 512, 24062306a36Sopenharmony_ci .strengths = axg_stepinfo_strengths, 24162306a36Sopenharmony_ci .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths) 24262306a36Sopenharmony_ci }, 24362306a36Sopenharmony_ci}; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic const struct nand_ecc_caps meson_axg_ecc_caps = { 24662306a36Sopenharmony_ci .stepinfos = axg_stepinfo, 24762306a36Sopenharmony_ci .nstepinfos = ARRAY_SIZE(axg_stepinfo), 24862306a36Sopenharmony_ci .calc_ecc_bytes = meson_nand_calc_ecc_bytes, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci return container_of(nand, struct meson_nfc_nand_chip, nand); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void meson_nfc_select_chip(struct nand_chip *nand, int chip) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 25962306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 26062306a36Sopenharmony_ci int ret, value; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (chip < 0 || WARN_ON_ONCE(chip >= meson_chip->nsels)) 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0; 26662306a36Sopenharmony_ci nfc->param.rb_select = nfc->param.chip_select; 26762306a36Sopenharmony_ci nfc->timing.twb = meson_chip->twb; 26862306a36Sopenharmony_ci nfc->timing.tadl = meson_chip->tadl; 26962306a36Sopenharmony_ci nfc->timing.tbers_max = meson_chip->tbers_max; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (nfc->clk_rate != meson_chip->clk_rate) { 27262306a36Sopenharmony_ci ret = clk_set_rate(nfc->nand_clk, meson_chip->clk_rate); 27362306a36Sopenharmony_ci if (ret) { 27462306a36Sopenharmony_ci dev_err(nfc->dev, "failed to set clock rate\n"); 27562306a36Sopenharmony_ci return; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci nfc->clk_rate = meson_chip->clk_rate; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci if (nfc->bus_timing != meson_chip->bus_timing) { 28062306a36Sopenharmony_ci value = (NFC_CLK_CYCLE - 1) | (meson_chip->bus_timing << 5); 28162306a36Sopenharmony_ci writel(value, nfc->reg_base + NFC_REG_CFG); 28262306a36Sopenharmony_ci writel((1 << 31), nfc->reg_base + NFC_REG_CMD); 28362306a36Sopenharmony_ci nfc->bus_timing = meson_chip->bus_timing; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff), 29062306a36Sopenharmony_ci nfc->reg_base + NFC_REG_CMD); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)), 29662306a36Sopenharmony_ci nfc->reg_base + NFC_REG_CMD); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir, 30062306a36Sopenharmony_ci int scrambler) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 30362306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); 30462306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 30562306a36Sopenharmony_ci u32 bch = meson_chip->bch_mode, cmd; 30662306a36Sopenharmony_ci int len = mtd->writesize, pagesize, pages; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci pagesize = nand->ecc.size; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (raw) { 31162306a36Sopenharmony_ci len = mtd->writesize + mtd->oobsize; 31262306a36Sopenharmony_ci cmd = len | scrambler | DMA_DIR(dir); 31362306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci pages = len / nand->ecc.size; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci cmd = CMDRWGEN(DMA_DIR(dir), scrambler, bch, 32062306a36Sopenharmony_ci NFC_CMD_SHORTMODE_DISABLE, pagesize, pages); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void meson_nfc_drain_cmd(struct meson_nfc *nfc) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * Insert two commands to make sure all valid commands are finished. 32962306a36Sopenharmony_ci * 33062306a36Sopenharmony_ci * The Nand flash controller is designed as two stages pipleline - 33162306a36Sopenharmony_ci * a) fetch and b) excute. 33262306a36Sopenharmony_ci * There might be cases when the driver see command queue is empty, 33362306a36Sopenharmony_ci * but the Nand flash controller still has two commands buffered, 33462306a36Sopenharmony_ci * one is fetched into NFC request queue (ready to run), and another 33562306a36Sopenharmony_ci * is actively executing. So pushing 2 "IDLE" commands guarantees that 33662306a36Sopenharmony_ci * the pipeline is emptied. 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci meson_nfc_cmd_idle(nfc, 0); 33962306a36Sopenharmony_ci meson_nfc_cmd_idle(nfc, 0); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc, 34362306a36Sopenharmony_ci unsigned int timeout_ms) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci u32 cmd_size = 0; 34662306a36Sopenharmony_ci int ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* wait cmd fifo is empty */ 34962306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size, 35062306a36Sopenharmony_ci !NFC_CMD_GET_SIZE(cmd_size), 35162306a36Sopenharmony_ci 10, timeout_ms * 1000); 35262306a36Sopenharmony_ci if (ret) 35362306a36Sopenharmony_ci dev_err(nfc->dev, "wait for empty CMD FIFO time out\n"); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int meson_nfc_wait_dma_finish(struct meson_nfc *nfc) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci meson_nfc_drain_cmd(nfc); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 36862306a36Sopenharmony_ci int len; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return meson_chip->data_buf + len; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 37862306a36Sopenharmony_ci int len, temp; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci temp = nand->ecc.size + nand->ecc.bytes; 38162306a36Sopenharmony_ci len = (temp + 2) * i; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return meson_chip->data_buf + len; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void meson_nfc_get_data_oob(struct nand_chip *nand, 38762306a36Sopenharmony_ci u8 *buf, u8 *oobbuf) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci int i, oob_len = 0; 39062306a36Sopenharmony_ci u8 *dsrc, *osrc; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci oob_len = nand->ecc.bytes + 2; 39362306a36Sopenharmony_ci for (i = 0; i < nand->ecc.steps; i++) { 39462306a36Sopenharmony_ci if (buf) { 39562306a36Sopenharmony_ci dsrc = meson_nfc_data_ptr(nand, i); 39662306a36Sopenharmony_ci memcpy(buf, dsrc, nand->ecc.size); 39762306a36Sopenharmony_ci buf += nand->ecc.size; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci osrc = meson_nfc_oob_ptr(nand, i); 40062306a36Sopenharmony_ci memcpy(oobbuf, osrc, oob_len); 40162306a36Sopenharmony_ci oobbuf += oob_len; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void meson_nfc_set_data_oob(struct nand_chip *nand, 40662306a36Sopenharmony_ci const u8 *buf, u8 *oobbuf) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci int i, oob_len = 0; 40962306a36Sopenharmony_ci u8 *dsrc, *osrc; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci oob_len = nand->ecc.bytes + 2; 41262306a36Sopenharmony_ci for (i = 0; i < nand->ecc.steps; i++) { 41362306a36Sopenharmony_ci if (buf) { 41462306a36Sopenharmony_ci dsrc = meson_nfc_data_ptr(nand, i); 41562306a36Sopenharmony_ci memcpy(dsrc, buf, nand->ecc.size); 41662306a36Sopenharmony_ci buf += nand->ecc.size; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci osrc = meson_nfc_oob_ptr(nand, i); 41962306a36Sopenharmony_ci memcpy(osrc, oobbuf, oob_len); 42062306a36Sopenharmony_ci oobbuf += oob_len; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int meson_nfc_wait_no_rb_pin(struct nand_chip *nand, int timeout_ms, 42562306a36Sopenharmony_ci bool need_cmd_read0) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 42862306a36Sopenharmony_ci u32 cmd, cfg; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci meson_nfc_cmd_idle(nfc, nfc->timing.twb); 43162306a36Sopenharmony_ci meson_nfc_drain_cmd(nfc); 43262306a36Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci cfg = readl(nfc->reg_base + NFC_REG_CFG); 43562306a36Sopenharmony_ci cfg |= NFC_RB_IRQ_EN; 43662306a36Sopenharmony_ci writel(cfg, nfc->reg_base + NFC_REG_CFG); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci reinit_completion(&nfc->completion); 43962306a36Sopenharmony_ci nand_status_op(nand, NULL); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* use the max erase time as the maximum clock for waiting R/B */ 44262306a36Sopenharmony_ci cmd = NFC_CMD_RB | NFC_CMD_RB_INT_NO_PIN | nfc->timing.tbers_max; 44362306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!wait_for_completion_timeout(&nfc->completion, 44662306a36Sopenharmony_ci msecs_to_jiffies(timeout_ms))) 44762306a36Sopenharmony_ci return -ETIMEDOUT; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (need_cmd_read0) 45062306a36Sopenharmony_ci nand_exit_status_op(nand); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int meson_nfc_wait_rb_pin(struct meson_nfc *nfc, int timeout_ms) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci u32 cmd, cfg; 45862306a36Sopenharmony_ci int ret = 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci meson_nfc_cmd_idle(nfc, nfc->timing.twb); 46162306a36Sopenharmony_ci meson_nfc_drain_cmd(nfc); 46262306a36Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci cfg = readl(nfc->reg_base + NFC_REG_CFG); 46562306a36Sopenharmony_ci cfg |= NFC_RB_IRQ_EN; 46662306a36Sopenharmony_ci writel(cfg, nfc->reg_base + NFC_REG_CFG); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci reinit_completion(&nfc->completion); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* use the max erase time as the maximum clock for waiting R/B */ 47162306a36Sopenharmony_ci cmd = NFC_CMD_RB | NFC_CMD_RB_INT 47262306a36Sopenharmony_ci | nfc->param.chip_select | nfc->timing.tbers_max; 47362306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = wait_for_completion_timeout(&nfc->completion, 47662306a36Sopenharmony_ci msecs_to_jiffies(timeout_ms)); 47762306a36Sopenharmony_ci if (ret == 0) 47862306a36Sopenharmony_ci ret = -1; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int meson_nfc_queue_rb(struct nand_chip *nand, int timeout_ms, 48462306a36Sopenharmony_ci bool need_cmd_read0) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (nfc->no_rb_pin) { 48962306a36Sopenharmony_ci /* This mode is used when there is no wired R/B pin. 49062306a36Sopenharmony_ci * It works like 'nand_soft_waitrdy()', but instead of 49162306a36Sopenharmony_ci * polling NAND_CMD_STATUS bit in the software loop, 49262306a36Sopenharmony_ci * it will wait for interrupt - controllers checks IO 49362306a36Sopenharmony_ci * bus and when it detects NAND_CMD_STATUS on it, it 49462306a36Sopenharmony_ci * raises interrupt. After interrupt, NAND_CMD_READ0 is 49562306a36Sopenharmony_ci * sent as terminator of the ready waiting procedure if 49662306a36Sopenharmony_ci * needed (for all cases except page programming - this 49762306a36Sopenharmony_ci * is reason of 'need_cmd_read0' flag). 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci return meson_nfc_wait_no_rb_pin(nand, timeout_ms, 50062306a36Sopenharmony_ci need_cmd_read0); 50162306a36Sopenharmony_ci } else { 50262306a36Sopenharmony_ci return meson_nfc_wait_rb_pin(nfc, timeout_ms); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 50962306a36Sopenharmony_ci __le64 *info; 51062306a36Sopenharmony_ci int i, count; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) { 51362306a36Sopenharmony_ci info = &meson_chip->info_buf[i]; 51462306a36Sopenharmony_ci *info |= oob_buf[count]; 51562306a36Sopenharmony_ci *info |= oob_buf[count + 1] << 8; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 52262306a36Sopenharmony_ci __le64 *info; 52362306a36Sopenharmony_ci int i, count; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) { 52662306a36Sopenharmony_ci info = &meson_chip->info_buf[i]; 52762306a36Sopenharmony_ci oob_buf[count] = *info; 52862306a36Sopenharmony_ci oob_buf[count + 1] = *info >> 8; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips, 53362306a36Sopenharmony_ci u64 *correct_bitmap) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 53662306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 53762306a36Sopenharmony_ci __le64 *info; 53862306a36Sopenharmony_ci int ret = 0, i; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci for (i = 0; i < nand->ecc.steps; i++) { 54162306a36Sopenharmony_ci info = &meson_chip->info_buf[i]; 54262306a36Sopenharmony_ci if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) { 54362306a36Sopenharmony_ci mtd->ecc_stats.corrected += ECC_ERR_CNT(*info); 54462306a36Sopenharmony_ci *bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info)); 54562306a36Sopenharmony_ci *correct_bitmap |= BIT_ULL(i); 54662306a36Sopenharmony_ci continue; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci if ((nand->options & NAND_NEED_SCRAMBLING) && 54962306a36Sopenharmony_ci ECC_ZERO_CNT(*info) < nand->ecc.strength) { 55062306a36Sopenharmony_ci mtd->ecc_stats.corrected += ECC_ZERO_CNT(*info); 55162306a36Sopenharmony_ci *bitflips = max_t(u32, *bitflips, 55262306a36Sopenharmony_ci ECC_ZERO_CNT(*info)); 55362306a36Sopenharmony_ci ret = ECC_CHECK_RETURN_FF; 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci ret = -EBADMSG; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf, 56262306a36Sopenharmony_ci int datalen, void *infobuf, int infolen, 56362306a36Sopenharmony_ci enum dma_data_direction dir) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 56662306a36Sopenharmony_ci u32 cmd; 56762306a36Sopenharmony_ci int ret = 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci nfc->daddr = dma_map_single(nfc->dev, databuf, datalen, dir); 57062306a36Sopenharmony_ci ret = dma_mapping_error(nfc->dev, nfc->daddr); 57162306a36Sopenharmony_ci if (ret) { 57262306a36Sopenharmony_ci dev_err(nfc->dev, "DMA mapping error\n"); 57362306a36Sopenharmony_ci return ret; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr); 57662306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr); 57962306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (infobuf) { 58262306a36Sopenharmony_ci nfc->iaddr = dma_map_single(nfc->dev, infobuf, infolen, dir); 58362306a36Sopenharmony_ci ret = dma_mapping_error(nfc->dev, nfc->iaddr); 58462306a36Sopenharmony_ci if (ret) { 58562306a36Sopenharmony_ci dev_err(nfc->dev, "DMA mapping error\n"); 58662306a36Sopenharmony_ci dma_unmap_single(nfc->dev, 58762306a36Sopenharmony_ci nfc->daddr, datalen, dir); 58862306a36Sopenharmony_ci return ret; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci nfc->info_bytes = infolen; 59162306a36Sopenharmony_ci cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr); 59262306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr); 59562306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci return ret; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic void meson_nfc_dma_buffer_release(struct nand_chip *nand, 60262306a36Sopenharmony_ci int datalen, int infolen, 60362306a36Sopenharmony_ci enum dma_data_direction dir) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci dma_unmap_single(nfc->dev, nfc->daddr, datalen, dir); 60862306a36Sopenharmony_ci if (infolen) { 60962306a36Sopenharmony_ci dma_unmap_single(nfc->dev, nfc->iaddr, infolen, dir); 61062306a36Sopenharmony_ci nfc->info_bytes = 0; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 61762306a36Sopenharmony_ci int ret = 0; 61862306a36Sopenharmony_ci u32 cmd; 61962306a36Sopenharmony_ci u8 *info; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci info = kzalloc(PER_INFO_BYTE, GFP_KERNEL); 62262306a36Sopenharmony_ci if (!info) 62362306a36Sopenharmony_ci return -ENOMEM; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci ret = meson_nfc_dma_buffer_setup(nand, buf, len, info, 62662306a36Sopenharmony_ci PER_INFO_BYTE, DMA_FROM_DEVICE); 62762306a36Sopenharmony_ci if (ret) 62862306a36Sopenharmony_ci goto out; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci cmd = NFC_CMD_N2M | len; 63162306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci meson_nfc_drain_cmd(nfc); 63462306a36Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, 1000); 63562306a36Sopenharmony_ci meson_nfc_dma_buffer_release(nand, len, PER_INFO_BYTE, DMA_FROM_DEVICE); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ciout: 63862306a36Sopenharmony_ci kfree(info); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return ret; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 64662306a36Sopenharmony_ci int ret = 0; 64762306a36Sopenharmony_ci u32 cmd; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL, 65062306a36Sopenharmony_ci 0, DMA_TO_DEVICE); 65162306a36Sopenharmony_ci if (ret) 65262306a36Sopenharmony_ci return ret; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci cmd = NFC_CMD_M2N | len; 65562306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci meson_nfc_drain_cmd(nfc); 65862306a36Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, 1000); 65962306a36Sopenharmony_ci meson_nfc_dma_buffer_release(nand, len, 0, DMA_TO_DEVICE); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return ret; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand, 66562306a36Sopenharmony_ci int page, bool in) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci const struct nand_sdr_timings *sdr = 66862306a36Sopenharmony_ci nand_get_sdr_timings(nand_get_interface_config(nand)); 66962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 67062306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 67162306a36Sopenharmony_ci u32 *addrs = nfc->cmdfifo.rw.addrs; 67262306a36Sopenharmony_ci u32 cs = nfc->param.chip_select; 67362306a36Sopenharmony_ci u32 cmd0, cmd_num, row_start; 67462306a36Sopenharmony_ci int i; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci cmd_num = sizeof(struct nand_rw_cmd) / sizeof(int); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN; 67962306a36Sopenharmony_ci nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci addrs[0] = cs | NFC_CMD_ALE | NFC_COLUMN_ADDR_0; 68262306a36Sopenharmony_ci if (mtd->writesize <= 512) { 68362306a36Sopenharmony_ci cmd_num--; 68462306a36Sopenharmony_ci row_start = 1; 68562306a36Sopenharmony_ci } else { 68662306a36Sopenharmony_ci addrs[1] = cs | NFC_CMD_ALE | NFC_COLUMN_ADDR_1; 68762306a36Sopenharmony_ci row_start = 2; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci addrs[row_start] = cs | NFC_CMD_ALE | ROW_ADDER(page, 0); 69162306a36Sopenharmony_ci addrs[row_start + 1] = cs | NFC_CMD_ALE | ROW_ADDER(page, 1); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (nand->options & NAND_ROW_ADDR_3) 69462306a36Sopenharmony_ci addrs[row_start + 2] = 69562306a36Sopenharmony_ci cs | NFC_CMD_ALE | ROW_ADDER(page, 2); 69662306a36Sopenharmony_ci else 69762306a36Sopenharmony_ci cmd_num--; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* subtract cmd1 */ 70062306a36Sopenharmony_ci cmd_num--; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci for (i = 0; i < cmd_num; i++) 70362306a36Sopenharmony_ci writel_relaxed(nfc->cmdfifo.cmd[i], 70462306a36Sopenharmony_ci nfc->reg_base + NFC_REG_CMD); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (in) { 70762306a36Sopenharmony_ci nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART; 70862306a36Sopenharmony_ci writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD); 70962306a36Sopenharmony_ci meson_nfc_queue_rb(nand, PSEC_TO_MSEC(sdr->tR_max), true); 71062306a36Sopenharmony_ci } else { 71162306a36Sopenharmony_ci meson_nfc_cmd_idle(nfc, nfc->timing.tadl); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int meson_nfc_write_page_sub(struct nand_chip *nand, 71862306a36Sopenharmony_ci int page, int raw) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci const struct nand_sdr_timings *sdr = 72162306a36Sopenharmony_ci nand_get_sdr_timings(nand_get_interface_config(nand)); 72262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 72362306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 72462306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 72562306a36Sopenharmony_ci int data_len, info_len; 72662306a36Sopenharmony_ci u32 cmd; 72762306a36Sopenharmony_ci int ret; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci meson_nfc_select_chip(nand, nand->cur_cs); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci data_len = mtd->writesize + mtd->oobsize; 73262306a36Sopenharmony_ci info_len = nand->ecc.steps * PER_INFO_BYTE; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRWRITE); 73562306a36Sopenharmony_ci if (ret) 73662306a36Sopenharmony_ci return ret; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf, 73962306a36Sopenharmony_ci data_len, meson_chip->info_buf, 74062306a36Sopenharmony_ci info_len, DMA_TO_DEVICE); 74162306a36Sopenharmony_ci if (ret) 74262306a36Sopenharmony_ci return ret; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (nand->options & NAND_NEED_SCRAMBLING) { 74562306a36Sopenharmony_ci meson_nfc_cmd_seed(nfc, page); 74662306a36Sopenharmony_ci meson_nfc_cmd_access(nand, raw, DIRWRITE, 74762306a36Sopenharmony_ci NFC_CMD_SCRAMBLER_ENABLE); 74862306a36Sopenharmony_ci } else { 74962306a36Sopenharmony_ci meson_nfc_cmd_access(nand, raw, DIRWRITE, 75062306a36Sopenharmony_ci NFC_CMD_SCRAMBLER_DISABLE); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG; 75462306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 75562306a36Sopenharmony_ci meson_nfc_queue_rb(nand, PSEC_TO_MSEC(sdr->tPROG_max), false); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return ret; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf, 76362306a36Sopenharmony_ci int oob_required, int page) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci u8 *oob_buf = nand->oob_poi; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci meson_nfc_set_data_oob(nand, buf, oob_buf); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return meson_nfc_write_page_sub(nand, page, 1); 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic int meson_nfc_write_page_hwecc(struct nand_chip *nand, 77362306a36Sopenharmony_ci const u8 *buf, int oob_required, int page) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 77662306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 77762306a36Sopenharmony_ci u8 *oob_buf = nand->oob_poi; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci memcpy(meson_chip->data_buf, buf, mtd->writesize); 78062306a36Sopenharmony_ci memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE); 78162306a36Sopenharmony_ci meson_nfc_set_user_byte(nand, oob_buf); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return meson_nfc_write_page_sub(nand, page, 0); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc, 78762306a36Sopenharmony_ci struct nand_chip *nand, int raw) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 79062306a36Sopenharmony_ci __le64 *info; 79162306a36Sopenharmony_ci u32 neccpages; 79262306a36Sopenharmony_ci int ret; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci neccpages = raw ? 1 : nand->ecc.steps; 79562306a36Sopenharmony_ci info = &meson_chip->info_buf[neccpages - 1]; 79662306a36Sopenharmony_ci do { 79762306a36Sopenharmony_ci usleep_range(10, 15); 79862306a36Sopenharmony_ci /* info is updated by nfc dma engine*/ 79962306a36Sopenharmony_ci smp_rmb(); 80062306a36Sopenharmony_ci dma_sync_single_for_cpu(nfc->dev, nfc->iaddr, nfc->info_bytes, 80162306a36Sopenharmony_ci DMA_FROM_DEVICE); 80262306a36Sopenharmony_ci ret = *info & ECC_COMPLETE; 80362306a36Sopenharmony_ci } while (!ret); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic int meson_nfc_read_page_sub(struct nand_chip *nand, 80762306a36Sopenharmony_ci int page, int raw) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 81062306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 81162306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 81262306a36Sopenharmony_ci int data_len, info_len; 81362306a36Sopenharmony_ci int ret; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci meson_nfc_select_chip(nand, nand->cur_cs); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci data_len = mtd->writesize + mtd->oobsize; 81862306a36Sopenharmony_ci info_len = nand->ecc.steps * PER_INFO_BYTE; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD); 82162306a36Sopenharmony_ci if (ret) 82262306a36Sopenharmony_ci return ret; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf, 82562306a36Sopenharmony_ci data_len, meson_chip->info_buf, 82662306a36Sopenharmony_ci info_len, DMA_FROM_DEVICE); 82762306a36Sopenharmony_ci if (ret) 82862306a36Sopenharmony_ci return ret; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (nand->options & NAND_NEED_SCRAMBLING) { 83162306a36Sopenharmony_ci meson_nfc_cmd_seed(nfc, page); 83262306a36Sopenharmony_ci meson_nfc_cmd_access(nand, raw, DIRREAD, 83362306a36Sopenharmony_ci NFC_CMD_SCRAMBLER_ENABLE); 83462306a36Sopenharmony_ci } else { 83562306a36Sopenharmony_ci meson_nfc_cmd_access(nand, raw, DIRREAD, 83662306a36Sopenharmony_ci NFC_CMD_SCRAMBLER_DISABLE); 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci ret = meson_nfc_wait_dma_finish(nfc); 84062306a36Sopenharmony_ci meson_nfc_check_ecc_pages_valid(nfc, nand, raw); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return ret; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf, 84862306a36Sopenharmony_ci int oob_required, int page) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci u8 *oob_buf = nand->oob_poi; 85162306a36Sopenharmony_ci int ret; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci ret = meson_nfc_read_page_sub(nand, page, 1); 85462306a36Sopenharmony_ci if (ret) 85562306a36Sopenharmony_ci return ret; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci meson_nfc_get_data_oob(nand, buf, oob_buf); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf, 86362306a36Sopenharmony_ci int oob_required, int page) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 86662306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 86762306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 86862306a36Sopenharmony_ci u64 correct_bitmap = 0; 86962306a36Sopenharmony_ci u32 bitflips = 0; 87062306a36Sopenharmony_ci u8 *oob_buf = nand->oob_poi; 87162306a36Sopenharmony_ci int ret, i; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci ret = meson_nfc_read_page_sub(nand, page, 0); 87462306a36Sopenharmony_ci if (ret) 87562306a36Sopenharmony_ci return ret; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci meson_nfc_get_user_byte(nand, oob_buf); 87862306a36Sopenharmony_ci ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap); 87962306a36Sopenharmony_ci if (ret == ECC_CHECK_RETURN_FF) { 88062306a36Sopenharmony_ci if (buf) 88162306a36Sopenharmony_ci memset(buf, 0xff, mtd->writesize); 88262306a36Sopenharmony_ci memset(oob_buf, 0xff, mtd->oobsize); 88362306a36Sopenharmony_ci } else if (ret < 0) { 88462306a36Sopenharmony_ci if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) { 88562306a36Sopenharmony_ci mtd->ecc_stats.failed++; 88662306a36Sopenharmony_ci return bitflips; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci ret = meson_nfc_read_page_raw(nand, buf, 0, page); 88962306a36Sopenharmony_ci if (ret) 89062306a36Sopenharmony_ci return ret; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci for (i = 0; i < nand->ecc.steps ; i++) { 89362306a36Sopenharmony_ci u8 *data = buf + i * ecc->size; 89462306a36Sopenharmony_ci u8 *oob = nand->oob_poi + i * (ecc->bytes + 2); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (correct_bitmap & BIT_ULL(i)) 89762306a36Sopenharmony_ci continue; 89862306a36Sopenharmony_ci ret = nand_check_erased_ecc_chunk(data, ecc->size, 89962306a36Sopenharmony_ci oob, ecc->bytes + 2, 90062306a36Sopenharmony_ci NULL, 0, 90162306a36Sopenharmony_ci ecc->strength); 90262306a36Sopenharmony_ci if (ret < 0) { 90362306a36Sopenharmony_ci mtd->ecc_stats.failed++; 90462306a36Sopenharmony_ci } else { 90562306a36Sopenharmony_ci mtd->ecc_stats.corrected += ret; 90662306a36Sopenharmony_ci bitflips = max_t(u32, bitflips, ret); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci } else if (buf && buf != meson_chip->data_buf) { 91062306a36Sopenharmony_ci memcpy(buf, meson_chip->data_buf, mtd->writesize); 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci return bitflips; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int meson_nfc_read_oob_raw(struct nand_chip *nand, int page) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci return meson_nfc_read_page_raw(nand, NULL, 1, page); 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic int meson_nfc_read_oob(struct nand_chip *nand, int page) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci return meson_nfc_read_page_hwecc(nand, NULL, 1, page); 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic bool meson_nfc_is_buffer_dma_safe(const void *buffer) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci if ((uintptr_t)buffer % DMA_ADDR_ALIGN) 92962306a36Sopenharmony_ci return false; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (virt_addr_valid(buffer) && (!object_is_on_stack(buffer))) 93262306a36Sopenharmony_ci return true; 93362306a36Sopenharmony_ci return false; 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic void * 93762306a36Sopenharmony_cimeson_nand_op_get_dma_safe_input_buf(const struct nand_op_instr *instr) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR)) 94062306a36Sopenharmony_ci return NULL; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (meson_nfc_is_buffer_dma_safe(instr->ctx.data.buf.in)) 94362306a36Sopenharmony_ci return instr->ctx.data.buf.in; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return kzalloc(instr->ctx.data.len, GFP_KERNEL); 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic void 94962306a36Sopenharmony_cimeson_nand_op_put_dma_safe_input_buf(const struct nand_op_instr *instr, 95062306a36Sopenharmony_ci void *buf) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR) || 95362306a36Sopenharmony_ci WARN_ON(!buf)) 95462306a36Sopenharmony_ci return; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (buf == instr->ctx.data.buf.in) 95762306a36Sopenharmony_ci return; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci memcpy(instr->ctx.data.buf.in, buf, instr->ctx.data.len); 96062306a36Sopenharmony_ci kfree(buf); 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic void * 96462306a36Sopenharmony_cimeson_nand_op_get_dma_safe_output_buf(const struct nand_op_instr *instr) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR)) 96762306a36Sopenharmony_ci return NULL; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (meson_nfc_is_buffer_dma_safe(instr->ctx.data.buf.out)) 97062306a36Sopenharmony_ci return (void *)instr->ctx.data.buf.out; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return kmemdup(instr->ctx.data.buf.out, 97362306a36Sopenharmony_ci instr->ctx.data.len, GFP_KERNEL); 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic void 97762306a36Sopenharmony_cimeson_nand_op_put_dma_safe_output_buf(const struct nand_op_instr *instr, 97862306a36Sopenharmony_ci const void *buf) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR) || 98162306a36Sopenharmony_ci WARN_ON(!buf)) 98262306a36Sopenharmony_ci return; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (buf != instr->ctx.data.buf.out) 98562306a36Sopenharmony_ci kfree(buf); 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic int meson_nfc_check_op(struct nand_chip *chip, 98962306a36Sopenharmony_ci const struct nand_operation *op) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci int op_id; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci for (op_id = 0; op_id < op->ninstrs; op_id++) { 99462306a36Sopenharmony_ci const struct nand_op_instr *instr; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci instr = &op->instrs[op_id]; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci switch (instr->type) { 99962306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 100062306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 100162306a36Sopenharmony_ci if (instr->ctx.data.len > NFC_CMD_RAW_LEN) 100262306a36Sopenharmony_ci return -ENOTSUPP; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci break; 100562306a36Sopenharmony_ci default: 100662306a36Sopenharmony_ci break; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci return 0; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic int meson_nfc_exec_op(struct nand_chip *nand, 101462306a36Sopenharmony_ci const struct nand_operation *op, bool check_only) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 101762306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 101862306a36Sopenharmony_ci const struct nand_op_instr *instr = NULL; 101962306a36Sopenharmony_ci void *buf; 102062306a36Sopenharmony_ci u32 op_id, delay_idle, cmd; 102162306a36Sopenharmony_ci int err; 102262306a36Sopenharmony_ci int i; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci err = meson_nfc_check_op(nand, op); 102562306a36Sopenharmony_ci if (err) 102662306a36Sopenharmony_ci return err; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (check_only) 102962306a36Sopenharmony_ci return 0; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci meson_nfc_select_chip(nand, op->cs); 103262306a36Sopenharmony_ci for (op_id = 0; op_id < op->ninstrs; op_id++) { 103362306a36Sopenharmony_ci instr = &op->instrs[op_id]; 103462306a36Sopenharmony_ci delay_idle = DIV_ROUND_UP(PSEC_TO_NSEC(instr->delay_ns), 103562306a36Sopenharmony_ci meson_chip->level1_divider * 103662306a36Sopenharmony_ci NFC_CLK_CYCLE); 103762306a36Sopenharmony_ci switch (instr->type) { 103862306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 103962306a36Sopenharmony_ci cmd = nfc->param.chip_select | NFC_CMD_CLE; 104062306a36Sopenharmony_ci cmd |= instr->ctx.cmd.opcode & 0xff; 104162306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 104262306a36Sopenharmony_ci meson_nfc_cmd_idle(nfc, delay_idle); 104362306a36Sopenharmony_ci break; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 104662306a36Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) { 104762306a36Sopenharmony_ci cmd = nfc->param.chip_select | NFC_CMD_ALE; 104862306a36Sopenharmony_ci cmd |= instr->ctx.addr.addrs[i] & 0xff; 104962306a36Sopenharmony_ci writel(cmd, nfc->reg_base + NFC_REG_CMD); 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci meson_nfc_cmd_idle(nfc, delay_idle); 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 105562306a36Sopenharmony_ci buf = meson_nand_op_get_dma_safe_input_buf(instr); 105662306a36Sopenharmony_ci if (!buf) 105762306a36Sopenharmony_ci return -ENOMEM; 105862306a36Sopenharmony_ci meson_nfc_read_buf(nand, buf, instr->ctx.data.len); 105962306a36Sopenharmony_ci meson_nand_op_put_dma_safe_input_buf(instr, buf); 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 106362306a36Sopenharmony_ci buf = meson_nand_op_get_dma_safe_output_buf(instr); 106462306a36Sopenharmony_ci if (!buf) 106562306a36Sopenharmony_ci return -ENOMEM; 106662306a36Sopenharmony_ci meson_nfc_write_buf(nand, buf, instr->ctx.data.len); 106762306a36Sopenharmony_ci meson_nand_op_put_dma_safe_output_buf(instr, buf); 106862306a36Sopenharmony_ci break; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 107162306a36Sopenharmony_ci meson_nfc_queue_rb(nand, instr->ctx.waitrdy.timeout_ms, 107262306a36Sopenharmony_ci true); 107362306a36Sopenharmony_ci if (instr->delay_ns) 107462306a36Sopenharmony_ci meson_nfc_cmd_idle(nfc, delay_idle); 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci meson_nfc_wait_cmd_finish(nfc, 1000); 107962306a36Sopenharmony_ci return 0; 108062306a36Sopenharmony_ci} 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_cistatic int meson_ooblayout_ecc(struct mtd_info *mtd, int section, 108362306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 108462306a36Sopenharmony_ci{ 108562306a36Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (section >= nand->ecc.steps) 108862306a36Sopenharmony_ci return -ERANGE; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci oobregion->offset = 2 + (section * (2 + nand->ecc.bytes)); 109162306a36Sopenharmony_ci oobregion->length = nand->ecc.bytes; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci return 0; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic int meson_ooblayout_free(struct mtd_info *mtd, int section, 109762306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (section >= nand->ecc.steps) 110262306a36Sopenharmony_ci return -ERANGE; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci oobregion->offset = section * (2 + nand->ecc.bytes); 110562306a36Sopenharmony_ci oobregion->length = 2; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci return 0; 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops meson_ooblayout_ops = { 111162306a36Sopenharmony_ci .ecc = meson_ooblayout_ecc, 111262306a36Sopenharmony_ci .free = meson_ooblayout_free, 111362306a36Sopenharmony_ci}; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic int meson_nfc_clk_init(struct meson_nfc *nfc) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct clk_parent_data nfc_divider_parent_data[1] = {0}; 111862306a36Sopenharmony_ci struct clk_init_data init = {0}; 111962306a36Sopenharmony_ci int ret; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* request core clock */ 112262306a36Sopenharmony_ci nfc->core_clk = devm_clk_get(nfc->dev, "core"); 112362306a36Sopenharmony_ci if (IS_ERR(nfc->core_clk)) { 112462306a36Sopenharmony_ci dev_err(nfc->dev, "failed to get core clock\n"); 112562306a36Sopenharmony_ci return PTR_ERR(nfc->core_clk); 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci nfc->device_clk = devm_clk_get(nfc->dev, "device"); 112962306a36Sopenharmony_ci if (IS_ERR(nfc->device_clk)) { 113062306a36Sopenharmony_ci dev_err(nfc->dev, "failed to get device clock\n"); 113162306a36Sopenharmony_ci return PTR_ERR(nfc->device_clk); 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci init.name = devm_kasprintf(nfc->dev, 113562306a36Sopenharmony_ci GFP_KERNEL, "%s#div", 113662306a36Sopenharmony_ci dev_name(nfc->dev)); 113762306a36Sopenharmony_ci if (!init.name) 113862306a36Sopenharmony_ci return -ENOMEM; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci init.ops = &clk_divider_ops; 114162306a36Sopenharmony_ci nfc_divider_parent_data[0].fw_name = "device"; 114262306a36Sopenharmony_ci init.parent_data = nfc_divider_parent_data; 114362306a36Sopenharmony_ci init.num_parents = 1; 114462306a36Sopenharmony_ci nfc->nand_divider.reg = nfc->reg_clk; 114562306a36Sopenharmony_ci nfc->nand_divider.shift = CLK_DIV_SHIFT; 114662306a36Sopenharmony_ci nfc->nand_divider.width = CLK_DIV_WIDTH; 114762306a36Sopenharmony_ci nfc->nand_divider.hw.init = &init; 114862306a36Sopenharmony_ci nfc->nand_divider.flags = CLK_DIVIDER_ONE_BASED | 114962306a36Sopenharmony_ci CLK_DIVIDER_ROUND_CLOSEST | 115062306a36Sopenharmony_ci CLK_DIVIDER_ALLOW_ZERO; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci nfc->nand_clk = devm_clk_register(nfc->dev, &nfc->nand_divider.hw); 115362306a36Sopenharmony_ci if (IS_ERR(nfc->nand_clk)) 115462306a36Sopenharmony_ci return PTR_ERR(nfc->nand_clk); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ 115762306a36Sopenharmony_ci writel(CLK_SELECT_NAND | readl(nfc->reg_clk), 115862306a36Sopenharmony_ci nfc->reg_clk); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci ret = clk_prepare_enable(nfc->core_clk); 116162306a36Sopenharmony_ci if (ret) { 116262306a36Sopenharmony_ci dev_err(nfc->dev, "failed to enable core clock\n"); 116362306a36Sopenharmony_ci return ret; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci ret = clk_prepare_enable(nfc->device_clk); 116762306a36Sopenharmony_ci if (ret) { 116862306a36Sopenharmony_ci dev_err(nfc->dev, "failed to enable device clock\n"); 116962306a36Sopenharmony_ci goto err_device_clk; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci ret = clk_prepare_enable(nfc->nand_clk); 117362306a36Sopenharmony_ci if (ret) { 117462306a36Sopenharmony_ci dev_err(nfc->dev, "pre enable NFC divider fail\n"); 117562306a36Sopenharmony_ci goto err_nand_clk; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci ret = clk_set_rate(nfc->nand_clk, 24000000); 117962306a36Sopenharmony_ci if (ret) 118062306a36Sopenharmony_ci goto err_disable_clk; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cierr_disable_clk: 118562306a36Sopenharmony_ci clk_disable_unprepare(nfc->nand_clk); 118662306a36Sopenharmony_cierr_nand_clk: 118762306a36Sopenharmony_ci clk_disable_unprepare(nfc->device_clk); 118862306a36Sopenharmony_cierr_device_clk: 118962306a36Sopenharmony_ci clk_disable_unprepare(nfc->core_clk); 119062306a36Sopenharmony_ci return ret; 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic void meson_nfc_disable_clk(struct meson_nfc *nfc) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci clk_disable_unprepare(nfc->nand_clk); 119662306a36Sopenharmony_ci clk_disable_unprepare(nfc->device_clk); 119762306a36Sopenharmony_ci clk_disable_unprepare(nfc->core_clk); 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic void meson_nfc_free_buffer(struct nand_chip *nand) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci kfree(meson_chip->info_buf); 120562306a36Sopenharmony_ci kfree(meson_chip->data_buf); 120662306a36Sopenharmony_ci} 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cistatic int meson_chip_buffer_init(struct nand_chip *nand) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 121162306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 121262306a36Sopenharmony_ci u32 page_bytes, info_bytes, nsectors; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci nsectors = mtd->writesize / nand->ecc.size; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci page_bytes = mtd->writesize + mtd->oobsize; 121762306a36Sopenharmony_ci info_bytes = nsectors * PER_INFO_BYTE; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci meson_chip->data_buf = kmalloc(page_bytes, GFP_KERNEL); 122062306a36Sopenharmony_ci if (!meson_chip->data_buf) 122162306a36Sopenharmony_ci return -ENOMEM; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci meson_chip->info_buf = kmalloc(info_bytes, GFP_KERNEL); 122462306a36Sopenharmony_ci if (!meson_chip->info_buf) { 122562306a36Sopenharmony_ci kfree(meson_chip->data_buf); 122662306a36Sopenharmony_ci return -ENOMEM; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci return 0; 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cistatic 123362306a36Sopenharmony_ciint meson_nfc_setup_interface(struct nand_chip *nand, int csline, 123462306a36Sopenharmony_ci const struct nand_interface_config *conf) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 123762306a36Sopenharmony_ci const struct nand_sdr_timings *timings; 123862306a36Sopenharmony_ci u32 div, bt_min, bt_max, tbers_clocks; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci timings = nand_get_sdr_timings(conf); 124162306a36Sopenharmony_ci if (IS_ERR(timings)) 124262306a36Sopenharmony_ci return -ENOTSUPP; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 124562306a36Sopenharmony_ci return 0; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci div = DIV_ROUND_UP((timings->tRC_min / 1000), NFC_CLK_CYCLE); 124862306a36Sopenharmony_ci bt_min = (timings->tREA_max + NFC_DEFAULT_DELAY) / div; 124962306a36Sopenharmony_ci bt_max = (NFC_DEFAULT_DELAY + timings->tRHOH_min + 125062306a36Sopenharmony_ci timings->tRC_min / 2) / div; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci meson_chip->twb = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tWB_max), 125362306a36Sopenharmony_ci div * NFC_CLK_CYCLE); 125462306a36Sopenharmony_ci meson_chip->tadl = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tADL_min), 125562306a36Sopenharmony_ci div * NFC_CLK_CYCLE); 125662306a36Sopenharmony_ci tbers_clocks = DIV_ROUND_UP_ULL(PSEC_TO_NSEC(timings->tBERS_max), 125762306a36Sopenharmony_ci div * NFC_CLK_CYCLE); 125862306a36Sopenharmony_ci meson_chip->tbers_max = ilog2(tbers_clocks); 125962306a36Sopenharmony_ci if (!is_power_of_2(tbers_clocks)) 126062306a36Sopenharmony_ci meson_chip->tbers_max++; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci bt_min = DIV_ROUND_UP(bt_min, 1000); 126362306a36Sopenharmony_ci bt_max = DIV_ROUND_UP(bt_max, 1000); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (bt_max < bt_min) 126662306a36Sopenharmony_ci return -EINVAL; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci meson_chip->level1_divider = div; 126962306a36Sopenharmony_ci meson_chip->clk_rate = 1000000000 / meson_chip->level1_divider; 127062306a36Sopenharmony_ci meson_chip->bus_timing = (bt_min + bt_max) / 2 + 1; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci return 0; 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic int meson_nand_bch_mode(struct nand_chip *nand) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 127862306a36Sopenharmony_ci int i; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (nand->ecc.strength > 60 || nand->ecc.strength < 8) 128162306a36Sopenharmony_ci return -EINVAL; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(meson_ecc); i++) { 128462306a36Sopenharmony_ci if (meson_ecc[i].strength == nand->ecc.strength && 128562306a36Sopenharmony_ci meson_ecc[i].size == nand->ecc.size) { 128662306a36Sopenharmony_ci meson_chip->bch_mode = meson_ecc[i].bch; 128762306a36Sopenharmony_ci return 0; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci return -EINVAL; 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic void meson_nand_detach_chip(struct nand_chip *nand) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci meson_nfc_free_buffer(nand); 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic int meson_nand_attach_chip(struct nand_chip *nand) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct meson_nfc *nfc = nand_get_controller_data(nand); 130262306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); 130362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 130462306a36Sopenharmony_ci int raw_writesize; 130562306a36Sopenharmony_ci int ret; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (!mtd->name) { 130862306a36Sopenharmony_ci mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL, 130962306a36Sopenharmony_ci "%s:nand%d", 131062306a36Sopenharmony_ci dev_name(nfc->dev), 131162306a36Sopenharmony_ci meson_chip->sels[0]); 131262306a36Sopenharmony_ci if (!mtd->name) 131362306a36Sopenharmony_ci return -ENOMEM; 131462306a36Sopenharmony_ci } 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci raw_writesize = mtd->writesize + mtd->oobsize; 131762306a36Sopenharmony_ci if (raw_writesize > NFC_CMD_RAW_LEN) { 131862306a36Sopenharmony_ci dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n", 131962306a36Sopenharmony_ci raw_writesize, NFC_CMD_RAW_LEN); 132062306a36Sopenharmony_ci return -EINVAL; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (nand->bbt_options & NAND_BBT_USE_FLASH) 132462306a36Sopenharmony_ci nand->bbt_options |= NAND_BBT_NO_OOB; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci nand->options |= NAND_NO_SUBPAGE_WRITE; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci ret = nand_ecc_choose_conf(nand, nfc->data->ecc_caps, 132962306a36Sopenharmony_ci mtd->oobsize - 2); 133062306a36Sopenharmony_ci if (ret) { 133162306a36Sopenharmony_ci dev_err(nfc->dev, "failed to ECC init\n"); 133262306a36Sopenharmony_ci return -EINVAL; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &meson_ooblayout_ops); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci ret = meson_nand_bch_mode(nand); 133862306a36Sopenharmony_ci if (ret) 133962306a36Sopenharmony_ci return -EINVAL; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 134262306a36Sopenharmony_ci nand->ecc.write_page_raw = meson_nfc_write_page_raw; 134362306a36Sopenharmony_ci nand->ecc.write_page = meson_nfc_write_page_hwecc; 134462306a36Sopenharmony_ci nand->ecc.write_oob_raw = nand_write_oob_std; 134562306a36Sopenharmony_ci nand->ecc.write_oob = nand_write_oob_std; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci nand->ecc.read_page_raw = meson_nfc_read_page_raw; 134862306a36Sopenharmony_ci nand->ecc.read_page = meson_nfc_read_page_hwecc; 134962306a36Sopenharmony_ci nand->ecc.read_oob_raw = meson_nfc_read_oob_raw; 135062306a36Sopenharmony_ci nand->ecc.read_oob = meson_nfc_read_oob; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (nand->options & NAND_BUSWIDTH_16) { 135362306a36Sopenharmony_ci dev_err(nfc->dev, "16bits bus width not supported"); 135462306a36Sopenharmony_ci return -EINVAL; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci ret = meson_chip_buffer_init(nand); 135762306a36Sopenharmony_ci if (ret) 135862306a36Sopenharmony_ci return -ENOMEM; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci return ret; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic const struct nand_controller_ops meson_nand_controller_ops = { 136462306a36Sopenharmony_ci .attach_chip = meson_nand_attach_chip, 136562306a36Sopenharmony_ci .detach_chip = meson_nand_detach_chip, 136662306a36Sopenharmony_ci .setup_interface = meson_nfc_setup_interface, 136762306a36Sopenharmony_ci .exec_op = meson_nfc_exec_op, 136862306a36Sopenharmony_ci}; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic int 137162306a36Sopenharmony_cimeson_nfc_nand_chip_init(struct device *dev, 137262306a36Sopenharmony_ci struct meson_nfc *nfc, struct device_node *np) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip; 137562306a36Sopenharmony_ci struct nand_chip *nand; 137662306a36Sopenharmony_ci struct mtd_info *mtd; 137762306a36Sopenharmony_ci int ret, i; 137862306a36Sopenharmony_ci u32 tmp, nsels; 137962306a36Sopenharmony_ci u32 nand_rb_val = 0; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32)); 138262306a36Sopenharmony_ci if (!nsels || nsels > MAX_CE_NUM) { 138362306a36Sopenharmony_ci dev_err(dev, "invalid register property size\n"); 138462306a36Sopenharmony_ci return -EINVAL; 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci meson_chip = devm_kzalloc(dev, struct_size(meson_chip, sels, nsels), 138862306a36Sopenharmony_ci GFP_KERNEL); 138962306a36Sopenharmony_ci if (!meson_chip) 139062306a36Sopenharmony_ci return -ENOMEM; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci meson_chip->nsels = nsels; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci for (i = 0; i < nsels; i++) { 139562306a36Sopenharmony_ci ret = of_property_read_u32_index(np, "reg", i, &tmp); 139662306a36Sopenharmony_ci if (ret) { 139762306a36Sopenharmony_ci dev_err(dev, "could not retrieve register property: %d\n", 139862306a36Sopenharmony_ci ret); 139962306a36Sopenharmony_ci return ret; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (test_and_set_bit(tmp, &nfc->assigned_cs)) { 140362306a36Sopenharmony_ci dev_err(dev, "CS %d already assigned\n", tmp); 140462306a36Sopenharmony_ci return -EINVAL; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci } 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci nand = &meson_chip->nand; 140962306a36Sopenharmony_ci nand->controller = &nfc->controller; 141062306a36Sopenharmony_ci nand->controller->ops = &meson_nand_controller_ops; 141162306a36Sopenharmony_ci nand_set_flash_node(nand, np); 141262306a36Sopenharmony_ci nand_set_controller_data(nand, nfc); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci nand->options |= NAND_USES_DMA; 141562306a36Sopenharmony_ci mtd = nand_to_mtd(nand); 141662306a36Sopenharmony_ci mtd->owner = THIS_MODULE; 141762306a36Sopenharmony_ci mtd->dev.parent = dev; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci ret = of_property_read_u32(np, "nand-rb", &nand_rb_val); 142062306a36Sopenharmony_ci if (ret == -EINVAL) 142162306a36Sopenharmony_ci nfc->no_rb_pin = true; 142262306a36Sopenharmony_ci else if (ret) 142362306a36Sopenharmony_ci return ret; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (nand_rb_val) 142662306a36Sopenharmony_ci return -EINVAL; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci ret = nand_scan(nand, nsels); 142962306a36Sopenharmony_ci if (ret) 143062306a36Sopenharmony_ci return ret; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 143362306a36Sopenharmony_ci if (ret) { 143462306a36Sopenharmony_ci dev_err(dev, "failed to register MTD device: %d\n", ret); 143562306a36Sopenharmony_ci nand_cleanup(nand); 143662306a36Sopenharmony_ci return ret; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci list_add_tail(&meson_chip->node, &nfc->chips); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci return 0; 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cistatic void meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci struct meson_nfc_nand_chip *meson_chip; 144762306a36Sopenharmony_ci struct mtd_info *mtd; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci while (!list_empty(&nfc->chips)) { 145062306a36Sopenharmony_ci meson_chip = list_first_entry(&nfc->chips, 145162306a36Sopenharmony_ci struct meson_nfc_nand_chip, node); 145262306a36Sopenharmony_ci mtd = nand_to_mtd(&meson_chip->nand); 145362306a36Sopenharmony_ci WARN_ON(mtd_device_unregister(mtd)); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci nand_cleanup(&meson_chip->nand); 145662306a36Sopenharmony_ci list_del(&meson_chip->node); 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci} 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_cistatic int meson_nfc_nand_chips_init(struct device *dev, 146162306a36Sopenharmony_ci struct meson_nfc *nfc) 146262306a36Sopenharmony_ci{ 146362306a36Sopenharmony_ci struct device_node *np = dev->of_node; 146462306a36Sopenharmony_ci struct device_node *nand_np; 146562306a36Sopenharmony_ci int ret; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci for_each_child_of_node(np, nand_np) { 146862306a36Sopenharmony_ci ret = meson_nfc_nand_chip_init(dev, nfc, nand_np); 146962306a36Sopenharmony_ci if (ret) { 147062306a36Sopenharmony_ci meson_nfc_nand_chip_cleanup(nfc); 147162306a36Sopenharmony_ci of_node_put(nand_np); 147262306a36Sopenharmony_ci return ret; 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci return 0; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistatic irqreturn_t meson_nfc_irq(int irq, void *id) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci struct meson_nfc *nfc = id; 148262306a36Sopenharmony_ci u32 cfg; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci cfg = readl(nfc->reg_base + NFC_REG_CFG); 148562306a36Sopenharmony_ci if (!(cfg & NFC_RB_IRQ_EN)) 148662306a36Sopenharmony_ci return IRQ_NONE; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci cfg &= ~(NFC_RB_IRQ_EN); 148962306a36Sopenharmony_ci writel(cfg, nfc->reg_base + NFC_REG_CFG); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci complete(&nfc->completion); 149262306a36Sopenharmony_ci return IRQ_HANDLED; 149362306a36Sopenharmony_ci} 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_cistatic const struct meson_nfc_data meson_gxl_data = { 149662306a36Sopenharmony_ci .ecc_caps = &meson_gxl_ecc_caps, 149762306a36Sopenharmony_ci}; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_cistatic const struct meson_nfc_data meson_axg_data = { 150062306a36Sopenharmony_ci .ecc_caps = &meson_axg_ecc_caps, 150162306a36Sopenharmony_ci}; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic const struct of_device_id meson_nfc_id_table[] = { 150462306a36Sopenharmony_ci { 150562306a36Sopenharmony_ci .compatible = "amlogic,meson-gxl-nfc", 150662306a36Sopenharmony_ci .data = &meson_gxl_data, 150762306a36Sopenharmony_ci }, { 150862306a36Sopenharmony_ci .compatible = "amlogic,meson-axg-nfc", 150962306a36Sopenharmony_ci .data = &meson_axg_data, 151062306a36Sopenharmony_ci }, 151162306a36Sopenharmony_ci {} 151262306a36Sopenharmony_ci}; 151362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_nfc_id_table); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic int meson_nfc_probe(struct platform_device *pdev) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 151862306a36Sopenharmony_ci struct meson_nfc *nfc; 151962306a36Sopenharmony_ci int ret, irq; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); 152262306a36Sopenharmony_ci if (!nfc) 152362306a36Sopenharmony_ci return -ENOMEM; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci nfc->data = of_device_get_match_data(&pdev->dev); 152662306a36Sopenharmony_ci if (!nfc->data) 152762306a36Sopenharmony_ci return -ENODEV; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci nand_controller_init(&nfc->controller); 153062306a36Sopenharmony_ci INIT_LIST_HEAD(&nfc->chips); 153162306a36Sopenharmony_ci init_completion(&nfc->completion); 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci nfc->dev = dev; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci nfc->reg_base = devm_platform_ioremap_resource_byname(pdev, "nfc"); 153662306a36Sopenharmony_ci if (IS_ERR(nfc->reg_base)) 153762306a36Sopenharmony_ci return PTR_ERR(nfc->reg_base); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci nfc->reg_clk = devm_platform_ioremap_resource_byname(pdev, "emmc"); 154062306a36Sopenharmony_ci if (IS_ERR(nfc->reg_clk)) 154162306a36Sopenharmony_ci return PTR_ERR(nfc->reg_clk); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 154462306a36Sopenharmony_ci if (irq < 0) 154562306a36Sopenharmony_ci return -EINVAL; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci ret = meson_nfc_clk_init(nfc); 154862306a36Sopenharmony_ci if (ret) { 154962306a36Sopenharmony_ci dev_err(dev, "failed to initialize NAND clock\n"); 155062306a36Sopenharmony_ci return ret; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci writel(0, nfc->reg_base + NFC_REG_CFG); 155462306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc); 155562306a36Sopenharmony_ci if (ret) { 155662306a36Sopenharmony_ci dev_err(dev, "failed to request NFC IRQ\n"); 155762306a36Sopenharmony_ci ret = -EINVAL; 155862306a36Sopenharmony_ci goto err_clk; 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 156262306a36Sopenharmony_ci if (ret) { 156362306a36Sopenharmony_ci dev_err(dev, "failed to set DMA mask\n"); 156462306a36Sopenharmony_ci goto err_clk; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci platform_set_drvdata(pdev, nfc); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci ret = meson_nfc_nand_chips_init(dev, nfc); 157062306a36Sopenharmony_ci if (ret) { 157162306a36Sopenharmony_ci dev_err(dev, "failed to init NAND chips\n"); 157262306a36Sopenharmony_ci goto err_clk; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci return 0; 157662306a36Sopenharmony_cierr_clk: 157762306a36Sopenharmony_ci meson_nfc_disable_clk(nfc); 157862306a36Sopenharmony_ci return ret; 157962306a36Sopenharmony_ci} 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_cistatic void meson_nfc_remove(struct platform_device *pdev) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci struct meson_nfc *nfc = platform_get_drvdata(pdev); 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci meson_nfc_nand_chip_cleanup(nfc); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci meson_nfc_disable_clk(nfc); 158862306a36Sopenharmony_ci} 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_cistatic struct platform_driver meson_nfc_driver = { 159162306a36Sopenharmony_ci .probe = meson_nfc_probe, 159262306a36Sopenharmony_ci .remove_new = meson_nfc_remove, 159362306a36Sopenharmony_ci .driver = { 159462306a36Sopenharmony_ci .name = "meson-nand", 159562306a36Sopenharmony_ci .of_match_table = meson_nfc_id_table, 159662306a36Sopenharmony_ci }, 159762306a36Sopenharmony_ci}; 159862306a36Sopenharmony_cimodule_platform_driver(meson_nfc_driver); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL"); 160162306a36Sopenharmony_ciMODULE_AUTHOR("Liang Yang <liang.yang@amlogic.com>"); 160262306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic's Meson NAND Flash Controller driver"); 1603