mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-05 04:36:13 +02:00
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:
commit
28dc47038e
@ -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 */
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user