本文基于mini2440开发板,Linux版本号是:linux-2.6.32.2
一.IIC总线device 硬件信息
#define S3C2410_PA_IIC (0x54000000)
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC,
.end = S3C_PA_IIC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
.id = 0,
#else
.id = -1,
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
.flags = 0,
.slave_addr = 0x10,
.frequency = 100*1000,
.sda_delay = 100,
};
其中,IIC寄存器的基地址为0x54000000。
s3c3440芯片的寄存器的地址如下:
二.IIC总线device注册
IIC总线device包含在mini2440_devices中,如下图所示:
将包含usb,lcd,i2c,nand等设备的mini2440_devices数组当成platform设备注册到内核
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))
三.IIC总线driver的注册
IIC总线driver的注册调用的是platform_driver_register函数,IIC总线driver注册是platform总线会去自动匹配相应的device,匹配的规则是:
先根据id_table来匹配
再根据device的名称来匹配
这个IICdriver可以匹配两种IIC device,如下所示:
四. IIC总线driver和device的匹配
IIC总线的driver和device匹配上后,会执行driver的probe函数,probe函数中执行了如下动作:
获取i2c时钟,并使能时钟
i2c->clk = clk_get(&pdev->dev, "i2c");
if (IS_ERR(i2c->clk)) {
dev_err(&pdev->dev, "cannot get clockn");
ret = -ENOENT;
goto err_noclk;
}
clk_enable(i2c->clk);
获取IIC寄存器的虚拟地址
i2c->regs = ioremap(res->start, resource_size(res));
获取IIC中断,并申请IIC中断
i2c->irq = ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
dev_err(&pdev->dev, "cannot find IRQn");
goto err_iomap;
}
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c);
IIC初始化,设置时钟,使能中断,使能ask, 设置寄存器
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 :获取platform 设备数据
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);
// IIC-bus acknowledge enable
//IC-Bus Tx/Rx interrupt enable
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 requiredn");
return -EINVAL;
}
/* todo - check that the i2c lines aren't being dragged anywhere */
dev_info(i2c->dev, "bus frequency set to %d KHzn", freq);
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lxn", iicon);
return 0;
}
注册 IIC adapter,在注册adapter之前要设置adap的名字,算法,父节点等信息
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
五.使能IIC总线应答
需要把 IICCON寄存器的第7位设置为1。
#define S3C2410_IICCON_ACKEN (1<<7)
static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)
{
unsigned long tmp;
tmp = readl(i2c->regs + S3C2410_IICCON);
writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);
}
六. 使能IIC总线中断
需要把 IICCON寄存器的第5位设置为1。
#define S3C2410_IICCON_IRQEN (1<<5)
static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)
{
unsigned long tmp;
tmp = readl(i2c->regs + S3C2410_IICCON);
writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
}
七.获取IIC总线的忙状态
读 IICSTAT寄存器的第5位,1表示忙,0表示空闲
#define S3C2410_IICSTAT_BUSBUSY (1<<5)
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
return (iicstat & S3C2410_IICSTAT_BUSBUSY);
八. IIC硬件中断
IIC中断产生的条件
①一个字节的数据发送或者接收完成产生硬件中断。
②发出从设备地址,成功匹配到从设备后产生硬件中断。
③总线仲裁失败产生中断。
正常情况下IIC中断产生的时机
发送数据时,IICDS移位寄存器向SDA一位一位发出电平信号,先发高位,循环8次后,数据发送完成, 接收到ASK信号后产生中断。
读数据时,IICDS移位寄存器从SDA线一位一位读入电平信号,先读高位,循环8次后,数据接收完成,产生中断。
3.中断处理过程中又来了新的IIC数据
产生硬件中断后,会设置中断挂起flag,同时将SCL拉低,IIC传输暂停,以防在中断处理过程中又来了新的IIC信号。
等待中断函数处理完成后,清除中断挂起flag,释放SCL,IIC继续发送和接收数据。
清除中断挂起flag,设置 IICCON寄存器的第4位为0。
#define S3C2410_IICCON_IRQPEND (1<<4)
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
九. IIC总线启动
使能IIC应答
s3c24xx_i2c_enable_ack(i2c);
2.使能IIC数据输入输出
#define S3C2410_IICSTAT_TXRXEN (1<<4)
stat |= S3C2410_IICSTAT_TXRXEN;
3.配置主机读写模式,若是读,设备地址最低位或上1
#define S3C2410_IICSTAT_MASTER_RX (2<<6)
#define S3C2410_IICSTAT_MASTER_TX (3<<6)
if (msg->flags & I2C_M_RD)
{
stat |= S3C2410_IICSTAT_MASTER_RX;
addr |= 1;
}
else
stat |= S3C2410_IICSTAT_MASTER_TX;
writel(stat, i2c->regs + S3C2410_IICSTAT);
4.写从设备地址到IICDS寄存器
writeb(addr, i2c->regs + S3C2410_IICDS);
5.开始IIC传输
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT);
十. IIC总线停止传输
1.需要向IICSTART寄存器的第5位写0
unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);
/* stop the transfer */
iicstat &= ~S3C2410_IICSTAT_START;
writel(iicstat, i2c->regs + S3C2410_IICSTAT);
2.禁止IIC中断
s3c24xx_i2c_disable_irq(i2c);
十一. IIC总线的algo
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
s3c24xx_i2c_algorithm赋值给adap.algo
i2c->adap.algo = &s3c24xx_i2c_algorithm;
该adap添加进内核,后面内核通过该adap控制这个IIC总线的数据传输。
i2c_add_numbered_adapter(&i2c->adap);
十二. IIC的数据传输函数s3c24xx_i2c_xfer
配置IIC引脚
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
2.调用s3c24xx_i2c_doxfer函数传输数据,最多重复操作2次,成功了就退出,失败了就继续操作。
adap->retries = 2。
for (retry = 0; retry < adap->retries; retry++)
{
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
if (ret != -EAGAIN)
return ret;
udelay(100);
}
十三. IIC 总线发数据
IIC总线调用s3c24xx_i2c_xfer发送数据,s3c24xx_i2c_xfer函数又调用s3c24xx_i2c_doxfer。
1.等待IIC不忙。
ret = s3c24xx_i2c_set_master(i2c);
2.设置IIC状态标志位
i2c->state = STATE_START;
3.使能IIC中断
s3c24xx_i2c_enable_irq(i2c);
IIC总线启动传输
s3c24xx_i2c_message_start(i2c, msgs);
5.进入睡眠,等待IIC的等待队列唤醒。
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
6.等待中断发生,中断函数在s3c24xx_i2c_probe里面注册。
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);
7.中断发生,进入中断
8.进入case STATE_START,判断最后接收到1bit数据是不是ASK, 没有收到ask,退出IIC传输(前提是没有设置I2C_M_IGNORE_NAK模式)
if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK))
{
/* ack was not received... */
dev_dbg(i2c->dev, "ack was not receivedn");
s3c24xx_i2c_stop(i2c, -ENXIO);
goto out_ack;
}
9.判断是读IIC消息还是写IIC消息
if (i2c->msg->flags & I2C_M_RD)
i2c->state = STATE_READ;
else
i2c->state = STATE_WRITE;
在这里是写IIC,i2c->state = STATE_WRITE;
10.判断该消息是不是最后一个消息,若是,停止IIC传输,否则,进入case STATE_WRITE。这里通常是i2c匹配设备的时候使用。
if (is_lastmsg(i2c) && i2c->msg->len == 0)
{
s3c24xx_i2c_stop(i2c, 0);
goto out_ack;
}
11.若这个msg的数据没有发完,继续发,每次发一个byte,就是给寄存器S3C2410_IICDS赋值。
if (!is_msgend(i2c))
{
byte = i2c->msg->buf[i2c->msg_ptr++];
writeb(byte, i2c->regs + S3C2410_IICDS);
ndelay(i2c->tx_setup);
}
若这个msg发完了,看看是不是最后一个msg。如果不是,指向下一个msg。判断下一个msg是不是变成读了,就是msg改变方向了。如果是读,要设置I2C_M_NOSTART标志,表示IIC重新发起一个起始信号,否则IIC停止传输。如果还是写,继续写下一个消息。
else if (!is_lastmsg(i2c))
{
/* we need to go to the next i2c message */
dev_dbg(i2c->dev, "WRITE: Next Messagen");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->msg++;
/* check to see if we need to do another message */
if (i2c->msg->flags & I2C_M_NOSTART)
{
if (i2c->msg->flags & I2C_M_RD)
{
/* cannot do this, the controller
* forces us to send a new START
* when we change direction */
s3c24xx_i2c_stop(i2c, -EINVAL);
}
goto retry_write;
}
else
{
/* send the new start */
s3c24xx_i2c_message_start(i2c, i2c->msg);
i2c->state = STATE_START;
}
若是所有的msg都发送完了,停止IIC传输
else
{
/* send stop */
s3c24xx_i2c_stop(i2c, 0);
}
写寄存器停止IIC传输
iicstat &= ~S3C2410_IICSTAT_START;
writel(iicstat, i2c->regs + S3C2410_IICSTAT);
IIC msg 清零
i2c->msg_ptr = 0;
i2c->msg = NULL;
i2c->msg_idx++;
i2c->msg_num = 0;
唤醒等待队列
wake_up(&i2c->wait);
17.禁止中断产生。
s3c24xx_i2c_disable_irq(i2c);
上一篇:mini2440 linuxi2c驱动
下一篇:mini2440 I2C驱动的分析与学习(二)
推荐阅读最新更新时间:2024-11-07 13:36
设计资源 培训 开发板 精华推荐
- AD8615AUJZ-R2高速光电二极管前置放大器典型应用电路
- TCR6DA1531、200mA、1.5V 和 3.1V 输出电压双路输出 CMOS 低压降稳压器的典型应用
- 【物联网】鸿蒙智能WIFI开关+:4214059A
- FRDM-CR20A,用于 MCR20A 无线收发器的 Freedom 开发板
- LT1952MPGN 36V 至 72V、3.3V、40A 同步正向转换器的典型应用电路
- 面向 3D 机器视觉应用并采用 DLP 技术的精确点云生成
- 使用 IXYS 的 eZ80F91 的参考设计
- LTM4628EV 双路 8A、1.5V 和 1.2V 输出 DC/DC 模块稳压器的典型应用电路
- LTC3616 -6A、4MHz 单片式同步降压型 DC/DC 转换器的典型应用
- MC78M08BTG 8V 电流升压稳压器的典型应用