OpenNetworkLinux:i2c-gpio.c源码学习笔记

2023-05-16

OpenNetworkLinux:i2c-gpio.c源码学习笔记

i2c-gpio的init和exit

  • i2c驱动需要首先在平台驱动上进行注册,方可提供自身的总线供适配器进行注册,注册流程类似于一个内核模块的注册流程;而平台驱动 - 总线驱动 - 适配器 - 设备的关系类似于树状结构,关系上层层递进;

·i2c_gpio_init函数代码:

static int __init i2c_gpio_init(void)
{
	int ret;

	ret = platform_driver_register(&i2c_gpio_driver);
	if (ret)
		printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);

	return ret;
}
subsys_initcall(i2c_gpio_init);

·i2c_gpio_exit函数代码:

static void __exit i2c_gpio_exit(void)
{
	platform_driver_unregister(&i2c_gpio_driver);
}
module_exit(i2c_gpio_exit);

i2c-gpio的相关结构体声明

  • 若定义了CONFIG_OF宏,则驱动名与i2c_gpio_dt_ids表格中定义的名称相匹配,probe/remove函数指针定义了i2c总线驱动的实例化和移除流程;

·具体的驱动结构体代码如下:

#if defined(CONFIG_OF)
static const struct of_device_id i2c_gpio_dt_ids[] = {
	{ .compatible = "i2c-gpio", },
	{ /* sentinel */ }
};

MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
#endif

static struct platform_driver i2c_gpio_driver = {
	.driver		= {
		.name	= "i2c-gpio",
		.of_match_table	= of_match_ptr(i2c_gpio_dt_ids),
	},
	.probe		= i2c_gpio_probe,
	.remove		= i2c_gpio_remove,
};

·i2c_adapter结构体

/*
 * i2c_adapter is the structure used to identify a physical i2c bus along
 * with the access algorithms necessary to access it.
 */
struct i2c_adapter {
	struct module *owner;
	unsigned int class;		  /* classes to allow probing for */
	const struct i2c_algorithm *algo; /* the algorithm to access the bus */
	void *algo_data;

	/* data fields that are valid for all devices	*/
	const struct i2c_lock_operations *lock_ops;
	struct rt_mutex bus_lock;
	struct rt_mutex mux_lock;

	int timeout;			/* in jiffies */
	int retries;
	struct device dev;		/* the adapter device */

	int nr;
	char name[48];
	struct completion dev_released;

	struct mutex userspace_clients_lock;
	struct list_head userspace_clients;

	struct i2c_bus_recovery_info *bus_recovery_info;
	const struct i2c_adapter_quirks *quirks;
};

·i2c_algo_bit_data结构体

/* --- Defines for bit-adapters ---------------------------------------	*/
/*
 * This struct contains the hw-dependent functions of bit-style adapters to
 * manipulate the line states, and to init any hw-specific features. This is
 * only used if you have more than one hw-type of adapter running.
 */
struct i2c_algo_bit_data {
	void *data;		/* private data for lowlevel routines */
	void (*setsda) (void *data, int state);
	void (*setscl) (void *data, int state);
	int  (*getsda) (void *data);
	int  (*getscl) (void *data);
	int  (*pre_xfer)  (struct i2c_adapter *);
	void (*post_xfer) (struct i2c_adapter *);

	/* local settings */
	int udelay;		/* half clock cycle time in us,
				   minimum 2 us for fast-mode I2C,
				   minimum 5 us for standard-mode I2C and SMBus,
				   maximum 50 us for SMBus */
	int timeout;		/* in jiffies */
};

·i2c_gpio_platform_data结构体

/**
 * struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
 * @sda_pin: GPIO pin ID to use for SDA
 * @scl_pin: GPIO pin ID to use for SCL
 * @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
 * @timeout: clock stretching timeout in jiffies. If the slave keeps
 *	SCL low for longer than this, the transfer will time out.
 * @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
 *	isn't actively driven high when setting the output value high.
 *	gpio_get_value() must return the actual pin state even if the
 *	pin is configured as an output.
 * @scl_is_open_drain: SCL is set up as open drain. Same requirements
 *	as for sda_is_open_drain apply.
 * @scl_is_output_only: SCL output drivers cannot be turned off.
 */
struct i2c_gpio_platform_data {
	unsigned int	sda_pin;
	unsigned int	scl_pin;
	int		udelay;
	int		timeout;
	unsigned int	sda_is_open_drain:1;
	unsigned int	scl_is_open_drain:1;
	unsigned int	scl_is_output_only:1;
};
  • 这个结构体主要描述gpio模拟i2c总线,sda_pin和scl_pin表示使用哪两个IO管脚来模拟I2C总线,udelay和timeout分别为它的时钟频率和超时时间,sda_is_open_drain和scl_is_open_drain表示sda、scl这两个管脚是否是开漏(opendrain)电路【开漏电路就是指以MOSFET的漏极为输出的电路。指内部输出和地之间有个N沟道的MOSFET(Q1),这些器件可以用于电平转换的应用,可以将多个开漏输出的Pin脚,连接到一条线上,形成“与逻辑”关系,即“线与”功能,任意一个变低后,开漏线上的逻辑就为0】,如果是设置为1,scl_is_output_only表示scl这个管脚是否只是作为输出,如果是设置为1。

·i2c_gpio_private_data结构体

struct i2c_gpio_private_data {
	struct i2c_adapter adap;
	struct i2c_algo_bit_data bit_data;
	struct i2c_gpio_platform_data pdata;
};

i2c-gpio的probe和remove

  • 首先通过devm_gpio_request函数获取获取GPIO管脚,为私有变量分配内存空间,并进行赋值。然后对私有变量中的adapter、algo、platform_data成员结构体进行赋值,adap->nr = pdev->id;含义是将适配器的总线id设置为总线驱动号,这里的总线id仅仅是软件上的划分,不是硬件号。

·i2c_gpio_probe函数代码:

static int i2c_gpio_probe(struct platform_device *pdev)
{
	struct i2c_gpio_private_data *priv;
	struct i2c_gpio_platform_data *pdata;
	struct i2c_algo_bit_data *bit_data;
	struct i2c_adapter *adap;
	unsigned int sda_pin, scl_pin;
	int ret;

	/* First get the GPIO pins; if it fails, we'll defer the probe. */
	if (pdev->dev.of_node) {
		ret = of_i2c_gpio_get_pins(pdev->dev.of_node,
					   &sda_pin, &scl_pin);
		if (ret)
			return ret;
	} else {
		if (!dev_get_platdata(&pdev->dev))
			return -ENXIO;
		pdata = dev_get_platdata(&pdev->dev);
		sda_pin = pdata->sda_pin;
		scl_pin = pdata->scl_pin;
	}

	ret = devm_gpio_request(&pdev->dev, sda_pin, "sda");
	if (ret) {
		if (ret == -EINVAL)
			ret = -EPROBE_DEFER;	/* Try again later */
		return ret;
	}
	ret = devm_gpio_request(&pdev->dev, scl_pin, "scl");
	if (ret) {
		if (ret == -EINVAL)
			ret = -EPROBE_DEFER;	/* Try again later */
		return ret;
	}

	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;
	adap = &priv->adap;
	bit_data = &priv->bit_data;
	pdata = &priv->pdata;

	if (pdev->dev.of_node) {
		pdata->sda_pin = sda_pin;
		pdata->scl_pin = scl_pin;
		of_i2c_gpio_get_props(pdev->dev.of_node, pdata);
	} else {
		memcpy(pdata, dev_get_platdata(&pdev->dev), sizeof(*pdata));
	}

	if (pdata->sda_is_open_drain) {
		gpio_direction_output(pdata->sda_pin, 1);
		bit_data->setsda = i2c_gpio_setsda_val;
	} else {
		gpio_direction_input(pdata->sda_pin);
		bit_data->setsda = i2c_gpio_setsda_dir;
	}

	if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
		gpio_direction_output(pdata->scl_pin, 1);
		bit_data->setscl = i2c_gpio_setscl_val;
	} else {
		gpio_direction_input(pdata->scl_pin);
		bit_data->setscl = i2c_gpio_setscl_dir;
	}

	if (!pdata->scl_is_output_only)
		bit_data->getscl = i2c_gpio_getscl;
	bit_data->getsda = i2c_gpio_getsda;

	if (pdata->udelay)
		bit_data->udelay = pdata->udelay;
	else if (pdata->scl_is_output_only)
		bit_data->udelay = 50;			/* 10 kHz */
	else
		bit_data->udelay = 5;			/* 100 kHz */

	if (pdata->timeout)
		bit_data->timeout = pdata->timeout;
	else
		bit_data->timeout = HZ / 10;		/* 100 ms */

	bit_data->data = pdata;

	adap->owner = THIS_MODULE;
	if (pdev->dev.of_node)
		strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
	else
		snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);

	adap->algo_data = bit_data;
	adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
	adap->dev.parent = &pdev->dev;
	adap->dev.of_node = pdev->dev.of_node;

	adap->nr = pdev->id;
	ret = i2c_bit_add_numbered_bus(adap);
	if (ret)
		return ret;

	platform_set_drvdata(pdev, priv);

	dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n",
		 pdata->sda_pin, pdata->scl_pin,
		 pdata->scl_is_output_only
		 ? ", no clock stretching" : "");

	return 0;
}

·i2c_gpio_remove函数代码:

static int i2c_gpio_remove(struct platform_device *pdev)
{
	struct i2c_gpio_private_data *priv;
	struct i2c_adapter *adap;

	priv = platform_get_drvdata(pdev);
	adap = &priv->adap;

	i2c_del_adapter(adap);

	return 0;
}

i2c-gpio适配器的注册

  • i2c-gpio驱动的probe函数中调用了i2c-algo-bit.c中的i2c_bit_add_numbered_bus函数,用于实现一个i2c总线id已确定的适配器的注册。其中,i2c_add_numbered_adapter是在i2c-core.c中实现的一个用于注册i2c总线id已确定的适配器函数。

·i2c_bit_add_numbered_bus函数代码

int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
{
	return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter);
}
EXPORT_SYMBOL(i2c_bit_add_numbered_bus);
  • __i2c_bit_add_bus函数代码
    • 主要完成测试适配器硬件、通过adap->algo = &i2c_bit_algo赋值,将i2c数据传递函数指针赋给适配器、注册适配器工作;
/*
 * registering functions to load algorithms at runtime
 */
static int __i2c_bit_add_bus(struct i2c_adapter *adap,
			     int (*add_adapter)(struct i2c_adapter *))
{
	struct i2c_algo_bit_data *bit_adap = adap->algo_data;
	int ret;

	if (bit_test) {
		ret = test_bus(adap);
		if (bit_test >= 2 && ret < 0)
			return -ENODEV;
	}

	/* register new adapter to i2c module... */
	adap->algo = &i2c_bit_algo;
	adap->retries = 3;
	if (bit_adap->getscl == NULL)
		adap->quirks = &i2c_bit_quirk_no_clk_stretch;

	ret = add_adapter(adap);
	if (ret < 0)
		return ret;

	/* Complain if SCL can't be read */
	if (bit_adap->getscl == NULL) {
		dev_warn(&adap->dev, "Not I2C compliant: can't read SCL\n");
		dev_warn(&adap->dev, "Bus may be unreliable\n");
	}
	return 0;
}
  • i2c_bit_algo结构体
/* -----exported algorithm data: -------------------------------------	*/

const struct i2c_algorithm i2c_bit_algo = {
	.master_xfer	= bit_xfer,
	.functionality	= bit_func,
};
EXPORT_SYMBOL(i2c_bit_algo);
  • test_bus函数代码(适配器硬件的完整性检查,仅在总线近似空闲时测试总线的SDA和SCL线)
/*
 * Sanity check for the adapter hardware - check the reaction of
 * the bus lines only if it seems to be idle.
 */
static int test_bus(struct i2c_adapter *i2c_adap)
{
	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
	const char *name = i2c_adap->name;
	int scl, sda, ret;

	if (adap->pre_xfer) {
		ret = adap->pre_xfer(i2c_adap);
		if (ret < 0)
			return -ENODEV;
	}

	if (adap->getscl == NULL)
		pr_info("%s: Testing SDA only, SCL is not readable\n", name);

	sda = getsda(adap);
	scl = (adap->getscl == NULL) ? 1 : getscl(adap);
	if (!scl || !sda) {
		printk(KERN_WARNING
		       "%s: bus seems to be busy (scl=%d, sda=%d)\n",
		       name, scl, sda);
		goto bailout;
	}

	sdalo(adap);
	sda = getsda(adap);
	scl = (adap->getscl == NULL) ? 1 : getscl(adap);
	if (sda) {
		printk(KERN_WARNING "%s: SDA stuck high!\n", name);
		goto bailout;
	}
	if (!scl) {
		printk(KERN_WARNING "%s: SCL unexpected low "
		       "while pulling SDA low!\n", name);
		goto bailout;
	}

	sdahi(adap);
	sda = getsda(adap);
	scl = (adap->getscl == NULL) ? 1 : getscl(adap);
	if (!sda) {
		printk(KERN_WARNING "%s: SDA stuck low!\n", name);
		goto bailout;
	}
	if (!scl) {
		printk(KERN_WARNING "%s: SCL unexpected low "
		       "while pulling SDA high!\n", name);
		goto bailout;
	}

	scllo(adap);
	sda = getsda(adap);
	scl = (adap->getscl == NULL) ? 0 : getscl(adap);
	if (scl) {
		printk(KERN_WARNING "%s: SCL stuck high!\n", name);
		goto bailout;
	}
	if (!sda) {
		printk(KERN_WARNING "%s: SDA unexpected low "
		       "while pulling SCL low!\n", name);
		goto bailout;
	}

	sclhi(adap);
	sda = getsda(adap);
	scl = (adap->getscl == NULL) ? 1 : getscl(adap);
	if (!scl) {
		printk(KERN_WARNING "%s: SCL stuck low!\n", name);
		goto bailout;
	}
	if (!sda) {
		printk(KERN_WARNING "%s: SDA unexpected low "
		       "while pulling SCL high!\n", name);
		goto bailout;
	}

	if (adap->post_xfer)
		adap->post_xfer(i2c_adap);

	pr_info("%s: Test OK\n", name);
	return 0;
bailout:
	sdahi(adap);
	sclhi(adap);

	if (adap->post_xfer)
		adap->post_xfer(i2c_adap);

	return -ENODEV;
}

·i2c_add_numbered_adapter函数代码

This routine is used to declare an I2C adapter when its bus number matters. For example, use it for I2C adapters from system-on-chip CPUs, or otherwise built in to the system’s mainboard, and where i2c_board_info is used to properly configure I2C devices. If the requested bus number is set to -1, then this function will behave identically to i2c_add_adapter, and will dynamically assign a bus number. If no devices have pre-been declared for this bus, then be sure to register the adapter before any dynamically allocated ones. Otherwise the required bus ID may not be available. When this returns zero, the specified adapter became available for clients using the bus number provided in adap->nr. Also, the table of I2C devices pre-declared using i2c_register_board_info() is scanned, and the appropriate driver model device nodes are created. Otherwise, a negative errno value is returned.

  • 本例程用于通过指定的总线号声明I2C适配器,当传入的总线号为-1时,此函数的行为将与i2c_add_adapter函数一致,并且给适配器动态分配一个总线号;

  • 如果尚未为总线分配任何设备,则必须保证设备在动态分配之前,先注册适配器,否则可能无法使用指定的总线号;

  • 指定的适配器可以使用adap->nr中提供的总线号供clients使用,使用i2c_register_board_info()函数预声明的I2C设备表将会被扫描并且分配合适的设备驱动节点,若执行成功则最终返回0,如果上述执行不成功,返回errno;

  • i2c_add_numbered_adapter函数代码清单:

    int i2c_add_numbered_adapter(struct i2c_adapter *adap)
    {
    	if (adap->nr == -1) /* -1 means dynamically assign bus id */
    		return i2c_add_adapter(adap);
    
    	return __i2c_add_numbered_adapter(adap);
    }
    EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
    
  • i2c_add_adapter函数代码:

    int i2c_add_adapter(struct i2c_adapter *adapter)
    {
    	struct device *dev = &adapter->dev;
    	int id;
    
    	if (dev->of_node) {															/* check if the dev_node is previously allocated. */
    		id = of_alias_get_id(dev->of_node, "i2c");
    		if (id >= 0) {
    			adapter->nr = id;
    			return __i2c_add_numbered_adapter(adapter);
    		}
    	}                                    
      
    	mutex_lock(&core_lock);																	/* no device in assigned bus, alloc for the adapter firstly */
    	id = idr_alloc(&i2c_adapter_idr, adapter,
    		       __i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
    	mutex_unlock(&core_lock);
    	if (WARN(id < 0, "couldn't get idr"))
    		return id;
    
    	adapter->nr = id;
    
    	return i2c_register_adapter(adapter);
    }
    EXPORT_SYMBOL(i2c_add_adapter);
    
  • __i2c_add_numbered_adapter函数代码:

    static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
    {
    	int id;
    
    	mutex_lock(&core_lock);
    	id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL);
    	mutex_unlock(&core_lock);
    	if (WARN(id < 0, "couldn't get idr"))
    		return id == -ENOSPC ? -EBUSY : id;
    
    	return i2c_register_adapter(adap);
    }
    
  • i2c_register_adapter函数代码:

    static int i2c_register_adapter(struct i2c_adapter *adap)
    {
    	int res = -EINVAL;
    
    	/* Can't register until after driver model init */
    	if (WARN_ON(!is_registered)) {
    		res = -EAGAIN;
    		goto out_list;
    	}
    
    	/* Sanity checks */
    	if (WARN(!adap->name[0], "i2c adapter has no name"))
    		goto out_list;
    
    	if (!adap->algo) {
    		pr_err("adapter '%s': no algo supplied!\n", adap->name);
    		goto out_list;
    	}
    
    	if (!adap->lock_ops)
    		adap->lock_ops = &i2c_adapter_lock_ops;
    
    	rt_mutex_init(&adap->bus_lock);
    	rt_mutex_init(&adap->mux_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) {
    		pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
    		goto out_list;
    	}
    
    	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
    
    	pm_runtime_no_callbacks(&adap->dev);
    	pm_suspend_ignore_children(&adap->dev, true);
    	pm_runtime_enable(&adap->dev);
    
    #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_init_recovery(adap);  										/* reinit SCL gpio value function */
    
    	/* create pre-declared device nodes */
    	of_i2c_register_devices(adap);
    	i2c_acpi_register_devices(adap);
    	i2c_acpi_install_space_handler(adap);
    
    	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;
    }
    

i2c-gpio适配器的数据传输方法

  • i2c-algo-bit.c中提供了bit_xfer/bit_func作为i2c数据传输的通用实现函数,在i2c-gpio适配器驱动中,同样也是使用其实现方法。

·bit_xfer函数代码

static int bit_xfer(struct i2c_adapter *i2c_adap,
		    struct i2c_msg msgs[], int num)
{
	struct i2c_msg *pmsg;
	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
	int i, ret;
	unsigned short nak_ok;

	if (adap->pre_xfer) {
		ret = adap->pre_xfer(i2c_adap);
		if (ret < 0)
			return ret;
	}

	bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
	i2c_start(adap);
	for (i = 0; i < num; i++) {
		pmsg = &msgs[i];
		nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;			/* 忽略应答信号标志 */
		if (!(pmsg->flags & I2C_M_NOSTART)) {							/* 无起始信号标志 */
			if (i) {
				bit_dbg(3, &i2c_adap->dev, "emitting "
					"repeated start condition\n");
				i2c_repstart(adap);
			}
			ret = bit_doAddress(i2c_adap, pmsg);
			if ((ret != 0) && !nak_ok) {
				bit_dbg(1, &i2c_adap->dev, "NAK from "
					"device addr 0x%02x msg #%d\n",
					msgs[i].addr, i);
				goto bailout;
			}
		}
		if (pmsg->flags & I2C_M_RD) {													/* 读标志 */
			/* read bytes into buffer*/
			ret = readbytes(i2c_adap, pmsg);
			if (ret >= 1)
				bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n",
					ret, ret == 1 ? "" : "s");
			if (ret < pmsg->len) {
				if (ret >= 0)
					ret = -EIO;
				goto bailout;
			}
		} else {																					/* 写标志 */
			/* write bytes from buffer */
			ret = sendbytes(i2c_adap, pmsg);
			if (ret >= 1)
				bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n",
					ret, ret == 1 ? "" : "s");
			if (ret < pmsg->len) {
				if (ret >= 0)
					ret = -EIO;
				goto bailout;
			}
		}
	}
	ret = i;

bailout:
	bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
	i2c_stop(adap);

	if (adap->post_xfer)
		adap->post_xfer(i2c_adap);
	return ret;
}

1. i2c_start(adap):适配器发送一个I2C起始信号、i2c_stop(adap):适配器发送一个I2C终止信号;

在 I2C 总线中,唯一出现的是被定义为起始 S 和停止P 条件的情况,其中一种情况是在 SCL 线是高电平时,SDA 线从高电平向低电平切换 这个情况表示起始条件;当 SCL 是高电平时,SDA 线由低电平向高电平切换表示停止条件。起始和停止条件一般由主机产生,总线在起始条件后被认为处于忙的状态,在停止条件的某段时间后,总线被认为再次处于空闲状态。

起始与终止条件

  • 具体的函数代码实现如下:

    static inline void sdalo(struct i2c_algo_bit_data *adap)
    {
    	setsda(adap, 0);
    	udelay((adap->udelay + 1) / 2);
    }
    
    static inline void sdahi(struct i2c_algo_bit_data *adap)
    {
    	setsda(adap, 1);
    	udelay((adap->udelay + 1) / 2);
    }
    
    static inline void scllo(struct i2c_algo_bit_data *adap)
    {
    	setscl(adap, 0);
    	udelay(adap->udelay / 2);
    }
    
    /*
     * Raise scl line, and do checking for delays. This is necessary for slower
     * devices.
     */
    static int sclhi(struct i2c_algo_bit_data *adap)
    {
    	unsigned long start;
    
    	setscl(adap, 1);
    
    	/* Not all adapters have scl sense line... */
    	if (!adap->getscl)
    		goto done;
    
    	start = jiffies;
    	while (!getscl(adap)) {
    		/* This hw knows how to read the clock line, so we wait
    		 * until it actually gets high.  This is safer as some
    		 * chips may hold it low ("clock stretching") while they
    		 * are processing data internally.
    		 */
            /*
            * 该硬件知道如何读取时钟线,因此我们等到它实际变高。 
            * 这样比较安全,因为某些芯片在内部处理数据时可能会
            * 将其保持在低电平(“时钟延长”)
            */
    		if (time_after(jiffies, start + adap->timeout)) {
    			/* Test one last time, as we may have been preempted
    			 * between last check and timeout test.
    			 */
    			if (getscl(adap))
    				break;
    			return -ETIMEDOUT;
    		}
    		cpu_relax();
    	}
    #ifdef DEBUG
    	if (jiffies != start && i2c_debug >= 3)
    		pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go "
    			 "high\n", jiffies - start);
    #endif
    
    done:
    	udelay(adap->udelay);
    	return 0;
    }
    
    /* --- other auxiliary functions --------------------------------------	*/
    static void i2c_start(struct i2c_algo_bit_data *adap)
    {
    	/* assert: scl, sda are high */
    	setsda(adap, 0);						/* set sda to low level */
    	udelay(adap->udelay);			/* wait for slave to acknowledge */
    	scllo(adap);								/* set scl to low level and delay */								
    }
    
    static void i2c_stop(struct i2c_algo_bit_data *adap)
    {
    	/* assert: scl is low */
    	sdalo(adap);							/* set sda to low level and delay */				
    	sclhi(adap);							/* set scl to high level and delay */				
    	setsda(adap, 1);					/* set sda to high level */
    	udelay(adap->udelay);		/* wait for slave to acknowledge */
    }
    

2.for循环处理从机地址字节发送、数据传输过程

  • 相关宏定义及其含义:

    I2C_M_TEN表示10位设备地址
    I2C_M_RD读标志
    I2C_M_NOSTART无起始信号标志
    I2C_M_IGNORE_NAK忽略应答信号标志
    
  • 首先解析是否对ack应答、以及是否为重复开始标志;

  • 通过解析寻址规则,查找从i2c设备,具体函数实现如doAddress,它通过生成开始条件(在try_address中)来启动传输(将地址字节发送出去),并以必要的格式传输地址以处理读取和写入字节(8位及10位地址)。返回值:0正常,从设备发送ack,或设置了IGNORE_NAK标志-x发生错误(例如:-ENXIO,设备未应答,或-ETIMEDOUT,例如,线路阻塞):

    /* doAddress initiates the transfer by generating the start condition (in
     * try_address) and transmits the address in the necessary format to handle
     * reads, writes as well as 10bit-addresses.
     * returns:
     *  0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set
     * -x an error occurred (like: -ENXIO if the device did not answer, or
     *	-ETIMEDOUT, for example if the lines are stuck...)
     */
    static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
    {
    	unsigned short flags = msg->flags;
    	unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
    	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
    	unsigned char addr;
    	int ret, retries;
    
    	retries = nak_ok ? 0 : i2c_adap->retries;
    
    	if (flags & I2C_M_TEN) {
    		/* a ten bit address */
    		addr = 0xf0 | ((msg->addr >> 7) & 0x06);
    		bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr);
    		/* try extended address code...*/
    		ret = try_address(i2c_adap, addr, retries);
    		if ((ret != 1) && !nak_ok)  {
    			dev_err(&i2c_adap->dev,
    				"died at extended address code\n");
    			return -ENXIO;
    		}
    		/* the remaining 8 bit address */
    		ret = i2c_outb(i2c_adap, msg->addr & 0xff);
    		if ((ret != 1) && !nak_ok) {
    			/* the chip did not ack / xmission error occurred */
    			dev_err(&i2c_adap->dev, "died at 2nd address code\n");
    			return -ENXIO;
    		}
    		if (flags & I2C_M_RD) {
    			bit_dbg(3, &i2c_adap->dev, "emitting repeated "
    				"start condition\n");
    			i2c_repstart(adap);
    			/* okay, now switch into reading mode */
    			addr |= 0x01;
    			ret = try_address(i2c_adap, addr, retries);
    			if ((ret != 1) && !nak_ok) {
    				dev_err(&i2c_adap->dev,
    					"died at repeated address code\n");
    				return -EIO;
    			}
    		}
    	} else {		/* normal 7bit address	*/
    		addr = msg->addr << 1;
    		if (flags & I2C_M_RD)
    			addr |= 1;
    		if (flags & I2C_M_REV_DIR_ADDR)
    			addr ^= 1;
    		ret = try_address(i2c_adap, addr, retries);
    		if ((ret != 1) && !nak_ok)
    			return -ENXIO;
    	}
    
    	return 0;
    }
    
  • tryAddress函数向芯片发送一个地址字节,用于匹配i2c从机,retries代表它的失败重试次数:

    /* try_address tries to contact a chip for a number of
     * times before it gives up.
     * return values:
     * 1 chip answered
     * 0 chip did not answer
     * -x transmission error
     */
    static int try_address(struct i2c_adapter *i2c_adap,
    		       unsigned char addr, int retries)
    {
    	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    	int i, ret = 0;
    
    	for (i = 0; i <= retries; i++) {
    		ret = i2c_outb(i2c_adap, addr);
    		if (ret == 1 || i == retries)
    			break;
    		bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
    		i2c_stop(adap);
    		udelay(adap->udelay);
    		yield();
    		bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
    		i2c_start(adap);
    	}
    	if (i && ret)
    		bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at "
    			"0x%02x: %s\n", i + 1,
    			addr & 1 ? "read from" : "write to", addr >> 1,
    			ret == 1 ? "success" : "failed, timeout?");
    	return ret;
    }
    
  • i2c_outb函数是实现tryAddress函数进行地址字节发送的关键,它将一个字节进行发送,并对ack进行检查,具体实现如下:

    /* send a byte without start cond., look for arbitration,
       check ackn. from slave */
    /* returns:
     * 1 if the device acknowledged
     * 0 if the device did not ack
     * -ETIMEDOUT if an error occurred (while raising the scl line)
     */
    static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
    {
    	int i;
    	int sb;
    	int ack;
    	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
    	/* assert: scl is low */
    	for (i = 7; i >= 0; i--) {
    		sb = (c >> i) & 1;
    		setsda(adap, sb);
    		udelay((adap->udelay + 1) / 2);
    		if (sclhi(adap) < 0) { /* timed out */
    			bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
    				"timeout at bit #%d\n", (int)c, i);
    			return -ETIMEDOUT;
    		}
    		/* FIXME do arbitration here:
    		 * if (sb && !getsda(adap)) -> ouch! Get out of here.
    		 *
    		 * Report a unique code, so higher level code can retry
    		 * the whole (combined) message and *NOT* issue STOP.
    		 */
    		scllo(adap);
    	}
    	sdahi(adap);
    	if (sclhi(adap) < 0) { /* pull high timeout, maybe slave is handling interupter event */
    		bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
    			"timeout at ack\n", (int)c);
    		return -ETIMEDOUT;
    	}
    
    	/* read ack: SDA should be pulled down by slave, or it may
    	 * NAK (usually to report problems with the data we wrote).
    	 */
    	ack = !getsda(adap);    /* ack: sda is pulled low -> success */
    	bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,
    		ack ? "A" : "NA");
    
    	scllo(adap);
    	return ack;
    	/* assert: scl is low (sda undef) */
    }
    
    • 首先是发送字节数据的最高位,在SCL为高电平期间(gpio值为1)将一位数据发送出去,最后是发送字节数据的最低位。发送完成之后,需将SDA值拉高,并读取SDA线状态(SDA的gpio值),若被从机拉低,则代表发送成功,最后将SCL拉低,还原至主机待发送状态,至此一个字节的发送周期结束。
  • 数据传输过程,如果是读则调用readbytes函数去读数据(主处理函数为i2c_inb),如果是写则调用sendbytes(主处理函数为i2c_outb)去写数据,代码具体实现如下:

    static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
    {
    	const unsigned char *temp = msg->buf;					/* sending content buffer */
    	int count = msg->len;														/* total content length */
    	unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
    	int retval;
    	int wrcount = 0;
    
    	while (count > 0) {
    		retval = i2c_outb(i2c_adap, *temp);
    
    		/* OK/ACK; or ignored NAK */
    		if ((retval > 0) || (nak_ok && (retval == 0))) {
    			count--;											/* remaining content minus one byte */
    			temp++;											/* shift to the next byte preparing for sending */
    			wrcount++;									/*Total writing bytes increases */
    
    		/* A slave NAKing the master means the slave didn't like
    		 * something about the data it saw.  For example, maybe
    		 * the SMBus PEC was wrong.
    		 */
    		} else if (retval == 0) {
    			dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n");
    			return -EIO;
    
    		/* Timeout; or (someday) lost arbitration
    		 *
    		 * FIXME Lost ARB implies retrying the transaction from
    		 * the first message, after the "winning" master issues
    		 * its STOP.  As a rule, upper layer code has no reason
    		 * to know or care about this ... it is *NOT* an error.
    		 */
    		} else {
    			dev_err(&i2c_adap->dev, "sendbytes: error %d\n",
    					retval);
    			return retval;
    		}
    	}
    	return wrcount;
    }
    
    /* 读n个字节 */
    static int i2c_inb(struct i2c_adapter *i2c_adap)
    {
    	/* read byte via i2c port, without start/stop sequence	*/
    	/* acknowledge is sent in i2c_read.			*/
    	int i;
    	unsigned char indata = 0;
    	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
    	/* assert: scl is low */
    	sdahi(adap);
    	for (i = 0; i < 8; i++) {
    		if (sclhi(adap) < 0) { /* timeout */
    			bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit "
    				"#%d\n", 7 - i);
    			return -ETIMEDOUT;
    		}
    		indata *= 2;
    		if (getsda(adap))
    			indata |= 0x01;
    		setscl(adap, 0);
    		udelay(i == 7 ? adap->udelay / 2 : adap->udelay);
    	}
    	/* assert: scl is low */
    	return indata;
    }
    
    /*
    * ACK 英文缩写: ACKnowledge Character
    * 在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。 
    * NAK 英文缩写: Negative Acknowledgment
    * 否定应答或者非应答的缩写。
    */
    static int acknak(struct i2c_adapter *i2c_adap, int is_ack)
    {
    	struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
    
    	/* assert: sda is high */
    	if (is_ack)		/* send ack */
    		setsda(adap, 0);
    	udelay((adap->udelay + 1) / 2);
    	if (sclhi(adap) < 0) {	/* timeout */
    		dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n");
    		return -ETIMEDOUT;
    	}
    	scllo(adap);
    	return 0;
    }
    
    static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
    {
    	int inval;
    	int rdcount = 0;	/* counts bytes read */
    	unsigned char *temp = msg->buf;
    	int count = msg->len;
    	const unsigned flags = msg->flags;
    
    	while (count > 0) {
    		inval = i2c_inb(i2c_adap);
    		if (inval >= 0) {
    			*temp = inval;
    			rdcount++;
    		} else {   /* read timed out */
    			break;
    		}
    
    		temp++;
    		count--;
    
    		/* Some SMBus transactions require that we receive the
    		   transaction length as the first read byte. */
    		if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) {
    			if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) {
    				if (!(flags & I2C_M_NO_RD_ACK))
    					acknak(i2c_adap, 0);
    				dev_err(&i2c_adap->dev, "readbytes: invalid "
    					"block length (%d)\n", inval);
    				return -EPROTO;
    			}
    			/* The original count value accounts for the extra
    			   bytes, that is, either 1 for a regular transaction,
    			   or 2 for a PEC transaction. */
    			count += inval;
    			msg->len += inval;
    		}
    
    		bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n",
    			inval,
    			(flags & I2C_M_NO_RD_ACK)
    				? "(no ack/nak)"
    				: (count ? "A" : "NA"));
    
    		if (!(flags & I2C_M_NO_RD_ACK)) {
    			inval = acknak(i2c_adap, count);
    			if (inval < 0)
    				return inval;
    		}
    	}
    	return rdcount;
    }
    
    • i2c_outb函数首先从字节最高位开始发送数据,每发送一位数据,尝试调高SCL线,若成功表示从机成功收到,则拉低SCL线,再次重复发送,直至8bit数据均成功发送,第九个bit,主机先分别拉高SDA线和SCL线,然后从机若拉低了SDA线,则代表一个ACK,表示该字节从机接收成功;i2c_inb函数则由字节最高位开始读,对SDA读取得到的数据进行或计算,并且乘2进行下一位读取,直至读完8位数据,是否发送ACK由read_bytes函数决定(对数据进行校验后确定)。原理图如下:

数据传输过程

·bit_func函数代码

static u32 bit_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
	       I2C_FUNC_SMBUS_READ_BLOCK_DATA |
	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
	       I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenNetworkLinux:i2c-gpio.c源码学习笔记 的相关文章

  • 品味树莓派:GPIO口定义、电气特性、启动状态等基础说明

    文章目录 目录 GPIO口定义 电气特性 启动状态 总结 目录 树莓派相比成品化的电脑来说有很多独立的GPIO口可供开发使用 可以方便的实现很多实物相关的功能 本文将对树莓派开放给用户的GPIO口的针脚定义 电气特性 启动状态等基础内容做个
  • 【STM32F4】二、I/O引脚的复用和映射

    目录 一 基本概念 1 什么是I O引脚 2 什么是I O引脚的复用 二 如何配置I O引脚复用 1 复用器 GPIOx AFRL GPIOx AFRH 和复用功能 AF 2 程序编写 2 1 打开I O时钟和复用功能对应外设时钟 2 2
  • 新唐NUC980使用记录(5.10.y内核):在用户应用中使用GPIO

    文章目录 目的 使用参考与演示 使用参考 存在的问题 问题定位 修改设备树 使用测试 总结 设备树文件内容 目的 GPIO是最基础的外设 使用频率也非常高 这篇文章将简单体验在NUC980 Liunx用户应用中使用GPIO功能 这篇文章中内
  • 【总线】I2C 通信协议

    目录 I2C 总线协议概述 参数总结 I2C 的工作原理 寻址 读 写位 数据帧 I2C数据传输的步骤 具有多个从机的单个主机 具有多个从机的多个主机 I2C的优缺点 优点 缺点 文章参考 I2C 总线协议概述 I2C 总线广泛应用在 OL
  • MC9S12XEP100引脚的复用方式

    嵌入式码农小明最近在研发一个嵌入式产品 用的MC9S12XEP100芯片 其中三个需求需要通过几个使用SPI通讯的芯片实现 硬件开发人员小红照着芯片原理图布好了线 不知是否是没考虑到SPI通讯可以通过CS line来控制要通讯的从机从而实现
  • GPIO模拟脉冲驱动步进电机

    引脚 PUL lt gt VCC 3 3v PUL lt gt PA6 DIR lt gt VCC 3 3v DIR lt gt PB12 ENA lt gt VCC 3 3v ENA lt gt PB15 按键 控制使能 PE6 控制方向
  • wiringPi引脚编号方式

    树莓派引出的20 2排针引脚 引脚定义使用gpio readall命令查看 如下 可以看到wiringpi库有三种引脚编号方式 分别为 BCM编号方式 就是使用芯片的GPIO引脚编号 wiringpi库编号方式 使用wiringpi库自己规
  • RK3568-GPIO控制

    RK3568 GPIO控制 1 Sysfs接口 实现逻辑 芯片的GPIO由芯片的GPIO控制器来管理 GPIO控制器封装在芯片内部 控制器的驱动芯片厂家已经写好了 RK3568有五组GPIO控制器 每组管理32个引脚 对应 dev下的gpi
  • CORE-ESP32C3

    目录 参考博文 源于网友oled eink aht10项目 源代码修改及复现说明 主要修改 显示效果 编辑硬件准备 软件版本 日志及soc下载工具 软件使用 接线说明 天气显示屏 硬件接线 温度采集 日期温度显示屏 正常初始化LOG 示例代
  • I2C接口

    I2C的结构和特点 他是一具有两条总线线路 即一条串行数据线SDA和一条串行时钟线SCL 每个连接到总线上的器件都可以通过唯一的地址联系主机 它是一个真正的多主机总线 数据传输通过冲突检测和仲裁防止数据被破坏 串行的8位双向数据传输位速率更
  • Linux:从用户空间实例化:eeprom new_device

    环境 x86 Ubuntu 14 04 我想获得类似的东西 i2c0 eeprom eeprom 50 compatible at 24c32 reg lt 0x50 gt 但因为在 x86 中没有可用的设备树 所以我遵循i2c insta
  • 如何在 QEMU x86 上模拟 i2c 设备?

    我正在研究 QEMU 1 5 1 6 但还没有看到任何在 i2c 总线上添加设备的文档 有人可以帮忙吗 Thanks 好吧 没人对这个问题感兴趣 我发布我自己的解决方案 由于 QEMU 不支持 I2C 总线级数据传输 因此在将多点触摸数据从
  • Raspberry pi 4 用 java 控制 GPIO

    我想用java控制我的树莓派4上的16 2液晶显示屏 问题是Pi4J 用java修改gpios的解决方案没有更新到pi4 还有其他解决方案吗 当我启动程序时出现此错误 pi raspberrypi desktop gpio sudo sta
  • HAL 锁定和解锁函数如何使用以及为什么?

    我试图理解另一位程序员编写的代码 它使用了I C http en wikipedia org wiki I C2 B2C通信以将数据写入 STM32 微控制器的 EEPROM 一般来说 我理解他的代码是如何工作的 但我不明白他为什么使用HA
  • docker 容器内的 I2C

    我正在尝试在 docker 容器内的树莓派上使用 i2c 引脚 我使用 RUN 安装所有模块 但是当我使用 CMD 运行我的 python 程序时 我收到一条错误消息 Trackback most recent call last file
  • 在 U-Boot 中使用 I2C 读取多个字节

    我的 Freescale p1022tw 板的 I2C 驱动程序有问题 U Boot 的控制台上有一个从 I2C 设备读取的命令 i2c md chip address 0 1 2 of objects 当我从 id 为 0x60 地址为
  • 如何检查您的内核是否支持硬件上的 GPIO?

    我的目标是控制 Intel 主板 带 C1037U 处理器的 NM70 芯片组 上 Peppermint 4 Linux 内核版本 3 8 0 中的 GPIO 引脚 如何检查您的内核是否支持硬件上的 GPIO 背景 主板 Intel NM7
  • Python使用sudo启动时找不到模块

    我有一个使用 Google Assistant 库的脚本 并且必须从那里导入一些模块 我发现这只适用于 Python 虚拟环境 这真的很奇怪 在同一个文件夹中 我有一个使用 GPIO 引脚并且必须使用 root 的脚本 它们相互交互 因此当
  • ESP8266 I2C从机不确认数据

    我有一个 TM4C123 处理器作为 I2C 主处理器 一个 ESP8266 作为从处理器 对于 ESP 我使用的是 Arduino IDE 并在 2 5 2 版安装了 ESP8266 支持 它应该支持 I2C 从模式 但是 我无法让它工作
  • 如何将 boost::asio 与 Linux GPIO 结合使用

    我有一个单线程 Linux 应用程序 使用 boost asio 进行异步输入 输出 现在我需要扩展此应用程序以读取 GPIO 输入 sys class gpio gpioXX value 可以在边沿触发的 GPIO 输入上使用 boost

随机推荐