2. i2c 總線驅動:實現i2c硬件體系結構中適配器(解決通訊中怎麼發數據)。
3. i2c 設備驅動: 尋找i2c適配器與CPU交換數據(解決通訊中向誰發數據與發什麼數據)。
框架圖:
在linux源碼 driver/i2c目錄下:
i2c-core.c 實現i2c 核心層
i2c-dev.c 實現適配器設備功能
buses文件 實現主機控制器設備驅動
algos文件夾 實現總線適配器通訊方法。
## 幾個重要的數據結構 ##
[code]//i2c_adapter對應一個物理上的適配器
struct i2c_adapter {
struct module *owner;
unsigned int id;
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 list_head userspace_clients;
};
//對應一套i2c總線通訊方法
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);
//一個i2c通訊周期的信號
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 *);
};
//對應一套驅動方法
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 if you can, it will probably
* be removed in a near future.
*/
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_adapter)(struct i2c_adapter *);
/* 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; //驅動支持i2c設備的id表
/* 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;
};
//對應一個真實的i2c物理設備
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消息結構體
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 */
};platform_device:
[code]//plat-s2410.c
struct platform_device s3c_device_i2c2 = {
.name = "s3c2410-i2c",
.id = 2,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource, //i2c寄存器與i2c中斷號
};
//傳給plat_driver的私有數據
static struct s3c2410_platform_i2c default_i2c_data2 __initdata = {
.flags = 0,
.bus_num = 2, //對應i2c-2
.slave_addr = 0x10,
.frequency = 100*1000,
.sda_delay = 100,
};
platform_driver:
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe, //匹配後執行
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
},
};
//match-id
static struct platform_device_id s3c24xx_driver_ids[] = { //id_table
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);probe函數初始化適配器硬件,時鐘,中斷等資源,最終注冊一個適配器。[code]static int s3c24xx_i2c_probe(struct platform_device *pdev)
pdata = pdev->dev.platform_data; //獲取私有數據
i2c->adap.algo = &s3c24xx_i2c_algorithm; //綁定信號傳輸函數
init_waitqueue_head(&i2c->wait); //申請等待隊列
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //獲取i2c控制寄存器
i2c->regs = ioremap(res->start, resource_size(res));
ret = s3c24xx_i2c_init(i2c); //初始化配置寄存器
i2c->irq = ret = platform_get_irq(pdev, 0); //獲取中斷號
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c); //申請中斷
i2c->adap.nr = pdata->bus_num; //設置適配器的bus_name
ret = i2c_add_numbered_adapter(&i2c->adap);//add_adapter到總線,注冊adapter
status = i2c_register_adapter(adap);
i2c_scan_static_board_info(adap); //掃描總線,適配產生i2c_client
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,&devinfo->board_info))
//信號傳輸函數
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
//發送iic信號幀
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
ret = s3c24xx_i2c_set_master(i2c);
i2c->msg = msgs;
i2c->msg_num = num;
s3c24xx_i2c_enable_irq(i2c);
s3c24xx_i2c_message_start(i2c, msgs); //發送start信號
unsigned int addr = (msg->addr & 0x7f) << 1; //注意msg的iic地址
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); //等待中斷完成
//中斷到來且i2c數據幀傳輸完成,就是喚醒i2c-wait的時候
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
i2s_s3c_irq_nextbyte(i2c, status); //繼續發送
.......//繼續發送讀寫byte,繼續中斷
s3c24xx_i2c_stop(i2c, 0); //發送stop信號
s3c24xx_i2c_master_complete(i2c, ret);
wake_up(&i2c->wait); //喚醒i2c->wait
s3c24xx_i2c_disable_irq(i2c);[code]//在注冊i2c_adapter會掃描__i2c_board_list,根據i2c_devinfo信息 創建帶adapter的適配器,供i2c_driver匹配。
//方法1.在bsp文件中注冊i2c_register_board_info()
//方法2.如果內核支持設備樹,在dts文件中添加一個i2c的節點
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //名字如 lm75
unsigned short flags;
unsigned short addr; //7位地址
void *platform_data;
struct dev_archdata *archdata;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
int irq;
};
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); //將i2c_board_info加入__i2c_board_list
在注冊i2c_adapter時:
static int i2c_register_adapter(struct i2c_adapter *adap)
i2c_scan_static_board_info(adap);
list_for_each_entry(devinfo, &__i2c_board_list, list)
i2c_new_device(adapter,&devinfo->board_info)//產生依附adaper的client
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
client->adapter = adap; //放adapter到client
client->dev.platform_data = info->platform_data;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
status = device_register(&client->dev); //將生成的i2c_client放到i2c_bus上
//在寫i2c_driver時,提供匹配的id_table
static struct i2c_device_id id_tables[] = {
{"lm75", 0x1111},
{"lm75a", 0x1112},
};
static struct i2c_driver lm75_drv = {
.probe = lm75_drv_probe, //在probe中注冊一個fops,來對設備進行讀寫
.remove = lm75_drv_remove,
//必須要有
.driver = {
.name = "lm75",
},
.id_table = id_tables,
};