amba总线驱动
R.wen
1).总线的注册
/*
* Primecells are part of the Advanced Microcontroller Bus Architecture,
* so we call the bus "amba".
*/
static struct bus_type amba_bustype = {
.name = "amba",
.dev_attrs = amba_dev_attrs,
.match = amba_match,
.uevent = amba_uevent,
.suspend = amba_suspend,
.resume = amba_resume,
};
static int __init amba_init(void)
{
return bus_register(&amba_bustype);
}
完整的bus_type结构如下:
struct bus_type {
const char * name;
struct subsystem subsys;
struct kset drivers;
struct kset devices;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
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, char **envp,
int num_envp, char *buffer, int buffer_size);
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);
};
我们看到,amba bus的注册结构中少了一般都会有的probe函数,当这个函数存在时,通用bus系统会调用这个函数,而这个函数又会调用设备的probe函数,否则,通用bus系统就会直接调用设备的probe函数。
2).设备驱动的注册函数.
/**
* amba_driver_register - register an AMBA device driver
* @drv: amba device driver structure
*
* Register an AMBA device driver with the Linux device model
* core. If devices pre-exist, the drivers probe function will
* be called.
*/
int amba_driver_register(struct amba_driver *drv)
{
drv->drv.bus = &amba_bustype;
#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn
SETFN(probe);
SETFN(remove);
SETFN(shutdown);
return driver_register(&drv->drv);
}
当一个设备想要注册进这个amba总线时,它就会调用以初始化好了的amba_driver结构调用amba_driver_register()这个驱动注册函数。而下一步这个函数就会调用通用的驱动注册函数driver_register。
这个函数对其他函数的调用路径如下:
driver_register -> bus_add_driver -> driver_attach -> __driver_attach(对每个设备) -> driver_probe_device -> drv->bus->match(dev, drv)(检查这个设备是否与新注册的驱动匹配) -> really_probe -> dev->bus->probe(dev)(如果存在) (否则) -> drv->probe(dev)
而且我们还可以看到,传递给驱动probe的参数,是一个具体的设备。这个结构是设备注册的时候已经分配好的了。
3)设备的注册
当往系统插入一个新的设备时,他就会调用amba_device_register函数,这个过程跟驱动注册的过程差不多。
总线维护两个队列,一个用于设备,一个用于驱动。当一个新的设备加入是,这个设备结构会链进设备队列。而当一个驱动加入是,它就会加入驱动队列。而且,无论是设备还是驱动,当有插入操作是,它都会遍历另一个队列,寻找相应的驱动或设备,如果找到匹配的, 就会就行drv->device = dev, dev->driver=drv操作,这个某个设备就和某个驱动关联了起来。这就是说这个驱动就可以用了。
amba设备的结构:
#define AMBA_NR_IRQS 2
struct amba_device {
struct device dev;
struct resource res;
u64 dma_mask;
unsigned int periphid;
unsigned int irq[AMBA_NR_IRQS];
};
/**
* amba_device_register - register an AMBA device
* @dev: AMBA device to register
* @parent: parent memory resource
*
* Setup the AMBA device, reading the cell ID if present.
* Claim the resource, and register the AMBA device with
* the Linux device manager.
*/
int amba_device_register(struct amba_device *dev, struct resource *parent)
{
u32 pid, cid;
void __iomem *tmp;
int i, ret;
dev->dev.release = amba_device_release;
dev->dev.bus = &amba_bustype;
dev->dev.dma_mask = &dev->dma_mask;
dev->res.name = dev->dev.bus_id;
if (!dev->dev.coherent_dma_mask && dev->dma_mask)
dev_warn(&dev->dev, "coherent dma mask is unset\n");
##申请资源
ret = request_resource(parent, &dev->res);
if (ret)
goto err_out;
##将物理地址映射到虚拟地址空间,是内核可见
tmp = ioremap(dev->res.start, SZ_4K);
if (!tmp) {
ret = -ENOMEM;
goto err_release;
}
for (pid = 0, i = 0; i < 4; i++)
pid |= (readl(tmp + 0xfe0 + 4 * i) & 255) << (i * 8);
for (cid = 0, i = 0; i < 4; i++)
cid |= (readl(tmp + 0xff0 + 4 * i) & 255) << (i * 8);
iounmap(tmp);
if (cid == 0xb105f00d)
dev->periphid = pid;
if (!dev->periphid) {
ret = -ENODEV;
goto err_release;
}
##注册设备,类似于驱动注册
ret = device_register(&dev->dev);
if (ret)
goto err_release;
if (dev->irq[0] != NO_IRQ)
ret = device_create_file(&dev->dev, &dev_attr_irq0);
if (ret == 0 && dev->irq[1] != NO_IRQ)
ret = device_create_file(&dev->dev, &dev_attr_irq1);
if (ret == 0)
return ret;
device_unregister(&dev->dev);
err_release:
release_resource(&dev->res);
err_out:
return ret;
}
对于设备,还会提供如下函数,用于申请一块可用的io内存
/**
* amba_request_regions - request all mem regions associated with device
* @dev: amba_device structure for device
* @name: name, or NULL to use driver name
*/
int amba_request_regions(struct amba_device *dev, const char *name)
{
int ret = 0;
if (!name)
name = dev->dev.driver->name;
if (!request_mem_region(dev->res.start, SZ_4K, name))
ret = -EBUSY;
return ret;
}
posted on 2008-08-20 11:26
puppy 阅读(557)
评论(0) 编辑 收藏 引用