Merge branch 'u-boot-nand-20241005' of https://gitlab.denx.de/u-boot/custodians/u-boot-nand-flash into next

These are a number of assorted upstream Linux fixes to the
BRCMNAND driver.

This patch set lowers the hamming distance between the Linux
and U-Boot drivers a bit as well, while we deviate quite
a bit it is still possible to bring fixes over thanks to
exercises like this.

The patches pass the pipeline CI:
https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/pipelines/22535
This commit is contained in:
Tom Rini 2024-10-05 11:18:38 -06:00
commit 28dc47038e
2 changed files with 166 additions and 45 deletions

View File

@ -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,14 +2356,37 @@ 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",
@ -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 */

View File

@ -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;
};