{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
status = i2c_check_addr_busy(adap, client->addr);
if( 0x0c == client->addr )
if (status)
{
printk("pei_add_akm1 %d,%x ,%s\n" ,status,info->addr ,__func__ );//pei_add 這裏打印
goto out_err;
}
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);
/* For 10-bit clients, add an arbitrary offset to avoid collisions */
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)
? 0xa000 : 0));
status = device_register(&client->dev);
if (status)
{
printk("pei_add_akm2 %d ,%s ,%s\n" ,status,dev_name(&client->dev),__func__);//pei_add
goto out_err;
}
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
{
struct i2c_client *client = i2c_verify_client(dev);
int addr = *(int *)addrp;
if (client && client->addr == addr) //pei_add
{
printk("pei_add_akm0xxx !!!!!!!!!!!!!!!!!!!!!!!!!!! %s, client->addr=0x%x , %s\n" ,client->name ,client->addr ,__func__);//pei_add
return -EBUSY;
}
return 0;
}
<4>[ 4.018498].(1)[1:swapper/0]pei_add_akm0xxx !!!!!!!!!!!!!!!!!!!!!!!!!!! AD5820AF, client->addr=0xc , __i2c_check_addr_busy
<4>[ 4.018507].(1)[1:swapper/0]pei_add_akm0 akm09911,c ,i2c_new_device
<4>[ 4.018513].(1)[1:swapper/0]pei_add_akm1 -16,c ,i2c_new_device
<4>[ 4.027913].(2)[1:swapper/0]pei_add_akm0xxx !!!!!!!!!!!!!!!!!!!!!!!!!!! kd_camera_hw, client->addr=0x7f , __i2c_check_addr_busy
<4>[ 4.027921].(2)[1:swapper/0]pei_add_akm1 -16,7f ,i2c_new_device
<4>[ 6.424342].(1)[1:swapper/0]pei_add_akm09911_0 akm09911_local_init
<4>[ 6.424355].(1)[1:swapper/0]pei_add_akm09911_1 akm09911_local_init
<4>[ 6.424868].(1)[1:swapper/0]pei_add_akm09911_3 akm09911_local_init
//i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
//i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
//i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));
for (i = 0; i < ARRAY_SIZE(mt_device_i2c); i++){
retval = platform_device_register(&mt_device_i2c[i]);
if (retval != 0){
return retval;
}
}
#endif
{
int ret, irq;
struct mt6573_i2c *i2c = NULL;
/* Request IO memory */
if (!request_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1,
pdev->name)) {
return -EBUSY;
}
if (NULL == (i2c = kzalloc(sizeof(struct mt6573_i2c), GFP_KERNEL)))
return -ENOMEM;
/* initialize mt6573_i2c structure */
irq = pdev->resource[1].start;
i2c->id = pdev->id;
i2c->base = pdev->resource[0].start;
i2c->irqnr = irq;
i2c->clk = I2C_CLK_RATE;
i2c->adap = &mt6573_i2c_adaptor[pdev->id];
i2c->dev = &mt6573_i2c_adaptor[pdev->id].dev;
i2c->adap->dev.parent = &pdev->dev;
if(0 == i2c->id)
i2c->pdmabase = AP_DMA_BASE + 0x0480;
else
i2c->pdmabase = AP_DMA_BASE + 0x0500;
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
ret = request_irq(irq, mt6573_i2c_irq, 0, DRV_NAME, i2c);
if (ret){
dev_err(&pdev->dev, "Can Not request I2C IRQ %d\n", irq);
goto free;
}
mt6573_i2c_init_hw(i2c);
i2c_set_adapdata(i2c->adap, i2c);
ret = i2c_add_adapter(i2c->adap); // adpater 註冊到 i2c core中去,adapter 包含了mtk 寫的adapter的i2c通信方法
if (ret){
dev_err(&pdev->dev, "failed to add i2c bus to i2c core\n");
goto free;
}
platform_set_drvdata(pdev, i2c);
return ret;
free:
mt6573_i2c_free(i2c);
return ret;
}
{
const struct of_device_id *of_id;
int ret = 0;
struct mtk_i2c *i2c;
struct clk *clk;
struct resource *res;
int irq;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c->pdmabase))
return PTR_ERR(i2c->pdmabase);
irq = platform_get_irq(pdev, 0);
if (irq <= 0)
return irq;
init_completion(&i2c->msg_complete);
of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
if (!of_id)
return -EINVAL;
i2c->dev_comp = of_id->data;
i2c->adap.dev.of_node = pdev->dev.of_node;
i2c->dev = &pdev->dev;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &mtk_i2c_algorithm; // mtk adapter 算法填充
i2c->adap.quirks = i2c->dev_comp->quirks;
i2c->adap.timeout = 2 * HZ;
i2c->adap.retries = 1;
ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c);
if (ret)
return -EINVAL;
if (i2c->dev_comp->timing_adjust)
i2c->clk_src_div = I2C_DEFAULT_CLK_DIV;
if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
return -EINVAL;
i2c->clk_main = devm_clk_get(&pdev->dev, "main");
if (IS_ERR(i2c->clk_main)) {
dev_err(&pdev->dev, "cannot get main clock\n");
return PTR_ERR(i2c->clk_main);
}
i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
if (IS_ERR(i2c->clk_dma)) {
dev_err(&pdev->dev, "cannot get dma clock\n");
return PTR_ERR(i2c->clk_dma);
}
clk = i2c->clk_main;
if (i2c->have_pmic) {
i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
if (IS_ERR(i2c->clk_pmic)) {
dev_err(&pdev->dev, "cannot get pmic clock\n");
return PTR_ERR(i2c->clk_pmic);
}
clk = i2c->clk_pmic;
}
strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk));
if (ret) {
dev_err(&pdev->dev, "Failed to set the speed.\n");
return -EINVAL;
}
if (i2c->dev_comp->support_33bits) {
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(33));
if (ret) {
dev_err(&pdev->dev, "dma_set_mask return error.\n");
return ret;
}
}
ret = mtk_i2c_clock_enable(i2c);
if (ret) {
dev_err(&pdev->dev, "clock enable failed!\n");
return ret;
}
mtk_i2c_init_hw(i2c);
mtk_i2c_clock_disable(i2c);
ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
if (ret < 0) {
dev_err(&pdev->dev,
"Request I2C IRQ %d fail\n", irq);
return ret;
}
i2c_set_adapdata(&i2c->adap, i2c);
ret = i2c_add_adapter(&i2c->adap);
if (ret) {
dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n");
return ret;
}
platform_set_drvdata(pdev, i2c);
return 0;
}
.probe = mtk_i2c_probe,
.remove = mtk_i2c_remove,
.driver = {
.name = I2C_DRV_NAME,
.pm = &mtk_i2c_pm,
.of_match_table = of_match_ptr(mtk_i2c_of_match),
},
};
module_platform_driver(mtk_i2c_driver);
{
const struct of_device_id *of_id;
int ret = 0;
struct mtk_i2c *i2c;
struct clk *clk;
struct resource *res;
int irq;
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c->base))
return PTR_ERR(i2c->base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
i2c->pdmabase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c->pdmabase))
return PTR_ERR(i2c->pdmabase);
irq = platform_get_irq(pdev, 0);
if (irq <= 0)
return irq;
init_completion(&i2c->msg_complete);
of_id = of_match_node(mtk_i2c_of_match, pdev->dev.of_node);
if (!of_id)
return -EINVAL;
i2c->dev_comp = of_id->data;
i2c->adap.dev.of_node = pdev->dev.of_node;
i2c->dev = &pdev->dev;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &mtk_i2c_algorithm; // mtk adapter
i2c->adap.quirks = i2c->dev_comp->quirks;
i2c->adap.timeout = 2 * HZ;
i2c->adap.retries = 1;
ret = mtk_i2c_parse_dt(pdev->dev.of_node, i2c);
if (ret)
return -EINVAL;
if (i2c->dev_comp->timing_adjust)
i2c->clk_src_div = I2C_DEFAULT_CLK_DIV;
if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c)
return -EINVAL;
i2c->clk_main = devm_clk_get(&pdev->dev, "main");
if (IS_ERR(i2c->clk_main)) {
dev_err(&pdev->dev, "cannot get main clock\n");
return PTR_ERR(i2c->clk_main);
}
i2c->clk_dma = devm_clk_get(&pdev->dev, "dma");
if (IS_ERR(i2c->clk_dma)) {
dev_err(&pdev->dev, "cannot get dma clock\n");
return PTR_ERR(i2c->clk_dma);
}
clk = i2c->clk_main;
if (i2c->have_pmic) {
i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic");
if (IS_ERR(i2c->clk_pmic)) {
dev_err(&pdev->dev, "cannot get pmic clock\n");
return PTR_ERR(i2c->clk_pmic);
}
clk = i2c->clk_pmic;
}
strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name));
ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk));
if (ret) {
dev_err(&pdev->dev, "Failed to set the speed.\n");
return -EINVAL;
}
if (i2c->dev_comp->support_33bits) {
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(33));
if (ret) {
dev_err(&pdev->dev, "dma_set_mask return error.\n");
return ret;
}
}
ret = mtk_i2c_clock_enable(i2c);
if (ret) {
dev_err(&pdev->dev, "clock enable failed!\n");
return ret;
}
mtk_i2c_init_hw(i2c);
mtk_i2c_clock_disable(i2c);
ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
IRQF_TRIGGER_NONE, I2C_DRV_NAME, i2c);
if (ret < 0) {
dev_err(&pdev->dev,
"Request I2C IRQ %d fail\n", irq);
return ret;
}
i2c_set_adapdata(&i2c->adap, i2c);
ret = i2c_add_adapter(&i2c->adap); // mtk adapter register
if (ret) {
dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n");
return ret;
}
platform_set_drvdata(pdev, i2c);
return 0;
}
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;
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
/* 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; /* i2c_client結構鏈表,該結構包含device,driver和 adapter結構*/
char name[48];
struct completion dev_released;
};
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{
int status;
mutex_lock(&__i2c_board_lock);
/* dynamic bus numbers will be assigned after the last static one */
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); //申請表示i2c設備的結構體空間
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!\n");
status = -ENOMEM;
break;
}
/* 填寫i2c設備描述結構 */
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); //添加到全局鏈表__i2c_board_list中
}
mutex_unlock(&__i2c_board_lock);
return status;
}
第二步:
系統初始化的時候,會根據板級i2c設備配置信息,創建i2c客戶端設備(i2c_client), 添加到i2c子系統中:
static void i2c_scan_static_board_info (struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
mutex_lock(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) { //遍歷全局鏈表__i2c_board_list
if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info))
printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n", i2c_adapter_id(adapter), devinfo->board_info.addr);
}
mutex_unlock(&__i2c_board_lock);
}
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
特別要提一下的是這個“i2c_check_addr”,引用<<i2c 源代碼情景分析>>裏的話:“i2c 設備的7 位地址是就當前i2c 總線而言的,是“相對地址”。
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)
大家知道,對於一個驅動程序有兩個元素不可或缺,即 設備和驅動,一般驅動都是通過 設備名和驅動名的匹配 建立關係的,
2、名字匹配
一個i2c驅動是可以有多個名字的,即一個驅動程序可以支持多個設備,該機制是通過 struct i2c_device_id 實現的,
3、進入probe
該過程困惑了我一段時間,其實要進入自己 驅動的probe首先需要進入總線的probe,而進入總線 probe的前提是與總線的match 成功。
Legacy 的相關知識:
(一)Linux的I2C驅動框架中的主要數據結構及其關係
Linux的I2C驅動框架中的主要數據結構包括:i2c_driver、i2c_client、i2c_adapter和i2c_algorithm。
i2c_adapter對應於物理上的一個適配器,這個適配器是基於不同的平臺的,
i2c_ driver對應一套驅動方法,不對應於任何的物理實體。i2c_client 對應於真實的物理設備,每個I2C設備都需要一個i2c_client 來描述。
(二)Linux的I2C體系結構中三個組成部分的作用
I2C核心提供了一組不依賴於硬件平臺的接口函數,I2C總線驅動和設備驅動之間依賴於I2C核心作爲紐帶。
I2C總線驅動包括I2C適配器驅動加載與卸載以及I2C總線通信方法。
I2C設備驅動主要用於I2C設備驅動模塊加載與卸載以及提供I2C設備驅動文件操作接口。
I2C設備驅動模塊加載與卸載的流程 如圖2 所示。