Loading drivers/nvmem/core.c +147 −48 Original line number Diff line number Diff line Loading @@ -60,6 +60,9 @@ struct nvmem_cell { static DEFINE_MUTEX(nvmem_mutex); static DEFINE_IDA(nvmem_ida); static DEFINE_MUTEX(nvmem_cell_mutex); static LIST_HEAD(nvmem_cell_tables); #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key eeprom_lock_key; #endif Loading Loading @@ -417,6 +420,110 @@ static int nvmem_setup_compat(struct nvmem_device *nvmem, return 0; } 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; int rval = 0, i; mutex_lock(&nvmem_cell_mutex); list_for_each_entry(table, &nvmem_cell_tables, node) { if (strcmp(nvmem_dev_name(nvmem), table->nvmem_name) == 0) { for (i = 0; i < table->ncells; i++) { info = &table->cells[i]; cell = kzalloc(sizeof(*cell), GFP_KERNEL); if (!cell) { rval = -ENOMEM; goto out; } rval = nvmem_cell_info_to_nvmem_cell(nvmem, info, cell); if (rval) { kfree(cell); goto out; } nvmem_cell_add(cell); } } } out: mutex_unlock(&nvmem_cell_mutex); return rval; } static struct nvmem_cell * nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index) { struct nvmem_cell *cell = NULL; int i = 0; mutex_lock(&nvmem_mutex); list_for_each_entry(cell, &nvmem->cells, node) { if (index == i++) break; } mutex_unlock(&nvmem_mutex); return cell; } 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; const __be32 *addr; int len; parent = dev->of_node; for_each_child_of_node(parent, child) { addr = of_get_property(child, "reg", &len); if (!addr || (len < 2 * sizeof(u32))) { dev_err(dev, "nvmem: invalid reg on %pOF\n", child); return -EINVAL; } cell = kzalloc(sizeof(*cell), GFP_KERNEL); if (!cell) return -ENOMEM; cell->nvmem = nvmem; cell->offset = be32_to_cpup(addr++); cell->bytes = be32_to_cpup(addr); cell->name = child->name; addr = of_get_property(child, "bits", &len); if (addr && len == (2 * sizeof(u32))) { cell->bit_offset = be32_to_cpup(addr++); cell->nbits = be32_to_cpup(addr); } if (cell->nbits) cell->bytes = DIV_ROUND_UP( cell->nbits + cell->bit_offset, BITS_PER_BYTE); if (!IS_ALIGNED(cell->offset, nvmem->stride)) { dev_err(dev, "cell %s unaligned to nvmem stride %d\n", cell->name, nvmem->stride); /* Cells already added will be freed later. */ kfree(cell); return -EINVAL; } nvmem_cell_add(cell); } return 0; } /** * nvmem_register() - Register a nvmem device for given nvmem_config. * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem Loading Loading @@ -503,8 +610,18 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_teardown_compat; } rval = nvmem_add_cells_from_table(nvmem); if (rval) goto err_remove_cells; rval = nvmem_add_cells_from_of(nvmem); if (rval) goto err_remove_cells; return nvmem; err_remove_cells: nvmem_device_remove_all_cells(nvmem); err_teardown_compat: if (config->compat) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); Loading Loading @@ -808,10 +925,8 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *name) { struct device_node *cell_np, *nvmem_np; struct nvmem_cell *cell; struct nvmem_device *nvmem; const __be32 *addr; int rval, len; struct nvmem_cell *cell; int index = 0; /* if cell name exists, find index to the name */ Loading @@ -831,54 +946,13 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, if (IS_ERR(nvmem)) return ERR_CAST(nvmem); addr = of_get_property(cell_np, "reg", &len); if (!addr || (len < 2 * sizeof(u32))) { dev_err(&nvmem->dev, "nvmem: invalid reg on %pOF\n", cell_np); rval = -EINVAL; goto err_mem; } cell = kzalloc(sizeof(*cell), GFP_KERNEL); cell = nvmem_find_cell_by_index(nvmem, index); if (!cell) { rval = -ENOMEM; goto err_mem; } cell->nvmem = nvmem; cell->offset = be32_to_cpup(addr++); cell->bytes = be32_to_cpup(addr); cell->name = cell_np->name; addr = of_get_property(cell_np, "bits", &len); if (addr && len == (2 * sizeof(u32))) { cell->bit_offset = be32_to_cpup(addr++); cell->nbits = be32_to_cpup(addr); } if (cell->nbits) cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset, BITS_PER_BYTE); if (!IS_ALIGNED(cell->offset, nvmem->stride)) { dev_err(&nvmem->dev, "cell %s unaligned to nvmem stride %d\n", cell->name, nvmem->stride); rval = -EINVAL; goto err_sanity; __nvmem_device_put(nvmem); return ERR_PTR(-ENOENT); } nvmem_cell_add(cell); return cell; err_sanity: kfree(cell); err_mem: __nvmem_device_put(nvmem); return ERR_PTR(rval); } EXPORT_SYMBOL_GPL(of_nvmem_cell_get); #endif Loading Loading @@ -984,7 +1058,6 @@ void nvmem_cell_put(struct nvmem_cell *cell) struct nvmem_device *nvmem = cell->nvmem; __nvmem_device_put(nvmem); nvmem_cell_drop(cell); } EXPORT_SYMBOL_GPL(nvmem_cell_put); Loading Loading @@ -1312,6 +1385,32 @@ int nvmem_device_write(struct nvmem_device *nvmem, } EXPORT_SYMBOL_GPL(nvmem_device_write); /** * nvmem_add_cell_table() - register a table of cell info entries * * @table: table of cell info entries */ void nvmem_add_cell_table(struct nvmem_cell_table *table) { mutex_lock(&nvmem_cell_mutex); list_add_tail(&table->node, &nvmem_cell_tables); mutex_unlock(&nvmem_cell_mutex); } EXPORT_SYMBOL_GPL(nvmem_add_cell_table); /** * nvmem_del_cell_table() - remove a previously registered cell info table * * @table: table of cell info entries */ void nvmem_del_cell_table(struct nvmem_cell_table *table) { mutex_lock(&nvmem_cell_mutex); list_del(&table->node); mutex_unlock(&nvmem_cell_mutex); } EXPORT_SYMBOL_GPL(nvmem_del_cell_table); /** * nvmem_dev_name() - Get the name of a given nvmem device. * Loading include/linux/nvmem-provider.h +24 −9 Original line number Diff line number Diff line Loading @@ -67,6 +67,25 @@ struct nvmem_config { struct device *base_dev; }; /** * struct nvmem_cell_table - NVMEM cell definitions for given provider * * @nvmem_name: Provider name. * @cells: Array of cell definitions. * @ncells: Number of cell definitions in the array. * @node: List node. * * This structure together with related helper functions is provided for users * that don't can't access the nvmem provided structure but wish to register * cell definitions for it e.g. board files registering an EEPROM device. */ struct nvmem_cell_table { const char *nvmem_name; const struct nvmem_cell_info *cells; size_t ncells; struct list_head node; }; #if IS_ENABLED(CONFIG_NVMEM) struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); Loading @@ -77,9 +96,9 @@ struct nvmem_device *devm_nvmem_register(struct device *dev, int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem); int nvmem_add_cells(struct nvmem_device *nvmem, const struct nvmem_cell_info *info, int ncells); void nvmem_add_cell_table(struct nvmem_cell_table *table); void nvmem_del_cell_table(struct nvmem_cell_table *table); #else static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) Loading @@ -105,12 +124,8 @@ devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) } static inline int nvmem_add_cells(struct nvmem_device *nvmem, const struct nvmem_cell_info *info, int ncells) { return -ENOSYS; } static inline void nvmem_add_cell_table(struct nvmem_cell_table *table) {} static inline void nvmem_del_cell_table(struct nvmem_cell_table *table) {} #endif /* CONFIG_NVMEM */ #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ Loading
drivers/nvmem/core.c +147 −48 Original line number Diff line number Diff line Loading @@ -60,6 +60,9 @@ struct nvmem_cell { static DEFINE_MUTEX(nvmem_mutex); static DEFINE_IDA(nvmem_ida); static DEFINE_MUTEX(nvmem_cell_mutex); static LIST_HEAD(nvmem_cell_tables); #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key eeprom_lock_key; #endif Loading Loading @@ -417,6 +420,110 @@ static int nvmem_setup_compat(struct nvmem_device *nvmem, return 0; } 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; int rval = 0, i; mutex_lock(&nvmem_cell_mutex); list_for_each_entry(table, &nvmem_cell_tables, node) { if (strcmp(nvmem_dev_name(nvmem), table->nvmem_name) == 0) { for (i = 0; i < table->ncells; i++) { info = &table->cells[i]; cell = kzalloc(sizeof(*cell), GFP_KERNEL); if (!cell) { rval = -ENOMEM; goto out; } rval = nvmem_cell_info_to_nvmem_cell(nvmem, info, cell); if (rval) { kfree(cell); goto out; } nvmem_cell_add(cell); } } } out: mutex_unlock(&nvmem_cell_mutex); return rval; } static struct nvmem_cell * nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index) { struct nvmem_cell *cell = NULL; int i = 0; mutex_lock(&nvmem_mutex); list_for_each_entry(cell, &nvmem->cells, node) { if (index == i++) break; } mutex_unlock(&nvmem_mutex); return cell; } 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; const __be32 *addr; int len; parent = dev->of_node; for_each_child_of_node(parent, child) { addr = of_get_property(child, "reg", &len); if (!addr || (len < 2 * sizeof(u32))) { dev_err(dev, "nvmem: invalid reg on %pOF\n", child); return -EINVAL; } cell = kzalloc(sizeof(*cell), GFP_KERNEL); if (!cell) return -ENOMEM; cell->nvmem = nvmem; cell->offset = be32_to_cpup(addr++); cell->bytes = be32_to_cpup(addr); cell->name = child->name; addr = of_get_property(child, "bits", &len); if (addr && len == (2 * sizeof(u32))) { cell->bit_offset = be32_to_cpup(addr++); cell->nbits = be32_to_cpup(addr); } if (cell->nbits) cell->bytes = DIV_ROUND_UP( cell->nbits + cell->bit_offset, BITS_PER_BYTE); if (!IS_ALIGNED(cell->offset, nvmem->stride)) { dev_err(dev, "cell %s unaligned to nvmem stride %d\n", cell->name, nvmem->stride); /* Cells already added will be freed later. */ kfree(cell); return -EINVAL; } nvmem_cell_add(cell); } return 0; } /** * nvmem_register() - Register a nvmem device for given nvmem_config. * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem Loading Loading @@ -503,8 +610,18 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_teardown_compat; } rval = nvmem_add_cells_from_table(nvmem); if (rval) goto err_remove_cells; rval = nvmem_add_cells_from_of(nvmem); if (rval) goto err_remove_cells; return nvmem; err_remove_cells: nvmem_device_remove_all_cells(nvmem); err_teardown_compat: if (config->compat) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); Loading Loading @@ -808,10 +925,8 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *name) { struct device_node *cell_np, *nvmem_np; struct nvmem_cell *cell; struct nvmem_device *nvmem; const __be32 *addr; int rval, len; struct nvmem_cell *cell; int index = 0; /* if cell name exists, find index to the name */ Loading @@ -831,54 +946,13 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, if (IS_ERR(nvmem)) return ERR_CAST(nvmem); addr = of_get_property(cell_np, "reg", &len); if (!addr || (len < 2 * sizeof(u32))) { dev_err(&nvmem->dev, "nvmem: invalid reg on %pOF\n", cell_np); rval = -EINVAL; goto err_mem; } cell = kzalloc(sizeof(*cell), GFP_KERNEL); cell = nvmem_find_cell_by_index(nvmem, index); if (!cell) { rval = -ENOMEM; goto err_mem; } cell->nvmem = nvmem; cell->offset = be32_to_cpup(addr++); cell->bytes = be32_to_cpup(addr); cell->name = cell_np->name; addr = of_get_property(cell_np, "bits", &len); if (addr && len == (2 * sizeof(u32))) { cell->bit_offset = be32_to_cpup(addr++); cell->nbits = be32_to_cpup(addr); } if (cell->nbits) cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset, BITS_PER_BYTE); if (!IS_ALIGNED(cell->offset, nvmem->stride)) { dev_err(&nvmem->dev, "cell %s unaligned to nvmem stride %d\n", cell->name, nvmem->stride); rval = -EINVAL; goto err_sanity; __nvmem_device_put(nvmem); return ERR_PTR(-ENOENT); } nvmem_cell_add(cell); return cell; err_sanity: kfree(cell); err_mem: __nvmem_device_put(nvmem); return ERR_PTR(rval); } EXPORT_SYMBOL_GPL(of_nvmem_cell_get); #endif Loading Loading @@ -984,7 +1058,6 @@ void nvmem_cell_put(struct nvmem_cell *cell) struct nvmem_device *nvmem = cell->nvmem; __nvmem_device_put(nvmem); nvmem_cell_drop(cell); } EXPORT_SYMBOL_GPL(nvmem_cell_put); Loading Loading @@ -1312,6 +1385,32 @@ int nvmem_device_write(struct nvmem_device *nvmem, } EXPORT_SYMBOL_GPL(nvmem_device_write); /** * nvmem_add_cell_table() - register a table of cell info entries * * @table: table of cell info entries */ void nvmem_add_cell_table(struct nvmem_cell_table *table) { mutex_lock(&nvmem_cell_mutex); list_add_tail(&table->node, &nvmem_cell_tables); mutex_unlock(&nvmem_cell_mutex); } EXPORT_SYMBOL_GPL(nvmem_add_cell_table); /** * nvmem_del_cell_table() - remove a previously registered cell info table * * @table: table of cell info entries */ void nvmem_del_cell_table(struct nvmem_cell_table *table) { mutex_lock(&nvmem_cell_mutex); list_del(&table->node); mutex_unlock(&nvmem_cell_mutex); } EXPORT_SYMBOL_GPL(nvmem_del_cell_table); /** * nvmem_dev_name() - Get the name of a given nvmem device. * Loading
include/linux/nvmem-provider.h +24 −9 Original line number Diff line number Diff line Loading @@ -67,6 +67,25 @@ struct nvmem_config { struct device *base_dev; }; /** * struct nvmem_cell_table - NVMEM cell definitions for given provider * * @nvmem_name: Provider name. * @cells: Array of cell definitions. * @ncells: Number of cell definitions in the array. * @node: List node. * * This structure together with related helper functions is provided for users * that don't can't access the nvmem provided structure but wish to register * cell definitions for it e.g. board files registering an EEPROM device. */ struct nvmem_cell_table { const char *nvmem_name; const struct nvmem_cell_info *cells; size_t ncells; struct list_head node; }; #if IS_ENABLED(CONFIG_NVMEM) struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); Loading @@ -77,9 +96,9 @@ struct nvmem_device *devm_nvmem_register(struct device *dev, int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem); int nvmem_add_cells(struct nvmem_device *nvmem, const struct nvmem_cell_info *info, int ncells); void nvmem_add_cell_table(struct nvmem_cell_table *table); void nvmem_del_cell_table(struct nvmem_cell_table *table); #else static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) Loading @@ -105,12 +124,8 @@ devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) } static inline int nvmem_add_cells(struct nvmem_device *nvmem, const struct nvmem_cell_info *info, int ncells) { return -ENOSYS; } static inline void nvmem_add_cell_table(struct nvmem_cell_table *table) {} static inline void nvmem_del_cell_table(struct nvmem_cell_table *table) {} #endif /* CONFIG_NVMEM */ #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */