* Raw NAND changes

- Sunxi: Support added for the H616 compatible
 - Qcom: Support added for the MDM9607 compatible
 - Support for the Toshiba TC58NVG1S3H part
 - GPMI: New debugfs entry to expose the chip geometry
 - PL353: Timing updates and software ECC support have been fixed
 
 * SPI NAND changes
 - fmsh: Support added for FM25G{01,02}B chips
 - HeYangTek: Support added for HYF1GQ4UDACAE
 
 Aside from these main changes, there is the usual load of misc fixes and
 hardening changes.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmp95DkACgkQJWrqGEe9
 VoRS+Af+NoNZau3sf27YVygCQT+LWaRQgd1KN2ftk6fbaz+W/V2ZmC5lkSMPyOki
 Yln3omlL84lRYIKjZEqq3jZ10V+EPb5x6qFx6uz4OSxvx8LzrQSYCDgMV2hCxNrg
 gZSHuEBJb6gsmmACQdU2KqwyUmeVTWrky5+nrj8VPB7Fc1v+fiy63wb6fEWvrfgF
 7tH2xZbfQLKuxMIHhli1mPr0Ij9gvUFHa7jNO3w8owv3MLj44xuEAzMmjp2cSqmx
 JZGbxkRQMv2Z+kkSVAGkS49ogdZKJ4czabWbsG1BG+GiALFiuqZY1mh/d3wZ5q4f
 omeMXnaW+roTjL+C9dewXihsT0PzVQ==
 =8+Q/
 -----END PGP SIGNATURE-----

Merge tag 'nand/for-7.3' into mtd/next

* Raw NAND changes
- Sunxi: Support added for the H616 compatible
- Qcom: Support added for the MDM9607 compatible
- Support for the Toshiba TC58NVG1S3H part
- GPMI: New debugfs entry to expose the chip geometry
- PL353: Timing updates and software ECC support have been fixed

* SPI NAND changes
- fmsh: Support added for FM25G{01,02}B chips
- HeYangTek: Support added for HYF1GQ4UDACAE

Aside from these main changes, there is the usual load of misc fixes and
hardening changes.
This commit is contained in:
Miquel Raynal 2026-08-23 14:30:31 +02:00
commit 9b7e2fe0fe
21 changed files with 576 additions and 77 deletions

View File

@ -22,17 +22,20 @@ properties:
- qcom,ipq4019-nand
- qcom,ipq6018-nand
- qcom,ipq8074-nand
- qcom,mdm9607-nand
- qcom,sdx55-nand
reg:
maxItems: 1
clocks:
minItems: 1
items:
- description: Core Clock
- description: Always ON Clock
clock-names:
minItems: 1
items:
- const: core
- const: aon
@ -101,6 +104,27 @@ allOf:
items:
- const: rxtx
# On MDM9607, the OS can only control a single clock.
# The 3 hardware clocks (core, aon, ahb) are invisible to the OS.
- if:
properties:
compatible:
contains:
enum:
- qcom,mdm9607-nand
then:
properties:
clocks:
maxItems: 1
clock-names:
maxItems: 1
else:
properties:
clocks:
minItems: 2
clock-names:
minItems: 2
- if:
properties:
compatible:
@ -121,6 +145,7 @@ allOf:
- qcom,ipq4019-nand
- qcom,ipq6018-nand
- qcom,ipq8074-nand
- qcom,mdm9607-nand
- qcom,sdx55-nand
then:

View File

@ -2129,6 +2129,13 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
of_property_read_bool(np, "gpmc,time-para-granularity");
}
static int gpmc_child_is_nand(struct device_node *child)
{
/* This has to match drivers/mtd/nand/raw/omap2.c's omap_nand_ids[] */
return of_device_is_compatible(child, "ti,omap2-nand") ||
of_device_is_compatible(child, "ti,am64-nand");
}
/**
* gpmc_probe_generic_child - configures the gpmc for a child device
* @pdev: pointer to gpmc platform device
@ -2220,7 +2227,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
goto err;
}
if (of_match_node(omap_nand_ids, child)) {
if (gpmc_child_is_nand(child)) {
/* NAND specific setup */
val = 8;
of_property_read_u32(child, "nand-bus-width", &val);

View File

@ -450,6 +450,7 @@ static const struct of_device_id rtl_ecc_of_ids[] = {
},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, rtl_ecc_of_ids);
static struct platform_driver rtl_ecc_driver = {
.driver = {

View File

@ -1799,8 +1799,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
* Legacy bindings only allow connecting a single NAND with a unique CS
* line to the controller.
*/
nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs),
GFP_KERNEL);
nand = devm_kzalloc(nc->dev, struct_size(nand, cs, 1), GFP_KERNEL);
if (!nand)
return -ENOMEM;

View File

@ -7,6 +7,7 @@
*/
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/sched/task_stack.h>
@ -732,6 +733,31 @@ static int common_nfc_set_geometry(struct gpmi_nand_data *this)
return err;
}
#ifdef CONFIG_DEBUG_FS
static void bch_remove_debugfs(void *data)
{
struct gpmi_nand_data *this = data;
debugfs_remove_recursive(this->dbg_root);
this->dbg_root = NULL;
}
static void bch_create_debugfs(struct gpmi_nand_data *this)
{
struct bch_geometry *bch_geo = &this->bch_geometry;
this->dbg_root = debugfs_create_dir("gpmi-nand", NULL);
this->dbg_bch_geo.data = (void *)bch_geo;
this->dbg_bch_geo.size = sizeof(struct bch_geometry);
this->raw_mode = true;
debugfs_create_blob("bch_geometry", 0444, this->dbg_root, &this->dbg_bch_geo);
debugfs_create_bool("raw_mode", 0444, this->dbg_root, &this->raw_mode);
devm_add_action_or_reset(this->dev, bch_remove_debugfs, this);
}
#else
static void bch_create_debugfs(struct gpmi_nand_data *this) {}
#endif /* CONFIG_DEBUG_FS */
/* Configures the geometry for BCH. */
static int bch_set_geometry(struct gpmi_nand_data *this)
{
@ -2282,6 +2308,9 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
if (ret)
return ret;
/* save BCH geometry to debugfs if CONFIG_DEBUG_FS is enabled */
bch_create_debugfs(this);
/* Init the nand_ecc_ctrl{} */
ecc->read_page = gpmi_ecc_read_page;
ecc->write_page = gpmi_ecc_write_page;

View File

@ -161,6 +161,12 @@ struct gpmi_nand_data {
#define DMA_CHANS 8
struct dma_chan *dma_chans[DMA_CHANS];
struct completion dma_done;
#ifdef CONFIG_DEBUG_FS
struct dentry *dbg_root;
struct debugfs_blob_wrapper dbg_bch_geo;
bool raw_mode;
#endif
};
/* BCH : Status Block Completion Codes */

View File

@ -29,6 +29,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"TC58NVG0S3E 1G 3.3V 8-bit",
{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
{"TC58NVG1S3H 2G 3.3V 8-bit",
{ .id = {0x98, 0xda, 0x90, 0x15, 0x76} },
SZ_2K, SZ_256, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
{"TC58NVG2S0F 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },

View File

@ -35,16 +35,21 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
struct nand_onfi_params *p)
{
struct nand_device *base = &chip->base;
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_ecc_props requirements;
struct onfi_ext_param_page *ep;
struct onfi_ext_section *s;
struct onfi_ext_ecc_info *ecc;
size_t remaining, section_len;
uint8_t *cursor;
int ret;
int len;
int i;
len = le16_to_cpu(p->ext_param_page_length) * 16;
if (len < sizeof(*ep))
return -EINVAL;
ep = kmalloc(len, GFP_KERNEL);
if (!ep)
return -ENOMEM;
@ -77,11 +82,29 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
/* find the ECC section. */
cursor = (uint8_t *)(ep + 1);
remaining = len - sizeof(*ep);
for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) {
s = ep->sections + i;
if (s->type == ONFI_SECTION_TYPE_2)
section_len = s->length * 16;
if (section_len > remaining) {
dev_dbg(&mtd->dev,
"ONFI extended parameter section %d exceeds page\n",
i);
goto ext_out;
}
if (s->type == ONFI_SECTION_TYPE_2) {
if (section_len < sizeof(*ecc)) {
dev_dbg(&mtd->dev,
"ONFI extended parameter ECC section %d is too short\n",
i);
goto ext_out;
}
break;
cursor += s->length * 16;
}
cursor += section_len;
remaining -= section_len;
}
if (i == ONFI_EXT_SECTION_MAX) {
pr_debug("We can not find the ECC section.\n");

View File

@ -287,7 +287,9 @@ static int toshiba_nand_init(struct nand_chip *chip)
if (!strncmp("TC58NVG0S3E", chip->parameters.model,
sizeof("TC58NVG0S3E") - 1))
tc58nvg0s3e_init(chip);
if ((!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
if ((!strncmp("TC58NVG1S3H", chip->parameters.model,
sizeof("TC58NVG1S3H") - 1)) ||
(!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
sizeof("TH58NVG2S3HBAI4") - 1)) ||
(!strncmp("TH58NVG3S0HBAI4", chip->parameters.model,
sizeof("TH58NVG3S0HBAI4") - 1)))

View File

@ -2318,7 +2318,11 @@ static void omap_nand_remove(struct platform_device *pdev)
nand_cleanup(nand_chip);
}
/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */
static const struct of_device_id omap_nand_ids[] = {
{ .compatible = "ti,omap2-nand" },
{ .compatible = "ti,am64-nand" },
{ }
};
MODULE_DEVICE_TABLE(of, omap_nand_ids);
static struct platform_driver omap_nand_driver = {

View File

@ -862,8 +862,11 @@ static int pl35x_nfc_setup_interface(struct nand_chip *chip, int cs,
PL35X_SMC_NAND_TAR_CYCLES(tmgs.t_ar) |
PL35X_SMC_NAND_TRR_CYCLES(tmgs.t_rr);
writel(plnand->timings, nfc->conf_regs + PL35X_SMC_CYCLES);
pl35x_smc_update_regs(nfc);
/*
* Reset nfc->selected_chip so the next command will cause the timing
* registers to be updated in ->*_select_target().
*/
nfc->selected_chip = NULL;
return 0;
}
@ -914,7 +917,6 @@ static int pl35x_nand_init_hw_ecc_controller(struct pl35x_nandc *nfc,
chip->ecc.steps = mtd->writesize / chip->ecc.size;
chip->ecc.read_page = pl35x_nand_read_page_hwecc;
chip->ecc.write_page = pl35x_nand_write_page_hwecc;
chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
pl35x_smc_set_ecc_pg_size(nfc, chip, mtd->writesize);
nfc->ecc_buf = devm_kmalloc(nfc->dev, chip->ecc.bytes * chip->ecc.steps,
@ -973,18 +975,19 @@ static int pl35x_nand_attach_chip(struct nand_chip *chip)
switch (chip->ecc.engine_type) {
case NAND_ECC_ENGINE_TYPE_ON_DIE:
dev_dbg(nfc->dev, "Using on-die ECC\n");
dev_dbg(nfc->dev, "Using on-die hardware ECC\n");
/* Keep these legacy BBT descriptors for ON_DIE situations */
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
fallthrough;
case NAND_ECC_ENGINE_TYPE_NONE:
dev_dbg(nfc->dev, "Using no ECC engine\n");
break;
case NAND_ECC_ENGINE_TYPE_SOFT:
dev_dbg(nfc->dev, "Using software ECC (Hamming 1-bit/512B)\n");
chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
dev_dbg(nfc->dev, "Using software ECC\n");
break;
case NAND_ECC_ENGINE_TYPE_ON_HOST:
dev_dbg(nfc->dev, "Using hardware ECC\n");
dev_dbg(nfc->dev, "Using on-host hardware ECC\n");
ret = pl35x_nand_init_hw_ecc_controller(nfc, chip);
if (ret)
return ret;
@ -995,6 +998,9 @@ static int pl35x_nand_attach_chip(struct nand_chip *chip)
return -EINVAL;
}
chip->ecc.read_page_raw = nand_monolithic_read_page_raw;
chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
return 0;
}

View File

@ -1564,7 +1564,7 @@ static int qcom_op_cmd_mapping(struct nand_chip *chip, u8 opcode,
cmd = OP_FETCH_ID;
break;
case NAND_CMD_PARAM:
if (nandc->props->qpic_version2)
if (nandc->props->has_onfi_read_op)
cmd = OP_PAGE_READ_ONFI_READ;
else
cmd = OP_PAGE_READ;
@ -1903,7 +1903,7 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
nandc->regs->ecc_buf_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE);
/* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
if (!nandc->props->qpic_version2) {
if (!nandc->props->has_onfi_read_op) {
nandc->regs->vld = cpu_to_le32((nandc->vld & ~READ_START_VLD));
nandc->regs->cmd1 = cpu_to_le32((nandc->cmd1 & ~READ_ADDR_MASK) |
FIELD_PREP(READ_ADDR_MASK, NAND_CMD_PARAM));
@ -1911,7 +1911,7 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
nandc->regs->exec = cpu_to_le32(1);
if (!nandc->props->qpic_version2) {
if (!nandc->props->has_onfi_read_op) {
nandc->regs->orig_cmd1 = cpu_to_le32(nandc->cmd1);
nandc->regs->orig_vld = cpu_to_le32(nandc->vld);
}
@ -1925,7 +1925,7 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
else
nandc_set_read_loc_first(chip, reg_base, 0, len, 1);
if (!nandc->props->qpic_version2) {
if (!nandc->props->has_onfi_read_op) {
qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
}
@ -1939,7 +1939,7 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
nandc->buf_count, 0);
/* restore CMD1 and VLD regs */
if (!nandc->props->qpic_version2) {
if (!nandc->props->has_onfi_read_op) {
qcom_write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1,
NAND_BAM_NEXT_SGL);
@ -2041,7 +2041,7 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
if (!nandc->props->nandc_part_of_qpic)
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
if (!nandc->props->qpic_version2)
if (!nandc->props->has_onfi_read_op)
nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
NAND_DEV_CMD_VLD_VAL);
@ -2063,7 +2063,7 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
}
/* save the original values of these registers */
if (!nandc->props->qpic_version2) {
if (!nandc->props->has_onfi_read_op) {
nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
nandc->vld = NAND_DEV_CMD_VLD_VAL;
}
@ -2280,7 +2280,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
if (IS_ERR(nandc->core_clk))
return PTR_ERR(nandc->core_clk);
nandc->aon_clk = devm_clk_get(dev, "aon");
nandc->aon_clk = devm_clk_get_optional(dev, "aon");
if (IS_ERR(nandc->aon_clk))
return PTR_ERR(nandc->aon_clk);
@ -2381,10 +2381,20 @@ static const struct qcom_nandc_props ipq8074_nandc_props = {
.bam_offset = 0x30000,
};
static const struct qcom_nandc_props mdm9607_nandc_props = {
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
.supports_bam = true,
.nandc_part_of_qpic = true,
.has_onfi_read_op = true,
.dev_cmd_reg_start = 0x7000,
.bam_offset = 0x30000,
};
static const struct qcom_nandc_props sdx55_nandc_props = {
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
.supports_bam = true,
.nandc_part_of_qpic = true,
.has_onfi_read_op = true,
.qpic_version2 = true,
.dev_cmd_reg_start = 0x7000,
.bam_offset = 0x30000,
@ -2411,6 +2421,10 @@ static const struct of_device_id qcom_nandc_of_match[] = {
.compatible = "qcom,ipq8074-nand",
.data = &ipq8074_nandc_props,
},
{
.compatible = "qcom,mdm9607-nand",
.data = &mdm9607_nandc_props,
},
{
.compatible = "qcom,sdx55-nand",
.data = &sdx55_nandc_props,

View File

@ -79,6 +79,12 @@
#define NFC_REG_H6_MDMA_BUF_ADDR 0x0210
#define NFC_REG_H6_MDMA_CNT 0x0214
#define NFC_H6_MDMA_STA_DESC0_COMPLETE BIT(0)
#define NFC_MDMA_DESC_LAST BIT(2)
#define NFC_MDMA_DESC_FIRST BIT(3)
#define NFC_MDMA_DESC_SIZE_MASK GENMASK(15, 0)
#define NFC_RAM0_BASE 0x0400
#define NFC_RAM1_BASE 0x0800
@ -237,6 +243,18 @@ struct sunxi_nand_hw_ecc {
u32 ecc_ctl;
};
#define SUNXI_NFC_TIMING_STEPS 4
/* Delay arrays contain internal NDFC clock cycles for field values 0 to 3. */
struct sunxi_nfc_timings {
/* Internal clock cycles used by T1-T4, T7 and T11. */
u8 setup_cycles;
s32 tWB[SUNXI_NFC_TIMING_STEPS];
s32 tADL[SUNXI_NFC_TIMING_STEPS];
s32 tWHR[SUNXI_NFC_TIMING_STEPS];
s32 tRHW[SUNXI_NFC_TIMING_STEPS];
};
/**
* struct sunxi_nand_chip - stores NAND chip device related information
*
@ -267,12 +285,19 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
return container_of(nand, struct sunxi_nand_chip, nand);
}
struct sunxi_nfc_mdma_desc {
__le32 config;
__le32 size;
__le32 buf;
__le32 next;
} __packed __aligned(4);
/*
* NAND Controller capabilities structure: stores NAND controller capabilities
* for distinction between compatible strings.
*
* @has_mdma: Use mbus dma mode, otherwise general dma
* through MBUS on A23/A33 needs extra configuration.
* @has_mdma: Use A23/A33-style MBUS DMA registers
* @has_mdma_desc: MBUS DMA uses H6-style descriptors
* @has_ecc_block_512: If the ECC can handle 512B or only 1024B chunks
* @has_ecc_clk: If the controller needs an ECC clock.
* @has_mbus_clk: If the controller needs a mbus clock.
@ -301,9 +326,11 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
* bytes to write
* @nuser_data_tab: Size of @user_data_len_tab
* @sram_size: Size of the NAND controller SRAM
* @timings: Controller timing characteristics
*/
struct sunxi_nfc_caps {
bool has_mdma;
bool has_mdma_desc;
bool has_ecc_block_512;
bool has_ecc_clk;
bool has_mbus_clk;
@ -327,6 +354,7 @@ struct sunxi_nfc_caps {
unsigned int nuser_data_tab;
unsigned int max_ecc_steps;
int sram_size;
const struct sunxi_nfc_timings *timings;
};
/**
@ -346,6 +374,9 @@ struct sunxi_nfc_caps {
* controller
* @complete: a completion object used to wait for NAND controller events
* @dmac: the DMA channel attached to the NAND controller
* @use_mdma: use an internal MBUS DMA backend
* @mdma_desc: H6-style MBUS DMA descriptor
* @mdma_desc_dma: DMA address of @mdma_desc
* @caps: NAND Controller capabilities
*/
struct sunxi_nfc {
@ -362,6 +393,9 @@ struct sunxi_nfc {
struct list_head chips;
struct completion complete;
struct dma_chan *dmac;
bool use_mdma;
struct sunxi_nfc_mdma_desc *mdma_desc;
dma_addr_t mdma_desc_dma;
const struct sunxi_nfc_caps *caps;
};
@ -466,7 +500,10 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
{
struct dma_async_tx_descriptor *dmad;
enum dma_transfer_direction tdir;
dma_addr_t buf_dma;
dma_cookie_t dmat;
int len = chunksize * nchunks;
u32 data_blocks = nchunks;
int ret;
if (ddir == DMA_FROM_DEVICE)
@ -474,12 +511,21 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
else
tdir = DMA_MEM_TO_DEV;
sg_init_one(sg, buf, nchunks * chunksize);
sg_init_one(sg, buf, len);
ret = dma_map_sg(nfc->dev, sg, 1, ddir);
if (!ret)
return -ENOMEM;
if (!nfc->caps->has_mdma) {
buf_dma = sg_dma_address(sg);
if (nfc->mdma_desc &&
(len > NFC_MDMA_DESC_SIZE_MASK || !IS_ALIGNED(len, 8) ||
!IS_ALIGNED(buf_dma, 4))) {
ret = -EINVAL;
goto err_unmap_buf;
}
if (!nfc->use_mdma) {
dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
if (!dmad) {
ret = -EINVAL;
@ -489,14 +535,35 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
nfc->regs + NFC_REG_CTL);
writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
/* H6/H616 use one enable bit per ECC data block. */
if (nfc->caps->has_mdma_desc)
data_blocks = GENMASK(nchunks - 1, 0);
writel(data_blocks, nfc->regs + NFC_REG_SECTOR_NUM);
writel(chunksize, nfc->regs + NFC_REG_CNT);
if (nfc->caps->has_mdma) {
if (nfc->use_mdma)
writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
nfc->regs + NFC_REG_CTL);
writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR);
if (nfc->mdma_desc) {
struct sunxi_nfc_mdma_desc *desc = nfc->mdma_desc;
desc->config = cpu_to_le32(NFC_MDMA_DESC_FIRST |
NFC_MDMA_DESC_LAST);
desc->size = cpu_to_le32(len);
/* Descriptor words are little-endian DMA memory, not MMIO. */
desc->buf = cpu_to_le32(buf_dma);
desc->next = cpu_to_le32(nfc->mdma_desc_dma);
writel(NFC_H6_MDMA_STA_DESC0_COMPLETE,
nfc->regs + NFC_REG_H6_MDMA_STA);
dma_wmb();
writel(nfc->mdma_desc_dma,
nfc->regs + NFC_REG_H6_MDMA_DLBA_REG);
} else if (nfc->caps->has_mdma) {
writel(len, nfc->regs + NFC_REG_MDMA_CNT);
writel(buf_dma, nfc->regs + NFC_REG_MDMA_ADDR);
} else {
dmat = dmaengine_submit(dmad);
@ -525,6 +592,14 @@ static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
nfc->regs + NFC_REG_CTL);
}
static void sunxi_nfc_dma_op_abort(struct sunxi_nfc *nfc)
{
if (nfc->use_mdma)
sunxi_nfc_rst(nfc);
else
dmaengine_terminate_all(nfc->dmac);
}
static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
{
struct mtd_info *mtd = nand_to_mtd(nand);
@ -1202,7 +1277,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
wait = NFC_CMD_INT_FLAG;
if (nfc->caps->has_mdma)
if (nfc->use_mdma)
wait |= NFC_DMA_INT_FLAG;
else
dma_async_issue_pending(nfc->dmac);
@ -1211,8 +1286,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
if (ret && !nfc->caps->has_mdma)
dmaengine_terminate_all(nfc->dmac);
if (ret)
sunxi_nfc_dma_op_abort(nfc);
sunxi_nfc_randomizer_disable(nand);
sunxi_nfc_hw_ecc_disable(nand);
@ -1613,7 +1688,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
wait = NFC_CMD_INT_FLAG;
if (nfc->caps->has_mdma)
if (nfc->use_mdma)
wait |= NFC_DMA_INT_FLAG;
else
dma_async_issue_pending(nfc->dmac);
@ -1623,8 +1698,8 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
if (ret && !nfc->caps->has_mdma)
dmaengine_terminate_all(nfc->dmac);
if (ret)
sunxi_nfc_dma_op_abort(nfc);
sunxi_nfc_randomizer_disable(nand);
sunxi_nfc_hw_ecc_disable(nand);
@ -1667,8 +1742,21 @@ static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
return nand_prog_page_end_op(nand);
}
static const s32 tWB_lut[] = {6, 12, 16, 20};
static const s32 tRHW_lut[] = {4, 8, 12, 20};
static const struct sunxi_nfc_timings sun4i_a10_nfc_timings = {
.setup_cycles = 1,
.tWB = { 6, 12, 16, 20 },
.tADL = { 7, 15, 23, 31 },
.tWHR = { 7, 15, 23, 31 },
.tRHW = { 4, 8, 12, 20 },
};
static const struct sunxi_nfc_timings sun50i_h616_nfc_timings = {
.setup_cycles = 2,
.tWB = { 28, 44, 60, 76 },
.tADL = { 0, 12, 28, 44 },
.tWHR = { 0, 12, 28, 44 },
.tRHW = { 8, 24, 40, 56 },
};
static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
u32 clk_period)
@ -1693,6 +1781,7 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
const struct sunxi_nfc_timings *nfc_timings = nfc->caps->timings;
const struct nand_sdr_timings *timings;
u32 min_clk_period = 0;
s32 tWB, tADL, tWHR, tRHW, tCAD;
@ -1703,20 +1792,28 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
return -ENOTSUPP;
/* T1 <=> tCLS */
if (timings->tCLS_min > min_clk_period)
min_clk_period = timings->tCLS_min;
if (timings->tCLS_min >
min_clk_period * nfc_timings->setup_cycles)
min_clk_period = DIV_ROUND_UP(timings->tCLS_min,
nfc_timings->setup_cycles);
/* T2 <=> tCLH */
if (timings->tCLH_min > min_clk_period)
min_clk_period = timings->tCLH_min;
if (timings->tCLH_min >
min_clk_period * nfc_timings->setup_cycles)
min_clk_period = DIV_ROUND_UP(timings->tCLH_min,
nfc_timings->setup_cycles);
/* T3 <=> tCS */
if (timings->tCS_min > min_clk_period)
min_clk_period = timings->tCS_min;
if (timings->tCS_min >
min_clk_period * nfc_timings->setup_cycles)
min_clk_period = DIV_ROUND_UP(timings->tCS_min,
nfc_timings->setup_cycles);
/* T4 <=> tCH */
if (timings->tCH_min > min_clk_period)
min_clk_period = timings->tCH_min;
if (timings->tCH_min >
min_clk_period * nfc_timings->setup_cycles)
min_clk_period = DIV_ROUND_UP(timings->tCH_min,
nfc_timings->setup_cycles);
/* T5 <=> tWP */
if (timings->tWP_min > min_clk_period)
@ -1727,8 +1824,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
min_clk_period = timings->tWH_min;
/* T7 <=> tALS */
if (timings->tALS_min > min_clk_period)
min_clk_period = timings->tALS_min;
if (timings->tALS_min >
min_clk_period * nfc_timings->setup_cycles)
min_clk_period = DIV_ROUND_UP(timings->tALS_min,
nfc_timings->setup_cycles);
/* T8 <=> tDS */
if (timings->tDS_min > min_clk_period)
@ -1743,8 +1842,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
/* T11 <=> tALH */
if (timings->tALH_min > min_clk_period)
min_clk_period = timings->tALH_min;
if (timings->tALH_min >
min_clk_period * nfc_timings->setup_cycles)
min_clk_period = DIV_ROUND_UP(timings->tALH_min,
nfc_timings->setup_cycles);
/* T12 <=> tRP */
if (timings->tRP_min > min_clk_period)
@ -1763,17 +1864,25 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
/* T16 - T19 + tCAD */
if (timings->tWB_max > (min_clk_period * 20))
min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
if (timings->tWB_max >
(min_clk_period * nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]))
min_clk_period = DIV_ROUND_UP(timings->tWB_max,
nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]);
if (timings->tADL_min > (min_clk_period * 32))
min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
if (timings->tADL_min >
(min_clk_period * nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1]))
min_clk_period = DIV_ROUND_UP(timings->tADL_min,
nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1]);
if (timings->tWHR_min > (min_clk_period * 32))
min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
if (timings->tWHR_min >
(min_clk_period * nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1]))
min_clk_period = DIV_ROUND_UP(timings->tWHR_min,
nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1]);
if (timings->tRHW_min > (min_clk_period * 20))
min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
if (timings->tRHW_min >
(min_clk_period * nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1]))
min_clk_period = DIV_ROUND_UP(timings->tRHW_min,
nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1]);
/*
* In non-EDO, tREA should be less than tRP to guarantee that the
@ -1789,26 +1898,28 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
if (timings->tREA_max > min_clk_period && !timings->tRLOH_min)
min_clk_period = timings->tREA_max;
tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
tWB = sunxi_nand_lookup_timing(nfc_timings->tWB, timings->tWB_max,
min_clk_period);
if (tWB < 0) {
dev_err(nfc->dev, "unsupported tWB\n");
return tWB;
}
tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
if (tADL > 3) {
tADL = sunxi_nand_lookup_timing(nfc_timings->tADL,
timings->tADL_min, min_clk_period);
if (tADL < 0) {
dev_err(nfc->dev, "unsupported tADL\n");
return -EINVAL;
return tADL;
}
tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
if (tWHR > 3) {
tWHR = sunxi_nand_lookup_timing(nfc_timings->tWHR,
timings->tWHR_min, min_clk_period);
if (tWHR < 0) {
dev_err(nfc->dev, "unsupported tWHR\n");
return -EINVAL;
return tWHR;
}
tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
tRHW = sunxi_nand_lookup_timing(nfc_timings->tRHW, timings->tRHW_min,
min_clk_period);
if (tRHW < 0) {
dev_err(nfc->dev, "unsupported tRHW\n");
@ -2073,11 +2184,13 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
if (nfc->dmac || nfc->caps->has_mdma) {
if (nfc->dmac || nfc->use_mdma) {
ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
nand->options |= NAND_USES_DMA;
if (nfc->mdma_desc)
nand->buf_align = 4;
} else {
ecc->read_page = sunxi_nfc_hw_ecc_read_page;
ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
@ -2426,8 +2539,32 @@ static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
{
int ret;
if (nfc->caps->has_mdma)
if (nfc->caps->has_mdma_desc) {
ret = dma_set_mask_and_coherent(nfc->dev, DMA_BIT_MASK(32));
if (ret) {
dev_warn(nfc->dev,
"failed to set MBUS DMA mask, using PIO: %d\n",
ret);
return 0;
}
nfc->mdma_desc =
dmam_alloc_coherent(nfc->dev, sizeof(*nfc->mdma_desc),
&nfc->mdma_desc_dma, GFP_KERNEL);
if (!nfc->mdma_desc) {
dev_warn(nfc->dev,
"failed to allocate MBUS DMA descriptor, using PIO\n");
return 0;
}
nfc->use_mdma = true;
return 0;
}
if (nfc->caps->has_mdma) {
nfc->use_mdma = true;
return 0;
}
nfc->dmac = dma_request_chan(nfc->dev, "rxtx");
if (IS_ERR(nfc->dmac)) {
@ -2595,6 +2732,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
.max_ecc_steps = 16,
.sram_size = 1024,
.timings = &sun4i_a10_nfc_timings,
};
static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
@ -2617,9 +2755,11 @@ static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
.nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
.max_ecc_steps = 16,
.sram_size = 1024,
.timings = &sun4i_a10_nfc_timings,
};
static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
.has_mdma_desc = true,
.has_ecc_clk = true,
.has_mbus_clk = true,
.reg_io_data = NFC_REG_A23_IO_DATA,
@ -2641,6 +2781,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
.nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
.max_ecc_steps = 32,
.sram_size = 8192,
.timings = &sun50i_h616_nfc_timings,
};
static const struct of_device_id sunxi_nfc_ids[] = {

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
spinand-objs := core.o otp.o
spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o
spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o heyangtek.o
spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o

View File

@ -1359,6 +1359,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
&fmsh_spinand_manufacturer,
&foresee_spinand_manufacturer,
&gigadevice_spinand_manufacturer,
&heyangtek_spinand_manufacturer,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
&paragon_spinand_manufacturer,

View File

@ -9,6 +9,16 @@
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
#define FM25G01B_STATUS_ECC_MASK (7 << 4)
#define FM25G01B_STATUS_ECC_NO_BITFLIPS (0 << 4)
#define FM25G01B_STATUS_ECC_1_3_BITFLIPS (1 << 4)
#define FM25G01B_STATUS_ECC_4_BITFLIPS (2 << 4)
#define FM25G01B_STATUS_ECC_5_BITFLIPS (3 << 4)
#define FM25G01B_STATUS_ECC_6_BITFLIPS (4 << 4)
#define FM25G01B_STATUS_ECC_7_BITFLIPS (5 << 4)
#define FM25G01B_STATUS_ECC_8_BITFLIPS (6 << 4)
#define FM25G01B_STATUS_ECC_UNCOR_ERROR (7 << 4)
#define FM25S01BI3_STATUS_ECC_MASK (7 << 4)
#define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4)
#define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4)
@ -34,6 +44,74 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
static SPINAND_OP_VARIANTS(fm25g_read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
static int fm25g01b_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
region->offset = 64;
region->length = 64;
return 0;
}
static int fm25g01b_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
/* reserve 2 bytes for the BBM */
region->offset = 2;
region->length = 62;
return 0;
}
static int fm25g01b_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
switch (status & FM25G01B_STATUS_ECC_MASK) {
case FM25G01B_STATUS_ECC_NO_BITFLIPS:
return 0;
case FM25G01B_STATUS_ECC_1_3_BITFLIPS:
return 3;
case FM25G01B_STATUS_ECC_4_BITFLIPS:
return 4;
case FM25G01B_STATUS_ECC_5_BITFLIPS:
return 5;
case FM25G01B_STATUS_ECC_6_BITFLIPS:
return 6;
case FM25G01B_STATUS_ECC_7_BITFLIPS:
return 7;
case FM25G01B_STATUS_ECC_8_BITFLIPS:
return 8;
case FM25G01B_STATUS_ECC_UNCOR_ERROR:
return -EBADMSG;
default:
break;
}
return -EINVAL;
}
static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@ -102,6 +180,11 @@ static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
return 0;
}
static const struct mtd_ooblayout_ops fm25g01b_ooblayout = {
.ecc = fm25g01b_ooblayout_ecc,
.free = fm25g01b_ooblayout_free,
};
static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
.ecc = fm25s01a_ooblayout_ecc,
.free = fm25s01a_ooblayout_free,
@ -113,6 +196,26 @@ static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
};
static const struct spinand_info fmsh_spinand_table[] = {
SPINAND_INFO("FM25G01B",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd1),
NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1),
NAND_ECCREQ(8, 528),
SPINAND_INFO_OP_VARIANTS(&fm25g_read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&fm25g01b_ooblayout,
fm25g01b_ecc_get_status)),
SPINAND_INFO("FM25G02B",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd2),
NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1),
NAND_ECCREQ(8, 528),
SPINAND_INFO_OP_VARIANTS(&fm25g_read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&fm25g01b_ooblayout,
fm25g01b_ecc_get_status)),
SPINAND_INFO("FM25S01A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),

View File

@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Authors:
* Andrey Zolotarev <andrey.zolotarev@keenetic.com> - the main driver logic
* Aleksei Sviridkin <f@lex.la> - adaptation to the mainline Linux kernel
*
* Based on:
* https://github.com/keenetic/kernel-49/commit/bacade569fb12bc0ad31ba09bca9b890118fbca7
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_HEYANGTEK 0xc9
#define HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS (3 << 4)
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
/*
* HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken
* from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each
* 16-byte section, the bad block marker sits in byte 0 and the remaining
* bytes are exposed as free.
*/
static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
return -ERANGE;
region->offset = (16 * section) + 8;
region->length = 8;
return 0;
}
static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
return -ERANGE;
if (section) {
region->offset = 16 * section;
region->length = 8;
} else {
/* section 0 has one byte reserved for the bad block marker */
region->offset = 1;
region->length = 7;
}
return 0;
}
static const struct mtd_ooblayout_ops hyf1gq4_ooblayout = {
.ecc = hyf1gq4_ooblayout_ecc,
.free = hyf1gq4_ooblayout_free,
};
static int hyf1gq4_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:
/*
* The die exposes only a coarse 2-bit ECC status and has no
* register for the exact bitflip count. This code means
* "corrected, below the refresh threshold", so report half of
* the ECC strength as a representative value.
*/
return nanddev_get_ecc_conf(nand)->strength / 2;
case HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS:
/*
* "Corrected, refresh recommended": report the full ECC
* strength so the upper layers relocate the data.
*/
return nanddev_get_ecc_conf(nand)->strength;
default:
break;
}
return -EINVAL;
}
static const struct spinand_info heyangtek_spinand_table[] = {
SPINAND_INFO("HYF1GQ4UDACAE",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x21),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
NAND_ECCREQ(4, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&hyf1gq4_ooblayout,
hyf1gq4_ecc_get_status)),
};
static const struct spinand_manufacturer_ops heyangtek_spinand_manuf_ops = {
};
const struct spinand_manufacturer heyangtek_spinand_manufacturer = {
.id = SPINAND_MFR_HEYANGTEK,
.name = "HeYangTek",
.chips = heyangtek_spinand_table,
.nchips = ARRAY_SIZE(heyangtek_spinand_table),
.ops = &heyangtek_spinand_manuf_ops,
};

View File

@ -192,6 +192,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
for (i = 0; i < numslots; i++) {
struct fis_list *new_fl, **prev;
size_t name_len;
if (buf[i].name[0] == 0xff) {
if (buf[i].name[1] == 0xff) {
@ -203,8 +204,14 @@ static int parse_redboot_partitions(struct mtd_info *master,
if (!redboot_checksum(&buf[i]))
break;
name_len = strnlen(buf[i].name, sizeof(buf[i].name));
if (name_len == sizeof(buf[i].name)) {
ret = -EINVAL;
goto out;
}
new_fl = kmalloc_obj(struct fis_list);
namelen += strlen(buf[i].name) + 1;
namelen += name_len + 1;
if (!new_fl) {
ret = -ENOMEM;
goto out;

View File

@ -443,6 +443,7 @@ struct qcom_nand_controller {
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
* @supports_bam - whether NAND controller is using BAM
* @nandc_part_of_qpic - whether NAND controller is part of qpic IP
* @has_onfi_read_op - whether ONFI param page read command is supported
* @qpic_version2 - flag to indicate QPIC IP version 2
* @use_codeword_fixup - whether NAND has different layout for boot partitions
*/
@ -452,6 +453,7 @@ struct qcom_nandc_props {
u32 bam_offset;
bool supports_bam;
bool nandc_part_of_qpic;
bool has_onfi_read_op;
bool qpic_version2;
bool use_codeword_fixup;
};

View File

@ -437,6 +437,7 @@ extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
extern const struct spinand_manufacturer foresee_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer heyangtek_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;
extern const struct spinand_manufacturer paragon_spinand_manufacturer;

View File

@ -7,7 +7,6 @@
#define _MTD_NAND_OMAP2_H
#include <linux/mtd/partitions.h>
#include <linux/mod_devicetable.h>
#define GPMC_BCH_NUM_REMAINDER 8
@ -63,10 +62,4 @@ struct gpmc_nand_regs {
void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
};
static const struct of_device_id omap_nand_ids[] = {
{ .compatible = "ti,omap2-nand", },
{ .compatible = "ti,am64-nand", },
{},
};
#endif /* _MTD_NAND_OMAP2_H */