i2c子系统

linux系统核心层的东西基本都是xx-core。

一 主要数据结构

struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};
i2c通信的基本单位,flag为1表示读;flag为0表示写。
struct i2c_algorithm {
	/* If an adapter algorithm can't do I2C-level access, set master_xfer
	   to NULL. If an adapter algorithm can do SMBus access, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

	/* To determine what the adapter supports */
	u32 (*functionality) (struct i2c_adapter *);
};

i2c的通信方法。

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	*/
	struct rt_mutex bus_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;
};

i2c_adapter对应一个物理的i2c总线,它需要一个通信方法i2c_algorithm。

struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct i2c_driver *driver;	/* and our access routines	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
};
i2c_client代表一个i2c从设备,需要有自己的地址(地址分7bit和8bit,8bit地址需要右移变成7bit),需要挂接的总线,也就是i2c_adapter,还需要i2c_driver来实现读写操作。addr和adapter需要在i2c_new_device之前都准备好,它们都与硬件相关。addr是从设备决定的,i2c_get_adapter()可以获得一个adapter,要保证的是,这个adapter必须是连接到从设备上的。drvier和device的匹配是个双向的过程,所以driver填充的路径有两条:
第一条:注册i2c_driver的时候
i2c_register_driver()->driver_register()->bus_add_driver()->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()->i2c_device_probe()->client->driver = driver;
第二条:添加slave device的时候
i2c_new_device()->device_register()->device_add()->bus_probe_device()->device_attach()->__device_attach()->driver_probe_device()->really_probe()->i2c_device_probe()->client->driver = driver;
struct i2c_driver {
	unsigned int class;

	/* Notifies the driver that a new bus has appeared or is about to be
	 * removed. You should avoid using this, it will be removed in a
	 * near future.
	 */
	int (*attach_adapter)(struct i2c_adapter *) __deprecated;
	int (*detach_adapter)(struct i2c_adapter *) __deprecated;

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *);
	int (*suspend)(struct i2c_client *, pm_message_t mesg);
	int (*resume)(struct i2c_client *);

	/* Alert callback, for example for the SMBus alert protocol.
	 * The format and meaning of the data value depends on the protocol.
	 * For the SMBus alert protocol, there is a single bit of data passed
	 * as the alert response's low bit ("event flag").
	 */
	void (*alert)(struct i2c_client *, unsigned int data);

	/* a ioctl like command that can be used to perform specific functions
	 * with the device.
	 */
	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

	struct device_driver driver;
	const struct i2c_device_id *id_table;

	/* Device detection callback for automatic device creation */
	int (*detect)(struct i2c_client *, struct i2c_board_info *);
	const unsigned short *address_list;
	struct list_head clients;
};

driver都是为device服务的,一般一个driver可以为很多device服务,通过driver_bound()->klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);把device bound到driver的list上,driver卸载的时候,要把挂在上边的device release。

union i2c_smbus_data {
	__u8 byte;
	__u16 word;
	__u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */
			       /* and one more for user-space compatibility */
};

smbus的通信数据,是个联合体。

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];
	unsigned short	flags;
	unsigned short	addr;
	void		*platform_data;
	struct dev_archdata	*archdata;
	struct device_node *of_node;
	struct acpi_dev_node acpi_node;
	int		irq;
};
这是一个信息结构,作为new device的参数,type最终会变成client->name。driver注册的时候会用driver->id_table->name和client->name进行match,只有这里match成功了,才会有接下来的device匹配。

二 主要函数接口

static int i2c_device_match(struct device *dev, struct device_driver *drv)‘

调用i2c_match_id(),通过strcmp(client->name, id->name)进行匹配;匹配成功返回1,否则返回0。

struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);

创建一个i2c device,实际就是创建一个i2c_client结构,并初始化。

void i2c_unregister_device(struct i2c_client *);

注销一个一个i2c device,实际是device_unregister(&client->dev);;是针对i2c_new_device()->device_register(&client->dev)做一些注销工作。

int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned n);

该函数中新出现的结构是全局的一个读写信号量、一个devinfo、一个全局的list。
extern struct rw_semaphore __i2c_board_lock;
struct i2c_devinfo *devinfo;
extern struct list_head __i2c_board_list;
struct i2c_devinfo {
	struct list_head	list;
	int			busnum;
	struct i2c_board_info	board_info;
};
i2c_devinfo是对 i2c_board_info的一层封装,增加了一个list,i2c_register_board_info函数正是利用这个list将devinfo挂在了全局的__i2c_board_list上。新增的busnum是对应i2c_adapter->nr的。i2c_register_board_info()还要更新__i2c_first_dynamic_bus_num,它记录的是第一个动态的bus num,被分配为最后一个静态申请的下一个,= busnum + 1。
#define I2C_BOARD_INFO(dev_type, dev_addr) \
	.type = dev_type, .addr = (dev_addr)
这个宏就是用来填充一个i2c_board_info结构的。

static void i2c_scan_static_board_info(struct i2c_adapter *adapter)

扫描__i2c_board_list,if (devinfo->busnum == adapter->nr)根据bus号进行匹配,则i2c_new_device(adapter, &devinfo->board_info)。

int i2c_add_adapter(struct i2c_adapter *adapter)

该函数先获得一个id,作为adapter的id,然后调用i2c_register_adapter(adapter)。
获得id,用到了IDR机制,这个机制后续再说。

static int i2c_register_adapter(struct i2c_adapter *adap)

adapter也是一种device,但凡device注册都会调用device_register()。
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
如果条件成立,说明之前用i2c_register_board_info()注册过i2c_devinfo,不然__i2c_first_dynamic_bus_num怎么会大于0?既然注册过__i2c_board_list上就挂了i2c_devinfo信息,这些都是i2c client的信息,是等待new device的信息。所以这里通过i2c_scan_static_board_info()扫描了整个info list创建i2c client。因为__i2c_first_dynamic_bus_num是静态注册的下一个,如果adap->nr > __i2c_first_dynamic_bus_num,说明nr这个bus上还没有静态注册info 信息,所以是不需要扫描的。
函数最后的bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
		     void *data, int (*fn)(struct device_driver *, void *))
{
	struct klist_iter i;
	struct device_driver *drv;
	int error = 0;

	if (!bus)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_drivers, &i,
			     start ? &start->p->knode_bus : NULL);
	while ((drv = next_driver(&i)) && !error)
		error = fn(drv, data);
	klist_iter_exit(&i);
	return error;
}
void klist_iter_init_node(struct klist *k, struct klist_iter *i,
			  struct klist_node *n)
{
	i->i_klist = k;
	i->i_cur = n;
	if (n)
		kref_get(&n->n_ref);
}
klist_iter_init_node()中初始化 klist_iter i,i->i_klist = &bus->p->klist_drivers,i->i_cur = start。klist_devices是对driver list的一个封装,上面挂的driver是driver_register()->bus_add_driver()->klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);时添加的。如果start 不是 NULL,并增加(&start ->n_ref)->->refcount引用计数。接下来next_driver()中如果start存在,就从start开始;否则从bus->p->klist_drivers开始,&bus->p->klist_drivers->next是一个*list_head,通过它找到一个*klist_node,挂的时候就是这样挂的,找的时候自然这样找,继续找到driver_private;这里就有driver了,driver_register()->bus_add_driver()->(priv->driver = drv;),注册的时候就藏到这了;经过结构的转换总能找到next driver,执行__process_new_adapter(device_driver, i2c_adapter)。
static int __process_new_adapter(struct device_driver *d, void *data)
{
	return i2c_do_add_adapter(to_i2c_driver(d), data);
}
原来只是针对dev->type == &i2c_adapter_type,才会有i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap),包括i2c_detect()和driver->attach_adapter(adap)。只是针对挂到bus->p->klist_devices的adapter(i2c client是一种device,i2c adapter也是一直device,它们都会挂上,就是通过dev->type区分的,显然client的dev->type = &i2c_client_type;);进行driver和adapter的依附动作,一般会在attach_adapter()中执行probe动作,为i2c client找到依附的adapter,这个函数基本不用了,已经被probe()函数取代了。
这是个双向的过程,drvier注册的时候也会执行。
i2c_register_driver()->i2c_for_each_dev(driver, __process_new_driver);
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
	int res;

	mutex_lock(&core_lock);
	res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
	mutex_unlock(&core_lock);

	return res;
}
int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)) && !error)
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}
klist_iter_init_node()中初始化 klist_iter i,i->i_klist = &bus->p->klist_devices,i->i_cur = start。klist_devices是对device list的一个封装,上面挂的device是device_register()->device_add()->bus_add_device()->klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);时添加的。如果start 不是 NULL,并增加(&start ->n_ref)->->refcount引用计数。接下来next_device()中如果start存在,就从start开始;否则从bus->p->klist_devices开始;&bus->p->klist_devices->next是一个*list_head,通过它找到一个*klist_node,继续找到device_private;这里就有device了,device_register()->device_add()->device_private_init()->(dev->p->device = dev);经过结构的转换总能找到next device,执行__process_new_driver(device, i2c_driver)。
static int __process_new_driver(struct device *dev, void *data)
{
	if (dev->type != &i2c_adapter_type)
		return 0;
	return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}

接下来的动作同上。

register adapterbus_for_each_drv

1__process_new_adapter(struct device_driver *d, void *data)参数是device_driver、i2c_adapter

register driverbus_for_each_dev

2__process_new_driver(struct device *dev, void *data)参数是devicei2c_driver

i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)参数是i2c_driveri2c_adapter

1需要to_i2c_driver(device_driver)找到i2c_driver

2需要to_i2c_adapter(dev)找到i2c_adapter

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

这个留给用户的通信接口了,信息格式i2c_msg,信息长度num。
i2c_transfer()保证顺利执行,就必须实现它的通信方法adap->algo->master_xfer;
ret = __i2c_transfer(adap, msgs, num);
ret = adap->algo->master_xfer(adap, msgs, num);

 

三 i2c总线注册

i2c是总线设备,注册等级要高,postcore_initcall(i2c_init);对应__define_initcall(fn, 2)第2级。我们平常使用的module_init(x)是第6级的。
retval = bus_register(&i2c_bus_type);
int __bus_register(struct bus_type *bus, struct lock_class_key *key)
注册一个子系统,主要是填充subsys_private这样一个结构。
初始化注册kobj、kset设备模型的一些东西,这个后续再说。
 

四 i2c驱动程序开发

i2c子系统已经搭建好了,驱动开发就是填充子系统中的一些结构。
1 注册i2c adapter,与硬件bus对应。
2 实现adapter的通信方法i2c_algorithm,主要是master_xfer。
master_xfer需要实现read、write函数。
 

五 软件模拟i2c

软件模拟i2c是通过GPIO实现的,按照总线时序模拟的;通用代码位于drver/i2c/algos/i2c-algo-bit.c和include/linux/i2c-algo-bit.h。
static inline void sdalo(struct i2c_algo_bit_data *adap)//数据线拉低
static inline void sdahi(struct i2c_algo_bit_data *adap)//数据线拉高
static inline void scllo(struct i2c_algo_bit_data *adap)//时钟线拉低
static int sclhi(struct i2c_algo_bit_data *adap)//时钟线拉高,需要通过getscl的值,确保已经拉高
static void i2c_start(struct i2c_algo_bit_data *adap)//start信号,要求数据和时钟都先是高;数据线先拉低,延时,时钟线拉低
static void i2c_repstart(struct i2c_algo_bit_data *adap)//要求时钟低;先拉高数据线,再拉高时钟线,然后是一个start信号
如果发送了start信号,而没有发送stop信号,就会用到repstart。
static void i2c_stop(struct i2c_algo_bit_data *adap)//要求时钟低;数据线拉低,时钟线拉高,数据线拉高,延时
static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)//要求时钟低;发送一个byte,先输出高位;时钟高的时候,要求数据稳定,不能变化;所以时钟低的时候改变;最后拉高sda,拉高scl,等ack(ack是一个低信号);拉低scl。有ack返回1,否则0。


static int i2c_inb(struct i2c_adapter *i2c_adap)//要求scl低,接收一个byt;也是先接收高位;但是indata << 2,用的是indata *= 2;。
static int test_bus(struct i2c_adapter *i2c_adap)//test bus是否idle;1 如果sda和scl不是都为高,就busy;2 只拉低sda;3 只拉高sda;4 只拉低scl;5 只拉高scl;分别读一下2、3、4、5的sda和scl是否正确。
static int try_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries)//测试i2c addr从机有没有应答,有则返回1。
static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)//发送一个msg,调用i2c_outb()实现。
static int acknak(struct i2c_adapter *i2c_adap, int is_ack)//要求sda高,发送一个ack。
static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)//接收一个msg,调用i2c_inb()和acknak()。
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)//发送地址;1 10bit地址,把addr拆分成2bit+8bit;i2c时序要求先发送11110 AA r/w(AA就是2bit的填充位置),然后发送8bit;还是i2c时序要求,在重复开始条件(Sr)之后,被匹配的从机会保持被寻址上的状态,此时从机会检查Sr之后的第一byte的高7bit是否正确,符合11110 AA,再测试bit0是否是1(读状态);所以if (flags & I2C_M_RD)成立,还需要repstart后,发送一个这样的addr。2 正常的7bit地址,只需左移到高7bit,bit0根据读写标志填充,但是如果if (flags & I2C_M_REV_DIR_ADDR)成立,需要把读写标志翻转。


static int bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)//终于到了真正的传输函数了,先发送start信号。if (!(pmsg->flags & I2C_M_NOSTART)) 成立则发送重复开始信号和addr,然后根据msg->flags判断是read还是write。需要注意的是这个I2C_M_NOSTART标志应该什么时候设置?如果msgs[0]就定义了该标志,就会发送S和Sr两个开始信号,没必要吧;所以一般msgs[0]不会定义。最后发送stop信号。
static u32 bit_func(struct i2c_adapter *adap)//确定一个adapter支持的功能
static int __i2c_bit_add_bus(struct i2c_adapter *adap, int (*add_adapter)(struct i2c_adapter *))//添加一个adapter,要求它的通信方法adap->algo = &i2c_bit_algo;
const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
.functionality = bit_func,
};
int i2c_bit_add_bus(struct i2c_adapter *adap)
{
return __i2c_bit_add_bus(adap, i2c_add_adapter);
}
int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
{
return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter);
}
前者是用动态的bus num,后者是动态的bus num。
对于模拟i2c驱动程序的开发,仍然是要注册一个adapter,调用i2c_bit_add_bus()或者i2c_bit_add_numbered_bus();至于通信方法已经指定了。写模拟驱动主要是填充i2c_algo_bit_data这个结构;__i2c_bit_add_bus()中有i2c_algo_bit_data *bit_adap = adap->algo_data;那么对应的在驱动中填充好这个结构也赋给adap->algo_data就好了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值