zynq-zc706.dts
&qspi { u-boot,dm-pre-reloc; status = "okay"; is-dual = <1>; num-cs = <1>; flash@0 { compatible = "n25q128a11";
compatible = "n25q128a11";
找不到对应的驱动
qspi控制器驱动的疑惑:
zynq-7000.dtsi
qspi: spi@e000d000 { clock-names = "ref_clk", "pclk"; clocks = <&clkc 10>, <&clkc 43>; compatible = "xlnx,zynq-qspi-1.0"; status = "disabled"; interrupt-parent = <&intc>; interrupts = <0 19 4>; reg = <0xe000d000 0x1000>; #address-cells = <1>; #size-cells = <0>; };
drivers/spi/spi-zynq-qspi.c qspi控制器驱动
830 static const struct of_device_id zynq_qspi_of_match[] = { 831 { .compatible = "xlnx,zynq-qspi-1.0", }, 832 { /* end of table */ } 833 };
这个路径比较奇怪,因为其他控制器不是放在spi目录下
比如cadence qspi控制器
compatible = "cdns,qspi-nor"
源码在drivers/mtd/spi-nor/cadence-quadspi.c
/linux-xxxx/drivers/mtd/spi-nor/spi-nor.c 是spi nor的封装层 可参考 https://blog.csdn.net/u011011827/article/details/95786541
颗粒的驱动在linux-xxxx/drivers/mtd/devices目录下
用的是m25p80.c
static const struct spi_device_id m25p_ids[] = { {"spi-nor"}, /* * Entries not used in DTs that should be safe to drop after replacing * them with "spi-nor" in platform data. */ {"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"}, /* * Entries that were used in DTs without "jedec,spi-nor" fallback and * should be kept for backward compatibility. */ {"at25df321a"}, {"at25df641"}, {"at26df081a"}, { }, }; MODULE_DEVICE_TABLE(spi, m25p_ids); static const struct of_device_id m25p_of_table[] = { /* * Generic compatibility for SPI NOR that can be identified by the * JEDEC READ ID opcode (0x9F). Use this, if possible. */ { .compatible = "jedec,spi-nor" }, {} }; MODULE_DEVICE_TABLE(of, m25p_of_table); static struct spi_mem_driver m25p80_driver = { .spidrv = { .driver = { .name = "m25p80", .of_match_table = m25p_of_table, }, .id_table = m25p_ids, }, .probe = m25p_probe, .remove = m25p_remove, .shutdown = m25p_shutdown, /* REVISIT: many of these chips have deep power-down modes, which * should clearly be entered on suspend() to minimize power use. * And also when they're otherwise idle... */ };
驱动和设备的匹配有四种方法,我们依次来看一下:
第 11~12行,第一种匹配方式, OF类型的匹配,也就是设备树采用的匹配方式,
of_driver_match_device函数定义在文件 include/linux/of_device.h中。 device_driver结构体 (表示
设备驱动 )中有个名为 of_match_table的成员变量,此成员变量保存着驱动的 compatible匹配表,
设备树中的每个设备节点的 compatible属性会和 of_match_table表中的所有成员比较,查看是
否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe函数
就会执行。
第 15~16行,第二种匹配方式, ACPI匹配方式。
第 19~20行,第三种匹配方式, id_table匹配,每个 platform_driver结构体有一个 id_table成员变量,顾名思义,保存了很多 id信 息。这些 id信息存放着这个 platformd驱动所 支持 的驱
动类型。
第 23行,第四种匹配方式,如果第三种匹配方式的 id_table不存在的话就直接比较驱动和
设备的 name字段,看看是不是相等,如果相等的话就匹配成功。
对于支持设备树的
Linux版本号,一般设备驱动为了兼容性都支持设备树和无设备树两种
匹配方式。也就是第一种匹配方式一般都会存在,第三种和第四种只要存在一种就可以,一般
用的最多的还是第四种,也就是直接比较驱动和设备的 name字段,毕竟这种方式最简单了。
以上是linux中的驱动,对于u-boot中
drivers/spi/zynq_qspi.c中
864 static const struct udevice_id zynq_qspi_ids[] = { 865 { .compatible = "xlnx,zynq-qspi-1.0" }, 866 { } 867 };
869 U_BOOT_DRIVER(zynq_qspi) = { 870 .name = "zynq_qspi", 871 .id = UCLASS_SPI, 872 .of_match = zynq_qspi_ids, 873 .ops = &zynq_qspi_ops, 874 .ofdata_to_platdata = zynq_qspi_ofdata_to_platdata, 875 .platdata_auto_alloc_size = sizeof(struct zynq_qspi_platdata), 876 .priv_auto_alloc_size = sizeof(struct zynq_qspi_priv), 877 .probe = zynq_qspi_probe, 878 .child_pre_probe = zynq_qspi_child_pre_probe, 879 };
flash器件
compatible = "n25q128a11";
sf probe
do_spi_flash (cmd/sf.c)
do_spi_flash_probe (cmd/sf.c)
/* Remove the old device, otherwise probe will just be a nop */
spi_find_bus_and_cs (spi-uclass.c)
uclass_find_device_by_seq (drivers/core/uclasee.c)//寻找spi总线设备(SPI控制器) 没有probe
//ret = uclass_find_device_by_seq(UCLASS_SPI, busnum, false, &bus);
spi_find_chip_select (drivers/spi/spi-uclass.c)//在SPI总线设备下找到FLASH设备
spi_flash_probe_bus_cs(driver/mtd/spi/sf-uclass.c)
spi_get_bus_and_cs (drivers/spi/spi-uclass.c)
/*ret = spi_get_bus_and_cs(busnum, cs, max_hz, spi_mode,
"spi_flash_std", str, &bus, &slave); 这里直接指定了driver name*/
uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus); //UCLASS_SPI cadence_spi_probe
spi_find_chip_select(bus, cs, &dev);
if (ret == -ENODEV && drv_name)
ret = device_bind_driver(bus, drv_name, dev_name, &dev);
device_probe (qspi flash颗粒 激活)
那么flash器件的dts参数如何获得呢?
cadence_spi_ofdata_to_platdata
static int cadence_spi_ofdata_to_platdata(struct udevice *bus) { struct cadence_spi_platdata *plat = bus->platdata; const void *blob = gd->fdt_blob; int node = dev_of_offset(bus); int subnode; plat->regbase = (void *)devfdt_get_addr_index(bus, 0); plat->ahbbase = (void *)devfdt_get_addr_index(bus, 1); plat->is_decoded_cs = fdtdec_get_bool(blob, node, "cdns,is-decoded-cs"); plat->fifo_depth = fdtdec_get_uint(blob, node, "cdns,fifo-depth", 128); plat->fifo_width = fdtdec_get_uint(blob, node, "cdns,fifo-width", 4); plat->trigger_address = fdtdec_get_uint(blob, node, "cdns,trigger-address", 0); /* All other paramters are embedded in the child node */
//以下是flash颗粒dts的解析
subnode = fdt_first_subnode(blob, node); if (subnode < 0) { printf("Error: subnode with SPI flash config missing! "); return -ENODEV; } /* Use 500 KHz as a suitable default */ plat->max_hz = fdtdec_get_uint(blob, subnode, "spi-max-frequency", 500000); /* Read other parameters from DT */ plat->page_size = fdtdec_get_uint(blob, subnode, "page-size", 256); plat->block_size = fdtdec_get_uint(blob, subnode, "block-size", 16); plat->tshsl_ns = fdtdec_get_uint(blob, subnode, "cdns,tshsl-ns", 200); plat->tsd2d_ns = fdtdec_get_uint(blob, subnode, "cdns,tsd2d-ns", 255); plat->tchsh_ns = fdtdec_get_uint(blob, subnode, "cdns,tchsh-ns", 20); plat->tslch_ns = fdtdec_get_uint(blob, subnode, "cdns,tslch-ns", 20); debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d ", __func__, plat->regbase, plat->ahbbase, plat->max_hz, plat->page_size); return 0; }