This patch set aims at providing nvmem cell post processing support.
cell post processing is required on some provider drivers as the data
is stored in encoded format that is not what consumer drivers are expecting,
example mac-address is stored in ascii or with delimiter or in reverse order.
This is much specific to vendor, this patchset would allow nvmem provider
drivers to post-process this before consumers sees the data.
First patch is to rework nvmem cell instance creation, which will help in
creating nvmem cell instances dynamically and associate a name with it.
Next to patches are adding post-processing callback and using it in imx driver.
thanks,
srini
Srinivas Kandagatla (3):
nvmem: core: rework nvmem cell instance creation
nvmem: core: add nvmem cell post processing callback
nvmem: imx-ocotp: add support for post processing
drivers/nvmem/core.c | 149 ++++++++++++++++++++++-----------
drivers/nvmem/imx-ocotp.c | 25 ++++++
include/linux/nvmem-provider.h | 5 ++
3 files changed, 132 insertions(+), 47 deletions(-)
--
2.21.0
In the existing design, instance of nvmem cell is directly by referencing
the list of nvmem cell entries that are added to provider.
However this design has some limitations when consumers want to assign name
or connection id the nvmem cell instance, ex: from "nvmem-cell-names" or
id in nvmem_cell_get().
Having a name associated with nvmem cell instance will also help provider
drivers in performing post processing of nvmem cell data if required
before its seen by the consumers.
With this patch nvmem cell will be created dynamically during nvmem_cell_get
and destroyed in nvmem_cell_put, allowing consumers to associate name with
nvmem cell instance.
Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/nvmem/core.c | 140 ++++++++++++++++++++++++++++---------------
1 file changed, 93 insertions(+), 47 deletions(-)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 3d87fadaa160..9da4e8f42d86 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -45,8 +45,7 @@ struct nvmem_device {
#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
#define FLAG_COMPAT BIT(0)
-
-struct nvmem_cell {
+struct nvmem_cell_entry {
const char *name;
int offset;
int bytes;
@@ -57,6 +56,11 @@ struct nvmem_cell {
struct list_head node;
};
+struct nvmem_cell {
+ struct nvmem_cell_entry *entry;
+ const char *id;
+};
+
static DEFINE_MUTEX(nvmem_mutex);
static DEFINE_IDA(nvmem_ida);
@@ -422,7 +426,7 @@ static struct bus_type nvmem_bus_type = {
.name = "nvmem",
};
-static void nvmem_cell_drop(struct nvmem_cell *cell)
+static void nvmem_cell_drop(struct nvmem_cell_entry *cell)
{
blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell);
mutex_lock(&nvmem_mutex);
@@ -435,13 +439,13 @@ static void nvmem_cell_drop(struct nvmem_cell *cell)
static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem)
{
- struct nvmem_cell *cell, *p;
+ struct nvmem_cell_entry *cell, *p;
list_for_each_entry_safe(cell, p, &nvmem->cells, node)
nvmem_cell_drop(cell);
}
-static void nvmem_cell_add(struct nvmem_cell *cell)
+static void nvmem_cell_add(struct nvmem_cell_entry *cell)
{
mutex_lock(&nvmem_mutex);
list_add_tail(&cell->node, &cell->nvmem->cells);
@@ -451,7 +455,7 @@ static void nvmem_cell_add(struct nvmem_cell *cell)
static int nvmem_cell_info_to_nvmem_cell_nodup(struct nvmem_device *nvmem,
const struct nvmem_cell_info *info,
- struct nvmem_cell *cell)
+ struct nvmem_cell_entry *cell)
{
cell->nvmem = nvmem;
cell->offset = info->offset;
@@ -477,7 +481,7 @@ static int nvmem_cell_info_to_nvmem_cell_nodup(struct nvmem_device *nvmem,
static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem,
const struct nvmem_cell_info *info,
- struct nvmem_cell *cell)
+ struct nvmem_cell_entry *cell)
{
int err;
@@ -505,7 +509,7 @@ static int nvmem_add_cells(struct nvmem_device *nvmem,
const struct nvmem_cell_info *info,
int ncells)
{
- struct nvmem_cell **cells;
+ struct nvmem_cell_entry **cells;
int i, rval;
cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL);
@@ -571,7 +575,7 @@ static int nvmem_add_cells_from_table(struct nvmem_device *nvmem)
{
const struct nvmem_cell_info *info;
struct nvmem_cell_table *table;
- struct nvmem_cell *cell;
+ struct nvmem_cell_entry *cell;
int rval = 0, i;
mutex_lock(&nvmem_cell_mutex);
@@ -604,10 +608,10 @@ static int nvmem_add_cells_from_table(struct nvmem_device *nvmem)
return rval;
}
-static struct nvmem_cell *
+static struct nvmem_cell_entry *
nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id)
{
- struct nvmem_cell *iter, *cell = NULL;
+ struct nvmem_cell_entry *iter, *cell = NULL;
mutex_lock(&nvmem_mutex);
list_for_each_entry(iter, &nvmem->cells, node) {
@@ -678,7 +682,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
{
struct device_node *parent, *child;
struct device *dev = &nvmem->dev;
- struct nvmem_cell *cell;
+ struct nvmem_cell_entry *cell;
const __be32 *addr;
int len;
@@ -1142,9 +1146,34 @@ struct nvmem_device *devm_nvmem_device_get(struct device *dev, const char *id)
}
EXPORT_SYMBOL_GPL(devm_nvmem_device_get);
+static struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry, const char *id)
+{
+ struct nvmem_cell *cell;
+ const char *name = NULL;
+
+ cell = kzalloc(sizeof(*cell), GFP_KERNEL);
+ if (!cell)
+ return ERR_PTR(-ENOMEM);
+
+ if (id) {
+ name = kstrdup_const(id, GFP_KERNEL);
+ if (!name) {
+ kfree(cell);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ cell->id = name;
+ cell->entry = entry;
+
+
+ return cell;
+}
+
static struct nvmem_cell *
nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
{
+ struct nvmem_cell_entry *cell_entry = NULL;
struct nvmem_cell *cell = ERR_PTR(-ENOENT);
struct nvmem_cell_lookup *lookup;
struct nvmem_device *nvmem;
@@ -1169,11 +1198,15 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
break;
}
- cell = nvmem_find_cell_by_name(nvmem,
+ cell_entry = nvmem_find_cell_by_name(nvmem,
lookup->cell_name);
- if (!cell) {
+ if (!cell_entry) {
__nvmem_device_put(nvmem);
cell = ERR_PTR(-ENOENT);
+ } else {
+ cell = nvmem_create_cell(cell_entry, con_id);
+ if (IS_ERR(cell))
+ __nvmem_device_put(nvmem);
}
break;
}
@@ -1184,10 +1217,10 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
}
#if IS_ENABLED(CONFIG_OF)
-static struct nvmem_cell *
+static struct nvmem_cell_entry *
nvmem_find_cell_by_node(struct nvmem_device *nvmem, struct device_node *np)
{
- struct nvmem_cell *iter, *cell = NULL;
+ struct nvmem_cell_entry *iter, *cell = NULL;
mutex_lock(&nvmem_mutex);
list_for_each_entry(iter, &nvmem->cells, node) {
@@ -1217,6 +1250,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
{
struct device_node *cell_np, *nvmem_np;
struct nvmem_device *nvmem;
+ struct nvmem_cell_entry *cell_entry;
struct nvmem_cell *cell;
int index = 0;
@@ -1237,12 +1271,16 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
if (IS_ERR(nvmem))
return ERR_CAST(nvmem);
- cell = nvmem_find_cell_by_node(nvmem, cell_np);
- if (!cell) {
+ cell_entry = nvmem_find_cell_by_node(nvmem, cell_np);
+ if (!cell_entry) {
__nvmem_device_put(nvmem);
return ERR_PTR(-ENOENT);
}
+ cell = nvmem_create_cell(cell_entry, id);
+ if (IS_ERR(cell))
+ __nvmem_device_put(nvmem);
+
return cell;
}
EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
@@ -1348,13 +1386,15 @@ EXPORT_SYMBOL(devm_nvmem_cell_put);
*/
void nvmem_cell_put(struct nvmem_cell *cell)
{
- struct nvmem_device *nvmem = cell->nvmem;
+ struct nvmem_device *nvmem = cell->entry->nvmem;
+ kfree_const(cell->id);
+ kfree(cell);
__nvmem_device_put(nvmem);
}
EXPORT_SYMBOL_GPL(nvmem_cell_put);
-static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
+static void nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void *buf)
{
u8 *p, *b;
int i, extra, bit_offset = cell->bit_offset;
@@ -1387,8 +1427,8 @@ static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
}
static int __nvmem_cell_read(struct nvmem_device *nvmem,
- struct nvmem_cell *cell,
- void *buf, size_t *len)
+ struct nvmem_cell_entry *cell,
+ void *buf, size_t *len, const char *id)
{
int rc;
@@ -1419,18 +1459,18 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
*/
void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
{
- struct nvmem_device *nvmem = cell->nvmem;
+ struct nvmem_device *nvmem = cell->entry->nvmem;
u8 *buf;
int rc;
if (!nvmem)
return ERR_PTR(-EINVAL);
- buf = kzalloc(cell->bytes, GFP_KERNEL);
+ buf = kzalloc(cell->entry->bytes, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
- rc = __nvmem_cell_read(nvmem, cell, buf, len);
+ rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id);
if (rc) {
kfree(buf);
return ERR_PTR(rc);
@@ -1440,7 +1480,7 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
}
EXPORT_SYMBOL_GPL(nvmem_cell_read);
-static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
+static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell_entry *cell,
u8 *_buf, int len)
{
struct nvmem_device *nvmem = cell->nvmem;
@@ -1493,34 +1533,25 @@ static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
return ERR_PTR(rc);
}
-/**
- * nvmem_cell_write() - Write to a given nvmem cell
- *
- * @cell: nvmem cell to be written.
- * @buf: Buffer to be written.
- * @len: length of buffer to be written to nvmem cell.
- *
- * Return: length of bytes written or negative on failure.
- */
-int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
+static int __nvmem_cell_write(struct nvmem_cell_entry *entry, void *buf, size_t len)
{
- struct nvmem_device *nvmem = cell->nvmem;
+ struct nvmem_device *nvmem = entry->nvmem;
int rc;
if (!nvmem || nvmem->read_only ||
- (cell->bit_offset == 0 && len != cell->bytes))
+ (entry->bit_offset == 0 && len != entry->bytes))
return -EINVAL;
- if (cell->bit_offset || cell->nbits) {
- buf = nvmem_cell_prepare_write_buffer(cell, buf, len);
+ if (entry->bit_offset || entry->nbits) {
+ buf = nvmem_cell_prepare_write_buffer(entry, buf, len);
if (IS_ERR(buf))
return PTR_ERR(buf);
}
- rc = nvmem_reg_write(nvmem, cell->offset, buf, cell->bytes);
+ rc = nvmem_reg_write(nvmem, entry->offset, buf, entry->bytes);
/* free the tmp buffer */
- if (cell->bit_offset || cell->nbits)
+ if (entry->bit_offset || entry->nbits)
kfree(buf);
if (rc)
@@ -1528,6 +1559,21 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
return len;
}
+
+/**
+ * nvmem_cell_write() - Write to a given nvmem cell
+ *
+ * @cell: nvmem cell to be written.
+ * @buf: Buffer to be written.
+ * @len: length of buffer to be written to nvmem cell.
+ *
+ * Return: length of bytes written or negative on failure.
+ */
+int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
+{
+ return __nvmem_cell_write(cell->entry, buf, len);
+}
+
EXPORT_SYMBOL_GPL(nvmem_cell_write);
static int nvmem_cell_read_common(struct device *dev, const char *cell_id,
@@ -1630,7 +1676,7 @@ static const void *nvmem_cell_read_variable_common(struct device *dev,
if (IS_ERR(cell))
return cell;
- nbits = cell->nbits;
+ nbits = cell->entry->nbits;
buf = nvmem_cell_read(cell, len);
nvmem_cell_put(cell);
if (IS_ERR(buf))
@@ -1726,7 +1772,7 @@ EXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u64);
ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf)
{
- struct nvmem_cell cell;
+ struct nvmem_cell_entry cell;
int rc;
ssize_t len;
@@ -1737,7 +1783,7 @@ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
if (rc)
return rc;
- rc = __nvmem_cell_read(nvmem, &cell, buf, &len);
+ rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL);
if (rc)
return rc;
@@ -1757,7 +1803,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_cell_read);
int nvmem_device_cell_write(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf)
{
- struct nvmem_cell cell;
+ struct nvmem_cell_entry cell;
int rc;
if (!nvmem)
@@ -1767,7 +1813,7 @@ int nvmem_device_cell_write(struct nvmem_device *nvmem,
if (rc)
return rc;
- return nvmem_cell_write(&cell, buf, cell.bytes);
+ return __nvmem_cell_write(&cell, buf, cell.bytes);
}
EXPORT_SYMBOL_GPL(nvmem_device_cell_write);
--
2.21.0
Add .cell_post_process callback for imx-ocotp to deal with MAC address,
since MAC address need to be reversed byte for some i.MX SoCs.
Signed-off-by: Srinivas Kandagatla <[email protected]>
Signed-off-by: Joakim Zhang <[email protected]>
---
drivers/nvmem/imx-ocotp.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index 08f41328cc71..89fd4d3eb649 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -97,6 +97,7 @@ struct ocotp_params {
unsigned int bank_address_words;
void (*set_timing)(struct ocotp_priv *priv);
struct ocotp_ctrl_reg ctrl;
+ bool reverse_mac_address;
};
static int imx_ocotp_wait_for_busy(struct ocotp_priv *priv, u32 flags)
@@ -221,6 +222,25 @@ static int imx_ocotp_read(void *context, unsigned int offset,
return ret;
}
+static int imx_ocotp_cell_pp(void *context, const char *id, unsigned int offset,
+ void *data, size_t bytes)
+{
+ struct ocotp_priv *priv = context;
+
+ /* Deal with some post processing of nvmem cell data */
+ if (!strcmp(id, "mac-address")) {
+ if (priv->params->reverse_mac_address) {
+ u8 *buf = data;
+ int i;
+
+ for (i = 0; i < bytes/2; i++)
+ swap(buf[i], buf[bytes - i - 1]);
+ }
+ }
+
+ return 0;
+}
+
static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv)
{
unsigned long clk_rate;
@@ -468,6 +488,7 @@ static struct nvmem_config imx_ocotp_nvmem_config = {
.stride = 1,
.reg_read = imx_ocotp_read,
.reg_write = imx_ocotp_write,
+ .cell_post_process = imx_ocotp_cell_pp,
};
static const struct ocotp_params imx6q_params = {
@@ -530,6 +551,7 @@ static const struct ocotp_params imx8mq_params = {
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
+ .reverse_mac_address = true,
};
static const struct ocotp_params imx8mm_params = {
@@ -537,6 +559,7 @@ static const struct ocotp_params imx8mm_params = {
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
+ .reverse_mac_address = true,
};
static const struct ocotp_params imx8mn_params = {
@@ -544,6 +567,7 @@ static const struct ocotp_params imx8mn_params = {
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_DEFAULT,
+ .reverse_mac_address = true,
};
static const struct ocotp_params imx8mp_params = {
@@ -551,6 +575,7 @@ static const struct ocotp_params imx8mp_params = {
.bank_address_words = 0,
.set_timing = imx_ocotp_set_imx6_timing,
.ctrl = IMX_OCOTP_BM_CTRL_8MP,
+ .reverse_mac_address = true,
};
static const struct of_device_id imx_ocotp_dt_ids[] = {
--
2.21.0
Some NVMEM providers have certain nvmem cells encoded, which requires
post processing before actually using it.
For example mac-address is stored in either in ascii or delimited or reverse-order.
Having a post-process callback hook to provider drivers would enable them to
do this vendor specific post processing before nvmem consumers see it.
Signed-off-by: Srinivas Kandagatla <[email protected]>
---
drivers/nvmem/core.c | 9 +++++++++
include/linux/nvmem-provider.h | 5 +++++
2 files changed, 14 insertions(+)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 9da4e8f42d86..108960aa49ab 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -38,6 +38,7 @@ struct nvmem_device {
unsigned int nkeepout;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
+ nvmem_cell_post_process_t cell_post_process;
struct gpio_desc *wp_gpio;
void *priv;
};
@@ -798,6 +799,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->type = config->type;
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
+ nvmem->cell_post_process = config->cell_post_process;
nvmem->keepout = config->keepout;
nvmem->nkeepout = config->nkeepout;
if (config->of_node)
@@ -1441,6 +1443,13 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
if (cell->bit_offset || cell->nbits)
nvmem_shift_read_buffer_in_place(cell, buf);
+ if (nvmem->cell_post_process) {
+ rc = nvmem->cell_post_process(nvmem->priv, id,
+ cell->offset, buf, cell->bytes);
+ if (rc)
+ return rc;
+ }
+
if (len)
*len = cell->bytes;
diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h
index 104505e9028f..98efb7b5660d 100644
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -19,6 +19,9 @@ typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
void *val, size_t bytes);
typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
void *val, size_t bytes);
+/* used for vendor specific post processing of cell data */
+typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, unsigned int offset,
+ void *buf, size_t bytes);
enum nvmem_type {
NVMEM_TYPE_UNKNOWN = 0,
@@ -62,6 +65,7 @@ struct nvmem_keepout {
* @no_of_node: Device should not use the parent's of_node even if it's !NULL.
* @reg_read: Callback to read data.
* @reg_write: Callback to write data.
+ * @cell_post_process: Callback for vendor specific post processing of cell data
* @size: Device size.
* @word_size: Minimum read/write access granularity.
* @stride: Minimum read/write access stride.
@@ -92,6 +96,7 @@ struct nvmem_config {
bool no_of_node;
nvmem_reg_read_t reg_read;
nvmem_reg_write_t reg_write;
+ nvmem_cell_post_process_t cell_post_process;
int size;
int word_size;
int stride;
--
2.21.0