目录
[(3)配置 SPI 通信参数](#(3)配置 SPI 通信参数)
根据 "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.c
static 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 表示需要自动检测
else
flash_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; // 寄存器操作使用标准SPI
nor->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 Register
if (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
执行实际的注册操作。
- 注册