首页 > 系统相关 >Linux NVMEM子系统:概述以及RK3588 OTP实例

Linux NVMEM子系统:概述以及RK3588 OTP实例

时间:2024-05-26 18:56:40浏览次数:28  
标签:NVMEM struct RK3588 -- nvmem cell Linux device

 NVMEM子系统为Non-Volatile类型存储提供统一内核处理框架。

1 NVMEM概述

NVMEM子系统为eeprom,otp,efuse类型设备提供统一的访问接口。一般都基于regmap实现读写。

NVMEM子系统初始化:

nvmem_init
    bus_register--注册NVMEM总线nvmem_bus_type。

2 NVMEM API和数据结构

2.1 NVMEM Provider

 struct nvmem_config表示一个NVMEM设备的配置, 作为nvmem_register()参数注册到NVMEM子系统,返货struct nvmem_device。

struct nvmem_config {
    struct device        *dev;
    const char        *name;--NVMEM设备名称。
    int            id;
    struct module        *owner;
    struct gpio_desc    *wp_gpio;--写保护GPIO。
    const struct nvmem_cell_info    *cells;--预定义cell列表。
    int            ncells;
    enum nvmem_type        type;
    bool            read_only;--只读。
    bool            root_only;--仅root用户可访问。
    bool            no_of_node;
    nvmem_reg_read_t    reg_read;--对NVMEM读回调函数。
    nvmem_reg_write_t    reg_write;--对NVMEM写回调函数。
    int    size;
    int    word_size;
    int    stride;
    void    *priv;
    /* To be only used by old driver/misc/eeprom drivers */
    bool            compat;
    struct device        *base_dev;
};

 devm_nvmem_register/devm_nvmem_unregister是NVMEM设备的注册和注销函数:

struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
void nvmem_unregister(struct nvmem_device *nvmem);

struct nvmem_device *devm_nvmem_register(struct device *dev,
                     const struct nvmem_config *cfg);

int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem);

devm_nvmem_register()是带资源管理的NVMEM注册函数:

devm_nvmem_register
  nvmem_register
    gpiod_get_optional--获取write protect引脚。
    device_property_present--判断dts设置是否为read-only。
    device_register--注册设备,设备属性位nvmem_dev_groups。创建binary类型的nvmem属性节点,type表示类型的属性节点。
    nvmem_sysfs_setup_compat
    nvmem_add_cells--根据nvmem_config中的cells创建cell。
    nvmem_add_cells_from_table--根据nvmem_cell_tables创建cell。
    nvmem_add_cells_from_of--根据dts创建cell。

对nvmem属性读写,最终会调用具体设备的struct nvmem_config的读写接口:

static struct bin_attribute bin_attr_rw_nvmem = {
    .attr    = {
        .name    = "nvmem",
        .mode    = 0644,
    },
    .read    = bin_attr_nvmem_read,
    .write    = bin_attr_nvmem_write,
};

bin_attr_nvmem_write
  nvmem_reg_write
    nvmem->reg_write

bin_attr_nvmem_read
  nvmem_reg_read
    nvmem->reg_read

struct nvmem_cell_table是表示一系列NVMEM Cell的组合列表:

struct nvmem_cell_table {
    const char        *nvmem_name;
    const struct nvmem_cell_info    *cells;
    size_t            ncells;
    struct list_head    node;
};

2.2 NVMEM Consumer

struct nvmem_cell_info表示一个cell:

struct nvmem_cell_info {
    const char        *name;--名称。
    unsigned int        offset;--byte为单位的偏移。
    unsigned int        bytes;--byte为单位的长度。
    unsigned int        bit_offset;--bit为单位的偏移。
    unsigned int        nbits;--bit为单位的长度。
};

获取struct device后,根据cell名称进行操作,获取cell:

struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *id);
struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *id);
void nvmem_cell_put(struct nvmem_cell *cell);
void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);

对cell进行读写:

void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len);
int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len);
int nvmem_cell_read_u8(struct device *dev, const char *cell_id, u8 *val);
int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val);
int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val);
int nvmem_cell_read_u64(struct device *dev, const char *cell_id, u64 *val);

获取struct device后,根据NVMEM设备名称获取struct nvmem_device后直接对裸设备进行读写:

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);
void nvmem_device_put(struct nvmem_device *nvmem);
void devm_nvmem_device_put(struct device *dev, 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);
ssize_t 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);

const char *nvmem_dev_name(struct nvmem_device *nvmem);

void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries,
                size_t nentries);
void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries,
                size_t nentries);

3 NVMEM驱动(RK3588 OTP)

3.1 RK3588 OTP DTS

dts中除了及地址/时钟/复位配置外,还包括一系列NVMEM Cell定义:

    otp: otp@fecc0000 {
        compatible = "rockchip,rk3588-otp";
        reg = <0x0 0xfecc0000 0x0 0x400>;
        #address-cells = <1>;
        #size-cells = <1>;
        clocks = <&cru CLK_OTPC_NS>, <&cru PCLK_OTPC_NS>,
             <&cru CLK_OTPC_ARB>, <&cru CLK_OTP_PHY_G>;
        clock-names = "otpc", "apb", "arb", "phy";
        resets = <&cru SRST_OTPC_NS>, <&cru SRST_P_OTPC_NS>,
             <&cru SRST_OTPC_ARB>;
        reset-names = "otpc", "apb", "arb";

        /* Data cells */
        cpu_code: cpu-code@2 {
            reg = <0x02 0x2>;
        };
...
    };

3.2 RK3588 OTP驱动

Rockchip OTP作为NVMEM设备初始化如下:

rockchip_otp_init
  rockchip_otp_driver
    rockchip_otp_probe
      devm_nvmem_register--注册NVMEM设备,默认配struct nvmem_config为otp_config。

otp_config中定义了OTP设备的通用配置:

static struct nvmem_config otp_config = {
    .name = "rockchip-otp",
    .owner = THIS_MODULE,
    .read_only = true,
    .reg_read = rockchip_otp_read,--此接口会在对nvmem属性节点读时调用,再去调用具体设备的读函数。
    .reg_write = rockchip_otp_write,--此接口会在对nvmem属性节点写时调用,再去调用具体设备的写函数。
    .stride = 1,
    .word_size = 1,
};

不同的设备差异通过of_device_id->data区分:

static const struct of_device_id rockchip_otp_match[] = {
#ifdef CONFIG_CPU_RK3588
    {
        .compatible = "rockchip,rk3588-otp",
        .data = (void *)&rk3588_data,
    },
#endif    { /* sentinel */ },
};

static const struct rockchip_data rk3588_data = {
    .size = 0x400,
    .clocks = rk3588_otp_clocks,
    .num_clks = ARRAY_SIZE(rk3588_otp_clocks),
    .reg_read = rk3588_otp_read,
};

3.3 NVMEM Consumer驱动

cpuinfo作为NVMEM Consumer,调用cpu_code节点:

    cpuinfo {
        compatible = "rockchip,cpuinfo";
        nvmem-cells = <&otp_id>, <&otp_cpu_version>, <&cpu_code>;--NVMEM设备提供过的句柄。
        nvmem-cell-names = "id", "cpu-version", "cpu-code";
    };

在设备驱动的中,通过

rockchip_cpuinfo_init
  rockchip_cpuinfo_driver
    rockchip_cpuinfo_probe
      nvmem_cell_get--获取Cell,然后读取内容。
      nvmem_cell_read
      nvmem_cell_put

4 用户空间接口

NVMEM设备节点如下:

/sys/devices/platform/fecc0000.otp/rockchip-otp0/
|-- nvmem
|-- of_node -> ../../../../firmware/devicetree/base/otp@fecc0000
|-- power
|   |-- async
|   |-- autosuspend_delay_ms
|   |-- control
|   |-- runtime_active_kids
|   |-- runtime_active_time
|   |-- runtime_enabled
|   |-- runtime_status
|   |-- runtime_suspended_time
|   `-- runtime_usage
|-- subsystem -> ../../../../bus/nvmem
|-- type
`-- uevent

 通过hexdump访问:

hexdump /sys/devices/platform/fecc0000.otp/rockchip-otp0/nvmem
0000000 4b52 8835 fe12 4121 4e32 4b35 0000 0000
0000010 0000 0000 1202 0b0a 0e0b 1630 0008 0000
0000020 0000 0000 0000 0000 120c 0000 3100 1c08
0000030 6551 5c09 2809 2907 ec07 f703 0003 0000
0000040 0000 0000 0000 0000 0000 0000 0000 0000
*
0000400

标签:NVMEM,struct,RK3588,--,nvmem,cell,Linux,device
From: https://www.cnblogs.com/arnoldlu/p/18213506

相关文章

  • Windows、Linux下,基于QT的打包方法
    整理这篇文档的意义在于:自己走了很多弯路,淋过雨所以想为别人撑伞,也方便回顾,仅供参考ps:第一次做Windows下打包,用了2小时,第二次20秒第一次做Linux(ubuntu)下打包,用了8小时,第二次1分半一、Windows有许多比较坑的帖子,会带新人走不少弯路,大家注意鉴别(没方法,随缘)1、首先,找到......
  • 32bit的linux系统内存分布
    32bit的linux系统内存分布​ 在32bit的linux系统,则每个运行的程序都会得到4G大小的内存空间,只不过每个程序得到的4G大小的内存都是虚拟内存,而物理内存才只有4G,物理内存是真实存在的,而虚拟内存是通过映射得到的。(如图所示一个程序有1M大小的物理内存,在运行之后会得到4G大小的虚......
  • Linux安装Anaconda
    清华大学开源软件镜像站https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/下载好安装bashAnaconda3-2020.07-Linux-x86_64.sh空格之后yes安装环境变量重新连接......
  • linux核心基础-权限管理
    1、更改文件的权限命令总结2、文件、目录的的rwx想要删除文件,要看是否有该文件所在目录,目录是否有w权限,才可以删除文件,且还得有x权限,才能进入文件夹。(用普通用户测试)3、环境变量1、env和set命令env命令为单个用户的环境变量命令为系统整体的环境变量[root@muserver1etc]......
  • OneForall工具的下载安装和使用(Windows和Linux)
    目录OneForall的介绍OneForall的下载OneForall的安装安装要求安装步骤(git版)安装(kali)OneForall的使用命令在Windows在Linux(kali)OneForall的结果说明免责声明本文所提供的文字和信息仅供学习和研究使用,请读者自觉遵守法律法规,不得利用本文所提供的信息从事任何违......
  • linux 死锁排查以及线程信息打印
     脚本名称,gstack.sh#!/bin/shiftest$#-ne1;thenecho"Usage:`basename$0.sh`<process-id>"1>&2exit1fiiftest!-r/proc/$1;thenecho"Process$1notfound."1>&2exit1fi#GDBdoesn&#......
  • Debian/Linux网络配置全面指南:从静态IP到DNS设置
    在Debian/Linux上配置网络涉及多个步骤,包括设置静态IP地址、配置网关和DNS服务器等。以下是一个详细的教程,指导你如何在Debian/Linux系统上进行网络配置。1.编辑网络接口配置文件在Debian/Linux上,网络接口的配置文件通常位于/etc/network/interfaces。首先,以超级用户身份......
  • 【Linux】为 VMware 的 Linux 系统(CentOS 7)设置静态IP地址
    文章目录准备工作查看子网掩码和网关IP确认准备设置的虚拟机端口没有被占用调整设置编辑配置文件配置文件说明完成配置,准备测试使用命令终端连接服务器我是一名立志把细节说清楚的博主,欢迎【关注】......
  • Linux 常用命令
    Linux常用命令查看资源使用情况top命令:实时显示系统资源的使用情况,包括CPU使用率、内存使用情况、进程信息等。它可以实时更新数据,您可以使用交互命令对输出进行排序和过滤。htop命令:与top类似,但提供了更友好的用户界面和更多的功能,如更丰富的进程信息和交互式......
  • Linux 防火墙只允许指定IP 端口访问
    开启和关闭防火墙命令如下:查看防火状态systemctlstatusfirewalld2:暂时关闭防火墙systemctlstopfirewalld3:永久关闭防火墙systemctldisablefirewalldsystemctlstopfirewalld.service4:重启防火墙systemctlenablefirewalld5、查看防火墙已开通的端口:sudo......