summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtdcore.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/mtdcore.c')
-rw-r--r--drivers/mtd/mtdcore.c148
1 files changed, 86 insertions, 62 deletions
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 97ac219c082e..76b4264936ff 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -41,6 +41,7 @@
#include <linux/reboot.h>
#include <linux/leds.h>
#include <linux/debugfs.h>
+#include <linux/nvmem-provider.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
@@ -154,7 +155,6 @@ static ssize_t mtd_flags_show(struct device *dev,
struct mtd_info *mtd = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)mtd->flags);
-
}
static DEVICE_ATTR(flags, S_IRUGO, mtd_flags_show, NULL);
@@ -165,7 +165,6 @@ static ssize_t mtd_size_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%llu\n",
(unsigned long long)mtd->size);
-
}
static DEVICE_ATTR(size, S_IRUGO, mtd_size_show, NULL);
@@ -175,7 +174,6 @@ static ssize_t mtd_erasesize_show(struct device *dev,
struct mtd_info *mtd = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->erasesize);
-
}
static DEVICE_ATTR(erasesize, S_IRUGO, mtd_erasesize_show, NULL);
@@ -185,7 +183,6 @@ static ssize_t mtd_writesize_show(struct device *dev,
struct mtd_info *mtd = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->writesize);
-
}
static DEVICE_ATTR(writesize, S_IRUGO, mtd_writesize_show, NULL);
@@ -196,7 +193,6 @@ static ssize_t mtd_subpagesize_show(struct device *dev,
unsigned int subpagesize = mtd->writesize >> mtd->subpage_sft;
return snprintf(buf, PAGE_SIZE, "%u\n", subpagesize);
-
}
static DEVICE_ATTR(subpagesize, S_IRUGO, mtd_subpagesize_show, NULL);
@@ -206,7 +202,6 @@ static ssize_t mtd_oobsize_show(struct device *dev,
struct mtd_info *mtd = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->oobsize);
-
}
static DEVICE_ATTR(oobsize, S_IRUGO, mtd_oobsize_show, NULL);
@@ -225,7 +220,6 @@ static ssize_t mtd_numeraseregions_show(struct device *dev,
struct mtd_info *mtd = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%u\n", mtd->numeraseregions);
-
}
static DEVICE_ATTR(numeraseregions, S_IRUGO, mtd_numeraseregions_show,
NULL);
@@ -236,7 +230,6 @@ static ssize_t mtd_name_show(struct device *dev,
struct mtd_info *mtd = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%s\n", mtd->name);
-
}
static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL);
@@ -488,6 +481,51 @@ int mtd_pairing_groups(struct mtd_info *mtd)
}
EXPORT_SYMBOL_GPL(mtd_pairing_groups);
+static int mtd_nvmem_reg_read(void *priv, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct mtd_info *mtd = priv;
+ size_t retlen;
+ int err;
+
+ err = mtd_read(mtd, offset, bytes, &retlen, val);
+ if (err && err != -EUCLEAN)
+ return err;
+
+ return retlen == bytes ? 0 : -EIO;
+}
+
+static int mtd_nvmem_add(struct mtd_info *mtd)
+{
+ struct nvmem_config config = {};
+
+ config.id = -1;
+ config.dev = &mtd->dev;
+ config.name = mtd->name;
+ config.owner = THIS_MODULE;
+ config.reg_read = mtd_nvmem_reg_read;
+ config.size = mtd->size;
+ config.word_size = 1;
+ config.stride = 1;
+ config.read_only = true;
+ config.root_only = true;
+ config.no_of_node = true;
+ config.priv = mtd;
+
+ mtd->nvmem = nvmem_register(&config);
+ if (IS_ERR(mtd->nvmem)) {
+ /* Just ignore if there is no NVMEM support in the kernel */
+ if (PTR_ERR(mtd->nvmem) == -EOPNOTSUPP) {
+ mtd->nvmem = NULL;
+ } else {
+ dev_err(&mtd->dev, "Failed to register NVMEM device\n");
+ return PTR_ERR(mtd->nvmem);
+ }
+ }
+
+ return 0;
+}
+
static struct dentry *dfs_dir_mtd;
/**
@@ -514,6 +552,14 @@ int add_mtd_device(struct mtd_info *mtd)
BUG_ON(mtd->writesize == 0);
+ /*
+ * MTD drivers should implement ->_{write,read}() or
+ * ->_{write,read}_oob(), but not both.
+ */
+ if (WARN_ON((mtd->_write && mtd->_write_oob) ||
+ (mtd->_read && mtd->_read_oob)))
+ return -EINVAL;
+
if (WARN_ON((!mtd->erasesize || !mtd->_erase) &&
!(mtd->flags & MTD_NO_ERASE)))
return -EINVAL;
@@ -570,6 +616,11 @@ int add_mtd_device(struct mtd_info *mtd)
if (error)
goto fail_added;
+ /* Add the nvmem provider */
+ error = mtd_nvmem_add(mtd);
+ if (error)
+ goto fail_nvmem_add;
+
if (!IS_ERR_OR_NULL(dfs_dir_mtd)) {
mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd);
if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) {
@@ -595,6 +646,8 @@ int add_mtd_device(struct mtd_info *mtd)
__module_get(THIS_MODULE);
return 0;
+fail_nvmem_add:
+ device_unregister(&mtd->dev);
fail_added:
of_node_put(mtd_get_of_node(mtd));
idr_remove(&mtd_idr, i);
@@ -637,6 +690,10 @@ int del_mtd_device(struct mtd_info *mtd)
mtd->index, mtd->name, mtd->usecount);
ret = -EBUSY;
} else {
+ /* Try to remove the NVMEM provider */
+ if (mtd->nvmem)
+ nvmem_unregister(mtd->nvmem);
+
device_unregister(&mtd->dev);
idr_remove(&mtd_idr, mtd->index);
@@ -665,6 +722,8 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd)
} else {
pr_debug("mtd device won't show a device symlink in sysfs\n");
}
+
+ mtd->orig_flags = mtd->flags;
}
/**
@@ -1031,67 +1090,32 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
u_char *buf)
{
- int ret_code;
- *retlen = 0;
- if (from < 0 || from >= mtd->size || len > mtd->size - from)
- return -EINVAL;
- if (!len)
- return 0;
+ struct mtd_oob_ops ops = {
+ .len = len,
+ .datbuf = buf,
+ };
+ int ret;
- ledtrig_mtd_activity();
- /*
- * In the absence of an error, drivers return a non-negative integer
- * representing the maximum number of bitflips that were corrected on
- * any one ecc region (if applicable; zero otherwise).
- */
- if (mtd->_read) {
- ret_code = mtd->_read(mtd, from, len, retlen, buf);
- } else if (mtd->_read_oob) {
- struct mtd_oob_ops ops = {
- .len = len,
- .datbuf = buf,
- };
-
- ret_code = mtd->_read_oob(mtd, from, &ops);
- *retlen = ops.retlen;
- } else {
- return -ENOTSUPP;
- }
+ ret = mtd_read_oob(mtd, from, &ops);
+ *retlen = ops.retlen;
- if (unlikely(ret_code < 0))
- return ret_code;
- if (mtd->ecc_strength == 0)
- return 0; /* device lacks ecc */
- return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(mtd_read);
int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
const u_char *buf)
{
- *retlen = 0;
- if (to < 0 || to >= mtd->size || len > mtd->size - to)
- return -EINVAL;
- if ((!mtd->_write && !mtd->_write_oob) ||
- !(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
- if (!len)
- return 0;
- ledtrig_mtd_activity();
+ struct mtd_oob_ops ops = {
+ .len = len,
+ .datbuf = (u8 *)buf,
+ };
+ int ret;
- if (!mtd->_write) {
- struct mtd_oob_ops ops = {
- .len = len,
- .datbuf = (u8 *)buf,
- };
- int ret;
+ ret = mtd_write_oob(mtd, to, &ops);
+ *retlen = ops.retlen;
- ret = mtd->_write_oob(mtd, to, &ops);
- *retlen = ops.retlen;
- return ret;
- }
-
- return mtd->_write(mtd, to, len, retlen, buf);
+ return ret;
}
EXPORT_SYMBOL_GPL(mtd_write);
@@ -1136,13 +1160,13 @@ static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs,
return -EINVAL;
if (ops->ooblen) {
- u64 maxooblen;
+ size_t maxooblen;
if (ops->ooboffs >= mtd_oobavail(mtd, ops))
return -EINVAL;
- maxooblen = ((mtd_div_by_ws(mtd->size, mtd) -
- mtd_div_by_ws(offs, mtd)) *
+ maxooblen = ((size_t)(mtd_div_by_ws(mtd->size, mtd) -
+ mtd_div_by_ws(offs, mtd)) *
mtd_oobavail(mtd, ops)) - ops->ooboffs;
if (ops->ooblen > maxooblen)
return -EINVAL;
OpenPOWER on IntegriCloud