关于我在驱动orangepi-r1-plus-lts的i2c0接口时遇到的问题
背景
最近我在学习Linux驱动开发,在学完i2c驱动后,打算用手里的orangepi-r1-plus-lts,做一个i2c屏幕,来显示系统的状态信息。
条件
orangepi-r1-plus-lts引脚很少,但庆幸的是开发板有一个i2c引脚。这里我使用的是Linux-7.2.3的内核。

编写设备树
编写驱动前,需要添加设备树节点。这里我们使用的i2c引脚是GPIO2_D0和GPIO2_D1,对应的是rk3328的i2c0。因为我们在i2c0下添加如下节点信息:
&i2c0 {
status = "okay";
ssd1306: ssd1306@3c {
compatible = "gwen,ssd1306";
reg = <0x3c>;
};
};
编写测试驱动
static int oled_probe(struct i2c_client *client){
printk("oled_probe\n");
int ret = 0;
//获取设备号
ret = alloc_chrdev_region(&ssd1306_dev.devno,0,1,NAME);
if(ret < 0) goto err_alloc_devn;
//注册设备
cdev_init(&ssd1306_dev.cdev,&fops);
ret = cdev_add(&ssd1306_dev.cdev,ssd1306_dev.devno,1);
if(ret < 0) goto err_cdev_add;
//设置驱动类别
ssd1306_dev.class = class_create(NAME);
if(IS_ERR(ssd1306_dev.class)){
ret = -EFAULT;
goto err_class_create;
}
//创建设备节点
ssd1306_dev.device = device_create(ssd1306_dev.class,NULL,ssd1306_dev.devno,NULL,NAME);
if(IS_ERR(ssd1306_dev.device)){
ret = -EFAULT;
goto err_device_create;
}
ssd1306_dev.private_data = client;
return 0;
err_device_create:
class_destroy(ssd1306_dev.class);
err_class_create:
cdev_del(&ssd1306_dev.cdev);
err_cdev_add:
unregister_chrdev_region(ssd1306_dev.devno,1);
err_alloc_devn:
return ret;
}
static void oled_remove(struct i2c_client *client){
device_destroy(ssd1306_dev.class,ssd1306_dev.devno);
class_destroy(ssd1306_dev.class);
cdev_del(&ssd1306_dev.cdev);
unregister_chrdev_region(ssd1306_dev.devno,1);
printk("oled_remove\n");
}
//传统匹配模式
static struct i2c_device_id ssd1306_id[] = {
{
.name = "gwen,ssd1306",
.driver_data = 0
},
{}
};
//设备树匹配
static struct of_device_id ssd1306_of_match[] = {
{
.compatible = "gwen,ssd1306",
},
{}
};
static struct i2c_driver driver = {
.probe = oled_probe,
.remove = oled_remove,
.driver = {
.name = "gwen,ssd1306",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ssd1306_of_match)
},
.id_table = ssd1306_id
};
static int __init oled_init(void)
{
int ret;
ret = i2c_add_driver(&driver);
if (ret) {
pr_err("ssd1306: i2c_add_driver failed: %d\n", ret);
return ret;
}
pr_info("ssd1306: i2c driver registered\n");
return 0;
}
static void __exit oled_exit(void){
i2c_del_driver(&driver);
}
module_init(oled_init);
module_exit(oled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gwen");
这里省略头文件和fops的具体实现。
问题出现
编译后,加载模块。但是probe函数居然没有被调用。因此可确定各,驱动没有和设备树的设备节点匹配成功。后面调试时,我把ssd1306节点又添加到了rk3328的i2c1,i2c2已经i2c3,都是可以触发probe函数的。这是基本可以确定,测试驱动代码时没有问题的,问题应该是在设备树上的了。
解决问题
经过我和GPT的共同努力下,终于找到的问题所在。
在内核提供的rk3328.dtsi设备树下。GPIO2_D0和GPIOD2_D1这两个GPIO被复用为了eth1的状态指示灯控制引脚了。如下图


因此,如果我们要使用orangepi r1-plus-lts的引脚提供的i2c,就必须舍弃eth0网口的状态灯。这里我们屏蔽掉pinctrl-nams 和pinctls-0就可以正常加载驱动了。

最终效果
加载驱动时,成功调用probe函数,退出时也调用了remove函数


浙公网安备 33010602011771号