linux powerpc i2c驱动 之 i2c adapter层的注册过程

来源:互联网 发布:苍蝇粉淘宝叫什么 编辑:程序博客网 时间:2024/05/16 09:21

I2cadapter层驱动,首先是其probe函数,adapter的结构体如下:

111

上面的结构体中其中algo实现i2c设备的读写操作函数


对于i2c_algorithm结构体,其中的functionality用来说明该算法所支持的i2c模式(有I2C_FUNC_I2CI2C_FUNC_10BIT_ADDRI2C_FUNC_SMBUS_BYTE

I2C_FUNC_SMBUS_BYTE_DATAI2C_FUNC_SMBUS_WORD_DATA等模式,具体看i2c.h中的定义)

对于master_xferi2c的读写函数,主要是根据i2c的时序图实现i2c的读出与写入操作,而smbus_xfer则用来实现smbus的读写函数,有些adapteralgo只实现了i2c的读写函数,当支持smbus操作时,就会使用i2c的读写函数来模拟smbus操作。

(在i2c核心层,i2c的通用读写函数i2c_transferi2c_master_send就是通过调用adapteralgo来实现对不同cpui2c总线进行读写的,而用户只需调用i2c_transferi2c_master_send等函数就可以实现i2c/smbus的读写,而不必关心其底层是怎么实现的)

下面的结构体是用来注册总线adapter驱动的注册有关的,当进行platform驱动注册时,就会将具体的adapter设备与总线adapter驱动实现交互


在上面的结构体中,xx_i2c_of_match用于和adapter的设备id进行比较,以实现adapter驱动与adapter设备的比较。而xx_i2c_of_probe

则用来实现adapters的声明与注册以及i2c设备的发现与注册


adapterprobe函数里,调用了iomap,该函数通过调用ioremap来实现地址映射,也就是将一个IO地址空间映射到内核的虚拟

地址空间中(因为在保护模式下,内核是不认物理地址的,当实现了地址映射后,就可以通过对虚拟地址的访问来实现对真正物理地

址的访问)。通过该函数就将i2c的基地址映射到内核虚拟地址了(可以查看具体cpudatasheet获取i2c的物理基地址)

i2c_add_adapter 主要实现adapter的注册与添加,而

of_register_i2c_devices则实现了i2c client的发现与添加。

static int __devinit xx_i2c_of_probe(struct of_device *op,

   const struct of_device_id *match)

{

struct xx_i2c_dev *i2c;

struct i2c_adapter *adap;

int result = 0;

u32 param1;

i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);

if (!i2c)

return -ENOMEM;

i2c->dev = &op->dev; /* for debug and error output */

  

i2c->base = of_iomap(op->node, 0);

if (!i2c->base) {

dev_err(i2c->dev, "failed to map controller\n");

result = -ENOMEM;

goto fail_map;

}

i2c->irq = irq_of_parse_and_map(op->node, 0);

if (i2c->irq != NO_IRQ) { /* i2c->irq = NO_IRQ implies polling */

result = request_irq(i2c->irq, i2c_xx_isr,

     0, "i2c-xx", i2c);

if (result < 0) {

dev_err(i2c->dev, "failed to attach interrupt\n");

goto fail_request;

}

}

i2c->write = writel;//master_xfer会调用这两个函数

i2c->read = readl;

init_completion(&i2c->cmd_complete);

mutex_init(&i2c->lock);

param1 = i2c->read(i2c->base + DW_IC_COMP_PARAM_1);

i2c->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1;

i2c->rx_fifo_depth = ((param1 >> 8)  & 0xff) + 1;

i2c_xx_init(i2c);

i2c->write(0,i2c->base + DW_IC_INTR_MASK); /* disable IRQ */

dev_set_drvdata(&op->dev, i2c);

adap = &i2c->adapter;

i2c_set_adapdata(adap, i2c);

adap->owner = THIS_MODULE;

adap->class = I2C_CLASS_HWMON;

strlcpy(adap->name, "Synopsys DesignWare I2C adapter",

sizeof(adap->name));

adap->algo = &i2c_xx_algo;

adap->dev.parent = &op->dev;

result = i2c_add_adapter(adap);

if (result) {

dev_err(i2c->dev, "failure adding adapter\n");

goto fail_add;

}

of_register_i2c_devices(adap, op->node);

return 0;

fail_add:

dev_set_drvdata(&op->dev, NULL);

//clk_disable(i2c->clk);

//clk_put(i2c->clk);

i2c->clk = NULL;

//fail_clk:

free_irq(i2c->irq, i2c);

fail_request:

irq_dispose_mapping(i2c->irq);

iounmap(i2c->base);

fail_map:

kfree(i2c);

return result;

}

of_register_i2c_devices中,用到了一个重要的结构体:i2c_board_info,将会描述具体i2c设备的addrtype等(其中typei2c clientid,这个值将会与i2c driverid进行比较,若相同则说明i2c clienti2c driver是相同的,就会进行i2c clienti2c driver的对于)。下面是i2c_board_info的定义

struct i2c_board_info {

char type[I2C_NAME_SIZE];

unsigned short flags;

unsigned short addr;

void *platform_data;

struct dev_archdata *archdata;

int irq;

};

对于powerpc,其设备的物理地址是写在设备树dts中的。设备的寄存器基地址、中断号等信息都写在dts中,内核通过platform_get_resource等标准接口即可自动获取这些值,实现了驱动和资源的分离。(在系统启动阶段会解析DTB文件,将相关资源注册到Platform bus,这样就可以通过platform_get_resource进行访问。)

of_register_i2c_devices里通过of_modalias_node与

of_get_property就可以获得i2c设备的typeaddr了;接着会调用

i2c_new_device,添加新的i2c client

void of_register_i2c_devices(struct i2c_adapter *adap,

     struct device_node *adap_node)

{

void *result;

struct device_node *node;

/*注册依附在该adapter上的所有 i2c client*/

for_each_child_of_node(adap_node, node) 

{

struct i2c_board_info info = {};

struct dev_archdata dev_ad = {};

const u32 *addr;

int len;

/*获取i2c 设备的名称,并将名称赋值到

i2c_board_infotype*/

if (of_modalias_node(node, info.type, sizeof(info.type)) < 0)

continue;

/*获取i2c设备的地址*/

addr = of_get_property(node, "reg", &len);

if (!addr || len < sizeof(int) || *addr > (1 << 10) - 1) {

printk(KERN_ERR

       "of-i2c: invalid i2c device entry\n");

continue;

}

info.irq = irq_of_parse_and_map(node, 0);

info.addr = *addr;

dev_archdata_set_node(&dev_ad, node);

info.archdata = &dev_ad;

request_module("%s", info.type);

result = i2c_new_device(adap, &info);

if (result == NULL) {

printk(KERN_ERR

       "of-i2c: Failed to load driver for %s\n",

       info.type);

irq_dispose_mapping(info.irq);

continue;

}

/*

 * Get the node to not lose the dev_archdata->of_node.

 * Currently there is no way to put it back, as well as no

 * of_unregister_i2c_devices() call.

 */

of_node_get(node);

}

}

i2c_new_device里,将会初始化一个i2c client。然后会调用device_register注册一个i2c设备

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 business */

status = i2c_check_addr(adap, client->addr);

if (status)

goto out_err;

client->dev.parent = &client->adapter->dev;

client->dev.bus = &i2c_bus_type;//这句很重要i2c client将会注册在该总线上

client->dev.type = &i2c_client_type;

dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),

     client->addr);

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);

kfree(client);

return NULL;

}

1、进行设备的初始化;2、调用device_add

int device_register(struct device *dev)

{

device_initialize(dev);

return device_add(dev);

}

device_add里,将会调用device_add_class_symlinksdevice_add_attrsbus_add_device,其中在bus_add_device就会将i2c client注册到i2c总线上的列表上,与i2c driver驱动注册到i2c总线的驱动列表上相似

int device_add(struct device *dev)

{

struct device *parent = NULL;

struct class_interface *class_intf;

int error = -EINVAL;

dev = get_device(dev);

if (!dev)

goto done;

if (!dev->p) {

error = device_private_init(dev);

if (error)

goto done;

}

if (dev->init_name) {

dev_set_name(dev, "%s", dev->init_name);

dev->init_name = NULL;

}

if (!dev_name(dev))

goto name_error;

pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

parent = get_device(dev->parent);

setup_parent(dev, parent);

/* use parent numa_node */

if (parent)

set_dev_node(dev, dev_to_node(parent));

/* first, register with generic layer. */

/* we require the name to be set before, and pass NULL */

error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

if (error)

goto Error;

/* notify platform of device entry */

if (platform_notify)

platform_notify(dev);

error = device_create_file(dev, &uevent_attr);

if (error)

goto attrError;

if (MAJOR(dev->devt)) {

error = device_create_file(dev, &devt_attr);

if (error)

goto ueventattrError;

error = device_create_sys_dev_entry(dev);

if (error)

goto devtattrError;

devtmpfs_create_node(dev);

}

error = device_add_class_symlinks(dev);

if (error)

goto SymlinkError;

error = device_add_attrs(dev);

if (error)

goto AttrsError;

error = bus_add_device(dev);//

if (error)

goto BusError;

error = dpm_sysfs_add(dev);

if (error)

goto DPMError;

device_pm_add(dev);

if (dev->bus)

blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

     BUS_NOTIFY_ADD_DEVICE, dev);

kobject_uevent(&dev->kobj, KOBJ_ADD);

bus_probe_device(dev);

if (parent)

klist_add_tail(&dev->p->knode_parent,

       &parent->p->klist_children);

if (dev->class) {

mutex_lock(&dev->class->p->class_mutex);

/* tie the class to the device */

klist_add_tail(&dev->knode_class,

       &dev->class->p->class_devices);

/* notify any interfaces that the device is here */

list_for_each_entry(class_intf,

    &dev->class->p->class_interfaces, node)

if (class_intf->add_dev)

class_intf->add_dev(dev, class_intf);

mutex_unlock(&dev->class->p->class_mutex);

}

done:

put_device(dev);

return error;

 DPMError:

bus_remove_device(dev);

 BusError:

device_remove_attrs(dev);

 AttrsError:

device_remove_class_symlinks(dev);

 SymlinkError:

if (MAJOR(dev->devt))

device_remove_sys_dev_entry(dev);

 devtattrError:

if (MAJOR(dev->devt))

device_remove_file(dev, &devt_attr);

 ueventattrError:

device_remove_file(dev, &uevent_attr);

 attrError:

kobject_uevent(&dev->kobj, KOBJ_REMOVE);

kobject_del(&dev->kobj);

 Error:

cleanup_device_parent(dev);

if (parent)

put_device(parent);

name_error:

kfree(dev->p);

dev->p = NULL;

goto done;

}

在该函数里, sysfs_create_link用来建立devbus之间的链接;然后调用

klist_add_tail,将devicedev->p->knode_bus添加到

bus->p->klist_devices,就实现将device添加到i2c 总线的device列表。这样,在i2c driver注册时,通过调用

bus_for_each_dev(i2c_register_driver-->driver_register

-->bus_add_driver-->driver_attach-->bus_for_each_dev),就会查找我们注册在bus->p->klist_devices上的每一个i2c设备,实现i2c总线上设备与驱动的对应。

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;

error = make_deprecated_bus_links(dev);

if (error)

goto out_deprecated;

klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

}

return 0;

out_deprecated:

sysfs_remove_link(&dev->kobj, "subsystem");

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;

}

原创粉丝点击