drivers/nvmem/core.c

Source file repositories/reference/linux-study-clean/drivers/nvmem/core.c

File Facts

System
Linux kernel
Corpus path
drivers/nvmem/core.c
Extension
.c
Size
51414 bytes
Lines
2195
Domain
Driver Families
Bucket
drivers/nvmem
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct bus_type nvmem_bus_type = {
	.name		= "nvmem",
};

static void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell)
{
	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell);
	mutex_lock(&nvmem_mutex);
	list_del(&cell->node);
	mutex_unlock(&nvmem_mutex);
	of_node_put(cell->np);
	kfree_const(cell->name);
	kfree(cell);
}

static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem)
{
	struct nvmem_cell_entry *cell, *p;

	list_for_each_entry_safe(cell, p, &nvmem->cells, node)
		nvmem_cell_entry_drop(cell);
}

static void nvmem_cell_entry_add(struct nvmem_cell_entry *cell)
{
	mutex_lock(&nvmem_mutex);
	list_add_tail(&cell->node, &cell->nvmem->cells);
	mutex_unlock(&nvmem_mutex);
	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell);
}

static int nvmem_cell_info_to_nvmem_cell_entry_nodup(struct nvmem_device *nvmem,
						     const struct nvmem_cell_info *info,
						     struct nvmem_cell_entry *cell)
{
	cell->nvmem = nvmem;
	cell->offset = info->offset;
	cell->raw_len = info->raw_len ?: info->bytes;
	cell->bytes = info->bytes;
	cell->name = info->name;
	cell->read_post_process = info->read_post_process;
	cell->priv = info->priv;

	cell->bit_offset = info->bit_offset;
	cell->nbits = info->nbits;
	cell->np = info->np;

	if (cell->nbits) {
		cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset,
					   BITS_PER_BYTE);
		cell->raw_len = ALIGN(cell->bytes, nvmem->word_size);
	}

	if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
		dev_err(&nvmem->dev,
			"cell %s unaligned to nvmem stride %d\n",
			cell->name ?: "<unknown>", nvmem->stride);
		return -EINVAL;
	}

	if (!IS_ALIGNED(cell->raw_len, nvmem->word_size)) {
		dev_err(&nvmem->dev,
			"cell %s raw len %zd unaligned to nvmem word size %d\n",
			cell->name ?: "<unknown>", cell->raw_len,
			nvmem->word_size);

		if (info->raw_len)
			return -EINVAL;

		cell->raw_len = ALIGN(cell->raw_len, nvmem->word_size);
	}

	return 0;
}

static int nvmem_cell_info_to_nvmem_cell_entry(struct nvmem_device *nvmem,
					       const struct nvmem_cell_info *info,
					       struct nvmem_cell_entry *cell)
{
	int err;

	err = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, cell);
	if (err)
		return err;

	cell->name = kstrdup_const(info->name, GFP_KERNEL);
	if (!cell->name)
		return -ENOMEM;

	return 0;

Annotation

Implementation Notes