Linux驱动之IIC总线

时间:2022-12-29 23:38:08

<作用>

电子设备中有很多IIC设备之间需要进行相互通信,这样就产生了IIC总线,常用来实现设备之间的数据通信。
 
<IIC总线结构>
IIC总线只有两条线,一条是串行数据线(SDA),另外一条是串行时钟线(SCL).
Linux驱动之IIC总线
注:每一个连接到总线上的设备都有一个唯一的地址可以访问(这一点有点像USB设备)
 
<IIC总线信号类型>
a:开始信号(S):当SCL信号为高电平,SDA的电平由高电平变为低电平表示开始传输数据。
b:结束信号(P):当SCL信号为高电平,SDA的电平由低电平变为高电平表示结束传输数据。
c:相应信号(ACK):从机接收到8位数据后,在第9个时钟周期,拉低SDA电平,表示接受到数据,这个信号称为应答信号。
 
<IIC数据传输方式>
主机(IIC控制器)主要通过SDA向从机发送数据,当总线处于空闲的时候SDA和SCL都处于高电平。
a:当主机检测到总线处于空闲状态时,主机发送给开始信号(S)
b:主机发出8位数据,8位数据中,前7位中表示从机地址,第8位表示数据的传输方向。
c:和地址匹配的从机发出相应信号。
d:从机传输一系列响应序列
e:主机接收到这些数据后,发出结束信号P,该次数据传输完成。
 
<IIC控制器结构图>
Linux驱动之IIC总线
a:主要涉及的寄存器
IICCON:控制寄存器
IICSTAT:状态寄存器
IICADD:地址寄存器
Linux驱动之IIC总线
 
IICCDS:接收和发送的数据存储寄存器
 
<linux 驱动之IIC>
a:背景
对于IIC设备的控制,但是IIC设备太多了,为了很好的管理这些设备(不同等的设备名,管理各种设备,不同设备的地址,不同设备对应的驱动),Linux内核开发了如下结构体:
b:IIC设备(i2c_client)
struct i2c_client {
unsigned short flags;
unsigned short addr;
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; //通过该指针可以找到适配器,然后通过适配器找到                       //适配器算法中的发送消息和接受函数完成同IIC设备                          //的通信              
struct i2c_driver *driver;
struct device dev; //用于建立设备模型
int irq;
struct list_head detected;
};
 
c:IIC驱动(i2c_driver)
struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;//用于建立设备模型
const struct i2c_device_id *id_table;//是一个数组,保存该驱动支持的所有的 的                               //设备信息 
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
d:IIC适配器(i2c_adapter)//其本质表示适配器这个设备
-----i2c_adapter is the structure used to identify a physical i2c bus along with the access algorithms necessary to access it.内核中可以有很多总线适配器,其中内核中有一个静态指针数组adapters记录了所有已经注册的总线适配器。
 
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设备
};
d:IIC适配器驱动(i2c_algorithm)
 
/*
 * The following structs are for those who like to implement new bus drivers:
 * i2c_algorithm is the interface to a class of hardware solutions which can
 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
 * to name two of the most common.
 */
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 *);
};
e:注意:i2c_driver和i2c_client属于设备层,i2c_adapter和i2c_algorithm属于总线层。他们之间的关系如图所示:
Linux驱动之IIC总线
f:IIC设备之间的通信方式
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_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#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 */
};
填该结构体,用于同IIC设备通信。
 
<IIC设备驱动流程>
a:流程图
Linux驱动之IIC总线 
(1)xxx_init()和i2c_add_driver()实现对IIC子系统的初始化,这项工作由内核完成。
(2)xxx_attach_adapter()用来将IIC设备驱动和适配器挂接在一起,这样才能 IIC设备驱动才能利用 适配器驱动控制 适配器和IIC设备 通信。(注意:在这两步中间需要根据具体适配器实现相应的驱动,以及相应struct algorithm{}的实现)
(3)i2c_probe()函数是在检测到IIC设备后调用的函数,进一步实现IIC设备的各数据结构的初始化。包括下面的函数都为了实现相应的功能。
 
b:IIC子系统初始化
(1)初始化函数
Linux驱动之IIC总线
(2)卸载函数
Linux驱动之IIC总线
 
c:适配器驱动程序
(1)IIC适配器内核结构
Linux驱动之IIC总线
注意:
"struct i2c_adapter adap"
这是内核提供的IIC适配器的结构体,特定类型的适配器结构体需要在这个结构上进行扩充(面向对象的思想)。
"void __iomem  *regs"
指向IIC适配器的控制寄存器地址,注意这个地址是物理地址
"struct resource *ioarea"
指向适配器申请的资源,这里应该是内存资源,先进行申请,然后进行映射
"struct i2c_msg *msg"
用来实现IIC设备驱动于设备之间的通信
 
2)适配器加载函数
Linux驱动之IIC总线
(3)适配器卸载函数
Linux驱动之IIC总线
注意:这个适配器驱动跟字符设备驱动很相似,因为这里不需要同用户空间进行交互,所以这里没有相应的fops,但是有相应的 类似fops—— struct i2c_algorithm{}。和字符设备驱动相似,
重点就是要实现这个类似fops,struct i2c_algorithm{}中的相应的函数通过操作"void __iomem  *regs"中的相应的寄存器。
 
<IIC适配器驱动模块的加载与卸载>
a:这里将适配器驱动定性为平台设备
加载函数
Linux驱动之IIC总线
b:卸载函数
Linux驱动之IIC总线
c:注意其中的"s3c2410_i2c_driver"
Linux驱动之IIC总线
d:对于一个平台设备来"s3c2410_i2c_probe()"函数是很重要的
Linux驱动之IIC总线
Linux驱动之IIC总线
Linux驱动之IIC总线
Linux驱动之IIC总线
(1)" s3c2410_i2c_probe()"中的"s3c2410_i2c_init()"函数
Linux驱动之IIC总线
Linux驱动之IIC总线
 
e:与" s3c2410_i2c_probe()"相反的函数是s3c2410_i2c_remove()。
Linux驱动之IIC总线
 
<IIC设备驱动实例>
a:设备文件操作函数集
static struct file_operations eep_fops = {
.owner = THIS_MODULE,
.llseek = eep_llseek,
.read = eep_read,
.ioctl = eep_ioctl,
.open = eep_open,
.release = eep_release,
.write = eep_write,
};
static dev_t dev_number; /* Allotted Device Number */
static struct class *eep_class; /* Device class */
/* Per-device client data structure for each
* memory bank supported by the driver
*/
 
b:IIC设备结构体
struct eep_bank {
struct i2c_client *client; /* I2C client for this bank */
unsigned int addr; /* Slave address of this bank */
unsigned short current_pointer; /* File pointer */
int bank_number; /* Actual memory bank number */
/* ... */ /* Spinlocks, data cache for slow devices,.. */
};
#define NUM_BANKS 2 /* Two supported banks */
#define BANK_SIZE 2048 /* Size of each bank */
struct ee_bank *ee_bank_list; /* List of private data
structures, one per bank */
c:初始化函数
int __init
eep_init(void)
{
int err, i;
/* Allocate the per-device data structure, ee_bank */
ee_bank_list = kmalloc(sizeof(struct ee_bank)*NUM_BANKS, GFP_KERNEL);
memset(ee_bank_list, 0, sizeof(struct ee_bank)*NUM_BANKS);
/* Register and create the /dev interfaces to access the EEPROM
banks. Refer back to Chapter 5, "Character Drivers" for more details */
if (alloc_chrdev_region(&dev_number, 0,
NUM_BANKS, "eep") < 0) {
printk(KERN_DEBUG "Can't register device\n");
return -1;
}
eep_class = class_create(THIS_MODULE, DEVICE_NAME);
for (i=0; i < NUM_BANKS;i++) {
/* Connect the file operations with cdev */
cdev_init(&ee_bank[i].cdev, &ee_fops);
/* Connect the major/minor number to the cdev */
if (cdev_add(&ee_bank[i].cdev, (dev_number + i), 1)) {
printk("Bad kmalloc\n");
return 1;
}
device_create(eep_class, NULL, MKDEV (MAJOR) (dev_number),i),
"eeprom%d", i);
}
/* Inform the I2C core about our existence. See the section
"Probing the Device" for the definition of eep_driver */
err = i2c_add_driver(&eep_driver);
if (err) {
printk("Registering I2C driver failed, errno is %d\n", err);
return err;
}
printk("EEPROM Driver Initialized.\n");
return 0;
}
(1) eep_driver结构体如下所示:
eep_init()在设备初始化的时调用i2c_add_driver()将注册eep_probe()
static struct i2c_driver eep_driver =
{
.driver = {
.name = "EEP", /* Name */
},
.id = I2C_DRIVERID_EEP, //设备标志符I2C_DRIVERID_EEP对于每个设备应该是唯一的
.attach_adapter = eep_probe, /* Probe Method */
.detach_client = eep_detach, /* Detach Method */
};
(2)当IIC核心调用表明主机适配器已经存在的客户驱动程序的probe()方法时,其会发过来调用i2c_probe(),该函数的参数是驱动程序所关联的设备地址以及具体的探测函数attach()。
 
#include <linux/i2c.h>
/* The EEPROM has two memory banks having addresses SLAVE_ADDR1
* and SLAVE_ADDR2, respectively
*/
static unsigned short normal_i2c[] = {
SLAVE_ADDR1, SLAVE_ADDR2, I2C_CLIENT_END
};
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.probe = ignore,
.ignore = ignore,
.forces = ignore,
};

static int eep_probe(struct i2c_adapter *adapter)
{
/* The callback function eep_attach(), is shown in Listing 8.5 */
return i2c_probe(adapter, &addr_data, eep_attach);
}

int eep_attach(struct i2c_adapter *adapter, int address, int kind)
{
static struct i2c_client *eep_client;
eep_client = kmalloc(sizeof(*eep_client), GFP_KERNEL);
eep_client->driver = &eep_driver; /* Registered in Listing 8.2 */
eep_client->addr = address; /* Detected Address */
eep_client->adapter = adapter; /* Host Adapter */
eep_client->flags = 0;
strlcpy(eep_client->name, "eep", I2C_NAME_SIZE);
/* Populate fields in the associated per-device data structure */
/* ... */
/* Attach */
i2c_attach_client(new_client);
}  
 
<整个程序框架>
Linux驱动之IIC总线
 
Linux驱动之IIC总线