https://blog.csdn.net/chenliang0224/article/details/79069908
1.前言:
linux I2C体系结构由三部分组成
①I2C核心:提供I2C总线驱动和设备驱动的注册、注销方法
②I2C总线驱动:主要包括两个数据结构,适配器I2c_adapter(包括algorithm结构体指针)、和algorithm数据结构(最终I2C的通信是由algorithm结构控制发送与接受数据)
③I2C设备驱动:是对I2C硬件体系结构中设备端的实现,两个数据结构,分别是I2c_driver(包括I2C的依附、脱离、探测、移除、关闭、恢复、读写数据。。。)对应一对通信方法、I2c_cilent(包括I2C硬件的芯片地址,设备名称、等,其中该结构体包括适配器I2c_adapter,和I2c_driver指针,这样就可以直接访问了)该结构体对应实在的硬件
2.Platform_device i2c 平台设备注册
2.1 i2c IO中断、IO管脚寄存器资源
-
static struct resource nuc970_i2c0_resource[] = {
-
[0] = {
-
.start = NUC970_PA_I2C0,
-
.end = NUC970_PA_I2C0 + NUC970_SZ_I2C0 - 1,
-
.flags = IORESOURCE_MEM,
-
},
-
[1] = {
-
.start = IRQ_I2C0,
-
.end = IRQ_I2C0,
-
.flags = IORESOURCE_IRQ,
-
}
-
};
2.2 i2c设备平台私有数据
包括I2C总线编号、总线频率
-
static struct nuc970_platform_i2c nuc970_i2c0_data = {
-
.bus_num = 0, //I2C总线,这里使用的是总线0
-
.bus_freq = 100000, //I2C总线频率
-
};
2.3 i2c平台设备注册
-
struct platform_device nuc970_device_i2c0 = {
-
.name = "nuc970-i2c0",
-
.id = -1,
-
.num_resources = ARRAY_SIZE(nuc970_i2c0_resource),
-
.resource = nuc970_i2c0_resource,
-
.dev = {
-
.platform_data = &nuc970_i2c0_data,
-
}
-
};
platform_device_register(nuc970_device_i2c0);
platform_device_register(...)将I2C设备注册到platform总线上,为了接下来的平台驱动匹配使用!
3.Board i2c从设备注册
3.1 i2c从设备结构体
-
struct i2c_board_info {
-
char type[I2C_NAME_SIZE]; //设备类型名称
-
unsigned short flags;
-
unsigned short addr; //I2C从设备地址
-
void *platform_data;
-
struct dev_archdata *archdata;
-
struct device_node *of_node;
-
struct acpi_dev_node acpi_node;
-
int irq;
-
};
-
#define I2C_BOARD_INFO(dev_type, dev_addr) \
-
.type = dev_type, .addr = (dev_addr)
3.2 i2c从设备信息结构初始化
注册一个从设备为“24c512”,从设备地址为0x50:
-
static struct i2c_board_info __initdata nuc970_i2c_clients0[] =
-
{
-
{I2C_BOARD_INFO("24c512", 0x50),},
-
};
3.3 i2c从设备注册到链表
i2c_register_board_info(0, nuc970_i2c_clients0, sizeof(nuc970_i2c_clients0)/sizeof(struct i2c_board_info));
-
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;
-
}
这里主要将I2C从设备信息注册到全局链表__i2c_board_list中
4.Platform_driver i2c 平台驱动注册
4.1 i2c设备驱动注册
-
static struct platform_driver nuc970_i2c0_driver = {
-
.probe = nuc970_i2c0_probe,
-
.remove = nuc970_i2c0_remove,
-
.driver = {
-
.name = "nuc970-i2c0",
-
.owner = THIS_MODULE,
-
},
-
};
-
module_platform_driver(nuc970_i2c0_driver);
设备驱动和设备通过名称"nuc970-i2c0"匹配相等(具体可以参考之前写的一篇文章:点击打开链接),调用探测函数nuc970_i2c0_probe(...)进行驱动注册。
4.2 i2c probe(...)
-
static int nuc970_i2c0_probe(struct platform_device *pdev)
-
{
-
struct nuc970_i2c *i2c;
-
struct nuc970_platform_i2c *pdata;
-
struct resource *res;
-
int ret;
-
pdata = pdev->dev.platform_data; //这里为设备nuc970_i2c0_data[]结构体数据
-
if (!pdata) {
-
dev_err(&pdev->dev, "no platform data\n");
-
return -EINVAL;
-
}
-
i2c = kzalloc(sizeof(struct nuc970_i2c), GFP_KERNEL); //分配一个i2c控制器
-
if (!i2c) {
-
dev_err(&pdev->dev, "no memory for state\n");
-
return -ENOMEM;
-
}
-
//用这个更合理些:strlcpy(i2c->adap.name, pdev->dev.name, sizeof(i2c->adap.name));
-
strlcpy(i2c->adap.name, "nuc970-i2c0", sizeof(i2c->adap.name)); //给i2c适配器命名
-
i2c->adap.owner = THIS_MODULE;
-
i2c->adap.algo = &nuc970_i2c0_algorithm; //i2c访问总线的方法,即读、写从设备操作
-
i2c->adap.retries = 2; //尝试次数
-
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
-
spin_lock_init(&i2c->lock);
-
init_waitqueue_head(&i2c->wait); //初始化等待队列
-
/* find the clock and enable it */
-
i2c->dev = &pdev->dev;
-
i2c->clk = clk_get(NULL, "i2c0");
-
if (IS_ERR(i2c->clk)) {
-
dev_err(&pdev->dev, "cannot get clock\n");
-
ret = -ENOENT;
-
goto err_noclk;
-
}
-
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
-
clk_prepare(i2c->clk);
-
clk_enable(i2c->clk);
-
/* map the registers */
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取IO寄存器资源
-
if (res == NULL) {
-
dev_err(&pdev->dev, "cannot find IO resource\n");
-
ret = -ENOENT;
-
goto err_clk;
-
}
-
i2c->ioarea = request_mem_region(res->start, resource_size(res), //申请内存资源
-
pdev->name);
-
if (i2c->ioarea == NULL) {
-
dev_err(&pdev->dev, "cannot request IO\n");
-
ret = -ENXIO;
-
goto err_clk;
-
}
-
i2c->regs = ioremap(res->start, resource_size(res)); //资源映射
-
if (i2c->regs == NULL) {
-
dev_err(&pdev->dev, "cannot map IO\n");
-
ret = -ENXIO;
-
goto err_ioarea;
-
}
-
dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
-
i2c->regs, i2c->ioarea, res);
-
/* setup info block for the i2c core */
-
i2c->adap.algo_data = i2c; //适配器算法数据指针指向i2c控制器
-
i2c->adap.dev.parent = &pdev->dev;
-
ret = clk_get_rate(i2c->clk)/(pdata->bus_freq * 5) - 1;
-
writel(ret & 0xffff, i2c->regs + DIVIDER);
-
/* find the IRQ for this unit (note, this relies on the init call to
-
* ensure no current IRQs pending
-
*/
-
i2c->irq = ret = platform_get_irq(pdev, 0);
-
if (ret <= 0) {
-
dev_err(&pdev->dev, "cannot find IRQ\n");
-
goto err_iomap;
-
}
-
ret = request_irq(i2c->irq, nuc970_i2c_irq, IRQF_SHARED, //申请i2c中断
-
dev_name(&pdev->dev), i2c);
-
if (ret != 0) {
-
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
-
goto err_iomap;
-
}
-
/* Note, previous versions of the driver used i2c_add_adapter()
-
* to add the bus at any number. We now pass the bus number via
-
* the platform data, so if unset it will now default to always
-
* being bus 0.
-
*/
-
i2c->adap.nr = pdata->bus_num; //适配器所属i2c总线编号,这里为0
-
ret = i2c_add_numbered_adapter(&i2c->adap); //增加一个适配器
-
if (ret < 0) {
-
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
-
goto err_irq;
-
}
-
platform_set_drvdata(pdev, i2c);
-
dev_info(&pdev->dev, "%s: nuc970 I2C adapter\n",
-
dev_name(&i2c->adap.dev));
-
return 0;
-
err_irq:
-
free_irq(i2c->irq, i2c);
-
err_iomap:
-
iounmap(i2c->regs);
-
err_ioarea:
-
release_resource(i2c->ioarea);
-
kfree(i2c->ioarea);
-
err_clk:
-
clk_disable(i2c->clk);
-
clk_put(i2c->clk);
-
err_noclk:
-
kfree(i2c);
-
return ret;
-
}
i2c算法操作:
-
static const struct i2c_algorithm nuc970_i2c0_algorithm = {
-
.master_xfer = nuc970_i2c0_xfer,
-
.functionality = nuc970_i2c0_func,
-
};
-
/* declare our i2c functionality */
-
static u32 nuc970_i2c0_func(struct i2c_adapter *adap)
-
{
-
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |
-
I2C_FUNC_PROTOCOL_MANGLING;
-
}
-
static int nuc970_i2c0_xfer(struct i2c_adapter *adap,
-
struct i2c_msg *msgs, int num)
-
{
-
struct nuc970_i2c *i2c = (struct nuc970_i2c *)adap->algo_data; //adap->algo_data指向i2c,见上代码
-
int retry;
-
int ret;
-
nuc970_i2c0_enable_irq(i2c); //使能中断
-
for (retry = 0; retry < adap->retries; retry++) { //尝试次数
-
ret = nuc970_i2c0_doxfer(i2c, msgs, num); //传输数据
-
if (ret != -EAGAIN)
-
return ret;
-
dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
-
udelay(100);
-
}
-
return -EREMOTEIO;
-
}
-
static int nuc970_i2c0_doxfer(struct nuc970_i2c *i2c,
-
struct i2c_msg *msgs, int num)
-
{
-
unsigned long iicstat, timeout;
-
int spins = 20;
-
int ret;
-
ret = nuc970_i2c0_set_master(i2c);
-
if (ret != 0) {
-
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
-
ret = -EAGAIN;
-
goto out;
-
}
-
spin_lock_irq(&i2c->lock);
-
i2c->msg = msgs; //消息指针
-
i2c->msg_num = num; //消息的个数
-
i2c->msg_ptr = 0;
-
i2c->msg_idx = 0;
-
i2c->state = STATE_START; //I2C传输状态设置为启动
-
nuc970_i2c0_message_start(i2c, msgs); //开始发送消息,当发送一个字节后将触发中断,本函数退出,进入下面的休眠zhuangta
-
spin_unlock_irq(&i2c->lock);
-
//i2c->msg_num != 0 时wait_event_timeout(...)将等待HZ*5的超时时间
-
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
-
if(i2c->arblost) {
-
printk("arb lost, stop\n");
-
i2c->arblost = 0;
-
nuc970_i2c0_stop(i2c, 0);
-
}
-
ret = i2c->msg_idx;
-
/* having these next two as dev_err() makes life very
-
* noisy when doing an i2cdetect
-
*/
-
if (timeout == 0)
-
dev_dbg(i2c->dev, "timeout\n");
-
else if (ret != num)
-
dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
-
/* ensure the stop has been through the bus */
-
dev_dbg(i2c->dev, "waiting for bus idle\n");
-
/* first, try busy waiting briefly */
-
do {
-
iicstat = readl(i2c->regs + CSR);
-
} while ((iicstat & I2CBUSY) && --spins);
-
/* if that timed out sleep */
-
if (!spins) {
-
msleep(1);
-
iicstat = readl(i2c->regs + CSR);
-
}
-
if (iicstat & I2CBUSY)
-
dev_warn(i2c->dev, "timeout waiting for bus idle\n");
-
out:
-
return ret;
-
}
这里发送一个字节从设备地址,然后进入中断,发送数据,关于中断部分待分析!!!
-
static void nuc970_i2c0_message_start(struct nuc970_i2c *i2c,
-
struct i2c_msg *msg)
-
{
-
unsigned int addr = (msg->addr & 0x7f) << 1; //从设备地址
-
if (msg->flags & I2C_M_RD) //是否是读数据
-
addr |= 0x1;
-
writel(addr & 0xff, i2c->regs + TXR); //传输地址
-
writel(I2C_CMD_START | I2C_CMD_WRITE, i2c->regs + CMDR);
-
}
4.3 i2c中断函数
申请一个中断:
-
ret = request_irq(i2c->irq, nuc970_i2c_irq, IRQF_SHARED, //申请i2c中断
-
dev_name(&pdev->dev), i2c);
中断回调函数:
-
static irqreturn_t nuc970_i2c_irq(int irqno, void *dev_id)
-
{
-
struct nuc970_i2c *i2c = dev_id;
-
unsigned long status;
-
status = readl(i2c->regs + CSR);
-
writel(status | IRQFLAG, i2c->regs + CSR);
-
if (status & ARBIT_LOST) {
-
/* deal with arbitration loss */
-
dev_err(i2c->dev, "deal with arbitration loss\n");
-
i2c->arblost = 1;
-
goto out;
-
}
-
if (i2c->state == STATE_IDLE) {
-
dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
-
goto out;
-
}
-
/* pretty much this leaves us with the fact that we've
-
* transmitted or received whatever byte we last sent
-
*/
-
i2c_nuc970_irq_nextbyte(i2c, status);
-
out:
-
return IRQ_HANDLED;
-
}
进入到字节传输,i2c采用状态机的形式进行消息发送、接收,这个函数很重要:
-
static void i2c_nuc970_irq_nextbyte(struct nuc970_i2c *i2c, unsigned long iicstat)
-
{
-
unsigned char byte;
-
switch (i2c->state) {
-
case STATE_IDLE:
-
dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);
-
break;
-
case STATE_STOP:
-
nuc970_i2c0_disable_irq(i2c);
-
break;
-
case STATE_START:
-
/* last thing we did was send a start condition on the
-
* bus, or started a new i2c message
-
*/
-
if (iicstat & SLAVE_ACK &&
-
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
-
/* ack was not received... */
-
dev_dbg(i2c->dev, "ack was not received\n");
-
nuc970_i2c0_stop(i2c, -ENXIO);
-
break;
-
}
-
if (i2c->msg->flags & I2C_M_RD)
-
i2c->state = STATE_READ;
-
else
-
i2c->state = STATE_WRITE;
-
/* terminate the transfer if there is nothing to do
-
* as this is used by the i2c probe to find devices.
-
*/
-
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
-
nuc970_i2c0_stop(i2c, 0);
-
break;
-
}
-
if (i2c->state == STATE_READ)
-
goto prepare_read;
-
/* fall through to the write state, as we will need to
-
* send a byte as well
-
*/
-
case STATE_WRITE:
-
/* we are writing data to the device... check for the
-
* end of the message, and if so, work out what to do
-
*/
-
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
-
if (iicstat & SLAVE_ACK) {
-
dev_dbg(i2c->dev, "WRITE: No Ack\n");
-
nuc970_i2c0_stop(i2c, -ECONNREFUSED);
-
break;
-
}
-
}
-
retry_write:
-
if (!is_msgend(i2c)) {
-
byte = i2c->msg->buf[i2c->msg_ptr++];
-
writeb(byte, i2c->regs + TXR);
-
writel(I2C_CMD_WRITE, i2c->regs + CMDR);
-
} else if (!is_lastmsg(i2c)) { //是否是最后的msg消息,个数由i2c->msg_num决定
-
/* we need to go to the next i2c message */
-
dev_dbg(i2c->dev, "WRITE: Next Message\n");
-
i2c->msg_ptr = 0; //msg消息清零
-
i2c->msg_idx++; //msg消息个数索引
-
i2c->msg++; //切换到下一个待发送的消息
-
/* check to see if we need to do another message */
-
if (i2c->msg->flags & I2C_M_NOSTART) {
-
if (i2c->msg->flags & I2C_M_RD) {
-
/* cannot do this, the controller
-
* forces us to send a new START
-
* when we change direction
-
*/
-
nuc970_i2c0_stop(i2c, -EINVAL);
-
}
-
goto retry_write;
-
} else {
-
/* send the new start */
-
nuc970_i2c0_message_start(i2c, i2c->msg); //发送消息
-
i2c->state = STATE_START;
-
}
-
} else {
-
/* send stop */
-
nuc970_i2c0_stop(i2c, 0);
-
}
-
break;
-
case STATE_READ:
-
/* we have a byte of data in the data register, do
-
* something with it, and then work out whether we are
-
* going to do any more read/write
-
*/
-
byte = readb(i2c->regs + RXR);
-
i2c->msg->buf[i2c->msg_ptr++] = byte;
-
prepare_read:
-
if (is_msglast(i2c)) { //是否是消息的最后一个字节
-
/* last byte of buffer */
-
if (is_lastmsg(i2c)) //是否是最后的消息
-
writel(I2C_CMD_READ | I2C_CMD_NACK,
-
i2c->regs + CMDR);
-
} else if (is_msgend(i2c)) { //消息是否发送完
-
/* ok, we've read the entire buffer, see if there
-
* is anything else we need to do
-
*/
-
if (is_lastmsg(i2c)) { //是否是最后的消息
-
/* last message, send stop and complete */
-
dev_dbg(i2c->dev, "READ: Send Stop\n");
-
nuc970_i2c0_stop(i2c, 0);
-
} else {
-
/* go to the next transfer */
-
dev_dbg(i2c->dev, "READ: Next Transfer\n");
-
i2c->msg_ptr = 0;
-
i2c->msg_idx++;
-
i2c->msg++;
-
writel(I2C_CMD_READ, i2c->regs + CMDR);
-
}
-
} else {
-
writel(I2C_CMD_READ, i2c->regs + CMDR);
-
}
-
break;
-
}
-
}
4.4 向I2C核心增加适配器
ret = i2c_add_numbered_adapter(&i2c->adap); //增加一个适配器
-
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);
-
}
-
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 (id < 0)
-
return id == -ENOSPC ? -EBUSY : id;
-
return i2c_register_adapter(adap);
-
}
idr_alloc(...)可以参考这位大神写的博客:点击打开链接,目的就是给这里的i2c适配器分配一个id号
-
static int i2c_register_adapter(struct i2c_adapter *adap)
-
{
-
int res = 0;
-
/* Can't register until after driver model init */
-
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
-
res = -EAGAIN;
-
goto out_list;
-
}
-
/* Sanity checks */
-
if (unlikely(adap->name[0] == '\0')) {
-
pr_err("i2c-core: Attempt to register an adapter with "
-
"no name!\n");
-
return -EINVAL;
-
}
-
if (unlikely(!adap->algo)) {
-
pr_err("i2c-core: Attempt to register adapter '%s' with "
-
"no algo!\n", adap->name);
-
return -EINVAL;
-
}
-
rt_mutex_init(&adap->bus_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); //设置适配器的名称为“i2c-0”
-
adap->dev.bus = &i2c_bus_type;
-
adap->dev.type = &i2c_adapter_type;
-
res = device_register(&adap->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
-
/* bus recovery specific initialization */
-
if (adap->bus_recovery_info) {
-
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
-
if (!bri->recover_bus) {
-
dev_err(&adap->dev, "No recover_bus() found, not using recovery\n");
-
adap->bus_recovery_info = NULL;
-
goto exit_recovery;
-
}
-
/* Generic GPIO recovery */
-
if (bri->recover_bus == i2c_generic_gpio_recovery) {
-
if (!gpio_is_valid(bri->scl_gpio)) {
-
dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n");
-
adap->bus_recovery_info = NULL;
-
goto exit_recovery;
-
}
-
if (gpio_is_valid(bri->sda_gpio))
-
bri->get_sda = get_sda_gpio_value;
-
else
-
bri->get_sda = NULL;
-
bri->get_scl = get_scl_gpio_value;
-
bri->set_scl = set_scl_gpio_value;
-
} else if (!bri->set_scl || !bri->get_scl) {
-
/* Generic SCL recovery */
-
dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n");
-
adap->bus_recovery_info = NULL;
-
}
-
}
-
exit_recovery:
-
/* 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);
-
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;
-
}
设备注册流程如下:
a. 设备注册:device_register(&adap->dev)
-
int device_register(struct device *dev)
-
{
-
device_initialize(dev);
-
return device_add(dev);
-
}
device_initialize(...)设备初始化,包括设备集合、设备对象,即在/sys/系统下的目录及文件
b. 设备增加:device_add(...)
c. 设备添加到总线:bus_add_device(...)
-
int bus_add_device(struct device *dev)
-
{
-
struct bus_type *bus = bus_get(dev->bus);
-
int error = 0;
-
if (bus) {
-
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
-
error = device_add_attrs(bus, dev);
-
if (error)
-
goto out_put;
-
error = sysfs_create_link(&bus->p->devices_kset->kobj,
-
&dev->kobj, dev_name(dev));
-
if (error)
-
goto out_id;
-
error = sysfs_create_link(&dev->kobj,
-
&dev->bus->p->subsys.kobj, "subsystem");
-
if (error)
-
goto out_subsys;
-
//将本次设备的的链表dev->p->knode_bus添加到bus->p->klist_devices链表中,
-
//这里很重要,设备驱动在注册的时候就是通过该链表与对应的设备进行匹配
-
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
-
}
-
return 0;
-
out_subsys:
-
sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
-
out_id:
-
device_remove_attrs(bus, dev);
-
out_put:
-
bus_put(dev->bus);
-
return error;
-
}
这里将设备dev添加到i2c Bus总线的内核链表中bus->p->klist_devices,必须理解,下面在注册at24c512设备时会通过该链表进行匹配!!!
__i2c_first_dynamic_bus_num变量由上面i2c_register_board_info(...)函数注册时赋值,这里__i2c_first_dynamic_bus_num=1,adap->nr=0,条件成立,接下来扫描i2c的board_info信息板级文件信息:
-
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
-
{
-
struct i2c_devinfo *devinfo;
-
down_read(&__i2c_board_lock);
-
/*
-
检索在arch/arm/mach-nuc970/dev.c 中i2c_register_board_info() 注册的板级文件,
-
比对本次传入参数adapter适配器的I2C总线编号是否一致,一根总
-
线下可以挂接很多设备,当总线号相等时,这里会注册多个板
-
级文件中的设备.
-
*/
-
list_for_each_entry(devinfo, &__i2c_board_list, list) {
-
if (devinfo->busnum == adapter->nr
-
&& !i2c_new_device(adapter,
-
&devinfo->board_info))
-
dev_err(&adapter->dev,
-
"Can't create device at 0x%02x\n",
-
devinfo->board_info.addr);
-
//printk("###kernel: i2c_scan_static_board_info(): devinfo->busnum=%d, adapter->nr=%d, devinfo->board_info.type=%s.\n", \
-
// devinfo->busnum, adapter->nr, devinfo->board_info.type);
-
}
-
up_read(&__i2c_board_lock);
-
}
通过全局链表__i2c_board_list中的总线编号与本次注册的适配器进行比对,匹配成功调用i2c_new_device(...)注册一个新的设备。
-
struct i2c_client *
-
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
-
{
-
struct i2c_client *client;
-
int status;
-
client = kzalloc(sizeof *client, GFP_KERNEL);
-
if (!client)
-
return NULL;
-
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;
-
client->dev.of_node = info->of_node;
-
ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);
-
/* For 10-bit clients, add an arbitrary offset to avoid collisions */
-
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
-
client->addr | ((client->flags & I2C_CLIENT_TEN)
-
? 0xa000 : 0));
-
status = device_register(&client->dev); //将当前的客户端设备注册到设备树中
-
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;
-
}
i2c_new_device(...)的目的是通过“适配器参数+板级文件信息=创建一个I2C从设备客户端”,成功就返回该信息:
在回到i2c_register_adapter(...)
-
/* Notify drivers */
-
mutex_lock(&core_lock);
-
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
-
mutex_unlock(&core_lock);
i2c_bus_type总线类型定义:
-
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,
-
};
5.at24c512设备驱动注册
5.1 at24c512驱动注册
at24系列产品型号,包括产品名称、存储空间、地址bit位数:
-
static const struct i2c_device_id at24_ids[] = {
-
/* needs 8 addresses as A0-A2 are ignored */
-
{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
-
/* old variants can't be handled with this generic entry! */
-
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
-
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
-
/* spd is a 24c02 in memory DIMMs */
-
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
-
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
-
{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
-
/* 24rf08 quirk is handled at i2c-core */
-
{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
-
{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
-
{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
-
{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
-
{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
-
{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
-
{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
-
{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
-
{ "at24", 0 },
-
{ /* END OF LIST */ }
-
};
at24_driver驱动:
-
static struct i2c_driver at24_driver = {
-
.driver = {
-
.name = "at24",
-
.owner = THIS_MODULE,
-
},
-
.probe = at24_probe,
-
.remove = at24_remove,
-
.id_table = at24_ids,
-
};
-
static int __init at24_init(void)
-
{
-
if (!io_limit) {
-
pr_err("at24: io_limit must not be 0!\n");
-
return -EINVAL;
-
}
-
io_limit = rounddown_pow_of_two(io_limit);
-
return i2c_add_driver(&at24_driver);
-
}
-
module_init(at24_init);
-
#define __define_initcall(level,fn) \
-
static initcall_t __initcall_##fn __used \
-
__attribute__((__section__(".initcall" level ".init"))) = fn
-
#define device_initcall(fn) __define_initcall(fn, 6)
-
#define __initcall(fn) device_initcall(fn)
-
#define module_init(x) __initcall(x);
module_init(...)宏路径:linux-3.10.x-20171019\include\linux\init.h
-
#define i2c_add_driver(driver) \
-
i2c_register_driver(THIS_MODULE, driver)
向i2c核心注册驱动i2c_register_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; //绑定驱动的总线为i2c_bus_type
-
/* When registration returns, the driver core
-
* will have called probe() for all matching-but-unbound devices.
-
*/
-
res = driver_register(&driver->driver); //驱动注册
-
if (res)
-
return res;
-
/* Drivers should switch to dev_pm_ops instead. */
-
if (driver->suspend)
-
pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
-
driver->driver.name);
-
if (driver->resume)
-
pr_warn("i2c-core: driver [%s] using legacy resume method\n",
-
driver->driver.name);
-
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
-
INIT_LIST_HEAD(&driver->clients);
-
/* Walk the adapters that are already present */
-
i2c_for_each_dev(driver, __process_new_driver); //遍历i2c_bus_type结构体的链表,判定当前设备是否有匹配的,详见下面
-
return 0;
-
}
i2c_for_each_dev(....)函数:
-
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 || !bus->p)
-
return -EINVAL;
-
klist_iter_init_node(&bus->p->klist_devices, &i,//设备通过bus_add_device(...)添加时,最终会将设备加入到bus->p->klist_devices设备链表中
-
(start ? &start->p->knode_bus : NULL));
-
while ((dev = next_device(&i)) && !error)
-
error = fn(dev, data);
-
klist_iter_exit(&i);
-
return error;
-
}
在bus_for_each_dev(...)函数中,可以看到遍历bus->p->klist_devices链表,而该链表在前面提到过,就是设备加入到的链表,这里通过获取链表的设备(i2c_bus_type)与data(本次注册的at24c512设备),通过fn(...)函数指针__process_new_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));
-
}
-
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) {
-
dev_warn(&adap->dev, "%s: attach_adapter method is deprecated\n",
-
driver->driver.name);
-
dev_warn(&adap->dev, "Please use another way to instantiate "
-
"your i2c_client\n");
-
/* We ignore the return code; if it fails, too bad */
-
driver->attach_adapter(adap);
-
}
-
return 0;
-
}
i2c探测:
-
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);
-
address_list = driver->address_list;
-
if (!driver->detect || !address_list)
-
return 0;
-
/* Stop here if the classes do not match */
-
if (!(adapter->class & driver->class))
-
return 0;
-
/* Set up a temporary client to help detect callback */
-
temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-
if (!temp_client)
-
return -ENOMEM;
-
temp_client->adapter = adapter;
-
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
-
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); //探测地址是否在i2c总线上
-
if (unlikely(err))
-
break;
-
}
-
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);
-
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);
-
else
-
dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n",
-
info.type, info.addr);
-
}
-
return 0;
-
}
-
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)) //校验SMBUS是否是快速的
-
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_WRITE, 0,
-
I2C_SMBUS_QUICK, NULL);
-
else if (i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_BYTE)) //校验SMBUS是否支持读
-
err = i2c_smbus_xfer(adap, addr, 0, I2C_SMBUS_READ, 0, //
-
I2C_SMBUS_BYTE, &dummy);
-
else {
-
dev_warn(&adap->dev, "No suitable probing method supported\n");
-
err = -EOPNOTSUPP;
-
}
-
return err >= 0;
-
}
其中i2c_check_functionality(...)函数用来校验i2c的功能,最红调用adap->algo->functionality(...)函数指针,该函数是上面nuc970_i2c0_algorithm结构体定义的:
-
static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
-
{
-
return (func & i2c_get_functionality(adap)) == func;
-
}
-
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
-
{
-
return adap->algo->functionality(adap);
-
}
这里重点分析i2c_smbus_xfer(...):
-
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 | I2C_CLIENT_SCCB;
-
if (adapter->algo->smbus_xfer) { //判断适配器是否支持 smbus_xfer,通过前面的介绍,这里是不支持的!
-
i2c_lock_adapter(adapter);
-
/* 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;
-
}
-
i2c_unlock_adapter(adapter);
-
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
-
return res;
-
/*
-
* Fall back to i2c_smbus_xfer_emulated if the adapter doesn't
-
* implement native support for the SMBus operation.
-
*/
-
}
-
return i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
-
command, protocol, data);
-
}
由于adapter->algo->smbus_xfer条件不成立,所以这里会调用i2c_smbus_xfer_emulated(...):
-
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
-
unsigned short flags,
-
char read_write, u8 command, int size,
-
union i2c_smbus_data *data)
-
{
-
/* So we need to generate a series of msgs. In the case of writing, we
-
need to use only one message; when reading, we need two. We initialize
-
most things with sane defaults, to keep the code below somewhat
-
simpler. */
-
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
-
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
-
int num = read_write == I2C_SMBUS_READ ? 2 : 1;
-
int i;
-
u8 partial_pec = 0;
-
int status;
-
struct i2c_msg msg[2] = {
-
{
-
.addr = addr,
-
.flags = flags,
-
.len = 1,
-
.buf = msgbuf0,
-
}, {
-
.addr = addr,
-
.flags = flags | I2C_M_RD,
-
.len = 0,
-
.buf = msgbuf1,
-
},
-
};
-
msgbuf0[0] = command;
-
switch (size) {
-
case I2C_SMBUS_QUICK:
-
msg[0].len = 0;
-
/* Special case: The read/write field is used as data */
-
msg[0].flags = flags | (read_write == I2C_SMBUS_READ ?
-
I2C_M_RD : 0);
-
num = 1;
-
break;
-
case I2C_SMBUS_BYTE:
-
if (read_write == I2C_SMBUS_READ) {
-
/* Special case: only a read! */
-
msg[0].flags = I2C_M_RD | flags;
-
num = 1;
-
}
-
break;
-
case I2C_SMBUS_BYTE_DATA:
-
if (read_write == I2C_SMBUS_READ)
-
msg[1].len = 1;
-
else {
-
msg[0].len = 2;
-
msgbuf0[1] = data->byte;
-
}
-
break;
-
case I2C_SMBUS_WORD_DATA:
-
if (read_write == I2C_SMBUS_READ)
-
msg[1].len = 2;
-
else {
-
msg[0].len = 3;
-
msgbuf0[1] = data->word & 0xff;
-
msgbuf0[2] = data->word >> 8;
-
}
-
break;
-
case I2C_SMBUS_PROC_CALL:
-
num = 2; /* Special case */
-
read_write = I2C_SMBUS_READ;
-
msg[0].len = 3;
-
msg[1].len = 2;
-
msgbuf0[1] = data->word & 0xff;
-
msgbuf0[2] = data->word >> 8;
-
break;
-
case I2C_SMBUS_BLOCK_DATA:
-
if (read_write == I2C_SMBUS_READ) {
-
msg[1].flags |= I2C_M_RECV_LEN;
-
msg[1].len = 1; /* block length will be added by
-
the underlying bus driver */
-
} else {
-
msg[0].len = data->block[0] + 2;
-
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
-
dev_err(&adapter->dev,
-
"Invalid block write size %d\n",
-
data->block[0]);
-
return -EINVAL;
-
}
-
for (i = 1; i < msg[0].len; i++)
-
msgbuf0[i] = data->block[i-1];
-
}
-
break;
-
case I2C_SMBUS_BLOCK_PROC_CALL:
-
num = 2; /* Another special case */
-
read_write = I2C_SMBUS_READ;
-
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-
dev_err(&adapter->dev,
-
"Invalid block write size %d\n",
-
data->block[0]);
-
return -EINVAL;
-
}
-
msg[0].len = data->block[0] + 2;
-
for (i = 1; i < msg[0].len; i++)
-
msgbuf0[i] = data->block[i-1];
-
msg[1].flags |= I2C_M_RECV_LEN;
-
msg[1].len = 1; /* block length will be added by
-
the underlying bus driver */
-
break;
-
case I2C_SMBUS_I2C_BLOCK_DATA:
-
if (read_write == I2C_SMBUS_READ) {
-
msg[1].len = data->block[0];
-
} else {
-
msg[0].len = data->block[0] + 1;
-
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {
-
dev_err(&adapter->dev,
-
"Invalid block write size %d\n",
-
data->block[0]);
-
return -EINVAL;
-
}
-
for (i = 1; i <= data->block[0]; i++)
-
msgbuf0[i] = data->block[i];
-
}
-
break;
-
default:
-
dev_err(&adapter->dev, "Unsupported transaction %d\n", size);
-
return -EOPNOTSUPP;
-
}
-
i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
-
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
-
if (i) {
-
/* Compute PEC if first message is a write */
-
if (!(msg[0].flags & I2C_M_RD)) {
-
if (num == 1) /* Write only */
-
i2c_smbus_add_pec(&msg[0]);
-
else /* Write followed by read */
-
partial_pec = i2c_smbus_msg_pec(0, &msg[0]);
-
}
-
/* Ask for PEC if last message is a read */
-
if (msg[num-1].flags & I2C_M_RD)
-
msg[num-1].len++;
-
}
-
status = i2c_transfer(adapter, msg, num);
-
if (status < 0)
-
return status;
-
/* Check PEC if last message is a read */
-
if (i && (msg[num-1].flags & I2C_M_RD)) {
-
status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
-
if (status < 0)
-
return status;
-
}
-
if (read_write == I2C_SMBUS_READ)
-
switch (size) {
-
case I2C_SMBUS_BYTE:
-
data->byte = msgbuf0[0];
-
break;
-
case I2C_SMBUS_BYTE_DATA:
-
data->byte = msgbuf1[0];
-
break;
-
case I2C_SMBUS_WORD_DATA:
-
case I2C_SMBUS_PROC_CALL:
-
data->word = msgbuf1[0] | (msgbuf1[1] << 8);
-
break;
-
case I2C_SMBUS_I2C_BLOCK_DATA:
-
for (i = 0; i < data->block[0]; i++)
-
data->block[i+1] = msgbuf1[i];
-
break;
-
case I2C_SMBUS_BLOCK_DATA:
-
case I2C_SMBUS_BLOCK_PROC_CALL:
-
for (i = 0; i < msgbuf1[0] + 1; i++)
-
data->block[i] = msgbuf1[i];
-
break;
-
}
-
return 0;
-
}
i2c i2c_transfer(...)传输函数:
-
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
-
{
-
int ret;
-
/* 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 = i2c_trylock_adapter(adap);
-
if (!ret)
-
/* I2C activity is ongoing. */
-
return -EAGAIN;
-
} else {
-
i2c_lock_adapter(adap);
-
}
-
ret = __i2c_transfer(adap, msgs, num);
-
i2c_unlock_adapter(adap);
-
return ret;
-
} else {
-
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
-
return -EOPNOTSUPP;
-
}
-
}
-
int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
-
{
-
unsigned long orig_jiffies;
-
int ret, try;
-
/* 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;
-
}
-
return ret;
-
}
到这里看到adap->algo->master_xfer(adap, msgs, num)函数指针为适配器结构体nuc970_i2c0_algorithm中成员nuc970_i2c0_xfer,关于该函数内部的实现细节,见前面的分析,至此就分析完了i2c 设备“at24c512”的注册、数据传输!
6.at24_probe(...)
-
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
-
{
-
struct at24_platform_data chip;
-
bool writable;
-
int use_smbus = 0;
-
struct at24_data *at24;
-
int err;
-
unsigned i, num_addresses;
-
kernel_ulong_t magic;
-
if (client->dev.platform_data) {
-
chip = *(struct at24_platform_data *)client->dev.platform_data;
-
} else {
-
if (!id->driver_data) {
-
err = -ENODEV;
-
goto err_out;
-
}
-
magic = id->driver_data;
-
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
-
magic >>= AT24_SIZE_BYTELEN;
-
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
-
/*
-
* This is slow, but we can't know all eeproms, so we better
-
* play safe. Specifying custom eeprom-types via platform_data
-
* is recommended anyhow.
-
*/
-
chip.page_size = 1;
-
/* update chipdata if OF is present */
-
at24_get_ofdata(client, &chip);
-
chip.setup = NULL;
-
chip.context = NULL;
-
}
-
if (!is_power_of_2(chip.byte_len))
-
dev_warn(&client->dev,
-
"byte_len looks suspicious (no power of 2)!\n");
-
if (!chip.page_size) {
-
dev_err(&client->dev, "page_size must not be 0!\n");
-
err = -EINVAL;
-
goto err_out;
-
}
-
if (!is_power_of_2(chip.page_size))
-
dev_warn(&client->dev,
-
"page_size looks suspicious (no power of 2)!\n");
-
/* Use I2C operations unless we're stuck with SMBus extensions. */
-
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-
if (chip.flags & AT24_FLAG_ADDR16) {
-
err = -EPFNOSUPPORT;
-
goto err_out;
-
}
-
if (i2c_check_functionality(client->adapter,
-
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
-
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
-
} else if (i2c_check_functionality(client->adapter,
-
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
-
use_smbus = I2C_SMBUS_WORD_DATA;
-
} else if (i2c_check_functionality(client->adapter,
-
I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
-
use_smbus = I2C_SMBUS_BYTE_DATA;
-
} else {
-
err = -EPFNOSUPPORT;
-
goto err_out;
-
}
-
}
-
if (chip.flags & AT24_FLAG_TAKE8ADDR)
-
num_addresses = 8;
-
else
-
num_addresses = DIV_ROUND_UP(chip.byte_len,
-
(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
-
at24 = kzalloc(sizeof(struct at24_data) +
-
num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
-
if (!at24) {
-
err = -ENOMEM;
-
goto err_out;
-
}
-
mutex_init(&at24->lock);
-
at24->use_smbus = use_smbus;
-
at24->chip = chip;
-
at24->num_addresses = num_addresses;
-
/*
-
* Export the EEPROM bytes through sysfs, since that's convenient.
-
* By default, only root should see the data (maybe passwords etc)
-
*/
-
sysfs_bin_attr_init(&at24->bin);
-
at24->bin.attr.name = "eeprom";
-
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
-
at24->bin.read = at24_bin_read;
-
at24->bin.size = chip.byte_len;
-
at24->macc.read = at24_macc_read;
-
writable = !(chip.flags & AT24_FLAG_READONLY);
-
if (writable) {
-
if (!use_smbus || i2c_check_functionality(client->adapter,
-
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
-
unsigned write_max = chip.page_size;
-
at24->macc.write = at24_macc_write;
-
at24->bin.write = at24_bin_write;
-
at24->bin.attr.mode |= S_IWUSR;
-
if (write_max > io_limit)
-
write_max = io_limit;
-
if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
-
write_max = I2C_SMBUS_BLOCK_MAX;
-
at24->write_max = write_max;
-
/* buffer (data + address at the beginning) */
-
at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
-
if (!at24->writebuf) {
-
err = -ENOMEM;
-
goto err_struct;
-
}
-
} else {
-
dev_warn(&client->dev,
-
"cannot write due to controller restrictions.");
-
}
-
}
-
at24->client[0] = client;
-
/* use dummy devices for multiple-address chips */
-
for (i = 1; i < num_addresses; i++) {
-
at24->client[i] = i2c_new_dummy(client->adapter,
-
client->addr + i);
-
if (!at24->client[i]) {
-
dev_err(&client->dev, "address 0x%02x unavailable\n",
-
client->addr + i);
-
err = -EADDRINUSE;
-
goto err_clients;
-
}
-
}
-
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
-
if (err)
-
goto err_clients;
-
i2c_set_clientdata(client, at24);
-
dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
-
at24->bin.size, client->name,
-
writable ? "writable" : "read-only", at24->write_max);
-
if (use_smbus == I2C_SMBUS_WORD_DATA ||
-
use_smbus == I2C_SMBUS_BYTE_DATA) {
-
dev_notice(&client->dev, "Falling back to %s reads, "
-
"performance will suffer\n", use_smbus ==
-
I2C_SMBUS_WORD_DATA ? "word" : "byte");
-
}
-
/* export data to kernel code */
-
if (chip.setup)
-
chip.setup(&at24->macc, chip.context);
-
return 0;
-
err_clients:
-
for (i = 1; i < num_addresses; i++)
-
if (at24->client[i])
-
i2c_unregister_device(at24->client[i]);
-
kfree(at24->writebuf);
-
err_struct:
-
kfree(at24);
-
err_out:
-
dev_dbg(&client->dev, "probe error %d\n", err);
-
return err;
-
}
7.总结
①. i2c platfrom_device设备注册;
②. i2c板级文件"at24c512"加入到全局链表__i2c_board_list中;
③. i2c platform_driver设备驱动注册、总线适配器创建,并且将该设备加入到结构体内核链表bus->p->klist_devices中;
④. i2c 设备"at24c512"注册,通过i2c_for_each_dev(...)遍历内核链表bus->p->klist_devices,根据设备名“at24c513”进行匹配,条件成立将进行从设备地址探测,成功将在/dev下创建对应的设备(我原来在调试i2c-rtc驱动时,由于硬件电路 SCLK\SDA出现问题,导致探测失败,结果是没有在/dev下创建设备rtc0设备,所以这也是检验硬件电路的一种方式!)