Skip to content

Commit

Permalink
drivers: nvmem: add API for nvmem controllers
Browse files Browse the repository at this point in the history
Add a nvmem API to access nvmem cells using device-tree description. This
API allows to register nvmeme provider and obtain nvmem cells for consumer.
Much like other subsystem, this one relies on the generic dt_driver
mechanism.

Signed-off-by: Clément Léger <[email protected]>
Signed-off-by: Thomas Perrot <[email protected]>
Reviewed-by: Etienne Carriere <[email protected]>
  • Loading branch information
clementleger authored and jforissier committed Jan 12, 2024
1 parent 4fd40c3 commit 515c1ba
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 0 deletions.
77 changes: 77 additions & 0 deletions core/drivers/nvmem/nvmem.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2023, Microchip
*/

#include <drivers/nvmem.h>
#include <kernel/dt.h>
#include <libfdt.h>

TEE_Result nvmem_cell_parse_dt(const void *fdt, int nodeoffset,
struct nvmem_cell *cell)
{
size_t buf_len = 0;
paddr_t offset = 0;

buf_len = fdt_reg_size(fdt, nodeoffset);
if (buf_len == DT_INFO_INVALID_REG_SIZE)
return TEE_ERROR_GENERIC;

offset = fdt_reg_base_address(fdt, nodeoffset);
if (offset == DT_INFO_INVALID_REG)
return TEE_ERROR_GENERIC;

cell->len = buf_len;
cell->offset = offset;

return TEE_SUCCESS;
}

TEE_Result nvmem_get_cell_by_name(const void *fdt, int nodeoffset,
const char *name, struct nvmem_cell **cell)
{
int index = 0;

index = fdt_stringlist_search(fdt, nodeoffset, "nvmem-cell-names",
name);
if (index < 0)
return TEE_ERROR_ITEM_NOT_FOUND;

return nvmem_get_cell_by_index(fdt, nodeoffset, index, cell);
}

TEE_Result nvmem_get_cell_by_index(const void *fdt,
int nodeoffset,
unsigned int index,
struct nvmem_cell **out_cell)
{
TEE_Result res = TEE_ERROR_GENERIC;
void *cell = NULL;

res = dt_driver_device_from_node_idx_prop("nvmem-cells", fdt,
nodeoffset, index,
DT_DRIVER_NVMEM, &cell);
if (!res)
*out_cell = cell;

return res;
}

TEE_Result nvmem_cell_malloc_and_read(struct nvmem_cell *cell,
uint8_t **out_data)
{
TEE_Result res = TEE_ERROR_GENERIC;

if (!cell->ops->read_cell)
return TEE_ERROR_NOT_SUPPORTED;

*out_data = malloc(cell->len);
if (!out_data)
return TEE_ERROR_OUT_OF_MEMORY;

res = cell->ops->read_cell(cell, *out_data);
if (res)
free(*out_data);

return res;
}
1 change: 1 addition & 0 deletions core/drivers/nvmem/sub.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
srcs-y += nvmem.c
1 change: 1 addition & 0 deletions core/drivers/sub.mk
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ subdirs-$(CFG_BNXT_FW) += bnxt
subdirs-$(CFG_DRIVERS_CLK) += clk
subdirs-$(CFG_DRIVERS_GPIO) += gpio
subdirs-$(CFG_DRIVERS_I2C) += i2c
subdirs-$(CFG_DRIVERS_NVMEM) += nvmem
subdirs-$(CFG_DRIVERS_PINCTRL) += pinctrl
subdirs-$(CFG_DRIVERS_REGULATOR) += regulator
subdirs-$(CFG_DRIVERS_RSTCTRL) += rstctrl
Expand Down
193 changes: 193 additions & 0 deletions core/include/drivers/nvmem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2023, Microchip
*/

#ifndef __DRIVERS_NVMEM_H
#define __DRIVERS_NVMEM_H

#include <kernel/dt_driver.h>
#include <tee_api_defines.h>
#include <tee_api_types.h>
#include <types_ext.h>

struct nvmem_cell;

/*
* struct nvmem_ops - NVMEM device driver operations
* @read_cell: Allocate @data in the heap and load @len bytes to from an
* NVMEM cell
* @put_cell: Release resources allocated from nvmem_dt_get_func callback
* function
*/
struct nvmem_ops {
/*
* Read data from an NVMEM cell.
* @cell: Cell to read data from
* @data: Output buffer of size greater or equal to @cell->size
*/
TEE_Result (*read_cell)(struct nvmem_cell *cell, uint8_t *data);
void (*put_cell)(struct nvmem_cell *cell);
};

/*
* struct nvmem_cell - Description of an NVMEM cell
* @offset: Cell byte offset in the NVMEM device
* @len: Cell byte size
* @ops: NVMEM device driver operation handlers
* @drv_data: NVMEM device driver private data
*/
struct nvmem_cell {
paddr_t offset;
size_t len;
const struct nvmem_ops *ops;
void *drv_data;
};

/*
* nvmem_dt_get_func - Typedef of handlers to get an NVMEM cell from a npode
* @args: Reference to phandle arguments
* @data: Pointer to data given at nvmem_dt_get_func() call
* @cell: Output reference to cell instance upon success
*
* Return TEE_SUCCESS in case of success.
* Return TEE_ERROR_DEFER_DRIVER_INIT if NVMEM driver is not initialized
* Return another TEE_Result compliant code otherwise.
*/
typedef TEE_Result (*nvmem_dt_get_func)(struct dt_pargs *args,
void *data, struct nvmem_cell **cell);

#ifdef CFG_DRIVERS_NVMEM
/**
* nvmem_register_provider() - Register a NVMEM controller
*
* @fdt: Device tree to work on
* @nodeoffset: Node offset of NVMEM cell consumer
* @get_dt_nvmem: Callback to match the devicetree NVMEM reference with
* nvmem_cell
* @data: Data which will be passed to the get_dt_nvmem callback
* Return a TEE_Result compliant value
*/
static inline TEE_Result nvmem_register_provider(const void *fdt,
int nodeoffset,
nvmem_dt_get_func get_dt_nvmem,
void *data)
{
return dt_driver_register_provider(fdt, nodeoffset,
(get_of_device_func)get_dt_nvmem,
data, DT_DRIVER_NVMEM);
}

/**
* nvmem_get_cell_by_name() - Obtain a NVMEM cell from its name in the DT node
*
* @fdt: Device tree to work on
* @nodeoffset: Node offset of NVMEM cell consumer
* @name: name of the NVMEM cell defined by property nvmem-cell-names to obtain
* from the device tree
* @cell: Pointer filled with the retrieved cell, must be freed after use
using nvmem_put_cell()
* Return a TEE_Result compliant value
*/
TEE_Result nvmem_get_cell_by_name(const void *fdt, int nodeoffset,
const char *name, struct nvmem_cell **cell);

/**
* nvmem_get_cell_by_index() - Obtain a NVMEM cell from property nvmem-cells
*
* @fdt: Device tree to work on
* @nodeoffset: Node offset of NVMEM cell consumer
* @index: Index of the NVMEM cell to obtain from device-tree
* @out_cell: Pointer filled with the retrieved cell, must be freed after use
* using nvmem_put_cell()
* Return a TEE_Result compliant value
*/
TEE_Result nvmem_get_cell_by_index(const void *fdt,
int nodeoffset,
unsigned int index,
struct nvmem_cell **out_cell);

/**
* nvmem_cell_parse_dt() - Parse device-tree information to fill a NVMEM cell
*
* @fdt: Device tree to work on
* @nodeoffset: Node offset of the NVMEM cell controller
* @cell: Pointer to cell that will be filled
*/
TEE_Result nvmem_cell_parse_dt(const void *fdt, int nodeoffset,
struct nvmem_cell *cell);

/**
* nvmem_put_cell() - Free resource allocated from nvmem_get_cell_by_*()
*
* @cell: Cell to be freed
*/
static inline void nvmem_put_cell(struct nvmem_cell *cell)
{
if (cell->ops->put_cell)
cell->ops->put_cell(cell);
}

/*
* nvmem_cell_read() - Read data from a NVMEM cell
* @cell: Cell to read from NVMEM
* @data: Output data read from the cell upon success, byte size >= @cell->size
*/
static inline TEE_Result nvmem_cell_read(struct nvmem_cell *cell,
uint8_t *data)
{
if (!cell->ops->read_cell)
return TEE_ERROR_NOT_SUPPORTED;

return cell->ops->read_cell(cell, data);
}

/*
* nvmem_cell_malloc_and_read() - Allocate and read data from a NVMEM cell
* @cell: Cell to read from NVMEM
* @data: Output allocated buffer where NVMEM cell data are stored upon success
*
* Upon success, the output buffer is allocated with malloc(). Caller is
* responsible for freeing the buffer with free() if needed.
*/
TEE_Result nvmem_cell_malloc_and_read(struct nvmem_cell *cell,
uint8_t **out_data);

#else /* CFG_DRIVERS_NVMEM */
static inline TEE_Result nvmem_register_provider(const void *fdt __unused,
int nodeoffset __unused,
nvmem_dt_get_func fn __unused,
void *data __unused)
{
return TEE_ERROR_NOT_SUPPORTED;
}

static inline TEE_Result nvmem_get_cell_by_name(const void *fdt __unused,
int nodeoffset __unused,
const char *name __unused,
struct nvmem_cell **c __unused)
{
return TEE_ERROR_NOT_SUPPORTED;
}

static inline
TEE_Result nvmem_get_cell_by_index(const void *fdt __unused,
int nodeoffset __unused,
unsigned int index __unused,
struct nvmem_cell **cell __unused)
{
return TEE_ERROR_NOT_SUPPORTED;
}

static inline TEE_Result nvmem_cell_parse_dt(const void *fdt __unused,
int nodeoffset __unused,
struct nvmem_cell *cell __unused)
{
return TEE_ERROR_NOT_SUPPORTED;
}

static inline void nvmem_put_cell(struct nvmem_cell *cell __unused)
{
}
#endif /* CFG_DRIVERS_NVMEM */
#endif /* __DRIVERS_NVMEM_H */
4 changes: 4 additions & 0 deletions mk/config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,10 @@ CFG_DRIVERS_GPIO ?= n
# When enabled, CFG_DRIVERS_I2C provides I2C controller and devices support.
CFG_DRIVERS_I2C ?= n

# When enabled, CFG_DRIVERS_NVMEM provides a framework to register nvmem
# providers and allow consumer drivers to get NVMEM cells using the Device Tree.
CFG_DRIVERS_NVMEM ?= n

# When enabled, CFG_DRIVERS_PINCTRL embeds a pin muxing controller framework in
# OP-TEE core to provide drivers a way to apply pin muxing configurations based
# on device-tree.
Expand Down

0 comments on commit 515c1ba

Please sign in to comment.