Linux IIC框架(上)
IIC的框架结构和SPI是类似的,它们都拥有总线驱动层(IIC主控制器驱动层),核心层和从设备驱动层。本节主要介绍IIC主控制器的注册以及从设备的注册过程。首先要介绍描述IIC主控制器的结构struct i2c_adapter和描述IIC从设备的结构struct i2c_client
struct i2c_adapter的定义如下:
struct i2c_adapter {struct module *owner; /*所属模块*/unsigned int id; /*algorithm的类型*/unsigned int class; /* classes to allow probing for */const struct i2c_algorithm *algo; /*总线通信方法*/void *algo_data; /*algorithm的数据*//* --- 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;int nr; /*主控制器的编号*/struct list_head clients;/*用于链接从设备的链表头*/char name[48]; /*控制器名*/struct completion dev_released;/*用于同步的完成量*/};algo中定义了主控制器的的数据传输方式,client是一个链表头,由于可能有多个从设备挂接在该总线上,因此client用于链接该控制器下的从设备
和SPI控制器一样,IIC控制器也是平台资源,因此以platform的方式注册进内核
static int __init i2c_adap_s3c_init(void){int ret;ret = platform_driver_register(&s3c2410_i2c_driver);if (ret == 0) {ret = platform_driver_register(&s3c2440_i2c_driver);if (ret)platform_driver_unregister(&s3c2410_i2c_driver);}return ret;}
s3c2410_i2c_driver和s3c2440_i2c_driver的定义除了name字段不一样外,其他部分都一样
static struct platform_driver s3c2410_i2c_driver = {.probe= s3c24xx_i2c_probe,.remove= s3c24xx_i2c_remove,.suspend_late= s3c24xx_i2c_suspend_late,.resume= s3c24xx_i2c_resume,.driver= {.owner= THIS_MODULE,.name= "s3c2410-i2c",},};static struct platform_driver s3c2440_i2c_driver = {.probe= s3c24xx_i2c_probe,.remove= s3c24xx_i2c_remove,.suspend_late= s3c24xx_i2c_suspend_late,.resume= s3c24xx_i2c_resume,.driver= {.owner= THIS_MODULE,.name= "s3c2440-i2c",},};
当和platform_device匹配成功后,便调用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;pdata = pdev->dev.platform_data;//获取平台IIC数据if (!pdata) {dev_err(&pdev->dev, "no platform data\n");return -EINVAL;}/*创建一个struct s3c24xx_i2c*/i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);if (!i2c) {dev_err(&pdev->dev, "no memory for state\n");return -ENOMEM;}/*设置IIC总线的相关项*/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;spin_lock_init(&i2c->lock);init_waitqueue_head(&i2c->wait);//初始化等待队列/* find the clock and enable it */i2c->dev = &pdev->dev;i2c->clk = clk_get(&pdev->dev, "i2c");if (IS_ERR(i2c->clk)) {dev_err(&pdev->dev, "cannot get clock\n");ret = -ENOENT;goto err_noclk;}dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);clk_enable(i2c->clk);/* map the registers *//*获取IIC的资源*/res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (res == NULL) {dev_err(&pdev->dev, "cannot find IO resource\n");ret = -ENOENT;goto err_clk;}/*为IIC的寄存器申请内存空间*/i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1, pdev->name);if (i2c->ioarea == NULL) {dev_err(&pdev->dev, "cannot request IO\n");ret = -ENXIO;goto err_clk;}/*重映射IIC的寄存器*/i2c->regs = ioremap(res->start, (res->end-res->start)+1);if (i2c->regs == NULL) {dev_err(&pdev->dev, "cannot map IO\n");ret = -ENXIO;goto err_ioarea;}dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",i2c->regs, i2c->ioarea, res);/* setup info block for the i2c core */i2c->adap.algo_data = i2c;i2c->adap.dev.parent = &pdev->dev;/* initialise the i2c controller *//*初始化s3c24xx的IIC控制器*/ret = s3c24xx_i2c_init(i2c);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;}/*注册IIC中断*/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;/*通过IIC 核心层函数注册IIC控制器*/ret = i2c_add_numbered_adapter(&i2c->adap);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;}
i2c_add_numbered_adapter()会调用i2c_register_adapter()来完成实际的注册工作
static int i2c_register_adapter(struct i2c_adapter *adap){int res = 0, dummy;/* Can't register until after driver model init */if (unlikely(WARN_ON(!i2c_bus_type.p)))return -EAGAIN;mutex_init(&adap->bus_lock);mutex_init(&adap->clist_lock);INIT_LIST_HEAD(&adap->clients);//初始化主控制器的从设备链表mutex_lock(&core_lock);/* Add the adapter to the driver core. * If the parent pointer is not set up, * we add this adapter to the host bus. */if (adap->dev.parent == NULL) {adap->dev.parent = &platform_bus;pr_debug("I2C adapter driver [%s] forgot to specify " "physical device\n", adap->name);}/* 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.release = &i2c_adapter_dev_release;adap->dev.class = &i2c_adapter_class;//所属类为i2c_adaoter_classres = device_register(&adap->dev);//注册设备if (res)goto out_list;dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);/* create pre-declared device nodes for new-style drivers *//*浏览板级信息注册依附在该控制器下的从设备*/if (adap->nr < __i2c_first_dynamic_bus_num)i2c_scan_static_board_info(adap);/* Notify drivers */dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap, i2c_do_add_adapter);out_unlock:mutex_unlock(&core_lock);return res;out_list:idr_remove(&i2c_adapter_idr, adap->nr);goto out_unlock;}
static void i2c_scan_static_board_info(struct i2c_adapter *adapter){struct i2c_devinfo*devinfo;mutex_lock(&__i2c_board_lock);/*遍历IIC板级信息列表*/list_for_each_entry(devinfo, &__i2c_board_list, list) {/*如果从设备所属的总线号等于IIC控制器的编号则创建新设备*/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);}mutex_unlock(&__i2c_board_lock);}
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){struct i2c_client*client;intstatus;client = kzalloc(sizeof *client, GFP_KERNEL);if (!client)return NULL;client->adapter = adap;//设定从设备所属的IIC控制器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));/* a new style driver may be bound to this device when we * return from this function, or any later moment (e.g. maybe * hotplugging will load the driver module). and the device * refcount model is the standard driver model one. */status = i2c_attach_client(client);if (status < 0) {kfree(client);client = NULL;}return client;}
int i2c_attach_client(struct i2c_client *client){struct i2c_adapter *adapter = client->adapter;int res;/* Check for address business *//*检测该从设备的地址是否和已有从设备的地址相同*/res = i2c_check_addr(adapter, client->addr);if (res)return res;client->dev.parent = &client->adapter->dev;client->dev.bus = &i2c_bus_type;//设置从设备所属的总线类型if (client->driver)client->dev.driver = &client->driver->driver;if (client->driver && !is_newstyle_driver(client->driver)) {client->dev.release = i2c_client_release;dev_set_uevent_suppress(&client->dev, 1);} elseclient->dev.release = i2c_client_dev_release;dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adapter), client->addr);//名字的构成为控制器号+地址res = device_register(&client->dev);//注册设备if (res)goto out_err;mutex_lock(&adapter->clist_lock);list_add_tail(&client->list, &adapter->clients);//将从设备链入IIC控制器mutex_unlock(&adapter->clist_lock);dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",client->name, dev_name(&client->dev));/*如果adapter中定义了从设备注册函数,则进一步调用该函数进行注册,s3c24xx中没定义该函数*/if (adapter->client_register) {if (adapter->client_register(client)) {dev_dbg(&adapter->dev, "client_register ""failed for client [%s] at 0x%02x\n",client->name, client->addr);}}return 0;out_err:dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x ""(%d)\n", client->name, client->addr, res);return res;}