(1)适配器驱动就是用来控制Soc上的I2C控制器的,封装I2C控制器的通信方式;
(2)适配器驱动会向I2C核心层注册,I2C核心层会管理内核中所有注册的适配器驱动,每一个注册的适配器驱动就代表Soc中的一个I2C控制器;
(3)I2C接口设备的驱动会通过I2C总线匹配上对应的适配器,然后调用适配器提供的数据收发接口进行通信;
/*********gslX680.c**************/
#define GSLX680_I2C_NAME "gslX680"static struct i2c_driver gsl_ts_driver = {.driver = {.name = GSLX680_I2C_NAME,.owner = THIS_MODULE,},
#ifndef CONFIG_HAS_EARLYSUSPEND.suspend = gsl_ts_suspend,.resume = gsl_ts_resume,
#endif.probe = gsl_ts_probe,.remove = __devexit_p(gsl_ts_remove),.id_table = gsl_ts_id,
};//函数调用关系
gsl_ts_init() //驱动加载函数i2c_add_driver(&gsl_ts_driver); //向I2C总线注册gslX680驱动gsl_ts_probe() //当gslX680驱动在I2C总线上匹配上struct i2c_client时就会调用probe方法/*********内核注册i2c_board_info信息************/
static struct i2c_board_info i2c_devs1[] __initdata = {{I2C_BOARD_INFO("gslX680", 0x40), //gslX680是用来和驱动匹配的名字,0x40是设备在I2C总线上的地址},};smdkc110_machine_init() //struct machine_desc->init_machine()i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); //向编号是1的适配器注册i2c_board_info信息
(1)上面是gslX680触摸屏驱动代码和在I2C总线上的匹配相关部分的代码;
(2)struct i2c_board_info结构体:会用来构成struct i2c_client结构体,将来I2C接口设备的驱动在I2C总线上匹配设备时,就会用名字来进行匹配,哪一个适配器上有该驱动的名字就会和哪个适配器匹配成功,并且还会把struct i2c_board_info结构体里的信息传给I2C驱动,比如:设备地址、中断号;
(3)每个适配器都会注册struct i2c_board_info结构体信息,将来struct i2c_board_info结构体里的名字会和I2C驱动的名字进行匹配, I2C驱动和适配器匹配上,将来I2C驱动通信就通过匹配上的适配器,也就是硬件上I2C接口设备接在哪一个I2C控制器;
static struct platform_device_id s3c24xx_driver_ids[] = {{.name = "s3c2410-i2c",.driver_data = TYPE_S3C2410,}, {.name = "s3c2440-i2c",.driver_data = TYPE_S3C2440,}, { },
};static struct platform_driver s3c24xx_i2c_driver = {.probe = s3c24xx_i2c_probe,.remove = s3c24xx_i2c_remove,.id_table = s3c24xx_driver_ids, //在platform总线上匹配设备时使用.driver = {.owner = THIS_MODULE,.name = "s3c-i2c",.pm = S3C24XX_DEV_PM_OPS,},
};i2c_adap_s3c_init() //驱动加载函数platform_driver_register(&s3c24xx_i2c_driver); //利用平台总线进行注册s3c24xx_i2c_probe() //在平台总线上匹配上设备后调用probe函数i2c_add_numbered_adapter(&i2c->adap); //利用platform_device传过来的数据构建适配器结构体,并向I2C核心层注册
(1) 在内核的struct machine_desc->init_machine()函数中会注册plat_device,会有多个plat_device和platform总线驱动s3c24xx_i2c_driver匹配上,基本上是Soc有几个I2C控制器就匹配上几次,也就是会向I2C核心层注册多个适配器;
(2)虽然被匹配上多次,但是每次plat_device传过来的数据都是不同的,包括I2C控制器的寄存器物理地址、中断号、适配器编号等;
struct s3c24xx_i2c {spinlock_t lock;wait_queue_head_t wait; //为了实现同步,使i2c->algorithm->master_xfer函数能在中断中的传输过程完成时返回unsigned int suspended:1;//记录待传输数据的信息struct i2c_msg *msg; //消息队列unsigned int msg_num; //消息队列中的消息数unsigned int msg_idx; //当前传输的消息是序列中的第几条unsigned int msg_ptr; //传输的是当前的第几个字节unsigned int tx_setup; //传输建立延时unsigned int irq; //使用的中断号enum s3c24xx_i2c_state state; //当前传输的状态,进行到哪一步unsigned long clkrate;void __iomem *regs; //用于访问内核IO内存的实际地址struct clk *clk;struct device *dev;struct resource *ioarea; //IO内存资源struct i2c_adapter adap; //对应的适配器
};
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{struct s3c24xx_i2c *i2c;struct s3c2410_platform_i2c *pdata;struct resource *res;int ret;//解析处platform总线设备传递的信息pdata = pdev->dev.platform_data;i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);//设置适配器的名字strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));//构建适配器结构体adapi2c->adap.owner = THIS_MODULE;i2c->adap.algo = &s3c24xx_i2c_algorithm; //设置适配器的通信方法i2c-&ies = 2; //重发次数是2i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;i2c->tx_setup = 50;//初始化自旋锁和等待队列spin_lock_init(&i2c->lock);init_waitqueue_head(&i2c->wait);/* 获取I2C控制器的时钟并使能 */i2c->dev = &pdev->dev;i2c->clk = clk_get(&pdev->dev, "i2c"); //获取时钟系统给I2C控制器的提供的时钟频率clk_enable(i2c->clk); //使能时钟/* 获取I2C控制器的IO地址资源 */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);······//申请寄存器的物理地址i2c->ioarea = request_mem_region(res->start, resource_size(res),pdev->name);//动态映射物理地址i2c->regs = ioremap(res->start, resource_size(res));//在master_xfer等方法中就能通过适配器的algo_data获取对应的i2c对象i2c->adap.algo_data = i2c;i2c->adap.dev.parent = &pdev->dev;//初始化芯片的I2C控制器:使能中断、ACK使能、设置对应的GPIO、初始化时钟等ret = s3c24xx_i2c_init(i2c);if (ret != 0)goto err_iomap;//获取中断号资源i2c->irq = ret = platform_get_irq(pdev, 0);if (ret <= 0) {dev_err(&pdev->dev, "cannot find IRQn");goto err_iomap;}//申请中断号并绑定中断处理程序s3c24xx_i2c_irqret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c);//利用内核通知链机制,当CPU频率变化时,时I2C时钟设置能做出调整ret = s3c24xx_i2c_register_cpufreq(i2c);if (ret < 0) {dev_err(&pdev->dev, "failed to register cpufreq notifiern");goto err_irq;}//从platform总线设备获得适配器的总线编号i2c-& = pdata->bus_num;//向I2C核心层注册适配器ret = i2c_add_numbered_adapter(&i2c->adap);if (ret < 0) {dev_err(&pdev->dev, "failed to add bus to i2c coren");goto err_cpufreq;}//将i2c保存到pdev->dev->p->driver_dataplatform_set_drvdata(pdev, i2c);clk_disable(i2c->clk);dev_info(&pdev->dev, "%s: S3C I2C adaptern", dev_name(&i2c->adap.dev));return 0;······
}
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {.master_xfer = s3c24xx_i2c_xfer,.functionality = s3c24xx_i2c_func,
};
s3c24xx_i2c_xfer() //adap->algo->master_xfers3c24xx_i2c_doxfer()s3c24xx_i2c_set_master() //确保当前I2C控制器处于空闲,才继续下面的操作s3c24xx_i2c_enable_irq() //使能i2C-Bus的Tx/Rx中断s3c24xx_i2c_message_start() //开启I2C通信wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); //发送函数进入等待,直到消息发送完成或者超过5s返回在中断程序中唤醒等待队列:s3c24xx_i2c_irq() //适配器绑定的中断处理函数i2c_s3c_irq_nextbyte() //发送数据s3c24xx_i2c_stop() //当数据收发完成或者通信出错时停止本次传输s3c24xx_i2c_master_complete() //本次传输完成wake_up(&i2c->wait); //唤醒等待队列
(1)I2C适配器描述结构体struct i2c_adapter中algo变量是适配器的通信方法,在驱动程序的prob函数中赋值;
(2)通信是以消息(struct i2c_msg)为单位进行的,消息描述结构体会指明要发送的设备的地址、数据传输方向等;
(1)首先是I2C设备驱动调用适配器的adap->algo->master_xfer进行发送消息,master_xfer方法在进行一些初始化后,开启本次I2C通信,然后就进入等待队列,直到消息发送完成或者5秒超时后返回;
(2)具体的发送是在probe方法绑定了中断函数中进行,这个中断函数是I2C控制器的中断处理函数,在发送完数据或者出错后,就会唤醒之前在等待s3c24xx_i2c_doxfer()函数;
本文发布于:2024-01-30 06:12:54,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170656637619801.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |