Add a driver that implements provisions to allow creationg of fake nvmem devices and cells whose data comes from device tree blob. Signed-off-by: Andrey Smirnov <andrew.smirnov@xxxxxxxxx> --- drivers/misc/Makefile | 1 + drivers/misc/nvmem-ro-of-blob.c | 196 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 drivers/misc/nvmem-ro-of-blob.c diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 94d2a4f..c7f56f8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_JTAG) += jtag.o obj-$(CONFIG_SRAM) += sram.o obj-$(CONFIG_STATE_DRV) += state.o obj-y += mac-address-map.o +obj-y += nvmem-ro-of-blob.o diff --git a/drivers/misc/nvmem-ro-of-blob.c b/drivers/misc/nvmem-ro-of-blob.c new file mode 100644 index 0000000..8a9f091 --- /dev/null +++ b/drivers/misc/nvmem-ro-of-blob.c @@ -0,0 +1,196 @@ +#include <common.h> +#include <driver.h> +#include <init.h> +#include <malloc.h> +#include <of.h> +#include <state.h> +#include <io.h> +#include <fcntl.h> +#include <net.h> +#include <regmap.h> + +#include <linux/err.h> +#include <linux/nvmem-provider.h> +#include <linux/nvmem-consumer.h> + +struct nvmem_ro_of_blob_cell { + struct list_head node; + + unsigned int start; + unsigned int end; + unsigned int size; + + u8 *data; +}; + +struct nvmem_ro_of_blob { + struct regmap *map; + struct nvmem_config config; + struct nvmem_device *nvmem; + struct list_head cells; +}; + +struct nvmem_ro_of_blob_cell * +nvmem_ro_of_blob_reg_to_cell(struct nvmem_ro_of_blob *nrob, + unsigned int reg) +{ + struct nvmem_ro_of_blob_cell *cell; + + list_for_each_entry(cell, &nrob->cells, node) { + if (cell->start <= reg && reg <= cell->end) + return cell; + } + + return NULL; +} + + +static int nvmem_ro_of_blob_reg_write(void *ctx, unsigned int reg, + unsigned int val) +{ + return -ENOTSUPP; +} + +static int nvmem_ro_of_blob_reg_read(void *ctx, unsigned int reg, + unsigned int *val) +{ + struct nvmem_ro_of_blob *nrob = ctx; + struct nvmem_ro_of_blob_cell *cell = + nvmem_ro_of_blob_reg_to_cell(nrob, reg); + + if (!cell) + return -ENOENT; + + *val = cell->data[reg - cell->start]; + return 0; +} + +static const struct regmap_bus nvmem_ro_of_blob_regmap_bus = { + .reg_write = nvmem_ro_of_blob_reg_write, + .reg_read = nvmem_ro_of_blob_reg_read, +}; + +static const struct regmap_config nvmem_ro_of_blob_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_stride = 1, + .max_register = 0xbc, +}; + +static int nvmem_ro_of_blob_validate_dt(struct device_node *np) +{ + /* struct device_node *child; */ + /* for_each_child_of_node(np, child) { */ + /* const __be32 *addr; */ + /* int len; */ + + /* addr = of_get_property(child, "reg", &len); */ + /* if (!addr) */ + /* return -EINVAL; */ + + /* } */ + + return 0; +} + +static int nvmem_ro_of_blob_probe(struct device_d *dev) +{ + int ret; + struct list_head *p, *n; + struct device_node *np = dev->device_node; + struct device_node *child; + + struct nvmem_ro_of_blob *nrob; + struct nvmem_ro_of_blob_cell *cell; + + BUG_ON(!np); + + if (!of_get_next_child(np, NULL)) { + dev_dbg(dev, "Device node doesn't specify any cells\n"); + return -EINVAL; + } + + ret = nvmem_ro_of_blob_validate_dt(np); + if (ret < 0) { + dev_dbg(dev, "Device tree validation failed\n"); + return ret; + } + + nrob = xzalloc(sizeof(*nrob)); + INIT_LIST_HEAD(&nrob->cells); + + for_each_child_of_node(np, child) { + struct property *pp; + const __be32 *addr; + int len; + + addr = of_get_property(child, "reg", &len); + BUG_ON(!addr); + cell = xzalloc(sizeof(*cell)); + + cell->start = be32_to_cpup(addr++); + cell->size = be32_to_cpup(addr); + cell->end = cell->start + cell->size - 1; + + pp = of_find_property(child, "data", NULL); + BUG_ON(!pp || pp->length != cell->size); + cell->data = pp->value; + + list_add_tail(&cell->node, &nrob->cells); + } + + nrob->map = regmap_init(dev, + &nvmem_ro_of_blob_regmap_bus, + nrob, + &nvmem_ro_of_blob_regmap_config); + if (IS_ERR(nrob->map)) { + dev_dbg(dev, "Failed to initilize regmap\n"); + ret = PTR_ERR(nrob->map); + goto free_resources; + } + + nrob->config.name = "nvmem-ro-of-blob"; + nrob->config.read_only = true; + nrob->config.dev = dev; + + nrob->nvmem = nvmem_register(&nrob->config, nrob->map); + if (IS_ERR(nrob->nvmem)) { + dev_dbg(dev, "Filed to register nvmem device\n"); + ret = PTR_ERR(nrob->nvmem); + goto free_resources; + } + + dev_dbg(dev, "probed\n"); + return 0; + +free_resources: + if (!IS_ERR_OR_NULL(nrob->map)) + regmap_exit(nrob->map); + + list_for_each_safe(p, n, &nrob->cells) { + cell = list_entry(p, + struct nvmem_ro_of_blob_cell, + node); + list_del(p); + free(cell->data); + free(cell); + } + free(nrob); + + return ret; +} + +static __maybe_unused struct of_device_id nvmem_ro_of_blob_ids[] = { + { + .compatible = "barebox,nvmem-ro-of-blob", + }, { + /* sentinel */ + } +}; + +static struct driver_d nvmem_ro_of_blob_driver = { + .name = "nvmem-ro-of-blob", + .probe = nvmem_ro_of_blob_probe, + .of_compatible = DRV_OF_COMPAT(nvmem_ro_of_blob_ids), +}; +device_platform_driver(nvmem_ro_of_blob_driver); -- 2.5.0 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox