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
执行实际的注册操作。
- 注册
相关文章:
Linux学习笔记协议篇(六):SPI FLASH设备驱动
目录 一、设备树解析 二、SPI设备驱动代码分析 1、spi_nor_probe 2、spi_nor_scan (1)协议配置 (2)初始化Flash参数(核心步骤) (3)MTD子系统集成 (3)配置 SPI 通信参数 spi…...
Spring Boot 核心模块全解析:12 个模块详解及作用说明
在当今的微服务与云原生时代,Spring Boot 已成为构建现代 Java 应用的事实标准。它通过“约定优于配置”的理念,大大降低了 Spring 应用的开发门槛,帮助开发者快速启动和部署独立的、生产级别的项目。 本篇文章将系统梳理 Spring Boot 框架中…...
【无人机】无人机方向的设置,PX4飞控方向,QGC中设置飞控的方向/旋转角度。PX4使用手册飞行控制器/传感器方向
目录 #1、基本概念:计算方向 #2、详细步骤:设置方向 #3、微调 默认情况下,飞行控制器(和外部指南针,如果有)应放置在框架顶部朝上,方向应使箭头指向飞机前部。 如果板或外部指南针安装在任何…...
【Spring Boot基础】MyBatis的基础操作:日志、增删查改、列名和属性名匹配 -- 注解实现
MyBatis的基础操作 1.打印日志2. 参数传递2.1不传参2.2 固定参数 3. 增(Insert)3.1 用对象接参3.2 用param注解接收参数3.3 返回主键 4. 删(Delete)4.1 用Integer接参4.2 用对象接参 5. 改(Update)6. 查(Select)6.1 查6.2 拼接SQL语句6.3 列名和属性名匹配6.3.1 起别名 as6.3.2…...
泰迪智能科技大模型应用平台功能特色优势
1.平台概述 大模型应用平台是一款专为高校在大模型应用场景下的教学和科研需求设计的知识库问答系统。平台具备便捷性,支持上传常见格式的数据文件,如txt、doc、pdf、md等,并提供简洁明了的操作配置界面,使用户能够轻松搭建和训练…...
【NLP 69、KG - BERT】
人们总是在无能为力的时候喜欢说顺其自然 —— 25.4.21 一、KG-BERT:基于BERT的知识图谱补全模型 1.模型结构与设计 Ⅰ、核心思想: 将知识图谱中的三元组(头实体-关系-尾实体)转化为文本序列,利用BERT的上下文理解能…...
Spring解决循环依赖
Spring 通过 三级缓存机制 解决循环依赖问题,其核心思想是 提前暴露未完全初始化的 Bean,允许依赖方在 Bean 完全初始化前引用其早期版本。以下是详细解析: 一、三级缓存机制 Spring 在单例 Bean 的创建过程中维护了三级缓存,用于…...
深入解析 Spring 中的 @Value 注解(含源码级剖析 + 自定义实现)
深入解析 Spring 中的 Value 注解(含源码级剖析 自定义实现) 在 Spring 开发中,我们经常使用 Value 注解将配置文件中的值注入到 Bean 的属性中。本文将深入探讨 Value 的使用方式、默认值支持、底层原理以及自定义实现方式。 一、Value 的…...
【Flink SQL实战】 UTC 时区格式的 ISO 时间转东八区时间
文章目录 一、原始数据格式二、解决方案三、其他要求 在实际开发中,我们常常会遇到此类情况:数据源里的时间格式是类似 2025-04-21T09:23:16.025Z 这种带 TimeZone 标识的 ISO 8601 格式,而我们需要在 Flink SQL 中将其转换成北京时间显示。 …...
【论文阅读23】-地下水预测-TCN-LSTM-Attention(2024-11)
这篇论文主要围绕利用深度学习模型检测地下水位异常以识别地震前兆展开。 [1] Chen X, Yang L, Liao X, et al. Groundwater level prediction and earthquake precursor anomaly analysis based on TCN-LSTM-attention network[J]. IEEE Access, 2024, 12: 176696-176718. 期刊…...
/proc/sys/vm/下各参数含义
/proc/sys/vm/下各参数含义 admin_reserve_kbytes如何计算最小有效预留? compact_memorycompaction_proactivenesscompact_unevictable_alloweddirty_background_bytesdirty_background_ratiodirty_bytesdirty_expire_centisecsdirty_ratiodirtytime_expire_seconds…...
算法分析与设计——动态规划复习题(待更新
检测题: 组合优化问题的目标函数通常不包括以下哪种形式? A. 需最小化的代价函数 B. 需最大化的回报函数 C. 需满足的硬约束条件 D. 需最小化的能量函数 答案:C 关于约束条件的说法,以下哪项是正确的? A. 硬约束可以通…...
【EasyPan】项目常见问题解答(自用持续更新中…)
EasyPan 网盘项目介绍 一、项目概述 EasyPan 是一个基于 Vue3 SpringBoot 的网盘系统,支持文件存储、在线预览、分享协作及后台管理,技术栈涵盖主流前后端框架及中间件(MySQL、Redis、FFmpeg)。 二、核心功能模块 用户认证 注册…...
基于Java的不固定长度字符集在指定宽度和自适应模型下图片绘制生成实战
目录 前言 一、需求介绍 1、指定宽度生成 2、指定列自适应生成 二、Java生成实现 1、公共方法 2、指定宽度生成 3、指定列自适应生成 三、总结 前言 在当今数字化与信息化飞速发展的时代,图像的生成与处理技术正日益成为众多领域关注的焦点。从创意设计到数…...
电子电器架构 ---软件定义汽车的电子/电气(E/E)架构
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…...
Stable Diffusion 制作角色三视图
对于漫画创作,DPM 2M Karras和UniPC是高效且稳定的首选采样方法,结合Karras噪声调度可显著提升画面质量。若需进一步优化,可参考具体场景调整步数并辅以ControlNet等工具。避免使用随机性强的采样器(如Euler a)&#x…...
C++--负载均衡在线OJ
这是本人写的第二个项目,相比第一个代码量更少一些,但是此项目涉及linux中的内容更多,同样是干货满满,实现了 类似 leetcode 的题⽬列表在线编程功能,地址仓库:xwy/C学习项目 1. 所用技术与开发环境 C11和…...
【数字图像处理】彩色图像处理(1)
研究彩色图像处理的原因 1:利用颜色信息,可以简化目标物的区分,以及从场景中提取出目标物 2:人眼对颜色非常敏感,可以分辨出来几千种颜色色调和亮度,却只能分别出几十种灰度 彩色图像分类 伪彩色图像处理&…...
【Easylive】consumes = MediaType.MULTIPART_FORM_DATA_VALUE 与 @RequestPart
【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版 consumes MediaType.MULTIPART_FORM_DATA_VALUE 的作用 1. 定义请求的数据格式 • 作用:告诉 Feign 和 HTTP 客户端,这个接口 接收的是 multipart/form-data 格式的…...
【python】copy deepcopy 赋值= 对比
上结论 写法是否独立是否安全修改copy() (用于一维列表)✅ 是独立副本✅ 安全deepcopy() (多层结构时用)✅ 是完全副本✅ 安全直接赋值()❌ 是引用❌ 改一个会影响另一个 一、.copy() 和 deepcopy() 有什…...
环形缓冲区容量耗尽解决方案
以下是针对环形缓冲区在时间窗口统计场景中容量耗尽问题的解决方案设计及优劣分析,结合搜索结果中的技术原理和工程实践: 一、核心问题定位 当环形缓冲区容量耗尽时,新数据覆盖旧数据会导致: 时间窗口统计失真:无法准…...
蓝桥杯 17.发现环
发现环 原题目链接 题目描述 小明的实验室有 N 台电脑,编号 1 ⋯ N。 原本这 N 台电脑之间有 N−1 条数据链接相连,恰好构成一个树形网络。 在树形网络上,任意两台电脑之间有唯一的路径相连。 不过在最近一次维护网络时,管理…...
数据库服务器架构
ORM ORM(Object Relational Mapping):对象与关系数据之间的映射 映射关系表: 类(class)—— 数据库的表(table) 对象(object)——记录(record…...
Netty前置基础知识之BIO、NIO以及AIO理论详细解析和实战案例
前言 Netty是什么? Netty 是一个基于 Java 的 高性能异步事件驱动网络应用框架,主要用于快速开发可维护的协议服务器和客户端。它简化了网络编程的复杂性,特别适合构建需要处理海量并发连接、低延迟和高吞吐量的分布式系统。 1)Netty 是…...
职坐标IT培训:人工智能职业跃迁路径
随着人工智能时代全面来临,职业发展格局正经历颠覆性重构。政策端,《新一代人工智能发展规划》与《生成式AI服务管理办法》双轨并行,既为行业注入动能,也划定了技术应用的合规边界。在此背景下,从业者需构建覆盖基础理…...
Redis 的单线程模型对微服务意味着什么?需要注意哪些潜在瓶颈?
Redis 的单线程模型是其高性能的关键因素之一,但这在微服务场景下既是优势,也可能带来潜在的瓶颈。理解这一点有助于我们在微服务架构中更好的使用Redis。 Redis 单线程模型的核心: 命令处理是单线程的: Redis 使用了一个主线程来接收客户端…...
Redis 有序集合(Sorted Set)
Redis 有序集合(Sorted Set) 以下从基础命令、内部编码和使用场景三个维度对 Redis 有序集合进行详细解析: 一、基础命令 命令时间复杂度命令含义zadd key score member [score member …] O ( k l o g ( n ) ) O(klog(n)) O(klog(n))&…...
C语言中联合体(Union)和结构体(Struct)的嵌套用法
联合体和结构体是C语言中两种重要的复合数据类型,它们可以相互嵌套使用,为复杂数据的表示提供了灵活的方式。 1. 联合体(Union)基础 联合体是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。联合体的所有成员共享同一块内存空…...
Rust: 从内存地址信息看内存布局
内存布局其实有几个:address(地址)、size(大小)、alignment(对齐位数,2 的自然数次幂,2,4,8…)。 今天主要从address来看内存的布局。 下面以Str…...
分类算法中one-vs-rest策略和one-vs-one 策略的区别是什么?
LGBMClassifier 参数中,常使用objective: 这个参数定义了模型的目标函数。 而对于多分类问题,通常使用 multiclass 或者 multiclassova。multiclass 表示 one-vs-rest 策略,而 multiclassova 则是 one-vs-one 策略。 在机器学习领域&#x…...
新能源汽车充电桩运营模式的发展与优化路径探析
摘要:以民用新能源汽车充电桩为研究对象,在分析政府主导型、电网企业主导型及汽车厂商主导型三种运营模式特点的基础上,结合我国新能源汽车发展现状,提出汽车厂商与电网企业协同共建的联盟模式。通过构建涵盖政府补贴、建设成本与…...
【前端样式】用 aspect-ratio 实现等比容器:视频封面与图片占位的终极解决方案
在网页开发中,处理视频封面、图片卡片等需要固定比例的容器一直是前端工程师的必修课。本文将以 aspect-ratio 属性为核心,深入探讨如何优雅实现等比容器,并通过完整代码示例和常见问题解析,助你彻底掌握这一现代布局利器。 目录…...
redis常用的五种数据类型
redis常用的五种数据类型 文档 redis单机安装redis数据类型-位图bitmap 说明 官网操作命令指南页面:https://redis.io/docs/latest/commands/?nameget&groupstring 常用命令 keys *:查看所有键exists k1 k2:键存在个数type k1&…...
Cribl 利用表向event 中插入相应的字段-example-02
Working with Lookups – Example 2 Let’s assume we have the following lookup file, and given both the fields impact and priority in an event, we would like to add a corresponding ingestion-time field called severity. cisco_sourcefire_severity.csv im…...
SystemWeaver详解:从入门到精通的深度实战指南
SystemWeaver详解:从入门到精通的深度实战指南 文章目录 SystemWeaver详解:从入门到精通的深度实战指南一、SystemWeaver环境搭建与基础配置1.1 多平台安装全流程 二、新手必学的十大核心操作2.1 项目创建全流程2.2 建模工具箱深度解析 三、需求工程与系…...
归一化对C4.5决策树无效的数学原理与实证分析
一、引言 在机器学习的预处理流程中,归一化(Normalization)常被视作提升模型性能的"银弹"。然而,这一经验法则在决策树算法中却遭遇挑战——尤其对基于信息增益比的C4.5算法,归一化操作几乎不产生任何效果。…...
# 05_Elastic Stack 从入门到实践(五)
05_Elastic Stack 从入门到实践(五) 一、Elasticsearch集群之分布式文档(文档的写操作、搜索操作) 1、文档的写操作 1)新建、索引和删除请求都是写(write)操作,它们必须在主分片上成功完成才能复制到相关的复制分片上。 2)下面我们罗列在主分片和复制分片上成功新建、…...
n8n 中文系列教程_05.如何在本机部署/安装 n8n(详细图文教程)
n8n 是一款强大的开源工作流自动化工具,可帮助你连接各类应用与服务,实现自动化任务。如果你想快速体验 n8n 的功能,本机部署是最简单的方式。本教程将手把手指导你在 Windows 或 MacOS 上通过 Docker 轻松安装和运行 n8n,无需服务…...
Spark,从0开始配置Spark的local模式
1.启动虚拟机 2.通过finalshell连接虚拟机,并上传安装文件到 /opt/software下 3.解压spark安装文件到/opt/module下 tar -zxvf spark-3.3.1-bin-hadoop3.tgz -C /opt/module/ 4.重命名,把解压后的文件夹改成spark-local。因为后续我们还会使用其他的配置…...
策略模式:优雅应对多变的业务需求
一、策略模式基础概念 策略模式(Strategy Pattern) 是一种行为型设计模式,它通过定义一系列可互换的算法族,并将每个算法封装成独立的策略类,使得算法可以独立于使用它的客户端变化。策略模式的核心思想是 “将算法的…...
性能比拼: Nginx vs Apache
本内容是对知名性能评测博主 Anton Putra Nginx vs Apache Performance 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 引言 在本视频中,我们将比较 Nginx 与 Apache 这两个 Web 服务器。我们将进行多项测试,并且实际上,在某些情…...
(7)NodeJS的使用与NPM包管理器
本系列教程目录:Vue3Element Plus全套学习笔记-目录大纲 文章目录 第1章 NodeJS1.1 NodeJS简介1.2 NodeJS下载1.3 Node基础入门1.3.1 控制台输出1.3.2 使用函数 1.4 NodeJS模块化编程1.5 NodeJS内置模块1.5.1 fs模块1.5.2 http模块 1.6 NPM包管理工具1.6.1 修改npm镜…...
策略模式:思考与解读
原文地址:策略模式:思考与解读 更多内容请关注:7.深入思考与解读设计模式 引言 你是否曾遇到过这样的情况:在一个系统中,有许多算法或策略,每种策略的实现逻辑相似,但在某些情况下需要进行替换和扩展&am…...
1.Vue3 - 创建Vue3工程
目录 一、 基于vue-cli 脚手架二、基于vite 推荐2.1 介绍2.2 创建项目2.3 文件介绍2.3.1 extensions.json2.3.2 脚手架的根目录2.3.3 主要文件 src2.3.3.1 main.js2.3.3.2 App.vue 组件2.3.3.3 conponents 2.3.4 env.d.ts2.3.5 index.html 入口文件2.3.6 package2.3.7 tsconfig…...
Spring MVC 执行流程全解析:从请求到响应的七步走
Spring MVC 的工作流程,漏了这一个今天刚好面试提到了,没回答出来,记录一下。 🧭 Spring MVC 整体执行流程一览(从请求到响应) ✅ 1. 请求发出:浏览器发送 HTTP 请求 如用户访问: …...
四元数转旋转矩阵
目录 gsplat 四元数转旋转矩阵等同代码实现 scipy 四元数转旋转矩阵替换代码 gsplat 四元数转旋转矩阵等同代码实现 import torch import torch.nn.functional as Fdef quat_act(x: torch.Tensor) -> torch.Tensor:return x / x.norm(dim-1, keepdimTrue)def normalized_q…...
Pytorch的极简transformer用于时间序列预测
本博客来源于CSDN机器鱼,未同意任何人转载。 更多内容,欢迎点击本专栏,查看更多内容。 目录 0.引言 1.数据准备 2. 网络搭建 3. 完整代码 4. 结语 0.引言 在【博客】中,我们基于tensorflow2.x深度学习框架搭建了transforme…...
垂直行业突围:工业软件在汽车、航空领域的 “破壁” 实践
在当今科技高速发展的时代,工业软件已悄然完成从通用工具到垂直行业 “战略武器” 的蜕变。特别是在汽车与航空这两大高端制造领域,工业软件的价值早已超越单纯的效率提升,成为关乎核心技术自主可控的关键要素,一场围绕工业软件的…...
人工智能在智能家居中的应用与发展
随着人工智能(AI)技术的飞速发展,智能家居逐渐成为现代生活的重要组成部分。从智能语音助手到智能家电,AI正在改变我们与家居环境的互动方式,让生活更加便捷、舒适和高效。本文将探讨人工智能在智能家居中的应用现状、…...
维度建模工具箱 提纲与总结
这里写自定义目录标题 基本概念事实表和维度表BI(Business Intelligence) 产品 事实表事实表的粒度事实表的种类 维度表建模技术基本原则避免用自然键作为维度表的主键,而要使用类似自增的整数键避免过度规范化避免变成形同事实表的维度表 SCD(Slowly Changed Dimen…...