嵌入式开发一个常见的任务是要调通新的设备,前期有许多工作,比如调通uboot,添加flash驱动支持,配置MTD分区,调通网络等等。其中MTD分区信息多通过cmdline的形式传递给内核,有两种途径,一是通过uboot参数传递,二是内核配置CONFIG_CMDLINE参数。
在cmdline中,MTD分区信息有固定的格式,如下:
mtdparts=[; := id>:[,]
:= [@<offset>][<name>][ro][lk]
id> := unique name used in mapping driver/device (mtd->name)
:= 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了。