Linux-IIC驱动(2)-Linux下IIC子系统的介绍

2024-08-31 06:38

本文主要是介绍Linux-IIC驱动(2)-Linux下IIC子系统的介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

IIC子系统架构

 

Linux下IIC的架构模型大概可以分为3层:

第一层是IIC的从设备驱动,他包含图中的device driver和i2c-dev。device driver需要用户编写,i2c-dev由内核实现,包含了IIC设备的通用方法,但是用户不能直接使用这个驱动,需要编写一个用户层驱动,它们2个合起来才可以实现一个驱动程序。

 

第二层总线驱动,他又叫做控制器驱动,比如说芯片内部的IIC控制器的使用需要实现一个驱动程序,比如所需要往IIC总线上传输数据它需要什么方法,需要实现那些函数等等。它包含图中的i2c-adapter和adapter-agio。

 

第三层是i2c-core。它作为一个媒介关联了总线驱动和i2c驱动。第二个作用就是它还提供了一个IIC驱动的注册和注销。

IIC总线驱动

 

图中我们可以知道编写i2c驱动有2种实现方法。

第一种是用户自己完全写一个i2c的驱动程序。

另一种方法是使用i2c的通用驱动i2c-dev,然后自己设计一个i2c用户模式驱动。

 

 

对于第一种情况,应用程序读写i2c设备的流程是比较明了的。

 

对于第二种情况的读写流程稍微有点复杂,我们先来介绍另一个比较重要的地方,就是i2c控制器驱动,i2c adapter/algorithm。

它是直接操作i2c设备的。里面包含2部分的东西,一个是adapter,适配器的意思,另一个是algorithm,算法的意思。

 

 

我们先来分析adapter,打开Linux源代码,搜索i2c_adapter

/** i2c_adapter is the structure used to identify a physical i2c bus along* with the access algorithms necessary to access it.*/
struct i2c_adapter {struct module *owner;unsigned int id;unsigned int class;		  /* classes to allow probing for */const struct i2c_algorithm *algo; /* the algorithm to access the bus */void *algo_data;/* data fields that are valid for all devices	*/u8 level; 			/* nesting level for lockdep */struct mutex bus_lock;int timeout;			/* in jiffies */int retries;struct device dev;		/* the adapter device */int nr;char name[48];struct completion dev_released;
};


在Linux中每一个i2c适配器或者说控制器都会有一个i2c_adapter来描述一个i2c控制器并确定i2c设备的访问方法。

里面有一个成员

const struct i2c_algorithm *algo; /* the algorithm to access the bus */

 

看看它的定义:

/** The following structs are for those who like to implement new bus drivers:* i2c_algorithm is the interface to a class of hardware solutions which can* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584* to name two of the most common.*/
struct i2c_algorithm {/* If an adapter algorithm can't do I2C-level access, set master_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, or a negative value on error */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *);
};


这个结构里面主要是一些函数指针,最重要的要数master_xfer了。这个函数主要来实现需要在i2c总线上传输数据的方法。比如说CPU需要通过i2c总线往一个i2c设备发送一些数据,这时候就可以使用i2c控制器里面实现的传输方法,比如说xxx_transfer来实现对i2c设备的读写。这样就把i2c数据的传输给封装起来了。

 

 

我们接下来分析一下2440上i2c控制器驱动的实现,它的实现在i2c-s3c2410.c文件中实现。

 

先来分析一下模块初始化和退出函数:

MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);static struct platform_driver s3c24xx_i2c_driver = {.probe		= s3c24xx_i2c_probe,.remove		= s3c24xx_i2c_remove,.id_table	= s3c24xx_driver_ids,.driver		= {.owner	= THIS_MODULE,.name	= "s3c-i2c",.pm	= S3C24XX_DEV_PM_OPS,},
};static int __init i2c_adap_s3c_init(void)
{return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);static void __exit i2c_adap_s3c_exit(void)
{platform_driver_unregister(&s3c24xx_i2c_driver);
}
module_exit(i2c_adap_s3c_exit);	.probe		= s3c24xx_i2c_probe,.remove		= s3c24xx_i2c_remove,.id_table	= s3c24xx_driver_ids,.driver		= {.owner	= THIS_MODULE,.name	= "s3c-i2c",.pm	= S3C24XX_DEV_PM_OPS,},
};static int __init i2c_adap_s3c_init(void)
{return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);static void __exit i2c_adap_s3c_exit(void)
{platform_driver_unregister(&s3c24xx_i2c_driver);
}
module_exit(i2c_adap_s3c_exit);

 

这里主要通过平台驱动注册了一个设备。设备名叫做s3c24xx_i2c_driver。上面是s3c24xx_i2c_driver的定义,先来分析一些s3c24xx_i2c_probe函数。里面的一些变量赋值和其他一些简单的函数就不分析了,我们分析一个重要的函数s3c24xx_i2c_init。

 

/* s3c24xx_i2c_init** initialise the controller, set the IO lines and frequency
*/static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;struct s3c2410_platform_i2c *pdata;unsigned int freq;/* get the plafrom data */pdata = i2c->dev->platform_data;/* inititalise the gpio */if (pdata->cfg_gpio)pdata->cfg_gpio(to_platform_device(i2c->dev));/* write slave address */writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);writel(iicon, i2c->regs + S3C2410_IICCON);/* we need to work out the divisors for the clock... */if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {writel(0, i2c->regs + S3C2410_IICCON);dev_err(i2c->dev, "cannot meet bus frequency required\n");return -EINVAL;}/* todo - check that the i2c lines aren't being dragged anywhere */dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);return 0;
}


我们可以看到这个函数主要完成了一下的功能:

1、unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;使能中断和应答

2、pdata->cfg_gpio(to_platform_device(i2c->dev));初始化GPIO

3、writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);写从地址

4、if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {初始化时钟。

 

可以发现,这里的初始化代码和裸机的代码也是非常类似的。

初始化之后还调用了i2c_add_numbered_adapter函数来注册一个i2c适配器。

 

 

prob函数中有一个重要的赋值语句i2c->adap.algo    = &s3c24xx_i2c_algorithm;之前说过algorithm是用来实现i2c设备读写方法的。这里面包含2个函数,一个是s3c24xx_i2c_xfer,这个函数用来实现i2c设备的读写,他们依次调用s3c24xx_i2c_doxfer->s3c24xx_i2c_message_start,最终实现设备的读写,这个函数如下:

 

/* s3c24xx_i2c_message_start** put the start of a message onto the bus
*/static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,struct i2c_msg *msg)
{unsigned int addr = (msg->addr & 0x7f) << 1;unsigned long stat;unsigned long iiccon;stat = 0;stat |=  S3C2410_IICSTAT_TXRXEN;if (msg->flags & I2C_M_RD) {stat |= S3C2410_IICSTAT_MASTER_RX;addr |= 1;} elsestat |= S3C2410_IICSTAT_MASTER_TX;if (msg->flags & I2C_M_REV_DIR_ADDR)addr ^= 1;/* todo - check for wether ack wanted or not */s3c24xx_i2c_enable_ack(i2c);iiccon = readl(i2c->regs + S3C2410_IICCON);writel(stat, i2c->regs + S3C2410_IICSTAT);dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);writeb(addr, i2c->regs + S3C2410_IICDS);/* delay here to ensure the data byte has gotten onto the bus* before the transaction is started */ndelay(i2c->tx_setup);dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);writel(iiccon, i2c->regs + S3C2410_IICCON);stat |= S3C2410_IICSTAT_START;writel(stat, i2c->regs + S3C2410_IICSTAT);
}


我们对比2440数据手册中的IIC读写时序:

可以发现读写的流程和数据手册是一致的(肯定是一致的,不然就读写失败了)。

 

不过后面从ACK period开始就属于中断的内容了,我们需要找到i2c的中断处理函数,肯定是在prob函数里面找的,发现了这么一段话:

ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c);


这里面注册了一个中断,s3c24xx_i2c_irq。中断处理程序如下:

/* s3c24xx_i2c_irq** top level IRQ servicing routine
*/static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{struct s3c24xx_i2c *i2c = dev_id;unsigned long status;unsigned long tmp;status = readl(i2c->regs + S3C2410_IICSTAT);if (status & S3C2410_IICSTAT_ARBITR) {/* deal with arbitration loss */dev_err(i2c->dev, "deal with arbitration loss\n");}if (i2c->state == STATE_IDLE) {dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");tmp = readl(i2c->regs + S3C2410_IICCON);tmp &= ~S3C2410_IICCON_IRQPEND;writel(tmp, i2c->regs +  S3C2410_IICCON);goto out;}/* pretty much this leaves us with the fact that we've* transmitted or received whatever byte we last sent */i2s_s3c_irq_nextbyte(i2c, status);out:return IRQ_HANDLED;
}


前面都是一些错误处理,主要分析i2s_s3c_irq_nextbyte函数。里面的步骤完成就是剩下的i2c读写的流程。

更多Linux资料及视频教程点击这里

 

 

这篇关于Linux-IIC驱动(2)-Linux下IIC子系统的介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123175

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念