diff options
Diffstat (limited to 'drivers/mtd')
| -rw-r--r-- | drivers/mtd/altera_qspi.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/cfi_flash.c | 36 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/brcmnand/brcmnand.c | 209 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/brcmnand/brcmnand.h | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/meson_nand.c | 84 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/nand_base.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/omap_gpmc.c | 5 | ||||
| -rw-r--r-- | drivers/mtd/nvmxip/nvmxip-uclass.c | 10 | ||||
| -rw-r--r-- | drivers/mtd/spi/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/mtd/spi/sandbox.c | 2 | ||||
| -rw-r--r-- | drivers/mtd/spi/spi-nor-core.c | 480 | ||||
| -rw-r--r-- | drivers/mtd/spi/spi-nor-ids.c | 15 | ||||
| -rw-r--r-- | drivers/mtd/ubi/fastmap.c | 8 |
13 files changed, 688 insertions, 172 deletions
diff --git a/drivers/mtd/altera_qspi.c b/drivers/mtd/altera_qspi.c index c26615821c8..e5c8df750b7 100644 --- a/drivers/mtd/altera_qspi.c +++ b/drivers/mtd/altera_qspi.c @@ -96,7 +96,7 @@ int flash_erase(flash_info_t *info, int s_first, int s_last) ret = mtd_erase(mtd, &instr); flash_set_verbose(0); if (ret) - return ERR_PROTECTED; + return FL_ERR_PROTECTED; puts(" done\n"); return 0; @@ -114,7 +114,7 @@ int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) ret = mtd_write(mtd, to, cnt, &retlen, src); if (ret) - return ERR_PROTECTED; + return FL_ERR_PROTECTED; return 0; } diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index a7826e81c17..e50502824ac 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -593,11 +593,11 @@ static int flash_status_check(flash_info_t *info, flash_sect_t sector, flash_read_long(info, sector, 0)); flash_write_cmd(info, sector, 0, info->cmd_reset); udelay(1); - return ERR_TIMEOUT; + return FL_ERR_TIMEOUT; } udelay(1); /* also triggers watchdog */ } - return ERR_OK; + return FL_ERR_OK; } /*----------------------------------------------------------------------- @@ -616,9 +616,9 @@ static int flash_full_status_check(flash_info_t *info, flash_sect_t sector, case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: - if (retcode == ERR_OK && + if (retcode == FL_ERR_OK && !flash_isset(info, sector, 0, FLASH_STATUS_DONE)) { - retcode = ERR_INVAL; + retcode = FL_ERR_INVAL; printf("Flash %s error at address %lx\n", prompt, info->start[sector]); if (flash_isset(info, sector, 0, FLASH_STATUS_ECLBS | @@ -627,14 +627,14 @@ static int flash_full_status_check(flash_info_t *info, flash_sect_t sector, } else if (flash_isset(info, sector, 0, FLASH_STATUS_ECLBS)) { puts("Block Erase Error.\n"); - retcode = ERR_NOT_ERASED; + retcode = FL_ERR_NOT_ERASED; } else if (flash_isset(info, sector, 0, FLASH_STATUS_PSLBS)) { puts("Locking Error\n"); } if (flash_isset(info, sector, 0, FLASH_STATUS_DPS)) { puts("Block locked.\n"); - retcode = ERR_PROTECTED; + retcode = FL_ERR_PROTECTED; } if (flash_isset(info, sector, 0, FLASH_STATUS_VPENS)) puts("Vpp Low Error.\n"); @@ -702,12 +702,12 @@ static int flash_status_poll(flash_info_t *info, void *src, void *dst, if (get_timer(start) > tout) { printf("Flash %s timeout at address %lx data %lx\n", prompt, (ulong)dst, (ulong)flash_read8(dst)); - return ERR_TIMEOUT; + return FL_ERR_TIMEOUT; } udelay(1); /* also triggers watchdog */ } #endif /* CONFIG_SYS_CFI_FLASH_STATUS_POLL */ - return ERR_OK; + return FL_ERR_OK; } /*----------------------------------------------------------------------- @@ -810,7 +810,7 @@ static int flash_write_cfiword(flash_info_t *info, ulong dest, cfiword_t cword) break; } if (!flag) - return ERR_NOT_ERASED; + return FL_ERR_NOT_ERASED; /* Disable interrupts which might cause a timeout here */ flag = disable_interrupts(); @@ -899,7 +899,7 @@ static int flash_write_cfibuffer(flash_info_t *info, ulong dest, uchar *cp, shift = 3; break; default: - retcode = ERR_INVAL; + retcode = FL_ERR_INVAL; goto out_unmap; } @@ -930,7 +930,7 @@ static int flash_write_cfibuffer(flash_info_t *info, ulong dest, uchar *cp, } } if (!flag) { - retcode = ERR_NOT_ERASED; + retcode = FL_ERR_NOT_ERASED; goto out_unmap; } @@ -950,7 +950,7 @@ static int flash_write_cfibuffer(flash_info_t *info, ulong dest, uchar *cp, retcode = flash_status_check(info, sector, info->buffer_write_tout, "write to buffer"); - if (retcode == ERR_OK) { + if (retcode == FL_ERR_OK) { /* reduce the number of loops by the width of * the port */ @@ -975,7 +975,7 @@ static int flash_write_cfibuffer(flash_info_t *info, ulong dest, uchar *cp, src += 8, dst += 8; break; default: - retcode = ERR_INVAL; + retcode = FL_ERR_INVAL; goto out_unmap; } } @@ -1025,7 +1025,7 @@ static int flash_write_cfibuffer(flash_info_t *info, ulong dest, uchar *cp, } break; default: - retcode = ERR_INVAL; + retcode = FL_ERR_INVAL; goto out_unmap; } @@ -1043,7 +1043,7 @@ static int flash_write_cfibuffer(flash_info_t *info, ulong dest, uchar *cp, default: debug("Unknown Command Set\n"); - retcode = ERR_INVAL; + retcode = FL_ERR_INVAL; break; } @@ -1389,7 +1389,7 @@ int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) if (i > cnt) i = cnt; rc = flash_write_cfibuffer(info, wp, src, i); - if (rc != ERR_OK) + if (rc != FL_ERR_OK) return rc; i -= i & (info->portwidth - 1); wp += i; @@ -1398,7 +1398,7 @@ int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) FLASH_SHOW_PROGRESS(scale, dots, digit, i); /* Only check every once in a while */ if ((cnt & 0xFFFF) < buffered_size && ctrlc()) - return ERR_ABORTED; + return FL_ERR_ABORTED; } #else while (cnt >= info->portwidth) { @@ -1413,7 +1413,7 @@ int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth); /* Only check every once in a while */ if ((cnt & 0xFFFF) < info->portwidth && ctrlc()) - return ERR_ABORTED; + return FL_ERR_ABORTED; } #endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */ diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index b1af3f717d4..749553c9df9 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -25,6 +25,7 @@ #include <linux/completion.h> #include <linux/errno.h> #include <linux/log2.h> +#include <linux/mtd/nand.h> #include <linux/mtd/rawnand.h> #include <asm/processor.h> #include <dm.h> @@ -218,6 +219,7 @@ struct brcmnand_controller { const unsigned int *page_sizes; unsigned int page_size_shift; unsigned int max_oob; + u32 ecc_level_shift; u32 features; /* for low-power standby/resume only */ @@ -544,6 +546,34 @@ enum { INTFC_CTLR_READY = BIT(31), }; +/*********************************************************************** + * NAND ACC CONTROL bitfield + * + * Some bits have remained constant throughout hardware revision, while + * others have shifted around. + ***********************************************************************/ + +/* Constant for all versions (where supported) */ +enum { + /* See BRCMNAND_HAS_CACHE_MODE */ + ACC_CONTROL_CACHE_MODE = BIT(22), + + /* See BRCMNAND_HAS_PREFETCH */ + ACC_CONTROL_PREFETCH = BIT(23), + + ACC_CONTROL_PAGE_HIT = BIT(24), + ACC_CONTROL_WR_PREEMPT = BIT(25), + ACC_CONTROL_PARTIAL_PAGE = BIT(26), + ACC_CONTROL_RD_ERASED = BIT(27), + ACC_CONTROL_FAST_PGM_RDIN = BIT(28), + ACC_CONTROL_WR_ECC = BIT(30), + ACC_CONTROL_RD_ECC = BIT(31), +}; + +#define ACC_CONTROL_ECC_SHIFT 16 +/* Only for v7.2 */ +#define ACC_CONTROL_ECC_EXT_SHIFT 13 + static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs) { return brcmnand_readl(ctrl->nand_base + offs); @@ -675,6 +705,12 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) #endif /* __UBOOT__ */ ctrl->features |= BRCMNAND_HAS_WP; + /* v7.2 has different ecc level shift in the acc register */ + if (ctrl->nand_version == 0x0702) + ctrl->ecc_level_shift = ACC_CONTROL_ECC_EXT_SHIFT; + else + ctrl->ecc_level_shift = ACC_CONTROL_ECC_SHIFT; + return 0; } @@ -733,6 +769,20 @@ static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, __raw_writel(val, ctrl->nand_fc + word * 4); } +static inline void brcmnand_read_data_bus(struct brcmnand_controller *ctrl, + void __iomem *flash_cache, u32 *buffer, int fc_words) +{ + struct brcmnand_soc *soc = ctrl->soc; + int i; + + if (soc && soc->read_data_bus) { + soc->read_data_bus(soc, flash_cache, buffer, fc_words); + } else { + for (i = 0; i < fc_words; i++) + buffer[i] = brcmnand_read_fc(ctrl, i); + } +} + static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl) { @@ -844,30 +894,6 @@ static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) return 0; } -/*********************************************************************** - * NAND ACC CONTROL bitfield - * - * Some bits have remained constant throughout hardware revision, while - * others have shifted around. - ***********************************************************************/ - -/* Constant for all versions (where supported) */ -enum { - /* See BRCMNAND_HAS_CACHE_MODE */ - ACC_CONTROL_CACHE_MODE = BIT(22), - - /* See BRCMNAND_HAS_PREFETCH */ - ACC_CONTROL_PREFETCH = BIT(23), - - ACC_CONTROL_PAGE_HIT = BIT(24), - ACC_CONTROL_WR_PREEMPT = BIT(25), - ACC_CONTROL_PARTIAL_PAGE = BIT(26), - ACC_CONTROL_RD_ERASED = BIT(27), - ACC_CONTROL_FAST_PGM_RDIN = BIT(28), - ACC_CONTROL_WR_ECC = BIT(30), - ACC_CONTROL_RD_ECC = BIT(31), -}; - static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) { if (ctrl->nand_version == 0x0702) @@ -880,18 +906,15 @@ static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) return GENMASK(4, 0); } -#define NAND_ACC_CONTROL_ECC_SHIFT 16 -#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13 - static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) { u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; - mask <<= NAND_ACC_CONTROL_ECC_SHIFT; + mask <<= ACC_CONTROL_ECC_SHIFT; /* v7.2 includes additional ECC levels */ - if (ctrl->nand_version >= 0x0702) - mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT; + if (ctrl->nand_version == 0x0702) + mask |= 0x7 << ACC_CONTROL_ECC_EXT_SHIFT; return mask; } @@ -905,8 +928,8 @@ static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) if (en) { acc_control |= ecc_flags; /* enable RD/WR ECC */ - acc_control |= host->hwcfg.ecc_level - << NAND_ACC_CONTROL_ECC_SHIFT; + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + acc_control |= host->hwcfg.ecc_level << ctrl->ecc_level_shift; } else { acc_control &= ~ecc_flags; /* disable RD/WR ECC */ acc_control &= ~brcmnand_ecc_level_mask(ctrl); @@ -957,6 +980,43 @@ static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) nand_writereg(ctrl, acc_control_offs, tmp); } +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 void brcmnand_get_ecc_settings(struct brcmnand_host *host, struct nand_chip *chip) +{ + struct brcmnand_controller *ctrl = host->ctrl; + u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, + BRCMNAND_CS_ACC_CONTROL); + bool sector_size_1k = brcmnand_get_sector_size_1k(host); + int spare_area_size, ecc_level; + u32 acc; + + spare_area_size = brcmnand_get_spare_size(host); + acc = nand_readreg(ctrl, acc_control_offs); + ecc_level = (acc & brcmnand_ecc_level_mask(ctrl)) >> ctrl->ecc_level_shift; + if (sector_size_1k) + chip->ecc.strength = ecc_level * 2; + else if (spare_area_size == 16 && ecc_level == 15) + chip->ecc.strength = 1; /* hamming */ + else + chip->ecc.strength = ecc_level; + + if (chip->ecc.size == 0) { + if (sector_size_1k) + chip->ecc.size = 1024; + else + chip->ecc.size = 512; + } +} + /*********************************************************************** * CS_NAND_SELECT ***********************************************************************/ @@ -1003,6 +1063,14 @@ static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl, } while (get_timer(base) < limit); #endif /* __UBOOT__ */ + /* + * do a final check after time out in case the CPU was busy and the driver + * did not get enough time to perform the polling to avoid false alarms + */ + val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); + if ((val & mask) == expected_val) + return 0; + dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n", expected_val, val & mask); @@ -1318,19 +1386,33 @@ static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, const u8 *oob, int sas, int sector_1k) { int tbytes = sas << sector_1k; - int j; + int j, k = 0; + u32 last = 0xffffffff; + u8 *plast = (u8 *)&last; /* Adjust OOB values for 1K sector size */ if (sector_1k && (i & 0x01)) tbytes = max(0, tbytes - (int)ctrl->max_oob); tbytes = min_t(int, tbytes, ctrl->max_oob); - for (j = 0; j < tbytes; j += 4) + /* + * tbytes may not be multiple of words. Make sure we don't read out of + * the boundary and stop at last word. + */ + for (j = 0; (j + 3) < tbytes; j += 4) oob_reg_write(ctrl, j, (oob[j + 0] << 24) | (oob[j + 1] << 16) | (oob[j + 2] << 8) | (oob[j + 3] << 0)); + + /* handle the remaing bytes */ + while (j < tbytes) + plast[k++] = oob[j++]; + + if (tbytes & 0x3) + oob_reg_write(ctrl, (tbytes & ~0x3), (__force u32)cpu_to_be32(last)); + return tbytes; } @@ -1781,7 +1863,7 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, { struct brcmnand_host *host = nand_get_controller_data(chip); struct brcmnand_controller *ctrl = host->ctrl; - int i, j, ret = 0; + int i, ret = 0; brcmnand_clear_ecc_addr(ctrl); @@ -1794,8 +1876,8 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, if (likely(buf)) { brcmnand_soc_data_bus_prepare(ctrl->soc, false); - for (j = 0; j < FC_WORDS; j++, buf++) - *buf = brcmnand_read_fc(ctrl, j); + brcmnand_read_data_bus(ctrl, ctrl->nand_fc, buf, FC_WORDS); + buf += FC_WORDS; brcmnand_soc_data_bus_unprepare(ctrl->soc, false); } @@ -2225,7 +2307,7 @@ static int brcmnand_set_cfg(struct brcmnand_host *host, tmp &= ~brcmnand_ecc_level_mask(ctrl); tmp &= ~brcmnand_spare_area_mask(ctrl); if (ctrl->nand_version >= 0x0302) { - tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; + tmp |= cfg->ecc_level << ctrl->ecc_level_shift; tmp |= cfg->spare_area_size; } nand_writereg(ctrl, acc_control_offs, tmp); @@ -2274,15 +2356,38 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) { struct mtd_info *mtd = nand_to_mtd(&host->chip); struct nand_chip *chip = &host->chip; + struct nand_device *nanddev = mtd_to_nanddev(mtd); + struct nand_memory_organization *memorg = nanddev_get_memorg(nanddev); struct brcmnand_controller *ctrl = host->ctrl; struct brcmnand_cfg *cfg = &host->hwcfg; - char msg[128]; u32 offs, tmp, oob_sector; + bool use_strap = false; + char msg[128]; int ret; memset(cfg, 0, sizeof(*cfg)); #ifndef __UBOOT__ + use_strap = of_property_read_bool(nand_get_flash_node(chip), + "brcm,nand-ecc-use-strap"): +#else + use_strap = ofnode_read_bool(nand_get_flash_node(chip), + "brcm,nand-ecc-use-strap"); +#endif /* __UBOOT__ */ + /* + * Either nand-ecc-xxx or brcm,nand-ecc-use-strap can be set. Error out + * if both exist. + */ + if (chip->ecc.strength && use_strap) { + dev_err(ctrl->dev, + "ECC strap and DT ECC configuration properties are mutually exclusive\n"); + return -EINVAL; + } + + if (use_strap) + brcmnand_get_ecc_settings(host, chip); + +#ifndef __UBOOT__ ret = of_property_read_u32(nand_get_flash_node(chip), "brcm,nand-oob-sector-size", &oob_sector); @@ -2291,20 +2396,25 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) "brcm,nand-oob-sector-size", &oob_sector); #endif /* __UBOOT__ */ + if (ret) { - /* Use detected size */ - cfg->spare_area_size = mtd->oobsize / - (mtd->writesize >> FC_SHIFT); + if (use_strap) + cfg->spare_area_size = brcmnand_get_spare_size(host); + else + /* Use detected size */ + cfg->spare_area_size = mtd->oobsize / + (mtd->writesize >> FC_SHIFT); } else { cfg->spare_area_size = oob_sector; } if (cfg->spare_area_size > ctrl->max_oob) cfg->spare_area_size = ctrl->max_oob; /* - * Set oobsize to be consistent with controller's spare_area_size, as - * the rest is inaccessible. + * Set mtd and memorg oobsize to be consistent with controller's + * spare_area_size, as the rest is inaccessible. */ mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); + memorg->oobsize = mtd->oobsize; cfg->device_size = mtd->size; cfg->block_size = mtd->erasesize; @@ -2796,8 +2906,17 @@ int brcmnand_probe(struct udevice *dev, struct brcmnand_soc *soc) /* Disable XOR addressing */ brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0); + /* Check if the board connects the WP pin */ +#ifndef __UBOOT__ + if (of_property_read_bool(dn, "brcm,wp-not-connected")) +#else + if (dev_read_bool(ctrl->dev, "brcm,wp-not-connected")) +#endif /* __UBOOT__ */ + wp_on = 0; + /* Read the write-protect configuration in the device tree */ - wp_on = dev_read_u32_default(dev, "write-protect", wp_on); + if (dev_read_bool(ctrl->dev, "write-protect")) + wp_on = dev_read_u32_default(dev, "write-protect", wp_on); if (ctrl->features & BRCMNAND_HAS_WP) { /* Permanently disable write protection */ diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h index 6946a62b067..3a1d6047136 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.h +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h @@ -11,6 +11,8 @@ struct brcmnand_soc { void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare, bool is_param); + void (*read_data_bus)(struct brcmnand_soc *soc, void __iomem *flash_cache, + u32 *buffer, int fc_words); void *ctrl; }; diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 12499a79478..28c851f103b 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -39,6 +39,7 @@ #define NFC_CMD_RB BIT(20) #define NFC_CMD_SCRAMBLER_ENABLE BIT(19) #define NFC_CMD_SCRAMBLER_DISABLE 0 +#define NFC_CMD_SHORTMODE_ENABLE 1 #define NFC_CMD_SHORTMODE_DISABLE 0 #define NFC_CMD_RB_INT BIT(14) #define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16)) @@ -77,6 +78,8 @@ #define DMA_DIR(dir) ((dir) ? NFC_CMD_N2M : NFC_CMD_M2N) +#define NFC_SHORT_MODE_ECC_SZ 384 + #define ECC_CHECK_RETURN_FF -1 #define NAND_CE0 (0xe << 10) @@ -140,6 +143,8 @@ struct meson_nfc_nand_chip { struct list_head node; struct nand_chip nand; + u32 boot_pages; + u32 boot_page_step; u32 bch_mode; u8 *data_buf; @@ -228,28 +233,49 @@ static void meson_nfc_cmd_seed(const struct meson_nfc *nfc, u32 seed) nfc->reg_base + NFC_REG_CMD); } -static void meson_nfc_cmd_access(struct nand_chip *nand, bool raw, bool dir, - int scrambler) +static int meson_nfc_is_boot_page(struct nand_chip *nand, int page) +{ + const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); + + return (nand->options & NAND_IS_BOOT_MEDIUM) && + !(page % meson_chip->boot_page_step) && + (page < meson_chip->boot_pages); +} + +static void meson_nfc_cmd_access(struct nand_chip *nand, bool raw, bool dir, int page) { + const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); struct mtd_info *mtd = nand_to_mtd(nand); const struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd)); - const struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); - u32 bch = meson_chip->bch_mode, cmd; int len = mtd->writesize, pagesize, pages; + unsigned int scrambler; + u32 cmd; - pagesize = nand->ecc.size; + if (nand->options & NAND_NEED_SCRAMBLING) + scrambler = NFC_CMD_SCRAMBLER_ENABLE; + else + scrambler = NFC_CMD_SCRAMBLER_DISABLE; if (raw) { len = mtd->writesize + mtd->oobsize; cmd = len | scrambler | DMA_DIR(dir); - writel(cmd, nfc->reg_base + NFC_REG_CMD); - return; - } + } else if (meson_nfc_is_boot_page(nand, page)) { + pagesize = NFC_SHORT_MODE_ECC_SZ >> 3; + pages = mtd->writesize / 512; + + scrambler = NFC_CMD_SCRAMBLER_ENABLE; + cmd = CMDRWGEN(DMA_DIR(dir), scrambler, NFC_ECC_BCH8_1K, + NFC_CMD_SHORTMODE_ENABLE, pagesize, pages); + } else { + pagesize = nand->ecc.size >> 3; + pages = len / nand->ecc.size; - pages = len / nand->ecc.size; + cmd = CMDRWGEN(DMA_DIR(dir), scrambler, meson_chip->bch_mode, + NFC_CMD_SHORTMODE_DISABLE, pagesize, pages); + } - cmd = CMDRWGEN(DMA_DIR(dir), scrambler, bch, - NFC_CMD_SHORTMODE_DISABLE, pagesize, pages); + if (scrambler == NFC_CMD_SCRAMBLER_ENABLE) + meson_nfc_cmd_seed(nfc, page); writel(cmd, nfc->reg_base + NFC_REG_CMD); } @@ -565,14 +591,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand, return ret; } - if (nand->options & NAND_NEED_SCRAMBLING) { - meson_nfc_cmd_seed(nfc, page); - meson_nfc_cmd_access(nand, raw, DIRWRITE, - NFC_CMD_SCRAMBLER_ENABLE); - } else { - meson_nfc_cmd_access(nand, raw, DIRWRITE, - NFC_CMD_SCRAMBLER_DISABLE); - } + meson_nfc_cmd_access(nand, raw, DIRWRITE, page); cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG; writel(cmd, nfc->reg_base + NFC_REG_CMD); @@ -643,14 +662,7 @@ static int meson_nfc_read_page_sub(struct nand_chip *nand, if (ret) return ret; - if (nand->options & NAND_NEED_SCRAMBLING) { - meson_nfc_cmd_seed(nfc, page); - meson_nfc_cmd_access(nand, raw, DIRREAD, - NFC_CMD_SCRAMBLER_ENABLE); - } else { - meson_nfc_cmd_access(nand, raw, DIRREAD, - NFC_CMD_SCRAMBLER_DISABLE); - } + meson_nfc_cmd_access(nand, raw, DIRREAD, page); meson_nfc_wait_dma_finish(nfc); meson_nfc_check_ecc_pages_valid(nfc, nand, raw); @@ -1137,6 +1149,24 @@ static int meson_nfc_nand_chip_init(struct udevice *dev, struct meson_nfc *nfc, goto err_chip_buf_free; } + if (nand->options & NAND_IS_BOOT_MEDIUM) { + ret = ofnode_read_u32(node, "amlogic,boot-pages", + &meson_chip->boot_pages); + if (ret) { + dev_err(dev, "could not retrieve 'amlogic,boot-pages' property: %d", + ret); + goto err_chip_buf_free; + } + + ret = ofnode_read_u32(node, "amlogic,boot-page-step", + &meson_chip->boot_page_step); + if (ret) { + dev_err(dev, "could not retrieve 'amlogic,boot-page-step' property: %d", + ret); + goto err_chip_buf_free; + } + } + ret = nand_register(0, mtd); if (ret) { dev_err(dev, "'nand_register()' failed: %d\n", ret); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 4401bdcdb90..0545c23e268 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4454,6 +4454,9 @@ static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, ofnode nod if (ret == 16) chip->options |= NAND_BUSWIDTH_16; + if (ofnode_read_bool(node, "nand-is-boot-medium")) + chip->options |= NAND_IS_BOOT_MEDIUM; + if (ofnode_read_bool(node, "nand-on-flash-bbt")) chip->bbt_options |= NAND_BBT_USE_FLASH; diff --git a/drivers/mtd/nand/raw/omap_gpmc.c b/drivers/mtd/nand/raw/omap_gpmc.c index 92a92ad63a0..a36e2a148cc 100644 --- a/drivers/mtd/nand/raw/omap_gpmc.c +++ b/drivers/mtd/nand/raw/omap_gpmc.c @@ -1188,7 +1188,10 @@ static int gpmc_nand_probe(struct udevice *dev) return ret; base = devm_ioremap(dev, res.start, resource_size(&res)); - gpmc_nand_init(nand, base); + ret = gpmc_nand_init(nand, base); + if (ret) + return ret; + mtd->dev = dev; nand_set_flash_node(nand, dev_ofnode(dev)); diff --git a/drivers/mtd/nvmxip/nvmxip-uclass.c b/drivers/mtd/nvmxip/nvmxip-uclass.c index 254f04e0b99..d18bd0e3d6b 100644 --- a/drivers/mtd/nvmxip/nvmxip-uclass.c +++ b/drivers/mtd/nvmxip/nvmxip-uclass.c @@ -47,19 +47,13 @@ int nvmxip_probe(struct udevice *udev) return ret; } - log_info("[%s]: the block device %s ready for use\n", udev->name, bdev_name); + log_debug("[%s]: the block device %s ready for use\n", udev->name, + bdev_name); return 0; } -static int nvmxip_post_bind(struct udevice *udev) -{ - dev_or_flags(udev, DM_FLAG_PROBE_AFTER_BIND); - return 0; -} - UCLASS_DRIVER(nvmxip) = { .name = "nvmxip", .id = UCLASS_NVMXIP, - .post_bind = nvmxip_post_bind, }; diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig index bedc4e970e4..63b0fd899fd 100644 --- a/drivers/mtd/spi/Kconfig +++ b/drivers/mtd/spi/Kconfig @@ -2,7 +2,7 @@ menu "SPI Flash Support" config DM_SPI_FLASH bool "Enable Driver Model for SPI flash" - depends on DM && DM_SPI + depends on DM_SPI imply SPI_FLASH help Enable driver model for SPI flash. This SPI flash interface diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c index 2d5a16bf6a2..e5ebc3479fb 100644 --- a/drivers/mtd/spi/sandbox.c +++ b/drivers/mtd/spi/sandbox.c @@ -138,7 +138,7 @@ static int sandbox_sf_probe(struct udevice *dev) return ret; } slave_plat = dev_get_parent_plat(dev); - cs = slave_plat->cs; + cs = slave_plat->cs[0]; debug("found at cs %d\n", cs); if (!pdata->filename) { diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index e126fa37c16..fcbcfe3587b 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -44,6 +44,12 @@ #define DEFAULT_READY_WAIT_JIFFIES (40UL * HZ) +/* + * For full-chip erase, calibrated to a 2MB flash (M25P16); should be scaled up + * for larger flash + */ +#define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) + #define ROUND_UP_TO(x, y) (((x) + (y) - 1) / (y) * (y)) struct sfdp_parameter_header { @@ -461,8 +467,9 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, } /* - * Read the status register, returning its value in the location - * Return the status register value. + * Return the status register value. If the chip is parallel, then the + * read will be striped, so we should read 2 bytes to get the sr + * register value from both of the parallel chips. * Returns negative if error occurred. */ static int read_sr(struct spi_nor *nor) @@ -494,18 +501,29 @@ static int read_sr(struct spi_nor *nor) if (spi_nor_protocol_is_dtr(nor->reg_proto)) op.data.nbytes = 2; - ret = spi_nor_read_write_reg(nor, &op, val); - if (ret < 0) { - pr_debug("error %d reading SR\n", (int)ret); - return ret; + if (nor->flags & SNOR_F_HAS_PARALLEL) { + op.data.nbytes = 2; + ret = spi_nor_read_write_reg(nor, &op, &val[0]); + if (ret < 0) { + pr_debug("error %d reading SR\n", (int)ret); + return ret; + } + val[0] |= val[1]; + } else { + ret = spi_nor_read_write_reg(nor, &op, &val[0]); + if (ret < 0) { + pr_debug("error %d reading SR\n", (int)ret); + return ret; + } } - return *val; + return val[0]; } /* - * Read the flag status register, returning its value in the location - * Return the status register value. + * Return the flag status register value. If the chip is parallel, then + * the read will be striped, so we should read 2 bytes to get the fsr + * register value from both of the parallel chips. * Returns negative if error occurred. */ static int read_fsr(struct spi_nor *nor) @@ -537,13 +555,23 @@ static int read_fsr(struct spi_nor *nor) if (spi_nor_protocol_is_dtr(nor->reg_proto)) op.data.nbytes = 2; - ret = spi_nor_read_write_reg(nor, &op, val); - if (ret < 0) { - pr_debug("error %d reading FSR\n", ret); - return ret; + if (nor->flags & SNOR_F_HAS_PARALLEL) { + op.data.nbytes = 2; + ret = spi_nor_read_write_reg(nor, &op, &val[0]); + if (ret < 0) { + pr_debug("error %d reading SR\n", (int)ret); + return ret; + } + val[0] &= val[1]; + } else { + ret = spi_nor_read_write_reg(nor, &op, &val[0]); + if (ret < 0) { + pr_debug("error %d reading FSR\n", ret); + return ret; + } } - return *val; + return val[0]; } /* @@ -567,6 +595,24 @@ static int read_cr(struct spi_nor *nor) } #endif +/** + * read_sr3() - Read status register 3 unique to newer Winbond flashes + * @nor: pointer to a 'struct spi_nor' + */ +static int read_sr3(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_RDSR3, &val, 1); + if (ret < 0) { + dev_dbg(nor->dev, "error %d reading SR3\n", ret); + return ret; + } + + return val; +} + /* * Write status register 1 byte * Returns negative if error occurred. @@ -577,6 +623,17 @@ static int write_sr(struct spi_nor *nor, u8 val) return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1); } +/** + * write_sr3() - Write status register 3 unique to newer Winbond flashes + * @nor: pointer to a 'struct spi_nor' + * @val: value to be written into SR3 + */ +static int write_sr3(struct spi_nor *nor, u8 val) +{ + nor->cmd_buf[0] = val; + return nor->write_reg(nor, SPINOR_OP_WRSR3, nor->cmd_buf, 1); +} + /* * Set write enable latch with Write Enable command. * Returns negative if error occurred. @@ -662,12 +719,17 @@ static u8 spi_nor_convert_3to4_erase(u8 opcode) static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, const struct flash_info *info) { + bool shift = 0; + + if (nor->flags & SNOR_F_HAS_PARALLEL) + shift = 1; + /* Do some manufacturer fixups first */ switch (JEDEC_MFR(info)) { case SNOR_MFR_SPANSION: /* No small sector erase for 4-byte command set */ nor->erase_opcode = SPINOR_OP_SE; - nor->mtd.erasesize = info->sector_size; + nor->mtd.erasesize = info->sector_size << shift; break; default: @@ -855,6 +917,20 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor) DEFAULT_READY_WAIT_JIFFIES); } +static int spi_nor_erase_chip_wait_till_ready(struct spi_nor *nor, unsigned long size) +{ + /* + * Scale the timeout linearly with the size of the flash, with + * a minimum calibrated to an old 2MB flash. We could try to + * pull these from CFI/SFDP, but these values should be good + * enough for now. + */ + unsigned long timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES, + CHIP_ERASE_2MB_READY_WAIT_JIFFIES * + (unsigned long)(size / SZ_2M)); + return spi_nor_wait_till_ready_with_timeout(nor, timeout); +} + #ifdef CONFIG_SPI_FLASH_BAR /* * This "clean_bar" is necessary in a situation when one was accessing @@ -881,12 +957,32 @@ static int clean_bar(struct spi_nor *nor) static int write_bar(struct spi_nor *nor, u32 offset) { - u8 cmd, bank_sel; + u8 cmd, bank_sel, upage_curr; int ret; + struct mtd_info *mtd = &nor->mtd; + + /* Wait until previous write command is finished */ + if (spi_nor_wait_till_ready(nor)) + return 1; + + if (nor->flags & (SNOR_F_HAS_PARALLEL | SNOR_F_HAS_STACKED) && + mtd->size <= SZ_32M) + return 0; + + if (mtd->size <= SZ_16M) + return 0; + + offset = offset % (u32)mtd->size; + bank_sel = offset >> 24; + + upage_curr = nor->spi->flags & SPI_XFER_U_PAGE; + + if (!(nor->flags & SNOR_F_HAS_STACKED) && bank_sel == nor->bank_curr) + return 0; + else if (upage_curr == nor->upage_prev && bank_sel == nor->bank_curr) + return 0; - bank_sel = offset / SZ_16M; - if (bank_sel == nor->bank_curr) - goto bar_end; + nor->upage_prev = upage_curr; cmd = nor->bank_write_cmd; write_enable(nor); @@ -896,15 +992,19 @@ static int write_bar(struct spi_nor *nor, u32 offset) return ret; } -bar_end: nor->bank_curr = bank_sel; - return nor->bank_curr; + + return write_disable(nor); } static int read_bar(struct spi_nor *nor, const struct flash_info *info) { u8 curr_bank = 0; int ret; + struct mtd_info *mtd = &nor->mtd; + + if (mtd->size <= SZ_16M) + return 0; switch (JEDEC_MFR(info)) { case SNOR_MFR_SPANSION: @@ -916,15 +1016,30 @@ static int read_bar(struct spi_nor *nor, const struct flash_info *info) nor->bank_write_cmd = SPINOR_OP_WREAR; } + if (nor->flags & SNOR_F_HAS_PARALLEL) + nor->spi->flags |= SPI_XFER_LOWER; + ret = nor->read_reg(nor, nor->bank_read_cmd, - &curr_bank, 1); + &curr_bank, 1); if (ret) { debug("SF: fail to read bank addr register\n"); return ret; } nor->bank_curr = curr_bank; - return 0; + // Make sure both chips use the same BAR + if (nor->flags & SNOR_F_HAS_PARALLEL) { + write_enable(nor); + ret = nor->write_reg(nor, nor->bank_write_cmd, &curr_bank, 1); + if (ret) + return ret; + + ret = write_disable(nor); + if (ret) + return ret; + } + + return ret; } #endif @@ -988,8 +1103,8 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) { struct spi_nor *nor = mtd_to_spi_nor(mtd); + u32 addr, len, rem, offset, max_size; bool addr_known = false; - u32 addr, len, rem; int ret, err; dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, @@ -1003,6 +1118,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) addr = instr->addr; len = instr->len; + max_size = instr->len; instr->state = MTD_ERASING; addr_known = true; @@ -1014,6 +1130,18 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) ret = -EINTR; goto erase_err; } + offset = addr; + if (nor->flags & SNOR_F_HAS_PARALLEL) + offset /= 2; + + if (nor->flags & SNOR_F_HAS_STACKED) { + if (offset >= (mtd->size / 2)) { + offset = offset - (mtd->size / 2); + nor->spi->flags |= SPI_XFER_U_PAGE; + } else { + nor->spi->flags &= ~SPI_XFER_U_PAGE; + } + } #ifdef CONFIG_SPI_FLASH_BAR ret = write_bar(nor, addr); if (ret < 0) @@ -1035,7 +1163,13 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) addr += ret; len -= ret; - ret = spi_nor_wait_till_ready(nor); + if (max_size == mtd->size && + !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { + ret = spi_nor_erase_chip_wait_till_ready(nor, mtd->size); + } else { + ret = spi_nor_wait_till_ready(nor); + } + if (ret) goto erase_err; } @@ -1419,6 +1553,9 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) u8 id[SPI_NOR_MAX_ID_LEN]; const struct flash_info *info; + if (nor->flags & SNOR_F_HAS_PARALLEL) + nor->spi->flags |= SPI_XFER_LOWER; + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); if (tmp < 0) { dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); @@ -1443,28 +1580,67 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, { struct spi_nor *nor = mtd_to_spi_nor(mtd); int ret; + loff_t offset = from; + u32 read_len = 0; + u32 rem_bank_len = 0; + u8 bank; + bool is_ofst_odd = false; dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); + if ((nor->flags & SNOR_F_HAS_PARALLEL) && (offset & 1)) { + /* We can hit this case when we use file system like ubifs */ + from--; + len++; + is_ofst_odd = true; + } + while (len) { - loff_t addr = from; - size_t read_len = len; + if (nor->addr_width == 3) { + if (nor->flags & SNOR_F_HAS_PARALLEL) { + bank = (u32)from / (SZ_16M << 0x01); + rem_bank_len = ((SZ_16M << 0x01) * + (bank + 1)) - from; + } else { + bank = (u32)from / SZ_16M; + rem_bank_len = (SZ_16M * (bank + 1)) - from; + } + } + offset = from; -#ifdef CONFIG_SPI_FLASH_BAR - u32 remain_len; + if (nor->flags & SNOR_F_HAS_STACKED) { + if (offset >= (mtd->size / 2)) { + offset = offset - (mtd->size / 2); + nor->spi->flags |= SPI_XFER_U_PAGE; + } else { + nor->spi->flags &= ~SPI_XFER_U_PAGE; + } + } - ret = write_bar(nor, addr); - if (ret < 0) - return log_ret(ret); - remain_len = (SZ_16M * (nor->bank_curr + 1)) - addr; + if (nor->flags & SNOR_F_HAS_PARALLEL) + offset /= 2; - if (len < remain_len) + if (nor->addr_width == 3) { +#ifdef CONFIG_SPI_FLASH_BAR + ret = write_bar(nor, offset); + if (ret < 0) + return log_ret(ret); +#endif + } + + if (len < rem_bank_len) read_len = len; else - read_len = remain_len; -#endif + read_len = rem_bank_len; - ret = nor->read(nor, addr, read_len, buf); + if (read_len == 0) + return -EIO; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto read_err; + + ret = nor->read(nor, offset, read_len, buf); if (ret == 0) { /* We shouldn't see 0-length reads */ ret = -EIO; @@ -1473,8 +1649,15 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, if (ret < 0) goto read_err; - *retlen += ret; - buf += ret; + if (is_ofst_odd == true) { + memmove(buf, (buf + 1), (len - 1)); + *retlen += (ret - 1); + buf += ret - 1; + is_ofst_odd = false; + } else { + *retlen += ret; + buf += ret; + } from += ret; len -= ret; } @@ -1769,6 +1952,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, struct spi_nor *nor = mtd_to_spi_nor(mtd); size_t page_offset, page_remain, i; ssize_t ret; + u32 offset; #ifdef CONFIG_SPI_FLASH_SST /* sst nor chips use AAI word program */ @@ -1778,6 +1962,27 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + if (!len) + return 0; + + /* + * Cannot write to odd offset in parallel mode, + * so write 2 bytes first + */ + if ((nor->flags & SNOR_F_HAS_PARALLEL) && (to & 1)) { + u8 two[2] = {0xff, buf[0]}; + size_t local_retlen; + + ret = spi_nor_write(mtd, to & ~1, 2, &local_retlen, two); + if (ret < 0) + return ret; + + *retlen += 1; /* We've written only one actual byte */ + ++buf; + --len; + ++to; + } + for (i = 0; i < len; ) { ssize_t written; loff_t addr = to + i; @@ -1795,18 +2000,35 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, page_offset = do_div(aux, nor->page_size); } + offset = (to + i); + if (nor->flags & SNOR_F_HAS_PARALLEL) + offset /= 2; + + if (nor->flags & SNOR_F_HAS_STACKED) { + if (offset >= (mtd->size / 2)) { + offset = offset - (mtd->size / 2); + nor->spi->flags |= SPI_XFER_U_PAGE; + } else { + nor->spi->flags &= ~SPI_XFER_U_PAGE; + } + } + + if (nor->addr_width == 3) { +#ifdef CONFIG_SPI_FLASH_BAR + ret = write_bar(nor, offset); + if (ret < 0) + return ret; +#endif + } /* the size of data remaining on the first page */ page_remain = min_t(size_t, nor->page_size - page_offset, len - i); -#ifdef CONFIG_SPI_FLASH_BAR - ret = write_bar(nor, addr); - if (ret < 0) - return ret; -#endif + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto write_err; write_enable(nor); - /* * On DTR capable flashes like Micron Xcella the writes cannot * start or end at an odd address in DTR mode. So we need to @@ -1814,7 +2036,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, * address and end address are even. */ if (spi_nor_protocol_is_dtr(nor->write_proto) && - ((addr | page_remain) & 1)) { + ((offset | page_remain) & 1)) { u_char *tmp; size_t extra_bytes = 0; @@ -1825,10 +2047,10 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, } /* Prepend a 0xff byte if the start address is odd. */ - if (addr & 1) { + if (offset & 1) { tmp[0] = 0xff; memcpy(tmp + 1, buf + i, page_remain); - addr--; + offset--; page_remain++; extra_bytes++; } else { @@ -1836,13 +2058,13 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, } /* Append a 0xff byte if the end address is odd. */ - if ((addr + page_remain) & 1) { + if ((offset + page_remain) & 1) { tmp[page_remain + extra_bytes] = 0xff; extra_bytes++; page_remain++; } - ret = nor->write(nor, addr, page_remain, tmp); + ret = nor->write(nor, offset, page_remain, tmp); kfree(tmp); @@ -1855,7 +2077,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, */ written = ret - extra_bytes; } else { - ret = nor->write(nor, addr, page_remain, buf + i); + ret = nor->write(nor, offset, page_remain, buf + i); if (ret < 0) goto write_err; written = ret; @@ -1864,6 +2086,11 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, ret = spi_nor_wait_till_ready(nor); if (ret) goto write_err; + + ret = write_disable(nor); + if (ret) + goto write_err; + *retlen += written; i += written; } @@ -1904,6 +2131,10 @@ static int macronix_quad_enable(struct spi_nor *nor) if (ret) return ret; + ret = write_disable(nor); + if (ret) + return ret; + ret = read_sr(nor); if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) { dev_err(nor->dev, "Macronix Quad bit not set\n"); @@ -1965,7 +2196,7 @@ static int spansion_quad_enable_volatile(struct spi_nor *nor, u32 addr_base, return -EINVAL; } - return 0; + return write_disable(nor); } #endif @@ -2141,6 +2372,10 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, nor->read_dummy = 8; while (len) { + /* Both chips are identical, so should be the SFDP data */ + if (nor->flags & SNOR_F_HAS_PARALLEL) + nor->spi->flags |= SPI_XFER_LOWER; + ret = nor->read(nor, addr, len, (u8 *)buf); if (!ret || ret > len) { ret = -EIO; @@ -2835,6 +3070,13 @@ static int spi_nor_init_params(struct spi_nor *nor, const struct flash_info *info, struct spi_nor_flash_parameter *params) { +#if CONFIG_IS_ENABLED(DM_SPI) && CONFIG_IS_ENABLED(SPI_ADVANCE) + struct udevice *dev = nor->spi->dev; + u64 flash_size[SNOR_FLASH_CNT_MAX] = {0}; + u32 idx = 0, i = 0; + int rc; +#endif + /* Set legacy flash parameters as default. */ memset(params, 0, sizeof(*params)); @@ -2952,7 +3194,62 @@ static int spi_nor_init_params(struct spi_nor *nor, memcpy(params, &sfdp_params, sizeof(*params)); } } +#if CONFIG_IS_ENABLED(DM_SPI) && CONFIG_IS_ENABLED(SPI_ADVANCE) + /* + * The flashes that are connected in stacked mode should be of same make. + * Except the flash size all other properties are identical for all the + * flashes connected in stacked mode. + * The flashes that are connected in parallel mode should be identical. + */ + while (i < SNOR_FLASH_CNT_MAX) { + rc = ofnode_read_u64_index(dev_ofnode(dev), "stacked-memories", + idx, &flash_size[i]); + if (rc == -EINVAL) { + break; + } else if (rc == -EOVERFLOW) { + idx++; + } else { + idx++; + i++; + if (!(nor->flags & SNOR_F_HAS_STACKED)) + nor->flags |= SNOR_F_HAS_STACKED; + if (!(nor->spi->flags & SPI_XFER_STACKED)) + nor->spi->flags |= SPI_XFER_STACKED; + } + } + i = 0; + idx = 0; + while (i < SNOR_FLASH_CNT_MAX) { + rc = ofnode_read_u64_index(dev_ofnode(dev), "parallel-memories", + idx, &flash_size[i]); + if (rc == -EINVAL) { + break; + } else if (rc == -EOVERFLOW) { + idx++; + } else { + idx++; + i++; + if (!(nor->flags & SNOR_F_HAS_PARALLEL)) + nor->flags |= SNOR_F_HAS_PARALLEL; + } + } + + if (nor->flags & (SNOR_F_HAS_STACKED | SNOR_F_HAS_PARALLEL)) { + params->size = 0; + for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) + params->size += flash_size[idx]; + } + /* + * In parallel-memories the erase operation is + * performed on both the flashes simultaneously + * so, double the erasesize. + */ + if (nor->flags & SNOR_F_HAS_PARALLEL) { + nor->mtd.erasesize <<= 1; + params->page_size <<= 1; + } +#endif spi_nor_post_sfdp_fixups(nor, params); return 0; @@ -3267,16 +3564,54 @@ static int spi_nor_select_erase(struct spi_nor *nor, /* prefer "small sector" erase if possible */ if (info->flags & SECT_4K) { nor->erase_opcode = SPINOR_OP_BE_4K; - mtd->erasesize = 4096; + /* + * In parallel-memories the erase operation is + * performed on both the flashes simultaneously + * so, double the erasesize. + */ + if (nor->flags & SNOR_F_HAS_PARALLEL) + mtd->erasesize = 4096 * 2; + else + mtd->erasesize = 4096; } else if (info->flags & SECT_4K_PMC) { nor->erase_opcode = SPINOR_OP_BE_4K_PMC; - mtd->erasesize = 4096; + /* + * In parallel-memories the erase operation is + * performed on both the flashes simultaneously + * so, double the erasesize. + */ + if (nor->flags & SNOR_F_HAS_PARALLEL) + mtd->erasesize = 4096 * 2; + else + mtd->erasesize = 4096; } else #endif { nor->erase_opcode = SPINOR_OP_SE; - mtd->erasesize = info->sector_size; + /* + * In parallel-memories the erase operation is + * performed on both the flashes simultaneously + * so, double the erasesize. + */ + if (nor->flags & SNOR_F_HAS_PARALLEL) + mtd->erasesize = info->sector_size * 2; + else + mtd->erasesize = info->sector_size; + } + + if ((JEDEC_MFR(info) == SNOR_MFR_SST) && info->flags & SECT_4K) { + nor->erase_opcode = SPINOR_OP_BE_4K; + /* + * In parallel-memories the erase operation is + * performed on both the flashes simultaneously + * so, double the erasesize. + */ + if (nor->flags & SNOR_F_HAS_PARALLEL) + mtd->erasesize = 4096 * 2; + else + mtd->erasesize = 4096; } + return 0; } @@ -3912,6 +4247,9 @@ static int spi_nor_init(struct spi_nor *nor) { int err; + if (nor->flags & SNOR_F_HAS_PARALLEL) + nor->spi->flags |= SPI_NOR_ENABLE_MULTI_CS; + err = spi_nor_octal_dtr_enable(nor); if (err) { dev_dbg(nor->dev, "Octal DTR mode not supported\n"); @@ -3930,6 +4268,24 @@ static int spi_nor_init(struct spi_nor *nor) write_enable(nor); write_sr(nor, 0); spi_nor_wait_till_ready(nor); + + /* + * Some Winbond SPI NORs have special SR3 register which is + * used among other things to control whether non-standard + * "Individual Block/Sector Write Protection" (WPS bit) + * locking scheme is activated. This non-standard locking + * scheme is not supported by either U-Boot or Linux SPI + * NOR stack so make sure it is disabled, otherwise the + * SPI NOR may appear locked for no obvious reason. + */ + if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) { + err = read_sr3(nor); + if (err > 0 && err & SR3_WPS) { + write_enable(nor); + write_sr3(nor, err & ~SR3_WPS); + write_disable(nor); + } + } } if (nor->quad_enable) { @@ -4078,6 +4434,7 @@ int spi_nor_scan(struct spi_nor *nor) struct spi_slave *spi = nor->spi; int ret; int cfi_mtd_nb = 0; + bool shift = 0; #ifdef CONFIG_FLASH_CFI_MTD cfi_mtd_nb = CFI_FLASH_BANKS; @@ -4215,7 +4572,9 @@ int spi_nor_scan(struct spi_nor *nor) nor->addr_width = 3; } - if (nor->addr_width == 3 && mtd->size > SZ_16M) { + if (nor->flags & (SNOR_F_HAS_PARALLEL | SNOR_F_HAS_STACKED)) + shift = 1; + if (nor->addr_width == 3 && (mtd->size >> shift) > SZ_16M) { #ifndef CONFIG_SPI_FLASH_BAR /* enable 4-byte addressing if the device exceeds 16MiB */ nor->addr_width = 4; @@ -4225,6 +4584,7 @@ int spi_nor_scan(struct spi_nor *nor) #else /* Configure the BAR - discover bank cmds and read current bank */ nor->addr_width = 3; + set_4byte(nor, info, 0); ret = read_bar(nor, info); if (ret < 0) return ret; @@ -4242,6 +4602,14 @@ int spi_nor_scan(struct spi_nor *nor) if (ret) return ret; + if (nor->flags & SNOR_F_HAS_STACKED) { + nor->spi->flags |= SPI_XFER_U_PAGE; + ret = spi_nor_init(nor); + if (ret) + return ret; + nor->spi->flags &= ~SPI_XFER_U_PAGE; + } + nor->rdsr_dummy = params.rdsr_dummy; nor->rdsr_addr_nbytes = params.rdsr_addr_nbytes; nor->name = info->name; diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index 9693c179b7e..b954631ff76 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -258,7 +258,6 @@ const struct flash_info spi_nor_ids[] = { { INFO("mx25u6435f", 0xc22537, 0, 64 * 1024, 128, SECT_4K) }, { INFO("mx25l12805d", 0xc22018, 0, 64 * 1024, 256, SECT_4K) }, { INFO("mx25u12835f", 0xc22538, 0, 64 * 1024, 256, SECT_4K) }, - { INFO("mx25u25635f", 0xc22539, 0, 64 * 1024, 512, SECT_4K) }, { INFO("mx25u51245g", 0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { INFO("mx25l12855e", 0xc22618, 0, 64 * 1024, 256, 0) }, @@ -433,11 +432,6 @@ const struct flash_info spi_nor_ids[] = { { INFO("w25x05", 0xef3010, 0, 64 * 1024, 1, SECT_4K) }, { INFO("w25x40", 0xef3013, 0, 64 * 1024, 8, SECT_4K) }, { INFO("w25x16", 0xef3015, 0, 64 * 1024, 32, SECT_4K) }, - { - INFO("w25q16dw", 0xef6015, 0, 64 * 1024, 32, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, { INFO("w25x32", 0xef3016, 0, 64 * 1024, 64, SECT_4K) }, { INFO("w25q20cl", 0xef4012, 0, 64 * 1024, 4, SECT_4K) }, { INFO("w25q20bw", 0xef5012, 0, 64 * 1024, 4, SECT_4K) }, @@ -445,7 +439,8 @@ const struct flash_info spi_nor_ids[] = { { INFO("w25q32", 0xef4016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("w25q16dw", 0xef6015, 0, 64 * 1024, 32, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, { INFO("w25q32dw", 0xef6016, 0, 64 * 1024, 64, @@ -544,7 +539,11 @@ const struct flash_info spi_nor_ids[] = { }, { INFO("w25q80", 0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { INFO("w25q80bl", 0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { INFO("w25q16cl", 0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { + INFO("w25q16cl", 0xef4015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { INFO("w25q32bv", 0xef4016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("w25q64cv", 0xef4017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("w25q128", 0xef4018, 0, 64 * 1024, 256, diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 21750e1817b..9c6b15b8cb5 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -581,13 +581,11 @@ static int count_fastmap_pebs(struct ubi_attach_info *ai) struct ubi_ainf_peb *aeb; struct ubi_ainf_volume *av; struct rb_node *rb1, *rb2; - int n = 0; + int n; - list_for_each_entry(aeb, &ai->erase, u.list) - n++; + n = list_count_nodes(&ai->erase); - list_for_each_entry(aeb, &ai->free, u.list) - n++; + n += list_count_nodes(&ai->free); ubi_rb_for_each_entry(rb1, av, &ai->volumes, rb) ubi_rb_for_each_entry(rb2, aeb, &av->root, u.rb) |
