一、I2C总线介绍
1.1 I2C电气特性
I2C(Inter-Integrated Circuit)总线是由
PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
I2C总线只有两根双向信号线。
SDA: Serial Data Line-数据线
SCL :Serial Clock-时钟线
1.2 总线寻址
I2C总线协议规定:从设备采用7位的地址。D7~D1:从设备地址。
D0位:数据传送方向位,为“0”时表示主设备向从设备写数据,为“1”时表示主机由从设备读数据。
主设备发送地址时,总线上的每个从设备都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主设备寻址,根据R/W位将自己确定为发送器或接收器。
从设备的地址由固定部分和用户自定
义部分组成。
1. 固定部分:D7-D4 共4位决定的。这是由从设备的生产厂商生产时就已确定的值。
2.
用户自定义部分: D3-D1 共3位。这3位通常对应设备的3个引脚(A0~A2)。把3个引脚接到不同的电平上,就可以形成一个3位的数值。
硬件设置A0,A1,A2拉低,I2C就可以按照000来寻找了。
二、I2C总线时序
2.1 空闲状态
I2C总线总线的SDA和SCL两条信号线同时处
于高电平时,规定为总线的空闲状态。
2.2 起始状态
在时钟线SCL保持高电平期间,数据线S
DA上的电平被拉低(即负跳变),定义为I2C总线总线的启动信号,它标志着一次数据传输的开始。
2.3 结束状态
在时钟线SCL保持高电平时,数据线SDA被释放,
使得SDA返回高电平(即正跳变),称为I2C总线的停止信号。
2.4 数据位传送
I2C总线上的所有数据(地址和数据)都是以8位一
个字节为单位传送的。
2.5 应答位
发送器每发送一个字节,就在时钟脉冲第9位释放数据线,
由接收器反馈一个应答信号。 应答信号为低电平时,定为有效应答位ACK,表示接收器已经成功地接收了该字节;应答信号为高电平时,定为非应答位(NACK),表示接收器没有成功接收该字节。
三、LINUX-I2C子系统
3.1 I2C子系统架构
①I2C设备驱动:用户自写驱动、通用驱动+用户层驱动
②I2C总线驱动:I2C-ADAPTER;adapter.algo
③I2C核心:①和②联系作用,包含注册、注销的方法
3.2 I2C总线驱动
描述一个I2C控制器
-
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;
-
-
/* --- administration stuff. */
-
int (*client_register)(struct i2c_client *) __deprecated;
-
int (*client_unregister)(struct i2c_client *) __deprecated;
-
-
/* data fields that are valid for all devices */
-
u8 level; /* nesting level for lockdep */
-
struct mutex bus_lock;
-
struct mutex clist_lock;
-
-
int timeout; /* in jiffies */
-
int retries;
-
struct device dev; /* the adapter device */
-
-
int nr;
-
struct list_head clients; /* DEPRECATED */
-
char name[48];
-
struct completion dev_released;
-
};
i2c_algorithm:
-
struct i2c_algorithm {
-
-
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);
-
-
u32 (*functionality) (struct i2c_adapter *);
-
};
i2c-s3c2410.c>>i2c_adap_s3c_init()>>s3c2410_i2c_driver>>s3c24xx_i2c_probe:
-
static int s3c24xx_i2c_probe(struct platform_device *pdev)
-
{
-
struct s3c24xx_i2c *i2c;
-
struct s3c2410_platform_i2c *pdata;
-
struct resource *res;
-
int ret;
-
-
............................
-
-
/* initialise the i2c controller */
-
-
ret = s3c24xx_i2c_init(i2c); //初始化i2c_init
-
if (ret != 0)
-
goto err_iomap;
-
-
/* find the IRQ for this unit (note, this relies on the init call to
-
* ensure no current IRQs pending
-
*/
-
-
i2c->irq = ret = platform_get_irq(pdev, 0);
-
if (ret <= 0) {
-
dev_err(&pdev->dev, "cannot find IRQ\n");
-
goto err_iomap;
-
}
-
-
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
-
dev_name(&pdev->dev), i2c);
-
-
if (ret != 0) {
-
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
-
goto err_iomap;
-
}
-
-
ret = s3c24xx_i2c_register_cpufreq(i2c);
-
if (ret < 0) {
-
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
-
goto err_irq;
-
}
-
-
/* Note, previous versions of the driver used i2c_add_adapter()
-
* to add the bus at any number. We now pass the bus number via
-
* the platform data, so if unset it will now default to always
-
* being bus 0.
-
*/
-
-
i2c->adap.nr = pdata->bus_num;
-
-
ret = i2c_add_numbered_adapter(&i2c->adap); //注册i2c控制器(CPU内部),类似函数还有i2c_add_adpater(CPU外接控制器)。由I2C核心提供
-
if (ret < 0) {
-
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
-
goto err_cpufreq;
-
}
-
-
platform_set_drvdata(pdev, i2c);
-
-
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
-
return 0;
-
-
err_cpufreq:
-
s3c24xx_i2c_deregister_cpufreq(i2c);
-
-
err_irq:
-
free_irq(i2c->irq, i2c);
-
-
err_iomap:
-
iounmap(i2c->regs);
-
-
err_ioarea:
-
release_resource(i2c->ioarea);
-
kfree(i2c->ioarea);
-
-
err_clk:
-
clk_disable(i2c->clk);
-
clk_put(i2c->clk);
-
-
err_noclk:
-
kfree(i2c);
-
return ret;
-
}
s3c24xx_i2c_init:
-
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
-
{
-
unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN; //赋值IICCON寄存器
-
struct s3c2410_platform_i2c *pdata;
-
unsigned int freq;
-
-
/* get the plafrom data */
-
-
pdata = i2c->dev->platform_data;
-
-
/* inititalise the gpio */ //初始化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);
-
-
/* check for s3c2440 i2c controller */
-
-
if (s3c24xx_i2c_is2440(i2c))
-
writel(0x0, i2c->regs + S3C2440_IICLC);
-
-
return 0;
-
}
i2c_algorithm:
-
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
-
.master_xfer = s3c24xx_i2c_xfer,
-
.functionality = s3c24xx_i2c_func,
-
};
s3c24xx_i2c_xfer:
-
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
-
struct i2c_msg *msgs, int num)
-
{
-
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
-
int retry;
-
int ret;
-
-
for (retry = 0; retry < adap->retries; retry++) {
-
-
ret = s3c24xx_i2c_doxfer(i2c, msgs, num); //调用doxfer函数
-
-
if (ret != -EAGAIN)
-
return ret;
-
-
dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
-
-
udelay(100);
-
}
-
-
return -EREMOTEIO;
-
}
s3c24xx_i2c_doxfer>>s3c24xx_i2c_message_start:
-
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; //设置IICSTAT模式
-
addr |= 1;
-
} else
-
stat |= 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; //写入OXF0
-
writel(stat, i2c->regs + S3C2410_IICSTAT); //传输
-
}
s3c24xx_i2c_irq:
-
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;
-
}
阅读(577) | 评论(0) | 转发(0) |