1. 程式人生 > >Kernel的IIC驅動分析

Kernel的IIC驅動分析

loss amp 支持 ice from see ots eno ado

涉及到的文件:

drivers/i2c/i2c-core.c

drivers/i2c/i2c-dev.c

drivers/i2c/busses/i2c-imx.c

等等

在下面分析的代碼中,不想關或者不重要的,我會省略掉。

1. 適配器設備的註冊

在Linux內核啟動的過程中,會調用到mx6_sabresd_board_init函數

static void __init mx6_sabresd_board_init(void)

{

。。。省略。。。

/*在這裏修改了i2c0適配器上設備的類型,同時添加了平臺數據,後面會分析到*/

strcpy(mxc_i2c0_board_info[0].type,

"wm8962");

mxc_i2c0_board_info[0].platform_data = &wm8962_config_data;

/*第一步:註冊適配器設備:詳見下面分析*/

imx6q_add_imx_i2c(0, &mx6q_sabresd_i2c_data);

/*第二步:添加I2C從設備*/

i2c_register_board_info(0, mxc_i2c0_board_info,

ARRAY_SIZE(mxc_i2c0_board_info));

。。。省略。。。

}

下面我們來分析第一步:註冊適配器設備

extern const struct imx_imx_i2c_data imx6q_imx_i2c_data[] __initconst;

#define imx6q_add_imx_i2c(id, pdata) \

imx_add_imx_i2c(&imx6q_imx_i2c_data[id], pdata)

接著追到imx_add_imx_i2c,我們可以知道

1). 通過imx_add_platform_device ,我們知道I2C的適配器設備是放入了平臺總線模型當中,並且名字為imx-i2c。

2). 第一個參數imx_imx_i2c_data是適配器的設備信息,包括I2C寄存器地址,中斷號,資源res是通過data的重新賦值。它包括了I2C寄存器的基地址,中斷號,總線編號。第二個參數imxi2c_platform_data是存放在平臺上的數據,下面我們來分析這兩個參數。

struct platform_device *__init imx_add_imx_i2c(

const struct imx_imx_i2c_data *data,

const struct imxi2c_platform_data *pdata)

{

struct resource res[] = {

{

.start = data->iobase,

.end = data->iobase + data->iosize - 1,

.flags = IORESOURCE_MEM,

}, {

.start = data->irq,

.end = data->irq,

.flags = IORESOURCE_IRQ,

},

};

return imx_add_platform_device("imx-i2c", data->id,

res, ARRAY_SIZE(res),

pdata, sizeof(*pdata));

}

我們先分析第一個參數imx_add_platform_device:

我們往回查看上述函數的關鍵數據

imx_imx_i2c_data* data= imx6q_imx_i2c_data[0]

const struct imx_imx_i2c_data imx6q_imx_i2c_data[] __initconst = {

#define imx6q_imx_i2c_data_entry(_id, _hwid) \

imx_imx_i2c_data_entry(MX6Q, _id, _hwid, SZ_4K)

imx6q_imx_i2c_data_entry(0, 1),

imx6q_imx_i2c_data_entry(1, 2),

imx6q_imx_i2c_data_entry(2, 3),

};

接著追imx_imx_i2c_data_entry

#define imx_imx_i2c_data_entry(soc, _id, _hwid, _size) \

[_id] = imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size)

再接著追imx_imx_i2c_data_entry

#define imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size) \

{ \

.id = _id, \

.iobase = soc ## _I2C ## _hwid ## _BASE_ADDR, \

.iosize = _size, \

.irq = soc ## _INT_I2C ## _hwid, \

}

通過一層層的代入展開,可以推導出:

imx6q_imx_i2c_data[0]=

{

.id = 0,

.iobase = MX6Q_I2C1_BASE_ADDR,

.iosize = _SZ_4K,

.irq = MX6Q_INT_I2C1,

}

第二個參數mx6q_sabresd_i2c_data是時鐘系數:

static struct imxi2c_platform_data mx6q_sabresd_i2c_data = {

.bitrate = 100000,

};

2. 適配器驅動的流程

從之前的代碼,我們得知適配器設備在平臺驅動模型中的名字為imx-i2c。

其代碼在drivers/i2c/busses/i2c-imx.c

static struct platform_driver i2c_imx_driver = {

.remove = __exit_p(i2c_imx_remove),

.driver = {

.name = DRIVER_NAME, /*imx-i2c*/

.owner = THIS_MODULE,

}

};

/*通過平臺模型註冊*/

static int __init i2c_adap_imx_init(void)

{

return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe);

}

當名字imx-i2c匹配上的時候,會自動調用探測函數i2c_imx_probe,其原理屬於平臺驅動模型。在這裏面會對I2C進行初始化:設置始終,初始化I2C寄存器,設置中斷等。

static int __init i2c_imx_probe(struct platform_device *pdev)

{

struct imx_i2c_struct *i2c_imx;

struct resource *res;

struct imxi2c_platform_data *pdata;

void __iomem *base;

resource_size_t res_size;

int irq;

int ret;

dev_dbg(&pdev->dev, "<%s>\n", __func__);

/*獲取來自適配器設備上的內存資源:I2C寄存器地址*/

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (!res) {

dev_err(&pdev->dev, "can‘t get device resources\n");

return -ENOENT;

}

/*獲取來自適配器設備上的中斷號*/

irq = platform_get_irq(pdev, 0);

if (irq < 0) {

dev_err(&pdev->dev, "can‘t get irq number\n");

return -ENOENT;

}

/*獲取平臺數據:也就是之前分析的時鐘系數*/

pdata = pdev->dev.platform_data;

if (pdata && pdata->init) {

ret = pdata->init(&pdev->dev);

if (ret)

return ret;

}

res_size = resource_size(res);

/*將寄存器地址映射為虛擬內存*/

if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {

ret = -EBUSY;

goto fail0;

}

base = ioremap(res->start, res_size);

if (!base) {

dev_err(&pdev->dev, "ioremap failed\n");

ret = -EIO;

goto fail1;

}

i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);

if (!i2c_imx) {

dev_err(&pdev->dev, "can‘t allocate interface\n");

ret = -ENOMEM;

goto fail2;

}

/* Setup i2c_imx driver structure */

strcpy(i2c_imx->adapter.name, pdev->name);

i2c_imx->adapter.owner = THIS_MODULE;

i2c_imx->adapter.algo = &i2c_imx_algo; /*算法層:這個是跟具體硬件和協議相關的,下面分析*/

i2c_imx->adapter.dev.parent = &pdev->dev;

i2c_imx->adapter.nr = pdev->id; /*名字*/

i2c_imx->irq = irq;

i2c_imx->base = base;

i2c_imx->res = res;

/* 設置I2C時鐘 */

i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");

if (IS_ERR(i2c_imx->clk)) {

ret = PTR_ERR(i2c_imx->clk);

dev_err(&pdev->dev, "can‘t get I2C clock\n");

goto fail3;

}

/* 設置中斷 */

ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);

if (ret) {

dev_err(&pdev->dev, "can‘t claim irq %d\n", i2c_imx->irq);

goto fail4;

}

/* 等待隊列 */

init_waitqueue_head(&i2c_imx->queue);

/* 設置適配器數據 */

i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

/* 設置時鐘分頻 */

if (pdata && pdata->bitrate)

i2c_imx_set_clk(i2c_imx, pdata->bitrate);

else

i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);

/* 設置I2C的寄存器 */

writeb(0, i2c_imx->base + IMX_I2C_I2CR);

writeb(0, i2c_imx->base + IMX_I2C_I2SR);

/* 註冊適配器驅動:這個我們後面會分析 */

ret = i2c_add_numbered_adapter(&i2c_imx->adapter);

if (ret < 0) {

dev_err(&pdev->dev, "registration failed\n");

goto fail5;

}

/* 將i2c_imx數據放入平臺總線中 */

platform_set_drvdata(pdev, i2c_imx);

dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq);

dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",

i2c_imx->res->start, i2c_imx->res->end);

dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n",

res_size, i2c_imx->res->start);

dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",

i2c_imx->adapter.name);

dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");

return 0; /* Return OK */

fail5:

free_irq(i2c_imx->irq, i2c_imx);

fail4:

clk_put(i2c_imx->clk);

fail3:

kfree(i2c_imx);

fail2:

iounmap(base);

fail1:

release_mem_region(res->start, resource_size(res));

fail0:

if (pdata && pdata->exit)

pdata->exit(&pdev->dev);

return ret; /* Return error number */

}

/*這個函數的主要功能是提供該適配器能提供的I2C功能,提供判斷*/

static u32 i2c_imx_func(struct i2c_adapter *adapter)

{

return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;

}

static struct i2c_algorithm i2c_imx_algo = {

.master_xfer = i2c_imx_xfer, /*具體的傳輸的實現*/

.functionality = i2c_imx_func, /*傳輸的方式*/

};

static int i2c_imx_xfer(struct i2c_adapter *adapter,

struct i2c_msg *msgs, int num)

{

unsigned int i, temp;

int result;

/*將該適配器上的數據取出來*/

struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);

dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

/* I2C起始位 */

result = i2c_imx_start(i2c_imx);

if (result)

goto fail0;

/* 對數據進行讀寫 */

for (i = 0; i < num; i++) {

if (i) {

dev_dbg(&i2c_imx->adapter.dev,

"<%s> repeated start\n", __func__);

temp = readb(i2c_imx->base + IMX_I2C_I2CR);

temp |= I2CR_RSTA;

writeb(temp, i2c_imx->base + IMX_I2C_I2CR);

result = i2c_imx_bus_busy(i2c_imx, 1);

if (result)

goto fail0;

}

dev_dbg(&i2c_imx->adapter.dev,

"<%s> transfer message: %d\n", __func__, i);

/* write/read data */

#ifdef CONFIG_I2C_DEBUG_BUS

temp = readb(i2c_imx->base + IMX_I2C_I2CR);

dev_dbg(&i2c_imx->adapter.dev, "<%s> CONTROL: IEN=%d, IIEN=%d, "

"MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n", __func__,

(temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),

(temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),

(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));

temp = readb(i2c_imx->base + IMX_I2C_I2SR);

dev_dbg(&i2c_imx->adapter.dev,

"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, "

"IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n", __func__,

(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),

(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),

(temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),

(temp & I2SR_RXAK ? 1 : 0));

#endif

if (msgs[i].flags & I2C_M_RD)

result = i2c_imx_read(i2c_imx, &msgs[i]);

else

result = i2c_imx_write(i2c_imx, &msgs[i]);

if (result)

goto fail0;

}

fail0:

/* 停止位 */

i2c_imx_stop(i2c_imx);

dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,

(result < 0) ? "error" : "success msg",

(result < 0) ? result : num);

return (result < 0) ? result : num;

}

這個函數裏面得到所有函數就是最底層真正對硬件的操作了,比如:

static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)

{

unsigned int temp = 0;

struct imxi2c_platform_data *pdata;

int result;

dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

/* Currently on Arik/Rigel, the I2C clk is from IPG_PERCLK which is

* sourced from IPG_CLK. In low bus freq mode, IPG_CLK is at 12MHz

* and IPG_PERCLK is down to 4MHz.

* Update I2C divider before set i2c clock.

*/

pdata = i2c_imx->adapter.dev.parent->platform_data;

if (pdata && pdata->bitrate)

i2c_imx_set_clk(i2c_imx, pdata->bitrate);

else

i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);

clk_enable(i2c_imx->clk);

writeb(i2c_imx->ifdr, i2c_imx->base + IMX_I2C_IFDR);

/* Enable I2C controller */

writeb(0, i2c_imx->base + IMX_I2C_I2SR);

writeb(I2CR_IEN, i2c_imx->base + IMX_I2C_I2CR);

/* Wait controller to be stable */

udelay(50);

/* Start I2C transaction */

temp = readb(i2c_imx->base + IMX_I2C_I2CR);

temp |= I2CR_MSTA;

writeb(temp, i2c_imx->base + IMX_I2C_I2CR);

result = i2c_imx_bus_busy(i2c_imx, 1);

if (result)

return result;

i2c_imx->stopped = 0;

temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;

writeb(temp, i2c_imx->base + IMX_I2C_I2CR);

return result;

}

在i2c_add_numbered_adapter中會對最後的適配器驅動進行註冊,在註冊之前還會對I2C設備驅動進行註冊。

int i2c_add_numbered_adapter(struct i2c_adapter *adap)

{

int id;

int status;

if (adap->nr & ~MAX_ID_MASK)

return -EINVAL;

retry:

if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)

return -ENOMEM;

mutex_lock(&core_lock);

/* "above" here means "above or equal to", sigh;

* we need the "equal to" result to force the result

*/

status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);

if (status == 0 && id != adap->nr) {

status = -EBUSY;

idr_remove(&i2c_adapter_idr, id);

}

mutex_unlock(&core_lock);

if (status == -EAGAIN)

goto retry;

if (status == 0)

/*在這裏真正註冊I2C適配器*/

status = i2c_register_adapter(adap);

return status;

}

static int i2c_register_adapter(struct i2c_adapter *adap)

{

int res = 0;

/* Can‘t register until after driver model init */

if (unlikely(WARN_ON(!i2c_bus_type.p))) {

res = -EAGAIN;

goto out_list;

}

/* Sanity checks */

if (unlikely(adap->name[0] == ‘\0‘)) {

pr_err("i2c-core: Attempt to register an adapter with "

"no name!\n");

return -EINVAL;

}

if (unlikely(!adap->algo)) {

pr_err("i2c-core: Attempt to register adapter ‘%s‘ with "

"no algo!\n", adap->name);

return -EINVAL;

}

rt_mutex_init(&adap->bus_lock);

mutex_init(&adap->userspace_clients_lock);

INIT_LIST_HEAD(&adap->userspace_clients);

/* Set default timeout to 1 second if not already set */

if (adap->timeout == 0)

adap->timeout = HZ;

dev_set_name(&adap->dev, "i2c-%d", adap->nr);

adap->dev.bus = &i2c_bus_type;

adap->dev.type = &i2c_adapter_type;

res = device_register(&adap->dev); /*註冊*/

if (res)

goto out_list;

dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

#ifdef CONFIG_I2C_COMPAT

res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,

adap->dev.parent);

if (res)

dev_warn(&adap->dev,

"Failed to create compatibility class link\n");

#endif

/* 在這裏對已經添加好的I2C設備進行註冊 */

if (adap->nr < __i2c_first_dynamic_bus_num)

i2c_scan_static_board_info(adap);

/* Notify drivers */

mutex_lock(&core_lock);

bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);

mutex_unlock(&core_lock);

return 0;

out_list:

mutex_lock(&core_lock);

idr_remove(&i2c_adapter_idr, adap->nr);

mutex_unlock(&core_lock);

return res;

}

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)

{

struct i2c_devinfo *devinfo;

down_read(&__i2c_board_lock);

list_for_each_entry(devinfo, &__i2c_board_list, list) {

if (devinfo->busnum == adapter->nr

&& !i2c_new_device(adapter,

&devinfo->board_info))

dev_err(&adapter->dev,

"Can‘t create device at 0x%02x\n",

devinfo->board_info.addr);

}

up_read(&__i2c_board_lock);

}

3. I2C從設備的添加

int __init

i2c_register_board_info(int busnum,

struct i2c_board_info const *info, unsigned len)

{

int status;

down_write(&__i2c_board_lock);

/* */

if (busnum >= __i2c_first_dynamic_bus_num)

__i2c_first_dynamic_bus_num = busnum + 1;

for (status = 0; len; len--, info++) {

struct i2c_devinfo *devinfo;

devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);

if (!devinfo) {

pr_debug("i2c-core: can‘t register boardinfo!\n");

status = -ENOMEM;

break;

}

/*放在哪個適配器上*/

devinfo->busnum = busnum;

devinfo->board_info = *info;

/*將所有的從設備都掛在一個鏈表中*/

list_add_tail(&devinfo->list, &__i2c_board_list);

}

up_write(&__i2c_board_lock);

return status;

}

4. AT24對於I2C的讀寫

只研究I2C,所以只是看下I2C在AT24C02中大概是如何使用,其他代碼都省略

static struct i2c_driver at24_driver = {

.driver = {

.name = "at24",

.owner = THIS_MODULE,

},

.probe = at24_probe,

.remove = __devexit_p(at24_remove),

.id_table = at24_ids, /*這是一個包含所有支持名字的列表,只要註冊的設備裏有其中一個名字匹配就匹配成功*/

};

static int __init at24_init(void)

{

。。。省略。。。

/*註冊從設備驅動*/

return i2c_add_driver(&at24_driver);

}

module_init(at24_init);

static inline int i2c_add_driver(struct i2c_driver *driver)

{

return i2c_register_driver(THIS_MODULE, driver);

}

從設備的註冊函數如下:

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

{

int res;

/* Can‘t register until after driver model init */

if (unlikely(WARN_ON(!i2c_bus_type.p)))

return -EAGAIN;

/* add the driver to the list of i2c drivers in the driver core */

driver->driver.owner = owner;

driver->driver.bus = &i2c_bus_type;

/*註冊設備*/

res = driver_register(&driver->driver);

if (res)

return res;

/* Drivers should switch to dev_pm_ops instead. */

if (driver->suspend)

pr_warn("i2c-core: driver [%s] using legacy suspend method\n",

driver->driver.name);

if (driver->resume)

pr_warn("i2c-core: driver [%s] using legacy resume method\n",

driver->driver.name);

pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);

INIT_LIST_HEAD(&driver->clients);

/* Walk the adapters that are already present */

i2c_for_each_dev(driver, __process_new_driver);

return 0;

}

static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)

{

if (client->dev.platform_data) {

chip = *(struct at24_platform_data *)client->dev.platform_data;

} else {

if (!id->driver_data) {

err = -ENODEV;

goto err_out;

}

/* Use I2C operations unless we‘re stuck with SMBus extensions. */

if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {

if (chip.flags & AT24_FLAG_ADDR16) {

err = -EPFNOSUPPORT;

goto err_out;

}

/*查看支持的模式*/

if (i2c_check_functionality(client->adapter,

I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {

use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;

} else if (i2c_check_functionality(client->adapter,

I2C_FUNC_SMBUS_READ_WORD_DATA)) {

use_smbus = I2C_SMBUS_WORD_DATA;

} else if (i2c_check_functionality(client->adapter,

I2C_FUNC_SMBUS_READ_BYTE_DATA)) {

use_smbus = I2C_SMBUS_BYTE_DATA;

} else {

err = -EPFNOSUPPORT;

goto err_out;

}

}

if (chip.flags & AT24_FLAG_TAKE8ADDR)

num_addresses = 8;

else

num_addresses = DIV_ROUND_UP(chip.byte_len,

(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);

at24 = kzalloc(sizeof(struct at24_data) +

num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);

if (!at24) {

err = -ENOMEM;

goto err_out;

} mutex_init(&at24->lock);

at24->use_smbus = use_smbus;

at24->chip = chip;

at24->num_addresses = num_addresses;

/*

* Export the EEPROM bytes through sysfs, since that‘s convenient.

* By default, only root should see the data (maybe passwords etc)

*/

sysfs_bin_attr_init(&at24->bin);

at24->bin.attr.name = "eeprom";

at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;

at24->bin.read = at24_bin_read;

at24->bin.size = chip.byte_len;

at24->macc.read = at24_macc_read;

writable = !(chip.flags & AT24_FLAG_READONLY);

if (writable) {

if (!use_smbus || i2c_check_functionality(client->adapter,

I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {

unsigned write_max = chip.page_size;

at24->macc.write = at24_macc_write;

at24->bin.write = at24_bin_write;

at24->bin.attr.mode |= S_IWUSR;

if (write_max > io_limit)

write_max = io_limit;

if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)

write_max = I2C_SMBUS_BLOCK_MAX;

at24->write_max = write_max;

/* buffer (data + address at the beginning) */

at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);

if (!at24->writebuf) {

err = -ENOMEM;

goto err_struct;

}

} else {

dev_warn(&client->dev,

"cannot write due to controller restrictions.");

}

}

}

static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,

struct bin_attribute *attr,

char *buf, loff_t off, size_t count)

{

struct at24_data *at24;

at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));

return at24_read(at24, buf, off, count);

}

然後一直往下追,在某部分功能會看到

static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,

unsigned offset, size_t count)

{

status = i2c_transfer(client->adapter, msg, 2);

}

drivers.c/i2c/i2c-core.c

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

{

unsigned long orig_jiffies;

int ret, try;

if (adap->algo->master_xfer) {

if (in_atomic() || irqs_disabled()) {

ret = i2c_trylock_adapter(adap);

if (!ret)

/* I2C activity is ongoing. */

return -EAGAIN;

} else {

i2c_lock_adapter(adap);

}

/* Retry automatically on arbitration loss */

orig_jiffies = jiffies;

for (ret = 0, try = 0; try <= adap->retries; try++) {

ret = adap->algo->master_xfer(adap, msgs, num);

if (ret != -EAGAIN)

break;

if (time_after(jiffies, orig_jiffies + adap->timeout))

break;

}

i2c_unlock_adapter(adap);

return ret;

} else {

dev_dbg(&adap->dev, "I2C level transfers not supported\n");

return -EOPNOTSUPP;

}

}

在這裏,我們看到了adap->algo->master_xfer,也就是底層最終的傳輸函數。

Kernel的IIC驅動分析