platform驱动框架分为总线、设备和驱动,驱动程序开发不需要管理platform驱动框架下的总线,只需关注设备和驱动的实现。而在有设备树的Linux内核下,设备的描述被放到了设备树中,我们不编写platform_device,只需编写platform_driver。
设备树创建设备节点描述设备信息,主要设置compatible属性值,用来匹配驱动。
gpioled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-led";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
compatible属性值要与platform_driver结构体中的struct device_driver driver下的of_match_table属性表中的值一致。
static const struct of_device_id leds_of_match[] = {
{ .compatible = "atkalpha-led" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, leds_of_match);
static struct platform_driver leds_platform_driver = {
.driver = {
.name = "imx6ul-led",
.of_match_table = leds_of_match,
},
.probe = leds_porbe,
.remove = leds_remove,
};
MODULE_DEVICE_TABLE声明leds_of_match这个设备匹配表。
platform_driver编写
例如驱动一个led,首先在leddev设备结构体增加LED设备节点node和LED灯GPIO标号led0,它们将分别在设备树文件中得到gpioled节点和led-gpio属性。
struct leddev_dev {
dev_t devid;
...
struct device_node *node;
int led0;
};
设备树文件已声明了gpio资源,led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>,可调用基于整数编号的Legacy API函数gpio_set_value()来控制gpio电平。
void led0_switch(u8 sta)
{
if (sta == LEDON )
gpio_set_value(leddev.led0, 0);
else if (sta == LEDOFF)
gpio_set_value(leddev.led0, 1);
}
由于是在非设备树的驱动程序上修改,而设备树文件已经提供了硬件资源的信息,所以led_probe函数删除platform_get_resource()获取资源的代码,删除配置gpio代码 ,增加访问设备树获取节点属性的代码。
static int led_probe(struct platform_device *dev)
{
...
leddev.node = of_find_node_by_path("/gpioled");
if (leddev.node == NULL) {
printk("gpioled node nost find!\r\n");
return -EINVAL;
}
leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0);
if (leddev.led0 < 0) {
printk("can't get led-gpio\r\n");
return -EINVAL;
}
gpio_request(leddev.led0, "led0");
gpio_direction_output(leddev.led0, 1);
return 0;
}
leddev.node和leddev.led0分别从设备树文件里读取到,"gpioled","led-gpio"分别是设备树文件中的节点和属性。
在结构体platform_driver中增加.of_match_table = led_of_match
static const struct of_device_id led_of_match[] = {
{ .compatible = "atkalpha-led" },
{ /* Sentinel */ }
};
static struct platform_driver led_driver = {
.driver = {
.name = "imx6ul-led",
.of_match_table = led_of_match,
},
.probe = led_probe,
.remove = led_remove,
};
.compatible 的属性值"atkalpha-gpioled"一定要和dts文件中的compatible一致。
设置这个 platform 驱动的name为"imx6ul-led",因此,当驱动加载成功以后就会在/sys/bus/platform/drivers/目录下存在一个名为"imx6u-led"的文件。
ledderiver驱动程序写好后,运行后有如下错误:
/lib/modules/4.1.15 # depmod
/lib/modules/4.1.15 # modprobe leddriver.ko
led driver and device has matched!
gpioled node not find!
imx6ul-led: probe of gpioled failed with error -22
经过排查,是下面的代码中了一个"/"。
错误代码:
leddev.node = of_find_node_by_path("gpioled");
应该修改成:
leddev.node = of_find_node_by_path("/gpioled");
重新编译运行,无报错,功能正常。
参考资料
-
I.MX6U 嵌入式 Linux 驱动开发指南 V2.0.1 正点原子
-
Linux设备驱动开发 详解:基于最新的Linux 4.0内核