明确一下关系:
adapter--对应的是soc上实际的iic控制器;
struct i2c_adapter {
struct module *owner; //所属模块
unsigned int id; //algorithm的类型,定义于i2c-id.h,
unsigned int class;
const struct i2c_algorithm *algo; //总线通信方法结构体指针
void *algo_data; //algorithm数据
struct rt_mutex bus_lock; //控制并发访问的自旋锁
int timeout;
int retries; //重试次数
struct device dev; //适配器设备
int nr; //存放在i2c_adapter_idr里的位置号
char name[48]; //适配器名称
struct completion dev_released; //用于同步
struct list_head userspace_clients; //client链表头
};
驱动工程师通过使用i2c_register_adapter函数,向内核注册一个iic控制器。被注册到内核的iic控制器下面又可以挂接iic设备(例如eeprom,电源管理芯片等)。
xiic_i2c 包含了xilinx iic驱动程序操作的
struct xiic_i2c {
struct device *dev;
void __iomem *base;
wait_queue_head_t wait;
struct i2c_adapter adap;
struct i2c_msg *tx_msg;
struct mutex lock;
unsigned int tx_pos;
unsigned int nmsgs;
enum xilinx_i2c_state state;
struct i2c_msg *rx_msg;
int rx_pos;
enum xiic_endian endianness;
struct clk *clk;
};
1、设备指针;
2、内存基地址;
3、等待队列头,实现阻塞操作使用,后面介绍;
4、iic适配器;
5、msg内容指针;
6、互斥锁;
7、当前发送的位置;
8、消息数量;
9、状态;
、
、
10、大小端
11、时钟
在probe函数中:
1、
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
是具有资源管理的 kzalloc() ,使用资源管理(resource-managed)函数分配的内存,是会与所属设备相关联。当设备从系统中分离或者设备驱动被卸载,该内存会被自动释放,也可以通过 devm_kfree() 来释放内存。同理 socket_kzalloc() 等等。
2、
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//得到物理地址(from devicetree)
i2c->base = devm_ioremap_resource(&pdev->dev, res);//转换成虚拟地址
3、
irq = platform_get_irq(pdev, 0);//获取中断号 (from device tree)
4、pdata = dev_get_platdata(&pdev->dev);//获取私有数据
5、添加iic控制器到iic总线?
/* hook up driver to tree */
platform_set_drvdata(pdev, i2c);
i2c->adap = xiic_adapter;
i2c_set_adapdata(&i2c->adap, i2c);
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.dev.of_node = pdev->dev.of_node;
6、初始化互斥锁和等待队列;
互斥锁在一个进程拿到该锁时,其他线程如果试图获取该锁就会进入休眠状态。
等待队列时进入等待队列的进程会进入休眠状态,直到其他进程或者中断服务函数调用了 weak_up_waitqueue();
7、i2c->clk = devm_clk_get(&pdev->dev, NULL);//向系统申请IIC时钟资源
8、ret = clk_prepare_enable(i2c->clk);//使能时钟
9、i2c->dev = &pdev->dev;
10、电源管理相关:
pm_runtime_enable(i2c->dev);
pm_runtime_set_autosuspend_delay(i2c->dev, XIIC_PM_TIMEOUT);
pm_runtime_use_autosuspend(i2c->dev);
pm_runtime_set_active(i2c->dev);
11、 ret = devm_request_threaded_irq(&pdev>dev, irq, xiic_isr,xiic_process, IRQF_ONESHOT,pdev->name, i2c);//注册中断服务函数。
linux中,中断处理机制分为上半部和下半部。上半部是直接在中断服务函数中执行的代码段,下半部主要有以下几种机制1、tasklet 2、workqueue 3、中断线程化 4、软irq
怎么区分它们呢?tasklet和软irq是一个级别的,在执行代码中不能有sleep。一般用于执行完top handler就立即执行的情况。workqueue和线程化中断相当于开启一个线程,在线程中完成中断处理。
12、xiic_reinit(i2c);
13、ret = i2c_add_adapter(&i2c->adap);/* add i2c adapter to i2c tree */
14、 for (i = 0; i < pdata->num_devices; i++)i2c_new_device(&i2c- >adap, pdata->devices + i);/* add in known devices to the bus */
添加已经连接在总线上的设备到IIC BUS;
虽然整个probe主要有14个函数,但是基本内容是:
1、分配设备结构体
2、获取硬件内存地址资源;
3、获取CPU中断资源,注册中断服务函数;
4、初始化锁,等待队列;
5、把IIC控制器加入到内核的IIC BUS中;
上面说过i2c_adapter,对应的一个具体的iic控制器,这里需要实例化一个i2c_adapter。然后注册到内核中。
static struct i2c_adapter xiic_adapter = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
.class = I2C_CLASS_DEPRECATED,
.algo = &xiic_algorithm,
};
在i2c_adapter 中,主要是.algo成员,该成员实现了iic控制器数据传输的方法;
static const struct i2c_algorithm xiic_algorithm = {
.master_xfer = xiic_xfer,
.functionality = xiic_func,
};
主要来看:
static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
1、struct xiic_i2c *i2c = i2c_get_adapdata(adap); //获取到iic资源
2、发送数据:i2c->tx_msg = msgs; i2c->nmsgs = num; xiic_start_xfer(i2c);
接收数据呢? 看下之前注册的中断handler
static irqreturn_t xiic_isr(int irq, void *dev_id)
{
struct xiic_i2c *i2c = dev_id;
u32 pend, isr, ier;
irqreturn_t ret = IRQ_NONE;
/* Do not processes a devices interrupts if the device has no
* interrupts pending
*/
dev_dbg(i2c->adap.dev.parent, "%s entry\n", __func__);
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
pend = isr & ier;
if (pend)
ret = IRQ_WAKE_THREAD;
return ret;
}
注意,在注册中断服务函数的时候,我们注册的线程化中断。因此,当中断发生时,会先执行中断服务函数,执行完中断服务函数以后,就会在线程中断中,处理线程化中断。
1、mutex_lock(&i2c->lock);首先,先把临界资源锁住;
2、isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
pend = isr & ier;
读取中断状态寄存器,下面将判断是接收中断?错误中断?发送中断?
3、if ((pend & XIIC_INTR_ARB_LOST_MASK) ||((pend & XIIC_INTR_TX_ERROR_MASK) &&
!(pend & XIIC_INTR_RX_FULL_MASK)));
错误中断,重启控制器;
4、if (pend & XIIC_INTR_RX_FULL_MASK) //接收中断
在接收中断中,先判断i2c->rx_msg是否已经被分配过,接下来就是调用接收函数对iic控制器fifo中的数据进行读取。
其余的就是一些fifo状态的判断等。