mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 11:41:29 -04:00
Merge tag 'mtd/for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull mtd updates from Miquel Raynal:
"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 a high load of misc fixes and
hardening changes, and exceptionally no SPI NOR change"
* tag 'mtd/for-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (30 commits)
mtd: rawnand: sunxi: fix H6/H616 controller timings
mtd: rawnand: sunxi: describe tADL and tWHR delays
mtd: rawnand: sunxi: group controller delay tables
mtd: maps: remove dead select of MTD_CFI_BE_BYTE_SWAP
mtd: rawnand: gpmi: add debugfs entry for BCH geometry
mtd: rawnand: validate ONFI extended parameter page sections
mtd: rawnand: sunxi: add H616 MBUS DMA support
mtd: spinand: fmsh: fix FM25G01B/FM25G02B Quad I/O read dummy cycles
mtd: part: reject MTDPART_OFS_RETAIN in mtd_add_partition()
mtd: mpc5121_nfc: use platform for irq and ioremap
mtd: mtdoops: free page bitmap when the backing MTD is removed
mtd: mtdswap: Avoid freeing registered blktrans device twice
mtd: afs: validate v2 image info bounds
mtd: intel-dg: Fix runtime PM error path in probe
mtd: nand-omap2: Move omap_nand_ids[] to raw nand driver
mtd: rawnand: add Toshiba TC58NVG1S3H
mtd: nand: realtek-ecc: add missing MODULE_DEVICE_TABLE()
mtd: rawnand: qcom: Add MDM9607 compatible
mtd: rawnand: qcom: Make has_onfi_read_op separate from qpic_version2
mtd: rawnand: qcom: Make "aon" clock optional
...
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -2565,6 +2565,12 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
case FL_PM_SUSPENDED:
|
||||
case FL_SHUTDOWN:
|
||||
/*
|
||||
* Already suspended, or put into array mode by the
|
||||
* reboot notifier ahead of an imminent power-off.
|
||||
* Either way there is nothing to do.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
@@ -780,7 +780,7 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
|
||||
dev_name(&aux_dev->dev), invm->regions[i].name);
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
goto err_norpm;
|
||||
}
|
||||
|
||||
nvm->regions[n].name = name;
|
||||
|
||||
@@ -101,7 +101,6 @@ config MTD_PHYSMAP_IXP4XX
|
||||
depends on MTD_PHYSMAP_OF
|
||||
depends on ARM
|
||||
select MTD_COMPLEX_MAPPINGS
|
||||
select MTD_CFI_BE_BYTE_SWAP if CPU_BIG_ENDIAN
|
||||
default ARCH_IXP4XX
|
||||
help
|
||||
This provides some extra DT physmap parsing for the Intel IXP4xx
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS
|
||||
* Out-of-line map I/O functions for simple maps when CONFIG_MTD_COMPLEX_MAPPINGS
|
||||
* is enabled.
|
||||
*/
|
||||
|
||||
|
||||
@@ -392,6 +392,9 @@ static void mtdoops_notify_remove(struct mtd_info *mtd)
|
||||
cxt->mtd = NULL;
|
||||
flush_work(&cxt->work_erase);
|
||||
flush_work(&cxt->work_write);
|
||||
vfree(cxt->oops_page_used);
|
||||
cxt->oops_page_used = NULL;
|
||||
cxt->oops_pages = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -258,7 +258,8 @@ int mtd_add_partition(struct mtd_info *parent, const char *name,
|
||||
|
||||
/* the direct offset is expected */
|
||||
if (offset == MTDPART_OFS_APPEND ||
|
||||
offset == MTDPART_OFS_NXTBLK)
|
||||
offset == MTDPART_OFS_NXTBLK ||
|
||||
offset == MTDPART_OFS_RETAIN)
|
||||
return -EINVAL;
|
||||
|
||||
if (length == MTDPART_SIZ_FULL)
|
||||
|
||||
@@ -1452,6 +1452,7 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
|
||||
debugfs_failed:
|
||||
del_mtd_blktrans_dev(mbd_dev);
|
||||
mbd_dev = NULL;
|
||||
|
||||
cleanup:
|
||||
mtdswap_cleanup(d);
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/mpc5121.h>
|
||||
@@ -618,14 +617,14 @@ static int mpc5121_nfc_probe(struct platform_device *op)
|
||||
struct clk *clk;
|
||||
struct device *dev = &op->dev;
|
||||
struct mpc5121_nfc_prv *prv;
|
||||
struct resource res;
|
||||
struct mtd_info *mtd;
|
||||
struct nand_chip *chip;
|
||||
unsigned long regs_paddr, regs_size;
|
||||
const __be32 *chips_no;
|
||||
void __iomem *regs;
|
||||
int resettime = 0;
|
||||
int retval = 0;
|
||||
int rev, len;
|
||||
int irq;
|
||||
|
||||
/*
|
||||
* Check SoC revision. This driver supports only NFC
|
||||
@@ -637,6 +636,14 @@ static int mpc5121_nfc_probe(struct platform_device *op)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
regs = devm_platform_ioremap_resource(op, 0);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
irq = platform_get_irq(op, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL);
|
||||
if (!prv)
|
||||
return -ENOMEM;
|
||||
@@ -660,17 +667,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
|
||||
return retval;
|
||||
}
|
||||
|
||||
prv->irq = irq_of_parse_and_map(dn, 0);
|
||||
if (!prv->irq) {
|
||||
dev_err(dev, "Error mapping IRQ!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
retval = of_address_to_resource(dn, 0, &res);
|
||||
if (retval) {
|
||||
dev_err(dev, "Error parsing memory region!\n");
|
||||
return retval;
|
||||
}
|
||||
prv->irq = irq;
|
||||
|
||||
chips_no = of_get_property(dn, "chips", &len);
|
||||
if (!chips_no || len != sizeof(*chips_no)) {
|
||||
@@ -678,19 +675,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regs_paddr = res.start;
|
||||
regs_size = resource_size(&res);
|
||||
|
||||
if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) {
|
||||
dev_err(dev, "Error requesting memory region!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
prv->regs = devm_ioremap(dev, regs_paddr, regs_size);
|
||||
if (!prv->regs) {
|
||||
dev_err(dev, "Error mapping memory region!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prv->regs = regs;
|
||||
|
||||
mtd->name = "MPC5121 NAND";
|
||||
chip->legacy.dev_ready = mpc5121_nfc_dev_ready;
|
||||
|
||||
@@ -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) },
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1359,6 +1359,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
||||
&fmsh_spinand_manufacturer,
|
||||
&foresee_spinand_manufacturer,
|
||||
&gigadevice_spinand_manufacturer,
|
||||
&heyangtek_spinand_manufacturer,
|
||||
¯onix_spinand_manufacturer,
|
||||
µn_spinand_manufacturer,
|
||||
¶gon_spinand_manufacturer,
|
||||
|
||||
@@ -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),
|
||||
|
||||
132
drivers/mtd/nand/spi/heyangtek.c
Normal file
132
drivers/mtd/nand/spi/heyangtek.c
Normal 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,
|
||||
};
|
||||
@@ -235,6 +235,9 @@ static int afs_parse_v2_partition(struct mtd_info *mtd,
|
||||
pr_debug("Parsing v2 partition @%08x-%08x\n",
|
||||
off, off + mtd->erasesize);
|
||||
|
||||
if (mtd->erasesize < sizeof(footer))
|
||||
return -EINVAL;
|
||||
|
||||
/* First read the footer */
|
||||
ptr = off + mtd->erasesize - sizeof(footer);
|
||||
ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer);
|
||||
@@ -245,6 +248,8 @@ static int afs_parse_v2_partition(struct mtd_info *mtd,
|
||||
}
|
||||
name = (char *) &footer[0];
|
||||
version = footer[9];
|
||||
if (footer[8] > mtd->erasesize - sizeof(footer))
|
||||
return -EINVAL;
|
||||
ptr = off + mtd->erasesize - sizeof(footer) - footer[8];
|
||||
|
||||
pr_debug("found image \"%s\", version %08x, info @%08x\n",
|
||||
@@ -278,6 +283,8 @@ static int afs_parse_v2_partition(struct mtd_info *mtd,
|
||||
entrypoint = imginfo[pad];
|
||||
attributes = imginfo[pad+1];
|
||||
region_count = imginfo[pad+2];
|
||||
if (region_count > (ARRAY_SIZE(imginfo) - pad - 3) / 4)
|
||||
return -EINVAL;
|
||||
block_start = imginfo[20];
|
||||
block_end = imginfo[21];
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user