分类: LINUX
2011-04-19 14:58:19
内核版本:
首先在S
static void __init smdk2440_machine_init(void)
{
s
s
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}
s
void __init s
{
struct s
if (!pd)
pd = &default_i
npd = kmemdup(pd, sizeof(struct s
if (!npd)
printk(KERN_ERR "%s: no memory for platform data\n", __func__);
else if (!npd->cfg_gpio)
npd->cfg_gpio = s
/* s
s
/*最后将struct device 中的platform_data指针直指向了初始化后的 s
}
函数s
void s
{
s
s
}
s
static struct s
.flags = 0,
.slave_addr = 0x10,
.frequency = 100*1000,
.sda_delay = 100,
};
s
/**
* struct s
* @bus_num: The bus number to use (if possible).
* @flags: Any flags for the I
* @slave_addr: The I
* @frequency: The desired frequency in Hz of the bus. This is
* guaranteed to not be exceeded. If the caller does
* not care, use zero and the driver will select a
* useful default.
* @sda_delay: The delay (in ns) applied to SDA edges.
* @cfg_gpio: A callback to configure the pins for I
*/
struct s
int bus_num;
unsigned int flags;
unsigned int slave_addr;
unsigned long frequency;
unsigned int sda_delay;
void (*cfg_gpio)(struct platform_device *dev);
};
在函数smdk2440_machine_init(void)中,调用了
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
即将smdk2440_devices结构体数组中platform_device添加到了系统中,也就是添加到了platform总线上。smdk2440_devices的具体内容如下:
static struct platform_device *smdk2440_devices[] __initdata = {
&s
&s
&s
&s
&s
&s
&s
};
其中s
struct platform_device s
.name = "s
/*设备名,platform总线的match函数中会用设备名和驱动名的比较来绑定设备和驱动程序*/
#ifdef CONFIG_S
.id = 0,
#else
.id = -1,
#endif
.num_resources = ARRAY_SIZE(s
.resource = s
};
其中s
static struct resource s
[0] = {
.start = S
.end = S
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
},
};
在后面注册具体设备驱动时也会添加到paltform总线上,platform总线会将具体的设备和驱动进行绑定,这样驱动就可以操作具体的设备了。platform实际上是一个虚拟的总线,本质上也是一个设备。
好了,上面是一些板级的硬件设备资源向系统的注册,没有设计到具体的硬件操作,在加载驱动程序时,驱动程序会根据已经注册到系统的具体设备的硬件资源进行初始化,也就是进行一些硬件操作,控制硬件设备的正常工作,下面来分析驱动程序的加载过程。
S
在驱动的加载程序中,将platform_driver类型的s
static int __init i
{
return platform_driver_register(&s
}
分析platform_driver_register(&s
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
/*将device_driver中的probe,remove,shutdown函数指针指向platform_driver中的函数,后面进行驱动和设备绑定后会调用probe函数 */
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
下图即为Linux 2.6中引入的设备驱动模型的结构图(只是个总体框架,并不是指这的platform总线,设备和驱动)。
总线上包括设备和驱动的集合,总线上所有设备组成双向循环链表,包含在platform_device的设备集合中,总线上所有驱动组成双向循环链表,包含在platform_dirver的驱动集合中。
platform_driver_register(struct platform_driver *drv)函数实际上是对driver_register(struct device_driver *drv)函数的一个简单封装。driver_register()函数的调用关系如下
driver_register()
—>bus_add_driver(drv);
—> driver_attach(drv);
—> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)函数会遍历总线上所有的设备,并调用__driver_attach函数,判断驱动是否和设备匹配,若匹配则将struct device中的 struct device_driver *driver指向此驱动,也就是进行了驱动和设备的绑定,若不匹配,则继续遍历下一个设备。事实上,在向总线注册设备时,同样会进行类似的操作,遍历总线上所有驱动程序,找到则进行设备与驱动程序的绑定。
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
/*调用platform总线的match()函数,即platform_match函数,判断设备和驱动是否匹配,若匹配则返真,找到对应的设备,继续执行后面的程序,若没有找到,则返回假,函数执行结束 。这里我们的I
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
/*设备是否已经找到驱动?显然,这里没有找到驱动,因为设备在向系统中platform总线注册时还没有驱动注册到platform总线上,所以dev->drive = NULL */
if (!dev->driver)
driver_probe_device(drv, dev);
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
driver_probe_device(drv, dev)函数进行驱动与设备的绑定。
/**
* 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
*
* This function returns -ENODEV if the device is not registered,
* 1 if the device is bound sucessfully 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;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
ret = really_probe(dev, drv);
return ret;
}
really_probe函数中 进行device和driver的绑定,并调用用户在device_driver 中注册的probe()例程。
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
/*将device中的device_driver指针指向了这个driver,即完成device和driver的绑定*/
dev->driver = drv;
f (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
/*若总线设置了probe函数,则调用总线的probe函数,然而platform总线并没有设置 */
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
}
/* 否则,调用驱动注册在device_driver里的probe,这个函数中一般进行获得硬件资源,初始化硬件等操作,这里实际调用了s
else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
/*将设备添加到driver所支持的设备列表中(因为一个驱动可以支持多个设备),并通知bus上的设备,表明BUS_NOTIFY_BOUND_DRIVER */
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
到这里,I
下面开始分析linux/drivers/i
/* s
static int s
{
struct s
struct s
struct resource *res;
int ret;
/*这里pdev->dev.platform_data 在s
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
}
/*申请一段sizeof(struct s
i
if (!i
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
}
strlcpy(i
i
i
i
i
i
spin_lock_init(&i
init_waitqueue_head(&i
/* find the clock and enable it */
/*获得I
i
i
if (IS_ERR(i
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto err_noclk;
}
dev_dbg(&pdev->dev, "clock source %p\n", i
clk_enable(i
/* map the registers */
/*获取系统的物理地址,中断等资源信息,并进行物理地址到虚拟地址的映射 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto err_clk;
}
i
pdev->name);
if (i
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto err_clk;
}
i
if (i
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto err_ioarea;
}
dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
i
/* setup info block for the i
i
i
/* initialise the i
ret = s
if (ret != 0)
goto err_iomap;
/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
i
if (ret <= 0) {
dev_err(&pdev->dev, "cannot find IRQ\n");
goto err_iomap;
}
ret = request_irq(i
dev_name(&pdev->dev), i
if (ret != 0) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i
goto err_iomap;
}
ret = s
if (ret < 0) {
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
goto err_irq;
}
/* Note, previous versions of the driver used i
* 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.
*/
/* 向对应的I
i
ret = i
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i
goto err_cpufreq;
}
platform_set_drvdata(pdev, i
dev_info(&pdev->dev, "%s: S
return 0;
err_cpufreq:
s
err_irq:
free_irq(i
err_iomap:
iounmap(i
err_ioarea:
release_resource(i
kfree(i
err_clk:
clk_disable(i
clk_put(i
err_noclk:
kfree(i
return ret;
}
系统在初始化时会将系统硬件中的时钟注册进系统,用双向循环连接起来,在linux/arch/arm/plat-s
void __init s
{
/* initialise the clocks here, to allow other things like the
* console to use them, and to add new ones after the initialisation
*/
s
s
s
}
其中s
/* s
* Add all the clocks used by the s
* such as the S
* We cannot use a system device as we are needed before any
* of the init-calls that initialise the devices are actually
* done.*/
int __init s
{
unsigned long clkslow = __raw_readl(S
unsigned long clkcon = __raw_readl(S
struct clk *clkp;
struct clk *xtal;
int ret;
int ptr;
clk_upll.enable = s
if (s
printk(KERN_ERR "failed to register usb bus clock\n");
/* register clocks from clock array */
clkp = init_clocks;
for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
/* ensure that we note the clock state */
clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
ret = s
if (ret < 0) {
printk(KERN_ERR "Failed to register clock %s (%d)\n",
clkp->name, ret);
}
}
/* We must be careful disabling the clocks we are not intending to
* be using at boot time, as subsystems such as the LCD which do
* their own DMA requests to the bus can cause the system to lockup
* if they where in the middle of requesting bus access.
*
* Disabling the LCD clock if the LCD is active is very dangerous,
* and therefore the bootloader should be careful to not enable
* the LCD clock if it is not needed.
*/
/* install (and disable) the clocks we do not need immediately */
clkp = init_clocks_disable;
for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
ret = s
if (ret < 0) {
printk(KERN_ERR "Failed to register clock %s (%d)\n",
clkp->name, ret);
}
s
}
/* show the clock-slow value */
xtal = clk_get(NULL, "xtal");
printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
print_mhz(clk_get_rate(xtal) /
( 2 * S
(clkslow & S
(clkslow & S
(clkslow & S
s
return 0;
}
根据注释中给的提示,时钟被分成了两部分,init_clocks和init_clocks_disable,其中init_clocks中的时钟是系统启动时会开启的,而init_clocks_disable中的时钟则在系统启动时会关闭。其中函数s
{
.name = "i
.id = -1,
.parent = &clk_p,
.enable = s
.ctrlbit = S
}
结构中保存了I
s
/* find the clock and enable it */
i
i
if (IS_ERR(i
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto err_noclk;
}
dev_dbg(&pdev->dev, "clock source %p\n", i
clk_enable(i
clk_get(&pdev->dev, "i
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *p;
struct clk *clk = ERR_PTR(-ENOENT);
int idno;
if (dev == NULL || dev->bus != &platform_bus_type)
idno = -1;
else
idno = to_platform_device(dev)->id;
spin_lock(&clocks_lock);
list_for_each_entry(p, &clocks, list) {
if (p->id == idno &&
strcmp(id, p->name) == 0 &&
try_module_get(p->owner)) {
clk = p;
break;
}
}
s
/* s
static int s
{
unsigned long iicon = S
struct s
unsigned int freq;
/* get the plafrom data */
pdata = i
/* inititalise the gpio */
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i
/* write slave address */
/* 写入从设备的地址 */
writeb(pdata->slave_addr, i
dev_info(i
/* 使能接收发送中断和I
writel(iicon, i
/* we need to work out the divisors for the clock... */
/*这里freq用来获取实际的I
if (s
writel(0, i
dev_err(i
return -EINVAL;
}
/* todo - check that the i
dev_info(i
dev_dbg(i
return 0;
}
/* s
*
* work out a divisor for the user requested frequency setting,
* either by the requested frequency, or scanning the acceptable
* range of frequencies until something is found
*/
static int s
{
struct s
/*从系统平台时钟队列中获取pclk的时钟频率,大小为50MHZ */
unsigned long clkin = clk_get_rate(i
unsigned int divs, div1;
unsigned long target_frequency;
u32 iiccon;
int freq;
i
clkin /= 1000; /* clkin now in KHz */
dev_dbg(i
target_frequency = pdata->frequency ? pdata->frequency : 100000;
target_frequency /= 1000; /* Target frequency now in KHz */
/* 目标频率在前面default_i
freq = s
if (freq > target_frequency) {
dev_err(i
"Unable to achieve desired frequency %luKHz." \
" Lowest achievable %dKHz\n", target_frequency, freq);
return -EINVAL;
}
*got = freq; /*通过传入的指针返回实际频率 */
/* 根据时钟选择和分频系数配置对应硬件寄存器 */
iiccon = readl(i
iiccon &= ~(S
iiccon |= (divs-1);
if (div1 == 512)
iiccon |= S
writel(iiccon, i
/* 判断是否为S
if (s
unsigned long sda_delay;
if (pdata->sda_delay) {
sda_delay = (freq / 1000) * pdata->sda_delay;
sda_delay /= 1000000;
sda_delay = DIV_ROUND_UP(sda_delay, 5);
if (sda_delay > 3)
sda_delay = 3;
sda_delay |= S
} else
sda_delay = 0;
dev_dbg(i
writel(sda_delay, i
}
return 0;
}
到这里,I