【学习分享】全志平台TWI子系统源码分析(3) 驱动层源码讲解

全志平台TWI子系统源码分析(3)驱动层源码讲解


前言

前两篇文章
【学习分享】全志平台TWI子系统源码分析(1)从设备树到寄存器

【学习分享】全志平台TWI子系统源码分析(2)从应用层到驱动层

学习完成后,大家对于TWI子系统已经有了宏观的认识,下面就进入驱动源码层面的讲解。在《全志平台TWI子系统源码分析(2)从应用层到驱动层》中,我们已经提到关键的几个源码文件,如下图所示:
在这里插入图片描述
就是i2c核心层i2c-core.c,i2c设备驱动i2c-dev.c,i2c控制器i2c-sunxi.c。下面将主要围绕这三个文件讲解。


一、设备驱动i2c-dev.c

这个C文件就是用户态的接口调用文件。一个模块三要素大家应该都懂吧,module_init,module_exit,module_license。所以先翻到c文件最后开始读代码。

MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
		"Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C /dev entries driver");
MODULE_LICENSE("GPL");

module_init(i2c_dev_init);
module_exit(i2c_dev_exit);

所以先查找i2c_dev_init作为起始代码阅读。

static int __init i2c_dev_init(void)
{
	int res;

	printk(KERN_INFO "i2c /dev entries driver\n");

	res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
	//注册字符设备
	if (res)
		goto out;

	i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");//向上提交目录
	if (IS_ERR(i2c_dev_class)) {
		res = PTR_ERR(i2c_dev_class);
		goto out_unreg_chrdev;
	}
	i2c_dev_class->dev_groups = i2c_groups;

	/* Keep track of adapters which will be added or removed later */
	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);//
	if (res)
		goto out_unreg_class;

	/* Bind to already existing adapters right away */
	i2c_for_each_dev(NULL, i2cdev_attach_adapter);

	return 0;

out_unreg_class:
	class_destroy(i2c_dev_class);
out_unreg_chrdev:
	unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
	printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
	return res;
}

i2cdev_notifier_call—>i2cdev_attach_adapter 通过此函数进行i2c设备驱动和控制器相匹配。

static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
	struct i2c_adapter *adap;
	struct i2c_dev *i2c_dev;
	int res;

	if (dev->type != &i2c_adapter_type)
		return 0;
	adap = to_i2c_adapter(dev);

	i2c_dev = get_free_i2c_dev(adap);
	if (IS_ERR(i2c_dev))
		return PTR_ERR(i2c_dev);

	cdev_init(&i2c_dev->cdev, &i2cdev_fops);//操作方法结构体
	i2c_dev->cdev.owner = THIS_MODULE;
	res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
	if (res)
		goto error_cdev;

	/* register this i2c device with the driver core */
	i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
				     MKDEV(I2C_MAJOR, adap->nr), NULL,
				     "i2c-%d", adap->nr);
	if (IS_ERR(i2c_dev->dev)) {
		res = PTR_ERR(i2c_dev->dev);
		goto error;
	}

	pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
		 adap->name, adap->nr);
	return 0;
error:
	cdev_del(&i2c_dev->cdev);
error_cdev:
	put_i2c_dev(i2c_dev);
	return res;
}

一大段代码都是在描述操作方法结构体,也是提供给应用层使用的接口。包括了读写,io系统调用。

static const struct file_operations i2cdev_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.read		= i2cdev_read,
	.write		= i2cdev_write,
	.unlocked_ioctl	= i2cdev_ioctl,
	.open		= i2cdev_open,
	.release	= i2cdev_release,
};

i2cdev_ioctl函数针对不同命令执行相应操作:

I2C_SLAVE和I2C_SLAVE_FORCE:设置I2C设备的从机地址。
I2C_TENBIT:根据arg参数启用或禁用10位地址模式。
I2C_PEC:启用或禁用包错误检查(PEC)。
I2C_FUNCS:获取I2C适配器支持的功能,并将其复制回用户空间。
I2C_RDWR:处理I2C读写操作请求。
I2C_SMBUS:处理SMBus读写操作请求。
I2C_RETRIES:设置I2C操作的重试次数。
I2C_TIMEOUT:设置I2C操作的超时时间。

总的函数流程我已经帮大家列好了,根据如下流程阅读代码就比较方便理解。
在这里插入图片描述
更深入的了解需要自行阅读源代码了。

二、核心层i2c-core.c

这个文件是核心层的接口调用,提供给i2c设备驱动使用的。因为有动态加载和静态加载的模块,所以需要使用导出符号表EXPORT_SYMBOL。

我们只需要掌握几个关键的i2c接口,比如i2c的消息传输,发送和接收。
1、i2c_transfer消息传输函数

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
	int ret;
	if (adap->algo->master_xfer) {
		if (in_atomic() || irqs_disabled()) {    //在消息传输之前检查是否处于原子上下文或IRQ被禁用的状态。
			ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT);//如果是,则尝试锁定i2c总线
			if (!ret)
				/* I2C activity is ongoing. */
				return -EAGAIN;
		} else {
			i2c_lock_bus(adap, I2C_LOCK_SEGMENT);//如果不是,则直接锁定i2c总线
		}

		ret = __i2c_transfer(adap, msgs, num);//执行消息传输。
		i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);

		return ret;
	} else {
		dev_dbg(&adap->dev, "I2C level transfers not supported\n");
		return -EOPNOTSUPP;
	}
}
EXPORT_SYMBOL(i2c_transfer);

这其中的__i2c_transfer函数追踪下,不难发现真正传输使用的是如下接口

adap->algo->master_xfer(adap, msgs, num)

在i2c-sunxi.c中可以找到对应sunxi_i2c_xfer,所以最终会使用到TWI控制器,从这点我们也可以看出,核心层在这里的作用就是封装函数的作用,方便设备驱动层的调用。
具体我们下一节再讲解。

static const struct i2c_algorithm sunxi_i2c_algorithm = {
	.master_xfer	  = sunxi_i2c_xfer,
	.functionality	  = sunxi_i2c_functionality,
};

2、i2c_master_send发送消息函数

int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
{
	int ret;
	struct i2c_adapter *adap = client->adapter;
	struct i2c_msg msg;

	msg.addr = client->addr;//指向当前i2c从设备的实例地址
	msg.flags = client->flags & I2C_M_TEN; //I2C_M_TEN指10-bit地址标志位
	msg.len = count; //要发送的数据长度
	msg.buf = (char *)buf;//要发送的数据

	ret = i2c_transfer(adap, &msg, 1);//使用i2c_transfer函数进行传输

	/*
	 * If everything went ok (i.e. 1 msg transmitted), return #bytes
	 * transmitted, else error code.
	 */
	return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_send);

3、i2c_master_recv消息接收函数

int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
{
	struct i2c_adapter *adap = client->adapter;
	struct i2c_msg msg;
	int ret;

	msg.addr = client->addr;
	msg.flags = client->flags & I2C_M_TEN;
	msg.flags |= I2C_M_RD;//标志位设置为读
	msg.len = count;//要接收的数据长度
	msg.buf = buf;//要接收的数据

	ret = i2c_transfer(adap, &msg, 1);//使用i2c_transfer函数进行传输

	/*
	 * If everything went ok (i.e. 1 msg received), return #bytes received,
	 * else error code.
	 */
	return (ret == 1) ? count : ret;
}
EXPORT_SYMBOL(i2c_master_recv);

三、TWI控制器i2c-sunxi.c

这个文件就是TWI总线驱动层的实现了,涉及到硬件层面的操作,会进行读写寄存器等操作。既然是总线驱动,那么首先肯定会定位到probe函数

static int sunxi_i2c_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct sunxi_i2c *i2c = NULL;
	struct resource *mem_res = NULL;
	struct sunxi_i2c_platform_data *pdata = NULL;
	int ret, irq;
	unsigned int int_flag = 0;
#ifndef CONFIG_SUNXI_REGULATOR_DT
	const char *str_vcc_twi;
#endif
	dma_addr_t phy_addr;

	if (np == NULL) {
		I2C_ERR("I2C failed to get of node\n");
		return -ENODEV;
	}

	i2c = kzalloc(sizeof(struct sunxi_i2c), GFP_KERNEL);//分配sunxi_i2c内存空间
	if (!i2c)
		return -ENOMEM;

	pdata = kzalloc(sizeof(struct sunxi_i2c_platform_data), GFP_KERNEL);//分配平台数据内存空间
	if (pdata == NULL) {
		kfree(i2c);
		return -ENOMEM;
	}
	i2c->dev = &pdev->dev;   
	pdev->dev.platform_data = pdata;
	pdev->dev.driver_data = i2c;

	pdev->id = of_alias_get_id(np, "twi");
	if (pdev->id < 0) {
		I2C_ERR("I2C failed to get alias id\n");
		ret = -EINVAL;
		goto emem;
	}
	pdata->bus_num  = pdev->id;

	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  //资源分配
	if (mem_res == NULL) {
		I2C_ERR("[i2c%d] failed to get MEM res\n", pdev->id);
		ret = -ENXIO;
		goto emem;
	}

	if (!request_mem_region(mem_res->start, resource_size(mem_res),
				mem_res->name))
		I2C_ERR("[i2c%d] failed to request mem region\n", pdev->id);
		ret = -EINVAL;
		goto emem;
	}

	i2c->base_addr = ioremap(mem_res->start, resource_size(mem_res)); {//内存区域映射到驱动地址空间
	if (!i2c->base_addr) {
		ret = -EIO;
		goto eiomap;
	}
	i2c->res = mem_res;

	irq = platform_get_irq(pdev, 0);//申请平台中断号
	if (irq < 0) {
		I2C_ERR("[i2c%d] failed to get irq\n", pdev->id);
		ret = -EINVAL;
		goto eiomap;
	}

	ret = of_property_read_u32(np, "clock-frequency", &pdata->frequency);//时钟频率配置,of_property_read_u32这些数据都是通过设备树获取的。
	if (ret) {
		I2C_ERR("[i2c%d] failed to get clock frequency\n", pdev->id);
		ret = -EINVAL;
		goto eiomap;
	}

#ifndef CONFIG_SUNXI_REGULATOR_DT
	ret = of_property_read_string(np, "twi_regulator", &str_vcc_twi);
	if (ret)
		I2C_ERR("[i2c%d] warning: failed to get regulator id\n",
				pdev->id);
	else if (strlen(str_vcc_twi) >= sizeof(pdata->regulator_id))
		I2C_ERR("[i2c%d] illegal regulator id\n", pdev->id);
	else {
		strcpy(pdata->regulator_id, str_vcc_twi);
		pr_info("[i2c%d] twi_regulator: %s\n", pdev->id,
					pdata->regulator_id);
	}
#endif
	pdev->dev.release = sunxi_i2c_release;
	i2c->adap.owner   = THIS_MODULE;
	i2c->adap.nr      = pdata->bus_num;
	i2c->adap.retries = 3;
	i2c->adap.timeout = 5*HZ;
	i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
	i2c->bus_freq     = pdata->frequency;
	i2c->irq          = irq;
	i2c->bus_num      = pdata->bus_num;
	i2c->status       = I2C_XFER_IDLE;

	dev_set_name(i2c->dev, SUNXI_TWI_DEV_NAME"%u", i2c->adap.nr);
	pdev->name        = dev_name(i2c->dev);
	snprintf(i2c->adap.name, sizeof(i2c->adap.name), dev_name(&pdev->dev));

	spin_lock_init(&i2c->lock);
	init_waitqueue_head(&i2c->wait);

	i2c->mclk = of_clk_get(np, 0);
	if (IS_ERR_OR_NULL(i2c->mclk)) {
		I2C_ERR("[i2c%d] request TWI clock failed\n", i2c->bus_num);
		ret = -EIO;
		goto eremap;
	}

	i2c->adap.algo = &sunxi_i2c_algorithm;//i2c算法配置

#ifndef CONFIG_SUNXI_ARISC
#if (!defined(CONFIG_ARCH_SUN50IW9) && !defined(CONFIG_ARCH_SUN8IW19) \
		&& !defined(CONFIG_ARCH_SUN50IW10))
	/* SUNXI_ARISC will only use twi0, enable gic interrupt when suspend */
	if (i2c->adap.nr == 0)
		int_flag |= IRQF_NO_SUSPEND;
#endif
#endif
	if (of_property_read_u32(np, "no_suspend", &i2c->no_suspend))
		i2c->no_suspend = 0;
	else
		int_flag |= IRQF_NO_SUSPEND;

	ret = request_irq(irq, sunxi_i2c_handler, int_flag,
					i2c->adap.name, i2c);//申请中断handler
	if (ret) {
		I2C_ERR("[i2c%d] requeset irq failed!\n", i2c->bus_num);
		goto ereqirq;
	}

	i2c->adap.algo_data  = i2c;
	i2c->adap.dev.parent = &pdev->dev;
	i2c->adap.dev.of_node = pdev->dev.of_node;

	if (of_property_read_u32(np, "twi_drv_used", &i2c->twi_drv_used))
		i2c->twi_drv_used = 0;
	dprintk(DEBUG_INIT, "[i2c%d] twi_drv_used = %d\n", i2c->bus_num,
			i2c->twi_drv_used);
	if (of_property_read_u32(np, "twi_pkt_interval", &i2c->pkt_interval))
		i2c->pkt_interval = 0;
	dprintk(DEBUG_INIT, "[i2c%d] twi_pkt_interval = %d\n",
			i2c->bus_num, i2c->pkt_interval);

	ret = sunxi_i2c_hw_init(i2c, pdata, &pdev->dev);//硬件初始化
	if (ret != 0) {
		I2C_ERR("[i2c%d] hw init failed!\n", i2c->bus_num);
		if (ret == -EPROBE_DEFER) {
			I2C_ERR("[i2c%d] regulator init try again!\n",
					i2c->bus_num);
		} else
			ret = -EINVAL;
		goto ehwinit;
	}

	pm_runtime_set_active(i2c->dev);
	if (i2c->no_suspend) {
		pm_runtime_get_noresume(i2c->dev);
	}
	pm_runtime_set_autosuspend_delay(i2c->dev, AUTOSUSPEND_TIMEOUT);
	pm_runtime_use_autosuspend(i2c->dev);
	pm_runtime_enable(i2c->dev);

	ret = i2c_add_numbered_adapter(&i2c->adap);
	if (ret < 0) {
		I2C_ERR("[i2c%d] failed to add adapter\n", i2c->bus_num);
		pm_runtime_set_suspended(i2c->dev);
		pm_runtime_disable(i2c->dev);
		goto eadapt;
	}

	phy_addr = (dma_addr_t)mem_res->start;

	/* Init DMA config if supported */
	if (i2c->twi_drv_used)
		sunxi_i2c_dma_request(i2c, phy_addr); //DMA配置

	i2c->pdev = pdev;

	platform_set_drvdata(pdev, i2c); //i2c指针存储在pdev中,可以通过pdev访问i2c设备。

#if (!defined(CONFIG_ARCH_SUN8IW16) && !defined(CONFIG_ARCH_SUN8IW19))
	sunxi_i2c_sysfs(pdev);
#endif

	return 0;

eadapt:
	clk_disable_unprepare(i2c->mclk);

ehwinit:
	free_irq(irq, i2c);

ereqirq:
	iounmap(i2c->base_addr);

eremap:
	if (!IS_ERR_OR_NULL(i2c->mclk)) {
		clk_put(i2c->mclk);
		i2c->mclk = NULL;
	}

eiomap:
	release_mem_region(mem_res->start, resource_size(mem_res));

emem:
	kfree(pdata);
	kfree(i2c);

	return ret;
}

很明显sunxi_i2c_hw_init硬件初始化是比较重要的函数,那么我们来看下它干了哪些事

static int sunxi_i2c_hw_init(struct sunxi_i2c *i2c,
		struct sunxi_i2c_platform_data *pdata, struct device *dev)
{
	int ret = 0;

	ret = twi_regulator_request(pdata, dev);//获取regulatior供电电源
	if (ret < 0) {
		I2C_ERR("[i2c%d] request regulator failed!\n", i2c->bus_num);
		return -EPROBE_DEFER;
	}

	ret = twi_regulator_enable(pdata);//使能供电regulator
	if (ret < 0) {
		I2C_ERR("[i2c%d] enable regulator failed!\n", i2c->bus_num);
		return ret;
	}

	ret = twi_request_gpio(i2c);//获取pin脚
	if (ret < 0) {
		I2C_ERR("[i2c%d] request i2c gpio failed!\n", i2c->bus_num);
		return ret;
	}

	if (sunxi_i2c_clk_init(i2c)) {//时钟初始化
		I2C_ERR("[i2c%d] init i2c clock failed!\n", i2c->bus_num);
		return -1;
	}

	if (!(i2c->twi_drv_used))
		twi_soft_reset(i2c->base_addr, TWI_SRST_REG, TWI_SRST_SRST);//twi软重置

	return ret;
}

那么包括twi的电源、pin脚、时钟、软重置等都已配置完成。
i2c算法配置初始化里面就包括sunxi_i2c_xfer,那么真正起作用的也就是sunxi_i2c_drv_do_xfer

static int
sunxi_i2c_drv_do_xfer(struct sunxi_i2c *i2c, struct i2c_msg *msgs, int num)
{
	int ret;
	unsigned long flags = 0;

	spin_lock_irqsave(&i2c->lock, flags);
	i2c->result = 0;
	spin_unlock_irqrestore(&i2c->lock, flags);

	i2c_drv_clear_pending(i2c->base_addr);
	i2c_drv_disable_tran_irq(TRAN_COM_INT | TRAN_ERR_INT
			| RX_REQ_INT | TX_REQ_INT, i2c->base_addr);
	i2c_drv_disable_dma_irq(DMA_TX | DMA_RX, i2c->base_addr);
	if (i2c_query_txfifo(i2c->base_addr))
		i2c_clear_txfifo(i2c->base_addr);

	if (num == 1) {//单条消息
		if (msgs->flags & I2C_M_RD) {//根据flags来确认是读还是写
			/* 1 msgs read */
			i2c_enable_read_tran_mode(i2c->base_addr);
			i2c_set_packet_addr_byte(0, i2c->base_addr);

			if (i2c->dma_rx && (msgs->len >= DMA_THRESHOLD)) {
				dprintk(DEBUG_INFO, "[i2c%d] master dma read\n",
						i2c->bus_num);
				ret =  sunxi_i2c_drv_dma_read(i2c, msgs, num);
			} else {
				dprintk(DEBUG_INFO, "[i2c%d] master cpu read\n",
						i2c->bus_num);
				ret = sunxi_i2c_drv_read(i2c, msgs, num);
			}
		} else {
			/* 1 msgs write */
			i2c_disable_read_tran_mode(i2c->base_addr);

			if (i2c->dma_tx && (msgs->len >= DMA_THRESHOLD)) {
				dprintk(DEBUG_INFO,
						"[i2c%d] master dma write\n",
						i2c->bus_num);
				ret =  sunxi_i2c_drv_dma_write(i2c, msgs);
			} else {
				dprintk(DEBUG_INFO,
						"[i2c%d] master cpu write\n",
						i2c->bus_num);
				ret = sunxi_i2c_drv_write(i2c, msgs);
			}
		}
	} else if ((num == 2) && ((msgs + 1)->flags & I2C_M_RD)) {//两条消息的读取
		/* 2 msgs read */
		i2c_disable_read_tran_mode(i2c->base_addr);
		i2c_set_packet_addr_byte(msgs->len, i2c->base_addr);

		if (i2c->dma_rx && ((msgs + 1)->len >= DMA_THRESHOLD)) {
			dprintk(DEBUG_INFO, "[i2c%d] master dma read\n",
					i2c->bus_num);
			ret =  sunxi_i2c_drv_dma_read(i2c, msgs, num);
		} else {
			dprintk(DEBUG_INFO, "[i2c%d] master cpu read\n",
					i2c->bus_num);
			ret = sunxi_i2c_drv_read(i2c, msgs, num);
		}
	} else {//多条消息的写入
		/* multiple write with the same format packet */
		i2c_disable_read_tran_mode(i2c->base_addr);
		i2c_set_packet_addr_byte(1, i2c->base_addr);

		if (i2c->dma_tx && ((num * msgs->len) >= DMA_THRESHOLD)) {
			dprintk(DEBUG_INFO,
				"[i2c%d] master dma multiple packet write\n",
				i2c->bus_num);
			ret = sunxi_i2c_drv_dma_mulpk_write(i2c, msgs, num);
		} else {
			dprintk(DEBUG_INFO,
				"[i2c%d] master cpu multiple packet write\n",
				i2c->bus_num);
			ret = sunxi_i2c_drv_mulpk_write(i2c, msgs, num);
		}
	}
	if (ret)
		return ret;

	ret = i2c_sunxi_drv_complete(i2c);//i2c响应完成检测

	if (ret)
		return ret;
	else
		return num;
}

sunxi_i2c_drv_read和sunxi_i2c_drv_write往下毫无疑问就是寄存器层面的操作了。例如sunxi_i2c_drv_read中sunxi_i2c_drv_slave_addr函数,其作用是设置i2c从设备的地址。

static void
sunxi_i2c_drv_slave_addr(struct sunxi_i2c *i2c, struct i2c_msg *msgs)
{
	unsigned int val = 0, cmd = 0;

	/* read, default value is write */
	if (msgs->flags & I2C_M_RD)//判断是读操作还是写操作,默认是写操作。
		cmd = SLV_RD_CMD;

	if (msgs->flags & I2C_M_TEN) {
		/* SLV_ID | CMD | SLV_ID_X */
		val = ((0x78 | ((msgs->addr >> 8) & 0x03)) << 9) | cmd
			| (msgs->addr & 0xff);
		dprintk(DEBUG_INFO2, "10bit addr\n");//如果是10bit地址,则将地址的高两位与0x78按位或,然后左移9位,再加上命令位和地址的低8位。
	} else {
		val = ((msgs->addr & 0x7f) << 9) | cmd;//如果是7bit地址,则左移9位并加上命令位。
		dprintk(DEBUG_INFO2, "7bit addr\n");
	}

	writel(val, i2c->base_addr + TWI_DRIVER_SLV);//将val值写入到TWI_DRIVER_SLV从设备地址寄存器,计算出寄存器的物理地址。
	dprintk(DEBUG_INFO2, "offset: 0x%x value: 0x%x\n", TWI_DRIVER_SLV,
			readl(i2c->base_addr + TWI_DRIVER_SLV));//

}

调用的寄存器可以从规格书中查找到,如果要理解寄存器操作就需要看寄存器的每一位的作用,逻辑上进行位操作。
在这里插入图片描述
struct sunxi_i2c这个数据结构是比较重要的,这个结构包含了执行i2c传输所需的所有信息。

struct sunxi_i2c {
	struct platform_device *pdev;
	int			bus_num;
	unsigned int		status; /* start, running, idle */
	struct i2c_adapter	adap;  
	struct device           *dev;

	spinlock_t		lock; /* syn */
	wait_queue_head_t	wait;
	struct completion	cmd_complete;

	struct i2c_msg		*msg;
	unsigned int		msg_num;
	unsigned int		msg_idx;
	unsigned int		msg_ptr;

	struct clk		*mclk;

	unsigned int		bus_freq;
	int			irq;
	unsigned int		debug_state; /* log the twi machine state */

	struct resource		*res;
	void __iomem		*base_addr;

	struct pinctrl		*pctrl;
	unsigned int		twi_drv_used;
	unsigned int		no_suspend;
	unsigned int		pkt_interval;

	unsigned char		result;
	struct sunxi_i2c_dma	*dma_tx;
	struct sunxi_i2c_dma	*dma_rx;
	struct sunxi_i2c_dma	*dma_using;

};

struct platform_device *pdev:指向与I2C控制器关联的平台设备的指针。
int bus_num:I2C总线的编号。
unsigned int status:表示I2C控制器的状态(如启动、运行、闲置)。
struct i2c_adapter adap:I2C适配器的数据结构,用于接入Linux内核的I2C核心。
struct device *dev:指向I2C控制器设备的指针。
spinlock_t lock:自旋锁,用于同步访问结构体中的数据。
wait_queue_head_t wait:等待队列,用于在异步操作中等待事件的发生。
struct completion cmd_complete:完成变量,用于信号传输操作已完成。
struct i2c_msg *msg:指向正在处理的I2C消息数组的指针。
unsigned int msg_num:消息数组中的消息数量。
unsigned int msg_idx:当前正在处理的消息索引。
unsigned int msg_ptr:当前消息中正在处理的字节位置。
struct clk *mclk:指向I2C控制器时钟的指针。
unsigned int bus_freq:I2C总线频率。
int irq:中断请求(IRQ)号,用于处理I2C事件的中断。
unsigned int debug_state:用于记录I2C控制器状态机状态的调试信息。
struct resource *res:指向I2C控制器资源的指针。
void __iomem *base_addr:I2C控制器寄存器的基础地址,用于内存映射I/O访问。
struct pinctrl *pctrl:指向引脚控制器结构的指针,用于管理I2C总线的引脚。
unsigned int twi_drv_used:标志变量,表明是否使用了TWI (Two-Wire Interface) 驱动程序。
unsigned int no_suspend:防止在某些情况下挂起设备的标志。
unsigned int pkt_interval:数据包间隔时间。
unsigned char result:表示最后一次I2C操作结果的变量。
struct sunxi_i2c_dma *dma_tx:指向I2C DMA传输结构的指针,用于处理发送操作。
struct sunxi_i2c_dma *dma_rx:指向I2C DMA传输结构的指针,用于处理接收操作。
struct sunxi_i2c_dma *dma_using:指向当前正在使用的DMA传输结构的指针。

可以看到的是设置成指针参数的形式struct sunxi_i2c *i2c来调用结构体的配置或者状态。至此驱动层源码讲解就结束了。


总结

本章对I2C驱动层源码进行了详细讲解,如果要深入学习还是得自己静下心来看看代码,会有更多的心得体会。

  • 32
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jason Yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值