首页 > 代码库 > 新内核 中关于spi nor的注册

新内核 中关于spi nor的注册

3.X的内核,对于platform_device的注册和platform data,全都放在dts文件中指定,对于spi nor来说,它的整个驱动
包括了master  driver,master device,spi nor driver 和spi nor device.


对于spi master的驱动是在driver/spi/下有相关的文件来完成,而spi master device是在dts中来指定,

对于zynq板子来说,我们在driver/spi/可以找到相关的spi master的驱动文件,在这里它就是一个platfrom driver.
对于device 是在dts文件中一指定的:
 ps7_qspi_0: ps7-qspi@e000d000 {
   clock-names = "ref_clk", "aper_clk";
   clocks = <&clkc 10>, <&clkc 43>;
   compatible = "xlnx,zynq-qspi-1.0";
   interrupt-parent = <&ps7_scugic_0>;
   interrupts = <0 19 4>;
   is-dual = <0>;
   num-chip-select = <1>;
   reg = <0xe000d000 0x1000>;
   xlnx,fb-clk = <0x1>;
   xlnx,qspi-mode = <0x0>;
   #address-cells = <1>;
   #size-cells = <0>;
   flash@0 {
    compatible = "s25fl256s1";
    reg = <0x0>;
    spi-max-frequency = <50000000>;
    #address-cells = <1>;
    #size-cells = <1>;
    partition@qspi-fsbl-uboot {
     label = "qspi-fsbl-uboot";
     reg = <0x0 0x100000>;
    };
    partition@qspi-linux {
     label = "qspi-linux";
     reg = <0x100000 0x500000>;
    };
    partition@qspi-device-tree {
     label = "qspi-device-tree";
     reg = <0x600000 0x20000>;
    };
    partition@qspi-rootfs {
     label = "qspi-rootfs";
     reg = <0x620000 0x5E0000>;
    };
    partition@qspi-bitstream {
     label = "qspi-bitstream";
     reg = <0xC00000 0x400000>;
    };
   };
  } ;

在linux启动后,会解析这个dts文件,当进行spi master driver注册时,会进行match,之后就调用spi master driver下的probe函数,
probe函数中会调用spi_register_master函数.我们知道有spi master只是有总线的驱动了,那一定还有spi相关的device,没错,
spi device就是在spi_register_master中来完成的,我们也可以通过上面的spi master 的dts代码看到,在这个spi bus 下只挂了一个spi nor flash.在spi_register_master中会对master下所挂的spi nor device进行add:


int spi_register_master(struct spi_master *master)
{
 static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
 struct device *dev = master->dev.parent;
 struct boardinfo *bi;
 int status = -ENODEV;
 int dynamic = 0;
 if (!dev)
  return -ENODEV;
 status = of_spi_register_master(master);
 if (status)
  return status;
 /* even if it‘s just one always-selected device, there must
  * be at least one chipselect
  */
 if (master->num_chipselect == 0)
  return -EINVAL;
 if ((master->bus_num < 0) && master->dev.of_node)
  master->bus_num = of_alias_get_id(master->dev.of_node, "spi");
 /* convention: dynamically assigned bus IDs count down from the max */
 if (master->bus_num < 0) {
  /* FIXME switch to an IDR based scheme, something like
   * I2C now uses, so we can‘t run out of "dynamic" IDs
   */
  master->bus_num = atomic_dec_return(&dyn_bus_id);
  dynamic = 1;
 }
 spin_lock_init(&master->bus_lock_spinlock);
 mutex_init(&master->bus_lock_mutex);
 master->bus_lock_flag = 0;
 init_completion(&master->xfer_completion);
 if (!master->max_dma_len)
  master->max_dma_len = INT_MAX;
 /* register the device, then userspace will see it.
  * registration fails if the bus ID is in use.
  */
 dev_set_name(&master->dev, "spi%u", master->bus_num);
 status = device_add(&master->dev);
 if (status < 0)
  goto done;
 dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
   dynamic ? " (dynamic)" : "");
 /* If we‘re using a queued driver, start the queue */
 if (master->transfer)
  dev_info(dev, "master is unqueued, this is deprecated\n");
 else {
  status = spi_master_initialize_queue(master);
  if (status) {
   device_del(&master->dev);
   goto done;
  }
 }
 mutex_lock(&board_lock);
 list_add_tail(&master->list, &spi_master_list);
 list_for_each_entry(bi, &board_list, list)
  spi_match_master_to_boardinfo(master, &bi->board_info);
 mutex_unlock(&board_lock);
 /* Register devices from the device tree and ACPI */
 of_register_spi_devices(master); //注册这个master下所挂载的spi device.
 acpi_register_spi_devices(master);
done:
 return status;
}

static void of_register_spi_devices(struct spi_master *master)
{
 struct spi_device *spi;
 struct device_node *nc;
 int rc;
 u32 value;
 if (!master->dev.of_node)
  return;
 for_each_available_child_of_node(master->dev.of_node, nc) {
  /* Alloc an spi_device */
  spi = spi_alloc_device(master);
  if (!spi) {
   dev_err(&master->dev, "spi_device alloc error for %s\n",
    nc->full_name);
   spi_dev_put(spi);
   continue;
  }
  /* Select device driver */
  if (of_modalias_node(nc, spi->modalias,
         sizeof(spi->modalias)) < 0) {
   dev_err(&master->dev, "cannot find modalias for %s\n",
    nc->full_name);
   spi_dev_put(spi);
   continue;
  }
  /* Device address */
  rc = of_property_read_u32(nc, "reg", &value);
  if (rc) {
   dev_err(&master->dev, "%s has no valid ‘reg‘ property (%d)\n",
    nc->full_name, rc);
   spi_dev_put(spi);
   continue;
  }
  spi->chip_select = value;
  /* Mode (clock phase/polarity/etc.) */
  if (of_find_property(nc, "spi-cpha", NULL))
   spi->mode |= SPI_CPHA;
  if (of_find_property(nc, "spi-cpol", NULL))
   spi->mode |= SPI_CPOL;
  if (of_find_property(nc, "spi-cs-high", NULL))
   spi->mode |= SPI_CS_HIGH;
  if (of_find_property(nc, "spi-3wire", NULL))
   spi->mode |= SPI_3WIRE;
  if (of_find_property(nc, "spi-lsb-first", NULL))
   spi->mode |= SPI_LSB_FIRST;
  /* Device DUAL/QUAD mode */
  if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) { //从dts中获取相关的写模式,
   switch (value) {
   case 1:
    break;
   case 2:
    spi->mode |= SPI_TX_DUAL;
    break;
   case 4:
    spi->mode |= SPI_TX_QUAD;
    break;
   default:
    dev_warn(&master->dev,
      "spi-tx-bus-width %d not supported\n",
      value);
    break;
   }
  }
  if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) { //获取read的mode
   switch (value) {
   case 1:
    break;
   case 2:
    spi->mode |= SPI_RX_DUAL;
    break;
   case 4:
    spi->mode |= SPI_RX_QUAD;
    break;
   default:
    dev_warn(&master->dev,
      "spi-rx-bus-width %d not supported\n",
      value);
    break;
   }
  }
  /* Device speed */
  rc = of_property_read_u32(nc, "spi-max-frequency", &value);
  if (rc) {
   dev_err(&master->dev, "%s has no valid ‘spi-max-frequency‘ property (%d)\n",
    nc->full_name, rc);
   spi_dev_put(spi);
   continue;
  }
  spi->max_speed_hz = value;
  /* IRQ */
  spi->irq = irq_of_parse_and_map(nc, 0);
  /* Store a pointer to the node in the device structure */
  of_node_get(nc);
  spi->dev.of_node = nc;
  /* Register the new device */
  request_module("%s%s", SPI_MODULE_PREFIX, spi->modalias); //加载设备对应的驱动文件
  rc = spi_add_device(spi); //增加这个spi device
  if (rc) {
   dev_err(&master->dev, "spi_device register error %s\n",
    nc->full_name);
   spi_dev_put(spi);
  }
 }
}


我们可以看到在这个函数中完成了相关spi device的增加和相关属性的指定.
但在这些代码中我们没有找到相关partition的指定,因为partition在spi nor driver的probe中会从spi device中的platfrom_data中直接获取.????如下,也有可能是在解析dts时就已经对device中的platform_data作了指定.可以看最后面的备注.




static int m25p_probe(struct spi_device *spi)
{
 struct mtd_part_parser_data ppdata;
 struct flash_platform_data *data;
 struct m25p *flash;
 struct spi_nor *nor;
 enum read_mode mode = SPI_NOR_NORMAL;
 int ret;
 flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
 if (!flash)
  return -ENOMEM;
 nor = &flash->spi_nor;
 /* install the hooks */
 nor->read = m25p80_read;
 nor->write = m25p80_write;
 nor->erase = m25p80_erase;
 nor->write_reg = m25p80_write_reg;
 nor->read_reg = m25p80_read_reg;
 nor->dev = &spi->dev;
 nor->mtd = &flash->mtd;
 nor->priv = flash;
 spi_set_drvdata(spi, flash);
 flash->mtd.priv = nor;
 flash->spi = spi;
 if (spi->mode & SPI_RX_QUAD)
  mode = SPI_NOR_QUAD;
 else if (spi->mode & SPI_RX_DUAL)
  mode = SPI_NOR_DUAL;
 ret = spi_nor_scan(nor, spi_get_device_id(spi), mode);
 if (ret)
  return ret;
 data = http://www.mamicode.com/dev_get_platdata(&spi->dev); //获取device platform_data
 ppdata.of_node = spi->dev.of_node;
 return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
   data ? data->parts : NULL,
   data ? data->nr_parts : 0);
}
注:

系统平台上挂载了很多总线,如i2c、spi、uart等等,每一个总线分别被描述为一个节点。Linux在启动后,到C入口时,会执行以下操作,加载系统平台上的总线和设备:

start_kernel() --> setup_arch() --> unflatten_device_tree() .

在执行完unflatten_device_tree()后,DTS节点信息被解析出来,保存到of_allnodes链表中,of_allnodes会在后面被用到。

随后,当系统启动到board文件时,会调用.init_machine,高通zynq平台对应的是zynq_init_machine()。接着调用of_platform_populate(....)接口,加载平台总线和平台设备。至此,系统平台上的所有已配置的总线和设备将被注册到系统中。


of_platform_populate会调用of_platform_bus_create函数,在这里面会地我上面所提到platform_data进行指定:of_platform_device_create_pdata.

新内核 中关于spi nor的注册