diff options
Diffstat (limited to 'drivers/mtd/nand')
| -rw-r--r-- | drivers/mtd/nand/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/ecc-realtek.c | 6 | ||||
| -rw-r--r-- | drivers/mtd/nand/onenand/onenand_samsung.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/cadence-nand-controller.c | 276 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/lpc32xx_slc.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/marvell_nand.c | 13 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/nand_base.c | 13 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/renesas-nand-controller.c | 5 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/sunxi_nand.c | 409 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/core.c | 1 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/esmt.c | 24 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/fmsh.c | 74 |
13 files changed, 728 insertions, 102 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 4a17271076bc..1e57c8de8578 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -63,7 +63,7 @@ config MTD_NAND_ECC_MEDIATEK config MTD_NAND_ECC_REALTEK tristate "Realtek RTL93xx hardware ECC engine" - depends on HAS_IOMEM + depends on HAS_IOMEM && HAS_DMA depends on MACH_REALTEK_RTL || COMPILE_TEST select MTD_NAND_ECC help diff --git a/drivers/mtd/nand/ecc-realtek.c b/drivers/mtd/nand/ecc-realtek.c index 7d718934c909..0046da37ea3e 100644 --- a/drivers/mtd/nand/ecc-realtek.c +++ b/drivers/mtd/nand/ecc-realtek.c @@ -380,7 +380,7 @@ static void rtl_ecc_cleanup_ctx(struct nand_device *nand) nand_ecc_cleanup_req_tweaking(&ctx->req_ctx); } -static struct nand_ecc_engine_ops rtl_ecc_engine_ops = { +static const struct nand_ecc_engine_ops rtl_ecc_engine_ops = { .init_ctx = rtl_ecc_init_ctx, .cleanup_ctx = rtl_ecc_cleanup_ctx, .prepare_io_req = rtl_ecc_prepare_io_req, @@ -418,8 +418,8 @@ static int rtl_ecc_probe(struct platform_device *pdev) rtlc->buf = dma_alloc_noncoherent(dev, RTL_ECC_DMA_SIZE, &rtlc->buf_dma, DMA_BIDIRECTIONAL, GFP_KERNEL); - if (IS_ERR(rtlc->buf)) - return PTR_ERR(rtlc->buf); + if (!rtlc->buf) + return -ENOMEM; rtlc->dev = dev; rtlc->engine.dev = dev; diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c index f37a6138e461..6d6aa709a21f 100644 --- a/drivers/mtd/nand/onenand/onenand_samsung.c +++ b/drivers/mtd/nand/onenand/onenand_samsung.c @@ -906,7 +906,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) err = devm_request_irq(&pdev->dev, r->start, s5pc110_onenand_irq, IRQF_SHARED, "onenand", - &onenand); + onenand); if (err) { dev_err(&pdev->dev, "failed to get irq\n"); return err; diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 6667eea95597..5f037753f78c 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -199,6 +199,7 @@ /* Common settings. */ #define COMMON_SET 0x1008 +#define OPR_MODE_NVDDR BIT(0) /* 16 bit device connected to the NAND Flash interface. */ #define COMMON_SET_DEVICE_16BIT BIT(8) @@ -211,12 +212,20 @@ #define SKIP_BYTES_OFFSET_VALUE GENMASK(23, 0) /* Timings configuration. */ +#define TOGGLE_TIMINGS_0 0x1014 +#define TOGGLE_TIMINGS_1 0x1018 + #define ASYNC_TOGGLE_TIMINGS 0x101c #define ASYNC_TOGGLE_TIMINGS_TRH GENMASK(28, 24) #define ASYNC_TOGGLE_TIMINGS_TRP GENMASK(20, 16) #define ASYNC_TOGGLE_TIMINGS_TWH GENMASK(12, 8) #define ASYNC_TOGGLE_TIMINGS_TWP GENMASK(4, 0) +#define SYNC_TIMINGS 0x1020 +#define SYNC_TCKWR GENMASK(21, 16) +#define SYNC_TWRCK GENMASK(13, 8) +#define SYNC_TCAD GENMASK(5, 0) + #define TIMINGS0 0x1024 #define TIMINGS0_TADL GENMASK(31, 24) #define TIMINGS0_TCCS GENMASK(23, 16) @@ -226,6 +235,7 @@ #define TIMINGS1 0x1028 #define TIMINGS1_TRHZ GENMASK(31, 24) #define TIMINGS1_TWB GENMASK(23, 16) +#define TIMINGS1_TCWAW GENMASK(15, 8) #define TIMINGS1_TVDLY GENMASK(7, 0) #define TIMINGS2 0x102c @@ -243,14 +253,23 @@ /* Register controlling DQ related timing. */ #define PHY_DQ_TIMING 0x2000 +#define PHY_DQ_TIMING_OE_END GENMASK(2, 0) +#define PHY_DQ_TIMING_OE_START GENMASK(6, 4) +#define PHY_DQ_TIMING_TSEL_END GENMASK(11, 8) +#define PHY_DQ_TIMING_TSEL_START GENMASK(15, 12) + /* Register controlling DSQ related timing. */ #define PHY_DQS_TIMING 0x2004 #define PHY_DQS_TIMING_DQS_SEL_OE_END GENMASK(3, 0) +#define PHY_DQS_TIMING_DQS_SEL_OE_START GENMASK(7, 4) +#define PHY_DQS_TIMING_DQS_SEL_TSEL_END GENMASK(11, 8) #define PHY_DQS_TIMING_PHONY_DQS_SEL BIT(16) #define PHY_DQS_TIMING_USE_PHONY_DQS BIT(20) /* Register controlling the gate and loopback control related timing. */ #define PHY_GATE_LPBK_CTRL 0x2008 +#define PHY_GATE_LPBK_CTRL_GATE_CFG GENMASK(3, 0) +#define PHY_GATE_LPBK_CTRL_GATE_CFG_CLOSE GENMASK(5, 4) #define PHY_GATE_LPBK_CTRL_RDS GENMASK(24, 19) /* Register holds the control for the master DLL logic. */ @@ -260,6 +279,12 @@ /* Register holds the control for the slave DLL logic. */ #define PHY_DLL_SLAVE_CTRL 0x2010 +/* Register controls the DQS related timing. */ +#define PHY_IE_TIMING 0x2014 +#define PHY_IE_TIMING_DQS_IE_START GENMASK(10, 8) +#define PHY_IE_TIMING_DQ_IE_START GENMASK(18, 16) +#define PHY_IE_TIMING_IE_ALWAYS_ON BIT(20) + /* This register handles the global control settings for the PHY. */ #define PHY_CTRL 0x2080 #define PHY_CTRL_SDR_DQS BIT(14) @@ -375,15 +400,41 @@ #define BCH_MAX_NUM_CORR_CAPS 8 #define BCH_MAX_NUM_SECTOR_SIZES 2 +/* NVDDR mode specific parameters and register values based on cadence specs */ +#define NVDDR_PHY_RD_DELAY 29 +#define NVDDR_PHY_RD_DELAY_MAX 31 +#define NVDDR_GATE_CFG_OPT 14 +#define NVDDR_GATE_CFG_STD 7 +#define NVDDR_GATE_CFG_MAX 15 +#define NVDDR_DATA_SEL_OE_START 1 +#define NVDDR_DATA_SEL_OE_START_MAX 7 +#define NVDDR_DATA_SEL_OE_END 6 +#define NVDDR_DATA_SEL_OE_END_MIN 4 +#define NVDDR_DATA_SEL_OE_END_MAX 15 +#define NVDDR_RS_HIGH_WAIT_CNT 7 +#define NVDDR_RS_IDLE_CNT 7 +#define NVDDR_TCWAW_DELAY 250000 +#define NVDDR_TVDLY_DELAY 500000 +#define NVDDR_TOGGLE_TIMINGS_0 0x00000301 +#define NVDDR_TOGGLE_TIMINGS_1 0x0a060102 +#define NVDDR_ASYNC_TOGGLE_TIMINGS 0 +#define NVDDR_PHY_CTRL 0x00004000 +#define NVDDR_PHY_TSEL 0 +#define NVDDR_PHY_DLL_MASTER_CTRL 0x00140004 +#define NVDDR_PHY_DLL_SLAVE_CTRL 0x00003c3c + struct cadence_nand_timings { u32 async_toggle_timings; + u32 sync_timings; u32 timings0; u32 timings1; u32 timings2; u32 dll_phy_ctrl; u32 phy_ctrl; + u32 phy_dq_timing; u32 phy_dqs_timing; u32 phy_gate_lpbk_ctrl; + u32 phy_ie_timing; }; /* Command DMA descriptor. */ @@ -2345,11 +2396,9 @@ static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min, return (trp_cnt + 1) * clk_period + trhoh_min - trea_max; } -static int -cadence_nand_setup_interface(struct nand_chip *chip, int chipnr, - const struct nand_interface_config *conf) +static int cadence_nand_setup_sdr_interface(struct nand_chip *chip, + const struct nand_sdr_timings *sdr) { - const struct nand_sdr_timings *sdr; struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); struct cadence_nand_timings *t = &cdns_chip->timings; @@ -2370,13 +2419,8 @@ cadence_nand_setup_interface(struct nand_chip *chip, int chipnr, u32 dll_phy_dqs_timing = 0, phony_dqs_timing = 0, rd_del_sel = 0; u32 sampling_point; - sdr = nand_get_sdr_timings(conf); - if (IS_ERR(sdr)) - return PTR_ERR(sdr); - memset(t, 0, sizeof(*t)); /* Sampling point calculation. */ - if (cdns_ctrl->caps2.is_phy_type_dll) phony_dqs_mod = 2; else @@ -2633,10 +2677,221 @@ cadence_nand_setup_interface(struct nand_chip *chip, int chipnr, PHY_DLL_MASTER_CTRL_BYPASS_MODE); dev_dbg(cdns_ctrl->dev, "PHY_DLL_SLAVE_CTRL_REG_SDR\t%x\n", 0); } + return 0; +} + +static int +cadence_nand_setup_nvddr_interface(struct nand_chip *chip, + const struct nand_nvddr_timings *nvddr) +{ + struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + struct cadence_nand_timings *t = &cdns_chip->timings; + u32 board_delay = cdns_ctrl->board_delay; + u32 clk_period = DIV_ROUND_DOWN_ULL(1000000000000ULL, + cdns_ctrl->nf_clk_rate); + u32 ddr_clk_ctrl_period = clk_period * 2; + u32 if_skew = cdns_ctrl->caps1->if_skew; + u32 tceh_cnt, tcs_cnt, tadl_cnt, tccs_cnt; + u32 twrck_cnt, tcad_cnt, tckwr_cnt = 0; + u32 tfeat_cnt, trhz_cnt, tvdly_cnt, tcwaw_cnt; + u32 trhw_cnt, twb_cnt, twhr_cnt; + u32 oe_start, oe_end, oe_end_dqsd; + u32 rd_del_sel = 0; + u32 dqs_driven_by_device, dqs_toogle_by_device, gate_open_delay; + u32 dll_phy_gate_open_delay, gate_close_delay, ie_start; + u32 dll_phy_rd_delay; + u32 reg; + + memset(t, 0, sizeof(*t)); + twrck_cnt = calc_cycl(nvddr->tWRCK_min, ddr_clk_ctrl_period); + tcad_cnt = calc_cycl(nvddr->tCAD_min, ddr_clk_ctrl_period); + + reg = FIELD_PREP(SYNC_TWRCK, twrck_cnt); + reg |= FIELD_PREP(SYNC_TCAD, tcad_cnt); + t->sync_timings = reg; + dev_dbg(cdns_ctrl->dev, "SYNC_TIMINGS_NVDDR\t%08x\n", reg); + + tadl_cnt = calc_cycl((nvddr->tADL_min + if_skew), ddr_clk_ctrl_period); + tccs_cnt = calc_cycl((nvddr->tCCS_min + if_skew), ddr_clk_ctrl_period); + twhr_cnt = calc_cycl((nvddr->tWHR_min + if_skew), ddr_clk_ctrl_period); + trhw_cnt = calc_cycl((nvddr->tRHW_min + if_skew), ddr_clk_ctrl_period); + reg = FIELD_PREP(TIMINGS0_TADL, tadl_cnt); + reg |= FIELD_PREP(TIMINGS0_TCCS, tccs_cnt); + reg |= FIELD_PREP(TIMINGS0_TWHR, twhr_cnt); + reg |= FIELD_PREP(TIMINGS0_TRHW, trhw_cnt); + t->timings0 = reg; + dev_dbg(cdns_ctrl->dev, "TIMINGS0_NVDDR\t%08x\n", reg); + twb_cnt = calc_cycl((nvddr->tWB_max + board_delay), + ddr_clk_ctrl_period); + /* + * Because of the two stage syncflop the value must be increased by 3 + * first value is related with sync, second value is related + * with output if delay. + */ + twb_cnt = twb_cnt + 3 + 5; + tvdly_cnt = calc_cycl(NVDDR_TVDLY_DELAY + if_skew, ddr_clk_ctrl_period); + tcwaw_cnt = calc_cycl(NVDDR_TCWAW_DELAY, ddr_clk_ctrl_period); + trhz_cnt = 1; + reg = FIELD_PREP(TIMINGS1_TWB, twb_cnt); + reg |= FIELD_PREP(TIMINGS1_TVDLY, tvdly_cnt); + reg |= FIELD_PREP(TIMINGS1_TRHZ, trhz_cnt); + reg |= FIELD_PREP(TIMINGS1_TCWAW, tcwaw_cnt); + t->timings1 = reg; + dev_dbg(cdns_ctrl->dev, "TIMINGS1_NVDDR\t%08x\n", reg); + + tfeat_cnt = calc_cycl(nvddr->tFEAT_max, ddr_clk_ctrl_period); + if (tfeat_cnt < twb_cnt) + tfeat_cnt = twb_cnt; + + tceh_cnt = calc_cycl(nvddr->tCEH_min, ddr_clk_ctrl_period); + tcs_cnt = calc_cycl((nvddr->tCS_min + if_skew), ddr_clk_ctrl_period); + reg = FIELD_PREP(TIMINGS2_TFEAT, tfeat_cnt); + reg |= FIELD_PREP(TIMINGS2_CS_HOLD_TIME, tceh_cnt); + reg |= FIELD_PREP(TIMINGS2_CS_SETUP_TIME, tcs_cnt); + t->timings2 = reg; + dev_dbg(cdns_ctrl->dev, "TIMINGS2_NVDDR\t%08x\n", reg); + + reg = FIELD_PREP(DLL_PHY_CTRL_RS_HIGH_WAIT_CNT, NVDDR_RS_HIGH_WAIT_CNT); + reg |= FIELD_PREP(DLL_PHY_CTRL_RS_IDLE_CNT, NVDDR_RS_IDLE_CNT); + t->dll_phy_ctrl = reg; + dev_dbg(cdns_ctrl->dev, "DLL_PHY_CTRL_NVDDR\t%08x\n", reg); + + reg = PHY_CTRL_SDR_DQS; + t->phy_ctrl = reg; + dev_dbg(cdns_ctrl->dev, "PHY_CTRL_REG_NVDDR\t%08x\n", reg); + + dqs_driven_by_device = (nvddr->tDQSD_max + board_delay) / 1000 + + if_skew; + dqs_toogle_by_device = (nvddr->tDQSCK_max + board_delay) / 1000 - + if_skew; + gate_open_delay = dqs_toogle_by_device / (clk_period / 1000); + if (dqs_toogle_by_device > clk_period / 1000) { + if (gate_open_delay > NVDDR_GATE_CFG_OPT) + dll_phy_gate_open_delay = NVDDR_GATE_CFG_MAX; + else + dll_phy_gate_open_delay = gate_open_delay + 1; + gate_close_delay = 0; + } else { + twrck_cnt = calc_cycl(dqs_driven_by_device * 1000, clk_period); + dll_phy_gate_open_delay = 1; + gate_close_delay = 0; + + reg = FIELD_PREP(SYNC_TCKWR, tckwr_cnt); + reg |= FIELD_PREP(SYNC_TWRCK, twrck_cnt); + reg |= FIELD_PREP(SYNC_TCAD, tcad_cnt); + t->sync_timings = reg; + dev_dbg(cdns_ctrl->dev, "SYNC_TIMINGS_NVDDR\t%08x\n", reg); + } + + if (dll_phy_gate_open_delay > NVDDR_GATE_CFG_STD) + ie_start = NVDDR_GATE_CFG_STD; + else + ie_start = dll_phy_gate_open_delay; + + dll_phy_rd_delay = ((nvddr->tDQSCK_max + board_delay) + + (clk_period / 2)) / clk_period; + if (dll_phy_rd_delay <= NVDDR_PHY_RD_DELAY) + rd_del_sel = dll_phy_rd_delay + 2; + else + rd_del_sel = NVDDR_PHY_RD_DELAY_MAX; + + reg = FIELD_PREP(PHY_GATE_LPBK_CTRL_GATE_CFG, dll_phy_gate_open_delay); + reg |= FIELD_PREP(PHY_GATE_LPBK_CTRL_GATE_CFG_CLOSE, gate_close_delay); + reg |= FIELD_PREP(PHY_GATE_LPBK_CTRL_RDS, rd_del_sel); + t->phy_gate_lpbk_ctrl = reg; + dev_dbg(cdns_ctrl->dev, "PHY_GATE_LPBK_CTRL_REG_NVDDR\t%08x\n", reg); + + oe_end_dqsd = ((nvddr->tDQSD_max / 1000) / ((clk_period / 2) / 1000)) + + NVDDR_DATA_SEL_OE_END_MIN; + oe_end = (NVDDR_DATA_SEL_OE_END_MIN + oe_end_dqsd) / 2; + if (oe_end > NVDDR_DATA_SEL_OE_END_MAX) + oe_end = NVDDR_DATA_SEL_OE_END_MAX; + + oe_start = ((nvddr->tDQSHZ_max / 1000) / ((clk_period / 2) / 1000)) + 1; + if (oe_start > NVDDR_DATA_SEL_OE_START_MAX) + oe_start = NVDDR_DATA_SEL_OE_START_MAX; + + reg = FIELD_PREP(PHY_DQ_TIMING_OE_END, NVDDR_DATA_SEL_OE_END); + reg |= FIELD_PREP(PHY_DQ_TIMING_OE_START, NVDDR_DATA_SEL_OE_START); + reg |= FIELD_PREP(PHY_DQ_TIMING_TSEL_END, NVDDR_DATA_SEL_OE_END); + reg |= FIELD_PREP(PHY_DQ_TIMING_TSEL_START, NVDDR_DATA_SEL_OE_START); + t->phy_dq_timing = reg; + dev_dbg(cdns_ctrl->dev, "PHY_DQ_TIMING_REG_NVDDR\t%08x\n", reg); + + reg = FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_END, oe_end); + reg |= FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_START, oe_start); + reg |= FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_TSEL_END, oe_end); + t->phy_dqs_timing = reg; + dev_dbg(cdns_ctrl->dev, "PHY_DQS_TIMING_REG_NVDDR\t%08x\n", reg); + + reg = FIELD_PREP(PHY_IE_TIMING_DQS_IE_START, ie_start); + reg |= FIELD_PREP(PHY_IE_TIMING_DQ_IE_START, ie_start); + reg |= FIELD_PREP(PHY_IE_TIMING_IE_ALWAYS_ON, 0); + t->phy_ie_timing = reg; + dev_dbg(cdns_ctrl->dev, "PHY_IE_TIMING_REG_NVDDR\t%08x\n", reg); + + reg = readl_relaxed(cdns_ctrl->reg + DLL_PHY_CTRL); + reg &= ~(DLL_PHY_CTRL_DLL_RST_N | + DLL_PHY_CTRL_EXTENDED_RD_MODE | + DLL_PHY_CTRL_EXTENDED_WR_MODE); + writel_relaxed(reg, cdns_ctrl->reg + DLL_PHY_CTRL); + writel_relaxed(OPR_MODE_NVDDR, cdns_ctrl->reg + COMMON_SET); + writel_relaxed(NVDDR_TOGGLE_TIMINGS_0, + cdns_ctrl->reg + TOGGLE_TIMINGS_0); + writel_relaxed(NVDDR_TOGGLE_TIMINGS_1, + cdns_ctrl->reg + TOGGLE_TIMINGS_1); + writel_relaxed(NVDDR_ASYNC_TOGGLE_TIMINGS, + cdns_ctrl->reg + ASYNC_TOGGLE_TIMINGS); + writel_relaxed(t->sync_timings, cdns_ctrl->reg + SYNC_TIMINGS); + writel_relaxed(t->timings0, cdns_ctrl->reg + TIMINGS0); + writel_relaxed(t->timings1, cdns_ctrl->reg + TIMINGS1); + writel_relaxed(t->timings2, cdns_ctrl->reg + TIMINGS2); + writel_relaxed(t->dll_phy_ctrl, cdns_ctrl->reg + DLL_PHY_CTRL); + writel_relaxed(t->phy_ctrl, cdns_ctrl->reg + PHY_CTRL); + writel_relaxed(NVDDR_PHY_TSEL, cdns_ctrl->reg + PHY_TSEL); + writel_relaxed(t->phy_dq_timing, cdns_ctrl->reg + PHY_DQ_TIMING); + writel_relaxed(t->phy_dqs_timing, cdns_ctrl->reg + PHY_DQS_TIMING); + writel_relaxed(t->phy_gate_lpbk_ctrl, + cdns_ctrl->reg + PHY_GATE_LPBK_CTRL); + writel_relaxed(NVDDR_PHY_DLL_MASTER_CTRL, + cdns_ctrl->reg + PHY_DLL_MASTER_CTRL); + writel_relaxed(NVDDR_PHY_DLL_SLAVE_CTRL, + cdns_ctrl->reg + PHY_DLL_SLAVE_CTRL); + writel_relaxed(t->phy_ie_timing, cdns_ctrl->reg + PHY_IE_TIMING); + writel_relaxed((reg | DLL_PHY_CTRL_DLL_RST_N), + cdns_ctrl->reg + DLL_PHY_CTRL); return 0; } +static int +cadence_nand_setup_interface(struct nand_chip *chip, int chipnr, + const struct nand_interface_config *conf) +{ + int ret = 0; + + if (chipnr < 0) + return ret; + + if (nand_interface_is_sdr(conf)) { + const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf); + + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + ret = cadence_nand_setup_sdr_interface(chip, sdr); + } else { + const struct nand_nvddr_timings *nvddr = nand_get_nvddr_timings(conf); + + if (IS_ERR(nvddr)) + return PTR_ERR(nvddr); + + ret = cadence_nand_setup_nvddr_interface(chip, nvddr); + } + return ret; +} + static int cadence_nand_attach_chip(struct nand_chip *chip) { struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); @@ -2871,7 +3126,7 @@ cadence_nand_irq_cleanup(int irqnum, struct cdns_nand_ctrl *cdns_ctrl) static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl) { dma_cap_mask_t mask; - struct dma_device *dma_dev = cdns_ctrl->dmac->device; + struct dma_device *dma_dev; int ret; cdns_ctrl->cdma_desc = dma_alloc_coherent(cdns_ctrl->dev, @@ -2915,6 +3170,7 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl) } } + dma_dev = cdns_ctrl->dmac->device; cdns_ctrl->io.iova_dma = dma_map_resource(dma_dev->dev, cdns_ctrl->io.dma, cdns_ctrl->io.size, DMA_BIDIRECTIONAL, 0); diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index a750f5839e34..51f595fbc834 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -191,7 +191,6 @@ static int gpmi_init(struct gpmi_nand_data *this) r->gpmi_regs + HW_GPMI_CTRL1_SET); err_out: - pm_runtime_mark_last_busy(this->dev); pm_runtime_put_autosuspend(this->dev); return ret; } @@ -761,7 +760,6 @@ static int bch_set_geometry(struct gpmi_nand_data *this) ret = 0; err_out: - pm_runtime_mark_last_busy(this->dev); pm_runtime_put_autosuspend(this->dev); return ret; @@ -2667,7 +2665,6 @@ unmap: this->bch = false; out_pm: - pm_runtime_mark_last_busy(this->dev); pm_runtime_put_autosuspend(this->dev); return ret; diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index b54d76547ffb..3ca30e7dce33 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -854,7 +854,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } /* Start with WP disabled, if available */ - host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW); + host->wp_gpio = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW); res = PTR_ERR_OR_ZERO(host->wp_gpio); if (res) { if (res != -EPROBE_DEFER) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 303b3016a070..38b7eb5b992c 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -290,13 +290,16 @@ static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = { MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0), MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,32, 30), MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,64, 30), - MARVELL_LAYOUT( 2048, 512, 16, 4, 4, 512, 0, 30, 0, 32, 30), + MARVELL_LAYOUT( 2048, 512, 12, 3, 2, 704, 0, 30,640, 0, 30), + MARVELL_LAYOUT( 2048, 512, 16, 5, 4, 512, 0, 30, 0, 32, 30), MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0), - MARVELL_LAYOUT( 4096, 512, 8, 4, 4, 1024, 0, 30, 0, 64, 30), - MARVELL_LAYOUT( 4096, 512, 16, 8, 8, 512, 0, 30, 0, 32, 30), + MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30), + MARVELL_LAYOUT( 4096, 512, 12, 6, 5, 704, 0, 30,576, 32, 30), + MARVELL_LAYOUT( 4096, 512, 16, 9, 8, 512, 0, 30, 0, 32, 30), MARVELL_LAYOUT( 8192, 512, 4, 4, 4, 2048, 0, 30, 0, 0, 0), - MARVELL_LAYOUT( 8192, 512, 8, 8, 8, 1024, 0, 30, 0, 160, 30), - MARVELL_LAYOUT( 8192, 512, 16, 16, 16, 512, 0, 30, 0, 32, 30), + MARVELL_LAYOUT( 8192, 512, 8, 9, 8, 1024, 0, 30, 0, 160, 30), + MARVELL_LAYOUT( 8192, 512, 12, 12, 11, 704, 0, 30,448, 64, 30), + MARVELL_LAYOUT( 8192, 512, 16, 17, 16, 512, 0, 30, 0, 32, 30), }; /** diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c7d9501f646b..ad6d66309597 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6338,11 +6338,14 @@ static int nand_scan_tail(struct nand_chip *chip) ecc->steps = mtd->writesize / ecc->size; if (!base->ecc.ctx.nsteps) base->ecc.ctx.nsteps = ecc->steps; - if (ecc->steps * ecc->size != mtd->writesize) { - WARN(1, "Invalid ECC parameters\n"); - ret = -EINVAL; - goto err_nand_manuf_cleanup; - } + + /* + * Validity check: Warn if ECC parameters are not compatible with page size. + * Due to the custom handling of ECC blocks in certain controllers the check + * may result in an expected failure. + */ + if (ecc->steps * ecc->size != mtd->writesize) + pr_warn("ECC parameters may be invalid in reference to underlying NAND chip\n"); if (!ecc->total) { ecc->total = ecc->steps * ecc->bytes; diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c index ac8c1b80d7be..201dd62b9990 100644 --- a/drivers/mtd/nand/raw/renesas-nand-controller.c +++ b/drivers/mtd/nand/raw/renesas-nand-controller.c @@ -1336,7 +1336,10 @@ static int rnandc_probe(struct platform_device *pdev) if (IS_ERR(rnandc->regs)) return PTR_ERR(rnandc->regs); - devm_pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; + ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index f6a8e8ae819d..9dcdc93734cb 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -29,6 +29,12 @@ #include <linux/iopoll.h> #include <linux/reset.h> +/* non compile-time field get/prep */ +#undef field_get +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) +#undef field_prep +#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) + #define NFC_REG_CTL 0x0000 #define NFC_REG_ST 0x0004 #define NFC_REG_INT 0x0008 @@ -45,13 +51,40 @@ #define NFC_REG_A23_IO_DATA 0x0300 #define NFC_REG_ECC_CTL 0x0034 #define NFC_REG_ECC_ST 0x0038 -#define NFC_REG_DEBUG 0x003C -#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) -#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) -#define NFC_REG_SPARE_AREA 0x00A0 -#define NFC_REG_PAT_ID 0x00A4 +#define NFC_REG_H6_PAT_FOUND 0x003C +#define NFC_REG_A10_ECC_ERR_CNT 0x0040 +#define NFC_REG_H6_ECC_ERR_CNT 0x0050 +#define NFC_REG_ECC_ERR_CNT(nfc, x) ((nfc->caps->reg_ecc_err_cnt + (x)) & ~0x3) +#define NFC_REG_H6_RDATA_CTL 0x0044 +#define NFC_REG_H6_RDATA_0 0x0048 +#define NFC_REG_H6_RDATA_1 0x004C +#define NFC_REG_A10_USER_DATA 0x0050 +#define NFC_REG_H6_USER_DATA 0x0080 +#define NFC_REG_USER_DATA(nfc, x) (nfc->caps->reg_user_data + ((x) * 4)) +#define NFC_REG_H6_USER_DATA_LEN 0x0070 +/* A USER_DATA_LEN register can hold the length of 8 USER_DATA registers */ +#define NFC_REG_USER_DATA_LEN_CAPACITY 8 +#define NFC_REG_USER_DATA_LEN(nfc, step) \ + (nfc->caps->reg_user_data_len + \ + ((step) / NFC_REG_USER_DATA_LEN_CAPACITY) * 4) +#define NFC_REG_SPARE_AREA(nfc) (nfc->caps->reg_spare_area) +#define NFC_REG_A10_SPARE_AREA 0x00A0 +#define NFC_REG_PAT_ID(nfc) (nfc->caps->reg_pat_id) +#define NFC_REG_A10_PAT_ID 0x00A4 #define NFC_REG_MDMA_ADDR 0x00C0 #define NFC_REG_MDMA_CNT 0x00C4 +#define NFC_REG_H6_EFNAND_STATUS 0x0110 +#define NFC_REG_H6_SPARE_AREA 0x0114 +#define NFC_REG_H6_PAT_ID 0x0118 +#define NFC_REG_H6_DDR2_SPEC_CTL 0x011C +#define NFC_REG_H6_NDMA_MODE_CTL 0x0120 +#define NFC_REG_H6_MDMA_DLBA_REG 0x0200 +#define NFC_REG_H6_MDMA_STA 0x0204 +#define NFC_REG_H6_MDMA_INT_MAS 0x0208 +#define NFC_REG_H6_MDMA_DESC_ADDR 0x020C +#define NFC_REG_H6_MDMA_BUF_ADDR 0x0210 +#define NFC_REG_H6_MDMA_CNT 0x0214 + #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -63,6 +96,7 @@ #define NFC_BUS_WIDTH_16 (1 << 2) #define NFC_RB_SEL_MSK BIT(3) #define NFC_RB_SEL(x) ((x) << 3) +/* CE_SEL BIT 27 is meant to be used for GPIO chipselect */ #define NFC_CE_SEL_MSK GENMASK(26, 24) #define NFC_CE_SEL(x) ((x) << 24) #define NFC_CE_CTL BIT(6) @@ -81,6 +115,9 @@ #define NFC_STA BIT(4) #define NFC_NATCH_INT_FLAG BIT(5) #define NFC_RB_STATE(x) BIT(x + 8) +#define NFC_RB_STATE_MSK GENMASK(11, 8) +#define NDFC_RDATA_STA_1 BIT(12) +#define NDFC_RDATA_STA_0 BIT(13) /* define bit use in NFC_INT */ #define NFC_B2R_INT_ENABLE BIT(0) @@ -92,6 +129,7 @@ /* define bit use in NFC_TIMING_CTL */ #define NFC_TIMING_CTL_EDO BIT(8) +#define NFC_TIMING_CTL_E_EDO BIT(9) /* define NFC_TIMING_CFG register layout */ #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ @@ -99,9 +137,15 @@ (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ (((tCAD) & 0x7) << 8)) +#define NFC_TIMING_CFG2(tCDQSS, tSC, tCLHZ, tCSS, tWC) \ + ((((tCDQSS) & 0x1) << 11) | (((tSC) & 0x3) << 12) | \ + (((tCLHZ) & 0x3) << 14) | (((tCSS) & 0x3) << 16) | \ + (((tWC) & 0x3) << 18)) + /* define bit use in NFC_CMD */ #define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) -#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) +#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) /* 15-10 reserved on H6 */ +#define NFC_CMD_ADR_NUM_MSK GENMASK(9, 8) #define NFC_CMD(x) (x) #define NFC_ADR_NUM_MSK GENMASK(18, 16) #define NFC_ADR_NUM(x) (((x) - 1) << 16) @@ -114,6 +158,7 @@ #define NFC_SEQ BIT(25) #define NFC_DATA_SWAP_METHOD BIT(26) #define NFC_ROW_AUTO_INC BIT(27) +#define NFC_H6_SEND_RND_CMD2 BIT(27) #define NFC_SEND_CMD3 BIT(28) #define NFC_SEND_CMD4 BIT(29) #define NFC_CMD_TYPE_MSK GENMASK(31, 30) @@ -125,6 +170,7 @@ #define NFC_READ_CMD_MSK GENMASK(7, 0) #define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) #define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) +#define NFC_RND_READ_CMD2_MSK GENMASK(31, 24) /* define bit use in NFC_WCMD_SET */ #define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) @@ -138,25 +184,46 @@ #define NFC_ECC_EXCEPTION BIT(4) #define NFC_ECC_BLOCK_SIZE_MSK BIT(5) #define NFC_ECC_BLOCK_512 BIT(5) -#define NFC_RANDOM_EN BIT(9) -#define NFC_RANDOM_DIRECTION BIT(10) -#define NFC_ECC_MODE_MSK GENMASK(15, 12) -#define NFC_ECC_MODE(x) ((x) << 12) +#define NFC_RANDOM_EN(nfc) (nfc->caps->random_en_mask) +#define NFC_RANDOM_DIRECTION(nfc) (nfc->caps->random_dir_mask) +#define NFC_ECC_MODE_MSK(nfc) (nfc->caps->ecc_mode_mask) +#define NFC_ECC_MODE(nfc, x) field_prep(NFC_ECC_MODE_MSK(nfc), (x)) +/* RANDOM_PAGE_SIZE: 0: ECC block size 1: page size */ +#define NFC_A23_RANDOM_PAGE_SIZE BIT(11) +#define NFC_H6_RANDOM_PAGE_SIZE BIT(7) #define NFC_RANDOM_SEED_MSK GENMASK(30, 16) #define NFC_RANDOM_SEED(x) ((x) << 16) /* define bit use in NFC_ECC_ST */ #define NFC_ECC_ERR(x) BIT(x) -#define NFC_ECC_ERR_MSK GENMASK(15, 0) -#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) +#define NFC_ECC_ERR_MSK(nfc) (nfc->caps->ecc_err_mask) + +/* + * define bit use in NFC_REG_PAT_FOUND + * For A10/A23, NFC_REG_PAT_FOUND == NFC_ECC_ST register + */ +#define NFC_ECC_PAT_FOUND_MSK(nfc) (nfc->caps->pat_found_mask) + #define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) -#define NFC_DEFAULT_TIMEOUT_MS 1000 +#define NFC_USER_DATA_LEN_MSK(step) \ + (0xf << (((step) % NFC_REG_USER_DATA_LEN_CAPACITY) * 4)) -#define NFC_SRAM_SIZE 1024 +#define NFC_DEFAULT_TIMEOUT_MS 1000 #define NFC_MAX_CS 7 +/* + * On A10/A23, this is the size of the NDFC User Data Register, containing the + * mandatory user data bytes following the ECC for each ECC step. + * Thus, for each ECC step, we need the ECC bytes + USER_DATA_SZ. + * Those bits are currently unsused, and kept as default value 0xffffffff. + * + * On H6/H616, this size became configurable, from 0 bytes to 32, via the + * USER_DATA_LEN registers. + */ +#define USER_DATA_SZ 4 + /** * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select * @@ -211,13 +278,57 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * * @has_mdma: Use mbus dma mode, otherwise general dma * through MBUS on A23/A33 needs extra configuration. + * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks + * @has_ecc_clk: If the controller needs an ECC clock. + * @has_mbus_clk: If the controller needs a mbus clock. * @reg_io_data: I/O data register + * @reg_ecc_err_cnt: ECC error counter register + * @reg_user_data: User data register + * @reg_user_data_len: User data length register + * @reg_spare_area: Spare Area Register + * @reg_pat_id: Pattern ID Register + * @reg_pat_found: Data Pattern Status Register + * @random_en_mask: RANDOM_EN mask in NFC_ECC_CTL register + * @random_dir_mask: RANDOM_DIRECTION mask in NFC_ECC_CTL register + * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register + * @ecc_err_mask: NFC_ECC_ERR mask in NFC_ECC_ST register + * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register * @dma_maxburst: DMA maxburst + * @ecc_strengths: Available ECC strengths array + * @nstrengths: Size of @ecc_strengths + * @max_ecc_steps: Maximum supported steps for ECC, this is also the + * number of user data registers + * @user_data_len_tab: Table of lenghts supported by USER_DATA_LEN register + * The table index is the value to set in NFC_USER_DATA_LEN + * registers, and the corresponding value is the number of + * bytes to write + * @nuser_data_tab: Size of @user_data_len_tab + * @sram_size: Size of the NAND controller SRAM */ struct sunxi_nfc_caps { bool has_mdma; + bool has_ecc_block_512; + bool has_ecc_clk; + bool has_mbus_clk; unsigned int reg_io_data; + unsigned int reg_ecc_err_cnt; + unsigned int reg_user_data; + unsigned int reg_user_data_len; + unsigned int reg_spare_area; + unsigned int reg_pat_id; + unsigned int reg_pat_found; + unsigned int random_en_mask; + unsigned int random_dir_mask; + unsigned int ecc_mode_mask; + unsigned int ecc_err_mask; + unsigned int pat_found_mask; unsigned int dma_maxburst; + const u8 *ecc_strengths; + unsigned int nstrengths; + const u8 *user_data_len_tab; + unsigned int nuser_data_tab; + unsigned int max_ecc_steps; + int sram_size; }; /** @@ -228,6 +339,8 @@ struct sunxi_nfc_caps { * @regs: NAND controller registers * @ahb_clk: NAND controller AHB clock * @mod_clk: NAND controller mod clock + * @ecc_clk: NAND controller ECC clock + * @mbus_clk: NAND controller MBUS clock * @reset: NAND controller reset line * @assigned_cs: bitmask describing already assigned CS lines * @clk_rate: NAND controller current clock rate @@ -243,6 +356,8 @@ struct sunxi_nfc { void __iomem *regs; struct clk *ahb_clk; struct clk *mod_clk; + struct clk *ecc_clk; + struct clk *mbus_clk; struct reset_control *reset; unsigned long assigned_cs; unsigned long clk_rate; @@ -431,7 +546,7 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs) if (sel->rb >= 0) ctl |= NFC_RB_SEL(sel->rb); - writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); + writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA(nfc)); if (nfc->clk_rate != sunxi_nand->clk_rate) { clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); @@ -455,7 +570,7 @@ static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len) while (len > offs) { bool poll = false; - cnt = min(len - offs, NFC_SRAM_SIZE); + cnt = min(len - offs, nfc->caps->sram_size); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -493,7 +608,7 @@ static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf, while (len > offs) { bool poll = false; - cnt = min(len - offs, NFC_SRAM_SIZE); + cnt = min(len - offs, nfc->caps->sram_size); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -623,13 +738,12 @@ static void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page, bool ecc) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + u32 ecc_ctl; u16 state; if (!(nand->options & NAND_NEED_SCRAMBLING)) return; - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); state = sunxi_nfc_randomizer_state(nand, page, ecc); ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); @@ -642,7 +756,7 @@ static void sunxi_nfc_randomizer_enable(struct nand_chip *nand) if (!(nand->options & NAND_NEED_SCRAMBLING)) return; - writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, + writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN(nfc), nfc->regs + NFC_REG_ECC_CTL); } @@ -653,7 +767,7 @@ static void sunxi_nfc_randomizer_disable(struct nand_chip *nand) if (!(nand->options & NAND_NEED_SCRAMBLING)) return; - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN(nfc), nfc->regs + NFC_REG_ECC_CTL); } @@ -717,20 +831,66 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), - oob); + sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)), oob); /* De-randomize the Bad Block Marker. */ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) sunxi_nfc_randomize_bbm(nand, page, oob); } +/* + * On H6/H6 the user_data length has to be set in specific registers + * before writing. + */ +static void sunxi_nfc_reset_user_data_len(struct sunxi_nfc *nfc) +{ + int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY; + + /* not all SoCs have this register */ + if (!nfc->caps->reg_user_data_len) + return; + + for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step) + writel(0, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, i)); +} + +static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc, + int len, int step) +{ + bool found = false; + u32 val; + int i; + + /* not all SoCs have this register */ + if (!nfc->caps->reg_user_data_len) + return; + + for (i = 0; i < nfc->caps->nuser_data_tab; i++) { + if (len == nfc->caps->user_data_len_tab[i]) { + found = true; + break; + } + } + + if (!found) { + dev_warn(nfc->dev, + "Unsupported length for user data reg: %d\n", len); + return; + } + + val = readl(nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step)); + + val &= ~NFC_USER_DATA_LEN_MSK(step); + val |= field_prep(NFC_USER_DATA_LEN_MSK(step), i); + writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step)); +} + static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, const u8 *oob, int step, bool bbm, int page) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u8 user_data[4]; + u8 user_data[USER_DATA_SZ]; /* Randomize the Bad Block Marker. */ if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { @@ -740,7 +900,7 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, } writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(step)); + nfc->regs + NFC_REG_USER_DATA(nfc, step)); } static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, @@ -757,7 +917,8 @@ static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, } static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, - int step, u32 status, bool *erased) + int step, u32 status, u32 pattern_found, + bool *erased) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; @@ -768,10 +929,10 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, if (status & NFC_ECC_ERR(step)) return -EBADMSG; - if (status & NFC_ECC_PAT_FOUND(step)) { + if (pattern_found & BIT(step)) { u8 pattern; - if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & 0x1))) { pattern = 0x0; } else { pattern = 0xff; @@ -782,12 +943,12 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, memset(data, pattern, ecc->size); if (oob) - memset(oob, pattern, ecc->bytes + 4); + memset(oob, pattern, ecc->bytes + USER_DATA_SZ); return 0; } - tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); + tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, step)); return NFC_ECC_ERR_CNT(step, tmp); } @@ -802,6 +963,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; int raw_mode = 0; + u32 pattern_found; bool erased; int ret; @@ -817,6 +979,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (ret) return ret; + sunxi_nfc_reset_user_data_len(nfc); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, @@ -827,10 +991,14 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + 4; + *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; + + pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); + pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0, readl(nfc->regs + NFC_REG_ECC_ST), + pattern_found, &erased); if (erased) return 1; @@ -847,11 +1015,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, - false); + nand_change_read_column_op(nand, oob_off, oob, + ecc->bytes + USER_DATA_SZ, false); - ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + 4, + ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, + ecc->bytes + USER_DATA_SZ, NULL, 0, ecc->strength); if (ret >= 0) raw_mode = 1; @@ -861,7 +1029,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, if (oob_required) { nand_change_read_column_op(nand, oob_off, NULL, 0, false); - sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4, + sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + USER_DATA_SZ, true, page); sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0, @@ -911,7 +1079,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf unsigned int max_bitflips = 0; int ret, i, raw_mode = 0; struct scatterlist sg; - u32 status, wait; + u32 status, pattern_found, wait; ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -923,6 +1091,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf return ret; sunxi_nfc_hw_ecc_enable(nand); + sunxi_nfc_reset_user_data_len(nfc); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); @@ -952,17 +1122,20 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf return ret; status = readl(nfc->regs + NFC_REG_ECC_ST); + pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); + pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); for (i = 0; i < nchunks; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; bool erased; ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL, oob_required ? oob : NULL, - i, status, &erased); + i, status, pattern_found, + &erased); /* ECC errors are handled in the second loop. */ if (ret < 0) @@ -972,7 +1145,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf /* TODO: use DMA to retrieve OOB */ nand_change_read_column_op(nand, mtd->writesize + oob_off, - oob, ecc->bytes + 4, false); + oob, ecc->bytes + USER_DATA_SZ, false); sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, !i, page); @@ -984,10 +1157,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret); } - if (status & NFC_ECC_ERR_MSK) { + if (status & NFC_ECC_ERR_MSK(nfc)) { for (i = 0; i < nchunks; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; @@ -1007,10 +1180,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf /* TODO: use DMA to retrieve OOB */ nand_change_read_column_op(nand, mtd->writesize + oob_off, - oob, ecc->bytes + 4, false); + oob, ecc->bytes + USER_DATA_SZ, false); - ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + 4, + ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, + ecc->bytes + USER_DATA_SZ, NULL, 0, ecc->strength); if (ret >= 0) @@ -1052,6 +1225,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, sunxi_nfc_randomizer_config(nand, page, false); sunxi_nfc_randomizer_enable(nand); + sunxi_nfc_reset_user_data_len(nfc); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | @@ -1063,7 +1238,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + 4; + *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; return 0; } @@ -1074,7 +1249,7 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand, { struct mtd_info *mtd = nand_to_mtd(nand); struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); + int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); int len = mtd->oobsize - offset; if (len <= 0) @@ -1107,7 +1282,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf, for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = buf + data_off; u8 *oob = nand->oob_poi + oob_off; @@ -1166,7 +1341,7 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand, for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = bufpoi + data_off; u8 *oob = nand->oob_poi + oob_off; @@ -1220,7 +1395,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand, for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); const u8 *data = buf + data_off; const u8 *oob = nand->oob_poi + oob_off; @@ -1258,7 +1433,7 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand, for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); const u8 *data = buf + data_off; const u8 *oob = nand->oob_poi + oob_off; @@ -1296,10 +1471,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, if (ret) goto pio_fallback; + sunxi_nfc_reset_user_data_len(nfc); for (i = 0; i < ecc->steps; i++) { - const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); + const u8 *oob = nand->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, i); } nand_prog_page_begin_op(nand, page, 0, NULL, 0); @@ -1567,7 +1744,7 @@ static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, if (section >= ecc->steps) return -ERANGE; - oobregion->offset = section * (ecc->bytes + 4) + 4; + oobregion->offset = section * (ecc->bytes + USER_DATA_SZ) + 4; oobregion->length = ecc->bytes; return 0; @@ -1601,10 +1778,10 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) return -ERANGE; - oobregion->offset = section * (ecc->bytes + 4); + oobregion->offset = section * (ecc->bytes + USER_DATA_SZ); if (section < ecc->steps) - oobregion->length = 4; + oobregion->length = USER_DATA_SZ; else oobregion->length = mtd->oobsize - oobregion->offset; @@ -1620,9 +1797,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, struct nand_ecc_ctrl *ecc, struct device_node *np) { - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + const u8 *strengths = nfc->caps->ecc_strengths; struct mtd_info *mtd = nand_to_mtd(nand); struct nand_device *nanddev = mtd_to_nanddev(mtd); int nsectors; @@ -1638,7 +1815,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, bytes = (mtd->oobsize - 2) / nsectors; /* 4 non-ECC bytes are added before each ECC bytes section */ - bytes -= 4; + bytes -= USER_DATA_SZ; /* and bytes has to be even. */ if (bytes % 2) @@ -1646,7 +1823,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->strength = bytes * 8 / fls(8 * ecc->size); - for (i = 0; i < ARRAY_SIZE(strengths); i++) { + for (i = 0; i < nfc->caps->nstrengths; i++) { if (strengths[i] > ecc->strength) break; } @@ -1667,7 +1844,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, } /* Add ECC info retrieval from DT */ - for (i = 0; i < ARRAY_SIZE(strengths); i++) { + for (i = 0; i < nfc->caps->nstrengths; i++) { if (ecc->strength <= strengths[i]) { /* * Update ecc->strength value with the actual strength @@ -1678,7 +1855,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, } } - if (i >= ARRAY_SIZE(strengths)) { + if (i >= nfc->caps->nstrengths) { dev_err(nfc->dev, "unsupported strength\n"); return -ENOTSUPP; } @@ -1691,7 +1868,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, nsectors = mtd->writesize / ecc->size; - if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) + if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors)) return -EINVAL; ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; @@ -1714,11 +1891,17 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->read_oob_raw = nand_read_oob_std; ecc->write_oob_raw = nand_write_oob_std; - sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION | + sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(nfc, i) | NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE | NFC_ECC_EN; - if (ecc->size == 512) - sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512; + if (ecc->size == 512) { + if (nfc->caps->has_ecc_block_512) { + sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512; + } else { + dev_err(nfc->dev, "512B ECC block not supported\n"); + return -EOPNOTSUPP; + } + } return 0; } @@ -1807,7 +1990,7 @@ static int sunxi_nfc_exec_subop(struct nand_chip *nand, case NAND_OP_DATA_OUT_INSTR: start = nand_subop_get_data_start_off(subop, i); remaining = nand_subop_get_data_len(subop, i); - cnt = min_t(u32, remaining, NFC_SRAM_SIZE); + cnt = min_t(u32, remaining, nfc->caps->sram_size); cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; if (instr->type == NAND_OP_DATA_OUT_INSTR) { @@ -2094,6 +2277,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (irq < 0) return irq; + nfc->caps = of_device_get_match_data(dev); + if (!nfc->caps) + return -EINVAL; + nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb"); if (IS_ERR(nfc->ahb_clk)) { dev_err(dev, "failed to retrieve ahb clk\n"); @@ -2106,6 +2293,22 @@ static int sunxi_nfc_probe(struct platform_device *pdev) return PTR_ERR(nfc->mod_clk); } + if (nfc->caps->has_ecc_clk) { + nfc->ecc_clk = devm_clk_get_enabled(dev, "ecc"); + if (IS_ERR(nfc->ecc_clk)) { + dev_err(dev, "failed to retrieve ecc clk\n"); + return PTR_ERR(nfc->ecc_clk); + } + } + + if (nfc->caps->has_mbus_clk) { + nfc->mbus_clk = devm_clk_get_enabled(dev, "mbus"); + if (IS_ERR(nfc->mbus_clk)) { + dev_err(dev, "failed to retrieve mbus clk\n"); + return PTR_ERR(nfc->mbus_clk); + } + } + nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); if (IS_ERR(nfc->reset)) return PTR_ERR(nfc->reset); @@ -2116,12 +2319,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev) return ret; } - nfc->caps = of_device_get_match_data(&pdev->dev); - if (!nfc->caps) { - ret = -EINVAL; - goto out_ahb_reset_reassert; - } - ret = sunxi_nfc_rst(nfc); if (ret) goto out_ahb_reset_reassert; @@ -2168,15 +2365,81 @@ static void sunxi_nfc_remove(struct platform_device *pdev) dma_release_channel(nfc->dmac); } +static const u8 sunxi_ecc_strengths_a10[] = { + 16, 24, 28, 32, 40, 48, 56, 60, 64 +}; + +static const u8 sunxi_ecc_strengths_h6[] = { + 16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80 +}; + +static const u8 sunxi_user_data_len_h6[] = { + 0, 4, 8, 12, 16, 20, 24, 28, 32 +}; + static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { + .has_ecc_block_512 = true, .reg_io_data = NFC_REG_A10_IO_DATA, + .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, + .reg_user_data = NFC_REG_A10_USER_DATA, + .reg_spare_area = NFC_REG_A10_SPARE_AREA, + .reg_pat_id = NFC_REG_A10_PAT_ID, + .reg_pat_found = NFC_REG_ECC_ST, + .random_en_mask = BIT(9), + .random_dir_mask = BIT(10), + .ecc_mode_mask = GENMASK(15, 12), + .ecc_err_mask = GENMASK(15, 0), + .pat_found_mask = GENMASK(31, 16), .dma_maxburst = 4, + .ecc_strengths = sunxi_ecc_strengths_a10, + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), + .max_ecc_steps = 16, + .sram_size = 1024, }; static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { .has_mdma = true, + .has_ecc_block_512 = true, + .reg_io_data = NFC_REG_A23_IO_DATA, + .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT, + .reg_user_data = NFC_REG_A10_USER_DATA, + .reg_spare_area = NFC_REG_A10_SPARE_AREA, + .reg_pat_id = NFC_REG_A10_PAT_ID, + .reg_pat_found = NFC_REG_ECC_ST, + .random_en_mask = BIT(9), + .random_dir_mask = BIT(10), + .ecc_mode_mask = GENMASK(15, 12), + .ecc_err_mask = GENMASK(15, 0), + .pat_found_mask = GENMASK(31, 16), + .dma_maxburst = 8, + .ecc_strengths = sunxi_ecc_strengths_a10, + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), + .max_ecc_steps = 16, + .sram_size = 1024, +}; + +static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { + .has_ecc_clk = true, + .has_mbus_clk = true, .reg_io_data = NFC_REG_A23_IO_DATA, + .reg_ecc_err_cnt = NFC_REG_H6_ECC_ERR_CNT, + .reg_user_data = NFC_REG_H6_USER_DATA, + .reg_user_data_len = NFC_REG_H6_USER_DATA_LEN, + .reg_spare_area = NFC_REG_H6_SPARE_AREA, + .reg_pat_id = NFC_REG_H6_PAT_ID, + .reg_pat_found = NFC_REG_H6_PAT_FOUND, + .random_en_mask = BIT(5), + .random_dir_mask = BIT(6), + .ecc_mode_mask = GENMASK(15, 8), + .ecc_err_mask = GENMASK(31, 0), + .pat_found_mask = GENMASK(31, 0), .dma_maxburst = 8, + .ecc_strengths = sunxi_ecc_strengths_h6, + .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6), + .user_data_len_tab = sunxi_user_data_len_h6, + .nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6), + .max_ecc_steps = 32, + .sram_size = 8192, }; static const struct of_device_id sunxi_nfc_ids[] = { @@ -2188,6 +2451,10 @@ static const struct of_device_id sunxi_nfc_ids[] = { .compatible = "allwinner,sun8i-a23-nand-controller", .data = &sunxi_nfc_a23_caps, }, + { + .compatible = "allwinner,sun50i-h616-nand-controller", + .data = &sunxi_nfc_h616_caps, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index f92133b8e1a6..d207286572d8 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1227,6 +1227,7 @@ static const struct nand_ops spinand_ops = { static const struct spinand_manufacturer *spinand_manufacturers[] = { &alliancememory_spinand_manufacturer, &ato_spinand_manufacturer, + &esmt_8c_spinand_manufacturer, &esmt_c8_spinand_manufacturer, &fmsh_spinand_manufacturer, &foresee_spinand_manufacturer, diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c index 9a9325c0bc49..e60e4ac1fd6f 100644 --- a/drivers/mtd/nand/spi/esmt.c +++ b/drivers/mtd/nand/spi/esmt.c @@ -12,6 +12,7 @@ /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */ #define SPINAND_MFR_ESMT_C8 0xc8 +#define SPINAND_MFR_ESMT_8C 0x8c #define ESMT_F50L1G41LB_CFG_OTP_PROTECT BIT(7) #define ESMT_F50L1G41LB_CFG_OTP_LOCK \ @@ -184,6 +185,21 @@ static const struct spinand_fact_otp_ops f50l1g41lb_fact_otp_ops = { .read = spinand_fact_otp_read, }; + +static const struct spinand_info esmt_8c_spinand_table[] = { + SPINAND_INFO("F50L1G41LC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL), + SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops), + SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)), +}; + static const struct spinand_info esmt_c8_spinand_table[] = { SPINAND_INFO("F50L1G41LB", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f, @@ -224,6 +240,14 @@ static const struct spinand_info esmt_c8_spinand_table[] = { static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { }; +const struct spinand_manufacturer esmt_8c_spinand_manufacturer = { + .id = SPINAND_MFR_ESMT_8C, + .name = "ESMT", + .chips = esmt_8c_spinand_table, + .nchips = ARRAY_SIZE(esmt_8c_spinand_table), + .ops = &esmt_spinand_manuf_ops, +}; + const struct spinand_manufacturer esmt_c8_spinand_manufacturer = { .id = SPINAND_MFR_ESMT_C8, .name = "ESMT", diff --git a/drivers/mtd/nand/spi/fmsh.c b/drivers/mtd/nand/spi/fmsh.c index 8b2097bfc771..f417955f7d1c 100644 --- a/drivers/mtd/nand/spi/fmsh.c +++ b/drivers/mtd/nand/spi/fmsh.c @@ -9,6 +9,13 @@ #include <linux/kernel.h> #include <linux/mtd/spinand.h> +#define FM25S01BI3_STATUS_ECC_MASK (7 << 4) + #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4) + #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4) + #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4) + #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4) + #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4) + #define SPINAND_MFR_FMSH 0xA1 static SPINAND_OP_VARIANTS(read_cache_variants, @@ -45,11 +52,66 @@ static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section, return 0; } +static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & FM25S01BI3_STATUS_ECC_MASK) { + case FM25S01BI3_STATUS_ECC_NO_BITFLIPS: + return 0; + + case FM25S01BI3_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS: + return 3; + + case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS: + return 6; + + case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS: + return 8; + + default: + break; + } + + return -EINVAL; +} + +static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 64; + region->length = 64; + + return 0; +} + +static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 4; + region->length = 12; + + return 0; +} + static const struct mtd_ooblayout_ops fm25s01a_ooblayout = { .ecc = fm25s01a_ooblayout_ecc, .free = fm25s01a_ooblayout_free, }; +static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = { + .ecc = fm25s01bi3_ooblayout_ecc, + .free = fm25s01bi3_ooblayout_free, +}; + static const struct spinand_info fmsh_spinand_table[] = { SPINAND_INFO("FM25S01A", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), @@ -58,8 +120,18 @@ static const struct spinand_info fmsh_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - SPINAND_HAS_QE_BIT, + 0, SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)), + SPINAND_INFO("FM25S01BI3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&fm25s01bi3_ooblayout, + fm25s01bi3_ecc_get_status)), }; static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = { |
