summaryrefslogtreecommitdiffstats
path: root/drivers/nvmem/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nvmem/core.c')
-rw-r--r--drivers/nvmem/core.c127
1 files changed, 94 insertions, 33 deletions
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index fed387c43a..bf393fc180 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -15,8 +15,7 @@
struct nvmem_device {
const char *name;
- struct device_d dev;
- const struct nvmem_bus *bus;
+ struct device dev;
struct list_head node;
int stride;
int word_size;
@@ -26,10 +25,16 @@ struct nvmem_device {
bool read_only;
struct cdev cdev;
void *priv;
+ nvmem_cell_post_process_t cell_post_process;
+ int (*reg_write)(void *ctx, unsigned int reg,
+ const void *val, size_t val_size);
+ int (*reg_read)(void *ctx, unsigned int reg,
+ void *val, size_t val_size);
};
struct nvmem_cell {
const char *name;
+ const char *id;
int offset;
int bytes;
int bit_offset;
@@ -95,12 +100,12 @@ static struct cdev_operations nvmem_chrdev_ops = {
static int nvmem_register_cdev(struct nvmem_device *nvmem, const char *name)
{
- struct device_d *dev = &nvmem->dev;
+ struct device *dev = &nvmem->dev;
struct cdev *cdev = &nvmem->cdev;
const char *alias;
int ret;
- alias = of_alias_get(dev->device_node);
+ alias = of_alias_get(dev->of_node);
cdev->name = xstrdup(alias ?: name);
cdev->ops = &nvmem_chrdev_ops;
@@ -111,7 +116,7 @@ static int nvmem_register_cdev(struct nvmem_device *nvmem, const char *name)
if (ret)
return ret;
- of_parse_partitions(cdev, dev->device_node);
+ of_parse_partitions(cdev, dev->of_node);
of_partitions_register_fixup(cdev);
return 0;
@@ -125,7 +130,7 @@ static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np)
return NULL;
list_for_each_entry(dev, &nvmem_devs, node)
- if (dev->dev.device_node == nvmem_np)
+ if (dev->dev.of_node == nvmem_np)
return dev;
return NULL;
@@ -136,7 +141,7 @@ static struct nvmem_cell *nvmem_find_cell(const char *cell_id)
struct nvmem_cell *p;
list_for_each_entry(p, &nvmem_cells, node)
- if (p && !strcmp(p->name, cell_id))
+ if (!strcmp(p->name, cell_id))
return p;
return NULL;
@@ -145,6 +150,7 @@ static struct nvmem_cell *nvmem_find_cell(const char *cell_id)
static void nvmem_cell_drop(struct nvmem_cell *cell)
{
list_del(&cell->node);
+ kfree(cell->id);
kfree(cell);
}
@@ -205,12 +211,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->word_size = config->word_size;
nvmem->size = config->size;
nvmem->dev.parent = config->dev;
- nvmem->bus = config->bus;
- np = config->cdev ? config->cdev->device_node : config->dev->device_node;
- nvmem->dev.device_node = np;
+ nvmem->reg_read = config->reg_read;
+ nvmem->reg_write = config->reg_write;
+ np = config->cdev ? cdev_of_node(config->cdev) : config->dev->of_node;
+ nvmem->dev.of_node = np;
nvmem->priv = config->priv;
+ nvmem->cell_post_process = config->cell_post_process;
- if (config->read_only || !config->bus->write || of_property_read_bool(np, "read-only"))
+ if (config->read_only || !config->reg_write || of_property_read_bool(np, "read-only"))
nvmem->read_only = true;
dev_set_name(&nvmem->dev, config->name);
@@ -325,12 +333,13 @@ EXPORT_SYMBOL_GPL(of_nvmem_device_get);
* Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
* on success.
*/
-struct nvmem_device *nvmem_device_get(struct device_d *dev, const char *dev_name)
+struct nvmem_device *nvmem_device_get(struct device *dev,
+ const char *dev_name)
{
- if (dev->device_node) { /* try dt first */
+ if (dev->of_node) { /* try dt first */
struct nvmem_device *nvmem;
- nvmem = of_nvmem_device_get(dev->device_node, dev_name);
+ nvmem = of_nvmem_device_get(dev->of_node, dev_name);
if (!IS_ERR(nvmem) || PTR_ERR(nvmem) == -EPROBE_DEFER)
return nvmem;
@@ -400,8 +409,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
addr = of_get_property(cell_np, "reg", &len);
if (!addr || (len < 2 * sizeof(u32))) {
- dev_err(&nvmem->dev, "nvmem: invalid reg on %s\n",
- cell_np->full_name);
+ dev_err(&nvmem->dev, "nvmem: invalid reg on %pOF\n", cell_np);
rval = -EINVAL;
goto err_mem;
}
@@ -416,6 +424,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
cell->offset = be32_to_cpup(addr++);
cell->bytes = be32_to_cpup(addr);
cell->name = cell_np->name;
+ cell->id = kstrdup_const(name, GFP_KERNEL);
addr = of_get_property(cell_np, "bits", &len);
if (addr && len == (2 * sizeof(u32))) {
@@ -463,12 +472,12 @@ EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
* to a struct nvmem_cell. The nvmem_cell will be freed by the
* nvmem_cell_put().
*/
-struct nvmem_cell *nvmem_cell_get(struct device_d *dev, const char *cell_id)
+struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *cell_id)
{
struct nvmem_cell *cell;
- if (dev->device_node) { /* try dt first */
- cell = of_nvmem_cell_get(dev->device_node, cell_id);
+ if (dev->of_node) { /* try dt first */
+ cell = of_nvmem_cell_get(dev->of_node, cell_id);
if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER)
return cell;
}
@@ -525,14 +534,21 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
{
int rc;
- rc = nvmem->bus->read(nvmem->priv, cell->offset, buf, cell->bytes);
- if (IS_ERR_VALUE(rc))
+ rc = nvmem->reg_read(nvmem->priv, cell->offset, buf, cell->bytes);
+ if (rc < 0)
return rc;
/* shift bits in-place */
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, cell->id,
+ cell->offset, buf, cell->bytes);
+ if (rc)
+ return rc;
+ }
+
*len = cell->bytes;
return 0;
@@ -561,7 +577,7 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
return ERR_PTR(-ENOMEM);
rc = __nvmem_cell_read(nvmem, cell, buf, len);
- if (IS_ERR_VALUE(rc)) {
+ if (rc < 0) {
kfree(buf);
return ERR_PTR(rc);
}
@@ -590,7 +606,10 @@ static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
*b <<= bit_offset;
/* setup the first byte with lsb bits from nvmem */
- rc = nvmem->bus->read(nvmem->priv, cell->offset, &v, 1);
+ rc = nvmem->reg_read(nvmem->priv, cell->offset, &v, 1);
+ if (rc < 0)
+ return ERR_PTR(rc);
+
*b++ |= GENMASK(bit_offset - 1, 0) & v;
/* setup rest of the byte if any */
@@ -607,8 +626,11 @@ static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
/* if it's not end on byte boundary */
if ((nbits + bit_offset) % BITS_PER_BYTE) {
/* setup the last byte with msb bits from nvmem */
- rc = nvmem->bus->read(nvmem->priv, cell->offset + cell->bytes - 1,
+ rc = nvmem->reg_read(nvmem->priv, cell->offset + cell->bytes - 1,
&v, 1);
+ if (rc < 0)
+ return ERR_PTR(rc);
+
*p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
}
@@ -640,13 +662,13 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
return PTR_ERR(buf);
}
- rc = nvmem->bus->write(nvmem->priv, cell->offset, buf, cell->bytes);
+ rc = nvmem->reg_write(nvmem->priv, cell->offset, buf, cell->bytes);
/* free the tmp buffer */
if (cell->bit_offset || cell->nbits)
kfree(buf);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
return len;
@@ -674,11 +696,11 @@ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
return -EINVAL;
rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
rc = __nvmem_cell_read(nvmem, &cell, buf, &len);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
return len;
@@ -704,7 +726,7 @@ int nvmem_device_cell_write(struct nvmem_device *nvmem,
return -EINVAL;
rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
return nvmem_cell_write(&cell, buf, cell.bytes);
@@ -737,9 +759,9 @@ int nvmem_device_read(struct nvmem_device *nvmem,
if (!bytes)
return 0;
- rc = nvmem->bus->read(nvmem->priv, offset, buf, bytes);
+ rc = nvmem->reg_read(nvmem->priv, offset, buf, bytes);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
return bytes;
@@ -771,9 +793,9 @@ int nvmem_device_write(struct nvmem_device *nvmem,
if (!bytes)
return 0;
- rc = nvmem->bus->write(nvmem->priv, offset, buf, bytes);
+ rc = nvmem->reg_write(nvmem->priv, offset, buf, bytes);
- if (IS_ERR_VALUE(rc))
+ if (rc < 0)
return rc;
@@ -803,3 +825,42 @@ void *nvmem_cell_get_and_read(struct device_node *np, const char *cell_name,
return value;
}
EXPORT_SYMBOL_GPL(nvmem_cell_get_and_read);
+
+/**
+ * nvmem_cell_read_variable_le_u32() - Read up to 32-bits of data as a little endian number.
+ *
+ * @dev: Device that requests the nvmem cell.
+ * @cell_id: Name of nvmem cell to read.
+ * @val: pointer to output value.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id,
+ u32 *val)
+{
+ size_t len;
+ const u8 *buf;
+ int i;
+
+ len = sizeof(*val);
+
+ buf = nvmem_cell_get_and_read(dev->of_node, cell_id, len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* Copy w/ implicit endian conversion */
+ *val = 0;
+ for (i = 0; i < len; i++)
+ *val |= buf[i] << (8 * i);
+
+ kfree(buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u32);
+
+struct device *nvmem_device_get_device(struct nvmem_device *nvmem)
+{
+ return &nvmem->dev;
+}
+EXPORT_SYMBOL_GPL(nvmem_device_get_device);