diff options
Diffstat (limited to 'drivers/nvmem/core.c')
-rw-r--r-- | drivers/nvmem/core.c | 184 |
1 files changed, 129 insertions, 55 deletions
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 06e1414769..bf393fc180 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * nvmem framework core. * * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> @@ -23,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; @@ -33,10 +24,17 @@ struct nvmem_device { size_t size; 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; @@ -48,11 +46,14 @@ struct nvmem_cell { static LIST_HEAD(nvmem_cells); static LIST_HEAD(nvmem_devs); -int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset, - size_t bytes, void *buf); -int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset, - size_t bytes, const void *buf); +void nvmem_devices_print(void) +{ + struct nvmem_device *dev; + list_for_each_entry(dev, &nvmem_devs, node) { + printf("%s\n", dev_name(&dev->dev)); + } +} static ssize_t nvmem_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, unsigned long flags) @@ -99,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; @@ -115,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; @@ -129,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->name && !strcmp(dev->dev.device_node->name, nvmem_np->name)) + if (dev->dev.of_node == nvmem_np) return dev; return NULL; @@ -140,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; @@ -149,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); } @@ -209,17 +211,20 @@ 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->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; - nvmem->read_only = of_property_read_bool(np, "read-only") | - config->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); nvmem->dev.id = DEVICE_ID_DYNAMIC; - dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); + dev_dbg(nvmem->dev.parent, "Registering nvmem device %s\n", config->name); rval = register_device(&nvmem->dev); if (rval) { @@ -227,10 +232,12 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) return ERR_PTR(rval); } - rval = nvmem_register_cdev(nvmem, config->name); - if (rval) { - kfree(nvmem); - return ERR_PTR(rval); + if (!config->cdev) { + rval = nvmem_register_cdev(nvmem, config->name); + if (rval) { + kfree(nvmem); + return ERR_PTR(rval); + } } list_add_tail(&nvmem->node, &nvmem_devs); @@ -239,13 +246,26 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) } EXPORT_SYMBOL_GPL(nvmem_register); +static int of_nvmem_device_ensure_probed(struct device_node *np) +{ + if (of_device_is_compatible(np, "nvmem-cells")) + return of_partition_ensure_probed(np); + + return of_device_ensure_probed(np); +} + static struct nvmem_device *__nvmem_device_get(struct device_node *np, struct nvmem_cell **cellp, const char *cell_id) { struct nvmem_device *nvmem = NULL; + int ret; if (np) { + ret = of_nvmem_device_ensure_probed(np); + if (ret) + return ERR_PTR(ret); + nvmem = of_nvmem_find(np); if (!nvmem) return ERR_PTR(-EPROBE_DEFER); @@ -290,13 +310,14 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) { struct device_node *nvmem_np; - int index; + int index = 0; - index = of_property_match_string(np, "nvmem-names", id); + if (id) + index = of_property_match_string(np, "nvmem-names", id); nvmem_np = of_parse_phandle(np, "nvmem", index); if (!nvmem_np) - return ERR_PTR(-EINVAL); + return ERR_PTR(-ENOENT); return __nvmem_device_get(nvmem_np, NULL, NULL); } @@ -312,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; @@ -387,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; } @@ -403,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))) { @@ -450,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; } @@ -512,14 +534,21 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem, { int rc; - rc = nvmem->bus->read(&nvmem->dev, 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; @@ -548,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); } @@ -577,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->dev, 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 */ @@ -594,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->dev, 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; } @@ -627,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->dev, 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; @@ -661,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; @@ -691,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); @@ -724,9 +759,9 @@ int nvmem_device_read(struct nvmem_device *nvmem, if (!bytes) return 0; - rc = nvmem->bus->read(&nvmem->dev, offset, buf, bytes); + rc = nvmem->reg_read(nvmem->priv, offset, buf, bytes); - if (IS_ERR_VALUE(rc)) + if (rc < 0) return rc; return bytes; @@ -758,9 +793,9 @@ int nvmem_device_write(struct nvmem_device *nvmem, if (!bytes) return 0; - rc = nvmem->bus->write(&nvmem->dev, offset, buf, bytes); + rc = nvmem->reg_write(nvmem->priv, offset, buf, bytes); - if (IS_ERR_VALUE(rc)) + if (rc < 0) return rc; @@ -790,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); |