首先我们需要在dts文件中增加i2c控制器(adapter)的device定义。
am57xx-evm-reva3.dtb中关于i2c adapter在\arch\arm\boot\dts\dra7.dtsi
文件中定义:
/ {
#address-cells = <2>;
#size-cells = <2>;
compatible = "ti,dra7xx";
interrupt-parent = <&crossbar_mpu>;
...
ocp {
compatible = "ti,dra7-l3-noc", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x0 0x0 0xc0000000>;
ti,hwmods = "l3_main_1", "l3_main_2";
reg = <0x0 0x44000000 0x0 0x1000000>,
<0x0 0x45000000 0x0 0x1000>;
interrupts-extended = <&crossbar_mpu GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
<&wakeupgen GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
...
i2c1: i2c@48070000 {
compatible = "ti,omap4-i2c";
reg = <0x48070000 0x100>;
interrupts = ;
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "i2c1";
status = "disabled";
};
i2c2: i2c@48072000 {
compatible = "ti,omap4-i2c";
reg = <0x48072000 0x100>;
interrupts = ;
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "i2c2";
status = "disabled";
};
i2c3: i2c@48060000 {
compatible = "ti,omap4-i2c";
reg = <0x48060000 0x100>;
interrupts = ;
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "i2c3";
status = "disabled";
};
i2c4: i2c@4807a000 {
compatible = "ti,omap4-i2c";
reg = <0x4807a000 0x100>;
interrupts = ;
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "i2c4";
status = "disabled";
};
i2c5: i2c@4807c000 {
compatible = "ti,omap4-i2c";
reg = <0x4807c000 0x100>;
interrupts = ;
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "i2c5";
status = "disabled";
};
...
}
}
一共5路i2c,对应AM5728芯片手册中的定义:
i2c adapter在dts中的定义路径为/ocp/i2c1
,因为/ocp
定义为"simple-bus",所以它的子节点i2c1
在系统初始化时也会自动创建为platform device。调用路径为:
start_kernel() -> ... ->do_initcalls() -> ... ->of_platform_populate() -> of_platform_bus_create() -> of_platform_device_create_pdata()
因为i2c adapter在dts中的定义会被自动创建为platform device,所以i2c adapter的driver必须注册成platform driver。
drivers\i2c\busses\i2c-omap.c:
static struct platform_driver omap_i2c_driver = {
.probe = omap_i2c_probe,
.remove = omap_i2c_remove,
.driver = {
.name = "omap_i2c",
.pm = OMAP_I2C_PM_OPS,
.of_match_table = of_match_ptr(omap_i2c_of_match),
},
};
static const struct of_device_id omap_i2c_of_match[] = {
{
.compatible = "ti,omap4-i2c",
.data = &omap4_pdata,
},
{
.compatible = "ti,omap3-i2c",
.data = &omap3_pdata,
},
{
.compatible = "ti,omap2430-i2c",
.data = &omap2430_pdata,
},
{
.compatible = "ti,omap2420-i2c",
.data = &omap2420_pdata,
},
{ },
};
driver和device因为.compatible = "ti,omap4-i2c"
而适配上。
i2c adapter完成最重要的两件事:
1.初始化i2c adapter,并调用i2c_register_adapter()注册成i2c_adapter device;
2.遍历i2c adapter(i2c 控制器)下挂的i2c设备,调用i2c_new_device()把设备注册成i2c_client device;
static int
omap_i2c_probe(struct platform_device *pdev)
{
struct omap_i2c_dev *omap;
struct i2c_adapter *adap;
struct resource *mem;
const struct omap_i2c_bus_platform_data *pdata =
dev_get_platdata(&pdev->dev);
struct device_node *node = pdev->dev.of_node;
const struct of_device_id *match;
int irq;
int r;
u32 rev;
u16 minor, major;
/* (1) 获取i2c adapter的中断号,在dts中定义的"interrupts"属性 */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
return irq;
}
omap = devm_kzalloc(&pdev->dev, sizeof(struct omap_i2c_dev), GFP_KERNEL);
if (!omap)
return -ENOMEM;
/* (2) 获取i2c adapter的寄存器基地址,在dts中定义的"reg"属性 */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
omap->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(omap->base))
return PTR_ERR(omap->base);
match = of_match_device(of_match_ptr(omap_i2c_of_match), &pdev->dev);
if (match) {
u32 freq = 100000; /* default to 100000 Hz */
pdata = match->data;
omap->flags = pdata->flags;
of_property_read_u32(node, "clock-frequency", &freq);
/* convert DT freq value in Hz into kHz for speed */
omap->speed = freq / 1000;
} else if (pdata != NULL) {
omap->speed = pdata->clkrate;
omap->flags = pdata->flags;
omap->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
}
omap->dev = &pdev->dev;
omap->irq = irq;
spin_lock_init(&omap->lock);
/* (3) 存储dev->driver_data数据 */
platform_set_drvdata(pdev, omap);
init_completion(&omap->cmd_complete);
omap->reg_shift = (omap->flags >> OMAP_I2C_FLAG_BUS_SHIFT__SHIFT) & 3;
pm_runtime_enable(omap->dev);
pm_runtime_set_autosuspend_delay(omap->dev, OMAP_I2C_PM_TIMEOUT);
pm_runtime_use_autosuspend(omap->dev);
r = pm_runtime_get_sync(omap->dev);
if (r < 0)
goto err_free_mem;
/*
* Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2.
* On omap1/3/2 Offset 4 is IE Reg the bit [15:14] is 0 at reset.
* Also since the omap_i2c_read_reg uses reg_map_ip_* a
* readw_relaxed is done.
*/
rev = readw_relaxed(omap->base + 0x04);
omap->scheme = OMAP_I2C_SCHEME(rev);
switch (omap->scheme) {
case OMAP_I2C_SCHEME_0:
omap->regs = (u8 *)reg_map_ip_v1;
omap->rev = omap_i2c_read_reg(omap, OMAP_I2C_REV_REG);
minor = OMAP_I2C_REV_SCHEME_0_MAJOR(omap->rev);
major = OMAP_I2C_REV_SCHEME_0_MAJOR(omap->rev);
break;
case OMAP_I2C_SCHEME_1:
/* FALLTHROUGH */
default:
omap->regs = (u8 *)reg_map_ip_v2;
rev = (rev << 16) |
omap_i2c_read_reg(omap, OMAP_I2C_IP_V2_REVNB_LO);
minor = OMAP_I2C_REV_SCHEME_1_MINOR(rev);
major = OMAP_I2C_REV_SCHEME_1_MAJOR(rev);
omap->rev = rev;
}
omap->errata = 0;
if (omap->rev >= OMAP_I2C_REV_ON_2430 &&
omap->rev < OMAP_I2C_REV_ON_4430_PLUS)
omap->errata |= I2C_OMAP_ERRATA_I207;
if (omap->rev <= OMAP_I2C_REV_ON_3430_3530)
omap->errata |= I2C_OMAP_ERRATA_I462;
if (!(omap->flags & OMAP_I2C_FLAG_NO_FIFO)) {
u16 s;
/* Set up the fifo size - Get total size */
s = (omap_i2c_read_reg(omap, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3;
omap->fifo_size = 0x8 << s;
/*
* Set up notification threshold as half the total available
* size. This is to ensure that we can handle the status on int
* call back latencies.
*/
omap->fifo_size = (omap->fifo_size / 2);
if (omap->rev < OMAP_I2C_REV_ON_3630)
omap->b_hw = 1; /* Enable hardware fixes */
/* calculate wakeup latency constraint for MPU */
if (omap->set_mpu_wkup_lat != NULL)
omap->latency = (1000000 * omap->fifo_size) /
(1000 * omap->speed / 8);
}
/* reset ASAP, clearing any IRQs */
/* (4) i2c adapter寄存器初始化 */
omap_i2c_init(omap);
/* (5) 中断注册 */
if (omap->rev < OMAP_I2C_OMAP1_REV_2)
r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr,
IRQF_NO_SUSPEND, pdev->name, omap);
else
r = devm_request_threaded_irq(&pdev->dev, omap->irq,
omap_i2c_isr, omap_i2c_isr_thread,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
pdev->name, omap);
if (r) {
dev_err(omap->dev, "failure requesting irq %i\n", omap->irq);
goto err_unuse_clocks;
}
/* (6) i2c adapter 初始化 */
adap = &omap->adapter;
i2c_set_adapdata(adap, omap);
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DEPRECATED;
strlcpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
adap->algo = &omap_i2c_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
adap->bus_recovery_info = &omap_i2c_bus_recovery_info;
/* i2c device drivers may be active on return from add_adapter() */
adap->nr = pdev->id;
/* (7) i2c adapter 注册 */
r = i2c_add_numbered_adapter(adap);
if (r) {
dev_err(omap->dev, "failure adding adapter\n");
goto err_unuse_clocks;
}
dev_info(omap->dev, "bus %d rev%d.%d at %d kHz\n", adap->nr,
major, minor, omap->speed);
pm_runtime_mark_last_busy(omap->dev);
pm_runtime_put_autosuspend(omap->dev);
return 0;
err_unuse_clocks:
omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
pm_runtime_put(omap->dev);
pm_runtime_disable(&pdev->dev);
err_free_mem:
return r;
}
↓
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
if (adap->nr == -1) /* -1 means dynamically assign bus id */
return i2c_add_adapter(adap);
return __i2c_add_numbered_adapter(adap);
}
↓
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
mutex_lock(&core_lock);
id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1,
GFP_KERNEL);
mutex_unlock(&core_lock);
if (id < 0)
return id == -ENOSPC ? -EBUSY : id;
return i2c_register_adapter(adap);
}
↓
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}
/* Sanity checks */
if (unlikely(adap->name[0] == '\0')) {
pr_err("i2c-core: Attempt to register an adapter with "
"no name!\n");
return -EINVAL;
}
if (unlikely(!adap->algo)) {
pr_err("i2c-core: Attempt to register adapter '%s' with "
"no algo!\n", adap->name);
return -EINVAL;
}
rt_mutex_init(&adap->bus_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
/* (7.1) 注册i2c adapter对应的device
可以看到, i2c adapter会注册两个device:
一个是platform bus上的device,在dts中定义由系统自动创建,对应的driver为platform driver;
另一个是下述i2c bus上的device,在i2c adapter注册时创建,一般没有对应的i2c driver,如果有特殊驱动需要会在后续的__process_new_adapter()中适配;
*/
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
pm_runtime_no_callbacks(&adap->dev);
#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif
/* bus recovery specific initialization */
/* (7.2) 注册总线恢复等一系列函数 */
if (adap->bus_recovery_info) {
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
if (!bri->recover_bus) {
dev_err(&adap->dev, "No recover_bus() found, not using recovery\n");
adap->bus_recovery_info = NULL;
goto exit_recovery;
}
/* Generic GPIO recovery */
if (bri->recover_bus == i2c_generic_gpio_recovery) {
if (!gpio_is_valid(bri->scl_gpio)) {
dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n");
adap->bus_recovery_info = NULL;
goto exit_recovery;
}
if (gpio_is_valid(bri->sda_gpio))
bri->get_sda = get_sda_gpio_value;
else
bri->get_sda = NULL;
bri->get_scl = get_scl_gpio_value;
bri->set_scl = set_scl_gpio_value;
} else if (!bri->set_scl || !bri->get_scl) {
/* Generic SCL recovery */
dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n");
adap->bus_recovery_info = NULL;
}
}
exit_recovery:
/* create pre-declared device nodes */
/* (7.3) 遍历i2c adapter上挂载的i2c物理设备,并创建对应的i2c_client device */
of_i2c_register_devices(adap);
acpi_i2c_register_devices(adap);
acpi_i2c_install_space_handler(adap);
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
/* (7.4) 特殊驱动需要适配i2c adapter的i2c bus device,调用__process_new_adapter()来适配 */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
return 0;
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
mutex_unlock(&core_lock);
return res;
}
i2c adapter的主要目的,就是在操作i2c_client时,通过引用对应的i2c adapter,发起i2c读写操作。
对i2c_client的读操作:
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
int ret;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.flags |= I2C_M_RD;
msg.len = count;
msg.buf = buf;
ret = i2c_transfer(adap, &msg, 1);
/*
* If everything went ok (i.e. 1 msg received), return #bytes received,
* else error code.
*/
return (ret == 1) ? count : ret;
}
对i2c_client的写操作:
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{
int ret;
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;
ret = i2c_transfer(adap, &msg, 1);
/*
* If everything went ok (i.e. 1 msg transmitted), return #bytes
* transmitted, else error code.
*/
return (ret == 1) ? count : ret;
}
读写都会调用到i2c_transfer()函数:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret;
if (adap->algo->master_xfer) {
if (in_atomic() || irqs_disabled()) {
ret = i2c_trylock_adapter(adap);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
i2c_lock_adapter(adap);
}
ret = __i2c_transfer(adap, msgs, num);
i2c_unlock_adapter(adap);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
↓
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
return -EOPNOTSUPP;
/* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
* enabled. This is an efficient way of keeping the for-loop from
* being executed when not needed.
*/
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < num; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_read(adap, &msgs[i], i);
else
trace_i2c_write(adap, &msgs[i], i);
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
if (static_key_false(&i2c_trace_msg)) {
int i;
for (i = 0; i < ret; i++)
if (msgs[i].flags & I2C_M_RD)
trace_i2c_reply(adap, &msgs[i], i);
trace_i2c_result(adap, i, ret);
}
return ret;
}
可以看到最终调用的是adap->algo->master_xfer()函数,对应i2c-omap的实现:
static const struct i2c_algorithm omap_i2c_algo = {
.master_xfer = omap_i2c_xfer,
.functionality = omap_i2c_func,
};
↓
static int
omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
int i;
int r;
r = pm_runtime_get_sync(omap->dev);
if (r < 0)
goto out;
r = omap_i2c_wait_for_bb_valid(omap);
if (r < 0)
goto out;
r = omap_i2c_wait_for_bb(omap);
if (r < 0)
goto out;
if (omap->set_mpu_wkup_lat != NULL)
omap->set_mpu_wkup_lat(omap->dev, omap->latency);
for (i = 0; i < num; i++) {
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
if (r != 0)
break;
}
if (r == 0)
r = num;
omap_i2c_wait_for_bb(omap);
if (omap->set_mpu_wkup_lat != NULL)
omap->set_mpu_wkup_lat(omap->dev, -1);
out:
pm_runtime_mark_last_busy(omap->dev);
pm_runtime_put_autosuspend(omap->dev);
return r;
}
omap_i2c_xfer()就是最后实际实现i2c读写操作的地方。
可以看到i2c_adapter在系统中没有单独使用的,必须通过操作i2c_client来操作i2c_adapter。
如果i2c_adapter控制器上没有挂载实际的物理设备,怎么来调试i2c_adapter呢?
系统针对每个i2c_adapter
都创建了对应的字符设备/dev/i2c-%d
,通过这个字符设备节点可以创建虚拟的i2c_client
,进而操作i2c_adapter
。
drivers\i2c\i2c-dev.c:
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
/* (1) 注册对应的字符设备 */
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
if (res)
goto out;
/* (2) 创建"/sys/class/i2c-dev" */
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
/* (3) 针对每一个i2c_adapter,创建"/sys/class/i2c-dev/i2c-%d"
udev会自动的创建"/dev/i2c-%d"节点
*/
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev(I2C_MAJOR, "i2c");
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
↓
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
/* (3.1) 获取到i2c_adapter的device */
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
/* register this i2c device with the driver core */
/* (3.2) 根据adap->nr,注册对应的"/sys/class/i2c-dev/i2c-%d" */
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error:
return_i2c_dev(i2c_dev);
return res;
}
对应的字符设备操作函数如下:
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
在open的时候会创建虚拟的i2c_client设备:
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
/* (1.1) 根据从设备号找到对应的i2c_dev */
i2c_dev = i2c_dev_get_by_minor(minor);
if (!i2c_dev)
return -ENODEV;
/* (1.2) 根据i2c_dev找到对应的i2c_adapter */
adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap)
return -ENODEV;
/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
*
* This client is ** NEVER REGISTERED ** with the driver model
* or I2C core code!! It just holds private copies of addressing
* information and maybe a PEC flag.
*/
/* (1.3) 分配新的i2c_client结构 */
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
/* (1.4) 将i2c_adapter连接到i2c_client */
client->adapter = adap;
file->private_data = client;
return 0;
}
可以通过ioctl的I2C_SLAVE
命令来设置i2c_client的地址:
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = file->private_data;
unsigned long funcs;
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
switch (cmd) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
if ((arg > 0x3ff) ||
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
/* (1.5) 设置i2c_client的地址 */
client->addr = arg;
return 0;
...
}
读写操作都是通过i2c_client,发起对i2c_adapter的读写:
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
iminor(file_inode(file)), count);
ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
kfree(tmp);
return ret;
}
static ssize_t i2cdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
int ret;
char *tmp;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
tmp = memdup_user(buf, count);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
iminor(file_inode(file)), count);
ret = i2c_master_send(client, tmp, count);
kfree(tmp);
return ret;
}
i2c adapter也是需要把对应的芯片pin脚配置成i2c功能管脚的。
目前dts中部分配置,arch\arm\boot\dts\am57xx-beagle-x15-common.dtsi:
&dra7_pmx_core {
...
i2c1_pins_default: i2c1_pins_default {
pinctrl-single,pins = <
DRA7XX_CORE_IOPAD(0x3800, (PIN_INPUT_PULLUP | MUX_MODE0)) /* i2c1_sda.sda */
DRA7XX_CORE_IOPAD(0x3804, (PIN_INPUT_PULLUP | MUX_MODE0)) /* i2c1_scl.scl */
>;
};
i2c4_pins_default: i2c4_pins_default {
pinctrl-single,pins = <
DRA7XX_CORE_IOPAD(0x3440, (PIN_INPUT_PULLUP | MUX_MODE7)) /* gpmc_a0.i2c4_scl */
DRA7XX_CORE_IOPAD(0x3444, (PIN_INPUT_PULLUP | MUX_MODE7)) /* gpmc_a1.i2c4_sda */
>;
};
...
}
&i2c1 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
pinctrl-0 = <&i2c1_pins_default>;
...
}
&i2c4 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
pinctrl-0 = <&i2c4_pins_default>;
...
};
在驱动probe时,会自动配置pin ctrl:
driver_probe_device() -> really_probe() -> pinctrl_bind_pins():
int pinctrl_bind_pins(struct device *dev)
{
int ret;
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
if (!dev->pins)
return -ENOMEM;
/* (1) 获取到device的pinctrl配置
根据dts中的配置创建
*/
dev->pins->p = devm_pinctrl_get(dev);
if (IS_ERR(dev->pins->p)) {
dev_dbg(dev, "no pinctrl handle\n");
ret = PTR_ERR(dev->pins->p);
goto cleanup_alloc;
}
/* (2) 获取"default"配置 */
dev->pins->default_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT);
if (IS_ERR(dev->pins->default_state)) {
dev_dbg(dev, "no default pinctrl state\n");
ret = 0;
goto cleanup_get;
}
/* (3) 获取"init"配置 */
dev->pins->init_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_INIT);
if (IS_ERR(dev->pins->init_state)) {
/* Not supplying this state is perfectly legal */
dev_dbg(dev, "no init pinctrl state\n");
/* (4.1) 配置"default"配置 */
ret = pinctrl_select_state(dev->pins->p,
dev->pins->default_state);
} else {
/* (4.2) 配置"init"配置 */
ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
}
if (ret) {
dev_dbg(dev, "failed to activate initial pinctrl state\n");
goto cleanup_get;
}
#ifdef CONFIG_PM
/*
* If power management is enabled, we also look for the optional
* sleep and idle pin states, with semantics as defined in
*
*/
dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_SLEEP);
if (IS_ERR(dev->pins->sleep_state))
/* Not supplying this state is perfectly legal */
dev_dbg(dev, "no sleep pinctrl state\n");
dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_IDLE);
if (IS_ERR(dev->pins->idle_state))
/* Not supplying this state is perfectly legal */
dev_dbg(dev, "no idle pinctrl state\n");
#endif
return 0;
/*
* If no pinctrl handle or default state was found for this device,
* let's explicitly free the pin container in the device, there is
* no point in keeping it around.
*/
cleanup_get:
devm_pinctrl_put(dev->pins->p);
cleanup_alloc:
devm_kfree(dev, dev->pins);
dev->pins = NULL;
/* Only return deferrals */
if (ret != -EPROBE_DEFER)
ret = 0;
return ret;
}
站在用户的角度其实不关心i2c控制器具体是什么实现的,我只要驱动起我的i2c设备即可,重点是在i2c设备的功能实现上。
i2c设备对应i2c_client,所以用户的业务都在i2c_client driver中实现。
首先我们需要在dts中定义一个i2c_client设备。
例如我们在i2c1总线上挂载一个PCF853 RTC芯片,arch\arm\boot\dts\am57xx-beagle-x15-common.dtsi:
i2c2_pins_default: i2c2_pins_default {
pinctrl-single,pins = <
DRA7XX_CORE_IOPAD(0x3808, (PIN_INPUT_PULLUP | MUX_MODE0)) /* i2c2_sda.sda */
DRA7XX_CORE_IOPAD(0x380C, (PIN_INPUT_PULLUP | MUX_MODE0)) /* i2c2_scl.scl */
>;
};
&i2c2 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
pinctrl-0 = <&i2c2_pins_default>;
pcf8563: pcf8563@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
};
...
}
对应的i2c_client device是在i2c adapter创建完成以后,遍历下挂的i2c设备时创建的:
omap_i2c_probe() -> i2c_add_numbered_adapter() -> __i2c_add_numbered_adapter() -> i2c_register_adapter() -> of_i2c_register_devices():
static void of_i2c_register_devices(struct i2c_adapter *adap)
{
struct device_node *node;
/* Only register child devices if the adapter has a node pointer set */
if (!adap->dev.of_node)
return;
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
/* (7.3.1) 遍历dts中i2c adapter节点"i2cN"下挂的子节点 */
for_each_available_child_of_node(adap->dev.of_node, node) {
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;
/* (7.3.2) 把每一个子节点创建成对应的i2c_client device */
of_i2c_register_device(adap, node);
}
}
↓
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node)
{
struct i2c_client *result;
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr_be;
u32 addr;
int len;
dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
/* (7.3.2.1) 获取节点的"compatible"属性 */
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
}
/* (7.3.2.2) 获取节点的"reg"属性,把其转换成i2c设备地址 */
addr_be = of_get_property(node, "reg", &len);
if (!addr_be || (len < sizeof(*addr_be))) {
dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
node->full_name);
return ERR_PTR(-EINVAL);
}
addr = be32_to_cpup(addr_be);
if (addr & I2C_TEN_BIT_ADDRESS) {
addr &= ~I2C_TEN_BIT_ADDRESS;
info.flags |= I2C_CLIENT_TEN;
}
if (addr & I2C_OWN_SLAVE_ADDRESS) {
addr &= ~I2C_OWN_SLAVE_ADDRESS;
info.flags |= I2C_CLIENT_SLAVE;
}
if (i2c_check_addr_validity(addr, info.flags)) {
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
info.addr, node->full_name);
return ERR_PTR(-EINVAL);
}
info.addr = addr;
info.of_node = of_node_get(node);
info.archdata = &dev_ad;
if (of_get_property(node, "wakeup-source", NULL))
info.flags |= I2C_CLIENT_WAKE;
/* (7.3.2.3) 创建i2c_client device */
result = i2c_new_device(adap, &info);
if (result == NULL) {
dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
node->full_name);
of_node_put(node);
return ERR_PTR(-EINVAL);
}
return result;
}
↓
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
/* (7.3.2.3.1) 分配i2c_client */
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
/* (7.3.2.3.2) 初始化i2c_client */
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
status = i2c_check_addr_validity(client->addr, client->flags);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
if (status)
goto out_err;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
client->dev.fwnode = info->fwnode;
i2c_dev_set_name(adap, client);
/* (7.3.2.3.3) 注册i2c_client */
status = device_register(&client->dev);
if (status)
goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
为了适配PCF853 RTC芯片,必须要写一个对应的i2c_client driver。drivers\rtc\rtc-pcf8563.c:
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "rtc-pcf8563",
.of_match_table = of_match_ptr(pcf8563_of_match),
},
.probe = pcf8563_probe,
.id_table = pcf8563_id,
};
static const struct of_device_id pcf8563_of_match[] = {
{ .compatible = "nxp,pcf8563" },
{}
};
在of_i2c_register_device()创建的过程中,没有处理"interrupts"属性,但是在i2c_client driver的probe()函数中又直接使用了client->irq
,这里可能有些问题会影响alarm功能的使用:
static int pcf8563_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
...
if (client->irq > 0) {
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf8563_irq,
IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
pcf8563->rtc->name, client);
if (err) {
dev_err(&client->dev, "unable to request IRQ %d\n",
client->irq);
return err;
}
}
...
}
在rtc的i2c_client driver中最重要的工作是调用devm_rtc_device_register()注册rtc device:
static int pcf8563_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pcf8563 *pcf8563;
int err;
unsigned char buf;
unsigned char alm_pending;
dev_dbg(&client->dev, "%s\n", __func__);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
pcf8563 = devm_kzalloc(&client->dev, sizeof(struct pcf8563),
GFP_KERNEL);
if (!pcf8563)
return -ENOMEM;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
i2c_set_clientdata(client, pcf8563);
pcf8563->client = client;
device_set_wakeup_capable(&client->dev, 1);
/* Set timer to lowest frequency to save power (ref Haoyu datasheet) */
buf = PCF8563_TMRC_1_60;
err = pcf8563_write_block_data(client, PCF8563_REG_TMRC, 1, &buf);
if (err < 0) {
dev_err(&client->dev, "%s: write error\n", __func__);
return err;
}
err = pcf8563_get_alarm_mode(client, NULL, &alm_pending);
if (err) {
dev_err(&client->dev, "%s: read error\n", __func__);
return err;
}
if (alm_pending)
pcf8563_set_alarm_mode(client, 0);
pcf8563->rtc = devm_rtc_device_register(&client->dev,
pcf8563_driver.driver.name,
&pcf8563_rtc_ops, THIS_MODULE);
if (IS_ERR(pcf8563->rtc))
return PTR_ERR(pcf8563->rtc);
if (client->irq > 0) {
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf8563_irq,
IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
pcf8563->rtc->name, client);
if (err) {
dev_err(&client->dev, "unable to request IRQ %d\n",
client->irq);
return err;
}
}
#ifdef CONFIG_COMMON_CLK
/* register clk in common clk framework */
pcf8563_clkout_register_clk(pcf8563);
#endif
/* the pcf8563 alarm only supports a minute accuracy */
pcf8563->rtc->uie_unsupported = 1;
return 0;
}
pcf8563_rtc_ops
为rtc的函数操作集,其中的功能主要分为两部分:timer 和 alarm。
static const struct rtc_class_ops pcf8563_rtc_ops = {
.ioctl = pcf8563_rtc_ioctl,
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
.read_alarm = pcf8563_rtc_read_alarm,
.set_alarm = pcf8563_rtc_set_alarm,
.alarm_irq_enable = pcf8563_irq_enable,
};
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
}
static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_set_datetime(to_i2c_client(dev), tm);
}
static irqreturn_t pcf8563_irq(int irq, void *dev_id)
{
struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id);
int err;
char pending;
err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending);
if (err)
return IRQ_NONE;
if (pending) {
rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF);
pcf8563_set_alarm_mode(pcf8563->client, 1);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
在系统初始化的时候linux walltime会从rtc中读取一个基准时刻,在随后的系统运行过程中walltime不再和rtc发生联系,而是使用tick来更新walltime。
static int __init rtc_hctosys(void)
{
int err = -ENODEV;
struct rtc_time tm;
struct timespec64 tv64 = {
.tv_nsec = NSEC_PER_SEC >> 1,
};
struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
if (rtc == NULL) {
pr_info("unable to open rtc device (%s)\n",
CONFIG_RTC_HCTOSYS_DEVICE);
goto err_open;
}
err = rtc_read_time(rtc, &tm);
if (err) {
dev_err(rtc->dev.parent,
"hctosys: unable to read the hardware clock\n");
goto err_read;
}
tv64.tv_sec = rtc_tm_to_time64(&tm);
err = do_settimeofday64(&tv64);
dev_info(rtc->dev.parent,
"setting system clock to "
"%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(long long) tv64.tv_sec);
err_read:
rtc_class_close(rtc);
err_open:
rtc_hctosys_ret = err;
return err;
}
walltime具体和哪个rtc同步由CONFIG_RTC_HCTOSYS_DEVICE
定义决定:
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
$ hwclock --help
Usage:
hwclock [function] [option...]
Query or set the hardware clock.
Functions:
-h, --help show this help text and exit
-r, --show read hardware clock and print result
--get read hardware clock and print drift corrected result
--set set the RTC to the time given with --date
-s, --hctosys set the system time from the hardware clock
-w, --systohc set the hardware clock from the current system time
--systz set the system time based on the current timezone
--adjust adjust the RTC to account for systematic drift since
the clock was last set or adjusted
-c, --compare periodically compare the system clock with the CMOS clock
--getepoch print out the kernel's hardware clock epoch value
--setepoch set the kernel's hardware clock epoch value to the
value given with --epoch
--predict predict RTC reading at time given with --date
-V, --version display version information and exit
选项:
-u, --utc the hardware clock is kept in UTC
--localtime the hardware clock is kept in local time
-f, --rtc special /dev/... file to use instead of default
--directisa access the ISA bus directly instead of /dev/rtc
--badyear ignore RTC's year because the BIOS is broken
--date
设置硬件时钟的操作:
# hwclock --set --date="09/17/2003 13:26:00"
或者
# clock --set --date="09/17/2003 13:26:00"
通用的设置格式:hwclock/clock --set --date=“月/日/年时:分:秒”。