NVMEM Subsystem

Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This document explains the NVMEM Framework along with the APIs provided,and how to use it.

1. Introduction

NVMEM is the abbreviation for Non Volatile Memory layer. It is used toretrieve configuration of SOC or Device specific data from non volatilememories like eeprom, efuses and so on.

Before this framework existed, NVMEM drivers like eeprom were stored indrivers/misc, where they all had to duplicate pretty much the same code toregister a sysfs file, allow in-kernel users to access the content of thedevices they were driving, etc.

This was also a problem as far as other in-kernel users were involved, sincethe solutions used were pretty much different from one driver to another, therewas a rather big abstraction leak.

This framework aims at solve these problems. It also introduces DTrepresentation for consumer devices to go get the data they require (MACAddresses, SoC/Revision ID, part numbers, and so on) from the NVMEMs.

NVMEM Providers

NVMEM provider refers to an entity that implements methods to initialize, readand write the non-volatile memory.

2. Registering/Unregistering the NVMEM provider

A NVMEM provider can register with NVMEM core by supplying relevantnvmem configuration tonvmem_register(), on success core would return a validnvmem_device pointer.

nvmem_unregister() is used to unregister a previously registered provider.

For example, a simple nvram case:

static int brcm_nvram_probe(struct platform_device *pdev){      struct nvmem_config config = {              .name = "brcm-nvram",              .reg_read = brcm_nvram_read,      };      ...      config.dev = &pdev->dev;      config.priv = priv;      config.size = resource_size(res);      devm_nvmem_register(&config);}

Device drivers can define and register an nvmem cell using the nvmem_cell_infostruct:

static const struct nvmem_cell_info foo_nvmem_cell = {      {              .name           = "macaddr",              .offset         = 0x7f00,              .bytes          = ETH_ALEN,      }};int nvmem_add_one_cell(nvmem, &foo_nvmem_cell);

Additionally it is possible to create nvmem cell lookup entries and registerthem with the nvmem framework from machine code as shown in the example below:

static struct nvmem_cell_lookup foo_nvmem_lookup = {      .nvmem_name             = "i2c-eeprom",      .cell_name              = "macaddr",      .dev_id                 = "foo_mac.0",      .con_id                 = "mac-address",};nvmem_add_cell_lookups(&foo_nvmem_lookup, 1);

NVMEM Consumers

NVMEM consumers are the entities which make use of the NVMEM provider toread from and to NVMEM.

3. NVMEM cell based consumer APIs

NVMEM cells are the data entries/fields in the NVMEM.The NVMEM framework provides 3 APIs to read/write NVMEM cells:

struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *name);struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *name);void nvmem_cell_put(struct nvmem_cell *cell);void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);void *nvmem_cell_read(struct nvmem_cell *cell, ssize_t *len);int nvmem_cell_write(struct nvmem_cell *cell, void *buf, ssize_t len);

*nvmem_cell_get() apis will get a reference to nvmem cell for a given id,and nvmem_cell_read/write() can then read or write to the cell.Once the usage of the cell is finished the consumer should call*nvmem_cell_put() to free all the allocation memory for the cell.

4. Direct NVMEM device based consumer APIs

In some instances it is necessary to directly read/write the NVMEM.To facilitate such consumers NVMEM framework provides below apis:

struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);struct nvmem_device *devm_nvmem_device_get(struct device *dev,                                         const char *name);struct nvmem_device *nvmem_device_find(void *data,                      int (*match)(struct device *dev, const void *data));void nvmem_device_put(struct nvmem_device *nvmem);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, void *buf);int nvmem_device_cell_read(struct nvmem_device *nvmem,                         struct nvmem_cell_info *info, void *buf);int nvmem_device_cell_write(struct nvmem_device *nvmem,                          struct nvmem_cell_info *info, void *buf);

Before the consumers can read/write NVMEM directly, it should get holdof nvmem_controller from one of the*nvmem_device_get() api.

The difference between these apis and cell based apis is that these apis alwaystake nvmem_device as parameter.

5. Releasing a reference to the NVMEM

When a consumer no longer needs the NVMEM, it has to release the referenceto the NVMEM it has obtained using the APIs mentioned in the above section.The NVMEM framework provides 2 APIs to release a reference to the NVMEM:

void nvmem_cell_put(struct nvmem_cell *cell);void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);void nvmem_device_put(struct nvmem_device *nvmem);void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem);

Both these APIs are used to release a reference to the NVMEM anddevm_nvmem_cell_put and devm_nvmem_device_put destroys the devres associatedwith this NVMEM.

Userspace

6. Userspace binary interface

Userspace can read/write the raw NVMEM file located at:

/sys/bus/nvmem/devices/*/nvmem

ex:

hexdump /sys/bus/nvmem/devices/qfprom0/nvmem0000000 0000 0000 0000 0000 0000 0000 0000 0000*00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c000000000 0000 0000 0000 0000 0000 0000 0000 0000...*0001000

7. DeviceTree Binding

See Documentation/devicetree/bindings/nvmem/nvmem.txt

8. NVMEM layouts

NVMEM layouts are yet another mechanism to create cells. With the devicetree binding it is possible to specify simple cells by using an offsetand a length. Sometimes, the cells doesn’t have a static offset, butthe content is still well defined, e.g. tag-length-values. In this case,the NVMEM device content has to be first parsed and the cells need tobe added accordingly. Layouts let you read the content of the NVMEM deviceand let you add cells dynamically.

Another use case for layouts is the post processing of cells. With layouts,it is possible to associate a custom post processing hook to a cell. Iteven possible to add this hook to cells not created by the layout itself.

9. Internal kernel API

intnvmem_add_one_cell(structnvmem_device*nvmem,conststructnvmem_cell_info*info)

Add one cell information to an nvmem device

Parameters

structnvmem_device*nvmem

nvmem device to add cells to.

conststructnvmem_cell_info*info

nvmem cell info to add to the device

Return

0 or negative error code on failure.

intnvmem_register_notifier(structnotifier_block*nb)

Register a notifier block for nvmem events.

Parameters

structnotifier_block*nb

notifier block to be called on nvmem events.

Return

0 on success, negative error number on failure.

intnvmem_unregister_notifier(structnotifier_block*nb)

Unregister a notifier block for nvmem events.

Parameters

structnotifier_block*nb

notifier block to be unregistered.

Return

0 on success, negative error number on failure.

structnvmem_device*nvmem_register(conststructnvmem_config*config)

Register a nvmem device for given nvmem_config. Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem

Parameters

conststructnvmem_config*config

nvmem device configuration with which nvmem device is created.

Return

Will be anERR_PTR() on error or a valid pointer to nvmem_deviceon success.

voidnvmem_unregister(structnvmem_device*nvmem)

Unregister previously registered nvmem device

Parameters

structnvmem_device*nvmem

Pointer to previously registered nvmem device.

structnvmem_device*devm_nvmem_register(structdevice*dev,conststructnvmem_config*config)

Register a managed nvmem device for given nvmem_config. Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem

Parameters

structdevice*dev

Device that uses the nvmem device.

conststructnvmem_config*config

nvmem device configuration with which nvmem device is created.

Return

Will be anERR_PTR() on error or a valid pointer to nvmem_deviceon success.

structnvmem_device*of_nvmem_device_get(structdevice_node*np,constchar*id)

Get nvmem device from a given id

Parameters

structdevice_node*np

Device tree node that uses the nvmem device.

constchar*id

nvmem name from nvmem-names property.

Return

ERR_PTR() on error or a valid pointer to astructnvmem_deviceon success.

structnvmem_device*nvmem_device_get(structdevice*dev,constchar*dev_name)

Get nvmem device from a given id

Parameters

structdevice*dev

Device that uses the nvmem device.

constchar*dev_name

name of the requested nvmem device.

Return

ERR_PTR() on error or a valid pointer to astructnvmem_deviceon success.

structnvmem_device*nvmem_device_find(void*data,int(*match)(structdevice*dev,constvoid*data))

Find nvmem device with matching function

Parameters

void*data

Data to pass to match function

int(*match)(structdevice*dev,constvoid*data)

Callback function to check device

Return

ERR_PTR() on error or a valid pointer to astructnvmem_deviceon success.

voiddevm_nvmem_device_put(structdevice*dev,structnvmem_device*nvmem)

put already got nvmem device

Parameters

structdevice*dev

Device that uses the nvmem device.

structnvmem_device*nvmem

pointer to nvmem device allocated bydevm_nvmem_cell_get(),that needs to be released.

voidnvmem_device_put(structnvmem_device*nvmem)

put already got nvmem device

Parameters

structnvmem_device*nvmem

pointer to nvmem device that needs to be released.

structnvmem_device*devm_nvmem_device_get(structdevice*dev,constchar*id)

Get nvmem device of device from a given id

Parameters

structdevice*dev

Device that requests the nvmem device.

constchar*id

name id for the requested nvmem device.

Return

ERR_PTR() on error or a valid pointer to astructnvmem_deviceon success. The nvmem_device will be freed by the automatically once thedevice is freed.

structnvmem_cell*of_nvmem_cell_get(structdevice_node*np,constchar*id)

Get a nvmem cell from given device node and cell id

Parameters

structdevice_node*np

Device tree node that uses the nvmem cell.

constchar*id

nvmem cell name from nvmem-cell-names property, or NULLfor the cell at index 0 (the lone cell with no accompanyingnvmem-cell-names property).

Return

Will be anERR_PTR() on error or a valid pointerto astructnvmem_cell. The nvmem_cell will be freed by thenvmem_cell_put().

structnvmem_cell*nvmem_cell_get(structdevice*dev,constchar*id)

Get nvmem cell of device from a given cell name

Parameters

structdevice*dev

Device that requests the nvmem cell.

constchar*id

nvmem cell name to get (this corresponds with the name from thenvmem-cell-names property for DT systems and with the con_id fromthe lookup entry for non-DT systems).

Return

Will be anERR_PTR() on error or a valid pointerto astructnvmem_cell. The nvmem_cell will be freed by thenvmem_cell_put().

structnvmem_cell*devm_nvmem_cell_get(structdevice*dev,constchar*id)

Get nvmem cell of device from a given id

Parameters

structdevice*dev

Device that requests the nvmem cell.

constchar*id

nvmem cell name id to get.

Return

Will be anERR_PTR() on error or a valid pointerto astructnvmem_cell. The nvmem_cell will be freed by theautomatically once the device is freed.

voiddevm_nvmem_cell_put(structdevice*dev,structnvmem_cell*cell)

Release previously allocated nvmem cell from devm_nvmem_cell_get.

Parameters

structdevice*dev

Device that requests the nvmem cell.

structnvmem_cell*cell

Previously allocated nvmem cell bydevm_nvmem_cell_get().

voidnvmem_cell_put(structnvmem_cell*cell)

Release previously allocated nvmem cell.

Parameters

structnvmem_cell*cell

Previously allocated nvmem cell bynvmem_cell_get().

void*nvmem_cell_read(structnvmem_cell*cell,size_t*len)

Read a given nvmem cell

Parameters

structnvmem_cell*cell

nvmem cell to be read.

size_t*len

pointer to length of cell which will be populated on successful read;can be NULL.

Return

ERR_PTR() on error or a valid pointer to a buffer on success. Thebuffer should be freed by the consumer with akfree().

intnvmem_cell_write(structnvmem_cell*cell,void*buf,size_tlen)

Write to a given nvmem cell

Parameters

structnvmem_cell*cell

nvmem cell to be written.

void*buf

Buffer to be written.

size_tlen

length of buffer to be written to nvmem cell.

Return

length of bytes written or negative on failure.

intnvmem_cell_read_u8(structdevice*dev,constchar*cell_id,u8*val)

Read a cell value as a u8

Parameters

structdevice*dev

Device that requests the nvmem cell.

constchar*cell_id

Name of nvmem cell to read.

u8*val

pointer to output value.

Return

0 on success or negative errno.

intnvmem_cell_read_u16(structdevice*dev,constchar*cell_id,u16*val)

Read a cell value as a u16

Parameters

structdevice*dev

Device that requests the nvmem cell.

constchar*cell_id

Name of nvmem cell to read.

u16*val

pointer to output value.

Return

0 on success or negative errno.

intnvmem_cell_read_u32(structdevice*dev,constchar*cell_id,u32*val)

Read a cell value as a u32

Parameters

structdevice*dev

Device that requests the nvmem cell.

constchar*cell_id

Name of nvmem cell to read.

u32*val

pointer to output value.

Return

0 on success or negative errno.

intnvmem_cell_read_u64(structdevice*dev,constchar*cell_id,u64*val)

Read a cell value as a u64

Parameters

structdevice*dev

Device that requests the nvmem cell.

constchar*cell_id

Name of nvmem cell to read.

u64*val

pointer to output value.

Return

0 on success or negative errno.

intnvmem_cell_read_variable_le_u32(structdevice*dev,constchar*cell_id,u32*val)

Read up to 32-bits of data as a little endian number.

Parameters

structdevice*dev

Device that requests the nvmem cell.

constchar*cell_id

Name of nvmem cell to read.

u32*val

pointer to output value.

Return

0 on success or negative errno.

intnvmem_cell_read_variable_le_u64(structdevice*dev,constchar*cell_id,u64*val)

Read up to 64-bits of data as a little endian number.

Parameters

structdevice*dev

Device that requests the nvmem cell.

constchar*cell_id

Name of nvmem cell to read.

u64*val

pointer to output value.

Return

0 on success or negative errno.

ssize_tnvmem_device_cell_read(structnvmem_device*nvmem,structnvmem_cell_info*info,void*buf)

Read a given nvmem device and cell

Parameters

structnvmem_device*nvmem

nvmem device to read from.

structnvmem_cell_info*info

nvmem cell info to be read.

void*buf

buffer pointer which will be populated on successful read.

Return

length of successful bytes read on success and negativeerror code on error.

intnvmem_device_cell_write(structnvmem_device*nvmem,structnvmem_cell_info*info,void*buf)

Write cell to a given nvmem device

Parameters

structnvmem_device*nvmem

nvmem device to be written to.

structnvmem_cell_info*info

nvmem cell info to be written.

void*buf

buffer to be written to cell.

Return

length of bytes written or negative error code on failure.

intnvmem_device_read(structnvmem_device*nvmem,unsignedintoffset,size_tbytes,void*buf)

Read from a given nvmem device

Parameters

structnvmem_device*nvmem

nvmem device to read from.

unsignedintoffset

offset in nvmem device.

size_tbytes

number of bytes to read.

void*buf

buffer pointer which will be populated on successful read.

Return

length of successful bytes read on success and negativeerror code on error.

intnvmem_device_write(structnvmem_device*nvmem,unsignedintoffset,size_tbytes,void*buf)

Write cell to a given nvmem device

Parameters

structnvmem_device*nvmem

nvmem device to be written to.

unsignedintoffset

offset in nvmem device.

size_tbytes

number of bytes to write.

void*buf

buffer to be written.

Return

length of bytes written or negative error code on failure.

voidnvmem_add_cell_lookups(structnvmem_cell_lookup*entries,size_tnentries)

register a list of cell lookup entries

Parameters

structnvmem_cell_lookup*entries

array of cell lookup entries

size_tnentries

number of cell lookup entries in the array

voidnvmem_del_cell_lookups(structnvmem_cell_lookup*entries,size_tnentries)

remove a list of previously added cell lookup entries

Parameters

structnvmem_cell_lookup*entries

array of cell lookup entries

size_tnentries

number of cell lookup entries in the array

constchar*nvmem_dev_name(structnvmem_device*nvmem)

Get the name of a given nvmem device.

Parameters

structnvmem_device*nvmem

nvmem device.

Return

name of the nvmem device.

size_tnvmem_dev_size(structnvmem_device*nvmem)

Get the size of a given nvmem device.

Parameters

structnvmem_device*nvmem

nvmem device.

Return

size of the nvmem device.