project:build.sh: Added fastboot support; custom modifications to U-Boot and kernel implemented using patches.

project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between
the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door
for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more
usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp:
Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for
compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem.
sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device
tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately.
sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick
boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades.
sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for
fastboot.

Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
This commit is contained in:
luckfox-eng29
2024-08-21 10:05:47 +08:00
parent e79fd21975
commit 8f34c2760d
20902 changed files with 6567362 additions and 11248383 deletions

View File

@@ -8,18 +8,78 @@
#include <linux/slab.h>
#ifdef BBT_DEBUG
#define BBT_DBG pr_err
#define bbt_dbg pr_err
#else
#define BBT_DBG(args...)
#define bbt_dbg(args...)
#endif
#define BBT_VERSION_INVALID (0xFFFFFFFFU)
#define BBT_VERSION_BLOCK_ABNORMAL (BBT_VERSION_INVALID - 1)
#define BBT_VERSION_MAX (BBT_VERSION_INVALID - 8)
struct nanddev_bbt_info {
u8 pattern[4];
unsigned int version;
u32 hash;
};
static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
#if defined(BBT_DEBUG) && defined(BBT_DEBUG_DUMP)
static void bbt_dbg_hex(char *s, void *buf, u32 len)
{
print_hex_dump(KERN_WARNING, s, DUMP_PREFIX_OFFSET, 4, 4, buf, len, 0);
}
#endif
static u32 js_hash(u8 *buf, u32 len)
{
u32 hash = 0x47C6A7E6;
u32 i;
for (i = 0; i < len; i++)
hash ^= ((hash << 5) + buf[i] + (hash >> 2));
return hash;
}
static bool bbt_check_hash(u8 *buf, u32 len, u32 hash_cmp)
{
u32 hash;
/* compatible with no-hash version */
if (hash_cmp == 0 || hash_cmp == 0xFFFFFFFF)
return 1;
hash = js_hash(buf, len);
if (hash != hash_cmp)
return 0;
return 1;
}
static u32 bbt_nand_isbad_bypass(struct nand_device *nand, u32 block)
{
struct mtd_info *mtd = nanddev_to_mtd(nand);
struct nand_pos pos;
nanddev_bbt_set_block_status(nand, block, NAND_BBT_BLOCK_STATUS_UNKNOWN);
nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
return nanddev_isbad(nand, &pos);
}
/**
* nanddev_read_bbt() - Read the BBT (Bad Block Table)
* @nand: NAND device
* @block: bbt block address
* @update: true - get version and overwrite bbt.cache with new version;
* false - get bbt version only;
*
* Initialize the in-memory BBT.
*
* Return: 0 in case of success, a negative error code otherwise.
*/
static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
{
unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
@@ -30,7 +90,7 @@ static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
u8 *data_buf, *oob_buf;
struct nanddev_bbt_info *bbt_info;
struct mtd_oob_ops ops;
int bbt_page_num;
u32 bbt_page_num;
int ret = 0;
unsigned int version = 0;
@@ -40,7 +100,7 @@ static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
if (block >= nblocks)
return -EINVAL;
/* Aligned to page size, and even pages is better */
/* aligned to page size, and even pages is better */
bbt_page_num = (sizeof(struct nanddev_bbt_info) + nbytes +
mtd->writesize - 1) >> (ffs(mtd->writesize) - 1);
bbt_page_num = (bbt_page_num + 1) / 2 * 2;
@@ -64,29 +124,72 @@ static int nanddev_read_bbt(struct nand_device *nand, u32 block, bool update)
ops.ooblen = bbt_page_num * mtd->oobsize;
ops.ooboffs = 0;
/* store one entry for each block */
ret = mtd_read_oob(mtd, block * mtd->erasesize, &ops);
if (ret && ret != -EUCLEAN) {
pr_err("%s fail %d\n", __func__, ret);
ret = -EIO;
pr_err("read_bbt blk=%d fail=%d update=%d\n", block, ret, update);
ret = 0;
version = BBT_VERSION_BLOCK_ABNORMAL;
goto out;
} else {
ret = 0;
}
if (oob_buf[0] != 0xff && !memcmp(bbt_pattern, bbt_info->pattern, 4))
version = bbt_info->version;
/* bad block or good block without bbt */
if (memcmp(bbt_pattern, bbt_info->pattern, 4)) {
ret = 0;
goto out;
}
BBT_DBG("read_bbt from blk=%d tag=%d ver=%d\n", block, update, version);
/* good block with abnornal bbt */
if (oob_buf[0] == 0xff ||
!bbt_check_hash(data_buf, nbytes + sizeof(struct nanddev_bbt_info) - 4, bbt_info->hash)) {
pr_err("read_bbt check fail blk=%d ret=%d update=%d\n", block, ret, update);
ret = 0;
version = BBT_VERSION_BLOCK_ABNORMAL;
goto out;
}
/* good block with good bbt */
version = bbt_info->version;
bbt_dbg("read_bbt from blk=%d ver=%d update=%d\n", block, version, update);
if (update && version > nand->bbt.version) {
memcpy(nand->bbt.cache, data_buf, nbytes);
nand->bbt.version = version;
}
out:
kfree(oob_buf);
kfree(data_buf);
#if defined(BBT_DEBUG) && defined(BBT_DEBUG_DUMP)
bbt_dbg_hex("bbt", data_buf, nbytes + sizeof(struct nanddev_bbt_info));
if (version) {
u8 *temp_buf = kzalloc(bbt_page_num * mtd->writesize, GFP_KERNEL);
bool in_scan = nand->bbt.option & NANDDEV_BBT_SCANNED;
return ret < 0 ? -EIO : version;
if (!temp_buf)
goto out;
memcpy(temp_buf, nand->bbt.cache, nbytes);
memcpy(nand->bbt.cache, data_buf, nbytes);
if (!in_scan)
nand->bbt.option |= NANDDEV_BBT_SCANNED;
for (block = 0; block < nblocks; block++) {
ret = nanddev_bbt_get_block_status(nand, block);
if (ret != NAND_BBT_BLOCK_GOOD)
bbt_dbg("bad block[0x%x], ret=%d\n", block, ret);
}
if (!in_scan)
nand->bbt.option &= ~NANDDEV_BBT_SCANNED;
memcpy(nand->bbt.cache, temp_buf, nbytes);
kfree(temp_buf);
ret = 0;
}
#endif
out:
kfree(data_buf);
kfree(oob_buf);
return ret < 0 ? -EIO : (int)version;
}
static int nanddev_write_bbt(struct nand_device *nand, u32 block)
@@ -99,18 +202,18 @@ static int nanddev_write_bbt(struct nand_device *nand, u32 block)
u8 *data_buf, *oob_buf;
struct nanddev_bbt_info *bbt_info;
struct mtd_oob_ops ops;
int bbt_page_num;
int ret = 0;
u32 bbt_page_num;
int ret = 0, version;
struct nand_pos pos;
BBT_DBG("write_bbt to blk=%d ver=%d\n", block, nand->bbt.version);
bbt_dbg("write_bbt to blk=%d ver=%d\n", block, nand->bbt.version);
if (!nand->bbt.cache)
return -ENOMEM;
if (block >= nblocks)
return -EINVAL;
/* Aligned to page size, and even pages is better */
/* aligned to page size, and even pages is better */
bbt_page_num = (sizeof(struct nanddev_bbt_info) + nbytes +
mtd->writesize - 1) >> (ffs(mtd->writesize) - 1);
bbt_page_num = (bbt_page_num + 1) / 2 * 2;
@@ -130,7 +233,9 @@ static int nanddev_write_bbt(struct nand_device *nand, u32 block)
memcpy(data_buf, nand->bbt.cache, nbytes);
memcpy(bbt_info, bbt_pattern, 4);
bbt_info->version = nand->bbt.version;
bbt_info->hash = js_hash(data_buf, nbytes + sizeof(struct nanddev_bbt_info) - 4);
/* store one entry for each block */
nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
ret = nand->ops->erase(nand, &pos);
if (ret)
@@ -144,10 +249,23 @@ static int nanddev_write_bbt(struct nand_device *nand, u32 block)
ops.ooblen = bbt_page_num * mtd->oobsize;
ops.ooboffs = 0;
ret = mtd_write_oob(mtd, block * mtd->erasesize, &ops);
if (ret) {
nand->ops->erase(nand, &pos);
goto out;
}
version = nanddev_read_bbt(nand, block, false);
if (version != bbt_info->version) {
pr_err("bbt_write fail, blk=%d recheck fail %d-%d\n",
block, version, bbt_info->version);
nand->ops->erase(nand, &pos);
ret = -EIO;
} else {
ret = 0;
}
out:
kfree(oob_buf);
kfree(data_buf);
kfree(oob_buf);
return ret;
}
@@ -158,14 +276,30 @@ static int nanddev_bbt_format(struct nand_device *nand)
struct mtd_info *mtd = nanddev_to_mtd(nand);
struct nand_pos pos;
u32 start_block, block;
unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS);
unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block,
BITS_PER_LONG);
start_block = nblocks - NANDDEV_BBT_SCAN_MAXBLOCKS;
for (block = 0; block < nblocks; block++) {
nanddev_offs_to_pos(nand, block * mtd->erasesize, &pos);
if (nanddev_isbad(nand, &pos))
if (nanddev_isbad(nand, &pos)) {
if (bbt_nand_isbad_bypass(nand, 0)) {
memset(nand->bbt.cache, 0, nwords * sizeof(*nand->bbt.cache));
pr_err("bbt_format fail, test good block %d fail\n", 0);
return -EIO;
}
if (!bbt_nand_isbad_bypass(nand, block)) {
memset(nand->bbt.cache, 0, nwords * sizeof(*nand->bbt.cache));
pr_err("bbt_format fail, test bad block %d fail\n", block);
return -EIO;
}
nanddev_bbt_set_block_status(nand, block,
NAND_BBT_BLOCK_FACTORY_BAD);
}
}
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
@@ -197,16 +331,34 @@ int nanddev_scan_bbt_in_flash(struct nand_device *nand)
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++)
nanddev_read_bbt(nand, start_block + block, true);
nand->bbt.option |= NANDDEV_BBT_SCANNED;
if (nand->bbt.version == 0) {
nanddev_bbt_format(nand);
ret = nanddev_bbt_format(nand);
if (ret) {
nand->bbt.option = 0;
pr_err("%s format fail\n", __func__);
return ret;
}
ret = nanddev_bbt_in_flash_update(nand);
if (ret) {
nand->bbt.option = 0;
pr_err("%s fail\n", __func__);
pr_err("%s update fail\n", __func__);
return ret;
}
}
nand->bbt.option |= NANDDEV_BBT_SCANNED;
#if defined(BBT_DEBUG)
pr_err("scan_bbt success\n");
if (nand->bbt.version) {
for (block = 0; block < nblocks; block++) {
ret = nanddev_bbt_get_block_status(nand, block);
if (ret != NAND_BBT_BLOCK_GOOD)
bbt_dbg("bad block[0x%x], ret=%d\n", block, ret);
}
}
#endif
return ret;
}
@@ -222,31 +374,31 @@ EXPORT_SYMBOL_GPL(nanddev_scan_bbt_in_flash);
*/
int nanddev_bbt_in_flash_update(struct nand_device *nand)
{
struct nand_pos pos;
struct mtd_info *mtd = nanddev_to_mtd(nand);
if (nand->bbt.option & NANDDEV_BBT_SCANNED) {
unsigned int nblocks = nanddev_neraseblocks(nand);
u32 bbt_version[NANDDEV_BBT_SCAN_MAXBLOCKS];
int start_block, block;
u32 min_version, block_des;
int ret, count = 0;
int ret, count = 0, status;
start_block = nblocks - NANDDEV_BBT_SCAN_MAXBLOCKS;
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
ret = nanddev_bbt_get_block_status(nand, start_block + block);
if (ret == NAND_BBT_BLOCK_FACTORY_BAD) {
bbt_version[block] = 0xFFFFFFFF;
continue;
}
ret = nanddev_read_bbt(nand, start_block + block,
false);
if (ret < 0)
bbt_version[block] = 0xFFFFFFFF;
else if (ret == 0)
bbt_version[block] = 0;
status = nanddev_bbt_get_block_status(nand, start_block + block);
ret = nanddev_read_bbt(nand, start_block + block, false);
if (ret == 0 && status == NAND_BBT_BLOCK_FACTORY_BAD)
bbt_version[block] = BBT_VERSION_INVALID;
else if (ret == -EIO)
bbt_version[block] = BBT_VERSION_INVALID;
else if (ret == BBT_VERSION_BLOCK_ABNORMAL)
bbt_version[block] = ret;
else
bbt_version[block] = ret;
}
get_min_ver:
min_version = 0xFFFFFFFF;
min_version = BBT_VERSION_MAX;
block_des = 0;
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
if (bbt_version[block] < min_version) {
@@ -255,25 +407,38 @@ get_min_ver:
}
}
/* Overwrite the BBT_VERSION_BLOCK_ABNORMAL block */
if (nand->bbt.version < min_version)
nand->bbt.version = min_version + 4;
if (block_des > 0) {
nand->bbt.version++;
ret = nanddev_write_bbt(nand, block_des);
bbt_version[block_des - start_block] = 0xFFFFFFFF;
if (ret) {
pr_err("%s blk= %d ret= %d\n", __func__,
block_des, ret);
goto get_min_ver;
} else {
count++;
if (count < 2)
goto get_min_ver;
BBT_DBG("%s success\n", __func__);
}
} else {
pr_err("%s failed\n", __func__);
pr_err("bbt_update fail, blk=%d ret= %d\n", block_des, ret);
return -EINVAL;
return -1;
}
bbt_version[block_des - start_block] = BBT_VERSION_INVALID;
count++;
if (count < 2)
goto get_min_ver;
bbt_dbg("bbt_update success\n");
} else {
pr_err("bbt_update failed\n");
ret = -1;
}
for (block = 0; block < NANDDEV_BBT_SCAN_MAXBLOCKS; block++) {
if (bbt_version[block] == BBT_VERSION_BLOCK_ABNORMAL) {
block_des = start_block + block;
nanddev_offs_to_pos(nand, block_des * mtd->erasesize, &pos);
nand->ops->erase(nand, &pos);
}
}
return ret;
}
return 0;

View File

@@ -91,7 +91,7 @@
#define DATA_INTERFACE_REG 0x6C
#define DIFACE_SDR_MODE(x) FIELD_PREP(GENMASK(2, 0), (x))
#define DIFACE_DDR_MODE(x) FIELD_PREP(GENMASK(5, 3), (X))
#define DIFACE_DDR_MODE(x) FIELD_PREP(GENMASK(5, 3), (x))
#define DIFACE_SDR 0
#define DIFACE_NVDDR BIT(9)
@@ -283,17 +283,17 @@ static int anfc_select_target(struct nand_chip *chip, int target)
/* Update clock frequency */
if (nfc->cur_clk != anand->clk) {
clk_disable_unprepare(nfc->controller_clk);
ret = clk_set_rate(nfc->controller_clk, anand->clk);
clk_disable_unprepare(nfc->bus_clk);
ret = clk_set_rate(nfc->bus_clk, anand->clk);
if (ret) {
dev_err(nfc->dev, "Failed to change clock rate\n");
return ret;
}
ret = clk_prepare_enable(nfc->controller_clk);
ret = clk_prepare_enable(nfc->bus_clk);
if (ret) {
dev_err(nfc->dev,
"Failed to re-enable the controller clock\n");
"Failed to re-enable the bus clock\n");
return ret;
}
@@ -884,21 +884,60 @@ static int anfc_setup_interface(struct nand_chip *chip, int target,
struct anand *anand = to_anand(chip);
struct arasan_nfc *nfc = to_anfc(chip->controller);
struct device_node *np = nfc->dev->of_node;
const struct nand_sdr_timings *sdr;
const struct nand_nvddr_timings *nvddr;
if (nand_interface_is_nvddr(conf)) {
nvddr = nand_get_nvddr_timings(conf);
if (IS_ERR(nvddr))
return PTR_ERR(nvddr);
/*
* The controller only supports data payload requests which are
* a multiple of 4. In practice, most data accesses are 4-byte
* aligned and this is not an issue. However, rounding up will
* simply be refused by the controller if we reached the end of
* the device *and* we are using the NV-DDR interface(!). In
* this situation, unaligned data requests ending at the device
* boundary will confuse the controller and cannot be performed.
*
* This is something that happens in nand_read_subpage() when
* selecting software ECC support and must be avoided.
*/
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT)
return -ENOTSUPP;
} else {
sdr = nand_get_sdr_timings(conf);
if (IS_ERR(sdr))
return PTR_ERR(sdr);
}
if (target < 0)
return 0;
anand->timings = DIFACE_SDR | DIFACE_SDR_MODE(conf->timings.mode);
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
if (nand_interface_is_sdr(conf))
anand->timings = DIFACE_SDR |
DIFACE_SDR_MODE(conf->timings.mode);
else
anand->timings = DIFACE_NVDDR |
DIFACE_DDR_MODE(conf->timings.mode);
if (nand_interface_is_sdr(conf)) {
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
} else {
/* ONFI timings are defined in picoseconds */
anand->clk = div_u64((u64)NSEC_PER_SEC * 1000,
conf->timings.nvddr.tCK_min);
}
/*
* Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work
* with f > 90MHz (default clock is 100MHz) but signals are unstable
* with higher modes. Hence we decrease a little bit the clock rate to
* 80MHz when using modes 2-5 with this SoC.
* 80MHz when using SDR modes 2-5 with this SoC.
*/
if (of_device_is_compatible(np, "xlnx,zynqmp-nand-controller") &&
conf->timings.mode >= 2)
nand_interface_is_sdr(conf) && conf->timings.mode >= 2)
anand->clk = ANFC_XLNX_SDR_HS_CORE_CLK;
return 0;

View File

@@ -405,6 +405,7 @@ static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc,
dma_async_issue_pending(nc->dmac);
wait_for_completion(&finished);
dma_unmap_single(nc->dev, buf_dma, len, dir);
return 0;
@@ -1246,7 +1247,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
nc = to_nand_controller(nand->base.controller);
/* DDR interface not supported. */
if (conf->type != NAND_SDR_IFACE)
if (!nand_interface_is_sdr(conf))
return -ENOTSUPP;
/*

View File

@@ -2983,11 +2983,10 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev)
if (IS_ERR(cdns_ctrl->reg))
return PTR_ERR(cdns_ctrl->reg);
res = platform_get_resource(ofdev, IORESOURCE_MEM, 1);
cdns_ctrl->io.dma = res->start;
cdns_ctrl->io.virt = devm_ioremap_resource(&ofdev->dev, res);
cdns_ctrl->io.virt = devm_platform_get_and_ioremap_resource(ofdev, 1, &res);
if (IS_ERR(cdns_ctrl->io.virt))
return PTR_ERR(cdns_ctrl->io.virt);
cdns_ctrl->io.dma = res->start;
dt->clk = devm_clk_get(cdns_ctrl->dev, "nf_clk");
if (IS_ERR(dt->clk))

View File

@@ -74,22 +74,21 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
return ret;
}
denali->reg = ioremap(csr_base, csr_len);
denali->reg = devm_ioremap(denali->dev, csr_base, csr_len);
if (!denali->reg) {
dev_err(&dev->dev, "Spectra: Unable to remap memory region\n");
return -ENOMEM;
}
denali->host = ioremap(mem_base, mem_len);
denali->host = devm_ioremap(denali->dev, mem_base, mem_len);
if (!denali->host) {
dev_err(&dev->dev, "Spectra: ioremap failed!");
ret = -ENOMEM;
goto out_unmap_reg;
return -ENOMEM;
}
ret = denali_init(denali);
if (ret)
goto out_unmap_host;
return ret;
nsels = denali->nbanks;
@@ -117,10 +116,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
out_remove_denali:
denali_remove(denali);
out_unmap_host:
iounmap(denali->host);
out_unmap_reg:
iounmap(denali->reg);
return ret;
}
@@ -129,8 +124,6 @@ static void denali_pci_remove(struct pci_dev *dev)
struct denali_controller *denali = pci_get_drvdata(dev);
denali_remove(denali);
iounmap(denali->reg);
iounmap(denali->host);
}
static struct pci_driver denali_pci_driver = {

View File

@@ -727,36 +727,40 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip)
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
unsigned int al;
switch (chip->ecc.engine_type) {
/*
* if ECC was not chosen in DT, decide whether to use HW or SW ECC from
* CS Base Register
*/
case NAND_ECC_ENGINE_TYPE_NONE:
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) {
/* If CS Base Register selects full hardware ECC then use it */
if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
BR_DECC_CHK_GEN) {
chip->ecc.read_page = fsl_elbc_read_page;
chip->ecc.write_page = fsl_elbc_write_page;
chip->ecc.write_subpage = fsl_elbc_write_subpage;
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
chip->ecc.size = 512;
chip->ecc.bytes = 3;
chip->ecc.strength = 1;
} else {
/* otherwise fall back to default software ECC */
chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
}
}
switch (chip->ecc.engine_type) {
/* if HW ECC was chosen, setup ecc and oob layout */
case NAND_ECC_ENGINE_TYPE_ON_HOST:
chip->ecc.read_page = fsl_elbc_read_page;
chip->ecc.write_page = fsl_elbc_write_page;
chip->ecc.write_subpage = fsl_elbc_write_subpage;
mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
chip->ecc.size = 512;
chip->ecc.bytes = 3;
chip->ecc.strength = 1;
break;
/* if SW ECC was chosen in DT, we do not need to set anything here */
/* if none or SW ECC was chosen, we do not need to set anything here */
case NAND_ECC_ENGINE_TYPE_NONE:
case NAND_ECC_ENGINE_TYPE_SOFT:
case NAND_ECC_ENGINE_TYPE_ON_DIE:
break;
/* should we also implement *_ECC_ENGINE_CONTROLLER to do as above? */
default:
return -EINVAL;
}

View File

@@ -653,8 +653,9 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
unsigned int tRP_ps;
bool use_half_period;
int sample_delay_ps, sample_delay_factor;
u16 busy_timeout_cycles;
unsigned int busy_timeout_cycles;
u8 wrn_dly_sel;
u64 busy_timeout_ps;
if (sdr->tRC_min >= 30000) {
/* ONFI non-EDO modes [0-3] */
@@ -678,7 +679,8 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps);
data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps);
data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps);
busy_timeout_cycles = TO_CYCLES(sdr->tWB_max + sdr->tR_max, period_ps);
busy_timeout_ps = max(sdr->tBERS_max, sdr->tPROG_max);
busy_timeout_cycles = TO_CYCLES(busy_timeout_ps, period_ps);
hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) |
BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) |

View File

@@ -2672,7 +2672,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
chip->controller = &nfc->controller;
nand_set_flash_node(chip, np);
if (!of_property_read_bool(np, "marvell,nand-keep-config"))
if (of_property_read_bool(np, "marvell,nand-keep-config"))
chip->options |= NAND_KEEP_TIMINGS;
mtd = nand_to_mtd(chip);

View File

@@ -454,7 +454,7 @@ static int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips,
if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) {
mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
*bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info));
*correct_bitmap |= 1 >> i;
*correct_bitmap |= BIT_ULL(i);
continue;
}
if ((nand->options & NAND_NEED_SCRAMBLING) &&
@@ -800,7 +800,7 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
u8 *data = buf + i * ecc->size;
u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
if (correct_bitmap & (1 << i))
if (correct_bitmap & BIT_ULL(i))
continue;
ret = nand_check_erased_ecc_chunk(data, ecc->size,
oob, ecc->bytes + 2,
@@ -1307,7 +1307,6 @@ static int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc)
if (ret)
return ret;
meson_nfc_free_buffer(&meson_chip->nand);
nand_cleanup(&meson_chip->nand);
list_del(&meson_chip->node);
}

View File

@@ -43,6 +43,7 @@
struct mtk_ecc_caps {
u32 err_mask;
u32 err_shift;
const u8 *ecc_strength;
const u32 *ecc_regs;
u8 num_ecc_strength;
@@ -76,7 +77,7 @@ static const u8 ecc_strength_mt2712[] = {
};
static const u8 ecc_strength_mt7622[] = {
4, 6, 8, 10, 12, 14, 16
4, 6, 8, 10, 12
};
enum mtk_ecc_regs {
@@ -221,7 +222,7 @@ void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
for (i = 0; i < sectors; i++) {
offset = (i >> 2) << 2;
err = readl(ecc->regs + ECC_DECENUM0 + offset);
err = err >> ((i % 4) * 8);
err = err >> ((i % 4) * ecc->caps->err_shift);
err &= ecc->caps->err_mask;
if (err == ecc->caps->err_mask) {
/* uncorrectable errors */
@@ -449,6 +450,7 @@ EXPORT_SYMBOL(mtk_ecc_get_parity_bits);
static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
.err_mask = 0x3f,
.err_shift = 8,
.ecc_strength = ecc_strength_mt2701,
.ecc_regs = mt2701_ecc_regs,
.num_ecc_strength = 20,
@@ -459,6 +461,7 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = {
static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
.err_mask = 0x7f,
.err_shift = 8,
.ecc_strength = ecc_strength_mt2712,
.ecc_regs = mt2712_ecc_regs,
.num_ecc_strength = 23,
@@ -468,10 +471,11 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = {
};
static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = {
.err_mask = 0x3f,
.err_mask = 0x1f,
.err_shift = 5,
.ecc_strength = ecc_strength_mt7622,
.ecc_regs = mt7622_ecc_regs,
.num_ecc_strength = 7,
.num_ecc_strength = 5,
.ecc_mode_shift = 4,
.parity_bits = 13,
.pg_irq_sel = 0,

View File

@@ -292,6 +292,261 @@ static const struct nand_interface_config onfi_sdr_timings[] = {
},
};
static const struct nand_interface_config onfi_nvddr_timings[] = {
/* Mode 0 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 0,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 10000,
.tCALH_min = 10000,
.tCALS_min = 10000,
.tCAS_min = 10000,
.tCEH_min = 20000,
.tCH_min = 10000,
.tCK_min = 50000,
.tCS_min = 35000,
.tDH_min = 5000,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 5000,
.tDS_min = 5000,
.tDSC_min = 50000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 6000,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 1 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 1,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 5000,
.tCALH_min = 5000,
.tCALS_min = 5000,
.tCAS_min = 5000,
.tCEH_min = 20000,
.tCH_min = 5000,
.tCK_min = 30000,
.tCS_min = 25000,
.tDH_min = 2500,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 2500,
.tDS_min = 3000,
.tDSC_min = 30000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 3000,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 2 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 2,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 4000,
.tCALH_min = 4000,
.tCALS_min = 4000,
.tCAS_min = 4000,
.tCEH_min = 20000,
.tCH_min = 4000,
.tCK_min = 20000,
.tCS_min = 15000,
.tDH_min = 1700,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 1700,
.tDS_min = 2000,
.tDSC_min = 20000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 2000,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 3 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 3,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 3000,
.tCALH_min = 3000,
.tCALS_min = 3000,
.tCAS_min = 3000,
.tCEH_min = 20000,
.tCH_min = 3000,
.tCK_min = 15000,
.tCS_min = 15000,
.tDH_min = 1300,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 1300,
.tDS_min = 1500,
.tDSC_min = 15000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 1500,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 4 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 4,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 2500,
.tCALH_min = 2500,
.tCALS_min = 2500,
.tCAS_min = 2500,
.tCEH_min = 20000,
.tCH_min = 2500,
.tCK_min = 12000,
.tCS_min = 15000,
.tDH_min = 1100,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 1000,
.tDS_min = 1100,
.tDSC_min = 12000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 1200,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
/* Mode 5 */
{
.type = NAND_NVDDR_IFACE,
.timings.mode = 5,
.timings.nvddr = {
.tCCS_min = 500000,
.tR_max = 200000000,
.tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX,
.tAC_min = 3000,
.tAC_max = 25000,
.tADL_min = 400000,
.tCAD_min = 45000,
.tCAH_min = 2000,
.tCALH_min = 2000,
.tCALS_min = 2000,
.tCAS_min = 2000,
.tCEH_min = 20000,
.tCH_min = 2000,
.tCK_min = 10000,
.tCS_min = 15000,
.tDH_min = 900,
.tDQSCK_min = 3000,
.tDQSCK_max = 25000,
.tDQSD_min = 0,
.tDQSD_max = 18000,
.tDQSHZ_max = 20000,
.tDQSQ_max = 850,
.tDS_min = 900,
.tDSC_min = 10000,
.tFEAT_max = 1000000,
.tITC_max = 1000000,
.tQHS_max = 1000,
.tRHW_min = 100000,
.tRR_min = 20000,
.tRST_max = 500000000,
.tWB_max = 100000,
.tWHR_min = 80000,
.tWRCK_min = 20000,
.tWW_min = 100000,
},
},
};
/* All NAND chips share the same reset data interface: SDR mode 0 */
const struct nand_interface_config *nand_get_reset_interface_config(void)
{

View File

@@ -384,7 +384,8 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
dma_addr_t dma_addr;
dma_cookie_t cookie;
uint32_t reg;
int ret;
int ret = 0;
unsigned long time_left;
if (dir == DMA_FROM_DEVICE) {
chan = flctl->chan_fifo0_rx;
@@ -425,13 +426,14 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
goto out;
}
ret =
time_left =
wait_for_completion_timeout(&flctl->dma_complete,
msecs_to_jiffies(3000));
if (ret <= 0) {
if (time_left == 0) {
dmaengine_terminate_all(chan);
dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n");
ret = -ETIMEDOUT;
}
out:
@@ -441,7 +443,7 @@ out:
dma_unmap_single(chan->device->dev, dma_addr, len, dir);
/* ret > 0 is success */
/* ret == 0 is success */
return ret;
}
@@ -465,7 +467,7 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
/* initiate DMA transfer */
if (flctl->chan_fifo0_rx && rlen >= 32 &&
flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE) > 0)
!flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE))
goto convert; /* DMA success */
/* do polling transfer */
@@ -524,7 +526,7 @@ static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen,
/* initiate DMA transfer */
if (flctl->chan_fifo0_tx && rlen >= 32 &&
flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE) > 0)
!flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE))
return; /* DMA success */
/* do polling transfer */

View File

@@ -527,7 +527,7 @@ static int spinand_read_page(struct spinand_device *spinand,
const struct nand_page_io_req *req,
bool ecc_enabled)
{
u8 status;
u8 status = 8;
int ret;
ret = spinand_load_page_op(spinand, req);
@@ -535,6 +535,13 @@ static int spinand_read_page(struct spinand_device *spinand,
return ret;
ret = spinand_wait(spinand, &status);
/*
* When there is data outside of OIP in the status, the status data is
* inaccurate and needs to be reconfirmed
*/
if (spinand->id.data[0] == 0x01 && status && !ret)
ret = spinand_wait(spinand, &status);
if (ret < 0)
return ret;

View File

@@ -9,7 +9,7 @@
#define SPINAND_MFR_DOSILICON 0xE5
#define DOSICON_STATUS_ECC_MASK GENMASK(7, 4)
#define DOSICON_STATUS_ECC_MASK GENMASK(6, 4)
#define DOSICON_STATUS_ECC_NO_BITFLIPS (0 << 4)
#define DOSICON_STATUS_ECC_1TO3_BITFLIPS (1 << 4)
#define DOSICON_STATUS_ECC_4TO6_BITFLIPS (3 << 4)
@@ -193,6 +193,26 @@ static const struct spinand_info dosilicon_spinand_table[] = {
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&ds35xxgb_ooblayout,
ds35xxgb_ecc_get_status)),
SPINAND_INFO("DS35Q12B",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xF5),
NAND_MEMORG(1, 2048, 128, 64, 512, 10, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&ds35xxgb_ooblayout,
ds35xxgb_ecc_get_status)),
SPINAND_INFO("DS35M12B",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xA5),
NAND_MEMORG(1, 2048, 128, 64, 512, 10, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&ds35xxgb_ooblayout,
ds35xxgb_ecc_get_status)),
};
static const struct spinand_manufacturer_ops dosilicon_spinand_manuf_ops = {

View File

@@ -70,12 +70,13 @@ static const struct mtd_ooblayout_ops js28u1gqscahg_ooblayout = {
static int js28u1gqscahg_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
u8 eccsr = (status & GENMASK(6, 4)) >> 2;
struct nand_device *nand = spinand_to_nand(spinand);
u8 eccsr = (status & GENMASK(6, 4)) >> 4;
if (eccsr <= 7)
if (eccsr < 4)
return eccsr;
else if (eccsr == 12)
return 8;
else if (eccsr == 4)
return nanddev_get_ecc_requirements(nand)->strength;
else
return -EBADMSG;
}

View File

@@ -26,8 +26,8 @@ static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
SPINAND_PROG_LOAD(false, 0, NULL, 0));
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
SPINAND_PROG_LOAD(true, 0, NULL, 0));
static int s35ml04g3_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)

View File

@@ -69,12 +69,13 @@ static const struct mtd_ooblayout_ops tx25g01_ooblayout = {
static int tx25g01_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
u8 eccsr = (status & GENMASK(6, 4)) >> 2;
struct nand_device *nand = spinand_to_nand(spinand);
u8 eccsr = (status & GENMASK(6, 4)) >> 4;
if (eccsr <= 7)
if (eccsr < 4)
return eccsr;
else if (eccsr == 12)
return 8;
else if (eccsr == 4)
return nanddev_get_ecc_requirements(nand)->strength;
else
return -EBADMSG;
}

View File

@@ -209,6 +209,15 @@ static const struct spinand_info winbond_spinand_table[] = {
0,
SPINAND_ECCINFO(&w25n02kv_ooblayout,
w25n02kv_ecc_get_status)),
SPINAND_INFO("W25N01JWZEIG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBC),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(1, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
};
static int winbond_spinand_init(struct spinand_device *spinand)

View File

@@ -203,6 +203,28 @@ static int xt26g01c_ecc_get_status(struct spinand_device *spinand,
return -EBADMSG;
}
static int xt26g11c_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
struct nand_device *nand = spinand_to_nand(spinand);
switch (status & STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:
return 0;
case STATUS_ECC_UNCOR_ERROR:
return -EBADMSG;
case STATUS_ECC_HAS_BITFLIPS:
return 1;
default:
return nanddev_get_ecc_requirements(nand)->strength;
}
return -EINVAL;
}
static const struct spinand_info xtx_spinand_table[] = {
SPINAND_INFO("XT26G01A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
@@ -293,7 +315,61 @@ static const struct spinand_info xtx_spinand_table[] = {
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g01c_ooblayout,
xt26g01c_ecc_get_status)),
xt26g11c_ecc_get_status)),
SPINAND_INFO("XT26Q02DWSIGA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
SPINAND_INFO("XT26Q01DWSIGA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
SPINAND_INFO("XT26Q04DWSIGA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x53),
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
SPINAND_INFO("XT26G01DWSIGA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x31),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g01b_ooblayout, xt26g11c_ecc_get_status)),
SPINAND_INFO("XT26G02DWSIGA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32),
NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g01b_ooblayout, xt26g11c_ecc_get_status)),
SPINAND_INFO("XT26G04DWSIGA",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x33),
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
};
static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {