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芯片,位于片选0flash0
是标签(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时钟频率29MHzspi-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 = µn_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_nor
的params->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_read
、spi_nor_select_pp
和 spi_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
执行实际的注册操作。
- 注册