Linux驱动之I2C设备驱动

Posted 嵌入式软件开发交流

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动之I2C设备驱动相关的知识,希望对你有一定的参考价值。


内核:4.20

芯片:HYM8563 RTC

下面的代码分析主要都在注释中,会按照驱动中函数的执行顺序分析。

一、加载和卸载函数

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);

二、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);
#endif

return 0;
}

上面删掉了一些判断和Log信息。

三、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);
}

四、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去读取寄存器的值。

Linux驱动之I2C设备驱动_嵌入式

上面的调用关系图显示了设备与控制器之间的关系。

Linux驱动之I2C设备驱动_linux_02

以上是关于Linux驱动之I2C设备驱动的主要内容,如果未能解决你的问题,请参考以下文章

Linux驱动之I2C设备驱动

linux驱动之i2c子系统mpu6050设备驱动

linux驱动之i2c学习

linux驱动之i2c总线驱动调用分析基于linux4.4

Linux驱动开发19-I2C子系统之客户驱动分析与移植

Linux——Linux驱动之玩转I2C(中)自己实现一个I2C总线client设备(Linux下I2C驱动框架非设备树下的client设备树下添加I2C设备节点)