dw-spi-mmio控制器驱动 需要知道:SPI大致分为了以下三个层次,分别是SPI核心,SPI控制器驱动,SPI设备驱动。
SPI核心层代码位于kernel/include/linux/spi/spi.h和kernel/drivers/spi/spi.c,核心层提供了SPI总线驱动和设备驱动的注册,注册方法,并提供一些控制器驱动实现的回调函数。核心层包含一些重要的结构体,struct spi_controller,struct spi_transfer,struct spi_message等。
控制器驱动就是本文核心。主要实现SPI硬件数据收发功能,只有这样,挂接在SPI总线上的SPI设备才能通过SPI控制器读写数据并与CPU进行数据交互。
SPI设备驱动一般挂接在受CPU控制的SPI控制器上,通过SPI控制器与CPU进行数据交互。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 graph TD subgraph 用户空间层 U3[应用程序] end subgraph 内核空间层 K4[SPI设备驱动] K3[SPI设备驱动] K2[SPI核心] K1[SPI控制器驱动] end subgraph 硬件层 H1[SPI控制器] H2[SPI设备1] H3[SPI设备2] end U3 --> K4 U3 --> K3 K4 --> K2 K3 --> K2 K2 --> K1 K1 --> H1 H1 --> H2 H1 --> H3
设备树配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 spi0: spi@f0383000 { compatible = "snps,dw-apb-ssi" ; status = "disabled" ; interrupt-parent = <&gic>; interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; reg = <0x0 0xf0383000 0x0 0x1000 >; reg-io-width = <4 >; num-cs = <1 >; dmas = <&dw_axi_dmac 0x1 0 0 &dw_axi_dmac 0x0 0 0 >; dma-names = "tx" , "rx" ; }; &spi0 { status = "disabled" ; #address-cells = <1> ; #size-cells = <0> ; cs-gpios = <&portb 1 GPIO_ACTIVE_LOW>; spi0_flash0: wnbnd0@0 { compatible = "jedec,spi-nor" ; spi-max-frequency = <400000 >; reg = <0 >; #address-cells = <1> ; #size-cells = <1> ; partition@spi-test0{ label = "test0" ; reg = <0x0 0x800000 >; }; }; };
spi0控制器下挂载了spi0_flash的flash。
spi0控制器对应的文件为:kernel/drivers/spi/spi-dw-mmio.c
与dw相关的驱动文件还有kernel/drivers/spi/spi-dw-core.c kernel/drivers/spi/spi-dw-dma.c
spi0_flash0对应的驱动文件为:kernel/drivers/mtd/devices/m25p80.c
与flash相关的重要驱动文件有kernel/drivers/mtd/spi-nor/spi-nor.c,实现了spi_nor_scan,spi_nor_setup。在spi-nor.h中只有spi_nor_scan开放出来了,因此这个函数需要重点分析
关于片选引脚:
在spi_device中有成员:chip_select,表示spi_master下的第几个设备
在spi_controller中有一个cs_gpios数组,里面存放有下面各个spi设备的片选引脚
spi_device的片选引脚就是:cs_gpios[spi_device.chip_select]
在spi_device中cs_gpio是可选项,也可以把spi_device的片选引脚记录在这里
在spi_controller中的是cs_gpios数组
SPI控制器驱动 spi-dw-mimo.c注册的是平台驱动
SPI 控制器驱动 (platform 驱动类型),负责匹配设备树中的 SPI 控制器节点(spi@f0383000)
1 2 3 4 static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "snps,dw-apb-ssi", .data = dw_spi_dw_apb_init}, { /* end of table */} };
compatible字段 :这是匹配的 “关键字”,与设备树中控制器节点的compatible属性完全对应。设备树中spi@f0383000的compatible = "snps,dw-apb-ssi",与第一个条目匹配。
.data字段 :可选的附加数据,这里指向dw_spi_dw_apb_init函数,用于初始化该类型控制器的特定参数(这里实现的是DMA配置实现)
1 2 3 4 5 6 7 8 9 10 static struct platform_driver dw_spi_mmio_driver = { .probe = dw_spi_mmio_probe, // 设备匹配成功后执行的初始化函数 .remove = dw_spi_mmio_remove, // 设备移除时执行的清理函数 .driver = { .name = DRIVER_NAME, // 驱动名称(辅助匹配,优先级低于compatible) .of_match_table = dw_spi_mmio_of_match, // 关联设备树匹配表 .acpi_match_table = ACPI_PTR(dw_spi_mmio_acpi_match), // 关联ACPI匹配表(用于非设备树系统) .pm = SPI_DEV_PM_OPS, // 电源管理操作集 }, };
SPI 控制器是直接挂在 CPU 总线上的设备(如 APB 总线),属于platform设备类型(内核对 “挂在总线上的设备” 的抽象),因此其驱动需用platform_driver结构体进行注册
SPI从设备驱动 SPI 从设备驱动 (spi_driver 类型),负责匹配 SPI 总线上的从设备(即wnbnd0@0 flash)
这里只列出通过spi总线访问nor flash的情况,对于通过qspi控制器访问nor flash暂时还没细看
1 2 3 4 5 6 7 8 9 static struct spi_driver m25p80_driver = { .driver = { .name = "m25p80", // 驱动名称 .of_match_table = m25p_of_table, // 关联设备树匹配表 }, .id_table = m25p_ids, // 基于SPI设备名称的匹配表(旧方式,优先级低于of_match_table) .probe = m25p_probe, // 设备匹配成功后执行的初始化函数 .remove = m25p_remove, // 设备移除时的清理函数 };
SPI 闪存是挂在 SPI 总线上的从设备,属于spi设备类型(内核对 “SPI 总线上的设备” 的抽象),因此其驱动需用spi_driver结构体
id_table的作用 :早期没有设备树时,通过 SPI 设备的name字段匹配(如name = "m25p80"),现在主要用于兼容旧设备,优先级低于of_match_table
设备与驱动的匹配流程(结合设备树)
内核启动时扫描设备树 : 解析spi@f0383000(控制器)和wnbnd0@0(闪存)节点,分别创建platform_device(控制器设备)和spi_device(闪存设备)结构体,其中包含节点的compatible属性。
驱动加载时注册匹配信息 :
spi-dw-mmio.ko加载后,通过platform_driver_register()注册dw_spi_mmio_driver,内核将其of_match_table加入全局设备树匹配表。
m25p80.ko加载后,通过spi_register_driver()注册m25p80_driver,内核将其of_match_table加入全局设备树匹配表。
匹配过程 :
对于spi@f0383000(platform 设备):内核遍历所有platform_driver,发现dw_spi_mmio_driver的of_match_table中compatible = "snps,dw-apb-ssi"与设备一致,触发dw_spi_mmio_probe()初始化控制器。
对于wnbnd0@0(spi 设备):内核遍历所有spi_driver,发现m25p80_driver的of_match_table中compatible = "jedec,spi-nor"与设备一致,触发m25p_probe()初始化闪存。
匹配成功后 : 控制器驱动初始化 SPI 硬件,设备驱动通过 SPI 核心层接口(如spi_sync())与控制器交互,完成数据传输。
SPI DW控制器驱动probe解析 当设备树中的spi节点被解析为paltform_device之后,会与platform_driver.driver->of_device_id中的compatible属性进行match,匹配成功后则会调用probe函数
1 2 3 4 5 static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "snps,dw-apb-ssi", .data = dw_spi_dw_apb_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
probe函数调用关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 dw_spi_mmio_probe struct dw_spi_mmio *dwsmmio = devm_kzalloc(&pdev->dev, sizeof (struct dw_spi_mmio),GFP_KERNEL); struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0 ); dwsmmio->dws.paddr = mem->start; dwsmmio->dws.regs = devm_ioremap_resource(&pdev->dev, mem); dwsmmio->dws.irq = platform_get_irq(pdev, 0 ); dwsmmio->clk = devm_clk_get(&pdev->dev, NULL ); dwsmmio->pclk = devm_clk_get(&pdev->dev, "pclk" ); dwsmmio->dws.max_freq = clk_get_rate(dwsmmio->clk); dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev,"spi" ); reset_control_deassert(dwsmmio->rstc); device_property_read_u32(&pdev->dev, "reg-io-width" , &dwsmmio->dws.reg_io_width); device_property_read_u32(&pdev->dev, "num-cs" , &dwsmmio->dws.num-cs); dwsmmio->dws.bus_num = pdev->id; init_func = of_device_get_match_data(&pdev->dev); ret = init_func(pdev, dwsmmio); ret = dw_spi_add_host(&pdev->dev, &dwsmmio->dws); platform_set_drvdata(pdev, dwsmmio);
dma_ops操作集初始化 完成DMA的dma_ops操作集的实现配置,文件位置在kernel/drivers/spi/spi-dw-dma.c可以通过标志位配置是否使用dma传输。
1 2 3 init_func = of_device_get_match_data(&pdev->dev); ret = init_func(pdev, dwsmmio); ==>static int dw_spi_dw_apb_init(pdev,dwsmmio)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static int dw_spi_dw_apb_init (struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { dw_spi_dma_setup_generic(&dwsmmio->dws); return 0 ; } static const struct dw_spi_dma_ops dw_spi_dma_generic_ops = { .dma_init = dw_spi_dma_init_generic, .dma_exit = dw_spi_dma_exit, .dma_setup = dw_spi_dma_setup, .can_dma = dw_spi_can_dma, .dma_transfer = dw_spi_dma_transfer, .dma_stop = dw_spi_dma_stop, }; void dw_spi_dma_setup_generic (struct dw_spi *dws) { dws->dma_ops = &dw_spi_dma_generic_ops; } EXPORT_SYMBOL_GPL(dw_spi_dma_setup_generic);
SPI DW控制器驱动add_host解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 ret = dw_spi_add_host(&pdev->dev, &dwsmmio->dws); dw_spi_add_host dws->master = spi_alloc_master(dev, 0 ); dws->dma_addr = (dma_addr_t )(dws->paddr + DW_SPI_DR); spi_hw_init(dev, dws); ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev),master); dw_spi_init_mem_ops(dws); dws->master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; dws->master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4 , 16 ); dws->master->bus_num = dws->bus_num; dws->master->num_chipselect = dws->num_cs; dws->master->setup = dw_spi_setup; dws->master->cleanup = dw_spi_cleanup; if (dws->set_cs) dws->master->set_cs = dws->set_cs; else dws->master->set_cs = dw_spi_set_cs; dws->master->transfer_one = dw_spi_transfer_one; dws->master->handle_err = dw_spi_handle_err; if (dws->mem_ops.exec_op) dws->master->mem_ops = &dws->mem_ops; dws->master->max_speed_hz = dws->max_freq; dws->master->dev.of_node = dev->of_node; dws->master->dev.fwnode = dev->fwnode; dws->master->flags = SPI_MASTER_GPIO_SS; dws->master->auto_runtime_pm = true ; device_property_read_u32(dev, "rx-sample-delay-ns" ,&dws->def_rx_sample_dly_ns); if (dws->dma_ops && dws->dma_ops->dma_init) { ret = dws->dma_ops->dma_init(dev, dws); if (ret) { dev_warn(dev, "DMA init failed\n" ); } else { master->can_dma = dws->dma_ops->can_dma; master->flags |= SPI_CONTROLLER_MUST_TX; } ret = spi_register_controller(dws->master); dw_spi_debugfs_init(dws); spi_controller_set_devdata(master, dws);
SPI DW控制器驱动dw_spi_transfer_one解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 static int dw_spi_transfer_one (struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { struct dw_spi *dws = spi_controller_get_devdata(master); struct dw_spi_cfg cfg = { .tmode = SPI_TMOD_TR, .dfs = transfer->bits_per_word, .freq = transfer->speed_hz, }; int ret; dws->dma_mapped = 0 ; dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); dws->tx = (void *)transfer->tx_buf; dws->tx_len = transfer->len / dws->n_bytes; dws->rx = transfer->rx_buf; dws->rx_len = dws->tx_len; smp_mb(); spi_enable_chip(dws, 0 ); dw_spi_update_config(dws, spi, &cfg); if (master->can_dma && master->can_dma(master, spi, transfer)) dws->dma_mapped = master->cur_msg_mapped; spi_mask_intr(dws, 0xff ); if (dws->dma_mapped) { ret = dws->dma_ops->dma_setup(dws, transfer); if (ret) return ret; } spi_enable_chip(dws, 1 ); if (dws->dma_mapped) return dws->dma_ops->dma_transfer(dws, transfer); else if (dws->irq == IRQ_NOTCONNECTED) return dw_spi_poll_transfer(dws, transfer); dw_spi_irq_setup(dws); return 1 ; }
dw_spi_transfer_one实现了SPI传输,有3种模式,DMA模式 poll轮询模式 中断传输模式。在ts芯片中优先使用DMA模式进行传输
SPI DW控制器驱动spi_register_controller解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 int spi_register_controller (struct spi_controller *ctlr) status = of_spi_register_master(ctlr); for (i = 0 ; i < nb; i++) cs[i] = of_get_named_gpio(ctlr->dev.of_node, "cs-gpios" , i); if (ctlr->bus_num >= 0 ) { mutex_lock(&board_lock); id = idr_alloc(&spi_master_idr, ctlr, ctlr->bus_num, ctlr->bus_num + 1 , GFP_KERNEL); mutex_unlock(&board_lock); if (WARN(id < 0 , "couldn't get idr" )) return id == -ENOSPC ? -EBUSY : id; ctlr->bus_num = id; } INIT_LIST_HEAD(&ctlr->queue ); spin_lock_init(&ctlr->queue_lock); spin_lock_init(&ctlr->bus_lock_spinlock); mutex_init(&ctlr->bus_lock_mutex); mutex_init(&ctlr->io_mutex); ctlr->bus_lock_flag = 0 ; init_completion(&ctlr->xfer_completion); if (!ctlr->max_dma_len) ctlr->max_dma_len = INT_MAX; dev_set_name(&ctlr->dev, "spi%u" , ctlr->bus_num); status = device_add(&ctlr->dev); if (status < 0 ) { mutex_lock(&board_lock); idr_remove(&spi_master_idr, ctlr->bus_num); mutex_unlock(&board_lock); goto done; } if (ctlr->transfer) dev_info(dev, "controller is unqueued, this is deprecated\n" ); else { status = spi_controller_initialize_queue(ctlr); if (status) { device_del(&ctlr->dev); mutex_lock(&board_lock); idr_remove(&spi_master_idr, ctlr->bus_num); mutex_unlock(&board_lock); goto done; } } spin_lock_init(&ctlr->statistics.lock); mutex_lock(&board_lock); list_add_tail(&ctlr->list , &spi_controller_list); list_for_each_entry(bi, &board_list, list ) spi_match_controller_to_boardinfo(ctlr, &bi->board_info); mutex_unlock(&board_lock); of_register_spi_devices(ctlr); acpi_register_spi_devices(ctlr);
SPI DW控制器注册从设备 of_register_spi_devices 遍历控制器设备树节点的子节点(每个子节点代表一个 SPI 从设备),解析 compatible、reg(reg表示片选号,代表第几个spi设备,不是寄存器)、spi-max-frequency 等属性,自动创建 struct spi_device 并注册。
1 2 3 4 5 6 7 8 9 10 spi_register_controller of_register_spi_devices of_register_spi_device spi = spi_alloc_device(ctlr); rc = of_modalias_node(nc, spi->modalias,sizeof (spi->modalias)); rc = of_spi_parse_dt(ctlr, spi, nc); rc = spi_add_device(spi);
1 2 3 4 5 6 7 8 9 10 11 12 13 spi0 { spi0_flash0: wnbnd0@0 { compatible = "jedec,spi-nor" ; spi-max-frequency = <1000000 >; reg = <0 >; #address-cells = <1> ; #size-cells = <1> ; partition@spi-test0{ label = "test0" ; reg = <0x0 0x800000 >; }; }; };
SPI从设备驱动(补充) 针对上一节中SPI从设备驱动的具体补充。由于这个从设备既和spi总线有关,又和mtd子系统有关,同时又从属于spi-nor系统框架之下,因此要想搞明白需要花很大的时间。这里只是简单介绍一些查到的介绍。
前置知识 mtd子系统 MTD设备通常可分为四层 ,设备节点、MTD设备层、MTD原始设备层和硬件驱动层。
硬件驱动层主要文件位于
1 2 3 4 drivers/mtd/chips : CFI/jedec接口通用驱动 drivers/mtd/nand : nand通用驱动和部分底层驱动程序 drivers/mtd/maps : nor flash映射关系相关函数 drivers/mtd/spi-nor: nor flash底层驱动
MTD设备层主要文件位于
1 2 mtdchar.c : MTD字符设备接口相关实现 mtdblock.c : MTD块设备接口相关实现
设备节点位于/dev/mtdx和/dev/mtdblockX,分别是字符设备节点和块设备节点。两者一般是一一对应的,在物理上两者其实是同一个东西,但是在软件上是进行区分的,可以理解为他们是对同一分区的不同描述方法。
dev/mtdx:是mtdchar.c注册生成的,支持open,read,write,ioctl等。flash_erase, nanddump等都是对字符节点,通过read,write,ioctl等执行
对应mtdblock.c和mtd_blkdevs.c,是Flash驱动中用add_mtd_partitions()添加MTD设备分区,而生成的对应的块设备
MTD块设备驱动程序可以让flash器件伪装成块设备,实际上它通过把整块的erase block放到ram里面进行访问,然后再更新到flash,用户可以在这个块设备上创建通常的文件系统
对于MTD块设备,MTD设备层是不提供ioctl的实现方法的,不能使用nandwrite,flash_eraseall,flash_erase等工具去对/dev/mtdblockN去进行操作
mtd-utils工具只能用与/dev/mtdN的MTD字符设备
mount、umount命令只对/dev/mtdblockN的MTD块设备有效
需要注意的是:MTD设备既非块设备也不是字符设备,但可以同时提供字符设备和块设备接口来操作它
spi-nor 系统框架 SPI总线只能处理字节流,总线控制器的操作与所连接的特定设备无关。SPI控制器只知道发送或接收字节(Tx和Rx)。因此,我们定义一个新的分层方案,在该方案下,控制器驱动程序知道SPI NOR协议的操作码、寻址和其他细节。
内容越看越多,暂时就整理这么多吧。先把从设备驱动分析一下,mtd子系统和spi nor系统框架单独整理到一个新文件中。
重要结构体 struct spi_nor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 struct spi_nor { struct mtd_info mtd ; struct mutex lock ; struct device *dev ; u32 page_size; u8 addr_width; u8 erase_opcode; u8 read_opcode; u8 read_dummy; u8 program_opcode; enum spi_nor_protocol read_proto ; enum spi_nor_protocol write_proto ; enum spi_nor_protocol reg_proto ; bool sst_write_second; u32 flags; u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops); void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); ssize_t (*read)(struct spi_nor *nor, loff_t from, size_t len, u_char *read_buf); ssize_t (*write)(struct spi_nor *nor, loff_t to, size_t len, const u_char *write_buf); int (*erase)(struct spi_nor *nor, loff_t offs); int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); void *priv; };
设备树匹配 1 2 3 4 5 6 7 8 9 10 11 12 13 spi0 { spi0_flash0: wnbnd0@0 { compatible = "jedec,spi-nor"; spi-max-frequency = <1000000>; reg = <0>; #address-cells = <1>; #size-cells = <1>; partition@spi-test0{ label = "test0"; reg = <0x0 0x800000>; /* 8MB */ }; }; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static const struct of_device_id m25p_of_table [] = { { .compatible = "jedec,spi-nor" }, {} }; static struct spi_driver m25p80_driver = { .driver = { .name = "m25p80" , .of_match_table = m25p_of_table, }, .id_table = m25p_ids, .probe = m25p_probe, .remove = m25p_remove, };
probe函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 static int m25p_probe (struct spi_device *spi) { struct flash_platform_data *data ; struct m25p *flash ; struct spi_nor *nor ; struct spi_nor_hwcaps hwcaps = { .mask = SNOR_HWCAPS_READ | SNOR_HWCAPS_READ_FAST | SNOR_HWCAPS_PP, }; char *flash_name; int ret; printk("m25p80: %s\n" , __func__); data = dev_get_platdata(&spi->dev); flash = devm_kzalloc(&spi->dev, sizeof (*flash), GFP_KERNEL); if (!flash) return -ENOMEM; nor = &flash->spi_nor; nor->read = m25p80_read; nor->write = m25p80_write; nor->write_reg = m25p80_write_reg; nor->read_reg = m25p80_read_reg; nor->dev = &spi->dev; spi_nor_set_flash_node(nor, spi->dev.of_node); nor->priv = flash; spi_set_drvdata(spi, flash); flash->spi = spi; if (spi->mode & SPI_RX_QUAD) { hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4; if (spi->mode & SPI_TX_QUAD) hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 | SNOR_HWCAPS_PP_1_1_4 | SNOR_HWCAPS_PP_1_4_4); } else if (spi->mode & SPI_RX_DUAL) { hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2; if (spi->mode & SPI_TX_DUAL) hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2; } if (data && data->name) nor->mtd.name = data->name; if (data && data->type) flash_name = data->type; else if (!strcmp (spi->modalias, "spi-nor" )) flash_name = NULL ; else flash_name = spi->modalias; ret = spi_nor_scan(nor, flash_name, &hwcaps); if (ret) { printk("%s %d ret:%d" ,__func__,__LINE__,ret); return ret; } return mtd_device_register(&nor->mtd, data ? data->parts : NULL , data ? data->nr_parts : 0 ); }
可以按照顺序划分为以下几个部分
设置struct spi_nor结构体
设置struct spi_nor_hwcaps结构体
spi_nor_scan注册struct spi_nor结构体
后面需要重点分析spi_nor_scan结构体
mtd device注册
spi_nor_scan**(重点分析)** 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 int spi_nor_scan (struct spi_nor *nor, const char *name, const struct spi_nor_hwcaps *hwcaps) { struct spi_nor_flash_parameter params ; const struct flash_info *info = NULL ; struct device *dev = nor->dev; struct mtd_info *mtd = &nor->mtd; struct device_node *np = spi_nor_get_flash_node(nor); int ret; int i; printk("spi_nor_scan start\n" ); ret = spi_nor_check(nor); if (ret) return ret; nor->reg_proto = SNOR_PROTO_1_1_1; nor->read_proto = SNOR_PROTO_1_1_1; nor->write_proto = SNOR_PROTO_1_1_1; if (name) info = spi_nor_match_id(name); if (!info) info = spi_nor_read_id(nor); if (IS_ERR_OR_NULL(info)) return -ENOENT; if (name && info->id_len) { const struct flash_info *jinfo ; jinfo = spi_nor_read_id(nor); if (IS_ERR(jinfo)) { return PTR_ERR(jinfo); } else if (jinfo != info) { dev_warn(dev, "found %s, expected %s\n" , jinfo->name, info->name); info = jinfo; } } mutex_init(&nor->lock); if (info->flags & SPI_S3AN) nor->flags |= SNOR_F_READY_XSR_RDY; ret = spi_nor_init_params(nor, info, ¶ms); if (ret) return ret; if (JEDEC_MFR(info) == SNOR_MFR_ATMEL || JEDEC_MFR(info) == SNOR_MFR_INTEL || JEDEC_MFR(info) == SNOR_MFR_SST || info->flags & SPI_NOR_HAS_LOCK) { write_enable(nor); write_sr(nor, 0 ); spi_nor_wait_till_ready(nor); } if (!mtd->name) mtd->name = dev_name(dev); mtd->priv = nor; mtd->type = MTD_NORFLASH; mtd->writesize = 1 ; mtd->flags = MTD_CAP_NORFLASH; mtd->size = params.size; mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; if (JEDEC_MFR(info) == SNOR_MFR_MICRON || info->flags & SPI_NOR_HAS_LOCK) { nor->flash_lock = stm_lock; nor->flash_unlock = stm_unlock; nor->flash_is_locked = stm_is_locked; } if (nor->flash_lock && nor->flash_unlock && nor->flash_is_locked) { mtd->_lock = spi_nor_lock; mtd->_unlock = spi_nor_unlock; mtd->_is_locked = spi_nor_is_locked; } if (info->flags & SST_WRITE) mtd->_write = sst_write; else mtd->_write = spi_nor_write; if (info->flags & USE_FSR) nor->flags |= SNOR_F_USE_FSR; if (info->flags & SPI_NOR_HAS_TB) nor->flags |= SNOR_F_HAS_SR_TB; 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; if (info->flags & SPI_NOR_NO_ERASE) mtd->flags |= MTD_NO_ERASE; mtd->dev.parent = dev; nor->page_size = params.page_size; mtd->writebufsize = nor->page_size; if (np) { if (of_property_read_bool(np, "m25p,fast-read" )) params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST; else params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; } else { params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST; } if (info->flags & SPI_NOR_NO_FR) params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST; ret = spi_nor_setup(nor, info, ¶ms, hwcaps); if (ret) return ret; if (nor->addr_width) { } else if (info->addr_width) { nor->addr_width = info->addr_width; } else if (mtd->size > 0x1000000 ) { nor->addr_width = 4 ; if (JEDEC_MFR(info) == SNOR_MFR_SPANSION || info->flags & SPI_NOR_4B_OPCODES) spi_nor_set_4byte_opcodes(nor, info); else set_4byte(nor, info, 1 ); } else { nor->addr_width = 3 ; } if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { dev_err(dev, "address width is too large: %u\n" , nor->addr_width); return -EINVAL; } if (info->flags & SPI_S3AN) { ret = s3an_nor_scan(info, nor); if (ret) return ret; } dev_info(dev, "%s (%lld Kbytes)\n" , info->name, (long long )mtd->size >> 10 ); dev_dbg(dev, "mtd .name = %s, .size = 0x%llx (%lldMiB), " ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n" , mtd->name, (long long )mtd->size, (long long )(mtd->size >> 20 ), mtd->erasesize, mtd->erasesize / 1024 , mtd->numeraseregions); printk("spi_nor_scan end\n" ); if (mtd->numeraseregions) for (i = 0 ; i < mtd->numeraseregions; i++) dev_dbg(dev, "mtd.eraseregions[%d] = { .offset = 0x%llx, " ".erasesize = 0x%.8x (%uKiB), " ".numblocks = %d }\n" , i, (long long )mtd->eraseregions[i].offset, mtd->eraseregions[i].erasesize, mtd->eraseregions[i].erasesize / 1024 , mtd->eraseregions[i].numblocks); return 0 ; } EXPORT_SYMBOL_GPL(spi_nor_scan);
需要重点分析
几个重要的函数 m25p80_read_reg 1 2 3 4 5 6 7 8 9 10 11 12 static int m25p80_read_reg (struct spi_nor *nor, u8 code, u8 *val, int len) { struct m25p *flash = nor->priv; struct spi_device *spi = flash->spi; int ret; ret = spi_write_then_read(spi, &code, 1 , val, len); if (ret < 0 ) dev_err(&spi->dev, "error %d reading %x\n" , ret, code); return ret; }
m25p80_write_reg 1 2 3 4 5 6 7 8 9 10 11 static int m25p80_write_reg (struct spi_nor *nor, u8 opcode, u8 *buf, int len) { struct m25p *flash = nor->priv; struct spi_device *spi = flash->spi; flash->command[0 ] = opcode; if (buf) memcpy (&flash->command[1 ], buf, len); return spi_write(spi, flash->command, len + 1 ); }
m25p80_write 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 static ssize_t m25p80_write (struct spi_nor *nor, loff_t to, size_t len, const u_char *buf) { struct m25p *flash = nor->priv; struct spi_device *spi = flash->spi; unsigned int inst_nbits, addr_nbits, data_nbits, data_idx; struct spi_transfer t [3] = {}; struct spi_message m ; int cmd_sz = m25p_cmdsz(nor); ssize_t ret; inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto); addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto); data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto); spi_message_init(&m); if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) cmd_sz = 1 ; flash->command[0 ] = nor->program_opcode; m25p_addr2cmd(nor, to, flash->command); t[0 ].tx_buf = flash->command; t[0 ].tx_nbits = inst_nbits; t[0 ].len = cmd_sz; spi_message_add_tail(&t[0 ], &m); data_idx = 1 ; if (addr_nbits != inst_nbits) { t[0 ].len = 1 ; t[1 ].tx_buf = &flash->command[1 ]; t[1 ].tx_nbits = addr_nbits; t[1 ].len = cmd_sz - 1 ; spi_message_add_tail(&t[1 ], &m); data_idx = 2 ; } t[data_idx].tx_buf = buf; t[data_idx].tx_nbits = data_nbits; t[data_idx].len = len; spi_message_add_tail(&t[data_idx], &m); ret = spi_sync(spi, &m); if (ret) return ret; ret = m.actual_length - cmd_sz; if (ret < 0 ) return -EIO; return ret; }
m25p80_read 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 static ssize_t m25p80_read (struct spi_nor *nor, loff_t from, size_t len, u_char *buf) { struct m25p *flash = nor->priv; struct spi_device *spi = flash->spi; unsigned int inst_nbits, addr_nbits, data_nbits, data_idx; struct spi_transfer t [3]; struct spi_message m ; unsigned int dummy = nor->read_dummy; ssize_t ret; int cmd_sz; inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto); addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto); data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto); dummy = (dummy * addr_nbits) / 8 ; if (spi_flash_read_supported(spi)) { struct spi_flash_read_message msg ; memset (&msg, 0 , sizeof (msg)); msg.buf = buf; msg.from = from; msg.len = len; msg.read_opcode = nor->read_opcode; msg.addr_width = nor->addr_width; msg.dummy_bytes = dummy; msg.opcode_nbits = inst_nbits; msg.addr_nbits = addr_nbits; msg.data_nbits = data_nbits; ret = spi_flash_read(spi, &msg); if (ret < 0 ) return ret; return msg.retlen; } spi_message_init(&m); memset (t, 0 , sizeof (t)); flash->command[0 ] = nor->read_opcode; m25p_addr2cmd(nor, from, flash->command); t[0 ].tx_buf = flash->command; t[0 ].tx_nbits = inst_nbits; t[0 ].len = m25p_cmdsz(nor) + dummy; spi_message_add_tail(&t[0 ], &m); cmd_sz = t[0 ].len; memset (flash->command + cmd_sz - dummy, 0xff , dummy); data_idx = 1 ; if (addr_nbits != inst_nbits) { t[0 ].len = 1 ; t[1 ].tx_buf = &flash->command[1 ]; t[1 ].tx_nbits = addr_nbits; t[1 ].len = cmd_sz - 1 ; spi_message_add_tail(&t[1 ], &m); data_idx = 2 ; } t[data_idx].rx_buf = buf; t[data_idx].rx_nbits = data_nbits; t[data_idx].len = min3(len, spi_max_transfer_size(spi), spi_max_message_size(spi) - cmd_sz); spi_message_add_tail(&t[data_idx], &m); ret = spi_sync(spi, &m); if (ret) return ret; ret = m.actual_length - cmd_sz; if (ret < 0 ) return -EIO; return ret; }