Skip to content

Commit

Permalink
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6
Browse files Browse the repository at this point in the history
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6: (36 commits)
  Driver core: show drivers in /sys/module/
  Documentation/driver-model/platform.txt update/rewrite
  Driver core: platform_driver_probe(), can save codespace
  driver core: Use klist_remove() in device_move()
  driver core: Introduce device_move(): move a device to a new parent.
  Driver core: make drivers/base/core.c:setup_parent() static
  driver core: Introduce device_find_child().
  sysfs: sysfs_write_file() writes zero terminated data
  cpu topology: consider sysfs_create_group return value
  Driver core: Call platform_notify_remove later
  ACPI: Change ACPI to use dev_archdata instead of firmware_data
  Driver core: add dev_archdata to struct device
  Driver core: convert sound core to use struct device
  Driver core: change mem class_devices to be real devices
  Driver core: convert fb code to use struct device
  Driver core: convert firmware code to use struct device
  Driver core: convert mmc code to use struct device
  Driver core: convert ppdev code to use struct device
  Driver core: convert PPP code to use struct device
  Driver core: convert cpuid code to use struct device
  ...
  • Loading branch information
Linus Torvalds committed Dec 2, 2006
2 parents 6b8cc71 + e17e0f5 commit 4549df8
Show file tree
Hide file tree
Showing 80 changed files with 1,418 additions and 607 deletions.
204 changes: 118 additions & 86 deletions Documentation/driver-model/platform.txt
Original file line number Diff line number Diff line change
@@ -1,99 +1,131 @@
Platform Devices and Drivers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See <linux/platform_device.h> for the driver model interface to the
platform bus: platform_device, and platform_driver. This pseudo-bus
is used to connect devices on busses with minimal infrastructure,
like those used to integrate peripherals on many system-on-chip
processors, or some "legacy" PC interconnects; as opposed to large
formally specified ones like PCI or USB.


Platform devices
~~~~~~~~~~~~~~~~
Platform devices are devices that typically appear as autonomous
entities in the system. This includes legacy port-based devices and
host bridges to peripheral buses.


Platform drivers
~~~~~~~~~~~~~~~~
Drivers for platform devices are typically very simple and
unstructured. Either the device was present at a particular I/O port
and the driver was loaded, or it was not. There was no possibility
of hotplugging or alternative discovery besides probing at a specific
I/O address and expecting a specific response.
host bridges to peripheral buses, and most controllers integrated
into system-on-chip platforms. What they usually have in common
is direct addressing from a CPU bus. Rarely, a platform_device will
be connected through a segment of some other kind of bus; but its
registers will still be directly addressible.

Platform devices are given a name, used in driver binding, and a
list of resources such as addresses and IRQs.

Other Architectures, Modern Firmware, and new Platforms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These devices are not always at the legacy I/O ports. This is true on
other architectures and on some modern architectures. In most cases,
the drivers are modified to discover the devices at other well-known
ports for the given platform. However, the firmware in these systems
does usually know where exactly these devices reside, and in some
cases, it's the only way of discovering them.
struct platform_device {
const char *name;
u32 id;
struct device dev;
u32 num_resources;
struct resource *resource;
};


The Platform Bus
~~~~~~~~~~~~~~~~
A platform bus has been created to deal with these issues. First and
foremost, it groups all the legacy devices under a common bus, and
gives them a common parent if they don't already have one.

But, besides the organizational benefits, the platform bus can also
accommodate firmware-based enumeration.


Device Discovery
Platform drivers
~~~~~~~~~~~~~~~~
The platform bus has no concept of probing for devices. Devices
discovery is left up to either the legacy drivers or the
firmware. These entities are expected to notify the platform of
devices that it discovers via the bus's add() callback:

platform_bus.add(parent,bus_id).


Bus IDs
~~~~~~~
Bus IDs are the canonical names for the devices. There is no globally
standard addressing mechanism for legacy devices. In the IA-32 world,
we have Pnp IDs to use, as well as the legacy I/O ports. However,
neither tell what the device really is or have any meaning on other
platforms.

Since both PnP IDs and the legacy I/O ports (and other standard I/O
ports for specific devices) have a 1:1 mapping, we map the
platform-specific name or identifier to a generic name (at least
within the scope of the kernel).

For example, a serial driver might find a device at I/O 0x3f8. The
ACPI firmware might also discover a device with PnP ID (_HID)
PNP0501. Both correspond to the same device and should be mapped to the
canonical name 'serial'.

The bus_id field should be a concatenation of the canonical name and
the instance of that type of device. For example, the device at I/O
port 0x3f8 should have a bus_id of "serial0". This places the
responsibility of enumerating devices of a particular type up to the
discovery mechanism. But, they are the entity that should know best
(as opposed to the platform bus driver).


Drivers
~~~~~~~
Drivers for platform devices should have a name that is the same as
the canonical name of the devices they support. This allows the
platform bus driver to do simple matching with the basic data
structures to determine if a driver supports a certain device.

For example, a legacy serial driver should have a name of 'serial' and
register itself with the platform bus.


Driver Binding
~~~~~~~~~~~~~~
Legacy drivers assume they are bound to the device once they start up
and probe an I/O port. Divorcing them from this will be a difficult
process. However, that shouldn't prevent us from implementing
firmware-based enumeration.

The firmware should notify the platform bus about devices before the
legacy drivers have had a chance to load. Once the drivers are loaded,
they driver model core will attempt to bind the driver to any
previously-discovered devices. Once that has happened, it will be free
to discover any other devices it pleases.
Platform drivers follow the standard driver model convention, where
discovery/enumeration is handled outside the drivers, and drivers
provide probe() and remove() methods. They support power management
and shutdown notifications using the standard conventions.

struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};

Note that probe() should general verify that the specified device hardware
actually exists; sometimes platform setup code can't be sure. The probing
can use device resources, including clocks, and device platform_data.

Platform drivers register themselves the normal way:

int platform_driver_register(struct platform_driver *drv);

Or, in common situations where the device is known not to be hot-pluggable,
the probe() routine can live in an init section to reduce the driver's
runtime memory footprint:

int platform_driver_probe(struct platform_driver *drv,
int (*probe)(struct platform_device *))


Device Enumeration
~~~~~~~~~~~~~~~~~~
As a rule, platform specific (and often board-specific) setup code wil
register platform devices:

int platform_device_register(struct platform_device *pdev);

int platform_add_devices(struct platform_device **pdevs, int ndev);

The general rule is to register only those devices that actually exist,
but in some cases extra devices might be registered. For example, a kernel
might be configured to work with an external network adapter that might not
be populated on all boards, or likewise to work with an integrated controller
that some boards might not hook up to any peripherals.

In some cases, boot firmware will export tables describing the devices
that are populated on a given board. Without such tables, often the
only way for system setup code to set up the correct devices is to build
a kernel for a specific target board. Such board-specific kernels are
common with embedded and custom systems development.

In many cases, the memory and IRQ resources associated with the platform
device are not enough to let the device's driver work. Board setup code
will often provide additional information using the device's platform_data
field to hold additional information.

Embedded systems frequently need one or more clocks for platform devices,
which are normally kept off until they're actively needed (to save power).
System setup also associates those clocks with the device, so that that
calls to clk_get(&pdev->dev, clock_name) return them as needed.


Device Naming and Driver Binding
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The platform_device.dev.bus_id is the canonical name for the devices.
It's built from two components:

* 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.

These are catenated, so name/id "serial"/0 indicates bus_id "serial.0", and
"serial/3" indicates bus_id "serial.3"; both would use the platform_driver
named "serial". While "my_rtc"/-1 would be bus_id "my_rtc" (no instance id)
and use the platform_driver called "my_rtc".

Driver binding is performed automatically by the driver core, invoking
driver probe() after finding a match between device and driver. If the
probe() succeeds, the driver and device are bound as usual. There are
three different ways to find such a match:

- Whenever a device is registered, the drivers for that bus are
checked for matches. Platform devices should be registered very
early during system boot.

- When a driver is registered using platform_driver_register(), all
unbound devices on that bus are checked for matches. Drivers
usually register later during booting, or by module loading.

- Registering a driver using platform_driver_probe() works just like
using platform_driver_register(), except that the the driver won't
be probed later if another device registers. (Which is OK, since
this interface is only for use with non-hotpluggable devices.)

20 changes: 10 additions & 10 deletions arch/i386/kernel/cpuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,14 @@ static struct file_operations cpuid_fops = {
.open = cpuid_open,
};

static int cpuid_class_device_create(int i)
static int cpuid_device_create(int i)
{
int err = 0;
struct class_device *class_err;
struct device *dev;

class_err = class_device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, i), NULL, "cpu%d",i);
if (IS_ERR(class_err))
err = PTR_ERR(class_err);
dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, i), "cpu%d",i);
if (IS_ERR(dev))
err = PTR_ERR(dev);
return err;
}

Expand All @@ -174,10 +174,10 @@ static int cpuid_class_cpu_callback(struct notifier_block *nfb, unsigned long ac

switch (action) {
case CPU_ONLINE:
cpuid_class_device_create(cpu);
cpuid_device_create(cpu);
break;
case CPU_DEAD:
class_device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
break;
}
return NOTIFY_OK;
Expand Down Expand Up @@ -206,7 +206,7 @@ static int __init cpuid_init(void)
goto out_chrdev;
}
for_each_online_cpu(i) {
err = cpuid_class_device_create(i);
err = cpuid_device_create(i);
if (err != 0)
goto out_class;
}
Expand All @@ -218,7 +218,7 @@ static int __init cpuid_init(void)
out_class:
i = 0;
for_each_online_cpu(i) {
class_device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, i));
device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, i));
}
class_destroy(cpuid_class);
out_chrdev:
Expand All @@ -232,7 +232,7 @@ static void __exit cpuid_exit(void)
int cpu = 0;

for_each_online_cpu(cpu)
class_device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu));
class_destroy(cpuid_class);
unregister_chrdev(CPUID_MAJOR, "cpu/cpuid");
unregister_hotcpu_notifier(&cpuid_class_cpu_notifier);
Expand Down
20 changes: 10 additions & 10 deletions arch/i386/kernel/msr.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,14 +239,14 @@ static struct file_operations msr_fops = {
.open = msr_open,
};

static int msr_class_device_create(int i)
static int msr_device_create(int i)
{
int err = 0;
struct class_device *class_err;
struct device *dev;

class_err = class_device_create(msr_class, NULL, MKDEV(MSR_MAJOR, i), NULL, "msr%d",i);
if (IS_ERR(class_err))
err = PTR_ERR(class_err);
dev = device_create(msr_class, NULL, MKDEV(MSR_MAJOR, i), "msr%d",i);
if (IS_ERR(dev))
err = PTR_ERR(dev);
return err;
}

Expand All @@ -258,10 +258,10 @@ static int msr_class_cpu_callback(struct notifier_block *nfb,

switch (action) {
case CPU_ONLINE:
msr_class_device_create(cpu);
msr_device_create(cpu);
break;
case CPU_DEAD:
class_device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu));
device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu));
break;
}
return NOTIFY_OK;
Expand Down Expand Up @@ -290,7 +290,7 @@ static int __init msr_init(void)
goto out_chrdev;
}
for_each_online_cpu(i) {
err = msr_class_device_create(i);
err = msr_device_create(i);
if (err != 0)
goto out_class;
}
Expand All @@ -302,7 +302,7 @@ static int __init msr_init(void)
out_class:
i = 0;
for_each_online_cpu(i)
class_device_destroy(msr_class, MKDEV(MSR_MAJOR, i));
device_destroy(msr_class, MKDEV(MSR_MAJOR, i));
class_destroy(msr_class);
out_chrdev:
unregister_chrdev(MSR_MAJOR, "cpu/msr");
Expand All @@ -314,7 +314,7 @@ static void __exit msr_exit(void)
{
int cpu = 0;
for_each_online_cpu(cpu)
class_device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu));
device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu));
class_destroy(msr_class);
unregister_chrdev(MSR_MAJOR, "cpu/msr");
unregister_hotcpu_notifier(&msr_class_cpu_notifier);
Expand Down
20 changes: 11 additions & 9 deletions drivers/acpi/glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,9 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
{
acpi_status status;

if (dev->firmware_data) {
if (dev->archdata.acpi_handle) {
printk(KERN_WARNING PREFIX
"Drivers changed 'firmware_data' for %s\n", dev->bus_id);
"Drivers changed 'acpi_handle' for %s\n", dev->bus_id);
return -EINVAL;
}
get_device(dev);
Expand All @@ -278,25 +278,26 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
put_device(dev);
return -EINVAL;
}
dev->firmware_data = handle;
dev->archdata.acpi_handle = handle;

return 0;
}

static int acpi_unbind_one(struct device *dev)
{
if (!dev->firmware_data)
if (!dev->archdata.acpi_handle)
return 0;
if (dev == acpi_get_physical_device(dev->firmware_data)) {
if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
/* acpi_get_physical_device increase refcnt by one */
put_device(dev);
acpi_detach_data(dev->firmware_data, acpi_glue_data_handler);
dev->firmware_data = NULL;
acpi_detach_data(dev->archdata.acpi_handle,
acpi_glue_data_handler);
dev->archdata.acpi_handle = NULL;
/* acpi_bind_one increase refcnt by one */
put_device(dev);
} else {
printk(KERN_ERR PREFIX
"Oops, 'firmware_data' corrupt for %s\n", dev->bus_id);
"Oops, 'acpi_handle' corrupt for %s\n", dev->bus_id);
}
return 0;
}
Expand Down Expand Up @@ -328,7 +329,8 @@ static int acpi_platform_notify(struct device *dev)
if (!ret) {
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };

acpi_get_name(dev->firmware_data, ACPI_FULL_PATHNAME, &buffer);
acpi_get_name(dev->archdata.acpi_handle,
ACPI_FULL_PATHNAME, &buffer);
DBG("Device %s -> %s\n", dev->bus_id, (char *)buffer.pointer);
kfree(buffer.pointer);
} else
Expand Down
Loading

0 comments on commit 4549df8

Please sign in to comment.