1.I2C子系統架構
- Linux下IIC的架構模型大概可以分爲3層:
- 第一層是I2C的從設備驅動,它包含圖中的device driver和i2c-dev。device driver需要用戶編寫,i2c-dev由內核實現,包含了I2C設備的通用方法,但是用戶不能直接使用這個驅動,需要編寫一個用戶層驅動,它們2個合起來纔可以實現一個驅動程序。
- 第二層總線驅動,它又叫做總線控制器驅動,比如說芯片內部的I2C控制器的使用需要實現一個驅動程序,比如所需要往I2C總線上傳輸數據它需要什麼方法,需要實現那些函數等等。它包含圖中的i2c-adapter和adapter-agio。
- 第三層是i2c-core,I2C總線和I2C設備驅動的中間樞紐,它提供了I2C總線驅動和設備驅動的註冊、註銷方法等
2.I2C總線驅動
- 圖中我們可以知道編寫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來描述,裏面有一個成員:
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_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, 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);
}
- 這裏主要通過平臺驅動註冊了一個設備。設備名叫做s3c24xx_i2c_driver。上面是s3c24xx_i2c_driver的定義,先來分析一些s3c24xx_i2c_probe函數。裏面的一些變量賦值和其他一些簡單的函數就不分析
/* 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;
} 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;
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;
}