summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2020-12-11 13:31:14 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2020-12-11 13:31:14 +0100
commit5f2377ac5502559ecfb7d70c2c99e5d553de1cd5 (patch)
tree8502a36e8b01c7f251a5a2a9a3d98a923c886feb
parentb33e3e0f99946260200a43e783488416eeed112a (diff)
parent41716009e150053a04c19bc2028ff7e1016a5fde (diff)
downloadbarebox-5f2377ac5502559ecfb7d70c2c99e5d553de1cd5.tar.gz
barebox-5f2377ac5502559ecfb7d70c2c99e5d553de1cd5.tar.xz
Merge branch 'for-next/mtd'
-rw-r--r--drivers/mtd/core.c31
-rw-r--r--drivers/mtd/nand/Kconfig9
-rw-r--r--drivers/mtd/nand/nand_base.c7
-rw-r--r--drivers/mtd/nand/nand_denali_dt.c57
-rw-r--r--drivers/mtd/nand/nand_omap_gpmc.c9
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c9
-rw-r--r--drivers/of/partition.c37
-rw-r--r--include/linux/mtd/mtd.h3
-rw-r--r--include/of.h17
9 files changed, 158 insertions, 21 deletions
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 1b1ed53a61..af33ad665c 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -639,6 +639,34 @@ static int mtd_detect(struct device_d *dev)
return ret;
}
+static int mtd_partition_fixup_generic(struct mtd_info *mtd, struct device_node *root)
+{
+ struct cdev *cdev = &mtd->cdev;
+ struct device_node *np, *mtdnp = mtd_get_of_node(mtd);
+ char *name;
+
+ name = of_get_reproducible_name(mtdnp);
+ np = of_find_node_by_reproducible_name(root, name);
+ free(name);
+ if (!np) {
+ dev_err(&mtd->dev, "Cannot find nodepath %s, cannot fixup\n",
+ mtdnp->full_name);
+ return -EINVAL;
+ }
+
+ return of_fixup_partitions(np, cdev);
+}
+
+static int mtd_partition_fixup(struct device_node *root, void *ctx)
+{
+ struct mtd_info *mtd = ctx;
+
+ if (mtd->of_fixup)
+ return mtd->of_fixup(mtd, root);
+
+ return mtd_partition_fixup_generic(mtd, root);
+}
+
int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
{
struct mtddev_hook *hook;
@@ -706,8 +734,7 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
dev_add_param_string(&mtd->dev, "partitions", mtd_partition_set, mtd_partition_get, &mtd->partition_string, mtd);
if (IS_ENABLED(CONFIG_OFDEVICE) && np) {
of_parse_partitions(&mtd->cdev, np);
- mtd->of_path = xstrdup(np->full_name);
- ret = of_partitions_register_fixup(&mtd->cdev);
+ ret = of_register_fixup(mtd_partition_fixup, mtd);
if (ret)
goto err1;
}
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 97819e5c0b..7c93260892 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -7,11 +7,20 @@ menuconfig NAND
if NAND
+config MTD_NAND_ECC_SOFT
+ bool
+ prompt "Support software ecc"
+
config MTD_NAND_ECC_SW_BCH
select BCH
+ depends on MTD_NAND_ECC_SOFT
bool
prompt "Support software BCH ecc"
+config NAND_ECC_HW_SYNDROME
+ bool
+ prompt "Support syndrome hardware ecc controllers"
+
config NAND_ALLOW_ERASE_BAD
bool
depends on MTD_WRITE
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 5db0b5625e..427aa7f0ec 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -5098,6 +5098,9 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
+ if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SOFT))
+ return -ENOSYS;
+
if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
return -EINVAL;
@@ -5619,6 +5622,10 @@ int nand_scan_tail(struct nand_chip *chip)
if (!ecc->write_subpage && ecc->hwctl && ecc->calculate)
ecc->write_subpage = nand_write_subpage_hwecc;
case NAND_ECC_HW_SYNDROME:
+ if (!IS_ENABLED(CONFIG_NAND_ECC_HW_SYNDROME)) {
+ ret = -ENOSYS;
+ goto err_nand_manuf_cleanup;
+ }
if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) &&
(!ecc->read_page ||
ecc->read_page == nand_read_page_hwecc ||
diff --git a/drivers/mtd/nand/nand_denali_dt.c b/drivers/mtd/nand/nand_denali_dt.c
index 877c40714a..8deea0292e 100644
--- a/drivers/mtd/nand/nand_denali_dt.c
+++ b/drivers/mtd/nand/nand_denali_dt.c
@@ -51,12 +51,65 @@ static const struct denali_dt_data denali_socfpga_data = {
.ecc_caps = &denali_socfpga_ecc_caps,
};
+/*
+ * Older versions of the kernel driver require the partition nodes
+ * to be direct subnodes of the controller node. Starting with Kernel
+ * v5.2 (d8e8fd0ebf8b ("mtd: rawnand: denali: decouple controller and
+ * NAND chips")) the device node for the Denali controller is seen as a
+ * NAND controller node which has subnodes for each chip attached to that
+ * controller. The chip subnodes then hold the partitions. The barebox
+ * Denali driver also supports chip subnodes like the newer Kernel
+ * driver. To find the container node for the partitions we first try
+ * to find the chip subnodes in the Kernel device tree. Only if we
+ * can't find these we try the controller device node and put the
+ * partitions there.
+ * Note that we take the existence of the chip subnodes in the kernel
+ * device tree as a sign that we put the partitions there. When they
+ * don't exist we use the controller node. This means you have to make
+ * sure the chip subnodes exist when you start a Kernel that requires
+ * these. Beginning with Kernel v5.5 (f34a5072c465 ("mtd: rawnand: denali:
+ * remove the old unified controller/chip DT support")) the chip subnodes
+ * are mandatory for the Kernel.
+ */
+static int denali_partition_fixup(struct mtd_info *mtd, struct device_node *root)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct denali_controller *denali = container_of(chip->controller,
+ struct denali_controller,
+ controller);
+ struct device_node *np, *mtdnp = mtd_get_of_node(mtd);
+ char *name;
+
+ name = of_get_reproducible_name(mtdnp);
+ np = of_find_node_by_reproducible_name(root, name);
+ free(name);
+
+ if (np) {
+ dev_info(denali->dev, "Fixing up chip node %s\n",
+ np->full_name);
+ } else {
+ name = of_get_reproducible_name(mtdnp->parent);
+ np = of_find_node_by_reproducible_name(root, name);
+ free(name);
+
+ if (np)
+ dev_info(denali->dev, "Fixing up controller node %s\n",
+ np->full_name);
+ }
+
+ if (!np)
+ return -EINVAL;
+
+ return of_fixup_partitions(np, &mtd->cdev);
+}
+
static int denali_dt_chip_init(struct denali_controller *denali,
struct device_node *chip_np)
{
struct denali_chip *dchip;
u32 bank;
int nsels, i, ret;
+ struct mtd_info *mtd;
nsels = of_property_count_elems_of_size(chip_np, "reg", sizeof(u32));
if (nsels < 0)
@@ -66,6 +119,10 @@ static int denali_dt_chip_init(struct denali_controller *denali,
dchip->nsels = nsels;
+ mtd = nand_to_mtd(&dchip->chip);
+
+ mtd->of_fixup = denali_partition_fixup;
+
for (i = 0; i < nsels; i++) {
ret = of_property_read_u32_index(chip_np, "reg", i, &bank);
if (ret)
diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 1d81500bce..db1ca88791 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -1128,8 +1128,11 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo,
err = elm_config(BCH16_ECC,
minfo->writesize / nand->ecc.size,
nand->ecc.size, nand->ecc.bytes);
- if (err < 0)
+ if (err < 0) {
+ dev_err(oinfo->pdev, "ELM config failed: %s\n",
+ strerror(-err));
return err;
+ }
nand->ecc.read_page = gpmc_read_page_hwecc_elm;
@@ -1319,7 +1322,9 @@ static int gpmc_nand_probe(struct device_d *pdev)
omap_gpmc_eccmode_set, NULL, (int *)&oinfo->ecc_mode,
ecc_mode_strings, ARRAY_SIZE(ecc_mode_strings), oinfo);
- omap_gpmc_eccmode(oinfo, oinfo->ecc_mode);
+ err = omap_gpmc_eccmode(oinfo, oinfo->ecc_mode);
+ if (err)
+ goto out_release_mem;
/* We are all set to register with the system now! */
err = add_mtd_nand_device(minfo, "nand");
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 350b82a6be..bd748ff5b4 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -470,6 +470,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
uint32_t rem;
int ret;
+ if (!IS_ENABLED(CONFIG_MTD_WRITE))
+ return -ENOSYS;
+
dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
(long long)instr->len);
@@ -928,6 +931,9 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t actual;
int ret;
+ if (!IS_ENABLED(CONFIG_MTD_WRITE))
+ return -ENOSYS;
+
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
@@ -1000,6 +1006,9 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t retval;
int ret;
+ if (!IS_ENABLED(CONFIG_MTD_WRITE))
+ return -ENOSYS;
+
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
diff --git a/drivers/of/partition.c b/drivers/of/partition.c
index 7367073c67..65c24c5426 100644
--- a/drivers/of/partition.c
+++ b/drivers/of/partition.c
@@ -123,20 +123,16 @@ static void delete_subnodes(struct device_node *np)
}
}
-static int of_partition_fixup(struct device_node *root, void *ctx)
+int of_fixup_partitions(struct device_node *np, struct cdev *cdev)
{
- struct cdev *cdev = ctx, *partcdev;
- struct device_node *np, *part, *partnode;
- char *name;
+ struct cdev *partcdev;
+ struct device_node *part, *partnode;
int ret;
int n_cells, n_parts = 0;
if (of_partition_binding == MTD_OF_BINDING_DONTTOUCH)
return 0;
- if (!cdev->device_node)
- return -EINVAL;
-
list_for_each_entry(partcdev, &cdev->partitions, partition_entry) {
if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE)
continue;
@@ -151,15 +147,6 @@ static int of_partition_fixup(struct device_node *root, void *ctx)
else
n_cells = 1;
- name = of_get_reproducible_name(cdev->device_node);
- np = of_find_node_by_reproducible_name(root, name);
- free(name);
- if (!np) {
- dev_err(cdev->dev, "Cannot find nodepath %s, cannot fixup\n",
- cdev->device_node->full_name);
- return -EINVAL;
- }
-
partnode = of_get_child_by_name(np, "partitions");
if (partnode) {
if (of_partition_binding == MTD_OF_BINDING_LEGACY) {
@@ -242,6 +229,24 @@ static int of_partition_fixup(struct device_node *root, void *ctx)
return 0;
}
+static int of_partition_fixup(struct device_node *root, void *ctx)
+{
+ struct cdev *cdev = ctx;
+ struct device_node *np;
+ char *name;
+
+ name = of_get_reproducible_name(cdev->device_node);
+ np = of_find_node_by_reproducible_name(root, name);
+ free(name);
+ if (!np) {
+ dev_err(cdev->dev, "Cannot find nodepath %s, cannot fixup\n",
+ cdev->device_node->full_name);
+ return -EINVAL;
+ }
+
+ return of_fixup_partitions(np, cdev);
+}
+
int of_partitions_register_fixup(struct cdev *cdev)
{
return of_register_fixup(of_partition_fixup, cdev);
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index ee37dfd5cb..0d977fea25 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -206,6 +206,8 @@ struct mtd_info {
int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
int (*_block_markgood) (struct mtd_info *mtd, loff_t ofs);
+ int (*of_fixup)(struct mtd_info *mtd, struct device_node *root);
+
/* ECC status information */
struct mtd_ecc_stats ecc_stats;
/* Subpage shift (NAND) */
@@ -248,7 +250,6 @@ struct mtd_info {
struct list_head partitions_entry;
char *partition_string;
- char *of_path;
unsigned int of_binding;
};
diff --git a/include/of.h b/include/of.h
index 6181da8c61..399fb7c5ba 100644
--- a/include/of.h
+++ b/include/of.h
@@ -277,6 +277,7 @@ extern struct device_d *of_device_enable_and_register_by_alias(
struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node);
int of_parse_partitions(struct cdev *cdev, struct device_node *node);
+int of_fixup_partitions(struct device_node *np, struct cdev *cdev);
int of_partitions_register_fixup(struct cdev *cdev);
int of_device_is_stdout_path(struct device_d *dev);
const char *of_get_model(void);
@@ -305,6 +306,11 @@ static inline int of_parse_partitions(struct cdev *cdev,
return -EINVAL;
}
+static inline int of_fixup_partitions(struct device_node *np, struct cdev *cdev)
+{
+ return -ENOSYS;
+}
+
static inline int of_partitions_register_fixup(struct cdev *cdev)
{
return -ENOSYS;
@@ -405,6 +411,17 @@ static inline struct device_node *of_get_child_by_name(
return NULL;
}
+static inline char *of_get_reproducible_name(struct device_node *node)
+{
+ return NULL;
+}
+
+static inline struct device_node *
+of_find_node_by_reproducible_name(struct device_node *from, const char *name)
+{
+ return NULL;
+}
+
static inline struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)