当前位置: 首页 > news >正文

Linux学习笔记协议篇(六):SPI FLASH设备驱动

目录

一、设备树解析

二、SPI设备驱动代码分析

1、spi_nor_probe

2、spi_nor_scan

(1)协议配置

(2)初始化Flash参数(核心步骤)

(3)MTD子系统集成

(3)配置 SPI 通信参数

spi_nor_setup

spi_nor_set_addr_width

(4)初始化设备

3、spi_nor_create_read_dirmap

4、mtd_device_register

三、spi_nor_probe函数整体总结



根据 “Linux学习笔记协议篇(六):SPI控制器驱动”:

SPI控制器驱动在与设备树匹配后,加载在platform总线上,生成相应SPI bus,随后扫秒该SPI控制器下挂的设备树子节点,将该子节点转化为设备信息

以/linux-5.14.10/arch/arm/boot/dts/imx6ul-14x14-evk.dtsi样板中的qspi设备为例:

一、设备树解析

&qspi {pinctrl-names = "default";pinctrl-0 = <&pinctrl_qspi>;status = "okay";flash0: n25q256a@0 {#address-cells = <1>;#size-cells = <1>;compatible = "micron,n25q256a", "jedec,spi-nor";spi-max-frequency = <29000000>;spi-rx-bus-width = <4>;spi-tx-bus-width = <4>;reg = <0>;};
};

flash0: n25q256a@0:定义了一个名为flash0的子节点

  • n25q256a@0表示这是Micron的n25q256a芯片,位于片选0
  • flash0是标签(label),可供其他节点引用

address-cells和size-cells

  • 都设置为1,表示地址和大小都用1个32位数表示

compatible

  • "micron,n25q256a": 精确匹配Micron的n25q256a芯片
  • "jedec,spi-nor": 作为后备,匹配任何符合JEDEC标准的SPI NOR Flash

SPI相关参数

  • spi-max-frequency = <29000000>: 最大SPI时钟频率29MHz
  • spi-rx-bus-width = <4>: 接收总线宽度为4线(Quad模式)
  • spi-tx-bus-width = <4>: 发送总线宽度为4线(Quad模式)

reg = <0>

  • 表示该设备使用片选信号0(CS0)

二、SPI设备驱动代码分析

在内核上电加载到该设备树子节点对应的设备驱动时,设备驱动会通过Compatible匹配与挂接在SPI控制器下子节点相关的设备,首先查找与n25q256a相适配的Compatible属性:

/linux-5.14.10/drivers/mtd/spi-nor/micron-st.cstatic const struct flash_info st_parts[] = {... ...{ "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K |USE_FSR | SPI_NOR_DUAL_READ |SPI_NOR_QUAD_READ) },... ...}const struct spi_nor_manufacturer spi_nor_st = {.name = "st",.parts = st_parts,.nparts = ARRAY_SIZE(st_parts),.fixups = &micron_st_fixups,
};/linux-5.14.10/drivers/mtd/spi-nor/core.c
static const struct spi_nor_manufacturer *manufacturers[] = {... ...&spi_nor_micron,&spi_nor_st,... ...
};

代码中没有找到与n25q256a相适配的Compatible属性,因此查找 jedec,spi-nor 属性:

static const struct of_device_id spi_nor_of_table[] = {... ...{ .compatible = "jedec,spi-nor" },{ /* sentinel */ },
};static struct spi_mem_driver spi_nor_driver = {.spidrv = {.driver = {.name = "spi-nor",.of_match_table = spi_nor_of_table,.dev_groups = spi_nor_sysfs_groups,},.id_table = spi_nor_dev_ids,},.probe = spi_nor_probe,.remove = spi_nor_remove,.shutdown = spi_nor_shutdown,
};
module_spi_mem_driver(spi_nor_driver);

.compatible = "jedec,spi-nor 表示该驱动支持所有兼容 JEDEC 标准的 SPI NOR 闪存,在设备树与驱动匹配后,会触发 spi_nor_probe 函数:

1、spi_nor_probe
static int spi_nor_probe(struct spi_mem *spimem)
{// 获取底层 SPI 设备struct spi_device *spi = spimem->spi;// 获取平台数据(传统方式,非设备树)struct flash_platform_data *data = dev_get_platdata(&spi->dev);struct spi_nor *nor;  // SPI NOR 设备结构体/** 默认启用所有硬件能力,核心会通过 spi_mem_supports_op() * 检查实际支持的能力后进行屏蔽*/const struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_ALL };char *flash_name;  // Flash 名称int ret;          // 返回值/* 1. 分配并初始化 spi_nor 结构体 */// 使用设备资源管理的内存分配(自动释放)nor = devm_kzalloc(&spi->dev, sizeof(*nor), GFP_KERNEL);if (!nor)return -ENOMEM;  // 内存不足错误/* 2. 基本设备信息设置 */nor->spimem = spimem;       // 关联 SPI 内存设备nor->dev = &spi->dev;       // 关联设备结构spi_nor_set_flash_node(nor, spi->dev.of_node);  // 设置设备树节点// 将 nor 设置为驱动私有数据spi_mem_set_drvdata(spimem, nor);/* 3. 设置 MTD 设备名称 */if (data && data->name)nor->mtd.name = data->name;  // 使用平台数据中的名称if (!nor->mtd.name)nor->mtd.name = spi_mem_get_name(spimem);  // 获取 SPI 设备名称/** 处理历史遗留的命名问题:* 许多平台在 flash_platform_data 中提供两个名称:* - "name": 通常设为 "m25p80"(通用名)* - "type": 实际芯片型号* 优先使用 "type",如果没有则尝试自动检测*/if (data && data->type)flash_name = data->type;  // 使用平台数据中的实际型号else if (!strcmp(spi->modalias, "spi-nor"))flash_name = NULL;  // NULL 表示需要自动检测elseflash_name = spi->modalias;  // 使用 SPI 设备别名/* 4. 扫描并识别 Flash 芯片 */ret = spi_nor_scan(nor, flash_name, &hwcaps);.../** 处理大页大小情况(安全防护):* 目前没有 NOR Flash 的页大小超过 PAGE_SIZE(通常 4KB),* 但为防止未来支持此类设备时出现缓冲区溢出,添加保护逻辑*/if (nor->page_size > PAGE_SIZE) {nor->bouncebuf_size = nor->page_size;devm_kfree(nor->dev, nor->bouncebuf);  // 释放原有缓冲区// 重新分配足够大的缓冲区nor->bouncebuf = devm_kmalloc(nor->dev,nor->bouncebuf_size,GFP_KERNEL);if (!nor->bouncebuf)return -ENOMEM;  // 内存不足}/* 5. 创建直接读取映射 */ret = spi_nor_create_read_dirmap(nor);if (ret)return ret;/* 6. 创建直接写入映射 */ret = spi_nor_create_write_dirmap(nor);if (ret)return ret;/* 7. 注册 MTD 设备 */return mtd_device_register(&nor->mtd, data ? data->parts : NULL,    // 分区信息data ? data->nr_parts : 0);  // 分区数量
}
2、spi_nor_scan
int spi_nor_scan(struct spi_nor *nor, const char *name,const struct spi_nor_hwcaps *hwcaps)
{const struct flash_info *info;  // Flash芯片信息表struct device *dev = nor->dev;  // 关联的设备结构struct mtd_info *mtd = &nor->mtd;  // MTD接口结构struct device_node *np = spi_nor_get_flash_node(nor);  // 设备树节点int ret;int i;/* 1. 基础检查 */ret = spi_nor_check(nor);  // 验证nor结构有效性if (ret)return ret;/* 2. 初始化默认SPI协议 */nor->reg_proto = SNOR_PROTO_1_1_1;    // 寄存器操作使用标准SPInor->read_proto = SNOR_PROTO_1_1_1;   // 读操作默认模式nor->write_proto = SNOR_PROTO_1_1_1;  // 写操作默认模式/* 3. 分配缓冲区(bounce buffer) */nor->bouncebuf_size = PAGE_SIZE;  // 初始按页大小分配(通常4KB)nor->bouncebuf = devm_kmalloc(dev, nor->bouncebuf_size, GFP_KERNEL);if (!nor->bouncebuf)return -ENOMEM;  // 用于DMA操作的临时缓冲区/* 4. 获取Flash芯片信息 */info = spi_nor_get_flash_info(nor, name);  // 通过JEDEC ID或名称匹配if (IS_ERR(info))return PTR_ERR(info);nor->info = info;  // 关联芯片信息表spi_nor_debugfs_init(nor, info);  // 初始化debugfs接口/* 5. 初始化互斥锁和特殊标志 */mutex_init(&nor->lock);  // 初始化设备操作锁// Xilinx S3AN特殊标志(与Atmel共享制造商ID)if (info->flags & SPI_NOR_XSR_RDY)nor->flags |= SNOR_F_READY_XSR_RDY;// 支持写保护锁的区域if (info->flags & SPI_NOR_HAS_LOCK)nor->flags |= SNOR_F_HAS_LOCK;/* 6. 初始化MTD操作接口 */mtd->_write = spi_nor_write;  // 绑定写操作函数/* 7. 初始化Flash参数(核心步骤) */ret = spi_nor_init_params(nor);  // 基于info和SFDP表初始化参数if (ret)return ret;/* 8. 设置MTD设备属性 */if (!mtd->name)mtd->name = dev_name(dev);  // 默认使用设备名mtd->priv = nor;        // 关联私有数据mtd->type = MTD_NORFLASH;  // 设备类型mtd->writesize = nor->params->writesize;  // 写入页大小mtd->flags = MTD_CAP_NORFLASH;  // 设备能力标志mtd->size = nor->params->size;  // 总容量mtd->_erase = spi_nor_erase;    // 擦除操作mtd->_read = spi_nor_read;      // 读操作mtd->_suspend = spi_nor_suspend; // 电源管理mtd->_resume = spi_nor_resume;mtd->_get_device = spi_nor_get_device;  // 设备引用计数mtd->_put_device = spi_nor_put_device;/* 9. 处理特殊功能标志 */if (info->flags & USE_FSR)nor->flags |= SNOR_F_USE_FSR;  // 使用Flag Status Registerif (info->flags & SPI_NOR_HAS_TB) {nor->flags |= SNOR_F_HAS_SR_TB;  // 支持Top/Bottom保护if (info->flags & SPI_NOR_TB_SR_BIT6)nor->flags |= SNOR_F_HAS_SR_TB_BIT6;  // TB位在状态寄存器第6位}/* 10. 擦除特性配置 */if (info->flags & NO_CHIP_ERASE)nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;  // 不支持整片擦除if (info->flags & USE_CLSR)nor->flags |= SNOR_F_USE_CLSR;  // 需要清除状态寄存器/* 11. 写保护配置 */if (info->flags & SPI_NOR_SWP_IS_VOLATILE)nor->flags |= SNOR_F_SWP_IS_VOLATILE;  // 易失性写保护if (info->flags & SPI_NOR_4BIT_BP) {nor->flags |= SNOR_F_HAS_4BIT_BP;  // 4位块保护if (info->flags & SPI_NOR_BP3_SR_BIT6)nor->flags |= SNOR_F_HAS_SR_BP3_BIT6;  // BP3位在状态寄存器第6位}/* 12. 特殊模式配置 */if (info->flags & SPI_NOR_NO_ERASE)mtd->flags |= MTD_NO_ERASE;  // 设备不支持擦除操作/* 13. 设备树特殊处理 */if (of_property_read_bool(np, "broken-flash-reset"))nor->flags |= SNOR_F_BROKEN_RESET;  // 标记复位有缺陷的设备/* 14. 配置SPI协议和操作码 */ret = spi_nor_setup(nor, hwcaps);  // 设置读/写/擦除操作码和协议if (ret)return ret;/* 15. 4字节地址模式处理 */if (info->flags & SPI_NOR_4B_OPCODES)nor->flags |= SNOR_F_4B_OPCODES;  // 支持4字节地址指令/* 16. 初始化地址宽度 */ret = spi_nor_set_addr_width(nor);  // 根据容量设置3B/4B地址模式if (ret)return ret;/* 17. 注册锁操作 */spi_nor_register_locking_ops(nor);  // 初始化写保护操作/* 18. 发送初始化序列 */ret = spi_nor_init(nor);  // 执行厂商特定的初始化if (ret)return ret;/* 19. OTP区域初始化 */spi_nor_otp_init(nor);  // 一次性可编程区域设置... ...

代码是一个函数用于扫描和初始化一个 SPI NOR Flash 存储设备。它主要完成以下任务:

  • 检查设备的基本配置。
  • 配置 SPI 通信协议。
  • 分配和初始化缓冲区。
  • 读取并解析设备的 Flash 信息。
  • 初始化 MTD(Memory Technology Device)子系统。
  • 配置 SPI NOR 的具体参数和操作。
  • 初始化设备的其他功能(如 OTP、锁定操作等)。
  • 打印设备信息。

关键点解析:

(1)协议配置
nor->reg_proto = SNOR_PROTO_1_1_1;    // 寄存器操作使用标准SPI
nor->read_proto = SNOR_PROTO_1_1_1;   // 读操作默认模式
nor->write_proto = SNOR_PROTO_1_1_1;  // 写操作默认模式SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(1, 1, 1),#define SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits)	\(SNOR_PROTO_INST(_inst_nbits) |				\SNOR_PROTO_ADDR(_addr_nbits) |				\SNOR_PROTO_DATA(_data_nbits))

使用了 SNOR_PROTO_1_1_1 初始化一个 SPI NOR Flash 存储器的配置,表示指令、地址和数据的位宽均为 1 位,即指定其通信协议为标准 SPI 模式

(2)初始化Flash参数(核心步骤)
ret = spi_nor_init_params(nor);  // 基于info和SFDP表初始化参数

整体调用流程:

spi_nor_init_params()
├── 分配 params 内存
├── spi_nor_info_init_params() [阶段1: 基础默认参数]
│   ├── 设置默认参数(quad_enable, params->setup, set_4byte_addr_mode等)
│   ├── 配置读取能力(READ, READ_FAST, DUAL/QUAD/OCTAL等)
│   ├── 配置页编程能力(PP)
│   └── 配置擦除类型(4K, sector等)
├── spi_nor_manufacturer_init_params() [阶段2: 制造商参数]
│   └── 调用制造商特定的初始化(通过->default_init钩子)
├── spi_nor_sfdp_init_params() [阶段3: SFDP标准参数] (条件执行)
│   ├── 备份当前参数
│   ├── spi_nor_parse_sfdp() [解析SFDP表]
│   └── 失败时恢复备份参数
├── spi_nor_post_sfdp_fixups() [阶段4: SFDP后修正]
│   ├── 调用制造商特定的修正(->post_sfdp)
│   └── 调用闪存信息特定的修正(->post_sfdp)
└── spi_nor_late_init_params() [阶段5: 后期初始化]└── 初始化默认锁定操作(如果需要)
(3)MTD子系统集成
mtd->_write = spi_nor_write;
... ...
if (!mtd->name)mtd->name = dev_name(dev);
mtd->priv = nor;
mtd->type = MTD_NORFLASH;
mtd->writesize = nor->params->writesize;
mtd->flags = MTD_CAP_NORFLASH;
mtd->size = nor->params->size;
mtd->_erase = spi_nor_erase;
mtd->_read = spi_nor_read;
mtd->_suspend = spi_nor_suspend;
mtd->_resume = spi_nor_resume;
mtd->_get_device = spi_nor_get_device;
mtd->_put_device = spi_nor_put_device;
  • 初始化 MTD 子系统,设置读写、擦除、挂起、恢复等操作的回调函数。
  • 设置 MTD 的类型为 MTD_NORFLASH,表示这是一个 NOR Flash 设备。
  • 配置 MTD 的其他参数,如名称、大小、写块大小等。
(3)配置 SPI 通信参数
ret = spi_nor_setup(nor, hwcaps);
... ...
ret = spi_nor_set_addr_width(nor);
  • 调用 spi_nor_setup 配置 SPI 通信参数,如操作码、虚拟周期数、SPI 协议等。
  • 调用 spi_nor_set_addr_width 设置地址宽度。

其中:

spi_nor_setup
static int spi_nor_setup(struct spi_nor *nor,const struct spi_nor_hwcaps *hwcaps)
{if (!nor->params->setup)return 0;return nor->params->setup(nor, hwcaps);
}
  • 该函数用于设置或初始化一个 spi_nor 对象,如果 spi_norparams->setup 函数指针为空,则无需设置,如果 setup 函数指针存在,则调用它并返回其结果,nor->params->setup有如下初始化:
--> spi_nor_scan--> spi_nor_init_params--> spi_nor_info_init_params--> params->setup = spi_nor_default_setup

spi_nor_default_setup 主要是为一个 SPI NOR 闪存设备设置默认的硬件能力和操作命令,以确保设备能够与 SPI 控制器正确协作:

关键部分:

(1)通过按位与操作,计算出 SPI 控制器和 SPI 闪存都支持的硬件能力掩码 shared_mask

shared_mask = hwcaps->mask & params->hwcaps.mask;

(2)如果使用 spimem 接口,调用 spi_nor_spimem_adjust_hwcaps 函数进一步调整 shared_mask。如果不使用 spimem 接口,移除不支持的 SPI n-n-n 协议能力

 if (nor->spimem) {spi_nor_spimem_adjust_hwcaps(nor, &shared_mask);} else {ignored_mask = SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR; // 定义需要忽略的能力掩码if (shared_mask & ignored_mask) {dev_dbg(nor->dev,"SPI n-n-n protocols are not supported.\n"); // 输出调试信息shared_mask &= ~ignored_mask; // 移除不支持的能力}}

(3)调用 spi_nor_select_readspi_nor_select_ppspi_nor_select_erase 函数,分别选择读、写和擦除操作命令。

    err = spi_nor_select_read(nor, shared_mask);.../* 选择 Page Program(页编程)写操作命令。 */err = spi_nor_select_pp(nor, shared_mask);.../* 选择 Sector Erase(扇区擦除)操作命令。 */err = spi_nor_select_erase(nor);

spi_nor_select_read 函数通过硬件能力协商和操作命令选择,最终从spi_nor_read_command_index中选择一个和最适合该SPI设备硬件的能力:

read = &nor->params->reads[cmd];
nor->read_opcode = read->opcode;
nor->read_proto = read->proto;enum spi_nor_read_command_index {...SNOR_CMD_READ_1_1_1_DTR,/* Dual SPI */SNOR_CMD_READ_1_1_2,...SNOR_CMD_READ_1_2_2_DTR,/* Quad SPI */SNOR_CMD_READ_1_1_4,...SNOR_CMD_READ_1_4_4_DTR,/* Octal SPI */SNOR_CMD_READ_1_1_8,...SNOR_CMD_READ_MAX
};

其他两个函数同理。

spi_nor_set_addr_width

主要做了以下几件事:

  • 地址宽度的选择
    • 优先使用 SFDP 配置的地址宽度。
    • 对于特定协议(如 8D-8D-8D),强制使用 4 字节地址宽度。
    • 根据设备大小动态调整地址宽度(超过 16MiB 时使用 4 字节)。
  • 4 字节操作码配置
    • 如果设备支持 4 字节地址操作码,并且满足特定条件,则配置 4 字节操作码。
(4)初始化设备
ret = spi_nor_init(nor);static int spi_nor_init(struct spi_nor *nor)
{int err;// 尝试启用八进制 DTR 模式(Double Transfer Rate)err = spi_nor_octal_dtr_enable(nor, true);...// 尝试启用四线模式(Quad I/O).../** 一些 SPI NOR 闪存在上电复位后会默认进入写保护状态,* 以防止在启动过程中发生意外写入。* 为了向后兼容,默认情况下需要在上电时解锁整个闪存内存数组。* 根据内核配置:* (1) 不做任何操作,* (2) 总是解锁整个闪存数组,* (3) 仅在软件写保护位是易失性的情况下解锁整个闪存数组。* 后者由 SNOR_F_SWP_IS_VOLATILE 标志指示。*/if (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE) ||(IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE) &&nor->flags & SNOR_F_SWP_IS_VOLATILE))// 如果配置为禁用软件写保护,或者在写保护易失性时解锁闪存spi_nor_try_unlock_all(nor);// 如果地址宽度为 4 字节,且未使用 8D-8D-8D 协议,且不支持 4 字节操作码if (nor->addr_width == 4 &&nor->read_proto != SNOR_PROTO_8_8_8_DTR &&!(nor->flags & SNOR_F_4B_OPCODES)) {/** 如果 RESET# 引脚未正确连接,或系统未在启动序列中执行复位命令,* 则无法 100% 防止意外重启(如崩溃)。警告用户(或系统设计者)可能存在风险。*/WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,"enabling reset hack; may not recover from unexpected reboots\n");// 强制启用 4 字节地址模式(如果复位引脚不可靠,则启用“复位 hack”nor->params->set_4byte_addr_mode(nor, true);}...
}

主要做了下面几个关键事情:

  • 调用 spi_nor_octal_dtr_enable 尝试启用八进制 DTR 模式。
  • 调用 spi_nor_quad_enable 启用四线模式。
  • 如果地址宽度为 4 字节,且未使用 8D-8D-8D 协议,且不支持 4 字节操作码:
    • 检查 SNOR_F_BROKEN_RESET 标志。
    • 如果复位引脚连接不当或系统未在启动序列中执行复位命令,则打印警告信息。
    • 调用 set_4byte_addr_mode 启用 4 字节地址模式。
3、spi_nor_create_read_dirmap
static int spi_nor_create_read_dirmap(struct spi_nor *nor)
{/* 初始化SPI内存直接映射信息结构体 */struct spi_mem_dirmap_info info = {/* 设置操作模板: 包含命令、地址、dummy周期和数据输入 */.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),       // 读取操作码SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),    // 地址宽度SPI_MEM_OP_DUMMY(nor->read_dummy, 0),      // dummy周期数SPI_MEM_OP_DATA_IN(0, NULL, 0)             // 数据输入(长度初始为0)),.offset = 0,                    // 映射起始偏移量(从0开始).length = nor->mtd.size,        // 映射长度(整个闪存大小)};struct spi_mem_op *op = &info.op_tmpl;  // 获取操作模板指针/* 根据读取协议设置SPI操作参数 */spi_nor_spimem_setup_op(nor, op, nor->read_proto);/* * 将dummy周期数转换为字节数:* 字节数 = (dummy周期数 * 总线宽度) / 8* 对于DTR(双传输率)模式需要乘以2*/op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;if (spi_nor_protocol_is_dtr(nor->read_proto))op->dummy.nbytes *= 2;/** 显式设置数据总线宽度:* spi_nor_spimem_setup_op()只在数据字节数非零时设置总线宽度,* 所以这里需要单独设置数据总线宽度*/op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);/* 创建直接读取映射描述符 */nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, &info);/* 返回描述符指针的错误状态(成功返回0,失败返回错误码) */return PTR_ERR_OR_ZERO(nor->dirmap.rdesc);
}

函数创建一个直接内存映射区域,简化从 SPI NOR 闪存读取数据的操作,提高读取效率

实现方式

  • 通过初始化 spi_mem_dirmap_info 结构体,定义读取操作模板(包括命令、地址、dummy 周期和数据输入)。
  • spi_nor_spimem_setup_op 根据读取协议配置 SPI 内存操作模板,包括命令、地址、dummy 和数据的总线宽度及 DTR 模式。
  • 调用 devm_spi_mem_dirmap_create 创建一个 SPI 内存的直接映射描述符(spi_mem_dirmap_desc),并将其附加到给定的设备上

spi_nor_create_write_dirmap创建直接写入映射同理

4、mtd_device_register

MTD 设备注册和分区解析函数,主要进行分区解析、回退分区信息、重启通知器分配等,完成对该芯片逻辑分区注册至mtd子系统中。涉及MTD体系结构内容较多,学习SPI FLASH设备初始化阶段不进行深入学习


三、spi_nor_probe函数整体总结

  • 内存分配
    • devm_kzalloc:分配 spi_nor 结构体的内存,并确保在设备释放时自动释放内存。
  • 初始化 spi_nor 结构体
    • 设置 nor->spimem 和 nor->dev
    • 调用 spi_nor_set_flash_node 设置设备节点(如果存在设备树节点)。
  • 设置设备名称
    • 优先使用平台数据中的 name 字段。
    • 如果 name 不存在,则从 spi_mem 获取默认名称。
    • 如果平台数据中存在 type 字段,则优先使用 type 作为实际芯片名称。
  • 扫描 SPI NOR Flash
    • spi_nor_scan
      • 识别制造商 ID 和设备 ID。
      • 确定设备的页大小、块大小等特性。
      • 调用内部函数执行这些操作,通常包括发送特定的 SPI 命令来读取设备信息。
  • 处理大页支持
    • 如果设备的页大小超过系统页大小(PAGE_SIZE),则分配一个更大的缓冲区(bouncebuf)以避免缓冲区溢出。
    • 使用 devm_kfree 释放旧的缓冲区(如果存在),并使用 devm_kmalloc 分配新的缓冲区。
  • 创建直接映射
    • spi_nor_create_read_dirmap
      • 初始化读映射表,用于优化读操作。
    • spi_nor_create_write_dirmap
      • 初始化写映射表,用于优化写操作。
  • 注册 MTD 设备
    • mtd_device_register
      • 注册 mtd_info 结构体,使其可以被上层文件系统或其他子系统使用。
      • 如果存在分区信息,则设置分区信息。
      • 调用 mtd_device_parse_register 执行实际的注册操作。

相关文章:

  • 嵌入式人工智能应用-第三章 opencv操作8 图像特征之HOG 特征
  • 网络原理 - 3(UDP 协议)
  • 读文献先读图:韦恩图怎么看?
  • 设备、管道绝热(保冷)设计计算
  • Flutter路由模块化管理方案
  • 文件包含漏洞,目录遍历漏洞,CSRF,SSRF
  • 深度解析云计算:概念、优势与分类全览
  • 爬虫获取sku信息需要哪些库
  • 用银河麒麟 LiveCD 快速查看原系统 IP 和打印机配置
  • 网页下载的m3u8格式文件使用FFmpeg转为MP4
  • three.js中的instancedMesh类优化渲染多个同网格材质的模型
  • 你的大模型服务如何压测:首 Token 延迟、并发与 QPS
  • JavaScript — 总结
  • 基于XC7V690T的在轨抗单粒子翻转系统设计
  • 【数学建模】随机森林算法详解:原理、优缺点及应用
  • TensorFlow中使用Keras
  • Vscode开发STM32标准库
  • Linux网络编程 多进程UDP聊天室:共享内存与多进程间通信实战解析
  • 反转字符串
  • 【数据结构入门训练DAY-19】总结数据结构中的栈
  • 解放日报:128岁的凤凰自行车“双轮驱动”逆风突围
  • 夜读丨一位医生0点后的朋友圈
  • 18条举措!上海国际金融中心进一步提升跨境金融服务便利化
  • 中国旅游日主题月期间,东航将准备超51.9万套特惠机票
  • 中汽协发布规范驾驶辅助宣传与应用倡议书
  • 罗马教皇方济各去世,享年88岁