--- zzzz-none-000/linux-4.19.183/drivers/mtd/nand/raw/brcmnand/brcmnand.c 2021-03-24 10:07:39.000000000 +0000 +++ bcm63-7530ax-756/linux-4.19.183/drivers/mtd/nand/raw/brcmnand/brcmnand.c 2023-06-28 08:54:19.000000000 +0000 @@ -38,6 +38,9 @@ #include #include "brcmnand.h" +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#include "bcm_intr.h" +#endif /* * This flag controls if WP stays on between erase/write commands to mitigate @@ -132,6 +135,12 @@ bool dma_pending; struct completion done; struct completion dma_done; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* polling or interrupt for cmd. For DSL chips with no dma use polling + * better throughput */ + int polling; +#endif + /* List of NAND hosts (one for each chip-select) */ struct list_head host_list; @@ -152,6 +161,10 @@ unsigned int max_page_size; const unsigned int *page_sizes; unsigned int max_oob; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + unsigned int ecc_req_factor; +#endif + u32 features; /* for low-power standby/resume only */ @@ -441,6 +454,36 @@ INTFC_CTLR_READY = BIT(31), }; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +/* BRCMNAND_TIMING_1 - Nand Flash Timing Parameters 1 */ +#define BRCMNAND_TIMING_1_tWP_MASK 0xf0000000 +#define BRCMNAND_TIMING_1_tWP_SHIFT 28 +#define BRCMNAND_TIMING_1_tWH_MASK 0x0f000000 +#define BRCMNAND_TIMING_1_tWH_SHIFT 24 +#define BRCMNAND_TIMING_1_tRP_MASK 0x00f00000 +#define BRCMNAND_TIMING_1_tRP_SHIFT 20 +#define BRCMNAND_TIMING_1_tREH_MASK 0x000f0000 +#define BRCMNAND_TIMING_1_tREH_SHIFT 16 +#define BRCMNAND_TIMING_1_tCS_MASK 0x0000f000 +#define BRCMNAND_TIMING_1_tCS_SHIFT 12 +#define BRCMNAND_TIMING_1_tCLH_MASK 0x00000f00 +#define BRCMNAND_TIMING_1_tCLH_SHIFT 8 +#define BRCMNAND_TIMING_1_tALH_MASK 0x000000f0 +#define BRCMNAND_TIMING_1_tALH_SHIFT 4 +#define BRCMNAND_TIMING_1_tADL_MASK 0x0000000f +#define BRCMNAND_TIMING_1_tADL_SHIFT 0 + +/* BRCMNAND_TIMING_2 - Nand Flash Timing Parameters 2 */ +#define BRCMNAND_TIMING_2_CLK_SELECT_MASK 0x80000000 +#define BRCMNAND_TIMING_2_CLK_SELECT_SHIFT 31 +#define BRCMNAND_TIMING_2_tWB_MASK 0x00001e00 +#define BRCMNAND_TIMING_2_tWB_SHIFT 9 +#define BRCMNAND_TIMING_2_tWHR_MASK 0x000001f0 +#define BRCMNAND_TIMING_2_tWHR_SHIFT 4 +#define BRCMNAND_TIMING_2_tREAD_MASK 0x0000000f +#define BRCMNAND_TIMING_2_tREAD_SHIFT 0 +#endif + static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) { return brcmnand_readl(ctrl->nand_base + offs); @@ -479,6 +522,19 @@ else if (ctrl->nand_version >= 0x0400) ctrl->reg_offsets = brcmnand_regs_v40; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + */ + if (ctrl->nand_version >= 0x0500) + ctrl->ecc_req_factor = 14; + else + ctrl->ecc_req_factor = 13; +#endif + + /* Chip-select stride */ if (ctrl->nand_version >= 0x0701) ctrl->reg_spacing = 0x14; @@ -699,6 +755,17 @@ return mask; } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +static inline int brcmnand_ecc_level_shift(struct brcmnand_controller *ctrl) +{ + if (ctrl->nand_version >= 0x0702) + return NAND_ACC_CONTROL_ECC_EXT_SHIFT; + else + return NAND_ACC_CONTROL_ECC_SHIFT; +} +#endif + + static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) { struct brcmnand_controller *ctrl = host->ctrl; @@ -708,8 +775,13 @@ if (en) { acc_control |= ecc_flags; /* enable RD/WR ECC */ +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + acc_control |= host->hwcfg.ecc_level << brcmnand_ecc_level_shift(ctrl); +#else acc_control |= host->hwcfg.ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; +#endif } else { acc_control &= ~ecc_flags; /* disable RD/WR ECC */ acc_control &= ~brcmnand_ecc_level_mask(ctrl); @@ -760,6 +832,113 @@ nand_writereg(ctrl, acc_control_offs, tmp); } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +static int brcmnand_get_spare_size(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + u32 acc = nand_readreg(ctrl, acc_control_offs); + + return (acc&brcmnand_spare_area_mask(ctrl)); +} + +static int brcmnand_get_ecc_strength(struct brcmnand_host *host) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + int sector_size_1k = brcmnand_get_sector_size_1k(host); + u32 acc; + int spare_area_size, ecc_level, ecc_strength; + + spare_area_size = brcmnand_get_spare_size(host); + acc = nand_readreg(ctrl, acc_control_offs); + ecc_level = (acc & brcmnand_ecc_level_mask(ctrl)) >> brcmnand_ecc_level_shift(ctrl); + if (sector_size_1k) + ecc_strength = ecc_level<<1; + else if(spare_area_size == 16 && ecc_level == 15 ) + ecc_strength = 1; /* hamming */ + else + ecc_strength = ecc_level; + + return ecc_strength; +} + + +static void brcmnand_adjust_timing(struct brcmnand_host *host, struct brcmnand_cfg *cfg) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1); + u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2); + u32 nand_timing_1 = nand_readreg(ctrl, t1_offs); + u32 nand_timing_2 = nand_readreg(ctrl, t2_offs); + + if ((cfg->timing_1 == 0) && (cfg->timing_2 == 0)) { + /* if we don't know better, force max read speed */ + cfg->timing_1 = 0x00320000; + cfg->timing_2 = 0x00000004; + } + else + dev_info(ctrl->dev, "Using timing parameters from Id table\n"); + + if (cfg->timing_1 & BRCMNAND_TIMING_1_tWP_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tWP_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tWP_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tWH_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tWH_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tWH_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tRP_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tRP_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tRP_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tREH_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tREH_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tREH_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tCS_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tCS_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tCS_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tCLH_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tCLH_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tCLH_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tALH_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tALH_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tALH_MASK); + } + if (cfg->timing_1 & BRCMNAND_TIMING_1_tADL_MASK) { + nand_timing_1 &= ~BRCMNAND_TIMING_1_tADL_MASK; + nand_timing_1 |= (cfg->timing_1 & BRCMNAND_TIMING_1_tADL_MASK); + } + + nand_writereg(ctrl, t1_offs, nand_timing_1); + + if (cfg->timing_2 & BRCMNAND_TIMING_2_tWB_MASK) { + nand_timing_2 &= ~BRCMNAND_TIMING_2_tWB_MASK; + nand_timing_2 |= (cfg->timing_2 & BRCMNAND_TIMING_2_tWB_MASK); + } + if (cfg->timing_2 & BRCMNAND_TIMING_2_tWHR_MASK) { + nand_timing_2 &= ~BRCMNAND_TIMING_2_tWHR_MASK; + nand_timing_2 |= (cfg->timing_2 & BRCMNAND_TIMING_2_tWHR_MASK); + } + if (cfg->timing_2 & BRCMNAND_TIMING_2_tREAD_MASK) { + nand_timing_2 &= ~BRCMNAND_TIMING_2_tREAD_MASK; + nand_timing_2 |= (cfg->timing_2 & BRCMNAND_TIMING_2_tREAD_MASK); + } + + nand_writereg(ctrl, t2_offs, nand_timing_2); + + dev_info(ctrl->dev, "Adjust timing_1 to 0x%08x timing_2 to 0x%08x\n", + nand_timing_1, nand_timing_2); + +} +#endif + + /*********************************************************************** * CS_NAND_SELECT ***********************************************************************/ @@ -942,8 +1121,12 @@ if (section >= sectors) return -ERANGE; - +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* fix upstream code bug */ + oobregion->offset = (section+1) * sas - chip->ecc.bytes; +#else oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; +#endif oobregion->length = chip->ecc.bytes; return 0; @@ -1025,7 +1208,9 @@ mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); return 0; } - +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + ecc->bytes = DIV_ROUND_UP(ecc_level * host->ctrl->ecc_req_factor, 8); +#else /* * CONTROLLER_VERSION: * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) @@ -1033,6 +1218,7 @@ * But we will just be conservative. */ ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); +#endif if (p->page_size == 512) mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); else @@ -1076,6 +1262,17 @@ brcmnand_set_wp(ctrl, wp); nand_status_op(chip, NULL); + +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* Not all the boards connect WP pin to the NAND chip where it is always + writeable. To avoid adding a configuration, we only check WP disable status + as it is critical before write/erase command on the board has WP connected. + It is OK to skip the check for WP enabled status check. There is no harm to + do that as it won't affect any read that follows. + */ + if (wp) + return; +#endif /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY | @@ -1084,7 +1281,6 @@ NAND_CTRL_RDY | NAND_STATUS_READY | (wp ? 0 : NAND_STATUS_WP), 0); - if (ret) dev_err_ratelimited(&host->pdev->dev, "nand #WP expected %s\n", @@ -1172,12 +1368,21 @@ tbytes = max(0, tbytes - (int)ctrl->max_oob); tbytes = min_t(int, tbytes, ctrl->max_oob); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + for (j = 0; (j + 3) < tbytes; j += 4) + oob_reg_write(ctrl, j, + (((j < tbytes ) ? oob[j] : 0xff) << 24) | + (((j + 1 < tbytes ) ? oob[j + 1] : 0xff) << 16) | + (((j + 2 < tbytes ) ? oob[j + 2] : 0xff) << 8) | + ((j + 3 < tbytes ) ? oob[j + 3] : 0xff)); +#else for (j = 0; j < tbytes; j += 4) oob_reg_write(ctrl, j, (oob[j + 0] << 24) | (oob[j + 1] << 16) | (oob[j + 2] << 8) | (oob[j + 3] << 0)); +#endif return tbytes; } @@ -1213,6 +1418,33 @@ return IRQ_HANDLED; } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +static int brcmnand_wait_cmd(struct brcmnand_host *host, unsigned long timeo) +{ + struct brcmnand_controller *ctrl = host->ctrl; + int ret = 0, ready = 0; + + if (ctrl->polling) { + timeo += jiffies; + while (time_before(jiffies, timeo)) { + ready = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)&INTFC_CTLR_READY; + if (ready) + return ret; + else + udelay(1); + } + + ret = -1; + + } else { + if (wait_for_completion_timeout(&ctrl->done, timeo) <= 0 ) + ret = -1; + } + + return ret; +} +#endif + static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) { struct brcmnand_controller *ctrl = host->ctrl; @@ -1220,6 +1452,17 @@ dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* + * If we came here through _panic_write and there is a pending + * command, give it 5ms to complete. If it doesn't, rather than + * hitting BUG_ON, just return so we don't crash while crashing. + */ + if (oops_in_progress) { + if (ctrl->cmd_pending && brcmnand_wait_cmd(host, msecs_to_jiffies(5))) + return; + } else +#endif BUG_ON(ctrl->cmd_pending != 0); ctrl->cmd_pending = cmd; @@ -1228,9 +1471,10 @@ mb(); /* flush previous writes */ brcmnand_write_reg(ctrl, BRCMNAND_CMD_START, - cmd << brcmnand_cmd_shift(ctrl)); + cmd << brcmnand_cmd_shift(ctrl)); } + /*********************************************************************** * NAND MTD API: read/program/erase ***********************************************************************/ @@ -1249,8 +1493,12 @@ unsigned long timeo = msecs_to_jiffies(100); dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (ctrl->cmd_pending && brcmnand_wait_cmd(host, timeo) != 0 ) { +#else if (ctrl->cmd_pending && wait_for_completion_timeout(&ctrl->done, timeo) <= 0) { +#endif u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) >> brcmnand_cmd_shift(ctrl); @@ -1351,8 +1599,18 @@ native_cmd = CMD_SPARE_AREA_READ; break; case NAND_CMD_ERASE1: +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (brcmnand_check_dying_gasp(ctrl->soc)) { + dev_warn(ctrl->dev, "system is losing power, abort nand erase at offset 0x%llx\n", addr); + native_cmd = 0; + } else { + native_cmd = CMD_BLOCK_ERASE; + brcmnand_wp(mtd, 0); + } +#else native_cmd = CMD_BLOCK_ERASE; brcmnand_wp(mtd, 0); +#endif break; case NAND_CMD_PARAM: native_cmd = CMD_PARAMETER_READ; @@ -1402,12 +1660,15 @@ * SECTOR_SIZE_1K may invalidate it */ for (i = 0; i < FC_WORDS; i++) +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + flash_cache[i] = brcmnand_read_fc(ctrl, i); +#else /* * Flash cache is big endian for parameter pages, at * least on STB SoCs */ flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i)); - +#endif brcmnand_soc_data_bus_unprepare(ctrl->soc, true); /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ @@ -1458,9 +1719,9 @@ /* At FC_BYTES boundary, switch to next column */ if (host->last_byte > 0 && offs == 0) nand_change_read_column_op(chip, addr, NULL, 0, false); - ret = ctrl->flash_cache[offs]; break; + case NAND_CMD_GET_FEATURES: if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { ret = 0; @@ -1625,9 +1886,14 @@ if (likely(buf)) { brcmnand_soc_data_bus_prepare(ctrl->soc, false); - +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + (void)j; + memcpy((void*)buf, (void*)ctrl->nand_fc, FC_BYTES); + buf += FC_WORDS; +#else for (j = 0; j < FC_WORDS; j++, buf++) *buf = brcmnand_read_fc(ctrl, j); +#endif brcmnand_soc_data_bus_unprepare(ctrl->soc, false); } @@ -1683,7 +1949,13 @@ int bitflips = 0; int page = addr >> chip->page_shift; int ret; - +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + struct mtd_oob_region ecc_region; + u8 *ecc_bytes; + u8 *ecc_chunk; +#endif if (!buf) { buf = chip->data_buf; /* Invalidate page cache */ @@ -1698,9 +1970,27 @@ return ret; for (i = 0; i < chip->ecc.steps; i++, oob += sas) { +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* only check for ECC bytes because JFFS2 may already write cleanmarker in OOB */ + if (mtd_ooblayout_ecc(mtd, i, &ecc_region) == 0 ) { + ecc_bytes = ((u8*)chip->oob_poi) + ecc_region.offset; + } else { + dev_err(ctrl->dev, "get ooblayout ecc failed region %d sas %d ecc steps %d\n", + i, sas, chip->ecc.steps); + ecc_bytes = NULL; + ecc_region.length = 0; + } + ecc_chunk = (u8*)buf + chip->ecc.size * i; + + ret = nand_check_erased_ecc_chunk(ecc_chunk, chip->ecc.size, + ecc_bytes, ecc_region.length, + NULL, 0, + chip->ecc.strength); +#else ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, oob, sas, NULL, 0, chip->ecc.strength); +#endif if (ret < 0) return ret; @@ -1846,7 +2136,13 @@ struct brcmnand_controller *ctrl = host->ctrl; unsigned int i, j, trans = mtd->writesize >> FC_SHIFT; int status, ret = 0; - +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + u8* oob_in = oob; + if (brcmnand_check_dying_gasp(ctrl->soc)) { + dev_warn(ctrl->dev, "system is losing power, abort nand write at offset 0x%llx\n", addr); + return -EIO; + } +#endif dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf); if (unlikely((unsigned long)buf & 0x03)) { @@ -1854,12 +2150,28 @@ buf = (u32 *)((unsigned long)buf & ~0x03); } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if ((chip->options & NAND_PAGE_NOP1) && !buf && oob) { // quit if writing OOB only to NOP=1 parallel NAND device + return 0; + } + + if( buf && !oob ) { + u64 err_addr = 0; + oob = (u8 *)chip->oob_poi; + brcmnand_read_by_pio(mtd, chip, addr, trans, NULL, oob, &err_addr); + } +#endif + brcmnand_wp(mtd, 0); for (i = 0; i < ctrl->max_oob; i += 4) oob_reg_write(ctrl, i, 0xffffffff); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (has_flash_dma(ctrl) && !oob_in && flash_dma_buf_ok(buf)) { +#else if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { +#endif if (brcmnand_dma_trans(host, addr, (u32 *)buf, mtd->writesize, CMD_PROGRAM_PAGE)) ret = -EIO; @@ -1894,10 +2206,19 @@ host->hwcfg.sector_size_1k); } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if( !buf && oob ) { + brcmnand_set_ecc_enabled(host, 0); + } +#endif /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */ brcmnand_send_cmd(host, CMD_PROGRAM_PAGE); status = brcmnand_waitfunc(mtd, chip); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if( !buf && oob ) + brcmnand_set_ecc_enabled(host, 1); +#endif if (status & NAND_STATUS_FAIL) { dev_info(ctrl->dev, "program failed at %llx\n", (unsigned long long)addr); @@ -2057,6 +2378,10 @@ /* threshold = ceil(BCH-level * 0.75) */ brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4)); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + brcmnand_adjust_timing(host, cfg); +#endif + return 0; } @@ -2071,6 +2396,11 @@ cfg->page_size >= 1024 ? "KiB" : "B", cfg->spare_area_size, cfg->device_width); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (host->chip.options & NAND_PAGE_NOP1) + buf += sprintf(buf, ", NOP=1"); +#endif + /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */ if (is_hamming_ecc(host->ctrl, cfg)) sprintf(buf, ", Hamming ECC"); @@ -2101,16 +2431,48 @@ char msg[128]; u32 offs, tmp, oob_sector; int ret; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + int sector_size_1k = 0; +#endif memset(cfg, 0, sizeof(*cfg)); +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* save the timing parameter from nand id table */ + cfg->timing_1 = chip->timing_1; + cfg->timing_2 = chip->timing_2; + + /* set ECC size and strength based on hw configuration from strap + * if dtb does not specify them + */ + sector_size_1k = brcmnand_get_sector_size_1k(host); + if (chip->ecc.size == 0) { + if (sector_size_1k< 0) + chip->ecc.size = 512; + else + chip->ecc.size = 512<ecc.strength = brcmnand_get_ecc_strength(host); + if (chip->ecc.strength == 0) { + dev_err(ctrl->dev, "ECC disable not supported\n"); + return -EINVAL; + } +#endif + ret = of_property_read_u32(nand_get_flash_node(chip), "brcm,nand-oob-sector-size", &oob_sector); if (ret) { +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + /* set spare area based on hw configuration from strap + * if dtb does not specify them + */ + cfg->spare_area_size = brcmnand_get_spare_size(host); +#else /* Use detected size */ cfg->spare_area_size = mtd->oobsize / (mtd->writesize >> FC_SHIFT); +#endif } else { cfg->spare_area_size = oob_sector; } @@ -2214,38 +2576,39 @@ static int brcmnand_attach_chip(struct nand_chip *chip) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct brcmnand_host *host = nand_get_controller_data(chip); - int ret; + struct mtd_info *mtd = nand_to_mtd(chip); + struct brcmnand_host *host = nand_get_controller_data(chip); + int ret; - chip->options |= NAND_NO_SUBPAGE_WRITE; - /* - * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA - * to/from, and have nand_base pass us a bounce buffer instead, as - * needed. - */ - chip->options |= NAND_USE_BOUNCE_BUFFER; + chip->options |= NAND_NO_SUBPAGE_WRITE; + /* + * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA + * to/from, and have nand_base pass us a bounce buffer instead, as + * needed. + */ + chip->options |= NAND_USE_BOUNCE_BUFFER; - if (chip->bbt_options & NAND_BBT_USE_FLASH) - chip->bbt_options |= NAND_BBT_NO_OOB; + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; - if (brcmnand_setup_dev(host)) - return -ENXIO; + if (brcmnand_setup_dev(host)) + return -ENXIO; - chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; + chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512; - /* only use our internal HW threshold */ - mtd->bitflip_threshold = 1; + /* only use our internal HW threshold */ + mtd->bitflip_threshold = 1; - ret = brcmstb_choose_ecc_layout(host); + ret = brcmstb_choose_ecc_layout(host); - return ret; + return ret; } static const struct nand_controller_ops brcmnand_controller_ops = { - .attach_chip = brcmnand_attach_chip, + .attach_chip = brcmnand_attach_chip, }; + static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) { struct brcmnand_controller *ctrl = host->ctrl; @@ -2309,9 +2672,15 @@ if (ret) return ret; +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + setup_mtd_parts(mtd); + return 0; +#else + ret = mtd_device_register(mtd, NULL, 0); if (ret) nand_cleanup(chip); +#endif return ret; } @@ -2547,11 +2916,24 @@ /* IRQ */ ctrl->irq = platform_get_irq(pdev, 0); if ((int)ctrl->irq < 0) { +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) +#if !defined(CONFIG_BCM947189) + ctrl->irq = INTERRUPT_ID_NAND_FLASH; +#endif + /* switch to polling for better throughput for now */ + ctrl->polling = 1; + ctrl->soc = soc; +#else dev_err(dev, "no IRQ defined\n"); ret = -ENODEV; goto err; +#endif } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + if (!ctrl->polling) { +#endif + /* * Some SoCs integrate this controller (e.g., its interrupt bits) in * interesting ways @@ -2576,6 +2958,10 @@ goto err; } +#if defined(CONFIG_BCM_KF_MTD_BCMNAND) + } +#endif + for_each_available_child_of_node(dn, child) { if (of_device_is_compatible(child, "brcm,nandcs")) { struct brcmnand_host *host;