分类: LINUX
2010-07-22 20:16:10
Platform设备注册
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
ek_spi_devices结构体如下:
/*
* SPI devices.
*/
static struct ek_spi_devices[] = {
{
.modalias = "spidev",
.chip_select = 1,
.max_speed_hz = 1 * 1000 * 1000,
.bus_num = 0,
.mode = SPI_MODE_3,
},
{
.modalias = "spidev",
.chip_select = 2,
.max_speed_hz = 1 * 1000 * 1000,
.bus_num = 0,
},
};
这里出现了spi_board_info的结构体,来看看它的定义
/*---------------------------------------------------------------------------*/
/*
* INTERFACE between board init code and SPI infrastructure.
*
* No SPI driver ever sees these SPI device table segments, but,
* it's how the SPI core (or adapters that get hotplugged) grows
* the driver model tree.
*
* As a rule, SPI devices can't be probed. Instead, board init code
* provides a table listing the devices which are present, with enough * information to bind and set up the device's driver. There's basic
* support for nonstatic configurations too; enough to handle adding
* parport adapters, or microcontrollers acting as USB-to-SPI bridges.
*/
板子初始化代码与spi的接口
虽然不能看到spi设备表,但是它表现了spi核心(或者可以热插拔的适配器)生长的驱动模型树。通常spi设备不能够被探测到,取代方案是板子的初始化代码提供一个存在的设备列表,这个设备列表必须提供足够的信息来绑定和启动设备驱动。这也是支持非静态配置的驱动的基础,也足以应付增加的parport适配器或者表现为USB-to-spi的微控制器。
/**
* struct spi_board_info - board-specific template for a SPI device
* @modalias: Initializes spi_device.modalias; identifies the driver. 设备名字,用来与后面的驱动的设备名字匹配,若与后面的platform_driver的设备的名字不一致,将会找不到设备
* @platform_data: Initializes spi_device.platform_data; the particular data stored there is driver-specific.初始化spi_device.platform_data,其中存储的数据是私有的
* @controller_data: Initializes spi_device.controller_data; some controllers need hints about hardware setup, e.g. for DMA.初始化spi_device.controller_data,一些控制器关于硬件启动比如DMA所必须的提示信息
* @irq: Initializes spi_device.irq; depends on how the board is wired.初始化spi_device.irq,由板子的线的接法决定。
* @max_speed_hz: Initializes spi_device.max_speed_hz; based on limits from the chip datasheet and board-specific signal quality issues.初始化spi_device.max_speed_hz,由芯片和板子具体的信号质量决定
* @bus_num: Identifies which spi_master parents the spi_device; unused by spi_new_device(), and otherwise depends on board wiring.标志哪个spi_master来主宰spi_device, spi_new_device()不需要使用它,其他的由板子的接法决定。
* @chip_select: Initializes spi_device.chip_select; depends on how the board is wired.初始化spi_device.chip_select,由板子的线的接法决定
* @mode: Initializes spi_device.mode; based on the chip datasheet, board
* wiring (some devices support both 3WIRE and standard modes), and
* possibly presence of an inverter in the chipselect path.spi的模式
*
* When adding new SPI devices to the device tree,
* as a partial device template. They hold information which can't always
* be determined by drivers. Information that probe() can establish (such
* as the default transfer wordsize) is not included here.
*当增加一个新的spi设备到设备树上时,这些结构就会作为结构模板的一部分。它们保存着那些不能由驱动决定的信息。Probe()可以建立的信息(比如默认的字节传输宽度)没有包括在这里。
* These structures are used in two places. Their primary role is to
* be stored in tables of board-specific device descriptors, which are
* declared early in board initialization and then used (much later) to
* populate a controller's device tree after the that controller's driver
* initializes. A secondary (and atypical) role is as a parameter to
* spi_new_device() call, which happens after those controller drivers
* are active in some dynamic board configuration models.
*/这些结构体用于2个地方。主要角色就是用于存储板子上具体的设备的描述,这些描述在板子初始化的就被提前声明,然后在控制器的驱动初始化后被用于构成控制器的设备树的一部分。第二个用途(和非典型的)是作为spi_new_device()的一个参数,主要用于控制器驱动动态加载的模式。
struct spi_board_info {
/* the device name and module name are coupled, like platform_bus;
* "modalias" is normally the driver name.
*
* platform_data goes to spi_device.dev.platform_data,
* controller_data goes to spi_device.controller_data,
* irq is copied too
*/
char modalias[32];
const void *platform_data;
void *controller_data;
int irq;
/* slower signaling on noisy or low voltage boards */
u32 max_speed_hz;
/* bus_num is board specific and matches the bus_num of some
* spi_master that will probably be registered later.
*
* chip_select reflects how this chip is wired to that master;
* it's less than num_chipselect.
*/
u16 bus_num;
u16 chip_select;
/* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong.
*/
u8 mode;
/* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff
* needed to behave without being bound to a driver:
* - quirks like clock rate mattering when not selected
*/
};
现在这个结构体应该比较清晰了,然后进入函数
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices)
{
int i;
unsigned long cs_pin;
short enable_spi0 = 0;
short enable_spi1 = 0;
/* Choose SPI chip-selects */
for (i = 0; i < nr_devices; i++) {
if (devices[i].controller_data)
cs_pin = (unsigned long) devices[i].controller_data;
else if (devices[i].bus_num == 0)
cs_pin = spi0_standard_cs[devices[i].chip_select];
else
cs_pin = spi1_standard_cs[devices[i].chip_select];
if (devices[i].bus_num == 0)
enable_spi0 = 1;
else
enable_spi1 = 1;
/* enable chip-select pin */
at91_set_gpio_output(cs_pin, 1);
/* pass chip-select pin to driver */
devices[i].controller_data = (void *) cs_pin;
}
spi_register_board_info(devices, nr_devices);
/* Configure SPI bus(es) */
if (enable_spi0) {
at91_set_B_periph(AT91_PIN_PA0, 0); /* SPI0_MISO */
at91_set_B_periph(AT91_PIN_PA1, 0); /* SPI0_MOSI */
at91_set_B_periph(AT91_PIN_PA2, 0); /* SPI0_SPCK */
at91_clock_associate("spi0_clk", &at91sam9263_spi0_device.dev, "spi_clk");
platform_device_register(&at91sam9263_spi0_device);
}
if (enable_spi1) {
at91_set_A_periph(AT91_PIN_PB12, 0); /* SPI1_MISO */
at91_set_A_periph(AT91_PIN_PB13, 0); /* SPI1_MOSI */
at91_set_A_periph(AT91_PIN_PB14, 0); /* SPI1_SPCK */
at91_clock_associate("spi1_clk", &at91sam9263_spi1_device.dev, "spi_clk");
platform_device_register(&at91sam9263_spi1_device);
}
}
先选择好控制器,然后选择片选脚,再初始化相应的PIN脚,时钟。
我们主要关注的以下2个函数。
spi_register_board_info(devices, nr_devices);
platform_device_register(&at91sam9263_spi0_device);
spi_register_board_info这个函数比较简单,主要是spi没有什么太多的信息,基本上什么都不用做。如下
static inline int spi_register_board_info(struct spi_board_info const *info, unsigned n)
{ return 0; }
然后看下platform_device_register(&at91sam9263_spi0_device);
看看at91sam9263_spi0_device的定义
static struct platform_device at91sam9263_spi0_device = {
.name = "atmel_spi",
.id = 0,
.dev = {
.dma_mask = &spi_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.resource = spi0_resources,
.num_resources = ARRAY_SIZE(spi0_resources),
};
结构体platform_device也介绍下
struct platform_device {
const char * name; // 设备名
int id; // 设备编号
struct device dev; // device 结构
u32 num_resources; //设备所使用的各类资源数量
struct resource * resource; // 资源 主要是io内存和irq
};
* platform_device.name ... which is also used to for driver matching.
可用于和驱动匹配
* platform_device.id ... the device instance number, or else "-1" to indicate there's only one.
设备的编号,如果是唯一设备,则用-1表示。
这里主要关心dev及spi0_resources,
.dev = {
.dma_mask = &spi_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
这里用到了DMA相关,看下device里面关于DMA的部分
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct device_dma_parameters *dma_parms;
spi_dmamask定义如下
static u64 spi_dmamask = DMA_BIT_MASK(32);说明该设备是支持32位DMA操作的
然后是spi0_resources,
static struct resource spi0_resources[] = {
[0] = {
.start = AT91SAM9263_BASE_SPI0,
.end = AT91SAM9263_BASE_SPI0 + SZ_16K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = AT91SAM9263_ID_SPI0,
.end = AT91SAM9263_ID_SPI0,
.flags = IORESOURCE_IRQ,
},
};
很明显它用到了IO内存和IO中断。
进入platform_device_register(&at91sam9263_spi0_device); // 平台设备注册
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*/
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
从函数中可以看出它就是把platform_device的设备初始化,然后再提供给platform_device_add
先看device_initialize(&pdev->dev);
/**
* device_initialize - init device structure.
* @dev: device.
*
* This prepares the device for use by other layers,
* including adding it to the device hierarchy.
* It is the first half of device_register(), if called by
* that, though it can also be called separately, so one
* may use @dev's fields (e.g. the refcount).
*/
这为设备让其他层使用准备,包括把它增加到设备的层次结构中去。
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}
这段代码就不说了,知道怎么回事就可以了。然后看看
/**
* platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we're adding
*
* This is part 2 of platform_device_register(), though may be called
* separately _iff_ pdev was allocated by platform_device_alloc().
*/
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent) // 如果为空则直接挂到platform上
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
if (pdev->id != -1) // 如果设备不是唯一的,则需要加上序号
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
pdev->id);
else // 设备唯一 则直接把名字复制上去
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
// 以下的循环主要是为了个平台设备申请资源
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) {
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d\n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0)
if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
release_resource(&pdev->resource[i]);
return ret;
}
这里先看看以下几个结构体
struct bus_type {
const char *name; // 总线的名字
struct bus_attribute *bus_attrs; //总线的属性
struct device_attribute *dev_attrs; // 设备属性
struct driver_attribute *drv_attrs; // 驱动属性
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct pm_ext_ops *pm;
struct bus_type_private *p;
};
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = PLATFORM_PM_OPS_PTR,
};
只有非常少的bus_tyoe成员需要初始化;它们中的大多数都由设备模型核心所控制,但是我们必须为总线指定名字以及其他必要的方法。
上面这个platform总线名字是platform,
属性platform_dev_attrs,
static struct device_attribute platform_dev_attrs[] = {
__ATTR_RO(modalias),
__ATTR_NULL,
};
也仅一个名字。
platform_match就是看总线设备与总线驱动能否匹配上,实际上就是比较了下名字。
platform_uevent主要用于热插拔。
看看函数的最后,ret = device_add(&pdev->dev);
进去瞅瞅 ,函数相对长点,但是也挺容易。
/**
* device_add - add device to device hierarchy.
* @dev: device.
*
* This is part 2 of device_register(), though may be called
* separately _iff_ device_initialize() has been called separately.
*
* This adds it to the kobject hierarchy via kobject_add(), adds it
* to the global and sibling lists for the device, then
* adds it to the other relevant subsystems of the driver model.
*/
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev); // 设备使用计数加1
if (!dev)
goto done;
/* Temporarily support init_name if it is set.
* It will override bus_id for now */
if (dev->init_name) // 如果有初始化名字,则覆盖掉bus_id
dev_set_name(dev, "%s", dev->init_name);
if (!strlen(dev->bus_id))
goto done;
pr_debug("device: '%s': %s\n", dev->bus_id, __func__);
parent = get_device(dev->parent); // 设备parent引用计数加1
setup_parent(dev, parent); // 建立起设备parent的结构
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
/* notify clients of device entry (new way) */
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
error = device_create_file(dev, &uevent_attr); // 增加uevent属性
if (error)
goto attrError;
if (MAJOR(dev->devt)) { // 如果定义了设备号,则增加dev设备号属性
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
}
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);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_attach_device(dev);
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
list_add_tail(&dev->node, &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:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
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);
goto done;
}
从以上可以看出设备注册的流程
platform_device_register->device_initialize->device_add->setup_parent->kobject_add->device_create_file->device_add_attrs->bus_add_device->kobject_uevent->bus_attach_device
怎样把设备添加到总线上呢,详细分析下bus_attach_device();
/**
* bus_attach_device - add device to bus
* @dev: device tried to attach to a driver
*
* - Add device to bus's list of devices. 增加设备到总线下面的设备上去
* - Try to attach to driver. 尝试连接到驱动程序
*/
void bus_attach_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) { // 如果有对应的总线
if (bus->p->drivers_autoprobe) // 如果 drivers_autoprobe为1
ret = device_attach(dev); // 则把相应的设备尝试连到驱动上
WARN_ON(ret < 0);
if (ret >= 0)
klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
}
}
进入ret = device_attach(dev)分析
/**
* device_attach - try to attach device to a driver. 尝试把设备连上驱动
* @dev: device.
*
* Walk the list of drivers that the bus has and call
* driver_probe_device() for each pair. If a compatible
* pair is found, break out and return.
*遍历总线上的驱动并且调用driver_probe_device(),如果有匹配的找到,则跳出并返回。
* Returns 1 if the device was bound to a driver;
* 0 if no matching device was found;
* -ENODEV if the device is not registered.
*
* When called for a USB interface, @dev->parent->sem must be held.
*/
int device_attach(struct device *dev)
{
int ret = 0;
down(&dev->sem);
if (dev->driver) { // 如果指定了驱动
ret = device_bind_driver(dev); // 则尝试把驱动绑定到设备上
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else { // 若没有指定驱动,则遍历总线以寻找驱动
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
up(&dev->sem);
return ret;
}
分析下bus_for_each_drv
/**
* bus_for_each_drv - driver iterator
* @bus: bus we're dealing with.
* @start: driver to start iterating on.
* @data: data to pass to the callback.
* @fn: function to call for each driver.
*
* This is nearly identical to the device iterator above.
* We iterate over each driver that belongs to @bus, and call
* @fn for each. If @fn returns anything but 0, we break out
* and return it. If @start is not NULL, we use it as the head
* of the list.
*该函数迭代了在总线上的每个驱动,并将相关的驱动结构传递给fn,同时传递给data值,如果start是NULL,则将从总线上的第一个驱动迭代,否则将从start后的第一个驱动迭代。如果fn返回一个非零值,则停止迭代,而这个值也会返回
* NOTE: we don't return the driver that returns a non-zero
* value, nor do we leave the reference count incremented for that
* driver. If the caller needs to know that info, it must set it
* in the callback. It must also be sure to increment the refcount
* so it doesn't disappear before returning to the caller.
*/
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{
struct klist_iter i;
struct device_driver *drv;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_drivers, &i,
start ? &start->p->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}
看看调用的迭代函数__device_attach(),里面调用了函数driver_probe_device
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* First, we call the bus's match function, if one present, which should
* compare the device IDs the driver supports with the device IDs of the
* device. Note we don't do this ourselves because we don't know the
* format of the ID structures, nor what is to be considered a match and
* what is not.
*首先,会调用总线匹配函数,就是简单的比较下driver的device id和device的device id。
* This function returns 1 if a match is found, -ENODEV if the device is
* not registered, and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev)) // 如果设备已经注册,返回错误
return -ENODEV;
if (drv->bus->match && !drv->bus->match(dev, drv)) //调用总线定义的
goto done; //match方法进行匹配
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev->bus_id, drv->name);
ret = really_probe(dev, drv); // 增加一些属性,并真正把设备和驱动绑定上
done:
return ret;
}
从以上可以看出匹配总线上驱动的软件流程如下
:bus_attach_device->device_attach->__device_attach->driver_probe_device()->really_probe->driver_bound