zc706 flash compatible引发的思考

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; }

原文地址:https://www.cnblogs.com/idyllcheung/p/14017283.html