Commit c4dfa25a authored by Alban Bedel's avatar Alban Bedel Committed by Greg Kroah-Hartman

mtd: add support for reading MTD devices via the nvmem API

Allow drivers that use the nvmem API to read data stored on MTD devices.
For this the mtd devices are registered as read-only NVMEM providers.

We don't support device tree systems for now.
Signed-off-by: default avatarAlban Bedel <albeu@free.fr>
[Bartosz:
  - include linux/nvmem-provider.h
  - set the name of the nvmem provider
  - set no_of_node to true in nvmem_config
  - don't check the return value of nvmem_unregister() - it cannot fail
  - tweaked the commit message]
Signed-off-by: default avatarBartosz Golaszewski <bgolaszewski@baylibre.com>
Acked-by: default avatarBoris Brezillon <boris.brezillon@bootlin.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 517f14d9
menuconfig MTD menuconfig MTD
tristate "Memory Technology Device (MTD) support" tristate "Memory Technology Device (MTD) support"
imply NVMEM
help help
Memory Technology Devices are flash, RAM and similar chips, often Memory Technology Devices are flash, RAM and similar chips, often
used for solid state file systems on embedded devices. This option used for solid state file systems on embedded devices. This option
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/nvmem-provider.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
...@@ -488,6 +489,50 @@ int mtd_pairing_groups(struct mtd_info *mtd) ...@@ -488,6 +489,50 @@ int mtd_pairing_groups(struct mtd_info *mtd)
} }
EXPORT_SYMBOL_GPL(mtd_pairing_groups); 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.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) == -ENOSYS) {
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; static struct dentry *dfs_dir_mtd;
/** /**
...@@ -570,6 +615,11 @@ int add_mtd_device(struct mtd_info *mtd) ...@@ -570,6 +615,11 @@ int add_mtd_device(struct mtd_info *mtd)
if (error) if (error)
goto fail_added; 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)) { if (!IS_ERR_OR_NULL(dfs_dir_mtd)) {
mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), 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)) { if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) {
...@@ -595,6 +645,8 @@ int add_mtd_device(struct mtd_info *mtd) ...@@ -595,6 +645,8 @@ int add_mtd_device(struct mtd_info *mtd)
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
return 0; return 0;
fail_nvmem_add:
device_unregister(&mtd->dev);
fail_added: fail_added:
of_node_put(mtd_get_of_node(mtd)); of_node_put(mtd_get_of_node(mtd));
idr_remove(&mtd_idr, i); idr_remove(&mtd_idr, i);
...@@ -637,6 +689,10 @@ int del_mtd_device(struct mtd_info *mtd) ...@@ -637,6 +689,10 @@ int del_mtd_device(struct mtd_info *mtd)
mtd->index, mtd->name, mtd->usecount); mtd->index, mtd->name, mtd->usecount);
ret = -EBUSY; ret = -EBUSY;
} else { } else {
/* Try to remove the NVMEM provider */
if (mtd->nvmem)
nvmem_unregister(mtd->nvmem);
device_unregister(&mtd->dev); device_unregister(&mtd->dev);
idr_remove(&mtd_idr, mtd->index); idr_remove(&mtd_idr, mtd->index);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/nvmem-provider.h>
#include <mtd/mtd-abi.h> #include <mtd/mtd-abi.h>
...@@ -341,6 +342,7 @@ struct mtd_info { ...@@ -341,6 +342,7 @@ struct mtd_info {
struct device dev; struct device dev;
int usecount; int usecount;
struct mtd_debug_info dbg; struct mtd_debug_info dbg;
struct nvmem_device *nvmem;
}; };
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment