这两天被设备文件快搞疯了,也怪自己学东西一知半解吧,弄了几天总算能把设备注册理清楚一点点了。就以spi子设备的注册为例总结一下,免得自己忘记。
首先以注册一个spidev的设备为例:
- static struct spi_board_info imx5_spi_printer_device[] __initdata =
- {
- {
- .modalias = "spidev",
- .max_speed_hz = 8000000,
- .bus_num = 1,
- .chip_select = 1,
- .mode = SPI_MODE_0,
- },
- };
spi_register_board_info(imx5_spi_printer_device,ARRAY_SIZE(imx5_spi_printer_device));
在mx5_loco.c文件中添加上面结构体spi_board_info,modalias必须指定已有的一个驱动,至于bus_num和chip_select,如果你不知道bus_num是多少,可以在你的父驱动中打印出来,这里的bus_num一定要和父类的bus_num一致,否则是无法生成设备文件的。如果spi一直没有时钟信号,很有可能是bus_num不对。
这样系统起来之后就会在/dev目录下出现一个名为spidev1.1的设备文件,读写这个文件就可以实现spi的操作
还有下面这种情况:
- static struct spi_board_info prt_spi_device[] __initdata = {
- {
- .modalias = "HotPRT",
- .max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */
- .bus_num = 1,
- .chip_select = 1,
- // .mode = SPI_MODE_0,
- .platform_data = 0,
- },
- };
spi_register_board_info(prt_spi_device, ARRAY_SIZE(prt_spi_device));
我自己实现了一个spi的驱动,然后需要创建一个设备文件,设备文件的创建是在probe中完成。
- static struct spi_driver prt_driver = {
- .driver = {
- .name = "HotPRT",
- .bus = &spi_bus_type,
- .owner = THIS_MODULE,
- },
- .probe = prt_probe,
- .remove = __devexit_p(prt_remove),
- };
- spi_register_driver(&prt_driver);
但是我开始一直触发不了probe,于是找啊找,总算知道probe的调用过程了,如下:- int spi_register_driver(struct spi_driver *sdrv)
- {
- sdrv->driver.bus = &spi_bus_type;
- if (sdrv->probe)
- sdrv->driver.probe = spi_drv_probe;
- if (sdrv->remove)
- sdrv->driver.remove = spi_drv_remove;
- if (sdrv->shutdown)
- sdrv->driver.shutdown = spi_drv_shutdown;
- return driver_register(&sdrv->driver);
- }
然后调用driver_register
- <pre name="code" class="cpp">int driver_register(struct device_driver *drv)
- {
- int ret;
- struct device_driver *other;
-
- BUG_ON(!drv->bus->p);
-
- if ((drv->bus->probe && drv->probe) ||
- (drv->bus->remove && drv->remove) ||
- (drv->bus->shutdown && drv->shutdown))
- printk(KERN_WARNING "Driver '%s' needs updating - please use "
- "bus_type methods\n", drv->name);
-
- other = driver_find(drv->name, drv->bus);
- if (other) {
- put_driver(other);
- printk(KERN_ERR "Error: Driver '%s' is already registered, "
- "aborting...\n", drv->name);
- return -EBUSY;
- }
-
- ret = bus_add_driver(drv);
- if (ret)
- return ret;
- ret = driver_add_groups(drv, drv->groups);
- if (ret)
- bus_remove_driver(drv);
- return ret;
- }
直接看bus_add_driver
- klist_init(&priv->klist_devices, NULL, NULL);
- priv->driver = drv;
- drv->p = priv;
- priv->kobj.kset = bus->p->drivers_kset;
- error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
- "%s", drv->name);
- if (error)
- goto out_unregister;
-
- if (drv->bus->p->drivers_autoprobe) {
- error = driver_attach(drv);
- if (error)
- goto out_unregister;
- }
- klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
- module_add_driver(drv->owner, drv);
这里只截取一部分,最后调用的是driver_attach
- int driver_attach(struct device_driver * drv)
- {
- return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
- }
真正起作用的是__driver_attach:
- static int __driver_attach(struct device * dev, void * data)
- {
- 。。。
- if (!dev->driver)
- driver_probe_device(drv, dev);
- 。。。
- }
- int driver_probe_device(struct device_driver * drv, struct device * dev)
- {
- 。。。
- //1.先是判断bus是否match:
- if (drv->bus->match && !drv->bus->match(dev, drv))
- goto done;
- //2.再具体执行probe:
- ret = really_probe(dev, drv);
- 。。。
- }
really_probe才是我们要找的函数:
- static int really_probe(struct device *dev, struct device_driver *drv)
- {
- 。。。
- //1.先是调用的驱动所属总线的probe函数:
- if (dev->bus->probe) {
- ret = dev->bus->probe(dev);
- if (ret)
- goto probe_failed;
-
- } else if (drv->probe) {
- //2.再调用你的驱动中的probe函数:
- ret = drv->probe(dev);
- if (ret)
- goto probe_failed;
- }
- 。。。
- }
其中,drv->probe(dev),才是真正调用你的驱动实现的具体的probe函数。至此probe函数被调用。
在板文件中添加spi_board_info,并在板文件的init函数中调用spi_register_board_info(
prt_spi_device<span style="font-family: NSimSum; line-height: 1.5; ">,ARRAY_SIZE(</span><span style="font-family: NSimSum; ">prt_spi_device</span><span style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; line-height: 1.5; "><span style="line-height: 1.5; font-family: NSimSum; ">))</span></span><span style="font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; line-height: 1.5; ">;</span>
//注册spi_board_info。这个代码会把spi_board_info注册到链表board_list上。spi_device封装了一个spi_master结构体,事实上spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,找到挂接在它上面的spi设备,然后创建并注册spi_device。
另外有关spi片选引脚的设置:1、直接将gpio配置成spi片选功能引脚。 2、将gpio配置成片选引脚,这个时候就需要设置结构体
- static void mx53_loco_gpio_spi_chipselect_active(int cspi_mode, int status,
- int chipselect)
- {
- switch (cspi_mode) {
- case 1: //ESPI1,bus_num 1
- switch (chipselect) {
- case 0x1://SS0 chipselect = .chip_select + 1
- {
- iomux_v3_cfg_t cspi_ss0 = MX53_PAD_CSI0_DAT7__ECSPI1_SS0;
- iomux_v3_cfg_t cspi_ss2 = MX53_PAD_KEY_ROW2__GPIO4_11;//SS2
-
-
- mxc_iomux_v3_setup_pad(cspi_ss0);
- mxc_iomux_v3_setup_pad(cspi_ss2);
-
-
- gpio_request(ECSPI1_CS2, "ecspi-cs2");
- gpio_direction_input(ECSPI1_CS2);
- }
- break;
- default:
- break;
- }
- case 2://ESPI2,bus_num 2
- switch (chipselect) {
- case 0x2://SS0
- {
- gpio_request(ECSPI2_SS1, "ecspi-cs1");
- gpio_direction_output(ECSPI2_SS1, 1);
- }
- break;
- case 0x3://SS1
- {
- gpio_request(ECSPI2_SS1, "ecspi-cs1");
- gpio_direction_output(ECSPI2_SS1, 0);
- }
- break;
- default:
- break;
- }
- default:
- break;
- }
- }
-
- static void mx53_loco_gpio_spi_chipselect_inactive(int cspi_mode, int status,
- int chipselect)
- {
- switch (cspi_mode) {
- case 1:
- switch (chipselect) {
- case 0x1:
- gpio_free(ECSPI1_CS2);
- break;
- default:
- break;
- }
- case 2:
- switch (chipselect) {
- case 0x2:
- {
- gpio_request(ECSPI2_SS1, "ecspi-cs1");
- gpio_direction_output(ECSPI2_SS1, 0);
- }
- break;
- case 0x3:
- {
- gpio_request(ECSPI2_SS1, "ecspi-cs1");
- gpio_direction_output(ECSPI2_SS1, 1);
- }
- break;
- default:
- break;
- }
- default:
- break;
- }
- }
-
- static struct mxc_spi_master mxcspi_data = {
-
- .maxchipselect = 4,
- .spi_version = 23,
- .chipselect_active = mx53_loco_gpio_spi_chipselect_active,
- .chipselect_inactive = mx53_loco_gpio_spi_chipselect_inactive,
- };
这样设置后就不再需要手动设置片选的状态了。
设备文件的生成:
1、用mknod手动生成
通过cat /proc/devices命令,可以看到主设备的编号,例如spi是153 ,如果想生成一个spi的子设备可以用 mknod /dev/spidev -c 153 1
mknod 设备名 设备类型 主设备号 子设备号
2、在驱动中就加入创建设备文件的代码:
- struct device *devi;
- prt_class = class_create(THIS_MODULE, PRT_DEV_NAME);
- if(IS_ERR(prt_class))
- PTR_ERR(prt_class);
-
- devi = device_create(prt_class,NULL,MKDEV(PRT_DEV_MAJOR, 1), NULL, PRT_DEV_NAME);
- if(IS_ERR(devi))
- PTR_ERR(devi);
首先class_create用它来创建一个类,这个类存放于sysfs下面,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。
本文深入解析SPI设备在Linux内核中的注册流程,包括spi_board_info结构体的使用,设备文件的生成方式,以及如何正确配置片选引脚。同时,详细介绍了驱动探针函数的调用机制,为理解SPI设备驱动提供了清晰的路径。
5223

被折叠的 条评论
为什么被折叠?



