为什么是spi32766.0
2018-02-08 23:40
281 查看
mtdparts中的mtd-id为什么常是spi32766.0?
嵌入式开发一个常见的任务是要调通新的设备,前期有许多工作,比如调通uboot,添加flash驱动支持,配置MTD分区,调通网络等等。其中MTD分区信息多通过cmdline的形式传递给内核,有两种途径,一是通过uboot参数传递,二是内核配置CONFIG_CMDLINE参数。在cmdline中,MTD分区信息有固定的格式,如下:
mtdparts=<mtddef>[;<mtddef] <mtddef> := <mtd-id>:<partdef>[,<partdef>] <partdef> := <size>[@<offset>][<name>][ro][lk] <mtd-id> := unique name used in mapping driver/device (mtd->name) <size> := standard linux memsize OR "-" to denote all remaining space size is automatically truncated at end of device if specified or truncated size is 0 the part is skipped <offset> := standard linux memsize if omitted the part will immediately follow the previous part or 0 if the first part <name> := '(' NAME ')'
举一个例子,mtdparts=spi32766.0:256K(uboot),2M(kernel),6M(rootfs).-(rootfsdata)
这其中mtd-id=spi32766.0,我接触过的多个项目均是采用这一个mtd-id,为什么是这个值?32766是怎么得来的?和flash芯片有关系吗?下面来分析。
1、32766=0x7FFE,起初觉得应该和flash型号有关系,但搜索flash驱动代码,没有发现两者有关联,并且使用不同flash芯片的设备均采用这一值,说明它与flash没有关系。
2、那么就分析代码,首先要知道内核中哪个变量代表了mtd-id,这里我不准备按照代码执行顺序来分析,而是按照我思考问题的方式来分析。既然mtdparts是cmdline参数,那我首先要看cmdline的解析函数。
static int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, struct mtd_part_parser_data *data) { ... const char *mtd_id = master->name;//mtd-id是由matser->name定义的 ... for (part = partitions; part; part = part->next) { if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) break; } //去匹配cmdline中的mtd-id ... }
上面代码可知内核中mtd-id由master->name来定义,那我们就从这个函数起往前跟踪。调用关系如下:
m25p_probe -->mtd_device_parse_register -->parse_mtd_partitions -->parse_cmdline_partitions
跟踪到源头m25p_probe,上面函数中的master为下面的flash->mtd
static int m25p_probe(struct spi_device *spi) { ... struct m25p *flash; struct spi_nor *nor; ... flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); ... nor->dev = &spi->dev;// spi设备device nor->mtd = &flash->mtd; // master源头 ... ret = spi_nor_scan(nor, spi_get_device_id(spi), mode);//这里对nor处理 ... return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, data ? data->parts : NULL, data ? data->nr_parts : 0); }
继续跟踪spi_nor_scan
int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id, enum read_mode mode) { ... struct device *dev = nor->dev; struct mtd_info *mtd = nor->mtd; ... mtd->name = dev_name(dev);//mtd-id是设备dev的name ... }
可以得出内核中mtd-id是spi设备名(spi->dev->name)
3、接下来我们去跟踪spi设备名是怎么得来的
查找spi设备驱动(platform_driver),一般是通过设备树dts信息去内核查找,跟踪platform_driver中相应的probe函数。这里的驱动函数就不列举了,不同的spi设备有不同的驱动,一般是由原厂商提供。驱动probe函数中一般的执行流程如下:
spi_alloc_master -->spi_register_master -->of_register_spi_devices -->spi_add_device -->device_add
1) spi_alloc_master
struct spi_master *spi_alloc_master(struct device *dev, unsigned size) { struct spi_master *master; ... master = kzalloc(size + sizeof(*master), GFP_KERNEL); ... master->bus_num = -1;//注意这里初始化为-1 master->num_chipselect = 1; master->dev.class = &spi_master_class; master->dev.parent = get_device(dev); spi_master_set_devdata(master, &master[1]); return master; }
2)spi_register_master
int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1); // 重点,dyn_bus_id初始化为0x7FFF struct device *dev = master->dev.parent; ... 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) { // 初始化时bus_num=-1 /* 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); //计算后bus_num=0x7FFE=32766 dynamic = 1; } ... /* 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);// master->dev->name="spi32766" status = device_add(&master->dev); if (status < 0) goto done; dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), dynamic ? " (dynamic)" : ""); ... /* Register devices from the device tree and ACPI */ of_register_spi_devices(master); acpi_register_spi_devices(master); done: return status; }
现在已经得到spi_master设备名为spi32766
3)of_register_spi_devices
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); // 这里会创建我们需要的spi设备 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 */ // 获取设备树里spi节点下的reg参数,赋值给spi->chip_select,代表片选ID,即第几片flash,0代表第一片flash 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;//一般为0,第一片flash ... } ... rc = spi_add_device(spi);//将spi设备(spi->dev)添加入linux device hierarchy,也可以说是devices bus中 ... }
4)spi_add_device
int spi_add_device(struct spi_device *spi) { ... spi_dev_set_name(spi);//确定spi->dev->name ... status = device_add(&spi->dev);//这是linux设备模型中一个很关键的函数,这里不详细讲,函数除了添加device到bus外,还会probe drivers for this device and attach device to driver. ... } static void spi_dev_set_name(struct spi_device *spi) { struct acpi_device *adev = ACPI_COMPANION(&spi->dev); if (adev) { dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev)); return; } //最终确定spi设备名为"spi32766.0",dev_name(&spi->master->dev)为"spi32766", spi->chip_select为0 dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev), spi->chip_select); }
至此,确定spi设备名spi32766.0,按代码流程分析,已经清楚为什么mtd-id是spi32766.0了。
相关文章推荐
- api和spi,oracle的jdbc驱动为什么不开源
- 为什么STM32中SPI的MISO引脚设置成复用推挽输出
- 为什么要有l两个Survivor区
- Java中为什么使用静态代码块
- 为什么要学习python
- JTAG/SPI/ISP/ICSP 接口电路
- 为什么会出现LNK2005"符号已定义"的链接错误?
- 为什么我以socket协议录制没有脚本出现
- UI控件为什么不能使用多线程
- 【郝斌数据结构自学笔记】53-56_一个函数为什么可以自己调用自己_递归必须满足三个条件_循环和递归的比较
- 为什么不能同时满足CAP原则
- Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化
- atitit.为什么笔记本跟个手机不能组装而pc可以
- 为什么要在服务层设计读写分离?
- 为什么使用Java泛型 以及 Java泛型使用方法
- 为什么到今天还要坚持写博客
- 为什么在12306买火车票要装根证书?
- IT人为什么快乐不起来
- 为什么微软选择了雅虎而未选择百度
- 为什么说Web开发和Vue.js是如此的有趣?