i2c 子系统分析
一、i2c驱动核心层分析
i2c驱动核心层主要为设备驱动层提供设备注册、通信、管理等接口,同时连接i2c控制器驱动。这里首先分析i2c子系统中比较重要的几个结构体:
1、driver
struct i2c_driver {
unsigned int class; // 驱动支持的设备类型,如传感器、eeprom等
/* 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; // 驱动支持设备的信息
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *); // 探测函数
const unsigned short *address_list; // 驱动支持的设备地址,传递给detect函数
struct list_head clients; // 驱动节点
};
这个结构体代表了一个i2c设备驱动,其中class代表i2c器件的类型,attach_adapter和detach_adapter代表从总线上添加或者删除设备,probe和remove用于绑定和解除绑定,alert用于SMBus,driver是设备的驱动模型,id_tabel代表驱动所支持设备的信息,detect用于设备探测,address_list传递给detect,clients代表我们创建的客户端。
2、device
struct i2c_client {
unsigned short flags; // 设备标志
unsigned short addr; // 设备地址-7位
char name[I2C_NAME_SIZE]; // 设备名称
struct i2c_adapter *adapter; // 设备所属的适配器
struct i2c_driver *driver; // 设备所用的驱动
struct device dev; // 设备模型
int irq; // 设备可能会用到的中断号
struct list_head detected;
};
该结构体代表一个i2c从设备,flags指定需要的特殊处理方式,addr是i2c设备的7位地址值,name是i2c设备名称,adapter为设备所属的适配器,driver为此设备的驱动程序,dev为设备的设备模型,irq为设备可能会用到的中断号。
struct i2c_board_info {
char type[I2C_NAME_SIZE]; // 设备名称
unsigned short flags; // 设备标志,如读取标志、十位地址位等
unsigned short addr; // 设备地址
void *platform_data; // 设备私有数据
struct dev_archdata *archdata;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
int irq; // 设备采用的中断号
};
这个结构体通常用于创建i2c设备,type为设备名称,platform_data为platform总线信息。通常采用如下方式注册i2c设备:
static struct i2c_board_info __initdata i2c_devices0[]= {
#ifdefined(CONFIG_REGULATOR_AXP192)
{
I2C_BOARD_INFO("axp192",0x34),
.irq = PMIC_IRQ,
.platform_data= &axp192_info,
},
#endif
};
该数组包含一个通道上所有设备的信息,然后通过i2c_register_board_info函数注册该通道上所有设备。
int __init i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
{
int status;
down_write(&__i2c_board_lock);
/* dynamic bus numbers will be assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + 1;
for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!\n");
status = -ENOMEM;
break;
}
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); // 将设备依次添加到全局设备链表中
}
up_write(&__i2c_board_lock);
return status;
}
其中busnum指定通道,info指定设备信息,len指定设备数量,该函数将设备依次添加到全局设备链表__i2c_board_list中。链表节点devinfo中有设备信息以及设备所连接的通道。
struct i2c_devinfo {
struct list_head list; // 设备节点
int busnum; // i2c通道
struct i2c_board_info board_info; // 设备信息
};
函数的调用方式如下:
i2c_register_board_info(0, i2c_devices0, ARRAY_SIZE(i2c_devices0));
3、adapter
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class; // 适配器支持的类型
const struct i2c_algorithm *algo; // 适配器通信的方法
void *algo_data; // 适配器私有数据
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; // 超时时间
int retries; // i2c超时重试次数
struct device dev; // 设备模型
int nr; // 适配器为i2c的第几通道
char name[48]; // 适配器名称
struct completion dev_released;
struct list_head userspace_clients;
};
i2c_adapter代表一个适配器,一个i2c通道就是一个适配器,algo指定该适配器的方法。
4、algorithm
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);
/* 获取适配器支持的通信方式 */
u32 (*functionality) (struct i2c_adapter *);
};
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
/* REVISIT the fault reporting model here is weak:
*
* - When we get an error after receiving N bytes from a slave,
* there is no way to report "N".
*
* - When we get a NAK after transmitting N bytes to a slave,
* there is no way to report "N" ... or to let the master
* continue executing the rest of this combined message, if
* that's the appropriate response.
*
* - When for example "num" is two and we successfully complete
* the first message but get an error part way through the
* second, it's unclear whether that should be reported as
* one (discarding status on the second message) or errno
* (discarding status on the first one).
*/
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
if (in_atomic() || irqs_disabled()) {
ret = rt_mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
rt_mutex_lock(&adap->bus_lock);
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num); // 发送或者读取数据包
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
rt_mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -EOPNOTSUPP;
}
}
i2c_algorithm结构体指定了i2c通信实现的方法。当需要发送或者接受i2c数据时就通过i2c_transfer实现对硬件的控制,该函数通常被封装成i2c_master_send以及i2c_master_recv供驱动程序调用。
int i2c_master_send(struct i2c_client *client, const char *buf, int count)
{
int ret;
struct i2c_adapter *adap = client->adapter;
struct i2c_msg msg;
/* 填充msg */
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;
/* S: telechips */
msg.flags = (client->flags & I2C_M_MODE) | (client->flags & I2C_M_WR_RD);
/* E: telechips */
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}int i2c_master_recv(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;
/* S: telechips */
msg.flags |= (client->flags & I2C_M_MODE);
/* E: telechips */
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
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 */
/* Telechips */
#define I2C_M_MODE 0x0040 /* if slave is TCC DxB chip */
#define I2C_M_WR_RD 0x0080 /* if write->read operation */
#define I2C_M_WM899x_CODEC 0x0100 /* if slave is wm899x codec */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
i2c_msg为i2c通信的数据包。
5、bus
下面我们来分析i2c总线的注册过程:
static int __init i2c_init(void)
{
int retval;
/* 注册i2c总线 */
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);
if (retval)
goto class_err;
return 0;
class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}
postcore_initcall(i2c_init); // 此处采用postcore_initcall调用i2c-bus注册
其中
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
static const struct dev_pm_ops i2c_device_pm_ops = {
.suspend = i2c_device_pm_suspend,
.resume = i2c_device_pm_resume,
.freeze = i2c_device_pm_freeze,
.thaw = i2c_device_pm_thaw,
.poweroff = i2c_device_pm_poweroff,
.restore = i2c_device_pm_restore,
SET_RUNTIME_PM_OPS(
pm_generic_runtime_suspend,
pm_generic_runtime_resume,
pm_generic_runtime_idle
)
};static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
driver = to_i2c_driver(drv);
/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
i2c设备和驱动的匹配函数。
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client)
return 0;
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}
该函数最终调用driver->probe进入设备驱动。
static int i2c_device_remove(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client || !dev->driver)
return 0;
driver = to_i2c_driver(dev->driver);
if (driver->remove) {
dev_dbg(dev, "remove\n");
status = driver->remove(client);
} else {
dev->driver = NULL;
status = 0;
}
if (status == 0) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}
该函数最终调用driver-> remove进入设备驱动。
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
6、i2c-driver注册流程。
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver); // 注册driver
if (res)
return res;
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
mutex_lock(&core_lock);
bus_for_each_dev(&i2c_bus_type, NULL, driver, __process_new_driver);
mutex_unlock(&core_lock);
return 0;
}
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));
}
其中bus_for_each_dev搜寻该总线上所有设备并调用__process_new_driver,设备已经在板级初始化程序中创建和注册。
static int i2c_do_add_adapter(struct i2c_driver *driver,
struct i2c_adapter *adap)
{
/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver);
/* Let legacy drivers scan this bus for matching devices */
if (driver->attach_adapter) {
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
return 0;
}static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
const unsigned short *address_list;
struct i2c_client *temp_client;
int i, err = 0;
int adap_id = i2c_adapter_id(adapter); // 获取device的适配器连接的哪个i2c通道
address_list = driver->address_list; // 获取driver中的地址列表
if (!driver->detect || !address_list)
return 0;
/* Set up a temporary client to help detect callback */
temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); // 创建一个临时i2c设备
if (!temp_client)
return -ENOMEM;
temp_client->adapter = adapter; // 初始化temp_client
/* 判断device和driver中的设备是否一致 */
if (!(adapter->class & driver->class))
goto exit_free;
/* 判断适配器是否支持探测 */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE)) {
if (address_list[0] == I2C_CLIENT_END)
goto exit_free;
dev_warn(&adapter->dev, "Probing not supported\n");
err = -EOPNOTSUPP;
goto exit_free;
}
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {// 枚举driver地址列表中的地址
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id, address_list[i]);
temp_client->addr = address_list[i];
err = i2c_detect_address(temp_client, driver); // 开始探测
if (err)
goto exit_free;
}
exit_free:
kfree(temp_client);
return err;
}static int i2c_detect_address(struct i2c_client *temp_client,
struct i2c_driver *driver)
{
struct i2c_board_info info;
struct i2c_adapter *adapter = temp_client->adapter;
int addr = temp_client->addr;
int err;
/* Make sure the address is valid */
err = i2c_check_addr_validity(addr); // 检测地址是否有效
if (err) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return err;
}
/* Skip if already in use */
if (i2c_check_addr_busy(adapter, addr)) // 检测地址是否被占用
return 0;
/* Make sure there is something at this address */
if (!i2c_default_probe(adapter, addr)) // 探测设备是否存在
return 0;
/* Finally call the custom detection function */
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
err = driver->detect(temp_client, &info); // 调用平台函数探测设备并填充info
if (err) {
/* -ENODEV is returned if the detection fails. We catch it
here as this isn't an error. */
return err == -ENODEV ? 0 : err;
}
/* Consistency check */
if (info.type[0] == '\0') { // 设备名为空
dev_err(&adapter->dev, "%s detection function provided "
"no name for 0x%x\n", driver->driver.name,
addr);
} else {
struct i2c_client *client;
/* Detection succeeded, instantiate the device */
dev_dbg(&adapter->dev, "Creating %s at 0x%02x\n",
info.type, info.addr);
client = i2c_new_device(adapter, &info);
if (client)
list_add_tail(&client->detected, &driver->clients);//将client添加到驱动程序链表
else
dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
info.type, info.addr);
}
return 0;
}将刚创建设备的client->detected添加到驱动程序链表里面。
static int i2c_check_addr_validity(unsigned short addr)
{
/*
* Reserved addresses per I2C specification:
* 0x00 General call address / START byte
* 0x01 CBUS address
* 0x02 Reserved for different bus format
* 0x03 Reserved for future purposes
* 0x04-0x07 Hs-mode master code
* 0x78-0x7b 10-bit slave addressing
* 0x7c-0x7f Reserved for future purposes
*/
if (addr < 0x08 || addr > 0x77)
return -EINVAL;
return 0;
}
该函数检测i2c地址是否有效。
static int __i2c_check_addr_busy(struct device *dev, void *addrp)
{
struct i2c_client *client = i2c_verify_client(dev);
int addr = *(int *)addrp;
if (client && client->addr == addr)
return -EBUSY;
return 0;
}
static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr)
{
return device_for_each_child(&adapter->dev, &addr,__i2c_check_addr_busy);
}
检测地址是否已经被占用。
static int i2c_default_probe(struct i2c_adapter *adap, unsigned short addr)
{
int err;
union i2c_smbus_data dummy;
#ifdef CONFIG_X86
if (addr == 0x73 && (adap->class & I2C_CLASS_HWMON)
&& i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE_DATA))
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
I2C_SMBUS_BYTE_DATA, &dummy);
else
#endif
if ((addr & ~0x07) == 0x30 || (addr & ~0x0f) == 0x50
|| !i2c_check_functionality(adap, I2C_FUNC_SMBUS_QUICK))
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0,
I2C_SMBUS_BYTE, &dummy);
else
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0,
I2C_SMBUS_QUICK, NULL);
return err >= 0;
}
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol,
union i2c_smbus_data *data)
{
unsigned long orig_jiffies;
int try;
s32 res;
flags &= I2C_M_TEN | I2C_CLIENT_PEC;
if (adapter->algo->smbus_xfer) {
rt_mutex_lock(&adapter->bus_lock);
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (res = 0, try = 0; try <= adapter->retries; try++) {
res = adapter->algo->smbus_xfer(adapter, addr, flags,
read_write, command,
protocol, data);
if (res != -EAGAIN)
break;
if (time_after(jiffies,
orig_jiffies + adapter->timeout))
break;
}
rt_mutex_unlock(&adapter->bus_lock);
} else // 如果不存在物理上的SMBus,则采用i2c模拟
res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
command, protocol, data);
return res;
}
i2c_default_probe检测该设备是否存在。
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
/* 创建client */
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
/* 填充client */
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
status = i2c_check_addr_busy(adap, client->addr);
if (status)
goto out_err;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
#ifdef CONFIG_OF
client->dev.of_node = info->of_node;
#endif
/* 根据设备占用的i2c通道和地址命名设备 */
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr);
status = device_register(&client->dev); // 注册device
if (status)
goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
首先搜寻总线上所有的设备 -> 根据驱动中的设备地址列表探测设备是否存在 -> 如果存在则创建一个client。
7、i2c-adapter注册流程
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
int status;
if (adap->nr & ~MAX_ID_MASK)
return -EINVAL;
retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;
mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh;
* we need the "equal to" result to force the result
*/
status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
if (status == 0 && id != adap->nr) {
status = -EBUSY;
idr_remove(&i2c_adapter_idr, id);
}
mutex_unlock(&core_lock);
if (status == -EAGAIN)
goto retry;
if (status == 0)
status = i2c_register_adapter(adap); // 调用注册函数
return status;
}
函数的功能是注册一个适配器,该适配器通道已定义,如果要注册一个动态通道号的适配器则采用i2c_add_adapter函数。
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}
rt_mutex_init(&adap->bus_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* 如果没有设置超时时间则设置为1秒 */
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); // 注册dev
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
#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
/* create pre-declared device nodes */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
mutex_lock(&core_lock);
dummy = 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;
}static int __process_new_adapter(struct device_driver *d, void *data)
{
return i2c_do_add_adapter(to_i2c_driver(d), data); // 转到i2c_do_add_adapter
}
二、i2c控制器驱动分析
控制器层驱动程序主要初始化i2c控制器,实现通信方法。
static struct platform_driver tcc_i2c_driver = {
.probe = tcc_i2c_probe,
.remove = tcc_i2c_remove,
.suspend = tcc_i2c_suspend_late,
.resume = tcc_i2c_resume_early,
.driver = {
.owner = THIS_MODULE,
.name = "tcc-i2c",
},
};
static int __init tcc_i2c_adap_init(void)
{
return platform_driver_register(&tcc_i2c_driver);
}
subsys_initcall(tcc_i2c_adap_init); // 此处采用subsys_initcall调用i2c子系统初始化函数static int tcc_i2c_probe(struct platform_device *pdev)
{
int i, ret;
struct tcc_i2c *i2c;
struct tcc_i2c_platform_data *tcc_platform_data = pdev->dev.platform_data;//设备资源
struct resource *resource[I2C_NUM_OF_CH];
i2c = kzalloc(sizeof(struct tcc_i2c) * I2C_NUM_OF_CH, GFP_KERNEL);
if (!i2c) {
ret = -ENOMEM;
goto err_nockl;
}
for (i = 0; i < I2C_NUM_OF_CH; i++) { // 初始化每个通道
resource[i] = platform_get_resource(pdev, IORESOURCE_IO, i);
if (!resource[i]) {
printk("i2c%d: no io resource? (ch%d)\n", pdev->id, i);
dev_err(&pdev->dev, "no io resource? (ch%d)\n", i);
ret = -ENODEV;
goto err_io;
}
i2c[i].adap.owner = THIS_MODULE;
i2c[i].adap.algo = &tcc_i2c_algorithm; // 通信方法
i2c[i].adap.retries = 2; // 超时后重复尝试次数
sprintf(i2c[i].adap.name, "%s-ch%d",pdev->name, i); // 初始化适配器名称
spin_lock_init(&i2c[i].lock);
init_waitqueue_head(&i2c[i].wait);
i2c[i].core = pdev->id; // 编号
i2c[i].ch = i; // 适配器通道
i2c[i].regs = (volatile struct tcc_i2c_regs *) IO_ADDRESS(resource[i]->start); // 设置寄存器起始地址
i2c[i].IRQSTR = resource[i]->end;
i2c[i].smu_i2c_flag = tcc_platform_data->smu_i2c_flag; // 设置标志位
i2c[i].i2c_clk_rate = tcc_platform_data->i2c_ch_clk_rate[i];// 设置i2c信号时钟频率
i2c[i].core_clk_rate = tcc_platform_data->core_clk_rate; // 设置i2c控制器频率
memcpy(i2c[i].core_clk_name, tcc_platform_data->core_clk_name, sizeof(tcc_platform_data->core_clk_name));
/* Set GPIO and Enable Clock */
if(tcc_i2c_init(i2c + i) < 0) {
goto err_io;
}
i2c[i].adap.algo_data = i2c + i;
i2c[i].adap.dev.parent = &pdev->dev;
#if 0
#define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */
#define I2C_CLASS_TV_ANALOG (1<<1) /* bttv + friends */
#define I2C_CLASS_TV_DIGITAL (1<<2) /* dvb cards */
#define I2C_CLASS_DDC (1<<3) /* DDC bus on graphics adapters */
#define I2C_CLASS_SPD (1<<7) /* SPD EEPROMs and similar */
#endif
i2c[i].adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; // 设置适配支持的设备类型
i2c[i].adap.nr = pdev->id * I2C_NUM_OF_CH + i;
ret = i2c_add_numbered_adapter(&i2c[i].adap); // 注册适配器
if (ret < 0) {
printk("%s: Failed to add bus\n", i2c[i].adap.name);
for (i--; i >= 0; i--)
i2c_del_adapter(&i2c[i].adap);
goto err_clk;
}
}
platform_set_drvdata(pdev, i2c);
return 0;
err_clk:
printk("~~~~~~ %s() err_clk\n", __func__);
//TODO: I2C peri bus disable
for(i = 0;i < I2C_NUM_OF_CH;i++) {
if(i2c[i].pclk != NULL) {
clk_disable(i2c[i].pclk);
clk_put(i2c[i].pclk);
i2c[i].pclk = NULL;
}
}
err_io:
printk("~~~~~~ %s() err_io\n", __func__);
kfree(i2c);
err_nockl:
printk("~~~~~~ %s() err_nock1\n", __func__);
return ret;
}static void tcc_i2c_message_start(struct tcc_i2c *i2c, struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1; // 获取设备地址,并左移1位
if (msg->flags & I2C_M_RD) // 如果是读则设置最后一位为1
addr |= 1;
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
i2c->regs->TXR = addr; // 设置地址寄存器
i2c->regs->CMD = Hw7 | Hw4; // 设置命令寄存器
}
static int wait_intr(struct tcc_i2c *i2c)
{
unsigned long cnt = 0;
if (!i2c->smu_i2c_flag) {
while (!(tcc_readl(i2c->IRQSTR) & (i2c->ch?Hw1:Hw0))) { // 等待应答信号
cnt++;
if (cnt > 100000) {
printk("i2c-tcc: time out! core%d ch%d\n", i2c->core, i2c->ch);
return -1;
}
}
} else {
/* SMU_I2C wait */
while (1) {
cnt = i2c->regs->SR;
if (!(cnt & Hw1)) break;
}
for (cnt = 0; cnt <15; cnt++);
}
return 0;
}
static int recv_i2c(struct tcc_i2c *i2c)
{
int ret, i;
dev_dbg(&i2c->adap.dev, "READ [%x][%d]\n", i2c->msg->addr, i2c->msg->len);
tcc_i2c_message_start(i2c, i2c->msg); // 开始通信,设置第一个字节值并开始发送
ret = wait_intr(i2c);
if (ret != 0)
return ret;
BITSET(i2c->regs->CMD, Hw0); /* Clear a pending interrupt */
for (i = 0; i < i2c->msg->len; i++) {
if (i2c->msg->flags & I2C_M_WM899x_CODEC) { /* B090183 */ // 检测标志
if (i == 0)
i2c->regs->CMD = Hw5;
else
i2c->regs->CMD = Hw5 | Hw3;
} else {
if (i == (i2c->msg->len - 1))
i2c->regs->CMD = Hw5 | Hw3;
else
i2c->regs->CMD = Hw5;
}
ret = wait_intr(i2c);
if (ret != 0)
return ret;
BITSET(i2c->regs->CMD, Hw0);
i2c->msg->buf[i] = (unsigned char) i2c->regs->RXR;
}
i2c->regs->CMD = Hw6;
ret = wait_intr(i2c);
if (ret != 0)
return ret;
BITSET(i2c->regs->CMD, Hw0);
return 1;
}
static int send_i2c(struct tcc_i2c *i2c)
{
int ret, i, no_stop = 0;
dev_dbg(&i2c->adap.dev, "SEND [%x][%d]", i2c->msg->addr, i2c->msg->len);
tcc_i2c_message_start(i2c, i2c->msg);
ret = wait_intr(i2c);
if (ret != 0)
return ret;
BITSET(i2c->regs->CMD, Hw0); /* Clear a pending interrupt */
for (i = 0; i < i2c->msg->len; i++) {
i2c->regs->TXR = i2c->msg->buf[i];
if(i2c->msg->flags == I2C_M_WM899x_CODEC) /* B090183 */
i2c->regs->CMD = Hw4 | Hw3;
else
i2c->regs->CMD = Hw4;
ret = wait_intr(i2c);
if (ret != 0)
return ret;
BITSET(i2c->regs->CMD, Hw0);
//i2c->msg->buf[i] = (unsigned char) i2c->regs->RXR; //XXX
}
/*
* Check no-stop operation condition (write -> read operation)
* 1. DxB
* 2. msg_num == 2
*/
if (i2c->msg->flags & I2C_M_WR_RD)
no_stop = 1;
if (i2c->msg_num > 1)
no_stop = 1;
if (no_stop == 0) {
i2c->regs->CMD = Hw6;
ret = wait_intr(i2c);
if (ret != 0)
return ret;
}
BITSET(i2c->regs->CMD, Hw0); /* Clear a pending interrupt */
return 1;
}
/* tcc_i2c_doxfer
*
* this starts an i2c transfer
*/
static int tcc_i2c_doxfer(struct tcc_i2c *i2c, struct i2c_msg *msgs, int num)
{
int ret, i;
for (i = 0; i < num; i++) { // 解析msg
spin_lock_irq(&i2c->lock);
i2c->msg = &msgs[i];
i2c->msg->flags = msgs[i].flags;
i2c->msg_num = num;
i2c->msg_ptr = 0;
i2c->msg_idx = 0;
i2c->state = STATE_START;
spin_unlock_irq(&i2c->lock);
if (i2c->msg->flags & I2C_M_RD) {
ret = recv_i2c(i2c);
if (ret != 1)
printk("recv_i2c failed\n");
} else {
ret = send_i2c(i2c);
if (ret != 1)
printk("send_i2c failed\n");
}
}
return ret;
}
/* tcc_i2c_xfer
*
* first port of call from the i2c bus code when an message needs
* transferring across the i2c bus.
*/
static int tcc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct tcc_i2c *i2c = (struct tcc_i2c *)adap->algo_data;
int retry;
int ret;
for (retry = 0; retry < adap->retries; retry++) {
ret = tcc_i2c_doxfer(i2c, msgs, num);
if (ret == 1)
return ret;
dev_dbg(&i2c->adap.dev, "Retrying transmission (%d)\n", retry);
udelay(100);
}
return -EREMOTEIO;
}
static u32 tcc_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; // 适配器拥有i2c和用i2c模拟SMBus的功能
}
/* i2c bus registration info */
static const struct i2c_algorithm tcc_i2c_algorithm = {
.master_xfer = tcc_i2c_xfer,
.functionality = tcc_i2c_func,
};static int tcc_i2c_init(struct tcc_i2c *i2c)
{
unsigned int prescale;
volatile struct tcc_i2c_regs *i2c_reg = i2c->regs;
if (!i2c->smu_i2c_flag) {
/* I2C GPIO setting */
tca_i2c_setgpio(i2c->core, i2c->ch); // 设置io口功能
/* I2C clock Enable */
if(i2c->pclk == NULL) {
i2c->pclk = clk_get(NULL, i2c->core_clk_name);
if (!i2c->pclk) {
dev_err(&i2c->dev, "can't get i2c clock\n");
return -1;
}
}
if(clk_enable(i2c->pclk) != 0) {
dev_err(&i2c->dev, "can't do i2c clock enable\n");
return -1;
}
/* Set i2c core clock to 4MHz */
clk_set_rate(i2c->pclk, i2c->core_clk_rate); // 设置控制器时钟频率
prescale = ((clk_get_rate(i2c->pclk) / 10) / (i2c->i2c_clk_rate * 5)) - 1;
writel(prescale, &i2c_reg->PRES); // 初始化控制器
writel(Hw7 | Hw6 | HwZERO, &i2c_reg->CTRL);//start enable, stop enable, 8bit mode
writel(Hw0 | readl(&i2c_reg->CMD), &i2c_reg->CMD);//clear pending interrupt
msleep(100); // Why does it need ?
}
else
{
/* SMU_I2C clock setting */
tcc_writel(0x80000000, i2c->IRQSTR);
i2c->regs->CTRL = 0;
i2c->regs->PRES = 4;
BITSET(i2c->regs->CTRL, Hw7|Hw6);
}
return 0;
}
三、i2c设备注册方法
常见有两种方式注册i2c设备,第一种在板级初始化程序中注册device(在第二小节已经分析过),然后在设备驱动程序中注册driver;第二种则同时在设备驱动程序中注册device和driver。
1、方式一:
static const struct i2c_device_id goodix_i2c_id[] = {
{ "goodix_ts", 0 },
{ }
};
static struct i2c_driver goodix_i2c_driver = {
.probe = goodix_i2c_probe,
.remove = goodix_i2c_remove,
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = goodix_i2c_suspend,
.resume = goodix_i2c_resume,
#endif
.driver = {
.name = "goodix_ts",
.owner = THIS_MODULE,
},
.id_table = goodix_i2c_id,
};
static int __init goodix_i2c_init(void)
{
return i2c_add_driver(&goodix_i2c_driver); // 注册已经注册了的device的driver
}注册后i2c_device_probe将会执行,通过driver->probe调用刚注册的goodix_i2c_probe,传递的参数有两个:(client, i2c_match_id(driver->id_table, client))已经注册成功的client和id。2、方式二
static const struct i2c_device_id it7260_i2c_id[] = {
{ "it7260_ts", 0, },
{ }
};
static struct i2c_driver it7260_i2c_driver = {
.driver = {
.name = "it7260_ts",
.owner = THIS_MODULE,
},
.probe = it7260_i2c_probe,
.remove = it7260_i2c_remove,
.id_table = it7260_i2c_id,
};
ret = i2c_add_driver(&it7260_i2c_driver);首先在驱动程序的probe里面注册driver。
static int it7260_i2c_register(void)
{
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
memset(&info, 0, sizeof(struct i2c_board_info)); // 初始化i2c_board_info
info.addr = TS_IT7260_I2C_ADDRESS; // i2c地址值
strlcpy(info.type, "it7260_ts", I2C_NAME_SIZE); // 设置设备名称
adapter = i2c_get_adapter(0); // 获取i2c通道
if (!adapter)
{
ts_it7260_dbg(KERN_ERR "can't get i2c adapter 0 for it7260\n");
return -ENODEV;
}
client = i2c_new_device(adapter, &info); // 注册device并获取client
i2c_put_adapter(adapter);
if (!client)
{
ts_it7260_dbg(KERN_ERR"can't add i2c device at 0x%x\n",(unsigned int)info.addr);
return -ENODEV;
}
it7260_i2c_client = client; // 将client赋给全局变量
return 0;
}
然后再调用it7260_i2c_register注册device。
更多推荐
所有评论(0)