summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/altera_qspi.c4
-rw-r--r--drivers/mtd/cfi_flash.c36
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.c209
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.h2
-rw-r--r--drivers/mtd/nand/raw/meson_nand.c84
-rw-r--r--drivers/mtd/nand/raw/nand_base.c3
-rw-r--r--drivers/mtd/nand/raw/omap_gpmc.c5
-rw-r--r--drivers/mtd/nvmxip/nvmxip-uclass.c10
-rw-r--r--drivers/mtd/spi/Kconfig2
-rw-r--r--drivers/mtd/spi/sandbox.c2
-rw-r--r--drivers/mtd/spi/spi-nor-core.c480
-rw-r--r--drivers/mtd/spi/spi-nor-ids.c15
-rw-r--r--drivers/mtd/ubi/fastmap.c8
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)