您的位置:首页 > 运维架构 > Linux

为什么是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了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux设备驱动