Skip to content

Commit

Permalink
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-cor…
Browse files Browse the repository at this point in the history
…e-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6:
  UIO: fix specific device driver missing statement for depmod
  Driver core: remove pr_fmt() from dynamic_dev_dbg() printk
  driver core: prevent device_for_each_child from oopsing
  dynamic debug: resurrect old pr_debug() semantics as pr_devel()
  Driver Core: early platform driver
  proc: mounts_poll() make consistent to mdstat_poll
  sysfs: sysfs poll keep the poll rule of regular file.
  driver core: allow non-root users to listen to uevents
  driver core: fix driver_match_device
  sysfs: don't use global workqueue in sysfs_schedule_callback()
  • Loading branch information
Linus Torvalds committed Apr 17, 2009
2 parents dd26bf6 + 912335c commit 74a205a
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 9 deletions.
59 changes: 59 additions & 0 deletions Documentation/driver-model/platform.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,62 @@ three different ways to find such a match:
be probed later if another device registers. (Which is OK, since
this interface is only for use with non-hotpluggable devices.)


Early Platform Devices and Drivers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The early platform interfaces provide platform data to platform device
drivers early on during the system boot. The code is built on top of the
early_param() command line parsing and can be executed very early on.

Example: "earlyprintk" class early serial console in 6 steps

1. Registering early platform device data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The architecture code registers platform device data using the function
early_platform_add_devices(). In the case of early serial console this
should be hardware configuration for the serial port. Devices registered
at this point will later on be matched against early platform drivers.

2. Parsing kernel command line
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The architecture code calls parse_early_param() to parse the kernel
command line. This will execute all matching early_param() callbacks.
User specified early platform devices will be registered at this point.
For the early serial console case the user can specify port on the
kernel command line as "earlyprintk=serial.0" where "earlyprintk" is
the class string, "serial" is the name of the platfrom driver and
0 is the platform device id. If the id is -1 then the dot and the
id can be omitted.

3. Installing early platform drivers belonging to a certain class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The architecture code may optionally force registration of all early
platform drivers belonging to a certain class using the function
early_platform_driver_register_all(). User specified devices from
step 2 have priority over these. This step is omitted by the serial
driver example since the early serial driver code should be disabled
unless the user has specified port on the kernel command line.

4. Early platform driver registration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiled-in platform drivers making use of early_platform_init() are
automatically registered during step 2 or 3. The serial driver example
should use early_platform_init("earlyprintk", &platform_driver).

5. Probing of early platform drivers belonging to a certain class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The architecture code calls early_platform_driver_probe() to match
registered early platform devices associated with a certain class with
registered early platform drivers. Matched devices will get probed().
This step can be executed at any point during the early boot. As soon
as possible may be good for the serial port case.

6. Inside the early platform driver probe()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The driver code needs to take special care during early boot, especially
when it comes to memory allocation and interrupt registration. The code
in the probe() function can use is_early_platform_device() to check if
it is called at early platform device or at the regular platform device
time. The early serial driver performs register_console() at this point.

For further information, see <linux/platform_device.h>.
2 changes: 1 addition & 1 deletion drivers/base/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ extern int driver_probe_device(struct device_driver *drv, struct device *dev);
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match && drv->bus->match(dev, drv);
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

extern void sysdev_shutdown(void);
Expand Down
3 changes: 3 additions & 0 deletions drivers/base/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,9 @@ int device_for_each_child(struct device *parent, void *data,
struct device *child;
int error = 0;

if (!parent->p)
return 0;

klist_iter_init(&parent->p->klist_children, &i);
while ((child = next_device(&i)) && !error)
error = fn(child, data);
Expand Down
239 changes: 239 additions & 0 deletions drivers/base/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,8 @@ int __init platform_bus_init(void)
{
int error;

early_platform_cleanup();

error = device_register(&platform_bus);
if (error)
return error;
Expand Down Expand Up @@ -1020,3 +1022,240 @@ u64 dma_get_required_mask(struct device *dev)
}
EXPORT_SYMBOL_GPL(dma_get_required_mask);
#endif

static __initdata LIST_HEAD(early_platform_driver_list);
static __initdata LIST_HEAD(early_platform_device_list);

/**
* early_platform_driver_register
* @edrv: early_platform driver structure
* @buf: string passed from early_param()
*/
int __init early_platform_driver_register(struct early_platform_driver *epdrv,
char *buf)
{
unsigned long index;
int n;

/* Simply add the driver to the end of the global list.
* Drivers will by default be put on the list in compiled-in order.
*/
if (!epdrv->list.next) {
INIT_LIST_HEAD(&epdrv->list);
list_add_tail(&epdrv->list, &early_platform_driver_list);
}

/* If the user has specified device then make sure the driver
* gets prioritized. The driver of the last device specified on
* command line will be put first on the list.
*/
n = strlen(epdrv->pdrv->driver.name);
if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) {
list_move(&epdrv->list, &early_platform_driver_list);

if (!strcmp(buf, epdrv->pdrv->driver.name))
epdrv->requested_id = -1;
else if (buf[n] == '.' && strict_strtoul(&buf[n + 1], 10,
&index) == 0)
epdrv->requested_id = index;
else
epdrv->requested_id = EARLY_PLATFORM_ID_ERROR;
}

return 0;
}

/**
* early_platform_add_devices - add a numbers of early platform devices
* @devs: array of early platform devices to add
* @num: number of early platform devices in array
*/
void __init early_platform_add_devices(struct platform_device **devs, int num)
{
struct device *dev;
int i;

/* simply add the devices to list */
for (i = 0; i < num; i++) {
dev = &devs[i]->dev;

if (!dev->devres_head.next) {
INIT_LIST_HEAD(&dev->devres_head);
list_add_tail(&dev->devres_head,
&early_platform_device_list);
}
}
}

/**
* early_platform_driver_register_all
* @class_str: string to identify early platform driver class
*/
void __init early_platform_driver_register_all(char *class_str)
{
/* The "class_str" parameter may or may not be present on the kernel
* command line. If it is present then there may be more than one
* matching parameter.
*
* Since we register our early platform drivers using early_param()
* we need to make sure that they also get registered in the case
* when the parameter is missing from the kernel command line.
*
* We use parse_early_options() to make sure the early_param() gets
* called at least once. The early_param() may be called more than
* once since the name of the preferred device may be specified on
* the kernel command line. early_platform_driver_register() handles
* this case for us.
*/
parse_early_options(class_str);
}

/**
* early_platform_match
* @edrv: early platform driver structure
* @id: id to match against
*/
static __init struct platform_device *
early_platform_match(struct early_platform_driver *epdrv, int id)
{
struct platform_device *pd;

list_for_each_entry(pd, &early_platform_device_list, dev.devres_head)
if (platform_match(&pd->dev, &epdrv->pdrv->driver))
if (pd->id == id)
return pd;

return NULL;
}

/**
* early_platform_left
* @edrv: early platform driver structure
* @id: return true if id or above exists
*/
static __init int early_platform_left(struct early_platform_driver *epdrv,
int id)
{
struct platform_device *pd;

list_for_each_entry(pd, &early_platform_device_list, dev.devres_head)
if (platform_match(&pd->dev, &epdrv->pdrv->driver))
if (pd->id >= id)
return 1;

return 0;
}

/**
* early_platform_driver_probe_id
* @class_str: string to identify early platform driver class
* @id: id to match against
* @nr_probe: number of platform devices to successfully probe before exiting
*/
static int __init early_platform_driver_probe_id(char *class_str,
int id,
int nr_probe)
{
struct early_platform_driver *epdrv;
struct platform_device *match;
int match_id;
int n = 0;
int left = 0;

list_for_each_entry(epdrv, &early_platform_driver_list, list) {
/* only use drivers matching our class_str */
if (strcmp(class_str, epdrv->class_str))
continue;

if (id == -2) {
match_id = epdrv->requested_id;
left = 1;

} else {
match_id = id;
left += early_platform_left(epdrv, id);

/* skip requested id */
switch (epdrv->requested_id) {
case EARLY_PLATFORM_ID_ERROR:
case EARLY_PLATFORM_ID_UNSET:
break;
default:
if (epdrv->requested_id == id)
match_id = EARLY_PLATFORM_ID_UNSET;
}
}

switch (match_id) {
case EARLY_PLATFORM_ID_ERROR:
pr_warning("%s: unable to parse %s parameter\n",
class_str, epdrv->pdrv->driver.name);
/* fall-through */
case EARLY_PLATFORM_ID_UNSET:
match = NULL;
break;
default:
match = early_platform_match(epdrv, match_id);
}

if (match) {
if (epdrv->pdrv->probe(match))
pr_warning("%s: unable to probe %s early.\n",
class_str, match->name);
else
n++;
}

if (n >= nr_probe)
break;
}

if (left)
return n;
else
return -ENODEV;
}

/**
* early_platform_driver_probe
* @class_str: string to identify early platform driver class
* @nr_probe: number of platform devices to successfully probe before exiting
* @user_only: only probe user specified early platform devices
*/
int __init early_platform_driver_probe(char *class_str,
int nr_probe,
int user_only)
{
int k, n, i;

n = 0;
for (i = -2; n < nr_probe; i++) {
k = early_platform_driver_probe_id(class_str, i, nr_probe - n);

if (k < 0)
break;

n += k;

if (user_only)
break;
}

return n;
}

/**
* early_platform_cleanup - clean up early platform code
*/
void __init early_platform_cleanup(void)
{
struct platform_device *pd, *pd2;

/* clean up the devres list used to chain devices */
list_for_each_entry_safe(pd, pd2, &early_platform_device_list,
dev.devres_head) {
list_del(&pd->dev.devres_head);
memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head));
}
}

1 change: 1 addition & 0 deletions drivers/uio/uio_cif.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,6 @@ static void __exit hilscher_exit_module(void)
module_init(hilscher_init_module);
module_exit(hilscher_exit_module);

MODULE_DEVICE_TABLE(pci, hilscher_pci_ids);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Hans J. Koch, Benedikt Spranger");
4 changes: 2 additions & 2 deletions fs/proc/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -648,14 +648,14 @@ static unsigned mounts_poll(struct file *file, poll_table *wait)
{
struct proc_mounts *p = file->private_data;
struct mnt_namespace *ns = p->ns;
unsigned res = 0;
unsigned res = POLLIN | POLLRDNORM;

poll_wait(file, &ns->poll, wait);

spin_lock(&vfsmount_lock);
if (p->event != ns->event) {
p->event = ns->event;
res = POLLERR;
res |= POLLERR | POLLPRI;
}
spin_unlock(&vfsmount_lock);

Expand Down
Loading

0 comments on commit 74a205a

Please sign in to comment.