Linux驱动 I2C总线

       这里以rk3288为例子,使用的是linux4.14,根据设备树节点i2c 与rk3x_i2c_driver,match之后,就会调用对应的probe(rk3x_i2c_probe),这里主要就是注册一个adapt(i2c_add_adapter),也就是i2c控制器,或者说是i2c主设备,既然是主设备,就需要提供读写的能力,以及SCL的时钟

1.i2c->adap.algo = &rk3x_i2c_algorithm;//提供读写的方法
2.clk_rate = clk_get_rate(i2c->clk);
  rk3x_i2c_adapt_div(i2c, clk_rate);   //设置时钟分频
3.ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq, 0, dev_name(&pdev->dev), i2c);
//申请对应的中断
4.ret = i2c_add_adapter(&i2c->adap);   //注册adapt

       在ret = i2c_add_adapter(&i2c->adap);中有一个特别的函数of_i2c_register_devices(adap);

它会根据adapt对应的设备节点,轮询其子节点,然后of_i2c_register_device(adap, node);,这里最后就会注册i2c_client,既然是从设备,那么也有几点必须的:

1.of_modalias_node(node, info.type, sizeof(info.type)) //名字,用于与i2c_driver匹配
2.addr_be = of_get_property(node, "reg", &len);  //每一种从设备都有自己的地址,地址其实是实现i2c总线的基础
3.i2c_new_device(adap, &info);

       然后另一方面,各种各样的i2c_driver也会注册到i2c_bus中,i2c_driver会与i2c_client 进行match (i2c_match中对type有要求,adapt不会被匹配上),如果匹配上,就会调用driver->probe,至于probe中干什么事情,就得根据该i2c的用途来决定了,比如是rt5640(音频),那么里面就会注册一个codec,如果是触摸屏,那就就会注册一个输入设备等等,但是无论是用于什么,里面一定会提供读写的方法,而这个方法最后一定会调用到 i2c->adap.algo->master_xfer。这样i2c的基本流程就已经完成了。

      通过设备树可以发现,一个adapt下往往挂着多个client,那么就有问题了,

      1.该如何发送给我想要发送的那个client呢?

      2.A,B两个从设备同时挂在一个adapt下,如果两个同时发送,会怎么处理呢?

      第一个问题上面也已经提到了,每一种设备都有一个设备地址,通过发送不同的设备地址,就可以访问到想要访问的从设备。

      第二个问题,就拿一般的i2c设备来说,读写会调用i2c_transfer

if (in_atomic() || irqs_disabled()) {
	ret = i2c_trylock_adapter(adap);
if (!ret)
	/* I2C activity is ongoing. */
		return -EAGAIN;
} else {
	i2c_lock_adapter(adap);
}

       这一段代码,使用互斥锁,保证了一个进程正在使用该adapt时,另一个进程就会被进入睡眠,如果在进行原子操作或者中断是关闭的(不允许调度),那么直接返回。

       最后看一下发送的具体实现:rk3x_i2c_xfer

1.clk_enable(i2c->clk);clk_enable(i2c->pclk);  //使能时钟 
2.rk3x_i2c_setup(i2c, msgs + i, num - i);  //配置从设备地址,以及读还是写 
3.rk3x_i2c_start(i2c); //使能i2c控制器,使能start中断,i2c的模式,最后发送start信号。
4.wait_event_timeout(i2c->wait, !i2c->busy,msecs_to_jiffies(WAIT_TIMEOUT));//等待唤醒。

       由于在第3步中使能了start中断,并且开启了start信号,就会进入到中断服务函数,那么查看一下中断服务函数rk3x_i2c_irq:其实就是一个很简单的状态机

switch (i2c->state) {
	case STATE_START:
		rk3x_i2c_handle_start(i2c, ipd);	//启动start信号后,触发中断,准备发送或读取数据
		break;
	case STATE_WRITE:
		rk3x_i2c_handle_write(i2c, ipd);	//发送完mtxcnt后,触发中断,是否继续发送或者stop
		break;
	case STATE_READ:
		rk3x_i2c_handle_read(i2c, ipd);		//收到mrxcnt后,触发中断,是否继续接收或者stop
		break;
	case STATE_STOP:
		rk3x_i2c_handle_stop(i2c, ipd);		//启动stop信号后,触发中断,唤醒i2c->wait
		break;
	case STATE_IDLE:
		break;
	}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值