
前言
i2c控制器的驱动分析完之后,接下来来分析i2c设备驱动,我们以HYM8563驱动作为分析对象。
I2C设备驱动分析
内核:4.20
芯片:HYM8563 RTC
下面的代码分析主要都在注释中,会按照驱动中函数的执行顺序分析。
(1) 加载和卸载函数
static const struct i2c_device_id hym8563_id[] = {{ "hym8563", 0 },{},};MODULE_DEVICE_TABLE(i2c, hym8563_id);static const struct of_device_id hym8563_dt_idtable[] = {{ .compatible = "haoyu,hym8563" },{},};MODULE_DEVICE_TABLE(of, hym8563_dt_idtable);static struct i2c_driver hym8563_driver = {.driver = {.name = "rtc-hym8563",.pm = &hym8563_pm_ops,.of_match_table = hym8563_dt_idtable, //dt匹配表},.probe = hym8563_probe,.id_table = hym8563_id,// id表};// 封住了module_init()和module_exit()// 里面会调用i2c_register_driver(hym8563_driver)// 和i2c_del_driver(hym8563_driver)module_i2c_driver(hym8563_driver);
(2) probe()函数
static int hym8563_probe(struct i2c_client *client,const struct i2c_device_id *id){struct hym8563 *hym8563;int ret;//申请内存空间hym8563 = devm_kzalloc(&client->dev, sizeof(*hym8563), GFP_KERNEL);if (!hym8563)return -ENOMEM;//保存数据hym8563->client = client;i2c_set_clientdata(client, hym8563);//HYM8563初始化ret = hym8563_init_device(client);//申请中断if (client->irq > 0) {ret = devm_request_threaded_irq(&client->dev, client->irq,NULL, hym8563_irq,IRQF_TRIGGER_LOW | IRQF_ONESHOT,client->name, hym8563);}//检查一下模块是否正常运行ret = i2c_smbus_read_byte_data(client, HYM8563_SEC);if (ret < 0)return ret;//VL位用来标识模块是否正常工作hym8563->valid = !(ret & HYM8563_SEC_VL);dev_dbg(&client->dev, "rtc information is %s\n",hym8563->valid ? "valid" : "invalid");//注册RTC设备hym8563->rtc = devm_rtc_device_register(&client->dev, client->name,&hym8563_rtc_ops, THIS_MODULE);/* the hym8563 alarm only supports a minute accuracy */hym8563->rtc->uie_unsupported = 1;#ifdef CONFIG_COMMON_CLK//HYM8563可以作为时钟源hym8563_clkout_register_clk(hym8563);#endifreturn 0;}
上面删掉了一些判断和Log信息。
(3) HYM8563初始化
//查看datasheet对8563进行初始化,对寄存器进行设置static int hym8563_init_device(struct i2c_client *client){int ret;/* Clear stop flag if present *///向寄存器中写入值ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0);if (ret < 0)return ret;//读取寄存器的值ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);if (ret < 0)return ret;/* Disable alarm and timer interrupts */ret &= ~HYM8563_CTL2_AIE;ret &= ~HYM8563_CTL2_TIE;/* Clear any pending alarm and timer flags */if (ret & HYM8563_CTL2_AF)ret &= ~HYM8563_CTL2_AF;if (ret & HYM8563_CTL2_TF)ret &= ~HYM8563_CTL2_TF;ret &= ~HYM8563_CTL2_TI_TP;//将修改后的值写入寄存器return i2c_smbus_write_byte_data(client, HYM8563_CTL2, ret);}
(4) HYM8563操作函数
static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm){struct i2c_client *client = to_i2c_client(dev);struct hym8563 *hym8563 = i2c_get_clientdata(client);u8 buf[7];int ret;if (!hym8563->valid) {dev_warn(&client->dev, "no valid clock/calendar values available\n");return -EPERM;}//读取寄存器值, 连续读取7个寄存器ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf);//bcd数转成2进制tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK);tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK);tm->tm_hour = bcd2bin(buf[2] & HYM8563_HOUR_MASK);tm->tm_mday = bcd2bin(buf[3] & HYM8563_DAY_MASK);tm->tm_wday = bcd2bin(buf[4] & HYM8563_WEEKDAY_MASK); /* 0 = Sun */tm->tm_mon = bcd2bin(buf[5] & HYM8563_MONTH_MASK) - 1; /* 0 = Jan */tm->tm_year = bcd2bin(buf[6]) + 100;return 0;}static const struct rtc_class_ops hym8563_rtc_ops = {.read_time = hym8563_rtc_read_time,.set_time = hym8563_rtc_set_time,.alarm_irq_enable = hym8563_rtc_alarm_irq_enable,.read_alarm = hym8563_rtc_read_alarm,.set_alarm = hym8563_rtc_set_alarm,};
其他的读写函数都是去通过I2C去读取寄存器的值。

上面的调用关系图显示了设备与控制器之间的关系。
总结
大部分的I2C设备驱动框架都差不多,大家可以配合下面两篇文章一起看。这样更能理解。虽然分析代码是一种比较无聊的事情,但是可以通过分析来理解具体驱动框架和学习一些好的编码风格。

长按识别图中二维码关注
文章转载自嵌入式软件开发交流,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




