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_class
	res = 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;
	int			status;

	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);
	} else
		client->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;
}


 




  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux IIC适配器是指在Linux操作系统中用于控制和管理IIC总线设备的驱动程序。在Linux中,IIC适配器可以由CPU控制,也可以直接集成在CPU内部。具体实现上,IIC适配器使用了i2c_adapter结构体,并设置了i2c_algorithm中的master_xfer函数来实现通信方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [适配器 控制器 (iic控制器,iic适配器(软件适配器)) (磁盘控制器,显卡(硬件适配器)) linux下驱动...](https://blog.csdn.net/dianqicyuyan/article/details/121996784)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Linux驱动开发之IIC驱动实验【完整教程】](https://blog.csdn.net/weixin_57037095/article/details/123801283)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Linux IIC 驱动分析](https://blog.csdn.net/db_linux_driver/article/details/122246708)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值