summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig129
-rw-r--r--drivers/base/Makefile22
-rw-r--r--drivers/base/attribute_container.c440
-rw-r--r--drivers/base/base.h107
-rw-r--r--drivers/base/bus.c1039
-rw-r--r--drivers/base/class.c505
-rw-r--r--drivers/base/core.c1527
-rw-r--r--drivers/base/cpu.c206
-rw-r--r--drivers/base/dd.c367
-rw-r--r--drivers/base/devres.c646
-rw-r--r--drivers/base/dma-mapping.c218
-rw-r--r--drivers/base/driver.c277
-rw-r--r--drivers/base/firmware.c27
-rw-r--r--drivers/base/firmware_class.c617
-rw-r--r--drivers/base/hypervisor.c24
-rw-r--r--drivers/base/init.c36
-rw-r--r--drivers/base/isa.c184
-rw-r--r--drivers/base/map.c155
-rw-r--r--drivers/base/memory.c476
-rw-r--r--drivers/base/module.c94
-rw-r--r--drivers/base/node.c373
-rw-r--r--drivers/base/platform.c984
-rw-r--r--drivers/base/power/Makefile6
-rw-r--r--drivers/base/power/main.c784
-rw-r--r--drivers/base/power/power.h49
-rw-r--r--drivers/base/power/sysfs.c98
-rw-r--r--drivers/base/power/trace.c231
-rw-r--r--drivers/base/sys.c528
-rw-r--r--drivers/base/topology.c183
-rw-r--r--drivers/base/transport_class.c279
30 files changed, 10611 insertions, 0 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
new file mode 100644
index 0000000..d8e8c49
--- /dev/null
+++ b/drivers/base/Kconfig
@@ -0,0 +1,129 @@
+menu "Generic Driver Options"
+
+config UEVENT_HELPER_PATH
+ string "path to uevent helper"
+ depends on HOTPLUG
+ default "/sbin/hotplug"
+ help
+ Path to uevent helper program forked by the kernel for
+ every uevent.
+
+config STANDALONE
+ bool "Select only drivers that don't need compile-time external firmware" if EXPERIMENTAL
+ default y
+ help
+ Select this option if you don't have magic firmware for drivers that
+ need it.
+
+ If unsure, say Y.
+
+config PREVENT_FIRMWARE_BUILD
+ bool "Prevent firmware from being built"
+ default y
+ help
+ Say yes to avoid building firmware. Firmware is usually shipped
+ with the driver, and only when updating the firmware a rebuild
+ should be made.
+ If unsure say Y here.
+
+config FW_LOADER
+ tristate "Userspace firmware loading support" if EMBEDDED
+ depends on HOTPLUG
+ default y
+ ---help---
+ This option is provided for the case where no in-kernel-tree modules
+ require userspace firmware loading support, but a module built outside
+ the kernel tree does.
+
+config FIRMWARE_IN_KERNEL
+ bool "Include in-kernel firmware blobs in kernel binary"
+ depends on FW_LOADER
+ default y
+ help
+ The kernel source tree includes a number of firmware 'blobs'
+ which are used by various drivers. The recommended way to
+ use these is to run "make firmware_install" and to copy the
+ resulting binary files created in usr/lib/firmware directory
+ of the kernel tree to the /lib/firmware on your system so
+ that they can be loaded by userspace helpers on request.
+
+ Enabling this option will build each required firmware blob
+ into the kernel directly, where request_firmware() will find
+ them without having to call out to userspace. This may be
+ useful if your root file system requires a device which uses
+ such firmware, and do not wish to use an initrd.
+
+ This single option controls the inclusion of firmware for
+ every driver which uses request_firmare() and ships its
+ firmware in the kernel source tree, to avoid a proliferation
+ of 'Include firmware for xxx device' options.
+
+ Say 'N' and let firmware be loaded from userspace.
+
+config EXTRA_FIRMWARE
+ string "External firmware blobs to build into the kernel binary"
+ depends on FW_LOADER
+ help
+ This option allows firmware to be built into the kernel, for the
+ cases where the user either cannot or doesn't want to provide it from
+ userspace at runtime (for example, when the firmware in question is
+ required for accessing the boot device, and the user doesn't want to
+ use an initrd).
+
+ This option is a string, and takes the (space-separated) names of the
+ firmware files -- the same names which appear in MODULE_FIRMWARE()
+ and request_firmware() in the source. These files should exist under
+ the directory specified by the EXTRA_FIRMWARE_DIR option, which is
+ by default the firmware/ subdirectory of the kernel source tree.
+
+ So, for example, you might set CONFIG_EXTRA_FIRMWARE="usb8388.bin",
+ copy the usb8388.bin file into the firmware/ directory, and build the
+ kernel. Then any request_firmware("usb8388.bin") will be
+ satisfied internally without needing to call out to userspace.
+
+ WARNING: If you include additional firmware files into your binary
+ kernel image which are not available under the terms of the GPL,
+ then it may be a violation of the GPL to distribute the resulting
+ image -- since it combines both GPL and non-GPL work. You should
+ consult a lawyer of your own before distributing such an image.
+
+config EXTRA_FIRMWARE_DIR
+ string "Firmware blobs root directory"
+ depends on EXTRA_FIRMWARE != ""
+ default "firmware"
+ help
+ This option controls the directory in which the kernel build system
+ looks for the firmware files listed in the EXTRA_FIRMWARE option.
+ The default is the firmware/ directory in the kernel source tree,
+ but by changing this option you can point it elsewhere, such as
+ the /lib/firmware/ directory or another separate directory
+ containing firmware files.
+
+config DEBUG_DRIVER
+ bool "Driver Core verbose debug messages"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here if you want the Driver core to produce a bunch of
+ debug messages to the system log. Select this if you are having a
+ problem with the driver core and want to see more of what is
+ going on.
+
+ If you are unsure about this, say N here.
+
+config DEBUG_DEVRES
+ bool "Managed device resources verbose debug messages"
+ depends on DEBUG_KERNEL
+ help
+ This option enables kernel parameter devres.log. If set to
+ non-zero, devres debug messages are printed. Select this if
+ you are having a problem with devres or want to debug
+ resource management for a managed device. devres.log can be
+ switched on and off from sysfs node.
+
+ If you are unsure about this, Say N here.
+
+config SYS_HYPERVISOR
+ bool
+ default n
+
+endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
new file mode 100644
index 0000000..c666373
--- /dev/null
+++ b/drivers/base/Makefile
@@ -0,0 +1,22 @@
+# Makefile for the Linux device tree
+
+obj-y := core.o sys.o bus.o dd.o \
+ driver.o class.o platform.o \
+ cpu.o firmware.o init.o map.o devres.o \
+ attribute_container.o transport_class.o
+obj-y += power/
+obj-$(CONFIG_HAS_DMA) += dma-mapping.o
+obj-$(CONFIG_ISA) += isa.o
+obj-$(CONFIG_FW_LOADER) += firmware_class.o
+obj-$(CONFIG_NUMA) += node.o
+obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o
+obj-$(CONFIG_SMP) += topology.o
+ifeq ($(CONFIG_SYSFS),y)
+obj-$(CONFIG_MODULES) += module.o
+endif
+obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
+
+ifeq ($(CONFIG_DEBUG_DRIVER),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
new file mode 100644
index 0000000..f57652d
--- /dev/null
+++ b/drivers/base/attribute_container.c
@@ -0,0 +1,440 @@
+/*
+ * attribute_container.c - implementation of a simple container for classes
+ *
+ * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
+ *
+ * This file is licensed under GPLv2
+ *
+ * The basic idea here is to enable a device to be attached to an
+ * aritrary numer of classes without having to allocate storage for them.
+ * Instead, the contained classes select the devices they need to attach
+ * to via a matching function.
+ */
+
+#include <linux/attribute_container.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include "base.h"
+
+/* This is a private structure used to tie the classdev and the
+ * container .. it should never be visible outside this file */
+struct internal_container {
+ struct klist_node node;
+ struct attribute_container *cont;
+ struct device classdev;
+};
+
+static void internal_container_klist_get(struct klist_node *n)
+{
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+ get_device(&ic->classdev);
+}
+
+static void internal_container_klist_put(struct klist_node *n)
+{
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+ put_device(&ic->classdev);
+}
+
+
+/**
+ * attribute_container_classdev_to_container - given a classdev, return the container
+ *
+ * @classdev: the class device created by attribute_container_add_device.
+ *
+ * Returns the container associated with this classdev.
+ */
+struct attribute_container *
+attribute_container_classdev_to_container(struct device *classdev)
+{
+ struct internal_container *ic =
+ container_of(classdev, struct internal_container, classdev);
+ return ic->cont;
+}
+EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container);
+
+static LIST_HEAD(attribute_container_list);
+
+static DEFINE_MUTEX(attribute_container_mutex);
+
+/**
+ * attribute_container_register - register an attribute container
+ *
+ * @cont: The container to register. This must be allocated by the
+ * callee and should also be zeroed by it.
+ */
+int
+attribute_container_register(struct attribute_container *cont)
+{
+ INIT_LIST_HEAD(&cont->node);
+ klist_init(&cont->containers,internal_container_klist_get,
+ internal_container_klist_put);
+
+ mutex_lock(&attribute_container_mutex);
+ list_add_tail(&cont->node, &attribute_container_list);
+ mutex_unlock(&attribute_container_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(attribute_container_register);
+
+/**
+ * attribute_container_unregister - remove a container registration
+ *
+ * @cont: previously registered container to remove
+ */
+int
+attribute_container_unregister(struct attribute_container *cont)
+{
+ int retval = -EBUSY;
+ mutex_lock(&attribute_container_mutex);
+ spin_lock(&cont->containers.k_lock);
+ if (!list_empty(&cont->containers.k_list))
+ goto out;
+ retval = 0;
+ list_del(&cont->node);
+ out:
+ spin_unlock(&cont->containers.k_lock);
+ mutex_unlock(&attribute_container_mutex);
+ return retval;
+
+}
+EXPORT_SYMBOL_GPL(attribute_container_unregister);
+
+/* private function used as class release */
+static void attribute_container_release(struct device *classdev)
+{
+ struct internal_container *ic
+ = container_of(classdev, struct internal_container, classdev);
+ struct device *dev = classdev->parent;
+
+ kfree(ic);
+ put_device(dev);
+}
+
+/**
+ * attribute_container_add_device - see if any container is interested in dev
+ *
+ * @dev: device to add attributes to
+ * @fn: function to trigger addition of class device.
+ *
+ * This function allocates storage for the class device(s) to be
+ * attached to dev (one for each matching attribute_container). If no
+ * fn is provided, the code will simply register the class device via
+ * device_add. If a function is provided, it is expected to add
+ * the class device at the appropriate time. One of the things that
+ * might be necessary is to allocate and initialise the classdev and
+ * then add it a later time. To do this, call this routine for
+ * allocation and initialisation and then use
+ * attribute_container_device_trigger() to call device_add() on
+ * it. Note: after this, the class device contains a reference to dev
+ * which is not relinquished until the release of the classdev.
+ */
+void
+attribute_container_add_device(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+ struct device *))
+{
+ struct attribute_container *cont;
+
+ mutex_lock(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ struct internal_container *ic;
+
+ if (attribute_container_no_classdevs(cont))
+ continue;
+
+ if (!cont->match(cont, dev))
+ continue;
+
+ ic = kzalloc(sizeof(*ic), GFP_KERNEL);
+ if (!ic) {
+ dev_printk(KERN_ERR, dev, "failed to allocate class container\n");
+ continue;
+ }
+
+ ic->cont = cont;
+ device_initialize(&ic->classdev);
+ ic->classdev.parent = get_device(dev);
+ ic->classdev.class = cont->class;
+ cont->class->dev_release = attribute_container_release;
+ strcpy(ic->classdev.bus_id, dev->bus_id);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else
+ attribute_container_add_class_device(&ic->classdev);
+ klist_add_tail(&ic->node, &cont->containers);
+ }
+ mutex_unlock(&attribute_container_mutex);
+}
+
+/* FIXME: can't break out of this unless klist_iter_exit is also
+ * called before doing the break
+ */
+#define klist_for_each_entry(pos, head, member, iter) \
+ for (klist_iter_init(head, iter); (pos = ({ \
+ struct klist_node *n = klist_next(iter); \
+ n ? container_of(n, typeof(*pos), member) : \
+ ({ klist_iter_exit(iter) ; NULL; }); \
+ }) ) != NULL; )
+
+
+/**
+ * attribute_container_remove_device - make device eligible for removal.
+ *
+ * @dev: The generic device
+ * @fn: A function to call to remove the device
+ *
+ * This routine triggers device removal. If fn is NULL, then it is
+ * simply done via device_unregister (note that if something
+ * still has a reference to the classdev, then the memory occupied
+ * will not be freed until the classdev is released). If you want a
+ * two phase release: remove from visibility and then delete the
+ * device, then you should use this routine with a fn that calls
+ * device_del() and then use attribute_container_device_trigger()
+ * to do the final put on the classdev.
+ */
+void
+attribute_container_remove_device(struct device *dev,
+ void (*fn)(struct attribute_container *,
+ struct device *,
+ struct device *))
+{
+ struct attribute_container *cont;
+
+ mutex_lock(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ if (attribute_container_no_classdevs(cont))
+ continue;
+
+ if (!cont->match(cont, dev))
+ continue;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+ if (dev != ic->classdev.parent)
+ continue;
+ klist_del(&ic->node);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else {
+ attribute_container_remove_attrs(&ic->classdev);
+ device_unregister(&ic->classdev);
+ }
+ }
+ }
+ mutex_unlock(&attribute_container_mutex);
+}
+
+/**
+ * attribute_container_device_trigger - execute a trigger for each matching classdev
+ *
+ * @dev: The generic device to run the trigger for
+ * @fn the function to execute for each classdev.
+ *
+ * This funcion is for executing a trigger when you need to know both
+ * the container and the classdev. If you only care about the
+ * container, then use attribute_container_trigger() instead.
+ */
+void
+attribute_container_device_trigger(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+ struct device *))
+{
+ struct attribute_container *cont;
+
+ mutex_lock(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ if (!cont->match(cont, dev))
+ continue;
+
+ if (attribute_container_no_classdevs(cont)) {
+ fn(cont, dev, NULL);
+ continue;
+ }
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+ if (dev == ic->classdev.parent)
+ fn(cont, dev, &ic->classdev);
+ }
+ }
+ mutex_unlock(&attribute_container_mutex);
+}
+
+/**
+ * attribute_container_trigger - trigger a function for each matching container
+ *
+ * @dev: The generic device to activate the trigger for
+ * @fn: the function to trigger
+ *
+ * This routine triggers a function that only needs to know the
+ * matching containers (not the classdev) associated with a device.
+ * It is more lightweight than attribute_container_device_trigger, so
+ * should be used in preference unless the triggering function
+ * actually needs to know the classdev.
+ */
+void
+attribute_container_trigger(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *))
+{
+ struct attribute_container *cont;
+
+ mutex_lock(&attribute_container_mutex);
+ list_for_each_entry(cont, &attribute_container_list, node) {
+ if (cont->match(cont, dev))
+ fn(cont, dev);
+ }
+ mutex_unlock(&attribute_container_mutex);
+}
+
+/**
+ * attribute_container_add_attrs - add attributes
+ *
+ * @classdev: The class device
+ *
+ * This simply creates all the class device sysfs files from the
+ * attributes listed in the container
+ */
+int
+attribute_container_add_attrs(struct device *classdev)
+{
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+ struct device_attribute **attrs = cont->attrs;
+ int i, error;
+
+ BUG_ON(attrs && cont->grp);
+
+ if (!attrs && !cont->grp)
+ return 0;
+
+ if (cont->grp)
+ return sysfs_create_group(&classdev->kobj, cont->grp);
+
+ for (i = 0; attrs[i]; i++) {
+ error = device_create_file(classdev, attrs[i]);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+/**
+ * attribute_container_add_class_device - same function as device_add
+ *
+ * @classdev: the class device to add
+ *
+ * This performs essentially the same function as device_add except for
+ * attribute containers, namely add the classdev to the system and then
+ * create the attribute files
+ */
+int
+attribute_container_add_class_device(struct device *classdev)
+{
+ int error = device_add(classdev);
+ if (error)
+ return error;
+ return attribute_container_add_attrs(classdev);
+}
+
+/**
+ * attribute_container_add_class_device_adapter - simple adapter for triggers
+ *
+ * This function is identical to attribute_container_add_class_device except
+ * that it is designed to be called from the triggers
+ */
+int
+attribute_container_add_class_device_adapter(struct attribute_container *cont,
+ struct device *dev,
+ struct device *classdev)
+{
+ return attribute_container_add_class_device(classdev);
+}
+
+/**
+ * attribute_container_remove_attrs - remove any attribute files
+ *
+ * @classdev: The class device to remove the files from
+ *
+ */
+void
+attribute_container_remove_attrs(struct device *classdev)
+{
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+ struct device_attribute **attrs = cont->attrs;
+ int i;
+
+ if (!attrs && !cont->grp)
+ return;
+
+ if (cont->grp) {
+ sysfs_remove_group(&classdev->kobj, cont->grp);
+ return ;
+ }
+
+ for (i = 0; attrs[i]; i++)
+ device_remove_file(classdev, attrs[i]);
+}
+
+/**
+ * attribute_container_class_device_del - equivalent of class_device_del
+ *
+ * @classdev: the class device
+ *
+ * This function simply removes all the attribute files and then calls
+ * device_del.
+ */
+void
+attribute_container_class_device_del(struct device *classdev)
+{
+ attribute_container_remove_attrs(classdev);
+ device_del(classdev);
+}
+
+/**
+ * attribute_container_find_class_device - find the corresponding class_device
+ *
+ * @cont: the container
+ * @dev: the generic device
+ *
+ * Looks up the device in the container's list of class devices and returns
+ * the corresponding class_device.
+ */
+struct device *
+attribute_container_find_class_device(struct attribute_container *cont,
+ struct device *dev)
+{
+ struct device *cdev = NULL;
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+ if (ic->classdev.parent == dev) {
+ cdev = &ic->classdev;
+ /* FIXME: must exit iterator then break */
+ klist_iter_exit(&iter);
+ break;
+ }
+ }
+
+ return cdev;
+}
+EXPORT_SYMBOL_GPL(attribute_container_find_class_device);
diff --git a/drivers/base/base.h b/drivers/base/base.h
new file mode 100644
index 0000000..0a5f055
--- /dev/null
+++ b/drivers/base/base.h
@@ -0,0 +1,107 @@
+
+/**
+ * struct bus_type_private - structure to hold the private to the driver core portions of the bus_type structure.
+ *
+ * @subsys - the struct kset that defines this bus. This is the main kobject
+ * @drivers_kset - the list of drivers associated with this bus
+ * @devices_kset - the list of devices associated with this bus
+ * @klist_devices - the klist to iterate over the @devices_kset
+ * @klist_drivers - the klist to iterate over the @drivers_kset
+ * @bus_notifier - the bus notifier list for anything that cares about things
+ * on this bus.
+ * @bus - pointer back to the struct bus_type that this structure is associated
+ * with.
+ *
+ * This structure is the one that is the actual kobject allowing struct
+ * bus_type to be statically allocated safely. Nothing outside of the driver
+ * core should ever touch these fields.
+ */
+struct bus_type_private {
+ struct kset subsys;
+ struct kset *drivers_kset;
+ struct kset *devices_kset;
+ struct klist klist_devices;
+ struct klist klist_drivers;
+ struct blocking_notifier_head bus_notifier;
+ unsigned int drivers_autoprobe:1;
+ struct bus_type *bus;
+};
+
+struct driver_private {
+ struct kobject kobj;
+ struct klist klist_devices;
+ struct klist_node knode_bus;
+ struct module_kobject *mkobj;
+ struct device_driver *driver;
+};
+#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+
+
+/**
+ * struct class_private - structure to hold the private to the driver core portions of the class structure.
+ *
+ * @class_subsys - the struct kset that defines this class. This is the main kobject
+ * @class_devices - list of devices associated with this class
+ * @class_interfaces - list of class_interfaces associated with this class
+ * @class_dirs - "glue" directory for virtual devices associated with this class
+ * @class_mutex - mutex to protect the children, devices, and interfaces lists.
+ * @class - pointer back to the struct class that this structure is associated
+ * with.
+ *
+ * This structure is the one that is the actual kobject allowing struct
+ * class to be statically allocated safely. Nothing outside of the driver
+ * core should ever touch these fields.
+ */
+struct class_private {
+ struct kset class_subsys;
+ struct klist class_devices;
+ struct list_head class_interfaces;
+ struct kset class_dirs;
+ struct mutex class_mutex;
+ struct class *class;
+};
+#define to_class(obj) \
+ container_of(obj, struct class_private, class_subsys.kobj)
+
+/* initialisation functions */
+extern int devices_init(void);
+extern int buses_init(void);
+extern int classes_init(void);
+extern int firmware_init(void);
+#ifdef CONFIG_SYS_HYPERVISOR
+extern int hypervisor_init(void);
+#else
+static inline int hypervisor_init(void) { return 0; }
+#endif
+extern int platform_bus_init(void);
+extern int system_bus_init(void);
+extern int cpu_dev_init(void);
+
+extern int bus_add_device(struct device *dev);
+extern void bus_attach_device(struct device *dev);
+extern void bus_remove_device(struct device *dev);
+
+extern int bus_add_driver(struct device_driver *drv);
+extern void bus_remove_driver(struct device_driver *drv);
+
+extern void driver_detach(struct device_driver *drv);
+extern int driver_probe_device(struct device_driver *drv, struct device *dev);
+
+extern void sysdev_shutdown(void);
+extern int sysdev_suspend(pm_message_t state);
+extern int sysdev_resume(void);
+
+extern char *make_class_name(const char *name, struct kobject *kobj);
+
+extern int devres_release_all(struct device *dev);
+
+extern struct kset *devices_kset;
+
+#if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS)
+extern void module_add_driver(struct module *mod, struct device_driver *drv);
+extern void module_remove_driver(struct device_driver *drv);
+#else
+static inline void module_add_driver(struct module *mod,
+ struct device_driver *drv) { }
+static inline void module_remove_driver(struct device_driver *drv) { }
+#endif
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
new file mode 100644
index 0000000..5aee1c0
--- /dev/null
+++ b/drivers/base/bus.c
@@ -0,0 +1,1039 @@
+/*
+ * bus.c - bus driver management
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include "base.h"
+#include "power/power.h"
+
+#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
+#define to_bus(obj) container_of(obj, struct bus_type_private, subsys.kobj)
+
+/*
+ * sysfs bindings for drivers
+ */
+
+#define to_drv_attr(_attr) container_of(_attr, struct driver_attribute, attr)
+
+
+static int __must_check bus_rescan_devices_helper(struct device *dev,
+ void *data);
+
+static struct bus_type *bus_get(struct bus_type *bus)
+{
+ if (bus) {
+ kset_get(&bus->p->subsys);
+ return bus;
+ }
+ return NULL;
+}
+
+static void bus_put(struct bus_type *bus)
+{
+ if (bus)
+ kset_put(&bus->p->subsys);
+}
+
+static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct driver_attribute *drv_attr = to_drv_attr(attr);
+ struct driver_private *drv_priv = to_driver(kobj);
+ ssize_t ret = -EIO;
+
+ if (drv_attr->show)
+ ret = drv_attr->show(drv_priv->driver, buf);
+ return ret;
+}
+
+static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct driver_attribute *drv_attr = to_drv_attr(attr);
+ struct driver_private *drv_priv = to_driver(kobj);
+ ssize_t ret = -EIO;
+
+ if (drv_attr->store)
+ ret = drv_attr->store(drv_priv->driver, buf, count);
+ return ret;
+}
+
+static struct sysfs_ops driver_sysfs_ops = {
+ .show = drv_attr_show,
+ .store = drv_attr_store,
+};
+
+static void driver_release(struct kobject *kobj)
+{
+ struct driver_private *drv_priv = to_driver(kobj);
+
+ pr_debug("driver: '%s': %s\n", kobject_name(kobj), __func__);
+ kfree(drv_priv);
+}
+
+static struct kobj_type driver_ktype = {
+ .sysfs_ops = &driver_sysfs_ops,
+ .release = driver_release,
+};
+
+/*
+ * sysfs bindings for buses
+ */
+static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct bus_attribute *bus_attr = to_bus_attr(attr);
+ struct bus_type_private *bus_priv = to_bus(kobj);
+ ssize_t ret = 0;
+
+ if (bus_attr->show)
+ ret = bus_attr->show(bus_priv->bus, buf);
+ return ret;
+}
+
+static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bus_attribute *bus_attr = to_bus_attr(attr);
+ struct bus_type_private *bus_priv = to_bus(kobj);
+ ssize_t ret = 0;
+
+ if (bus_attr->store)
+ ret = bus_attr->store(bus_priv->bus, buf, count);
+ return ret;
+}
+
+static struct sysfs_ops bus_sysfs_ops = {
+ .show = bus_attr_show,
+ .store = bus_attr_store,
+};
+
+int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
+{
+ int error;
+ if (bus_get(bus)) {
+ error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
+ bus_put(bus);
+ } else
+ error = -EINVAL;
+ return error;
+}
+EXPORT_SYMBOL_GPL(bus_create_file);
+
+void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
+{
+ if (bus_get(bus)) {
+ sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);
+ bus_put(bus);
+ }
+}
+EXPORT_SYMBOL_GPL(bus_remove_file);
+
+static struct kobj_type bus_ktype = {
+ .sysfs_ops = &bus_sysfs_ops,
+};
+
+static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
+{
+ struct kobj_type *ktype = get_ktype(kobj);
+
+ if (ktype == &bus_ktype)
+ return 1;
+ return 0;
+}
+
+static struct kset_uevent_ops bus_uevent_ops = {
+ .filter = bus_uevent_filter,
+};
+
+static struct kset *bus_kset;
+
+
+#ifdef CONFIG_HOTPLUG
+/* Manually detach a device from its associated driver. */
+static ssize_t driver_unbind(struct device_driver *drv,
+ const char *buf, size_t count)
+{
+ struct bus_type *bus = bus_get(drv->bus);
+ struct device *dev;
+ int err = -ENODEV;
+
+ dev = bus_find_device_by_name(bus, NULL, buf);
+ if (dev && dev->driver == drv) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ device_release_driver(dev);
+ if (dev->parent)
+ up(&dev->parent->sem);
+ err = count;
+ }
+ put_device(dev);
+ bus_put(bus);
+ return err;
+}
+static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind);
+
+/*
+ * Manually attach a device to a driver.
+ * Note: the driver must want to bind to the device,
+ * it is not possible to override the driver's id table.
+ */
+static ssize_t driver_bind(struct device_driver *drv,
+ const char *buf, size_t count)
+{
+ struct bus_type *bus = bus_get(drv->bus);
+ struct device *dev;
+ int err = -ENODEV;
+
+ dev = bus_find_device_by_name(bus, NULL, buf);
+ if (dev && dev->driver == NULL) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ down(&dev->sem);
+ err = driver_probe_device(drv, dev);
+ up(&dev->sem);
+ if (dev->parent)
+ up(&dev->parent->sem);
+
+ if (err > 0) {
+ /* success */
+ err = count;
+ } else if (err == 0) {
+ /* driver didn't accept device */
+ err = -ENODEV;
+ }
+ }
+ put_device(dev);
+ bus_put(bus);
+ return err;
+}
+static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
+
+static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
+{
+ return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
+}
+
+static ssize_t store_drivers_autoprobe(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ if (buf[0] == '0')
+ bus->p->drivers_autoprobe = 0;
+ else
+ bus->p->drivers_autoprobe = 1;
+ return count;
+}
+
+static ssize_t store_drivers_probe(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ struct device *dev;
+
+ dev = bus_find_device_by_name(bus, NULL, buf);
+ if (!dev)
+ return -ENODEV;
+ if (bus_rescan_devices_helper(dev, NULL) != 0)
+ return -EINVAL;
+ return count;
+}
+#endif
+
+static struct device *next_device(struct klist_iter *i)
+{
+ struct klist_node *n = klist_next(i);
+ return n ? container_of(n, struct device, knode_bus) : NULL;
+}
+
+/**
+ * bus_for_each_dev - device iterator.
+ * @bus: bus type.
+ * @start: device to start iterating from.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
+ *
+ * Iterate over @bus's list of devices, and call @fn for each,
+ * passing it @data. If @start is not NULL, we use that device to
+ * begin iterating from.
+ *
+ * We check the return of @fn each time. If it returns anything
+ * other than 0, we break out and return that value.
+ *
+ * NOTE: The device that returns a non-zero value is not retained
+ * in any way, nor is its refcount incremented. If the caller needs
+ * to retain this data, it should do, and increment the reference
+ * count in the supplied callback.
+ */
+int bus_for_each_dev(struct bus_type *bus, struct device *start,
+ void *data, int (*fn)(struct device *, void *))
+{
+ struct klist_iter i;
+ struct device *dev;
+ int error = 0;
+
+ if (!bus)
+ return -EINVAL;
+
+ klist_iter_init_node(&bus->p->klist_devices, &i,
+ (start ? &start->knode_bus : NULL));
+ while ((dev = next_device(&i)) && !error)
+ error = fn(dev, data);
+ klist_iter_exit(&i);
+ return error;
+}
+EXPORT_SYMBOL_GPL(bus_for_each_dev);
+
+/**
+ * bus_find_device - device iterator for locating a particular device.
+ * @bus: bus type
+ * @start: Device to begin with
+ * @data: Data to pass to match function
+ * @match: Callback function to check device
+ *
+ * This is similar to the bus_for_each_dev() function above, but it
+ * returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero, this function will
+ * return to the caller and not iterate over any more devices.
+ */
+struct device *bus_find_device(struct bus_type *bus,
+ struct device *start, void *data,
+ int (*match)(struct device *dev, void *data))
+{
+ struct klist_iter i;
+ struct device *dev;
+
+ if (!bus)
+ return NULL;
+
+ klist_iter_init_node(&bus->p->klist_devices, &i,
+ (start ? &start->knode_bus : NULL));
+ while ((dev = next_device(&i)))
+ if (match(dev, data) && get_device(dev))
+ break;
+ klist_iter_exit(&i);
+ return dev;
+}
+EXPORT_SYMBOL_GPL(bus_find_device);
+
+static int match_name(struct device *dev, void *data)
+{
+ const char *name = data;
+
+ return sysfs_streq(name, dev->bus_id);
+}
+
+/**
+ * bus_find_device_by_name - device iterator for locating a particular device of a specific name
+ * @bus: bus type
+ * @start: Device to begin with
+ * @name: name of the device to match
+ *
+ * This is similar to the bus_find_device() function above, but it handles
+ * searching by a name automatically, no need to write another strcmp matching
+ * function.
+ */
+struct device *bus_find_device_by_name(struct bus_type *bus,
+ struct device *start, const char *name)
+{
+ return bus_find_device(bus, start, (void *)name, match_name);
+}
+EXPORT_SYMBOL_GPL(bus_find_device_by_name);
+
+static struct device_driver *next_driver(struct klist_iter *i)
+{
+ struct klist_node *n = klist_next(i);
+ struct driver_private *drv_priv;
+
+ if (n) {
+ drv_priv = container_of(n, struct driver_private, knode_bus);
+ return drv_priv->driver;
+ }
+ return NULL;
+}
+
+/**
+ * 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.
+ *
+ * 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;
+}
+EXPORT_SYMBOL_GPL(bus_for_each_drv);
+
+static int device_add_attrs(struct bus_type *bus, struct device *dev)
+{
+ int error = 0;
+ int i;
+
+ if (!bus->dev_attrs)
+ return 0;
+
+ for (i = 0; attr_name(bus->dev_attrs[i]); i++) {
+ error = device_create_file(dev, &bus->dev_attrs[i]);
+ if (error) {
+ while (--i >= 0)
+ device_remove_file(dev, &bus->dev_attrs[i]);
+ break;
+ }
+ }
+ return error;
+}
+
+static void device_remove_attrs(struct bus_type *bus, struct device *dev)
+{
+ int i;
+
+ if (bus->dev_attrs) {
+ for (i = 0; attr_name(bus->dev_attrs[i]); i++)
+ device_remove_file(dev, &bus->dev_attrs[i]);
+ }
+}
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+static int make_deprecated_bus_links(struct device *dev)
+{
+ return sysfs_create_link(&dev->kobj,
+ &dev->bus->p->subsys.kobj, "bus");
+}
+
+static void remove_deprecated_bus_links(struct device *dev)
+{
+ sysfs_remove_link(&dev->kobj, "bus");
+}
+#else
+static inline int make_deprecated_bus_links(struct device *dev) { return 0; }
+static inline void remove_deprecated_bus_links(struct device *dev) { }
+#endif
+
+/**
+ * bus_add_device - add device to bus
+ * @dev: device being added
+ *
+ * - Add the device to its bus's list of devices.
+ * - Create link to device's bus.
+ */
+int bus_add_device(struct device *dev)
+{
+ struct bus_type *bus = bus_get(dev->bus);
+ int error = 0;
+
+ if (bus) {
+ pr_debug("bus: '%s': add device %s\n", bus->name, dev->bus_id);
+ error = device_add_attrs(bus, dev);
+ if (error)
+ goto out_put;
+ error = sysfs_create_link(&bus->p->devices_kset->kobj,
+ &dev->kobj, dev->bus_id);
+ if (error)
+ goto out_id;
+ error = sysfs_create_link(&dev->kobj,
+ &dev->bus->p->subsys.kobj, "subsystem");
+ if (error)
+ goto out_subsys;
+ error = make_deprecated_bus_links(dev);
+ if (error)
+ goto out_deprecated;
+ }
+ return 0;
+
+out_deprecated:
+ sysfs_remove_link(&dev->kobj, "subsystem");
+out_subsys:
+ sysfs_remove_link(&bus->p->devices_kset->kobj, dev->bus_id);
+out_id:
+ device_remove_attrs(bus, dev);
+out_put:
+ bus_put(dev->bus);
+ return error;
+}
+
+/**
+ * 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)
+ ret = device_attach(dev);
+ WARN_ON(ret < 0);
+ if (ret >= 0)
+ klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
+ }
+}
+
+/**
+ * bus_remove_device - remove device from bus
+ * @dev: device to be removed
+ *
+ * - Remove symlink from bus's directory.
+ * - Delete device from bus's list.
+ * - Detach from its driver.
+ * - Drop reference taken in bus_add_device().
+ */
+void bus_remove_device(struct device *dev)
+{
+ if (dev->bus) {
+ sysfs_remove_link(&dev->kobj, "subsystem");
+ remove_deprecated_bus_links(dev);
+ sysfs_remove_link(&dev->bus->p->devices_kset->kobj,
+ dev->bus_id);
+ device_remove_attrs(dev->bus, dev);
+ if (klist_node_attached(&dev->knode_bus))
+ klist_del(&dev->knode_bus);
+
+ pr_debug("bus: '%s': remove device %s\n",
+ dev->bus->name, dev->bus_id);
+ device_release_driver(dev);
+ bus_put(dev->bus);
+ }
+}
+
+static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv)
+{
+ int error = 0;
+ int i;
+
+ if (bus->drv_attrs) {
+ for (i = 0; attr_name(bus->drv_attrs[i]); i++) {
+ error = driver_create_file(drv, &bus->drv_attrs[i]);
+ if (error)
+ goto err;
+ }
+ }
+done:
+ return error;
+err:
+ while (--i >= 0)
+ driver_remove_file(drv, &bus->drv_attrs[i]);
+ goto done;
+}
+
+static void driver_remove_attrs(struct bus_type *bus,
+ struct device_driver *drv)
+{
+ int i;
+
+ if (bus->drv_attrs) {
+ for (i = 0; attr_name(bus->drv_attrs[i]); i++)
+ driver_remove_file(drv, &bus->drv_attrs[i]);
+ }
+}
+
+#ifdef CONFIG_HOTPLUG
+/*
+ * Thanks to drivers making their tables __devinit, we can't allow manual
+ * bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
+ */
+static int __must_check add_bind_files(struct device_driver *drv)
+{
+ int ret;
+
+ ret = driver_create_file(drv, &driver_attr_unbind);
+ if (ret == 0) {
+ ret = driver_create_file(drv, &driver_attr_bind);
+ if (ret)
+ driver_remove_file(drv, &driver_attr_unbind);
+ }
+ return ret;
+}
+
+static void remove_bind_files(struct device_driver *drv)
+{
+ driver_remove_file(drv, &driver_attr_bind);
+ driver_remove_file(drv, &driver_attr_unbind);
+}
+
+static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
+static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
+ show_drivers_autoprobe, store_drivers_autoprobe);
+
+static int add_probe_files(struct bus_type *bus)
+{
+ int retval;
+
+ retval = bus_create_file(bus, &bus_attr_drivers_probe);
+ if (retval)
+ goto out;
+
+ retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
+ if (retval)
+ bus_remove_file(bus, &bus_attr_drivers_probe);
+out:
+ return retval;
+}
+
+static void remove_probe_files(struct bus_type *bus)
+{
+ bus_remove_file(bus, &bus_attr_drivers_autoprobe);
+ bus_remove_file(bus, &bus_attr_drivers_probe);
+}
+#else
+static inline int add_bind_files(struct device_driver *drv) { return 0; }
+static inline void remove_bind_files(struct device_driver *drv) {}
+static inline int add_probe_files(struct bus_type *bus) { return 0; }
+static inline void remove_probe_files(struct bus_type *bus) {}
+#endif
+
+static ssize_t driver_uevent_store(struct device_driver *drv,
+ const char *buf, size_t count)
+{
+ enum kobject_action action;
+
+ if (kobject_action_type(buf, count, &action) == 0)
+ kobject_uevent(&drv->p->kobj, action);
+ return count;
+}
+static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store);
+
+/**
+ * bus_add_driver - Add a driver to the bus.
+ * @drv: driver.
+ */
+int bus_add_driver(struct device_driver *drv)
+{
+ struct bus_type *bus;
+ struct driver_private *priv;
+ int error = 0;
+
+ bus = bus_get(drv->bus);
+ if (!bus)
+ return -EINVAL;
+
+ pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ error = -ENOMEM;
+ goto out_put_bus;
+ }
+ klist_init(&priv->klist_devices, NULL, NULL);
+ priv->driver = drv;
+ drv->p = priv;
+ priv->kobj.kset = bus->p->drivers_kset;
+ error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
+ "%s", drv->name);
+ if (error)
+ goto out_unregister;
+
+ if (drv->bus->p->drivers_autoprobe) {
+ error = driver_attach(drv);
+ if (error)
+ goto out_unregister;
+ }
+ klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
+ module_add_driver(drv->owner, drv);
+
+ error = driver_create_file(drv, &driver_attr_uevent);
+ if (error) {
+ printk(KERN_ERR "%s: uevent attr (%s) failed\n",
+ __func__, drv->name);
+ }
+ error = driver_add_attrs(bus, drv);
+ if (error) {
+ /* How the hell do we get out of this pickle? Give up */
+ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
+ __func__, drv->name);
+ }
+ error = add_bind_files(drv);
+ if (error) {
+ /* Ditto */
+ printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
+ __func__, drv->name);
+ }
+
+ kobject_uevent(&priv->kobj, KOBJ_ADD);
+ return error;
+out_unregister:
+ kobject_put(&priv->kobj);
+out_put_bus:
+ bus_put(bus);
+ return error;
+}
+
+/**
+ * bus_remove_driver - delete driver from bus's knowledge.
+ * @drv: driver.
+ *
+ * Detach the driver from the devices it controls, and remove
+ * it from its bus's list of drivers. Finally, we drop the reference
+ * to the bus we took in bus_add_driver().
+ */
+void bus_remove_driver(struct device_driver *drv)
+{
+ if (!drv->bus)
+ return;
+
+ remove_bind_files(drv);
+ driver_remove_attrs(drv->bus, drv);
+ driver_remove_file(drv, &driver_attr_uevent);
+ klist_remove(&drv->p->knode_bus);
+ pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name);
+ driver_detach(drv);
+ module_remove_driver(drv);
+ kobject_put(&drv->p->kobj);
+ bus_put(drv->bus);
+}
+
+/* Helper for bus_rescan_devices's iter */
+static int __must_check bus_rescan_devices_helper(struct device *dev,
+ void *data)
+{
+ int ret = 0;
+
+ if (!dev->driver) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ ret = device_attach(dev);
+ if (dev->parent)
+ up(&dev->parent->sem);
+ }
+ return ret < 0 ? ret : 0;
+}
+
+/**
+ * bus_rescan_devices - rescan devices on the bus for possible drivers
+ * @bus: the bus to scan.
+ *
+ * This function will look for devices on the bus with no driver
+ * attached and rescan it against existing drivers to see if it matches
+ * any by calling device_attach() for the unbound devices.
+ */
+int bus_rescan_devices(struct bus_type *bus)
+{
+ return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
+}
+EXPORT_SYMBOL_GPL(bus_rescan_devices);
+
+/**
+ * device_reprobe - remove driver for a device and probe for a new driver
+ * @dev: the device to reprobe
+ *
+ * This function detaches the attached driver (if any) for the given
+ * device and restarts the driver probing process. It is intended
+ * to use if probing criteria changed during a devices lifetime and
+ * driver attachment should change accordingly.
+ */
+int device_reprobe(struct device *dev)
+{
+ if (dev->driver) {
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ device_release_driver(dev);
+ if (dev->parent)
+ up(&dev->parent->sem);
+ }
+ return bus_rescan_devices_helper(dev, NULL);
+}
+EXPORT_SYMBOL_GPL(device_reprobe);
+
+/**
+ * find_bus - locate bus by name.
+ * @name: name of bus.
+ *
+ * Call kset_find_obj() to iterate over list of buses to
+ * find a bus by name. Return bus if found.
+ *
+ * Note that kset_find_obj increments bus' reference count.
+ */
+#if 0
+struct bus_type *find_bus(char *name)
+{
+ struct kobject *k = kset_find_obj(bus_kset, name);
+ return k ? to_bus(k) : NULL;
+}
+#endif /* 0 */
+
+
+/**
+ * bus_add_attrs - Add default attributes for this bus.
+ * @bus: Bus that has just been registered.
+ */
+
+static int bus_add_attrs(struct bus_type *bus)
+{
+ int error = 0;
+ int i;
+
+ if (bus->bus_attrs) {
+ for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
+ error = bus_create_file(bus, &bus->bus_attrs[i]);
+ if (error)
+ goto err;
+ }
+ }
+done:
+ return error;
+err:
+ while (--i >= 0)
+ bus_remove_file(bus, &bus->bus_attrs[i]);
+ goto done;
+}
+
+static void bus_remove_attrs(struct bus_type *bus)
+{
+ int i;
+
+ if (bus->bus_attrs) {
+ for (i = 0; attr_name(bus->bus_attrs[i]); i++)
+ bus_remove_file(bus, &bus->bus_attrs[i]);
+ }
+}
+
+static void klist_devices_get(struct klist_node *n)
+{
+ struct device *dev = container_of(n, struct device, knode_bus);
+
+ get_device(dev);
+}
+
+static void klist_devices_put(struct klist_node *n)
+{
+ struct device *dev = container_of(n, struct device, knode_bus);
+
+ put_device(dev);
+}
+
+static ssize_t bus_uevent_store(struct bus_type *bus,
+ const char *buf, size_t count)
+{
+ enum kobject_action action;
+
+ if (kobject_action_type(buf, count, &action) == 0)
+ kobject_uevent(&bus->p->subsys.kobj, action);
+ return count;
+}
+static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
+
+/**
+ * bus_register - register a bus with the system.
+ * @bus: bus.
+ *
+ * Once we have that, we registered the bus with the kobject
+ * infrastructure, then register the children subsystems it has:
+ * the devices and drivers that belong to the bus.
+ */
+int bus_register(struct bus_type *bus)
+{
+ int retval;
+ struct bus_type_private *priv;
+
+ priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->bus = bus;
+ bus->p = priv;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
+
+ retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
+ if (retval)
+ goto out;
+
+ priv->subsys.kobj.kset = bus_kset;
+ priv->subsys.kobj.ktype = &bus_ktype;
+ priv->drivers_autoprobe = 1;
+
+ retval = kset_register(&priv->subsys);
+ if (retval)
+ goto out;
+
+ retval = bus_create_file(bus, &bus_attr_uevent);
+ if (retval)
+ goto bus_uevent_fail;
+
+ priv->devices_kset = kset_create_and_add("devices", NULL,
+ &priv->subsys.kobj);
+ if (!priv->devices_kset) {
+ retval = -ENOMEM;
+ goto bus_devices_fail;
+ }
+
+ priv->drivers_kset = kset_create_and_add("drivers", NULL,
+ &priv->subsys.kobj);
+ if (!priv->drivers_kset) {
+ retval = -ENOMEM;
+ goto bus_drivers_fail;
+ }
+
+ klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
+ klist_init(&priv->klist_drivers, NULL, NULL);
+
+ retval = add_probe_files(bus);
+ if (retval)
+ goto bus_probe_files_fail;
+
+ retval = bus_add_attrs(bus);
+ if (retval)
+ goto bus_attrs_fail;
+
+ pr_debug("bus: '%s': registered\n", bus->name);
+ return 0;
+
+bus_attrs_fail:
+ remove_probe_files(bus);
+bus_probe_files_fail:
+ kset_unregister(bus->p->drivers_kset);
+bus_drivers_fail:
+ kset_unregister(bus->p->devices_kset);
+bus_devices_fail:
+ bus_remove_file(bus, &bus_attr_uevent);
+bus_uevent_fail:
+ kset_unregister(&bus->p->subsys);
+ kfree(bus->p);
+out:
+ return retval;
+}
+EXPORT_SYMBOL_GPL(bus_register);
+
+/**
+ * bus_unregister - remove a bus from the system
+ * @bus: bus.
+ *
+ * Unregister the child subsystems and the bus itself.
+ * Finally, we call bus_put() to release the refcount
+ */
+void bus_unregister(struct bus_type *bus)
+{
+ pr_debug("bus: '%s': unregistering\n", bus->name);
+ bus_remove_attrs(bus);
+ remove_probe_files(bus);
+ kset_unregister(bus->p->drivers_kset);
+ kset_unregister(bus->p->devices_kset);
+ bus_remove_file(bus, &bus_attr_uevent);
+ kset_unregister(&bus->p->subsys);
+ kfree(bus->p);
+}
+EXPORT_SYMBOL_GPL(bus_unregister);
+
+int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(bus_register_notifier);
+
+int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&bus->p->bus_notifier, nb);
+}
+EXPORT_SYMBOL_GPL(bus_unregister_notifier);
+
+struct kset *bus_get_kset(struct bus_type *bus)
+{
+ return &bus->p->subsys;
+}
+EXPORT_SYMBOL_GPL(bus_get_kset);
+
+struct klist *bus_get_device_klist(struct bus_type *bus)
+{
+ return &bus->p->klist_devices;
+}
+EXPORT_SYMBOL_GPL(bus_get_device_klist);
+
+/*
+ * Yes, this forcably breaks the klist abstraction temporarily. It
+ * just wants to sort the klist, not change reference counts and
+ * take/drop locks rapidly in the process. It does all this while
+ * holding the lock for the list, so objects can't otherwise be
+ * added/removed while we're swizzling.
+ */
+static void device_insertion_sort_klist(struct device *a, struct list_head *list,
+ int (*compare)(const struct device *a,
+ const struct device *b))
+{
+ struct list_head *pos;
+ struct klist_node *n;
+ struct device *b;
+
+ list_for_each(pos, list) {
+ n = container_of(pos, struct klist_node, n_node);
+ b = container_of(n, struct device, knode_bus);
+ if (compare(a, b) <= 0) {
+ list_move_tail(&a->knode_bus.n_node,
+ &b->knode_bus.n_node);
+ return;
+ }
+ }
+ list_move_tail(&a->knode_bus.n_node, list);
+}
+
+void bus_sort_breadthfirst(struct bus_type *bus,
+ int (*compare)(const struct device *a,
+ const struct device *b))
+{
+ LIST_HEAD(sorted_devices);
+ struct list_head *pos, *tmp;
+ struct klist_node *n;
+ struct device *dev;
+ struct klist *device_klist;
+
+ device_klist = bus_get_device_klist(bus);
+
+ spin_lock(&device_klist->k_lock);
+ list_for_each_safe(pos, tmp, &device_klist->k_list) {
+ n = container_of(pos, struct klist_node, n_node);
+ dev = container_of(n, struct device, knode_bus);
+ device_insertion_sort_klist(dev, &sorted_devices, compare);
+ }
+ list_splice(&sorted_devices, &device_klist->k_list);
+ spin_unlock(&device_klist->k_lock);
+}
+EXPORT_SYMBOL_GPL(bus_sort_breadthfirst);
+
+int __init buses_init(void)
+{
+ bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
+ if (!bus_kset)
+ return -ENOMEM;
+ return 0;
+}
diff --git a/drivers/base/class.c b/drivers/base/class.c
new file mode 100644
index 0000000..eb85e43
--- /dev/null
+++ b/drivers/base/class.c
@@ -0,0 +1,505 @@
+/*
+ * class.c - basic device class management
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2003-2004 Greg Kroah-Hartman
+ * Copyright (c) 2003-2004 IBM Corp.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/kdev_t.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/genhd.h>
+#include <linux/mutex.h>
+#include "base.h"
+
+#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
+
+static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct class_attribute *class_attr = to_class_attr(attr);
+ struct class_private *cp = to_class(kobj);
+ ssize_t ret = -EIO;
+
+ if (class_attr->show)
+ ret = class_attr->show(cp->class, buf);
+ return ret;
+}
+
+static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct class_attribute *class_attr = to_class_attr(attr);
+ struct class_private *cp = to_class(kobj);
+ ssize_t ret = -EIO;
+
+ if (class_attr->store)
+ ret = class_attr->store(cp->class, buf, count);
+ return ret;
+}
+
+static void class_release(struct kobject *kobj)
+{
+ struct class_private *cp = to_class(kobj);
+ struct class *class = cp->class;
+
+ pr_debug("class '%s': release.\n", class->name);
+
+ if (class->class_release)
+ class->class_release(class);
+ else
+ pr_debug("class '%s' does not have a release() function, "
+ "be careful\n", class->name);
+}
+
+static struct sysfs_ops class_sysfs_ops = {
+ .show = class_attr_show,
+ .store = class_attr_store,
+};
+
+static struct kobj_type class_ktype = {
+ .sysfs_ops = &class_sysfs_ops,
+ .release = class_release,
+};
+
+/* Hotplug events for classes go to the class class_subsys */
+static struct kset *class_kset;
+
+
+int class_create_file(struct class *cls, const struct class_attribute *attr)
+{
+ int error;
+ if (cls)
+ error = sysfs_create_file(&cls->p->class_subsys.kobj,
+ &attr->attr);
+ else
+ error = -EINVAL;
+ return error;
+}
+
+void class_remove_file(struct class *cls, const struct class_attribute *attr)
+{
+ if (cls)
+ sysfs_remove_file(&cls->p->class_subsys.kobj, &attr->attr);
+}
+
+static struct class *class_get(struct class *cls)
+{
+ if (cls)
+ kset_get(&cls->p->class_subsys);
+ return cls;
+}
+
+static void class_put(struct class *cls)
+{
+ if (cls)
+ kset_put(&cls->p->class_subsys);
+}
+
+static int add_class_attrs(struct class *cls)
+{
+ int i;
+ int error = 0;
+
+ if (cls->class_attrs) {
+ for (i = 0; attr_name(cls->class_attrs[i]); i++) {
+ error = class_create_file(cls, &cls->class_attrs[i]);
+ if (error)
+ goto error;
+ }
+ }
+done:
+ return error;
+error:
+ while (--i >= 0)
+ class_remove_file(cls, &cls->class_attrs[i]);
+ goto done;
+}
+
+static void remove_class_attrs(struct class *cls)
+{
+ int i;
+
+ if (cls->class_attrs) {
+ for (i = 0; attr_name(cls->class_attrs[i]); i++)
+ class_remove_file(cls, &cls->class_attrs[i]);
+ }
+}
+
+static void klist_class_dev_get(struct klist_node *n)
+{
+ struct device *dev = container_of(n, struct device, knode_class);
+
+ get_device(dev);
+}
+
+static void klist_class_dev_put(struct klist_node *n)
+{
+ struct device *dev = container_of(n, struct device, knode_class);
+
+ put_device(dev);
+}
+
+int __class_register(struct class *cls, struct lock_class_key *key)
+{
+ struct class_private *cp;
+ int error;
+
+ pr_debug("device class '%s': registering\n", cls->name);
+
+ cp = kzalloc(sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+ klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put);
+ INIT_LIST_HEAD(&cp->class_interfaces);
+ kset_init(&cp->class_dirs);
+ __mutex_init(&cp->class_mutex, "struct class mutex", key);
+ error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
+ if (error) {
+ kfree(cp);
+ return error;
+ }
+
+ /* set the default /sys/dev directory for devices of this class */
+ if (!cls->dev_kobj)
+ cls->dev_kobj = sysfs_dev_char_kobj;
+
+#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
+ /* let the block class directory show up in the root of sysfs */
+ if (cls != &block_class)
+ cp->class_subsys.kobj.kset = class_kset;
+#else
+ cp->class_subsys.kobj.kset = class_kset;
+#endif
+ cp->class_subsys.kobj.ktype = &class_ktype;
+ cp->class = cls;
+ cls->p = cp;
+
+ error = kset_register(&cp->class_subsys);
+ if (error) {
+ kfree(cp);
+ return error;
+ }
+ error = add_class_attrs(class_get(cls));
+ class_put(cls);
+ return error;
+}
+EXPORT_SYMBOL_GPL(__class_register);
+
+void class_unregister(struct class *cls)
+{
+ pr_debug("device class '%s': unregistering\n", cls->name);
+ remove_class_attrs(cls);
+ kset_unregister(&cls->p->class_subsys);
+}
+
+static void class_create_release(struct class *cls)
+{
+ pr_debug("%s called for %s\n", __func__, cls->name);
+ kfree(cls);
+}
+
+/**
+ * class_create - create a struct class structure
+ * @owner: pointer to the module that is to "own" this struct class
+ * @name: pointer to a string for the name of this class.
+ * @key: the lock_class_key for this class; used by mutex lock debugging
+ *
+ * This is used to create a struct class pointer that can then be used
+ * in calls to device_create().
+ *
+ * Note, the pointer created here is to be destroyed when finished by
+ * making a call to class_destroy().
+ */
+struct class *__class_create(struct module *owner, const char *name,
+ struct lock_class_key *key)
+{
+ struct class *cls;
+ int retval;
+
+ cls = kzalloc(sizeof(*cls), GFP_KERNEL);
+ if (!cls) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ cls->name = name;
+ cls->owner = owner;
+ cls->class_release = class_create_release;
+
+ retval = __class_register(cls, key);
+ if (retval)
+ goto error;
+
+ return cls;
+
+error:
+ kfree(cls);
+ return ERR_PTR(retval);
+}
+EXPORT_SYMBOL_GPL(__class_create);
+
+/**
+ * class_destroy - destroys a struct class structure
+ * @cls: pointer to the struct class that is to be destroyed
+ *
+ * Note, the pointer to be destroyed must have been created with a call
+ * to class_create().
+ */
+void class_destroy(struct class *cls)
+{
+ if ((cls == NULL) || (IS_ERR(cls)))
+ return;
+
+ class_unregister(cls);
+}
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+char *make_class_name(const char *name, struct kobject *kobj)
+{
+ char *class_name;
+ int size;
+
+ size = strlen(name) + strlen(kobject_name(kobj)) + 2;
+
+ class_name = kmalloc(size, GFP_KERNEL);
+ if (!class_name)
+ return NULL;
+
+ strcpy(class_name, name);
+ strcat(class_name, ":");
+ strcat(class_name, kobject_name(kobj));
+ return class_name;
+}
+#endif
+
+/**
+ * class_dev_iter_init - initialize class device iterator
+ * @iter: class iterator to initialize
+ * @class: the class we wanna iterate over
+ * @start: the device to start iterating from, if any
+ * @type: device_type of the devices to iterate over, NULL for all
+ *
+ * Initialize class iterator @iter such that it iterates over devices
+ * of @class. If @start is set, the list iteration will start there,
+ * otherwise if it is NULL, the iteration starts at the beginning of
+ * the list.
+ */
+void class_dev_iter_init(struct class_dev_iter *iter, struct class *class,
+ struct device *start, const struct device_type *type)
+{
+ struct klist_node *start_knode = NULL;
+
+ if (start)
+ start_knode = &start->knode_class;
+ klist_iter_init_node(&class->p->class_devices, &iter->ki, start_knode);
+ iter->type = type;
+}
+EXPORT_SYMBOL_GPL(class_dev_iter_init);
+
+/**
+ * class_dev_iter_next - iterate to the next device
+ * @iter: class iterator to proceed
+ *
+ * Proceed @iter to the next device and return it. Returns NULL if
+ * iteration is complete.
+ *
+ * The returned device is referenced and won't be released till
+ * iterator is proceed to the next device or exited. The caller is
+ * free to do whatever it wants to do with the device including
+ * calling back into class code.
+ */
+struct device *class_dev_iter_next(struct class_dev_iter *iter)
+{
+ struct klist_node *knode;
+ struct device *dev;
+
+ while (1) {
+ knode = klist_next(&iter->ki);
+ if (!knode)
+ return NULL;
+ dev = container_of(knode, struct device, knode_class);
+ if (!iter->type || iter->type == dev->type)
+ return dev;
+ }
+}
+EXPORT_SYMBOL_GPL(class_dev_iter_next);
+
+/**
+ * class_dev_iter_exit - finish iteration
+ * @iter: class iterator to finish
+ *
+ * Finish an iteration. Always call this function after iteration is
+ * complete whether the iteration ran till the end or not.
+ */
+void class_dev_iter_exit(struct class_dev_iter *iter)
+{
+ klist_iter_exit(&iter->ki);
+}
+EXPORT_SYMBOL_GPL(class_dev_iter_exit);
+
+/**
+ * class_for_each_device - device iterator
+ * @class: the class we're iterating
+ * @start: the device to start with in the list, if any.
+ * @data: data for the callback
+ * @fn: function to be called for each device
+ *
+ * Iterate over @class's list of devices, and call @fn for each,
+ * passing it @data. If @start is set, the list iteration will start
+ * there, otherwise if it is NULL, the iteration starts at the
+ * beginning of the list.
+ *
+ * We check the return of @fn each time. If it returns anything
+ * other than 0, we break out and return that value.
+ *
+ * @fn is allowed to do anything including calling back into class
+ * code. There's no locking restriction.
+ */
+int class_for_each_device(struct class *class, struct device *start,
+ void *data, int (*fn)(struct device *, void *))
+{
+ struct class_dev_iter iter;
+ struct device *dev;
+ int error = 0;
+
+ if (!class)
+ return -EINVAL;
+ if (!class->p) {
+ WARN(1, "%s called for class '%s' before it was initialized",
+ __func__, class->name);
+ return -EINVAL;
+ }
+
+ class_dev_iter_init(&iter, class, start, NULL);
+ while ((dev = class_dev_iter_next(&iter))) {
+ error = fn(dev, data);
+ if (error)
+ break;
+ }
+ class_dev_iter_exit(&iter);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(class_for_each_device);
+
+/**
+ * class_find_device - device iterator for locating a particular device
+ * @class: the class we're iterating
+ * @start: Device to begin with
+ * @data: data for the match function
+ * @match: function to check device
+ *
+ * This is similar to the class_for_each_dev() function above, but it
+ * returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero, this function will
+ * return to the caller and not iterate over any more devices.
+ *
+ * Note, you will need to drop the reference with put_device() after use.
+ *
+ * @fn is allowed to do anything including calling back into class
+ * code. There's no locking restriction.
+ */
+struct device *class_find_device(struct class *class, struct device *start,
+ void *data,
+ int (*match)(struct device *, void *))
+{
+ struct class_dev_iter iter;
+ struct device *dev;
+
+ if (!class)
+ return NULL;
+ if (!class->p) {
+ WARN(1, "%s called for class '%s' before it was initialized",
+ __func__, class->name);
+ return NULL;
+ }
+
+ class_dev_iter_init(&iter, class, start, NULL);
+ while ((dev = class_dev_iter_next(&iter))) {
+ if (match(dev, data)) {
+ get_device(dev);
+ break;
+ }
+ }
+ class_dev_iter_exit(&iter);
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(class_find_device);
+
+int class_interface_register(struct class_interface *class_intf)
+{
+ struct class *parent;
+ struct class_dev_iter iter;
+ struct device *dev;
+
+ if (!class_intf || !class_intf->class)
+ return -ENODEV;
+
+ parent = class_get(class_intf->class);
+ if (!parent)
+ return -EINVAL;
+
+ mutex_lock(&parent->p->class_mutex);
+ list_add_tail(&class_intf->node, &parent->p->class_interfaces);
+ if (class_intf->add_dev) {
+ class_dev_iter_init(&iter, parent, NULL, NULL);
+ while ((dev = class_dev_iter_next(&iter)))
+ class_intf->add_dev(dev, class_intf);
+ class_dev_iter_exit(&iter);
+ }
+ mutex_unlock(&parent->p->class_mutex);
+
+ return 0;
+}
+
+void class_interface_unregister(struct class_interface *class_intf)
+{
+ struct class *parent = class_intf->class;
+ struct class_dev_iter iter;
+ struct device *dev;
+
+ if (!parent)
+ return;
+
+ mutex_lock(&parent->p->class_mutex);
+ list_del_init(&class_intf->node);
+ if (class_intf->remove_dev) {
+ class_dev_iter_init(&iter, parent, NULL, NULL);
+ while ((dev = class_dev_iter_next(&iter)))
+ class_intf->remove_dev(dev, class_intf);
+ class_dev_iter_exit(&iter);
+ }
+ mutex_unlock(&parent->p->class_mutex);
+
+ class_put(parent);
+}
+
+int __init classes_init(void)
+{
+ class_kset = kset_create_and_add("class", NULL, NULL);
+ if (!class_kset)
+ return -ENOMEM;
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(class_create_file);
+EXPORT_SYMBOL_GPL(class_remove_file);
+EXPORT_SYMBOL_GPL(class_unregister);
+EXPORT_SYMBOL_GPL(class_destroy);
+
+EXPORT_SYMBOL_GPL(class_interface_register);
+EXPORT_SYMBOL_GPL(class_interface_unregister);
diff --git a/drivers/base/core.c b/drivers/base/core.c
new file mode 100644
index 0000000..8c2cc26
--- /dev/null
+++ b/drivers/base/core.c
@@ -0,0 +1,1527 @@
+/*
+ * drivers/base/core.c - core driver model code (device registration, etc)
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2006 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2006 Novell, Inc.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/kdev_t.h>
+#include <linux/notifier.h>
+#include <linux/genhd.h>
+#include <linux/kallsyms.h>
+#include <linux/semaphore.h>
+#include <linux/mutex.h>
+
+#include "base.h"
+#include "power/power.h"
+
+int (*platform_notify)(struct device *dev) = NULL;
+int (*platform_notify_remove)(struct device *dev) = NULL;
+static struct kobject *dev_kobj;
+struct kobject *sysfs_dev_char_kobj;
+struct kobject *sysfs_dev_block_kobj;
+
+#ifdef CONFIG_BLOCK
+static inline int device_is_not_partition(struct device *dev)
+{
+ return !(dev->type == &part_type);
+}
+#else
+static inline int device_is_not_partition(struct device *dev)
+{
+ return 1;
+}
+#endif
+
+/**
+ * dev_driver_string - Return a device's driver name, if at all possible
+ * @dev: struct device to get the name of
+ *
+ * Will return the device's driver's name if it is bound to a device. If
+ * the device is not bound to a device, it will return the name of the bus
+ * it is attached to. If it is not attached to a bus either, an empty
+ * string will be returned.
+ */
+const char *dev_driver_string(const struct device *dev)
+{
+ return dev->driver ? dev->driver->name :
+ (dev->bus ? dev->bus->name :
+ (dev->class ? dev->class->name : ""));
+}
+EXPORT_SYMBOL(dev_driver_string);
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
+
+static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct device_attribute *dev_attr = to_dev_attr(attr);
+ struct device *dev = to_dev(kobj);
+ ssize_t ret = -EIO;
+
+ if (dev_attr->show)
+ ret = dev_attr->show(dev, dev_attr, buf);
+ if (ret >= (ssize_t)PAGE_SIZE) {
+ print_symbol("dev_attr_show: %s returned bad count\n",
+ (unsigned long)dev_attr->show);
+ }
+ return ret;
+}
+
+static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct device_attribute *dev_attr = to_dev_attr(attr);
+ struct device *dev = to_dev(kobj);
+ ssize_t ret = -EIO;
+
+ if (dev_attr->store)
+ ret = dev_attr->store(dev, dev_attr, buf, count);
+ return ret;
+}
+
+static struct sysfs_ops dev_sysfs_ops = {
+ .show = dev_attr_show,
+ .store = dev_attr_store,
+};
+
+
+/**
+ * device_release - free device structure.
+ * @kobj: device's kobject.
+ *
+ * This is called once the reference count for the object
+ * reaches 0. We forward the call to the device's release
+ * method, which should handle actually freeing the structure.
+ */
+static void device_release(struct kobject *kobj)
+{
+ struct device *dev = to_dev(kobj);
+
+ if (dev->release)
+ dev->release(dev);
+ else if (dev->type && dev->type->release)
+ dev->type->release(dev);
+ else if (dev->class && dev->class->dev_release)
+ dev->class->dev_release(dev);
+ else
+ WARN(1, KERN_ERR "Device '%s' does not have a release() "
+ "function, it is broken and must be fixed.\n",
+ dev->bus_id);
+}
+
+static struct kobj_type device_ktype = {
+ .release = device_release,
+ .sysfs_ops = &dev_sysfs_ops,
+};
+
+
+static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
+{
+ struct kobj_type *ktype = get_ktype(kobj);
+
+ if (ktype == &device_ktype) {
+ struct device *dev = to_dev(kobj);
+ if (dev->uevent_suppress)
+ return 0;
+ if (dev->bus)
+ return 1;
+ if (dev->class)
+ return 1;
+ }
+ return 0;
+}
+
+static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
+{
+ struct device *dev = to_dev(kobj);
+
+ if (dev->bus)
+ return dev->bus->name;
+ if (dev->class)
+ return dev->class->name;
+ return NULL;
+}
+
+static int dev_uevent(struct kset *kset, struct kobject *kobj,
+ struct kobj_uevent_env *env)
+{
+ struct device *dev = to_dev(kobj);
+ int retval = 0;
+
+ /* add the major/minor if present */
+ if (MAJOR(dev->devt)) {
+ add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
+ add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
+ }
+
+ if (dev->type && dev->type->name)
+ add_uevent_var(env, "DEVTYPE=%s", dev->type->name);
+
+ if (dev->driver)
+ add_uevent_var(env, "DRIVER=%s", dev->driver->name);
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+ if (dev->class) {
+ struct device *parent = dev->parent;
+
+ /* find first bus device in parent chain */
+ while (parent && !parent->bus)
+ parent = parent->parent;
+ if (parent && parent->bus) {
+ const char *path;
+
+ path = kobject_get_path(&parent->kobj, GFP_KERNEL);
+ if (path) {
+ add_uevent_var(env, "PHYSDEVPATH=%s", path);
+ kfree(path);
+ }
+
+ add_uevent_var(env, "PHYSDEVBUS=%s", parent->bus->name);
+
+ if (parent->driver)
+ add_uevent_var(env, "PHYSDEVDRIVER=%s",
+ parent->driver->name);
+ }
+ } else if (dev->bus) {
+ add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);
+
+ if (dev->driver)
+ add_uevent_var(env, "PHYSDEVDRIVER=%s",
+ dev->driver->name);
+ }
+#endif
+
+ /* have the bus specific function add its stuff */
+ if (dev->bus && dev->bus->uevent) {
+ retval = dev->bus->uevent(dev, env);
+ if (retval)
+ pr_debug("device: '%s': %s: bus uevent() returned %d\n",
+ dev->bus_id, __func__, retval);
+ }
+
+ /* have the class specific function add its stuff */
+ if (dev->class && dev->class->dev_uevent) {
+ retval = dev->class->dev_uevent(dev, env);
+ if (retval)
+ pr_debug("device: '%s': %s: class uevent() "
+ "returned %d\n", dev->bus_id,
+ __func__, retval);
+ }
+
+ /* have the device type specific fuction add its stuff */
+ if (dev->type && dev->type->uevent) {
+ retval = dev->type->uevent(dev, env);
+ if (retval)
+ pr_debug("device: '%s': %s: dev_type uevent() "
+ "returned %d\n", dev->bus_id,
+ __func__, retval);
+ }
+
+ return retval;
+}
+
+static struct kset_uevent_ops device_uevent_ops = {
+ .filter = dev_uevent_filter,
+ .name = dev_uevent_name,
+ .uevent = dev_uevent,
+};
+
+static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct kobject *top_kobj;
+ struct kset *kset;
+ struct kobj_uevent_env *env = NULL;
+ int i;
+ size_t count = 0;
+ int retval;
+
+ /* search the kset, the device belongs to */
+ top_kobj = &dev->kobj;
+ while (!top_kobj->kset && top_kobj->parent)
+ top_kobj = top_kobj->parent;
+ if (!top_kobj->kset)
+ goto out;
+
+ kset = top_kobj->kset;
+ if (!kset->uevent_ops || !kset->uevent_ops->uevent)
+ goto out;
+
+ /* respect filter */
+ if (kset->uevent_ops && kset->uevent_ops->filter)
+ if (!kset->uevent_ops->filter(kset, &dev->kobj))
+ goto out;
+
+ env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
+ if (!env)
+ return -ENOMEM;
+
+ /* let the kset specific function add its keys */
+ retval = kset->uevent_ops->uevent(kset, &dev->kobj, env);
+ if (retval)
+ goto out;
+
+ /* copy keys to file */
+ for (i = 0; i < env->envp_idx; i++)
+ count += sprintf(&buf[count], "%s\n", env->envp[i]);
+out:
+ kfree(env);
+ return count;
+}
+
+static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ enum kobject_action action;
+
+ if (kobject_action_type(buf, count, &action) == 0) {
+ kobject_uevent(&dev->kobj, action);
+ goto out;
+ }
+
+ dev_err(dev, "uevent: unsupported action-string; this will "
+ "be ignored in a future kernel version\n");
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+out:
+ return count;
+}
+
+static struct device_attribute uevent_attr =
+ __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
+
+static int device_add_attributes(struct device *dev,
+ struct device_attribute *attrs)
+{
+ int error = 0;
+ int i;
+
+ if (attrs) {
+ for (i = 0; attr_name(attrs[i]); i++) {
+ error = device_create_file(dev, &attrs[i]);
+ if (error)
+ break;
+ }
+ if (error)
+ while (--i >= 0)
+ device_remove_file(dev, &attrs[i]);
+ }
+ return error;
+}
+
+static void device_remove_attributes(struct device *dev,
+ struct device_attribute *attrs)
+{
+ int i;
+
+ if (attrs)
+ for (i = 0; attr_name(attrs[i]); i++)
+ device_remove_file(dev, &attrs[i]);
+}
+
+static int device_add_groups(struct device *dev,
+ struct attribute_group **groups)
+{
+ int error = 0;
+ int i;
+
+ if (groups) {
+ for (i = 0; groups[i]; i++) {
+ error = sysfs_create_group(&dev->kobj, groups[i]);
+ if (error) {
+ while (--i >= 0)
+ sysfs_remove_group(&dev->kobj,
+ groups[i]);
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+static void device_remove_groups(struct device *dev,
+ struct attribute_group **groups)
+{
+ int i;
+
+ if (groups)
+ for (i = 0; groups[i]; i++)
+ sysfs_remove_group(&dev->kobj, groups[i]);
+}
+
+static int device_add_attrs(struct device *dev)
+{
+ struct class *class = dev->class;
+ struct device_type *type = dev->type;
+ int error;
+
+ if (class) {
+ error = device_add_attributes(dev, class->dev_attrs);
+ if (error)
+ return error;
+ }
+
+ if (type) {
+ error = device_add_groups(dev, type->groups);
+ if (error)
+ goto err_remove_class_attrs;
+ }
+
+ error = device_add_groups(dev, dev->groups);
+ if (error)
+ goto err_remove_type_groups;
+
+ return 0;
+
+ err_remove_type_groups:
+ if (type)
+ device_remove_groups(dev, type->groups);
+ err_remove_class_attrs:
+ if (class)
+ device_remove_attributes(dev, class->dev_attrs);
+
+ return error;
+}
+
+static void device_remove_attrs(struct device *dev)
+{
+ struct class *class = dev->class;
+ struct device_type *type = dev->type;
+
+ device_remove_groups(dev, dev->groups);
+
+ if (type)
+ device_remove_groups(dev, type->groups);
+
+ if (class)
+ device_remove_attributes(dev, class->dev_attrs);
+}
+
+
+static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return print_dev_t(buf, dev->devt);
+}
+
+static struct device_attribute devt_attr =
+ __ATTR(dev, S_IRUGO, show_dev, NULL);
+
+/* kset to create /sys/devices/ */
+struct kset *devices_kset;
+
+/**
+ * device_create_file - create sysfs attribute file for device.
+ * @dev: device.
+ * @attr: device attribute descriptor.
+ */
+int device_create_file(struct device *dev, struct device_attribute *attr)
+{
+ int error = 0;
+ if (dev)
+ error = sysfs_create_file(&dev->kobj, &attr->attr);
+ return error;
+}
+
+/**
+ * device_remove_file - remove sysfs attribute file.
+ * @dev: device.
+ * @attr: device attribute descriptor.
+ */
+void device_remove_file(struct device *dev, struct device_attribute *attr)
+{
+ if (dev)
+ sysfs_remove_file(&dev->kobj, &attr->attr);
+}
+
+/**
+ * device_create_bin_file - create sysfs binary attribute file for device.
+ * @dev: device.
+ * @attr: device binary attribute descriptor.
+ */
+int device_create_bin_file(struct device *dev, struct bin_attribute *attr)
+{
+ int error = -EINVAL;
+ if (dev)
+ error = sysfs_create_bin_file(&dev->kobj, attr);
+ return error;
+}
+EXPORT_SYMBOL_GPL(device_create_bin_file);
+
+/**
+ * device_remove_bin_file - remove sysfs binary attribute file
+ * @dev: device.
+ * @attr: device binary attribute descriptor.
+ */
+void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
+{
+ if (dev)
+ sysfs_remove_bin_file(&dev->kobj, attr);
+}
+EXPORT_SYMBOL_GPL(device_remove_bin_file);
+
+/**
+ * device_schedule_callback_owner - helper to schedule a callback for a device
+ * @dev: device.
+ * @func: callback function to invoke later.
+ * @owner: module owning the callback routine
+ *
+ * Attribute methods must not unregister themselves or their parent device
+ * (which would amount to the same thing). Attempts to do so will deadlock,
+ * since unregistration is mutually exclusive with driver callbacks.
+ *
+ * Instead methods can call this routine, which will attempt to allocate
+ * and schedule a workqueue request to call back @func with @dev as its
+ * argument in the workqueue's process context. @dev will be pinned until
+ * @func returns.
+ *
+ * This routine is usually called via the inline device_schedule_callback(),
+ * which automatically sets @owner to THIS_MODULE.
+ *
+ * Returns 0 if the request was submitted, -ENOMEM if storage could not
+ * be allocated, -ENODEV if a reference to @owner isn't available.
+ *
+ * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an
+ * underlying sysfs routine (since it is intended for use by attribute
+ * methods), and if sysfs isn't available you'll get nothing but -ENOSYS.
+ */
+int device_schedule_callback_owner(struct device *dev,
+ void (*func)(struct device *), struct module *owner)
+{
+ return sysfs_schedule_callback(&dev->kobj,
+ (void (*)(void *)) func, dev, owner);
+}
+EXPORT_SYMBOL_GPL(device_schedule_callback_owner);
+
+static void klist_children_get(struct klist_node *n)
+{
+ struct device *dev = container_of(n, struct device, knode_parent);
+
+ get_device(dev);
+}
+
+static void klist_children_put(struct klist_node *n)
+{
+ struct device *dev = container_of(n, struct device, knode_parent);
+
+ put_device(dev);
+}
+
+/**
+ * device_initialize - init device structure.
+ * @dev: device.
+ *
+ * This prepares the device for use by other layers by initializing
+ * its fields.
+ * It is the first half of device_register(), if called by
+ * that function, though it can also be called separately, so one
+ * may use @dev's fields. In particular, get_device()/put_device()
+ * may be used for reference counting of @dev after calling this
+ * function.
+ *
+ * NOTE: Use put_device() to give up your reference instead of freeing
+ * @dev directly once you have called this function.
+ */
+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_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);
+}
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+static struct kobject *get_device_parent(struct device *dev,
+ struct device *parent)
+{
+ /* class devices without a parent live in /sys/class/<classname>/ */
+ if (dev->class && (!parent || parent->class != dev->class))
+ return &dev->class->p->class_subsys.kobj;
+ /* all other devices keep their parent */
+ else if (parent)
+ return &parent->kobj;
+
+ return NULL;
+}
+
+static inline void cleanup_device_parent(struct device *dev) {}
+static inline void cleanup_glue_dir(struct device *dev,
+ struct kobject *glue_dir) {}
+#else
+static struct kobject *virtual_device_parent(struct device *dev)
+{
+ static struct kobject *virtual_dir = NULL;
+
+ if (!virtual_dir)
+ virtual_dir = kobject_create_and_add("virtual",
+ &devices_kset->kobj);
+
+ return virtual_dir;
+}
+
+static struct kobject *get_device_parent(struct device *dev,
+ struct device *parent)
+{
+ int retval;
+
+ if (dev->class) {
+ struct kobject *kobj = NULL;
+ struct kobject *parent_kobj;
+ struct kobject *k;
+
+ /*
+ * If we have no parent, we live in "virtual".
+ * Class-devices with a non class-device as parent, live
+ * in a "glue" directory to prevent namespace collisions.
+ */
+ if (parent == NULL)
+ parent_kobj = virtual_device_parent(dev);
+ else if (parent->class)
+ return &parent->kobj;
+ else
+ parent_kobj = &parent->kobj;
+
+ /* find our class-directory at the parent and reference it */
+ spin_lock(&dev->class->p->class_dirs.list_lock);
+ list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
+ if (k->parent == parent_kobj) {
+ kobj = kobject_get(k);
+ break;
+ }
+ spin_unlock(&dev->class->p->class_dirs.list_lock);
+ if (kobj)
+ return kobj;
+
+ /* or create a new class-directory at the parent device */
+ k = kobject_create();
+ if (!k)
+ return NULL;
+ k->kset = &dev->class->p->class_dirs;
+ retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
+ if (retval < 0) {
+ kobject_put(k);
+ return NULL;
+ }
+ /* do not emit an uevent for this simple "glue" directory */
+ return k;
+ }
+
+ if (parent)
+ return &parent->kobj;
+ return NULL;
+}
+
+static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
+{
+ /* see if we live in a "glue" directory */
+ if (!glue_dir || !dev->class ||
+ glue_dir->kset != &dev->class->p->class_dirs)
+ return;
+
+ kobject_put(glue_dir);
+}
+
+static void cleanup_device_parent(struct device *dev)
+{
+ cleanup_glue_dir(dev, dev->kobj.parent);
+}
+#endif
+
+static void setup_parent(struct device *dev, struct device *parent)
+{
+ struct kobject *kobj;
+ kobj = get_device_parent(dev, parent);
+ if (kobj)
+ dev->kobj.parent = kobj;
+}
+
+static int device_add_class_symlinks(struct device *dev)
+{
+ int error;
+
+ if (!dev->class)
+ return 0;
+
+ error = sysfs_create_link(&dev->kobj,
+ &dev->class->p->class_subsys.kobj,
+ "subsystem");
+ if (error)
+ goto out;
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+ /* stacked class devices need a symlink in the class directory */
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
+ device_is_not_partition(dev)) {
+ error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
+ if (error)
+ goto out_subsys;
+ }
+
+ if (dev->parent && device_is_not_partition(dev)) {
+ struct device *parent = dev->parent;
+ char *class_name;
+
+ /*
+ * stacked class devices have the 'device' link
+ * pointing to the bus device instead of the parent
+ */
+ while (parent->class && !parent->bus && parent->parent)
+ parent = parent->parent;
+
+ error = sysfs_create_link(&dev->kobj,
+ &parent->kobj,
+ "device");
+ if (error)
+ goto out_busid;
+
+ class_name = make_class_name(dev->class->name,
+ &dev->kobj);
+ if (class_name)
+ error = sysfs_create_link(&dev->parent->kobj,
+ &dev->kobj, class_name);
+ kfree(class_name);
+ if (error)
+ goto out_device;
+ }
+ return 0;
+
+out_device:
+ if (dev->parent && device_is_not_partition(dev))
+ sysfs_remove_link(&dev->kobj, "device");
+out_busid:
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
+ device_is_not_partition(dev))
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ dev->bus_id);
+#else
+ /* link in the class directory pointing to the device */
+ error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
+ if (error)
+ goto out_subsys;
+
+ if (dev->parent && device_is_not_partition(dev)) {
+ error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
+ "device");
+ if (error)
+ goto out_busid;
+ }
+ return 0;
+
+out_busid:
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id);
+#endif
+
+out_subsys:
+ sysfs_remove_link(&dev->kobj, "subsystem");
+out:
+ return error;
+}
+
+static void device_remove_class_symlinks(struct device *dev)
+{
+ if (!dev->class)
+ return;
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+ if (dev->parent && device_is_not_partition(dev)) {
+ char *class_name;
+
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ if (class_name) {
+ sysfs_remove_link(&dev->parent->kobj, class_name);
+ kfree(class_name);
+ }
+ sysfs_remove_link(&dev->kobj, "device");
+ }
+
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
+ device_is_not_partition(dev))
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ dev->bus_id);
+#else
+ if (dev->parent && device_is_not_partition(dev))
+ sysfs_remove_link(&dev->kobj, "device");
+
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id);
+#endif
+
+ sysfs_remove_link(&dev->kobj, "subsystem");
+}
+
+/**
+ * dev_set_name - set a device name
+ * @dev: device
+ * @fmt: format string for the device's name
+ */
+int dev_set_name(struct device *dev, const char *fmt, ...)
+{
+ va_list vargs;
+
+ va_start(vargs, fmt);
+ vsnprintf(dev->bus_id, sizeof(dev->bus_id), fmt, vargs);
+ va_end(vargs);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dev_set_name);
+
+/**
+ * device_to_dev_kobj - select a /sys/dev/ directory for the device
+ * @dev: device
+ *
+ * By default we select char/ for new entries. Setting class->dev_obj
+ * to NULL prevents an entry from being created. class->dev_kobj must
+ * be set (or cleared) before any devices are registered to the class
+ * otherwise device_create_sys_dev_entry() and
+ * device_remove_sys_dev_entry() will disagree about the the presence
+ * of the link.
+ */
+static struct kobject *device_to_dev_kobj(struct device *dev)
+{
+ struct kobject *kobj;
+
+ if (dev->class)
+ kobj = dev->class->dev_kobj;
+ else
+ kobj = sysfs_dev_char_kobj;
+
+ return kobj;
+}
+
+static int device_create_sys_dev_entry(struct device *dev)
+{
+ struct kobject *kobj = device_to_dev_kobj(dev);
+ int error = 0;
+ char devt_str[15];
+
+ if (kobj) {
+ format_dev_t(devt_str, dev->devt);
+ error = sysfs_create_link(kobj, &dev->kobj, devt_str);
+ }
+
+ return error;
+}
+
+static void device_remove_sys_dev_entry(struct device *dev)
+{
+ struct kobject *kobj = device_to_dev_kobj(dev);
+ char devt_str[15];
+
+ if (kobj) {
+ format_dev_t(devt_str, dev->devt);
+ sysfs_remove_link(kobj, devt_str);
+ }
+}
+
+/**
+ * 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 @dev 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.
+ *
+ * NOTE: _Never_ directly free @dev after calling this function, even
+ * if it returned an error! Always use put_device() to give up your
+ * reference instead.
+ */
+int device_add(struct device *dev)
+{
+ struct device *parent = NULL;
+ struct class_interface *class_intf;
+ int error = -EINVAL;
+
+ dev = get_device(dev);
+ if (!dev)
+ goto done;
+
+ /* Temporarily support init_name if it is set.
+ * It will override bus_id for now */
+ if (dev->init_name)
+ 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);
+ setup_parent(dev, 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);
+ if (error)
+ goto attrError;
+
+ if (MAJOR(dev->devt)) {
+ 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 */
+ klist_add_tail(&dev->knode_class,
+ &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;
+}
+
+/**
+ * device_register - register a device with the system.
+ * @dev: pointer to the device structure
+ *
+ * This happens in two clean steps - initialize the device
+ * and add it to the system. The two steps can be called
+ * separately, but this is the easiest and most common.
+ * I.e. you should only call the two helpers separately if
+ * have a clearly defined need to use and refcount the device
+ * before it is added to the hierarchy.
+ *
+ * NOTE: _Never_ directly free @dev after calling this function, even
+ * if it returned an error! Always use put_device() to give up the
+ * reference initialized in this function instead.
+ */
+int device_register(struct device *dev)
+{
+ device_initialize(dev);
+ return device_add(dev);
+}
+
+/**
+ * get_device - increment reference count for device.
+ * @dev: device.
+ *
+ * This simply forwards the call to kobject_get(), though
+ * we do take care to provide for the case that we get a NULL
+ * pointer passed in.
+ */
+struct device *get_device(struct device *dev)
+{
+ return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
+}
+
+/**
+ * put_device - decrement reference count.
+ * @dev: device in question.
+ */
+void put_device(struct device *dev)
+{
+ /* might_sleep(); */
+ if (dev)
+ kobject_put(&dev->kobj);
+}
+
+/**
+ * device_del - delete device from system.
+ * @dev: device.
+ *
+ * This is the first part of the device unregistration
+ * sequence. This removes the device from the lists we control
+ * from here, has it removed from the other driver model
+ * subsystems it was added to in device_add(), and removes it
+ * from the kobject hierarchy.
+ *
+ * NOTE: this should be called manually _iff_ device_add() was
+ * also called manually.
+ */
+void device_del(struct device *dev)
+{
+ struct device *parent = dev->parent;
+ struct class_interface *class_intf;
+
+ device_pm_remove(dev);
+ dpm_sysfs_remove(dev);
+ if (parent)
+ klist_del(&dev->knode_parent);
+ if (MAJOR(dev->devt)) {
+ device_remove_sys_dev_entry(dev);
+ device_remove_file(dev, &devt_attr);
+ }
+ if (dev->class) {
+ device_remove_class_symlinks(dev);
+
+ mutex_lock(&dev->class->p->class_mutex);
+ /* notify any interfaces that the device is now gone */
+ list_for_each_entry(class_intf,
+ &dev->class->p->class_interfaces, node)
+ if (class_intf->remove_dev)
+ class_intf->remove_dev(dev, class_intf);
+ /* remove the device from the class list */
+ klist_del(&dev->knode_class);
+ mutex_unlock(&dev->class->p->class_mutex);
+ }
+ device_remove_file(dev, &uevent_attr);
+ device_remove_attrs(dev);
+ bus_remove_device(dev);
+
+ /*
+ * Some platform devices are driven without driver attached
+ * and managed resources may have been acquired. Make sure
+ * all resources are released.
+ */
+ devres_release_all(dev);
+
+ /* Notify the platform of the removal, in case they
+ * need to do anything...
+ */
+ if (platform_notify_remove)
+ platform_notify_remove(dev);
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_DEL_DEVICE, dev);
+ kobject_uevent(&dev->kobj, KOBJ_REMOVE);
+ cleanup_device_parent(dev);
+ kobject_del(&dev->kobj);
+ put_device(parent);
+}
+
+/**
+ * device_unregister - unregister device from system.
+ * @dev: device going away.
+ *
+ * We do this in two parts, like we do device_register(). First,
+ * we remove it from all the subsystems with device_del(), then
+ * we decrement the reference count via put_device(). If that
+ * is the final reference count, the device will be cleaned up
+ * via device_release() above. Otherwise, the structure will
+ * stick around until the final reference to the device is dropped.
+ */
+void device_unregister(struct device *dev)
+{
+ pr_debug("device: '%s': %s\n", dev->bus_id, __func__);
+ device_del(dev);
+ put_device(dev);
+}
+
+static struct device *next_device(struct klist_iter *i)
+{
+ struct klist_node *n = klist_next(i);
+ return n ? container_of(n, struct device, knode_parent) : NULL;
+}
+
+/**
+ * device_for_each_child - device child iterator.
+ * @parent: parent struct device.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
+ *
+ * Iterate over @parent's child devices, and call @fn for each,
+ * passing it @data.
+ *
+ * We check the return of @fn each time. If it returns anything
+ * other than 0, we break out and return that value.
+ */
+int device_for_each_child(struct device *parent, void *data,
+ int (*fn)(struct device *dev, void *data))
+{
+ struct klist_iter i;
+ struct device *child;
+ int error = 0;
+
+ klist_iter_init(&parent->klist_children, &i);
+ while ((child = next_device(&i)) && !error)
+ error = fn(child, data);
+ klist_iter_exit(&i);
+ return error;
+}
+
+/**
+ * device_find_child - device iterator for locating a particular device.
+ * @parent: parent struct device
+ * @data: Data to pass to match function
+ * @match: Callback function to check device
+ *
+ * This is similar to the device_for_each_child() function above, but it
+ * returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero and a reference to the
+ * current device can be obtained, this function will return to the caller
+ * and not iterate over any more devices.
+ */
+struct device *device_find_child(struct device *parent, void *data,
+ int (*match)(struct device *dev, void *data))
+{
+ struct klist_iter i;
+ struct device *child;
+
+ if (!parent)
+ return NULL;
+
+ klist_iter_init(&parent->klist_children, &i);
+ while ((child = next_device(&i)))
+ if (match(child, data) && get_device(child))
+ break;
+ klist_iter_exit(&i);
+ return child;
+}
+
+int __init devices_init(void)
+{
+ devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
+ if (!devices_kset)
+ return -ENOMEM;
+ dev_kobj = kobject_create_and_add("dev", NULL);
+ if (!dev_kobj)
+ goto dev_kobj_err;
+ sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
+ if (!sysfs_dev_block_kobj)
+ goto block_kobj_err;
+ sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
+ if (!sysfs_dev_char_kobj)
+ goto char_kobj_err;
+
+ return 0;
+
+ char_kobj_err:
+ kobject_put(sysfs_dev_block_kobj);
+ block_kobj_err:
+ kobject_put(dev_kobj);
+ dev_kobj_err:
+ kset_unregister(devices_kset);
+ return -ENOMEM;
+}
+
+EXPORT_SYMBOL_GPL(device_for_each_child);
+EXPORT_SYMBOL_GPL(device_find_child);
+
+EXPORT_SYMBOL_GPL(device_initialize);
+EXPORT_SYMBOL_GPL(device_add);
+EXPORT_SYMBOL_GPL(device_register);
+
+EXPORT_SYMBOL_GPL(device_del);
+EXPORT_SYMBOL_GPL(device_unregister);
+EXPORT_SYMBOL_GPL(get_device);
+EXPORT_SYMBOL_GPL(put_device);
+
+EXPORT_SYMBOL_GPL(device_create_file);
+EXPORT_SYMBOL_GPL(device_remove_file);
+
+
+static void device_create_release(struct device *dev)
+{
+ pr_debug("device: '%s': %s\n", dev->bus_id, __func__);
+ kfree(dev);
+}
+
+/**
+ * device_create_vargs - creates a device and registers it with sysfs
+ * @class: pointer to the struct class that this device should be registered to
+ * @parent: pointer to the parent struct device of this new device, if any
+ * @devt: the dev_t for the char device to be added
+ * @drvdata: the data to be added to the device for callbacks
+ * @fmt: string for the device's name
+ * @args: va_list for the device's name
+ *
+ * This function can be used by char device classes. A struct device
+ * will be created in sysfs, registered to the specified class.
+ *
+ * A "dev" file will be created, showing the dev_t for the device, if
+ * the dev_t is not 0,0.
+ * If a pointer to a parent struct device is passed in, the newly created
+ * struct device will be a child of that device in sysfs.
+ * The pointer to the struct device will be returned from the call.
+ * Any further sysfs files that might be required can be created using this
+ * pointer.
+ *
+ * Note: the struct class passed to this function must have previously
+ * been created with a call to class_create().
+ */
+struct device *device_create_vargs(struct class *class, struct device *parent,
+ dev_t devt, void *drvdata, const char *fmt,
+ va_list args)
+{
+ struct device *dev = NULL;
+ int retval = -ENODEV;
+
+ if (class == NULL || IS_ERR(class))
+ goto error;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ dev->devt = devt;
+ dev->class = class;
+ dev->parent = parent;
+ dev->release = device_create_release;
+ dev_set_drvdata(dev, drvdata);
+
+ vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);
+ retval = device_register(dev);
+ if (retval)
+ goto error;
+
+ return dev;
+
+error:
+ put_device(dev);
+ return ERR_PTR(retval);
+}
+EXPORT_SYMBOL_GPL(device_create_vargs);
+
+/**
+ * device_create - creates a device and registers it with sysfs
+ * @class: pointer to the struct class that this device should be registered to
+ * @parent: pointer to the parent struct device of this new device, if any
+ * @devt: the dev_t for the char device to be added
+ * @drvdata: the data to be added to the device for callbacks
+ * @fmt: string for the device's name
+ *
+ * This function can be used by char device classes. A struct device
+ * will be created in sysfs, registered to the specified class.
+ *
+ * A "dev" file will be created, showing the dev_t for the device, if
+ * the dev_t is not 0,0.
+ * If a pointer to a parent struct device is passed in, the newly created
+ * struct device will be a child of that device in sysfs.
+ * The pointer to the struct device will be returned from the call.
+ * Any further sysfs files that might be required can be created using this
+ * pointer.
+ *
+ * Note: the struct class passed to this function must have previously
+ * been created with a call to class_create().
+ */
+struct device *device_create(struct class *class, struct device *parent,
+ dev_t devt, void *drvdata, const char *fmt, ...)
+{
+ va_list vargs;
+ struct device *dev;
+
+ va_start(vargs, fmt);
+ dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
+ va_end(vargs);
+ return dev;
+}
+EXPORT_SYMBOL_GPL(device_create);
+
+static int __match_devt(struct device *dev, void *data)
+{
+ dev_t *devt = data;
+
+ return dev->devt == *devt;
+}
+
+/**
+ * device_destroy - removes a device that was created with device_create()
+ * @class: pointer to the struct class that this device was registered with
+ * @devt: the dev_t of the device that was previously registered
+ *
+ * This call unregisters and cleans up a device that was created with a
+ * call to device_create().
+ */
+void device_destroy(struct class *class, dev_t devt)
+{
+ struct device *dev;
+
+ dev = class_find_device(class, NULL, &devt, __match_devt);
+ if (dev) {
+ put_device(dev);
+ device_unregister(dev);
+ }
+}
+EXPORT_SYMBOL_GPL(device_destroy);
+
+/**
+ * device_rename - renames a device
+ * @dev: the pointer to the struct device to be renamed
+ * @new_name: the new name of the device
+ *
+ * It is the responsibility of the caller to provide mutual
+ * exclusion between two different calls of device_rename
+ * on the same device to ensure that new_name is valid and
+ * won't conflict with other devices.
+ */
+int device_rename(struct device *dev, char *new_name)
+{
+ char *old_class_name = NULL;
+ char *new_class_name = NULL;
+ char *old_device_name = NULL;
+ int error;
+
+ dev = get_device(dev);
+ if (!dev)
+ return -EINVAL;
+
+ pr_debug("device: '%s': %s: renaming to '%s'\n", dev->bus_id,
+ __func__, new_name);
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+ if ((dev->class) && (dev->parent))
+ old_class_name = make_class_name(dev->class->name, &dev->kobj);
+#endif
+
+ old_device_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
+ if (!old_device_name) {
+ error = -ENOMEM;
+ goto out;
+ }
+ strlcpy(old_device_name, dev->bus_id, BUS_ID_SIZE);
+ strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);
+
+ error = kobject_rename(&dev->kobj, new_name);
+ if (error) {
+ strlcpy(dev->bus_id, old_device_name, BUS_ID_SIZE);
+ goto out;
+ }
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+ if (old_class_name) {
+ new_class_name = make_class_name(dev->class->name, &dev->kobj);
+ if (new_class_name) {
+ error = sysfs_create_link_nowarn(&dev->parent->kobj,
+ &dev->kobj,
+ new_class_name);
+ if (error)
+ goto out;
+ sysfs_remove_link(&dev->parent->kobj, old_class_name);
+ }
+ }
+#else
+ if (dev->class) {
+ error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
+ if (error)
+ goto out;
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ old_device_name);
+ }
+#endif
+
+out:
+ put_device(dev);
+
+ kfree(new_class_name);
+ kfree(old_class_name);
+ kfree(old_device_name);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(device_rename);
+
+static int device_move_class_links(struct device *dev,
+ struct device *old_parent,
+ struct device *new_parent)
+{
+ int error = 0;
+#ifdef CONFIG_SYSFS_DEPRECATED
+ char *class_name;
+
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ if (!class_name) {
+ error = -ENOMEM;
+ goto out;
+ }
+ if (old_parent) {
+ sysfs_remove_link(&dev->kobj, "device");
+ sysfs_remove_link(&old_parent->kobj, class_name);
+ }
+ if (new_parent) {
+ error = sysfs_create_link(&dev->kobj, &new_parent->kobj,
+ "device");
+ if (error)
+ goto out;
+ error = sysfs_create_link(&new_parent->kobj, &dev->kobj,
+ class_name);
+ if (error)
+ sysfs_remove_link(&dev->kobj, "device");
+ } else
+ error = 0;
+out:
+ kfree(class_name);
+ return error;
+#else
+ if (old_parent)
+ sysfs_remove_link(&dev->kobj, "device");
+ if (new_parent)
+ error = sysfs_create_link(&dev->kobj, &new_parent->kobj,
+ "device");
+ return error;
+#endif
+}
+
+/**
+ * device_move - moves a device to a new parent
+ * @dev: the pointer to the struct device to be moved
+ * @new_parent: the new parent of the device (can by NULL)
+ */
+int device_move(struct device *dev, struct device *new_parent)
+{
+ int error;
+ struct device *old_parent;
+ struct kobject *new_parent_kobj;
+
+ dev = get_device(dev);
+ if (!dev)
+ return -EINVAL;
+
+ new_parent = get_device(new_parent);
+ new_parent_kobj = get_device_parent(dev, new_parent);
+
+ pr_debug("device: '%s': %s: moving to '%s'\n", dev->bus_id,
+ __func__, new_parent ? new_parent->bus_id : "<NULL>");
+ error = kobject_move(&dev->kobj, new_parent_kobj);
+ if (error) {
+ cleanup_glue_dir(dev, new_parent_kobj);
+ put_device(new_parent);
+ goto out;
+ }
+ old_parent = dev->parent;
+ dev->parent = new_parent;
+ if (old_parent)
+ klist_remove(&dev->knode_parent);
+ if (new_parent) {
+ klist_add_tail(&dev->knode_parent, &new_parent->klist_children);
+ set_dev_node(dev, dev_to_node(new_parent));
+ }
+
+ if (!dev->class)
+ goto out_put;
+ error = device_move_class_links(dev, old_parent, new_parent);
+ if (error) {
+ /* We ignore errors on cleanup since we're hosed anyway... */
+ device_move_class_links(dev, new_parent, old_parent);
+ if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
+ if (new_parent)
+ klist_remove(&dev->knode_parent);
+ dev->parent = old_parent;
+ if (old_parent) {
+ klist_add_tail(&dev->knode_parent,
+ &old_parent->klist_children);
+ set_dev_node(dev, dev_to_node(old_parent));
+ }
+ }
+ cleanup_glue_dir(dev, new_parent_kobj);
+ put_device(new_parent);
+ goto out;
+ }
+out_put:
+ put_device(old_parent);
+out:
+ put_device(dev);
+ return error;
+}
+EXPORT_SYMBOL_GPL(device_move);
+
+/**
+ * device_shutdown - call ->shutdown() on each device to shutdown.
+ */
+void device_shutdown(void)
+{
+ struct device *dev, *devn;
+
+ list_for_each_entry_safe_reverse(dev, devn, &devices_kset->list,
+ kobj.entry) {
+ if (dev->bus && dev->bus->shutdown) {
+ dev_dbg(dev, "shutdown\n");
+ dev->bus->shutdown(dev);
+ } else if (dev->driver && dev->driver->shutdown) {
+ dev_dbg(dev, "shutdown\n");
+ dev->driver->shutdown(dev);
+ }
+ }
+ kobject_put(sysfs_dev_char_kobj);
+ kobject_put(sysfs_dev_block_kobj);
+ kobject_put(dev_kobj);
+}
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
new file mode 100644
index 0000000..64f5d54
--- /dev/null
+++ b/drivers/base/cpu.c
@@ -0,0 +1,206 @@
+/*
+ * drivers/base/cpu.c - basic CPU class support
+ */
+
+#include <linux/sysdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/cpu.h>
+#include <linux/topology.h>
+#include <linux/device.h>
+#include <linux/node.h>
+
+#include "base.h"
+
+struct sysdev_class cpu_sysdev_class = {
+ .name = "cpu",
+};
+EXPORT_SYMBOL(cpu_sysdev_class);
+
+static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices);
+
+#ifdef CONFIG_HOTPLUG_CPU
+static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+
+ return sprintf(buf, "%u\n", !!cpu_online(cpu->sysdev.id));
+}
+
+static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+ ssize_t ret;
+
+ switch (buf[0]) {
+ case '0':
+ ret = cpu_down(cpu->sysdev.id);
+ if (!ret)
+ kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
+ break;
+ case '1':
+ ret = cpu_up(cpu->sysdev.id);
+ if (!ret)
+ kobject_uevent(&dev->kobj, KOBJ_ONLINE);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret >= 0)
+ ret = count;
+ return ret;
+}
+static SYSDEV_ATTR(online, 0644, show_online, store_online);
+
+static void __cpuinit register_cpu_control(struct cpu *cpu)
+{
+ sysdev_create_file(&cpu->sysdev, &attr_online);
+}
+void unregister_cpu(struct cpu *cpu)
+{
+ int logical_cpu = cpu->sysdev.id;
+
+ unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
+
+ sysdev_remove_file(&cpu->sysdev, &attr_online);
+
+ sysdev_unregister(&cpu->sysdev);
+ per_cpu(cpu_sys_devices, logical_cpu) = NULL;
+ return;
+}
+#else /* ... !CONFIG_HOTPLUG_CPU */
+static inline void register_cpu_control(struct cpu *cpu)
+{
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+#ifdef CONFIG_KEXEC
+#include <linux/kexec.h>
+
+static ssize_t show_crash_notes(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct cpu *cpu = container_of(dev, struct cpu, sysdev);
+ ssize_t rc;
+ unsigned long long addr;
+ int cpunum;
+
+ cpunum = cpu->sysdev.id;
+
+ /*
+ * Might be reading other cpu's data based on which cpu read thread
+ * has been scheduled. But cpu data (memory) is allocated once during
+ * boot up and this data does not change there after. Hence this
+ * operation should be safe. No locking required.
+ */
+ addr = __pa(per_cpu_ptr(crash_notes, cpunum));
+ rc = sprintf(buf, "%Lx\n", addr);
+ return rc;
+}
+static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
+#endif
+
+/*
+ * Print cpu online, possible, present, and system maps
+ */
+static ssize_t print_cpus_map(char *buf, cpumask_t *map)
+{
+ int n = cpulist_scnprintf(buf, PAGE_SIZE-2, *map);
+
+ buf[n++] = '\n';
+ buf[n] = '\0';
+ return n;
+}
+
+#define print_cpus_func(type) \
+static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \
+{ \
+ return print_cpus_map(buf, &cpu_##type##_map); \
+} \
+static struct sysdev_class_attribute attr_##type##_map = \
+ _SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)
+
+print_cpus_func(online);
+print_cpus_func(possible);
+print_cpus_func(present);
+
+static struct sysdev_class_attribute *cpu_state_attr[] = {
+ &attr_online_map,
+ &attr_possible_map,
+ &attr_present_map,
+};
+
+static int cpu_states_init(void)
+{
+ int i;
+ int err = 0;
+
+ for (i = 0; i < ARRAY_SIZE(cpu_state_attr); i++) {
+ int ret;
+ ret = sysdev_class_create_file(&cpu_sysdev_class,
+ cpu_state_attr[i]);
+ if (!err)
+ err = ret;
+ }
+ return err;
+}
+
+/*
+ * register_cpu - Setup a sysfs device for a CPU.
+ * @cpu - cpu->hotpluggable field set to 1 will generate a control file in
+ * sysfs for this CPU.
+ * @num - CPU number to use when creating the device.
+ *
+ * Initialize and register the CPU device.
+ */
+int __cpuinit register_cpu(struct cpu *cpu, int num)
+{
+ int error;
+ cpu->node_id = cpu_to_node(num);
+ cpu->sysdev.id = num;
+ cpu->sysdev.cls = &cpu_sysdev_class;
+
+ error = sysdev_register(&cpu->sysdev);
+
+ if (!error && cpu->hotpluggable)
+ register_cpu_control(cpu);
+ if (!error)
+ per_cpu(cpu_sys_devices, num) = &cpu->sysdev;
+ if (!error)
+ register_cpu_under_node(num, cpu_to_node(num));
+
+#ifdef CONFIG_KEXEC
+ if (!error)
+ error = sysdev_create_file(&cpu->sysdev, &attr_crash_notes);
+#endif
+ return error;
+}
+
+struct sys_device *get_cpu_sysdev(unsigned cpu)
+{
+ if (cpu < nr_cpu_ids && cpu_possible(cpu))
+ return per_cpu(cpu_sys_devices, cpu);
+ else
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(get_cpu_sysdev);
+
+int __init cpu_dev_init(void)
+{
+ int err;
+
+ err = sysdev_class_register(&cpu_sysdev_class);
+ if (!err)
+ err = cpu_states_init();
+
+#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
+ if (!err)
+ err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
+#endif
+
+ return err;
+}
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
new file mode 100644
index 0000000..20febc0
--- /dev/null
+++ b/drivers/base/dd.c
@@ -0,0 +1,367 @@
+/*
+ * drivers/base/dd.c - The core device/driver interactions.
+ *
+ * This file contains the (sometimes tricky) code that controls the
+ * interactions between devices and drivers, which primarily includes
+ * driver binding and unbinding.
+ *
+ * All of this code used to exist in drivers/base/bus.c, but was
+ * relocated to here in the name of compartmentalization (since it wasn't
+ * strictly code just for the 'struct bus_type'.
+ *
+ * Copyright (c) 2002-5 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+
+#include "base.h"
+#include "power/power.h"
+
+
+static void driver_bound(struct device *dev)
+{
+ if (klist_node_attached(&dev->knode_driver)) {
+ printk(KERN_WARNING "%s: device %s already bound\n",
+ __func__, kobject_name(&dev->kobj));
+ return;
+ }
+
+ pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->bus_id,
+ __func__, dev->driver->name);
+
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_BOUND_DRIVER, dev);
+
+ klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices);
+}
+
+static int driver_sysfs_add(struct device *dev)
+{
+ int ret;
+
+ ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
+ kobject_name(&dev->kobj));
+ if (ret == 0) {
+ ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
+ "driver");
+ if (ret)
+ sysfs_remove_link(&dev->driver->p->kobj,
+ kobject_name(&dev->kobj));
+ }
+ return ret;
+}
+
+static void driver_sysfs_remove(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv) {
+ sysfs_remove_link(&drv->p->kobj, kobject_name(&dev->kobj));
+ sysfs_remove_link(&dev->kobj, "driver");
+ }
+}
+
+/**
+ * device_bind_driver - bind a driver to one device.
+ * @dev: device.
+ *
+ * Allow manual attachment of a driver to a device.
+ * Caller must have already set @dev->driver.
+ *
+ * Note that this does not modify the bus reference count
+ * nor take the bus's rwsem. Please verify those are accounted
+ * for before calling this. (It is ok to call with no other effort
+ * from a driver's probe() method.)
+ *
+ * This function must be called with @dev->sem held.
+ */
+int device_bind_driver(struct device *dev)
+{
+ int ret;
+
+ ret = driver_sysfs_add(dev);
+ if (!ret)
+ driver_bound(dev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(device_bind_driver);
+
+static atomic_t probe_count = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
+
+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->bus_id);
+ WARN_ON(!list_empty(&dev->devres_head));
+
+ dev->driver = drv;
+ if (driver_sysfs_add(dev)) {
+ printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
+ __func__, dev->bus_id);
+ goto probe_failed;
+ }
+
+ if (dev->bus->probe) {
+ ret = dev->bus->probe(dev);
+ if (ret)
+ goto probe_failed;
+ } else if (drv->probe) {
+ ret = drv->probe(dev);
+ if (ret)
+ goto probe_failed;
+ }
+
+ driver_bound(dev);
+ ret = 1;
+ pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
+ drv->bus->name, __func__, dev->bus_id, 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->bus_id, 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;
+}
+
+/**
+ * driver_probe_done
+ * Determine if the probe sequence is finished or not.
+ *
+ * Should somehow figure out how to use a semaphore, not an atomic variable...
+ */
+int driver_probe_done(void)
+{
+ pr_debug("%s: probe_count = %d\n", __func__,
+ atomic_read(&probe_count));
+ if (atomic_read(&probe_count))
+ return -EBUSY;
+ return 0;
+}
+
+/**
+ * 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.
+ *
+ * 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;
+
+ 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;
+}
+
+static int __device_attach(struct device_driver *drv, void *data)
+{
+ struct device *dev = data;
+ return driver_probe_device(drv, 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.
+ *
+ * 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;
+}
+EXPORT_SYMBOL_GPL(device_attach);
+
+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.
+ */
+
+ if (drv->bus->match && !drv->bus->match(dev, drv))
+ return 0;
+
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ down(&dev->sem);
+ if (!dev->driver)
+ driver_probe_device(drv, dev);
+ up(&dev->sem);
+ if (dev->parent)
+ up(&dev->parent->sem);
+
+ return 0;
+}
+
+/**
+ * driver_attach - try to bind driver to devices.
+ * @drv: driver.
+ *
+ * Walk the list of devices that the bus has on it and try to
+ * match the driver with each one. If driver_probe_device()
+ * returns 0 and the @dev->driver is set, we've found a
+ * compatible pair.
+ */
+int driver_attach(struct device_driver *drv)
+{
+ return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
+}
+EXPORT_SYMBOL_GPL(driver_attach);
+
+/*
+ * __device_release_driver() must be called with @dev->sem held.
+ * When called for a USB interface, @dev->parent->sem must be held as well.
+ */
+static void __device_release_driver(struct device *dev)
+{
+ struct device_driver *drv;
+
+ drv = dev->driver;
+ if (drv) {
+ driver_sysfs_remove(dev);
+ sysfs_remove_link(&dev->kobj, "driver");
+
+ if (dev->bus)
+ blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
+ BUS_NOTIFY_UNBIND_DRIVER,
+ dev);
+
+ if (dev->bus && dev->bus->remove)
+ dev->bus->remove(dev);
+ else if (drv->remove)
+ drv->remove(dev);
+ devres_release_all(dev);
+ dev->driver = NULL;
+ klist_remove(&dev->knode_driver);
+ }
+}
+
+/**
+ * device_release_driver - manually detach device from driver.
+ * @dev: device.
+ *
+ * Manually detach device from driver.
+ * When called for a USB interface, @dev->parent->sem must be held.
+ */
+void device_release_driver(struct device *dev)
+{
+ /*
+ * If anyone calls device_release_driver() recursively from
+ * within their ->remove callback for the same device, they
+ * will deadlock right here.
+ */
+ down(&dev->sem);
+ __device_release_driver(dev);
+ up(&dev->sem);
+}
+EXPORT_SYMBOL_GPL(device_release_driver);
+
+/**
+ * driver_detach - detach driver from all devices it controls.
+ * @drv: driver.
+ */
+void driver_detach(struct device_driver *drv)
+{
+ struct device *dev;
+
+ for (;;) {
+ spin_lock(&drv->p->klist_devices.k_lock);
+ if (list_empty(&drv->p->klist_devices.k_list)) {
+ spin_unlock(&drv->p->klist_devices.k_lock);
+ break;
+ }
+ dev = list_entry(drv->p->klist_devices.k_list.prev,
+ struct device, knode_driver.n_node);
+ get_device(dev);
+ spin_unlock(&drv->p->klist_devices.k_lock);
+
+ if (dev->parent) /* Needed for USB */
+ down(&dev->parent->sem);
+ down(&dev->sem);
+ if (dev->driver == drv)
+ __device_release_driver(dev);
+ up(&dev->sem);
+ if (dev->parent)
+ up(&dev->parent->sem);
+ put_device(dev);
+ }
+}
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
new file mode 100644
index 0000000..e8beb8e
--- /dev/null
+++ b/drivers/base/devres.c
@@ -0,0 +1,646 @@
+/*
+ * drivers/base/devres.c - device resource management
+ *
+ * Copyright (c) 2006 SUSE Linux Products GmbH
+ * Copyright (c) 2006 Tejun Heo <teheo@suse.de>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+
+#include "base.h"
+
+struct devres_node {
+ struct list_head entry;
+ dr_release_t release;
+#ifdef CONFIG_DEBUG_DEVRES
+ const char *name;
+ size_t size;
+#endif
+};
+
+struct devres {
+ struct devres_node node;
+ /* -- 3 pointers */
+ unsigned long long data[]; /* guarantee ull alignment */
+};
+
+struct devres_group {
+ struct devres_node node[2];
+ void *id;
+ int color;
+ /* -- 8 pointers */
+};
+
+#ifdef CONFIG_DEBUG_DEVRES
+static int log_devres = 0;
+module_param_named(log, log_devres, int, S_IRUGO | S_IWUSR);
+
+static void set_node_dbginfo(struct devres_node *node, const char *name,
+ size_t size)
+{
+ node->name = name;
+ node->size = size;
+}
+
+static void devres_log(struct device *dev, struct devres_node *node,
+ const char *op)
+{
+ if (unlikely(log_devres))
+ dev_printk(KERN_ERR, dev, "DEVRES %3s %p %s (%lu bytes)\n",
+ op, node, node->name, (unsigned long)node->size);
+}
+#else /* CONFIG_DEBUG_DEVRES */
+#define set_node_dbginfo(node, n, s) do {} while (0)
+#define devres_log(dev, node, op) do {} while (0)
+#endif /* CONFIG_DEBUG_DEVRES */
+
+/*
+ * Release functions for devres group. These callbacks are used only
+ * for identification.
+ */
+static void group_open_release(struct device *dev, void *res)
+{
+ /* noop */
+}
+
+static void group_close_release(struct device *dev, void *res)
+{
+ /* noop */
+}
+
+static struct devres_group * node_to_group(struct devres_node *node)
+{
+ if (node->release == &group_open_release)
+ return container_of(node, struct devres_group, node[0]);
+ if (node->release == &group_close_release)
+ return container_of(node, struct devres_group, node[1]);
+ return NULL;
+}
+
+static __always_inline struct devres * alloc_dr(dr_release_t release,
+ size_t size, gfp_t gfp)
+{
+ size_t tot_size = sizeof(struct devres) + size;
+ struct devres *dr;
+
+ dr = kmalloc_track_caller(tot_size, gfp);
+ if (unlikely(!dr))
+ return NULL;
+
+ memset(dr, 0, tot_size);
+ INIT_LIST_HEAD(&dr->node.entry);
+ dr->node.release = release;
+ return dr;
+}
+
+static void add_dr(struct device *dev, struct devres_node *node)
+{
+ devres_log(dev, node, "ADD");
+ BUG_ON(!list_empty(&node->entry));
+ list_add_tail(&node->entry, &dev->devres_head);
+}
+
+#ifdef CONFIG_DEBUG_DEVRES
+void * __devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
+ const char *name)
+{
+ struct devres *dr;
+
+ dr = alloc_dr(release, size, gfp);
+ if (unlikely(!dr))
+ return NULL;
+ set_node_dbginfo(&dr->node, name, size);
+ return dr->data;
+}
+EXPORT_SYMBOL_GPL(__devres_alloc);
+#else
+/**
+ * devres_alloc - Allocate device resource data
+ * @release: Release function devres will be associated with
+ * @size: Allocation size
+ * @gfp: Allocation flags
+ *
+ * Allocate devres of @size bytes. The allocated area is zeroed, then
+ * associated with @release. The returned pointer can be passed to
+ * other devres_*() functions.
+ *
+ * RETURNS:
+ * Pointer to allocated devres on success, NULL on failure.
+ */
+void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
+{
+ struct devres *dr;
+
+ dr = alloc_dr(release, size, gfp);
+ if (unlikely(!dr))
+ return NULL;
+ return dr->data;
+}
+EXPORT_SYMBOL_GPL(devres_alloc);
+#endif
+
+/**
+ * devres_free - Free device resource data
+ * @res: Pointer to devres data to free
+ *
+ * Free devres created with devres_alloc().
+ */
+void devres_free(void *res)
+{
+ if (res) {
+ struct devres *dr = container_of(res, struct devres, data);
+
+ BUG_ON(!list_empty(&dr->node.entry));
+ kfree(dr);
+ }
+}
+EXPORT_SYMBOL_GPL(devres_free);
+
+/**
+ * devres_add - Register device resource
+ * @dev: Device to add resource to
+ * @res: Resource to register
+ *
+ * Register devres @res to @dev. @res should have been allocated
+ * using devres_alloc(). On driver detach, the associated release
+ * function will be invoked and devres will be freed automatically.
+ */
+void devres_add(struct device *dev, void *res)
+{
+ struct devres *dr = container_of(res, struct devres, data);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ add_dr(dev, &dr->node);
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+}
+EXPORT_SYMBOL_GPL(devres_add);
+
+static struct devres *find_dr(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ struct devres_node *node;
+
+ list_for_each_entry_reverse(node, &dev->devres_head, entry) {
+ struct devres *dr = container_of(node, struct devres, node);
+
+ if (node->release != release)
+ continue;
+ if (match && !match(dev, dr->data, match_data))
+ continue;
+ return dr;
+ }
+
+ return NULL;
+}
+
+/**
+ * devres_find - Find device resource
+ * @dev: Device to lookup resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev which is associated with @release
+ * and for which @match returns 1. If @match is NULL, it's considered
+ * to match all.
+ *
+ * RETURNS:
+ * Pointer to found devres, NULL if not found.
+ */
+void * devres_find(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ struct devres *dr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ dr = find_dr(dev, release, match, match_data);
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ if (dr)
+ return dr->data;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(devres_find);
+
+/**
+ * devres_get - Find devres, if non-existent, add one atomically
+ * @dev: Device to lookup or add devres for
+ * @new_res: Pointer to new initialized devres to add if not found
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev which has the same release function
+ * as @new_res and for which @match return 1. If found, @new_res is
+ * freed; otherwise, @new_res is added atomically.
+ *
+ * RETURNS:
+ * Pointer to found or added devres.
+ */
+void * devres_get(struct device *dev, void *new_res,
+ dr_match_t match, void *match_data)
+{
+ struct devres *new_dr = container_of(new_res, struct devres, data);
+ struct devres *dr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ dr = find_dr(dev, new_dr->node.release, match, match_data);
+ if (!dr) {
+ add_dr(dev, &new_dr->node);
+ dr = new_dr;
+ new_dr = NULL;
+ }
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+ devres_free(new_dr);
+
+ return dr->data;
+}
+EXPORT_SYMBOL_GPL(devres_get);
+
+/**
+ * devres_remove - Find a device resource and remove it
+ * @dev: Device to find resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev associated with @release and for
+ * which @match returns 1. If @match is NULL, it's considered to
+ * match all. If found, the resource is removed atomically and
+ * returned.
+ *
+ * RETURNS:
+ * Pointer to removed devres on success, NULL if not found.
+ */
+void * devres_remove(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ struct devres *dr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ dr = find_dr(dev, release, match, match_data);
+ if (dr) {
+ list_del_init(&dr->node.entry);
+ devres_log(dev, &dr->node, "REM");
+ }
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ if (dr)
+ return dr->data;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(devres_remove);
+
+/**
+ * devres_destroy - Find a device resource and destroy it
+ * @dev: Device to find resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev associated with @release and for
+ * which @match returns 1. If @match is NULL, it's considered to
+ * match all. If found, the resource is removed atomically and freed.
+ *
+ * RETURNS:
+ * 0 if devres is found and freed, -ENOENT if not found.
+ */
+int devres_destroy(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data)
+{
+ void *res;
+
+ res = devres_remove(dev, release, match, match_data);
+ if (unlikely(!res))
+ return -ENOENT;
+
+ devres_free(res);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devres_destroy);
+
+static int remove_nodes(struct device *dev,
+ struct list_head *first, struct list_head *end,
+ struct list_head *todo)
+{
+ int cnt = 0, nr_groups = 0;
+ struct list_head *cur;
+
+ /* First pass - move normal devres entries to @todo and clear
+ * devres_group colors.
+ */
+ cur = first;
+ while (cur != end) {
+ struct devres_node *node;
+ struct devres_group *grp;
+
+ node = list_entry(cur, struct devres_node, entry);
+ cur = cur->next;
+
+ grp = node_to_group(node);
+ if (grp) {
+ /* clear color of group markers in the first pass */
+ grp->color = 0;
+ nr_groups++;
+ } else {
+ /* regular devres entry */
+ if (&node->entry == first)
+ first = first->next;
+ list_move_tail(&node->entry, todo);
+ cnt++;
+ }
+ }
+
+ if (!nr_groups)
+ return cnt;
+
+ /* Second pass - Scan groups and color them. A group gets
+ * color value of two iff the group is wholly contained in
+ * [cur, end). That is, for a closed group, both opening and
+ * closing markers should be in the range, while just the
+ * opening marker is enough for an open group.
+ */
+ cur = first;
+ while (cur != end) {
+ struct devres_node *node;
+ struct devres_group *grp;
+
+ node = list_entry(cur, struct devres_node, entry);
+ cur = cur->next;
+
+ grp = node_to_group(node);
+ BUG_ON(!grp || list_empty(&grp->node[0].entry));
+
+ grp->color++;
+ if (list_empty(&grp->node[1].entry))
+ grp->color++;
+
+ BUG_ON(grp->color <= 0 || grp->color > 2);
+ if (grp->color == 2) {
+ /* No need to update cur or end. The removed
+ * nodes are always before both.
+ */
+ list_move_tail(&grp->node[0].entry, todo);
+ list_del_init(&grp->node[1].entry);
+ }
+ }
+
+ return cnt;
+}
+
+static int release_nodes(struct device *dev, struct list_head *first,
+ struct list_head *end, unsigned long flags)
+{
+ LIST_HEAD(todo);
+ int cnt;
+ struct devres *dr, *tmp;
+
+ cnt = remove_nodes(dev, first, end, &todo);
+
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ /* Release. Note that both devres and devres_group are
+ * handled as devres in the following loop. This is safe.
+ */
+ list_for_each_entry_safe_reverse(dr, tmp, &todo, node.entry) {
+ devres_log(dev, &dr->node, "REL");
+ dr->node.release(dev, dr->data);
+ kfree(dr);
+ }
+
+ return cnt;
+}
+
+/**
+ * devres_release_all - Release all managed resources
+ * @dev: Device to release resources for
+ *
+ * Release all resources associated with @dev. This function is
+ * called on driver detach.
+ */
+int devres_release_all(struct device *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ return release_nodes(dev, dev->devres_head.next, &dev->devres_head,
+ flags);
+}
+
+/**
+ * devres_open_group - Open a new devres group
+ * @dev: Device to open devres group for
+ * @id: Separator ID
+ * @gfp: Allocation flags
+ *
+ * Open a new devres group for @dev with @id. For @id, using a
+ * pointer to an object which won't be used for another group is
+ * recommended. If @id is NULL, address-wise unique ID is created.
+ *
+ * RETURNS:
+ * ID of the new group, NULL on failure.
+ */
+void * devres_open_group(struct device *dev, void *id, gfp_t gfp)
+{
+ struct devres_group *grp;
+ unsigned long flags;
+
+ grp = kmalloc(sizeof(*grp), gfp);
+ if (unlikely(!grp))
+ return NULL;
+
+ grp->node[0].release = &group_open_release;
+ grp->node[1].release = &group_close_release;
+ INIT_LIST_HEAD(&grp->node[0].entry);
+ INIT_LIST_HEAD(&grp->node[1].entry);
+ set_node_dbginfo(&grp->node[0], "grp<", 0);
+ set_node_dbginfo(&grp->node[1], "grp>", 0);
+ grp->id = grp;
+ if (id)
+ grp->id = id;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ add_dr(dev, &grp->node[0]);
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+ return grp->id;
+}
+EXPORT_SYMBOL_GPL(devres_open_group);
+
+/* Find devres group with ID @id. If @id is NULL, look for the latest. */
+static struct devres_group * find_group(struct device *dev, void *id)
+{
+ struct devres_node *node;
+
+ list_for_each_entry_reverse(node, &dev->devres_head, entry) {
+ struct devres_group *grp;
+
+ if (node->release != &group_open_release)
+ continue;
+
+ grp = container_of(node, struct devres_group, node[0]);
+
+ if (id) {
+ if (grp->id == id)
+ return grp;
+ } else if (list_empty(&grp->node[1].entry))
+ return grp;
+ }
+
+ return NULL;
+}
+
+/**
+ * devres_close_group - Close a devres group
+ * @dev: Device to close devres group for
+ * @id: ID of target group, can be NULL
+ *
+ * Close the group identified by @id. If @id is NULL, the latest open
+ * group is selected.
+ */
+void devres_close_group(struct device *dev, void *id)
+{
+ struct devres_group *grp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+
+ grp = find_group(dev, id);
+ if (grp)
+ add_dr(dev, &grp->node[1]);
+ else
+ WARN_ON(1);
+
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+}
+EXPORT_SYMBOL_GPL(devres_close_group);
+
+/**
+ * devres_remove_group - Remove a devres group
+ * @dev: Device to remove group for
+ * @id: ID of target group, can be NULL
+ *
+ * Remove the group identified by @id. If @id is NULL, the latest
+ * open group is selected. Note that removing a group doesn't affect
+ * any other resources.
+ */
+void devres_remove_group(struct device *dev, void *id)
+{
+ struct devres_group *grp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+
+ grp = find_group(dev, id);
+ if (grp) {
+ list_del_init(&grp->node[0].entry);
+ list_del_init(&grp->node[1].entry);
+ devres_log(dev, &grp->node[0], "REM");
+ } else
+ WARN_ON(1);
+
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+
+ kfree(grp);
+}
+EXPORT_SYMBOL_GPL(devres_remove_group);
+
+/**
+ * devres_release_group - Release resources in a devres group
+ * @dev: Device to release group for
+ * @id: ID of target group, can be NULL
+ *
+ * Release all resources in the group identified by @id. If @id is
+ * NULL, the latest open group is selected. The selected group and
+ * groups properly nested inside the selected group are removed.
+ *
+ * RETURNS:
+ * The number of released non-group resources.
+ */
+int devres_release_group(struct device *dev, void *id)
+{
+ struct devres_group *grp;
+ unsigned long flags;
+ int cnt = 0;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+
+ grp = find_group(dev, id);
+ if (grp) {
+ struct list_head *first = &grp->node[0].entry;
+ struct list_head *end = &dev->devres_head;
+
+ if (!list_empty(&grp->node[1].entry))
+ end = grp->node[1].entry.next;
+
+ cnt = release_nodes(dev, first, end, flags);
+ } else {
+ WARN_ON(1);
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+ }
+
+ return cnt;
+}
+EXPORT_SYMBOL_GPL(devres_release_group);
+
+/*
+ * Managed kzalloc/kfree
+ */
+static void devm_kzalloc_release(struct device *dev, void *res)
+{
+ /* noop */
+}
+
+static int devm_kzalloc_match(struct device *dev, void *res, void *data)
+{
+ return res == data;
+}
+
+/**
+ * devm_kzalloc - Resource-managed kzalloc
+ * @dev: Device to allocate memory for
+ * @size: Allocation size
+ * @gfp: Allocation gfp flags
+ *
+ * Managed kzalloc. Memory allocated with this function is
+ * automatically freed on driver detach. Like all other devres
+ * resources, guaranteed alignment is unsigned long long.
+ *
+ * RETURNS:
+ * Pointer to allocated memory on success, NULL on failure.
+ */
+void * devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
+{
+ struct devres *dr;
+
+ /* use raw alloc_dr for kmalloc caller tracing */
+ dr = alloc_dr(devm_kzalloc_release, size, gfp);
+ if (unlikely(!dr))
+ return NULL;
+
+ set_node_dbginfo(&dr->node, "devm_kzalloc_release", size);
+ devres_add(dev, dr->data);
+ return dr->data;
+}
+EXPORT_SYMBOL_GPL(devm_kzalloc);
+
+/**
+ * devm_kfree - Resource-managed kfree
+ * @dev: Device this memory belongs to
+ * @p: Memory to free
+ *
+ * Free memory allocated with dev_kzalloc().
+ */
+void devm_kfree(struct device *dev, void *p)
+{
+ int rc;
+
+ rc = devres_destroy(dev, devm_kzalloc_release, devm_kzalloc_match, p);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_kfree);
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
new file mode 100644
index 0000000..ca9186f
--- /dev/null
+++ b/drivers/base/dma-mapping.c
@@ -0,0 +1,218 @@
+/*
+ * drivers/base/dma-mapping.c - arch-independent dma-mapping routines
+ *
+ * Copyright (c) 2006 SUSE Linux Products GmbH
+ * Copyright (c) 2006 Tejun Heo <teheo@suse.de>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/dma-mapping.h>
+
+/*
+ * Managed DMA API
+ */
+struct dma_devres {
+ size_t size;
+ void *vaddr;
+ dma_addr_t dma_handle;
+};
+
+static void dmam_coherent_release(struct device *dev, void *res)
+{
+ struct dma_devres *this = res;
+
+ dma_free_coherent(dev, this->size, this->vaddr, this->dma_handle);
+}
+
+static void dmam_noncoherent_release(struct device *dev, void *res)
+{
+ struct dma_devres *this = res;
+
+ dma_free_noncoherent(dev, this->size, this->vaddr, this->dma_handle);
+}
+
+static int dmam_match(struct device *dev, void *res, void *match_data)
+{
+ struct dma_devres *this = res, *match = match_data;
+
+ if (this->vaddr == match->vaddr) {
+ WARN_ON(this->size != match->size ||
+ this->dma_handle != match->dma_handle);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * dmam_alloc_coherent - Managed dma_alloc_coherent()
+ * @dev: Device to allocate coherent memory for
+ * @size: Size of allocation
+ * @dma_handle: Out argument for allocated DMA handle
+ * @gfp: Allocation flags
+ *
+ * Managed dma_alloc_coherent(). Memory allocated using this function
+ * will be automatically released on driver detach.
+ *
+ * RETURNS:
+ * Pointer to allocated memory on success, NULL on failure.
+ */
+void * dmam_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp)
+{
+ struct dma_devres *dr;
+ void *vaddr;
+
+ dr = devres_alloc(dmam_coherent_release, sizeof(*dr), gfp);
+ if (!dr)
+ return NULL;
+
+ vaddr = dma_alloc_coherent(dev, size, dma_handle, gfp);
+ if (!vaddr) {
+ devres_free(dr);
+ return NULL;
+ }
+
+ dr->vaddr = vaddr;
+ dr->dma_handle = *dma_handle;
+ dr->size = size;
+
+ devres_add(dev, dr);
+
+ return vaddr;
+}
+EXPORT_SYMBOL(dmam_alloc_coherent);
+
+/**
+ * dmam_free_coherent - Managed dma_free_coherent()
+ * @dev: Device to free coherent memory for
+ * @size: Size of allocation
+ * @vaddr: Virtual address of the memory to free
+ * @dma_handle: DMA handle of the memory to free
+ *
+ * Managed dma_free_coherent().
+ */
+void dmam_free_coherent(struct device *dev, size_t size, void *vaddr,
+ dma_addr_t dma_handle)
+{
+ struct dma_devres match_data = { size, vaddr, dma_handle };
+
+ dma_free_coherent(dev, size, vaddr, dma_handle);
+ WARN_ON(devres_destroy(dev, dmam_coherent_release, dmam_match,
+ &match_data));
+}
+EXPORT_SYMBOL(dmam_free_coherent);
+
+/**
+ * dmam_alloc_non_coherent - Managed dma_alloc_non_coherent()
+ * @dev: Device to allocate non_coherent memory for
+ * @size: Size of allocation
+ * @dma_handle: Out argument for allocated DMA handle
+ * @gfp: Allocation flags
+ *
+ * Managed dma_alloc_non_coherent(). Memory allocated using this
+ * function will be automatically released on driver detach.
+ *
+ * RETURNS:
+ * Pointer to allocated memory on success, NULL on failure.
+ */
+void *dmam_alloc_noncoherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp)
+{
+ struct dma_devres *dr;
+ void *vaddr;
+
+ dr = devres_alloc(dmam_noncoherent_release, sizeof(*dr), gfp);
+ if (!dr)
+ return NULL;
+
+ vaddr = dma_alloc_noncoherent(dev, size, dma_handle, gfp);
+ if (!vaddr) {
+ devres_free(dr);
+ return NULL;
+ }
+
+ dr->vaddr = vaddr;
+ dr->dma_handle = *dma_handle;
+ dr->size = size;
+
+ devres_add(dev, dr);
+
+ return vaddr;
+}
+EXPORT_SYMBOL(dmam_alloc_noncoherent);
+
+/**
+ * dmam_free_coherent - Managed dma_free_noncoherent()
+ * @dev: Device to free noncoherent memory for
+ * @size: Size of allocation
+ * @vaddr: Virtual address of the memory to free
+ * @dma_handle: DMA handle of the memory to free
+ *
+ * Managed dma_free_noncoherent().
+ */
+void dmam_free_noncoherent(struct device *dev, size_t size, void *vaddr,
+ dma_addr_t dma_handle)
+{
+ struct dma_devres match_data = { size, vaddr, dma_handle };
+
+ dma_free_noncoherent(dev, size, vaddr, dma_handle);
+ WARN_ON(!devres_destroy(dev, dmam_noncoherent_release, dmam_match,
+ &match_data));
+}
+EXPORT_SYMBOL(dmam_free_noncoherent);
+
+#ifdef ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY
+
+static void dmam_coherent_decl_release(struct device *dev, void *res)
+{
+ dma_release_declared_memory(dev);
+}
+
+/**
+ * dmam_declare_coherent_memory - Managed dma_declare_coherent_memory()
+ * @dev: Device to declare coherent memory for
+ * @bus_addr: Bus address of coherent memory to be declared
+ * @device_addr: Device address of coherent memory to be declared
+ * @size: Size of coherent memory to be declared
+ * @flags: Flags
+ *
+ * Managed dma_declare_coherent_memory().
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int dmam_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
+ dma_addr_t device_addr, size_t size, int flags)
+{
+ void *res;
+ int rc;
+
+ res = devres_alloc(dmam_coherent_decl_release, 0, GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ rc = dma_declare_coherent_memory(dev, bus_addr, device_addr, size,
+ flags);
+ if (rc == 0)
+ devres_add(dev, res);
+ else
+ devres_free(res);
+
+ return rc;
+}
+EXPORT_SYMBOL(dmam_declare_coherent_memory);
+
+/**
+ * dmam_release_declared_memory - Managed dma_release_declared_memory().
+ * @dev: Device to release declared coherent memory for
+ *
+ * Managed dmam_release_declared_memory().
+ */
+void dmam_release_declared_memory(struct device *dev)
+{
+ WARN_ON(devres_destroy(dev, dmam_coherent_decl_release, NULL, NULL));
+}
+EXPORT_SYMBOL(dmam_release_declared_memory);
+
+#endif
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
new file mode 100644
index 0000000..1e2bda7
--- /dev/null
+++ b/drivers/base/driver.c
@@ -0,0 +1,277 @@
+/*
+ * driver.c - centralized device driver management
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include "base.h"
+
+static struct device *next_device(struct klist_iter *i)
+{
+ struct klist_node *n = klist_next(i);
+ return n ? container_of(n, struct device, knode_driver) : NULL;
+}
+
+/**
+ * driver_for_each_device - Iterator for devices bound to a driver.
+ * @drv: Driver we're iterating.
+ * @start: Device to begin with
+ * @data: Data to pass to the callback.
+ * @fn: Function to call for each device.
+ *
+ * Iterate over the @drv's list of devices calling @fn for each one.
+ */
+int driver_for_each_device(struct device_driver *drv, struct device *start,
+ void *data, int (*fn)(struct device *, void *))
+{
+ struct klist_iter i;
+ struct device *dev;
+ int error = 0;
+
+ if (!drv)
+ return -EINVAL;
+
+ klist_iter_init_node(&drv->p->klist_devices, &i,
+ start ? &start->knode_driver : NULL);
+ while ((dev = next_device(&i)) && !error)
+ error = fn(dev, data);
+ klist_iter_exit(&i);
+ return error;
+}
+EXPORT_SYMBOL_GPL(driver_for_each_device);
+
+/**
+ * driver_find_device - device iterator for locating a particular device.
+ * @drv: The device's driver
+ * @start: Device to begin with
+ * @data: Data to pass to match function
+ * @match: Callback function to check device
+ *
+ * This is similar to the driver_for_each_device() function above, but
+ * it returns a reference to a device that is 'found' for later use, as
+ * determined by the @match callback.
+ *
+ * The callback should return 0 if the device doesn't match and non-zero
+ * if it does. If the callback returns non-zero, this function will
+ * return to the caller and not iterate over any more devices.
+ */
+struct device *driver_find_device(struct device_driver *drv,
+ struct device *start, void *data,
+ int (*match)(struct device *dev, void *data))
+{
+ struct klist_iter i;
+ struct device *dev;
+
+ if (!drv)
+ return NULL;
+
+ klist_iter_init_node(&drv->p->klist_devices, &i,
+ (start ? &start->knode_driver : NULL));
+ while ((dev = next_device(&i)))
+ if (match(dev, data) && get_device(dev))
+ break;
+ klist_iter_exit(&i);
+ return dev;
+}
+EXPORT_SYMBOL_GPL(driver_find_device);
+
+/**
+ * driver_create_file - create sysfs file for driver.
+ * @drv: driver.
+ * @attr: driver attribute descriptor.
+ */
+int driver_create_file(struct device_driver *drv,
+ struct driver_attribute *attr)
+{
+ int error;
+ if (drv)
+ error = sysfs_create_file(&drv->p->kobj, &attr->attr);
+ else
+ error = -EINVAL;
+ return error;
+}
+EXPORT_SYMBOL_GPL(driver_create_file);
+
+/**
+ * driver_remove_file - remove sysfs file for driver.
+ * @drv: driver.
+ * @attr: driver attribute descriptor.
+ */
+void driver_remove_file(struct device_driver *drv,
+ struct driver_attribute *attr)
+{
+ if (drv)
+ sysfs_remove_file(&drv->p->kobj, &attr->attr);
+}
+EXPORT_SYMBOL_GPL(driver_remove_file);
+
+/**
+ * driver_add_kobj - add a kobject below the specified driver
+ * @drv: requesting device driver
+ * @kobj: kobject to add below this driver
+ * @fmt: format string that names the kobject
+ *
+ * You really don't want to do this, this is only here due to one looney
+ * iseries driver, go poke those developers if you are annoyed about
+ * this...
+ */
+int driver_add_kobj(struct device_driver *drv, struct kobject *kobj,
+ const char *fmt, ...)
+{
+ va_list args;
+ char *name;
+ int ret;
+
+ va_start(args, fmt);
+ name = kvasprintf(GFP_KERNEL, fmt, args);
+ va_end(args);
+
+ if (!name)
+ return -ENOMEM;
+
+ ret = kobject_add(kobj, &drv->p->kobj, "%s", name);
+ kfree(name);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(driver_add_kobj);
+
+/**
+ * get_driver - increment driver reference count.
+ * @drv: driver.
+ */
+struct device_driver *get_driver(struct device_driver *drv)
+{
+ if (drv) {
+ struct driver_private *priv;
+ struct kobject *kobj;
+
+ kobj = kobject_get(&drv->p->kobj);
+ priv = to_driver(kobj);
+ return priv->driver;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(get_driver);
+
+/**
+ * put_driver - decrement driver's refcount.
+ * @drv: driver.
+ */
+void put_driver(struct device_driver *drv)
+{
+ kobject_put(&drv->p->kobj);
+}
+EXPORT_SYMBOL_GPL(put_driver);
+
+static int driver_add_groups(struct device_driver *drv,
+ struct attribute_group **groups)
+{
+ int error = 0;
+ int i;
+
+ if (groups) {
+ for (i = 0; groups[i]; i++) {
+ error = sysfs_create_group(&drv->p->kobj, groups[i]);
+ if (error) {
+ while (--i >= 0)
+ sysfs_remove_group(&drv->p->kobj,
+ groups[i]);
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+static void driver_remove_groups(struct device_driver *drv,
+ struct attribute_group **groups)
+{
+ int i;
+
+ if (groups)
+ for (i = 0; groups[i]; i++)
+ sysfs_remove_group(&drv->p->kobj, groups[i]);
+}
+
+/**
+ * driver_register - register driver with bus
+ * @drv: driver to register
+ *
+ * We pass off most of the work to the bus_add_driver() call,
+ * since most of the things we have to do deal with the bus
+ * structures.
+ */
+int driver_register(struct device_driver *drv)
+{
+ int ret;
+ struct device_driver *other;
+
+ if ((drv->bus->probe && drv->probe) ||
+ (drv->bus->remove && drv->remove) ||
+ (drv->bus->shutdown && drv->shutdown))
+ printk(KERN_WARNING "Driver '%s' needs updating - please use "
+ "bus_type methods\n", drv->name);
+
+ other = driver_find(drv->name, drv->bus);
+ if (other) {
+ put_driver(other);
+ printk(KERN_ERR "Error: Driver '%s' is already registered, "
+ "aborting...\n", drv->name);
+ return -EEXIST;
+ }
+
+ ret = bus_add_driver(drv);
+ if (ret)
+ return ret;
+ ret = driver_add_groups(drv, drv->groups);
+ if (ret)
+ bus_remove_driver(drv);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(driver_register);
+
+/**
+ * driver_unregister - remove driver from system.
+ * @drv: driver.
+ *
+ * Again, we pass off most of the work to the bus-level call.
+ */
+void driver_unregister(struct device_driver *drv)
+{
+ driver_remove_groups(drv, drv->groups);
+ bus_remove_driver(drv);
+}
+EXPORT_SYMBOL_GPL(driver_unregister);
+
+/**
+ * driver_find - locate driver on a bus by its name.
+ * @name: name of the driver.
+ * @bus: bus to scan for the driver.
+ *
+ * Call kset_find_obj() to iterate over list of drivers on
+ * a bus to find driver by name. Return driver if found.
+ *
+ * Note that kset_find_obj increments driver's reference count.
+ */
+struct device_driver *driver_find(const char *name, struct bus_type *bus)
+{
+ struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
+ struct driver_private *priv;
+
+ if (k) {
+ priv = to_driver(k);
+ return priv->driver;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(driver_find);
diff --git a/drivers/base/firmware.c b/drivers/base/firmware.c
new file mode 100644
index 0000000..1138155
--- /dev/null
+++ b/drivers/base/firmware.c
@@ -0,0 +1,27 @@
+/*
+ * firmware.c - firmware subsystem hoohaw.
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007 Novell Inc.
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include "base.h"
+
+struct kobject *firmware_kobj;
+EXPORT_SYMBOL_GPL(firmware_kobj);
+
+int __init firmware_init(void)
+{
+ firmware_kobj = kobject_create_and_add("firmware", NULL);
+ if (!firmware_kobj)
+ return -ENOMEM;
+ return 0;
+}
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
new file mode 100644
index 0000000..b7e5710
--- /dev/null
+++ b/drivers/base/firmware_class.c
@@ -0,0 +1,617 @@
+/*
+ * firmware_class.c - Multi purpose firmware loading support
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz
+ *
+ * Please see Documentation/firmware_class/ for more information.
+ *
+ */
+
+#include <linux/capability.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+
+#include <linux/firmware.h>
+#include "base.h"
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+
+MODULE_AUTHOR("Manuel Estrada Sainz");
+MODULE_DESCRIPTION("Multi purpose firmware loading support");
+MODULE_LICENSE("GPL");
+
+enum {
+ FW_STATUS_LOADING,
+ FW_STATUS_DONE,
+ FW_STATUS_ABORT,
+};
+
+static int loading_timeout = 60; /* In seconds */
+
+/* fw_lock could be moved to 'struct firmware_priv' but since it is just
+ * guarding for corner cases a global lock should be OK */
+static DEFINE_MUTEX(fw_lock);
+
+struct firmware_priv {
+ char fw_id[FIRMWARE_NAME_MAX];
+ struct completion completion;
+ struct bin_attribute attr_data;
+ struct firmware *fw;
+ unsigned long status;
+ int alloc_size;
+ struct timer_list timeout;
+};
+
+#ifdef CONFIG_FW_LOADER
+extern struct builtin_fw __start_builtin_fw[];
+extern struct builtin_fw __end_builtin_fw[];
+#else /* Module case. Avoid ifdefs later; it'll all optimise out */
+static struct builtin_fw *__start_builtin_fw;
+static struct builtin_fw *__end_builtin_fw;
+#endif
+
+static void
+fw_load_abort(struct firmware_priv *fw_priv)
+{
+ set_bit(FW_STATUS_ABORT, &fw_priv->status);
+ wmb();
+ complete(&fw_priv->completion);
+}
+
+static ssize_t
+firmware_timeout_show(struct class *class, char *buf)
+{
+ return sprintf(buf, "%d\n", loading_timeout);
+}
+
+/**
+ * firmware_timeout_store - set number of seconds to wait for firmware
+ * @class: device class pointer
+ * @buf: buffer to scan for timeout value
+ * @count: number of bytes in @buf
+ *
+ * Sets the number of seconds to wait for the firmware. Once
+ * this expires an error will be returned to the driver and no
+ * firmware will be provided.
+ *
+ * Note: zero means 'wait forever'.
+ **/
+static ssize_t
+firmware_timeout_store(struct class *class, const char *buf, size_t count)
+{
+ loading_timeout = simple_strtol(buf, NULL, 10);
+ if (loading_timeout < 0)
+ loading_timeout = 0;
+ return count;
+}
+
+static CLASS_ATTR(timeout, 0644, firmware_timeout_show, firmware_timeout_store);
+
+static void fw_dev_release(struct device *dev);
+
+static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct firmware_priv *fw_priv = dev_get_drvdata(dev);
+
+ if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
+ return -ENOMEM;
+ if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static struct class firmware_class = {
+ .name = "firmware",
+ .dev_uevent = firmware_uevent,
+ .dev_release = fw_dev_release,
+};
+
+static ssize_t firmware_loading_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct firmware_priv *fw_priv = dev_get_drvdata(dev);
+ int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
+ return sprintf(buf, "%d\n", loading);
+}
+
+/**
+ * firmware_loading_store - set value in the 'loading' control file
+ * @dev: device pointer
+ * @attr: device attribute pointer
+ * @buf: buffer to scan for loading control value
+ * @count: number of bytes in @buf
+ *
+ * The relevant values are:
+ *
+ * 1: Start a load, discarding any previous partial load.
+ * 0: Conclude the load and hand the data to the driver code.
+ * -1: Conclude the load with an error and discard any written data.
+ **/
+static ssize_t firmware_loading_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct firmware_priv *fw_priv = dev_get_drvdata(dev);
+ int loading = simple_strtol(buf, NULL, 10);
+
+ switch (loading) {
+ case 1:
+ mutex_lock(&fw_lock);
+ if (!fw_priv->fw) {
+ mutex_unlock(&fw_lock);
+ break;
+ }
+ vfree(fw_priv->fw->data);
+ fw_priv->fw->data = NULL;
+ fw_priv->fw->size = 0;
+ fw_priv->alloc_size = 0;
+ set_bit(FW_STATUS_LOADING, &fw_priv->status);
+ mutex_unlock(&fw_lock);
+ break;
+ case 0:
+ if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
+ complete(&fw_priv->completion);
+ clear_bit(FW_STATUS_LOADING, &fw_priv->status);
+ break;
+ }
+ /* fallthrough */
+ default:
+ dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
+ /* fallthrough */
+ case -1:
+ fw_load_abort(fw_priv);
+ break;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
+
+static ssize_t
+firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct device *dev = to_dev(kobj);
+ struct firmware_priv *fw_priv = dev_get_drvdata(dev);
+ struct firmware *fw;
+ ssize_t ret_count;
+
+ mutex_lock(&fw_lock);
+ fw = fw_priv->fw;
+ if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ ret_count = -ENODEV;
+ goto out;
+ }
+ ret_count = memory_read_from_buffer(buffer, count, &offset,
+ fw->data, fw->size);
+out:
+ mutex_unlock(&fw_lock);
+ return ret_count;
+}
+
+static int
+fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
+{
+ u8 *new_data;
+ int new_size = fw_priv->alloc_size;
+
+ if (min_size <= fw_priv->alloc_size)
+ return 0;
+
+ new_size = ALIGN(min_size, PAGE_SIZE);
+ new_data = vmalloc(new_size);
+ if (!new_data) {
+ printk(KERN_ERR "%s: unable to alloc buffer\n", __func__);
+ /* Make sure that we don't keep incomplete data */
+ fw_load_abort(fw_priv);
+ return -ENOMEM;
+ }
+ fw_priv->alloc_size = new_size;
+ if (fw_priv->fw->data) {
+ memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
+ vfree(fw_priv->fw->data);
+ }
+ fw_priv->fw->data = new_data;
+ BUG_ON(min_size > fw_priv->alloc_size);
+ return 0;
+}
+
+/**
+ * firmware_data_write - write method for firmware
+ * @kobj: kobject for the device
+ * @bin_attr: bin_attr structure
+ * @buffer: buffer being written
+ * @offset: buffer offset for write in total data store area
+ * @count: buffer size
+ *
+ * Data written to the 'data' attribute will be later handed to
+ * the driver as a firmware image.
+ **/
+static ssize_t
+firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct device *dev = to_dev(kobj);
+ struct firmware_priv *fw_priv = dev_get_drvdata(dev);
+ struct firmware *fw;
+ ssize_t retval;
+
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ mutex_lock(&fw_lock);
+ fw = fw_priv->fw;
+ if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ retval = -ENODEV;
+ goto out;
+ }
+ retval = fw_realloc_buffer(fw_priv, offset + count);
+ if (retval)
+ goto out;
+
+ memcpy((u8 *)fw->data + offset, buffer, count);
+
+ fw->size = max_t(size_t, offset + count, fw->size);
+ retval = count;
+out:
+ mutex_unlock(&fw_lock);
+ return retval;
+}
+
+static struct bin_attribute firmware_attr_data_tmpl = {
+ .attr = {.name = "data", .mode = 0644},
+ .size = 0,
+ .read = firmware_data_read,
+ .write = firmware_data_write,
+};
+
+static void fw_dev_release(struct device *dev)
+{
+ struct firmware_priv *fw_priv = dev_get_drvdata(dev);
+
+ kfree(fw_priv);
+ kfree(dev);
+
+ module_put(THIS_MODULE);
+}
+
+static void
+firmware_class_timeout(u_long data)
+{
+ struct firmware_priv *fw_priv = (struct firmware_priv *) data;
+ fw_load_abort(fw_priv);
+}
+
+static inline void fw_setup_device_id(struct device *f_dev, struct device *dev)
+{
+ /* XXX warning we should watch out for name collisions */
+ strlcpy(f_dev->bus_id, dev->bus_id, BUS_ID_SIZE);
+}
+
+static int fw_register_device(struct device **dev_p, const char *fw_name,
+ struct device *device)
+{
+ int retval;
+ struct firmware_priv *fw_priv = kzalloc(sizeof(*fw_priv),
+ GFP_KERNEL);
+ struct device *f_dev = kzalloc(sizeof(*f_dev), GFP_KERNEL);
+
+ *dev_p = NULL;
+
+ if (!fw_priv || !f_dev) {
+ dev_err(device, "%s: kmalloc failed\n", __func__);
+ retval = -ENOMEM;
+ goto error_kfree;
+ }
+
+ init_completion(&fw_priv->completion);
+ fw_priv->attr_data = firmware_attr_data_tmpl;
+ strlcpy(fw_priv->fw_id, fw_name, FIRMWARE_NAME_MAX);
+
+ fw_priv->timeout.function = firmware_class_timeout;
+ fw_priv->timeout.data = (u_long) fw_priv;
+ init_timer(&fw_priv->timeout);
+
+ fw_setup_device_id(f_dev, device);
+ f_dev->parent = device;
+ f_dev->class = &firmware_class;
+ dev_set_drvdata(f_dev, fw_priv);
+ f_dev->uevent_suppress = 1;
+ retval = device_register(f_dev);
+ if (retval) {
+ dev_err(device, "%s: device_register failed\n", __func__);
+ goto error_kfree;
+ }
+ *dev_p = f_dev;
+ return 0;
+
+error_kfree:
+ kfree(fw_priv);
+ kfree(f_dev);
+ return retval;
+}
+
+static int fw_setup_device(struct firmware *fw, struct device **dev_p,
+ const char *fw_name, struct device *device,
+ int uevent)
+{
+ struct device *f_dev;
+ struct firmware_priv *fw_priv;
+ int retval;
+
+ *dev_p = NULL;
+ retval = fw_register_device(&f_dev, fw_name, device);
+ if (retval)
+ goto out;
+
+ /* Need to pin this module until class device is destroyed */
+ __module_get(THIS_MODULE);
+
+ fw_priv = dev_get_drvdata(f_dev);
+
+ fw_priv->fw = fw;
+ retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data);
+ if (retval) {
+ dev_err(device, "%s: sysfs_create_bin_file failed\n", __func__);
+ goto error_unreg;
+ }
+
+ retval = device_create_file(f_dev, &dev_attr_loading);
+ if (retval) {
+ dev_err(device, "%s: device_create_file failed\n", __func__);
+ goto error_unreg;
+ }
+
+ if (uevent)
+ f_dev->uevent_suppress = 0;
+ *dev_p = f_dev;
+ goto out;
+
+error_unreg:
+ device_unregister(f_dev);
+out:
+ return retval;
+}
+
+static int
+_request_firmware(const struct firmware **firmware_p, const char *name,
+ struct device *device, int uevent)
+{
+ struct device *f_dev;
+ struct firmware_priv *fw_priv;
+ struct firmware *firmware;
+ struct builtin_fw *builtin;
+ int retval;
+
+ if (!firmware_p)
+ return -EINVAL;
+
+ *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
+ if (!firmware) {
+ dev_err(device, "%s: kmalloc(struct firmware) failed\n",
+ __func__);
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ for (builtin = __start_builtin_fw; builtin != __end_builtin_fw;
+ builtin++) {
+ if (strcmp(name, builtin->name))
+ continue;
+ dev_info(device, "firmware: using built-in firmware %s\n",
+ name);
+ firmware->size = builtin->size;
+ firmware->data = builtin->data;
+ return 0;
+ }
+
+ if (uevent)
+ dev_info(device, "firmware: requesting %s\n", name);
+
+ retval = fw_setup_device(firmware, &f_dev, name, device, uevent);
+ if (retval)
+ goto error_kfree_fw;
+
+ fw_priv = dev_get_drvdata(f_dev);
+
+ if (uevent) {
+ if (loading_timeout > 0) {
+ fw_priv->timeout.expires = jiffies + loading_timeout * HZ;
+ add_timer(&fw_priv->timeout);
+ }
+
+ kobject_uevent(&f_dev->kobj, KOBJ_ADD);
+ wait_for_completion(&fw_priv->completion);
+ set_bit(FW_STATUS_DONE, &fw_priv->status);
+ del_timer_sync(&fw_priv->timeout);
+ } else
+ wait_for_completion(&fw_priv->completion);
+
+ mutex_lock(&fw_lock);
+ if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) {
+ retval = -ENOENT;
+ release_firmware(fw_priv->fw);
+ *firmware_p = NULL;
+ }
+ fw_priv->fw = NULL;
+ mutex_unlock(&fw_lock);
+ device_unregister(f_dev);
+ goto out;
+
+error_kfree_fw:
+ kfree(firmware);
+ *firmware_p = NULL;
+out:
+ return retval;
+}
+
+/**
+ * request_firmware: - send firmware request and wait for it
+ * @firmware_p: pointer to firmware image
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ *
+ * @firmware_p will be used to return a firmware image by the name
+ * of @name for device @device.
+ *
+ * Should be called from user context where sleeping is allowed.
+ *
+ * @name will be used as $FIRMWARE in the uevent environment and
+ * should be distinctive enough not to be confused with any other
+ * firmware image for this or any other device.
+ **/
+int
+request_firmware(const struct firmware **firmware_p, const char *name,
+ struct device *device)
+{
+ int uevent = 1;
+ return _request_firmware(firmware_p, name, device, uevent);
+}
+
+/**
+ * release_firmware: - release the resource associated with a firmware image
+ * @fw: firmware resource to release
+ **/
+void
+release_firmware(const struct firmware *fw)
+{
+ struct builtin_fw *builtin;
+
+ if (fw) {
+ for (builtin = __start_builtin_fw; builtin != __end_builtin_fw;
+ builtin++) {
+ if (fw->data == builtin->data)
+ goto free_fw;
+ }
+ vfree(fw->data);
+ free_fw:
+ kfree(fw);
+ }
+}
+
+/* Async support */
+struct firmware_work {
+ struct work_struct work;
+ struct module *module;
+ const char *name;
+ struct device *device;
+ void *context;
+ void (*cont)(const struct firmware *fw, void *context);
+ int uevent;
+};
+
+static int
+request_firmware_work_func(void *arg)
+{
+ struct firmware_work *fw_work = arg;
+ const struct firmware *fw;
+ int ret;
+ if (!arg) {
+ WARN_ON(1);
+ return 0;
+ }
+ ret = _request_firmware(&fw, fw_work->name, fw_work->device,
+ fw_work->uevent);
+ if (ret < 0)
+ fw_work->cont(NULL, fw_work->context);
+ else {
+ fw_work->cont(fw, fw_work->context);
+ release_firmware(fw);
+ }
+ module_put(fw_work->module);
+ kfree(fw_work);
+ return ret;
+}
+
+/**
+ * request_firmware_nowait: asynchronous version of request_firmware
+ * @module: module requesting the firmware
+ * @uevent: sends uevent to copy the firmware image if this flag
+ * is non-zero else the firmware copy must be done manually.
+ * @name: name of firmware file
+ * @device: device for which firmware is being loaded
+ * @context: will be passed over to @cont, and
+ * @fw may be %NULL if firmware request fails.
+ * @cont: function will be called asynchronously when the firmware
+ * request is over.
+ *
+ * Asynchronous variant of request_firmware() for contexts where
+ * it is not possible to sleep.
+ **/
+int
+request_firmware_nowait(
+ struct module *module, int uevent,
+ const char *name, struct device *device, void *context,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ struct task_struct *task;
+ struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
+ GFP_ATOMIC);
+
+ if (!fw_work)
+ return -ENOMEM;
+ if (!try_module_get(module)) {
+ kfree(fw_work);
+ return -EFAULT;
+ }
+
+ *fw_work = (struct firmware_work) {
+ .module = module,
+ .name = name,
+ .device = device,
+ .context = context,
+ .cont = cont,
+ .uevent = uevent,
+ };
+
+ task = kthread_run(request_firmware_work_func, fw_work,
+ "firmware/%s", name);
+
+ if (IS_ERR(task)) {
+ fw_work->cont(NULL, fw_work->context);
+ module_put(fw_work->module);
+ kfree(fw_work);
+ return PTR_ERR(task);
+ }
+ return 0;
+}
+
+static int __init
+firmware_class_init(void)
+{
+ int error;
+ error = class_register(&firmware_class);
+ if (error) {
+ printk(KERN_ERR "%s: class_register failed\n", __func__);
+ return error;
+ }
+ error = class_create_file(&firmware_class, &class_attr_timeout);
+ if (error) {
+ printk(KERN_ERR "%s: class_create_file failed\n",
+ __func__);
+ class_unregister(&firmware_class);
+ }
+ return error;
+
+}
+static void __exit
+firmware_class_exit(void)
+{
+ class_unregister(&firmware_class);
+}
+
+fs_initcall(firmware_class_init);
+module_exit(firmware_class_exit);
+
+EXPORT_SYMBOL(release_firmware);
+EXPORT_SYMBOL(request_firmware);
+EXPORT_SYMBOL(request_firmware_nowait);
diff --git a/drivers/base/hypervisor.c b/drivers/base/hypervisor.c
new file mode 100644
index 0000000..6428cba
--- /dev/null
+++ b/drivers/base/hypervisor.c
@@ -0,0 +1,24 @@
+/*
+ * hypervisor.c - /sys/hypervisor subsystem.
+ *
+ * Copyright (C) IBM Corp. 2006
+ * Copyright (C) 2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2007 Novell Inc.
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include "base.h"
+
+struct kobject *hypervisor_kobj;
+EXPORT_SYMBOL_GPL(hypervisor_kobj);
+
+int __init hypervisor_init(void)
+{
+ hypervisor_kobj = kobject_create_and_add("hypervisor", NULL);
+ if (!hypervisor_kobj)
+ return -ENOMEM;
+ return 0;
+}
diff --git a/drivers/base/init.c b/drivers/base/init.c
new file mode 100644
index 0000000..7bd9b6a
--- /dev/null
+++ b/drivers/base/init.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/memory.h>
+
+#include "base.h"
+
+/**
+ * driver_init - initialize driver model.
+ *
+ * Call the driver model init functions to initialize their
+ * subsystems. Called early from init/main.c.
+ */
+void __init driver_init(void)
+{
+ /* These are the core pieces */
+ devices_init();
+ buses_init();
+ classes_init();
+ firmware_init();
+ hypervisor_init();
+
+ /* These are also core pieces, but must come after the
+ * core core pieces.
+ */
+ platform_bus_init();
+ system_bus_init();
+ cpu_dev_init();
+ memory_dev_init();
+}
diff --git a/drivers/base/isa.c b/drivers/base/isa.c
new file mode 100644
index 0000000..efd5775
--- /dev/null
+++ b/drivers/base/isa.c
@@ -0,0 +1,184 @@
+/*
+ * ISA bus.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/isa.h>
+
+static struct device isa_bus = {
+ .bus_id = "isa"
+};
+
+struct isa_dev {
+ struct device dev;
+ struct device *next;
+ unsigned int id;
+};
+
+#define to_isa_dev(x) container_of((x), struct isa_dev, dev)
+
+static int isa_bus_match(struct device *dev, struct device_driver *driver)
+{
+ struct isa_driver *isa_driver = to_isa_driver(driver);
+
+ if (dev->platform_data == isa_driver) {
+ if (!isa_driver->match ||
+ isa_driver->match(dev, to_isa_dev(dev)->id))
+ return 1;
+ dev->platform_data = NULL;
+ }
+ return 0;
+}
+
+static int isa_bus_probe(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->probe)
+ return isa_driver->probe(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static int isa_bus_remove(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->remove)
+ return isa_driver->remove(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static void isa_bus_shutdown(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->shutdown)
+ isa_driver->shutdown(dev, to_isa_dev(dev)->id);
+}
+
+static int isa_bus_suspend(struct device *dev, pm_message_t state)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->suspend)
+ return isa_driver->suspend(dev, to_isa_dev(dev)->id, state);
+
+ return 0;
+}
+
+static int isa_bus_resume(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->resume)
+ return isa_driver->resume(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static struct bus_type isa_bus_type = {
+ .name = "isa",
+ .match = isa_bus_match,
+ .probe = isa_bus_probe,
+ .remove = isa_bus_remove,
+ .shutdown = isa_bus_shutdown,
+ .suspend = isa_bus_suspend,
+ .resume = isa_bus_resume
+};
+
+static void isa_dev_release(struct device *dev)
+{
+ kfree(to_isa_dev(dev));
+}
+
+void isa_unregister_driver(struct isa_driver *isa_driver)
+{
+ struct device *dev = isa_driver->devices;
+
+ while (dev) {
+ struct device *tmp = to_isa_dev(dev)->next;
+ device_unregister(dev);
+ dev = tmp;
+ }
+ driver_unregister(&isa_driver->driver);
+}
+EXPORT_SYMBOL_GPL(isa_unregister_driver);
+
+int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev)
+{
+ int error;
+ unsigned int id;
+
+ isa_driver->driver.bus = &isa_bus_type;
+ isa_driver->devices = NULL;
+
+ error = driver_register(&isa_driver->driver);
+ if (error)
+ return error;
+
+ for (id = 0; id < ndev; id++) {
+ struct isa_dev *isa_dev;
+
+ isa_dev = kzalloc(sizeof *isa_dev, GFP_KERNEL);
+ if (!isa_dev) {
+ error = -ENOMEM;
+ break;
+ }
+
+ isa_dev->dev.parent = &isa_bus;
+ isa_dev->dev.bus = &isa_bus_type;
+
+ snprintf(isa_dev->dev.bus_id, BUS_ID_SIZE, "%s.%u",
+ isa_driver->driver.name, id);
+
+ isa_dev->dev.platform_data = isa_driver;
+ isa_dev->dev.release = isa_dev_release;
+ isa_dev->id = id;
+
+ isa_dev->dev.coherent_dma_mask = DMA_24BIT_MASK;
+ isa_dev->dev.dma_mask = &isa_dev->dev.coherent_dma_mask;
+
+ error = device_register(&isa_dev->dev);
+ if (error) {
+ put_device(&isa_dev->dev);
+ break;
+ }
+
+ if (isa_dev->dev.platform_data) {
+ isa_dev->next = isa_driver->devices;
+ isa_driver->devices = &isa_dev->dev;
+ } else
+ device_unregister(&isa_dev->dev);
+ }
+
+ if (!error && !isa_driver->devices)
+ error = -ENODEV;
+
+ if (error)
+ isa_unregister_driver(isa_driver);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(isa_register_driver);
+
+static int __init isa_bus_init(void)
+{
+ int error;
+
+ error = bus_register(&isa_bus_type);
+ if (!error) {
+ error = device_register(&isa_bus);
+ if (error)
+ bus_unregister(&isa_bus_type);
+ }
+ return error;
+}
+
+device_initcall(isa_bus_init);
diff --git a/drivers/base/map.c b/drivers/base/map.c
new file mode 100644
index 0000000..e87017f
--- /dev/null
+++ b/drivers/base/map.c
@@ -0,0 +1,155 @@
+/*
+ * linux/drivers/base/map.c
+ *
+ * (C) Copyright Al Viro 2002,2003
+ * Released under GPL v2.
+ *
+ * NOTE: data structure needs to be changed. It works, but for large dev_t
+ * it will be too slow. It is isolated, though, so these changes will be
+ * local to that file.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/kdev_t.h>
+#include <linux/kobject.h>
+#include <linux/kobj_map.h>
+
+struct kobj_map {
+ struct probe {
+ struct probe *next;
+ dev_t dev;
+ unsigned long range;
+ struct module *owner;
+ kobj_probe_t *get;
+ int (*lock)(dev_t, void *);
+ void *data;
+ } *probes[255];
+ struct mutex *lock;
+};
+
+int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
+ struct module *module, kobj_probe_t *probe,
+ int (*lock)(dev_t, void *), void *data)
+{
+ unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
+ unsigned index = MAJOR(dev);
+ unsigned i;
+ struct probe *p;
+
+ if (n > 255)
+ n = 255;
+
+ p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);
+
+ if (p == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++, p++) {
+ p->owner = module;
+ p->get = probe;
+ p->lock = lock;
+ p->dev = dev;
+ p->range = range;
+ p->data = data;
+ }
+ mutex_lock(domain->lock);
+ for (i = 0, p -= n; i < n; i++, p++, index++) {
+ struct probe **s = &domain->probes[index % 255];
+ while (*s && (*s)->range < range)
+ s = &(*s)->next;
+ p->next = *s;
+ *s = p;
+ }
+ mutex_unlock(domain->lock);
+ return 0;
+}
+
+void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range)
+{
+ unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
+ unsigned index = MAJOR(dev);
+ unsigned i;
+ struct probe *found = NULL;
+
+ if (n > 255)
+ n = 255;
+
+ mutex_lock(domain->lock);
+ for (i = 0; i < n; i++, index++) {
+ struct probe **s;
+ for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) {
+ struct probe *p = *s;
+ if (p->dev == dev && p->range == range) {
+ *s = p->next;
+ if (!found)
+ found = p;
+ break;
+ }
+ }
+ }
+ mutex_unlock(domain->lock);
+ kfree(found);
+}
+
+struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
+{
+ struct kobject *kobj;
+ struct probe *p;
+ unsigned long best = ~0UL;
+
+retry:
+ mutex_lock(domain->lock);
+ for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
+ struct kobject *(*probe)(dev_t, int *, void *);
+ struct module *owner;
+ void *data;
+
+ if (p->dev > dev || p->dev + p->range - 1 < dev)
+ continue;
+ if (p->range - 1 >= best)
+ break;
+ if (!try_module_get(p->owner))
+ continue;
+ owner = p->owner;
+ data = p->data;
+ probe = p->get;
+ best = p->range - 1;
+ *index = dev - p->dev;
+ if (p->lock && p->lock(dev, data) < 0) {
+ module_put(owner);
+ continue;
+ }
+ mutex_unlock(domain->lock);
+ kobj = probe(dev, index, data);
+ /* Currently ->owner protects _only_ ->probe() itself. */
+ module_put(owner);
+ if (kobj)
+ return kobj;
+ goto retry;
+ }
+ mutex_unlock(domain->lock);
+ return NULL;
+}
+
+struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
+{
+ struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
+ struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
+ int i;
+
+ if ((p == NULL) || (base == NULL)) {
+ kfree(p);
+ kfree(base);
+ return NULL;
+ }
+
+ base->dev = 1;
+ base->range = ~0;
+ base->get = base_probe;
+ for (i = 0; i < 255; i++)
+ p->probes[i] = base;
+ p->lock = lock;
+ return p;
+}
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
new file mode 100644
index 0000000..5260e9e
--- /dev/null
+++ b/drivers/base/memory.c
@@ -0,0 +1,476 @@
+/*
+ * drivers/base/memory.c - basic Memory class support
+ *
+ * Written by Matt Tolentino <matthew.e.tolentino@intel.com>
+ * Dave Hansen <haveblue@us.ibm.com>
+ *
+ * This file provides the necessary infrastructure to represent
+ * a SPARSEMEM-memory-model system's physical memory in /sysfs.
+ * All arch-independent code that assumes MEMORY_HOTPLUG requires
+ * SPARSEMEM should be contained here, or in mm/memory_hotplug.c.
+ */
+
+#include <linux/sysdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/topology.h>
+#include <linux/capability.h>
+#include <linux/device.h>
+#include <linux/memory.h>
+#include <linux/kobject.h>
+#include <linux/memory_hotplug.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/stat.h>
+
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#define MEMORY_CLASS_NAME "memory"
+
+static struct sysdev_class memory_sysdev_class = {
+ .name = MEMORY_CLASS_NAME,
+};
+
+static const char *memory_uevent_name(struct kset *kset, struct kobject *kobj)
+{
+ return MEMORY_CLASS_NAME;
+}
+
+static int memory_uevent(struct kset *kset, struct kobject *obj, struct kobj_uevent_env *env)
+{
+ int retval = 0;
+
+ return retval;
+}
+
+static struct kset_uevent_ops memory_uevent_ops = {
+ .name = memory_uevent_name,
+ .uevent = memory_uevent,
+};
+
+static BLOCKING_NOTIFIER_HEAD(memory_chain);
+
+int register_memory_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&memory_chain, nb);
+}
+EXPORT_SYMBOL(register_memory_notifier);
+
+void unregister_memory_notifier(struct notifier_block *nb)
+{
+ blocking_notifier_chain_unregister(&memory_chain, nb);
+}
+EXPORT_SYMBOL(unregister_memory_notifier);
+
+/*
+ * register_memory - Setup a sysfs device for a memory block
+ */
+static
+int register_memory(struct memory_block *memory, struct mem_section *section)
+{
+ int error;
+
+ memory->sysdev.cls = &memory_sysdev_class;
+ memory->sysdev.id = __section_nr(section);
+
+ error = sysdev_register(&memory->sysdev);
+ return error;
+}
+
+static void
+unregister_memory(struct memory_block *memory, struct mem_section *section)
+{
+ BUG_ON(memory->sysdev.cls != &memory_sysdev_class);
+ BUG_ON(memory->sysdev.id != __section_nr(section));
+
+ /* drop the ref. we got in remove_memory_block() */
+ kobject_put(&memory->sysdev.kobj);
+ sysdev_unregister(&memory->sysdev);
+}
+
+/*
+ * use this as the physical section index that this memsection
+ * uses.
+ */
+
+static ssize_t show_mem_phys_index(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ struct memory_block *mem =
+ container_of(dev, struct memory_block, sysdev);
+ return sprintf(buf, "%08lx\n", mem->phys_index);
+}
+
+/*
+ * Show whether the section of memory is likely to be hot-removable
+ */
+static ssize_t show_mem_removable(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ unsigned long start_pfn;
+ int ret;
+ struct memory_block *mem =
+ container_of(dev, struct memory_block, sysdev);
+
+ start_pfn = section_nr_to_pfn(mem->phys_index);
+ ret = is_mem_section_removable(start_pfn, PAGES_PER_SECTION);
+ return sprintf(buf, "%d\n", ret);
+}
+
+/*
+ * online, offline, going offline, etc.
+ */
+static ssize_t show_mem_state(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ struct memory_block *mem =
+ container_of(dev, struct memory_block, sysdev);
+ ssize_t len = 0;
+
+ /*
+ * We can probably put these states in a nice little array
+ * so that they're not open-coded
+ */
+ switch (mem->state) {
+ case MEM_ONLINE:
+ len = sprintf(buf, "online\n");
+ break;
+ case MEM_OFFLINE:
+ len = sprintf(buf, "offline\n");
+ break;
+ case MEM_GOING_OFFLINE:
+ len = sprintf(buf, "going-offline\n");
+ break;
+ default:
+ len = sprintf(buf, "ERROR-UNKNOWN-%ld\n",
+ mem->state);
+ WARN_ON(1);
+ break;
+ }
+
+ return len;
+}
+
+int memory_notify(unsigned long val, void *v)
+{
+ return blocking_notifier_call_chain(&memory_chain, val, v);
+}
+
+/*
+ * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is
+ * OK to have direct references to sparsemem variables in here.
+ */
+static int
+memory_block_action(struct memory_block *mem, unsigned long action)
+{
+ int i;
+ unsigned long psection;
+ unsigned long start_pfn, start_paddr;
+ struct page *first_page;
+ int ret;
+ int old_state = mem->state;
+
+ psection = mem->phys_index;
+ first_page = pfn_to_page(psection << PFN_SECTION_SHIFT);
+
+ /*
+ * The probe routines leave the pages reserved, just
+ * as the bootmem code does. Make sure they're still
+ * that way.
+ */
+ if (action == MEM_ONLINE) {
+ for (i = 0; i < PAGES_PER_SECTION; i++) {
+ if (PageReserved(first_page+i))
+ continue;
+
+ printk(KERN_WARNING "section number %ld page number %d "
+ "not reserved, was it already online? \n",
+ psection, i);
+ return -EBUSY;
+ }
+ }
+
+ switch (action) {
+ case MEM_ONLINE:
+ start_pfn = page_to_pfn(first_page);
+ ret = online_pages(start_pfn, PAGES_PER_SECTION);
+ break;
+ case MEM_OFFLINE:
+ mem->state = MEM_GOING_OFFLINE;
+ start_paddr = page_to_pfn(first_page) << PAGE_SHIFT;
+ ret = remove_memory(start_paddr,
+ PAGES_PER_SECTION << PAGE_SHIFT);
+ if (ret) {
+ mem->state = old_state;
+ break;
+ }
+ break;
+ default:
+ WARN(1, KERN_WARNING "%s(%p, %ld) unknown action: %ld\n",
+ __func__, mem, action, action);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int memory_block_change_state(struct memory_block *mem,
+ unsigned long to_state, unsigned long from_state_req)
+{
+ int ret = 0;
+ mutex_lock(&mem->state_mutex);
+
+ if (mem->state != from_state_req) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = memory_block_action(mem, to_state);
+ if (!ret)
+ mem->state = to_state;
+
+out:
+ mutex_unlock(&mem->state_mutex);
+ return ret;
+}
+
+static ssize_t
+store_mem_state(struct sys_device *dev,
+ struct sysdev_attribute *attr, const char *buf, size_t count)
+{
+ struct memory_block *mem;
+ unsigned int phys_section_nr;
+ int ret = -EINVAL;
+
+ mem = container_of(dev, struct memory_block, sysdev);
+ phys_section_nr = mem->phys_index;
+
+ if (!present_section_nr(phys_section_nr))
+ goto out;
+
+ if (!strncmp(buf, "online", min((int)count, 6)))
+ ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE);
+ else if(!strncmp(buf, "offline", min((int)count, 7)))
+ ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE);
+out:
+ if (ret)
+ return ret;
+ return count;
+}
+
+/*
+ * phys_device is a bad name for this. What I really want
+ * is a way to differentiate between memory ranges that
+ * are part of physical devices that constitute
+ * a complete removable unit or fru.
+ * i.e. do these ranges belong to the same physical device,
+ * s.t. if I offline all of these sections I can then
+ * remove the physical device?
+ */
+static ssize_t show_phys_device(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ struct memory_block *mem =
+ container_of(dev, struct memory_block, sysdev);
+ return sprintf(buf, "%d\n", mem->phys_device);
+}
+
+static SYSDEV_ATTR(phys_index, 0444, show_mem_phys_index, NULL);
+static SYSDEV_ATTR(state, 0644, show_mem_state, store_mem_state);
+static SYSDEV_ATTR(phys_device, 0444, show_phys_device, NULL);
+static SYSDEV_ATTR(removable, 0444, show_mem_removable, NULL);
+
+#define mem_create_simple_file(mem, attr_name) \
+ sysdev_create_file(&mem->sysdev, &attr_##attr_name)
+#define mem_remove_simple_file(mem, attr_name) \
+ sysdev_remove_file(&mem->sysdev, &attr_##attr_name)
+
+/*
+ * Block size attribute stuff
+ */
+static ssize_t
+print_block_size(struct class *class, char *buf)
+{
+ return sprintf(buf, "%lx\n", (unsigned long)PAGES_PER_SECTION * PAGE_SIZE);
+}
+
+static CLASS_ATTR(block_size_bytes, 0444, print_block_size, NULL);
+
+static int block_size_init(void)
+{
+ return sysfs_create_file(&memory_sysdev_class.kset.kobj,
+ &class_attr_block_size_bytes.attr);
+}
+
+/*
+ * Some architectures will have custom drivers to do this, and
+ * will not need to do it from userspace. The fake hot-add code
+ * as well as ppc64 will do all of their discovery in userspace
+ * and will require this interface.
+ */
+#ifdef CONFIG_ARCH_MEMORY_PROBE
+static ssize_t
+memory_probe_store(struct class *class, const char *buf, size_t count)
+{
+ u64 phys_addr;
+ int nid;
+ int ret;
+
+ phys_addr = simple_strtoull(buf, NULL, 0);
+
+ nid = memory_add_physaddr_to_nid(phys_addr);
+ ret = add_memory(nid, phys_addr, PAGES_PER_SECTION << PAGE_SHIFT);
+
+ if (ret)
+ count = ret;
+
+ return count;
+}
+static CLASS_ATTR(probe, S_IWUSR, NULL, memory_probe_store);
+
+static int memory_probe_init(void)
+{
+ return sysfs_create_file(&memory_sysdev_class.kset.kobj,
+ &class_attr_probe.attr);
+}
+#else
+static inline int memory_probe_init(void)
+{
+ return 0;
+}
+#endif
+
+/*
+ * Note that phys_device is optional. It is here to allow for
+ * differentiation between which *physical* devices each
+ * section belongs to...
+ */
+
+static int add_memory_block(unsigned long node_id, struct mem_section *section,
+ unsigned long state, int phys_device)
+{
+ struct memory_block *mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ int ret = 0;
+
+ if (!mem)
+ return -ENOMEM;
+
+ mem->phys_index = __section_nr(section);
+ mem->state = state;
+ mutex_init(&mem->state_mutex);
+ mem->phys_device = phys_device;
+
+ ret = register_memory(mem, section);
+ if (!ret)
+ ret = mem_create_simple_file(mem, phys_index);
+ if (!ret)
+ ret = mem_create_simple_file(mem, state);
+ if (!ret)
+ ret = mem_create_simple_file(mem, phys_device);
+ if (!ret)
+ ret = mem_create_simple_file(mem, removable);
+
+ return ret;
+}
+
+/*
+ * For now, we have a linear search to go find the appropriate
+ * memory_block corresponding to a particular phys_index. If
+ * this gets to be a real problem, we can always use a radix
+ * tree or something here.
+ *
+ * This could be made generic for all sysdev classes.
+ */
+static struct memory_block *find_memory_block(struct mem_section *section)
+{
+ struct kobject *kobj;
+ struct sys_device *sysdev;
+ struct memory_block *mem;
+ char name[sizeof(MEMORY_CLASS_NAME) + 9 + 1];
+
+ /*
+ * This only works because we know that section == sysdev->id
+ * slightly redundant with sysdev_register()
+ */
+ sprintf(&name[0], "%s%d", MEMORY_CLASS_NAME, __section_nr(section));
+
+ kobj = kset_find_obj(&memory_sysdev_class.kset, name);
+ if (!kobj)
+ return NULL;
+
+ sysdev = container_of(kobj, struct sys_device, kobj);
+ mem = container_of(sysdev, struct memory_block, sysdev);
+
+ return mem;
+}
+
+int remove_memory_block(unsigned long node_id, struct mem_section *section,
+ int phys_device)
+{
+ struct memory_block *mem;
+
+ mem = find_memory_block(section);
+ mem_remove_simple_file(mem, phys_index);
+ mem_remove_simple_file(mem, state);
+ mem_remove_simple_file(mem, phys_device);
+ mem_remove_simple_file(mem, removable);
+ unregister_memory(mem, section);
+
+ return 0;
+}
+
+/*
+ * need an interface for the VM to add new memory regions,
+ * but without onlining it.
+ */
+int register_new_memory(struct mem_section *section)
+{
+ return add_memory_block(0, section, MEM_OFFLINE, 0);
+}
+
+int unregister_memory_section(struct mem_section *section)
+{
+ if (!present_section(section))
+ return -EINVAL;
+
+ return remove_memory_block(0, section, 0);
+}
+
+/*
+ * Initialize the sysfs support for memory devices...
+ */
+int __init memory_dev_init(void)
+{
+ unsigned int i;
+ int ret;
+ int err;
+
+ memory_sysdev_class.kset.uevent_ops = &memory_uevent_ops;
+ ret = sysdev_class_register(&memory_sysdev_class);
+ if (ret)
+ goto out;
+
+ /*
+ * Create entries for memory sections that were found
+ * during boot and have been initialized
+ */
+ for (i = 0; i < NR_MEM_SECTIONS; i++) {
+ if (!present_section_nr(i))
+ continue;
+ err = add_memory_block(0, __nr_to_section(i), MEM_ONLINE, 0);
+ if (!ret)
+ ret = err;
+ }
+
+ err = memory_probe_init();
+ if (!ret)
+ ret = err;
+ err = block_size_init();
+ if (!ret)
+ ret = err;
+out:
+ if (ret)
+ printk(KERN_ERR "%s() failed: %d\n", __func__, ret);
+ return ret;
+}
diff --git a/drivers/base/module.c b/drivers/base/module.c
new file mode 100644
index 0000000..103be9c
--- /dev/null
+++ b/drivers/base/module.c
@@ -0,0 +1,94 @@
+/*
+ * module.c - module sysfs fun for drivers
+ *
+ * This file is released under the GPLv2
+ *
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include "base.h"
+
+static char *make_driver_name(struct device_driver *drv)
+{
+ char *driver_name;
+
+ driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2,
+ GFP_KERNEL);
+ if (!driver_name)
+ return NULL;
+
+ sprintf(driver_name, "%s:%s", drv->bus->name, drv->name);
+ return driver_name;
+}
+
+static void module_create_drivers_dir(struct module_kobject *mk)
+{
+ if (!mk || mk->drivers_dir)
+ return;
+
+ mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj);
+}
+
+void module_add_driver(struct module *mod, struct device_driver *drv)
+{
+ char *driver_name;
+ int no_warn;
+ struct module_kobject *mk = NULL;
+
+ if (!drv)
+ return;
+
+ if (mod)
+ mk = &mod->mkobj;
+ else if (drv->mod_name) {
+ struct kobject *mkobj;
+
+ /* Lookup built-in module entry in /sys/modules */
+ mkobj = kset_find_obj(module_kset, drv->mod_name);
+ if (mkobj) {
+ mk = container_of(mkobj, struct module_kobject, kobj);
+ /* remember our module structure */
+ drv->p->mkobj = mk;
+ /* kset_find_obj took a reference */
+ kobject_put(mkobj);
+ }
+ }
+
+ if (!mk)
+ return;
+
+ /* Don't check return codes; these calls are idempotent */
+ no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module");
+ driver_name = make_driver_name(drv);
+ if (driver_name) {
+ module_create_drivers_dir(mk);
+ no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
+ driver_name);
+ kfree(driver_name);
+ }
+}
+
+void module_remove_driver(struct device_driver *drv)
+{
+ struct module_kobject *mk = NULL;
+ char *driver_name;
+
+ if (!drv)
+ return;
+
+ sysfs_remove_link(&drv->p->kobj, "module");
+
+ if (drv->owner)
+ mk = &drv->owner->mkobj;
+ else if (drv->p->mkobj)
+ mk = drv->p->mkobj;
+ if (mk && mk->drivers_dir) {
+ driver_name = make_driver_name(drv);
+ if (driver_name) {
+ sysfs_remove_link(mk->drivers_dir, driver_name);
+ kfree(driver_name);
+ }
+ }
+}
diff --git a/drivers/base/node.c b/drivers/base/node.c
new file mode 100644
index 0000000..f520709
--- /dev/null
+++ b/drivers/base/node.c
@@ -0,0 +1,373 @@
+/*
+ * drivers/base/node.c - basic Node class support
+ */
+
+#include <linux/sysdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/node.h>
+#include <linux/hugetlb.h>
+#include <linux/cpumask.h>
+#include <linux/topology.h>
+#include <linux/nodemask.h>
+#include <linux/cpu.h>
+#include <linux/device.h>
+#include <linux/swap.h>
+
+static struct sysdev_class node_class = {
+ .name = "node",
+};
+
+
+static ssize_t node_read_cpumap(struct sys_device *dev, int type, char *buf)
+{
+ struct node *node_dev = to_node(dev);
+ node_to_cpumask_ptr(mask, node_dev->sysdev.id);
+ int len;
+
+ /* 2008/04/07: buf currently PAGE_SIZE, need 9 chars per 32 bits. */
+ BUILD_BUG_ON((NR_CPUS/32 * 9) > (PAGE_SIZE-1));
+
+ len = type?
+ cpulist_scnprintf(buf, PAGE_SIZE-2, *mask):
+ cpumask_scnprintf(buf, PAGE_SIZE-2, *mask);
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ return len;
+}
+
+static inline ssize_t node_read_cpumask(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ return node_read_cpumap(dev, 0, buf);
+}
+static inline ssize_t node_read_cpulist(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ return node_read_cpumap(dev, 1, buf);
+}
+
+static SYSDEV_ATTR(cpumap, S_IRUGO, node_read_cpumask, NULL);
+static SYSDEV_ATTR(cpulist, S_IRUGO, node_read_cpulist, NULL);
+
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+static ssize_t node_read_meminfo(struct sys_device * dev,
+ struct sysdev_attribute *attr, char * buf)
+{
+ int n;
+ int nid = dev->id;
+ struct sysinfo i;
+
+ si_meminfo_node(&i, nid);
+
+ n = sprintf(buf, "\n"
+ "Node %d MemTotal: %8lu kB\n"
+ "Node %d MemFree: %8lu kB\n"
+ "Node %d MemUsed: %8lu kB\n"
+ "Node %d Active: %8lu kB\n"
+ "Node %d Inactive: %8lu kB\n"
+ "Node %d Active(anon): %8lu kB\n"
+ "Node %d Inactive(anon): %8lu kB\n"
+ "Node %d Active(file): %8lu kB\n"
+ "Node %d Inactive(file): %8lu kB\n"
+#ifdef CONFIG_UNEVICTABLE_LRU
+ "Node %d Unevictable: %8lu kB\n"
+ "Node %d Mlocked: %8lu kB\n"
+#endif
+#ifdef CONFIG_HIGHMEM
+ "Node %d HighTotal: %8lu kB\n"
+ "Node %d HighFree: %8lu kB\n"
+ "Node %d LowTotal: %8lu kB\n"
+ "Node %d LowFree: %8lu kB\n"
+#endif
+ "Node %d Dirty: %8lu kB\n"
+ "Node %d Writeback: %8lu kB\n"
+ "Node %d FilePages: %8lu kB\n"
+ "Node %d Mapped: %8lu kB\n"
+ "Node %d AnonPages: %8lu kB\n"
+ "Node %d PageTables: %8lu kB\n"
+ "Node %d NFS_Unstable: %8lu kB\n"
+ "Node %d Bounce: %8lu kB\n"
+ "Node %d WritebackTmp: %8lu kB\n"
+ "Node %d Slab: %8lu kB\n"
+ "Node %d SReclaimable: %8lu kB\n"
+ "Node %d SUnreclaim: %8lu kB\n",
+ nid, K(i.totalram),
+ nid, K(i.freeram),
+ nid, K(i.totalram - i.freeram),
+ nid, K(node_page_state(nid, NR_ACTIVE_ANON) +
+ node_page_state(nid, NR_ACTIVE_FILE)),
+ nid, K(node_page_state(nid, NR_INACTIVE_ANON) +
+ node_page_state(nid, NR_INACTIVE_FILE)),
+ nid, K(node_page_state(nid, NR_ACTIVE_ANON)),
+ nid, K(node_page_state(nid, NR_INACTIVE_ANON)),
+ nid, K(node_page_state(nid, NR_ACTIVE_FILE)),
+ nid, K(node_page_state(nid, NR_INACTIVE_FILE)),
+#ifdef CONFIG_UNEVICTABLE_LRU
+ nid, K(node_page_state(nid, NR_UNEVICTABLE)),
+ nid, K(node_page_state(nid, NR_MLOCK)),
+#endif
+#ifdef CONFIG_HIGHMEM
+ nid, K(i.totalhigh),
+ nid, K(i.freehigh),
+ nid, K(i.totalram - i.totalhigh),
+ nid, K(i.freeram - i.freehigh),
+#endif
+ nid, K(node_page_state(nid, NR_FILE_DIRTY)),
+ nid, K(node_page_state(nid, NR_WRITEBACK)),
+ nid, K(node_page_state(nid, NR_FILE_PAGES)),
+ nid, K(node_page_state(nid, NR_FILE_MAPPED)),
+ nid, K(node_page_state(nid, NR_ANON_PAGES)),
+ nid, K(node_page_state(nid, NR_PAGETABLE)),
+ nid, K(node_page_state(nid, NR_UNSTABLE_NFS)),
+ nid, K(node_page_state(nid, NR_BOUNCE)),
+ nid, K(node_page_state(nid, NR_WRITEBACK_TEMP)),
+ nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE) +
+ node_page_state(nid, NR_SLAB_UNRECLAIMABLE)),
+ nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE)),
+ nid, K(node_page_state(nid, NR_SLAB_UNRECLAIMABLE)));
+ n += hugetlb_report_node_meminfo(nid, buf + n);
+ return n;
+}
+
+#undef K
+static SYSDEV_ATTR(meminfo, S_IRUGO, node_read_meminfo, NULL);
+
+static ssize_t node_read_numastat(struct sys_device * dev,
+ struct sysdev_attribute *attr, char * buf)
+{
+ return sprintf(buf,
+ "numa_hit %lu\n"
+ "numa_miss %lu\n"
+ "numa_foreign %lu\n"
+ "interleave_hit %lu\n"
+ "local_node %lu\n"
+ "other_node %lu\n",
+ node_page_state(dev->id, NUMA_HIT),
+ node_page_state(dev->id, NUMA_MISS),
+ node_page_state(dev->id, NUMA_FOREIGN),
+ node_page_state(dev->id, NUMA_INTERLEAVE_HIT),
+ node_page_state(dev->id, NUMA_LOCAL),
+ node_page_state(dev->id, NUMA_OTHER));
+}
+static SYSDEV_ATTR(numastat, S_IRUGO, node_read_numastat, NULL);
+
+static ssize_t node_read_distance(struct sys_device * dev,
+ struct sysdev_attribute *attr, char * buf)
+{
+ int nid = dev->id;
+ int len = 0;
+ int i;
+
+ /* buf currently PAGE_SIZE, need ~4 chars per node */
+ BUILD_BUG_ON(MAX_NUMNODES*4 > PAGE_SIZE/2);
+
+ for_each_online_node(i)
+ len += sprintf(buf + len, "%s%d", i ? " " : "", node_distance(nid, i));
+
+ len += sprintf(buf + len, "\n");
+ return len;
+}
+static SYSDEV_ATTR(distance, S_IRUGO, node_read_distance, NULL);
+
+
+/*
+ * register_node - Setup a sysfs device for a node.
+ * @num - Node number to use when creating the device.
+ *
+ * Initialize and register the node device.
+ */
+int register_node(struct node *node, int num, struct node *parent)
+{
+ int error;
+
+ node->sysdev.id = num;
+ node->sysdev.cls = &node_class;
+ error = sysdev_register(&node->sysdev);
+
+ if (!error){
+ sysdev_create_file(&node->sysdev, &attr_cpumap);
+ sysdev_create_file(&node->sysdev, &attr_cpulist);
+ sysdev_create_file(&node->sysdev, &attr_meminfo);
+ sysdev_create_file(&node->sysdev, &attr_numastat);
+ sysdev_create_file(&node->sysdev, &attr_distance);
+
+ scan_unevictable_register_node(node);
+ }
+ return error;
+}
+
+/**
+ * unregister_node - unregister a node device
+ * @node: node going away
+ *
+ * Unregisters a node device @node. All the devices on the node must be
+ * unregistered before calling this function.
+ */
+void unregister_node(struct node *node)
+{
+ sysdev_remove_file(&node->sysdev, &attr_cpumap);
+ sysdev_remove_file(&node->sysdev, &attr_cpulist);
+ sysdev_remove_file(&node->sysdev, &attr_meminfo);
+ sysdev_remove_file(&node->sysdev, &attr_numastat);
+ sysdev_remove_file(&node->sysdev, &attr_distance);
+
+ scan_unevictable_unregister_node(node);
+
+ sysdev_unregister(&node->sysdev);
+}
+
+struct node node_devices[MAX_NUMNODES];
+
+/*
+ * register cpu under node
+ */
+int register_cpu_under_node(unsigned int cpu, unsigned int nid)
+{
+ if (node_online(nid)) {
+ struct sys_device *obj = get_cpu_sysdev(cpu);
+ if (!obj)
+ return 0;
+ return sysfs_create_link(&node_devices[nid].sysdev.kobj,
+ &obj->kobj,
+ kobject_name(&obj->kobj));
+ }
+
+ return 0;
+}
+
+int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)
+{
+ if (node_online(nid)) {
+ struct sys_device *obj = get_cpu_sysdev(cpu);
+ if (obj)
+ sysfs_remove_link(&node_devices[nid].sysdev.kobj,
+ kobject_name(&obj->kobj));
+ }
+ return 0;
+}
+
+int register_one_node(int nid)
+{
+ int error = 0;
+ int cpu;
+
+ if (node_online(nid)) {
+ int p_node = parent_node(nid);
+ struct node *parent = NULL;
+
+ if (p_node != nid)
+ parent = &node_devices[p_node];
+
+ error = register_node(&node_devices[nid], nid, parent);
+
+ /* link cpu under this node */
+ for_each_present_cpu(cpu) {
+ if (cpu_to_node(cpu) == nid)
+ register_cpu_under_node(cpu, nid);
+ }
+ }
+
+ return error;
+
+}
+
+void unregister_one_node(int nid)
+{
+ unregister_node(&node_devices[nid]);
+}
+
+/*
+ * node states attributes
+ */
+
+static ssize_t print_nodes_state(enum node_states state, char *buf)
+{
+ int n;
+
+ n = nodelist_scnprintf(buf, PAGE_SIZE, node_states[state]);
+ if (n > 0 && PAGE_SIZE > n + 1) {
+ *(buf + n++) = '\n';
+ *(buf + n++) = '\0';
+ }
+ return n;
+}
+
+static ssize_t print_nodes_possible(struct sysdev_class *class, char *buf)
+{
+ return print_nodes_state(N_POSSIBLE, buf);
+}
+
+static ssize_t print_nodes_online(struct sysdev_class *class, char *buf)
+{
+ return print_nodes_state(N_ONLINE, buf);
+}
+
+static ssize_t print_nodes_has_normal_memory(struct sysdev_class *class,
+ char *buf)
+{
+ return print_nodes_state(N_NORMAL_MEMORY, buf);
+}
+
+static ssize_t print_nodes_has_cpu(struct sysdev_class *class, char *buf)
+{
+ return print_nodes_state(N_CPU, buf);
+}
+
+static SYSDEV_CLASS_ATTR(possible, 0444, print_nodes_possible, NULL);
+static SYSDEV_CLASS_ATTR(online, 0444, print_nodes_online, NULL);
+static SYSDEV_CLASS_ATTR(has_normal_memory, 0444, print_nodes_has_normal_memory,
+ NULL);
+static SYSDEV_CLASS_ATTR(has_cpu, 0444, print_nodes_has_cpu, NULL);
+
+#ifdef CONFIG_HIGHMEM
+static ssize_t print_nodes_has_high_memory(struct sysdev_class *class,
+ char *buf)
+{
+ return print_nodes_state(N_HIGH_MEMORY, buf);
+}
+
+static SYSDEV_CLASS_ATTR(has_high_memory, 0444, print_nodes_has_high_memory,
+ NULL);
+#endif
+
+struct sysdev_class_attribute *node_state_attr[] = {
+ &attr_possible,
+ &attr_online,
+ &attr_has_normal_memory,
+#ifdef CONFIG_HIGHMEM
+ &attr_has_high_memory,
+#endif
+ &attr_has_cpu,
+};
+
+static int node_states_init(void)
+{
+ int i;
+ int err = 0;
+
+ for (i = 0; i < NR_NODE_STATES; i++) {
+ int ret;
+ ret = sysdev_class_create_file(&node_class, node_state_attr[i]);
+ if (!err)
+ err = ret;
+ }
+ return err;
+}
+
+static int __init register_node_type(void)
+{
+ int ret;
+
+ ret = sysdev_class_register(&node_class);
+ if (!ret)
+ ret = node_states_init();
+
+ /*
+ * Note: we're not going to unregister the node class if we fail
+ * to register the node state class attribute files.
+ */
+ return ret;
+}
+postcore_initcall(register_node_type);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
new file mode 100644
index 0000000..dfcbfe5
--- /dev/null
+++ b/drivers/base/platform.c
@@ -0,0 +1,984 @@
+/*
+ * platform.c - platform 'pseudo' bus for legacy devices
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * Copyright (c) 2002-3 Open Source Development Labs
+ *
+ * This file is released under the GPLv2
+ *
+ * Please see Documentation/driver-model/platform.txt for more
+ * information.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/bootmem.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "base.h"
+
+#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
+ driver))
+
+struct device platform_bus = {
+ .bus_id = "platform",
+};
+EXPORT_SYMBOL_GPL(platform_bus);
+
+/**
+ * platform_get_resource - get a resource for a device
+ * @dev: platform device
+ * @type: resource type
+ * @num: resource index
+ */
+struct resource *platform_get_resource(struct platform_device *dev,
+ unsigned int type, unsigned int num)
+{
+ int i;
+
+ for (i = 0; i < dev->num_resources; i++) {
+ struct resource *r = &dev->resource[i];
+
+ if (type == resource_type(r) && num-- == 0)
+ return r;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(platform_get_resource);
+
+/**
+ * platform_get_irq - get an IRQ for a device
+ * @dev: platform device
+ * @num: IRQ number index
+ */
+int platform_get_irq(struct platform_device *dev, unsigned int num)
+{
+ struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);
+
+ return r ? r->start : -ENXIO;
+}
+EXPORT_SYMBOL_GPL(platform_get_irq);
+
+/**
+ * platform_get_resource_byname - get a resource for a device by name
+ * @dev: platform device
+ * @type: resource type
+ * @name: resource name
+ */
+struct resource *platform_get_resource_byname(struct platform_device *dev,
+ unsigned int type, char *name)
+{
+ int i;
+
+ for (i = 0; i < dev->num_resources; i++) {
+ struct resource *r = &dev->resource[i];
+
+ if (type == resource_type(r) && !strcmp(r->name, name))
+ return r;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(platform_get_resource_byname);
+
+/**
+ * platform_get_irq - get an IRQ for a device
+ * @dev: platform device
+ * @name: IRQ name
+ */
+int platform_get_irq_byname(struct platform_device *dev, char *name)
+{
+ struct resource *r = platform_get_resource_byname(dev, IORESOURCE_IRQ,
+ name);
+
+ return r ? r->start : -ENXIO;
+}
+EXPORT_SYMBOL_GPL(platform_get_irq_byname);
+
+/**
+ * platform_add_devices - add a numbers of platform devices
+ * @devs: array of platform devices to add
+ * @num: number of platform devices in array
+ */
+int platform_add_devices(struct platform_device **devs, int num)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < num; i++) {
+ ret = platform_device_register(devs[i]);
+ if (ret) {
+ while (--i >= 0)
+ platform_device_unregister(devs[i]);
+ break;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(platform_add_devices);
+
+struct platform_object {
+ struct platform_device pdev;
+ char name[1];
+};
+
+/**
+ * platform_device_put
+ * @pdev: platform device to free
+ *
+ * Free all memory associated with a platform device. This function must
+ * _only_ be externally called in error cases. All other usage is a bug.
+ */
+void platform_device_put(struct platform_device *pdev)
+{
+ if (pdev)
+ put_device(&pdev->dev);
+}
+EXPORT_SYMBOL_GPL(platform_device_put);
+
+static void platform_device_release(struct device *dev)
+{
+ struct platform_object *pa = container_of(dev, struct platform_object,
+ pdev.dev);
+
+ kfree(pa->pdev.dev.platform_data);
+ kfree(pa->pdev.resource);
+ kfree(pa);
+}
+
+/**
+ * platform_device_alloc
+ * @name: base name of the device we're adding
+ * @id: instance id
+ *
+ * Create a platform device object which can have other objects attached
+ * to it, and which will have attached objects freed when it is released.
+ */
+struct platform_device *platform_device_alloc(const char *name, int id)
+{
+ struct platform_object *pa;
+
+ pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
+ if (pa) {
+ strcpy(pa->name, name);
+ pa->pdev.name = pa->name;
+ pa->pdev.id = id;
+ device_initialize(&pa->pdev.dev);
+ pa->pdev.dev.release = platform_device_release;
+ }
+
+ return pa ? &pa->pdev : NULL;
+}
+EXPORT_SYMBOL_GPL(platform_device_alloc);
+
+/**
+ * platform_device_add_resources
+ * @pdev: platform device allocated by platform_device_alloc to add resources to
+ * @res: set of resources that needs to be allocated for the device
+ * @num: number of resources
+ *
+ * Add a copy of the resources to the platform device. The memory
+ * associated with the resources will be freed when the platform device is
+ * released.
+ */
+int platform_device_add_resources(struct platform_device *pdev,
+ struct resource *res, unsigned int num)
+{
+ struct resource *r;
+
+ r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL);
+ if (r) {
+ memcpy(r, res, sizeof(struct resource) * num);
+ pdev->resource = r;
+ pdev->num_resources = num;
+ }
+ return r ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(platform_device_add_resources);
+
+/**
+ * platform_device_add_data
+ * @pdev: platform device allocated by platform_device_alloc to add resources to
+ * @data: platform specific data for this platform device
+ * @size: size of platform specific data
+ *
+ * Add a copy of platform specific data to the platform device's
+ * platform_data pointer. The memory associated with the platform data
+ * will be freed when the platform device is released.
+ */
+int platform_device_add_data(struct platform_device *pdev, const void *data,
+ size_t size)
+{
+ void *d;
+
+ d = kmalloc(size, GFP_KERNEL);
+ if (d) {
+ memcpy(d, data, size);
+ pdev->dev.platform_data = d;
+ }
+ return d ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(platform_device_add_data);
+
+/**
+ * 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)
+ 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 (resource_type(r) == IORESOURCE_MEM)
+ p = &iomem_resource;
+ else if (resource_type(r) == 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) {
+ struct resource *r = &pdev->resource[i];
+ unsigned long type = resource_type(r);
+
+ if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
+ release_resource(r);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(platform_device_add);
+
+/**
+ * platform_device_del - remove a platform-level device
+ * @pdev: platform device we're removing
+ *
+ * Note that this function will also release all memory- and port-based
+ * resources owned by the device (@dev->resource). This function must
+ * _only_ be externally called in error cases. All other usage is a bug.
+ */
+void platform_device_del(struct platform_device *pdev)
+{
+ int i;
+
+ if (pdev) {
+ device_del(&pdev->dev);
+
+ for (i = 0; i < pdev->num_resources; i++) {
+ struct resource *r = &pdev->resource[i];
+ unsigned long type = resource_type(r);
+
+ if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
+ release_resource(r);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(platform_device_del);
+
+/**
+ * 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);
+}
+EXPORT_SYMBOL_GPL(platform_device_register);
+
+/**
+ * platform_device_unregister - unregister a platform-level device
+ * @pdev: platform device we're unregistering
+ *
+ * Unregistration is done in 2 steps. First we release all resources
+ * and remove it from the subsystem, then we drop reference count by
+ * calling platform_device_put().
+ */
+void platform_device_unregister(struct platform_device *pdev)
+{
+ platform_device_del(pdev);
+ platform_device_put(pdev);
+}
+EXPORT_SYMBOL_GPL(platform_device_unregister);
+
+/**
+ * platform_device_register_simple
+ * @name: base name of the device we're adding
+ * @id: instance id
+ * @res: set of resources that needs to be allocated for the device
+ * @num: number of resources
+ *
+ * This function creates a simple platform device that requires minimal
+ * resource and memory management. Canned release function freeing memory
+ * allocated for the device allows drivers using such devices to be
+ * unloaded without waiting for the last reference to the device to be
+ * dropped.
+ *
+ * This interface is primarily intended for use with legacy drivers which
+ * probe hardware directly. Because such drivers create sysfs device nodes
+ * themselves, rather than letting system infrastructure handle such device
+ * enumeration tasks, they don't fully conform to the Linux driver model.
+ * In particular, when such drivers are built as modules, they can't be
+ * "hotplugged".
+ */
+struct platform_device *platform_device_register_simple(const char *name,
+ int id,
+ struct resource *res,
+ unsigned int num)
+{
+ struct platform_device *pdev;
+ int retval;
+
+ pdev = platform_device_alloc(name, id);
+ if (!pdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ if (num) {
+ retval = platform_device_add_resources(pdev, res, num);
+ if (retval)
+ goto error;
+ }
+
+ retval = platform_device_add(pdev);
+ if (retval)
+ goto error;
+
+ return pdev;
+
+error:
+ platform_device_put(pdev);
+ return ERR_PTR(retval);
+}
+EXPORT_SYMBOL_GPL(platform_device_register_simple);
+
+/**
+ * platform_device_register_data
+ * @parent: parent device for the device we're adding
+ * @name: base name of the device we're adding
+ * @id: instance id
+ * @data: platform specific data for this platform device
+ * @size: size of platform specific data
+ *
+ * This function creates a simple platform device that requires minimal
+ * resource and memory management. Canned release function freeing memory
+ * allocated for the device allows drivers using such devices to be
+ * unloaded without waiting for the last reference to the device to be
+ * dropped.
+ */
+struct platform_device *platform_device_register_data(
+ struct device *parent,
+ const char *name, int id,
+ const void *data, size_t size)
+{
+ struct platform_device *pdev;
+ int retval;
+
+ pdev = platform_device_alloc(name, id);
+ if (!pdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ pdev->dev.parent = parent;
+
+ if (size) {
+ retval = platform_device_add_data(pdev, data, size);
+ if (retval)
+ goto error;
+ }
+
+ retval = platform_device_add(pdev);
+ if (retval)
+ goto error;
+
+ return pdev;
+
+error:
+ platform_device_put(pdev);
+ return ERR_PTR(retval);
+}
+
+static int platform_drv_probe(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->probe(dev);
+}
+
+static int platform_drv_probe_fail(struct device *_dev)
+{
+ return -ENXIO;
+}
+
+static int platform_drv_remove(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->remove(dev);
+}
+
+static void platform_drv_shutdown(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ drv->shutdown(dev);
+}
+
+static int platform_drv_suspend(struct device *_dev, pm_message_t state)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->suspend(dev, state);
+}
+
+static int platform_drv_resume(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->resume(dev);
+}
+
+/**
+ * platform_driver_register
+ * @drv: platform driver structure
+ */
+int platform_driver_register(struct platform_driver *drv)
+{
+ drv->driver.bus = &platform_bus_type;
+ 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;
+ if (drv->suspend)
+ drv->driver.suspend = platform_drv_suspend;
+ if (drv->resume)
+ drv->driver.resume = platform_drv_resume;
+ if (drv->pm)
+ drv->driver.pm = &drv->pm->base;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(platform_driver_register);
+
+/**
+ * platform_driver_unregister
+ * @drv: platform driver structure
+ */
+void platform_driver_unregister(struct platform_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(platform_driver_unregister);
+
+/**
+ * platform_driver_probe - register driver for non-hotpluggable device
+ * @drv: platform driver structure
+ * @probe: the driver probe routine, probably from an __init section
+ *
+ * Use this instead of platform_driver_register() when you know the device
+ * is not hotpluggable and has already been registered, and you want to
+ * remove its run-once probe() infrastructure from memory after the driver
+ * has bound to the device.
+ *
+ * One typical use for this would be with drivers for controllers integrated
+ * into system-on-chip processors, where the controller devices have been
+ * configured as part of board setup.
+ *
+ * Returns zero if the driver registered and bound to a device, else returns
+ * a negative error code and with the driver not registered.
+ */
+int __init_or_module platform_driver_probe(struct platform_driver *drv,
+ int (*probe)(struct platform_device *))
+{
+ int retval, code;
+
+ /* temporary section violation during probe() */
+ drv->probe = probe;
+ retval = code = platform_driver_register(drv);
+
+ /* Fixup that section violation, being paranoid about code scanning
+ * the list of drivers in order to probe new devices. Check to see
+ * if the probe was successful, and make sure any forced probes of
+ * new devices fail.
+ */
+ spin_lock(&platform_bus_type.p->klist_drivers.k_lock);
+ drv->probe = NULL;
+ if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
+ retval = -ENODEV;
+ drv->driver.probe = platform_drv_probe_fail;
+ spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);
+
+ if (code != retval)
+ platform_driver_unregister(drv);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(platform_driver_probe);
+
+/* modalias support enables more hands-off userspace setup:
+ * (a) environment variable lets new-style hotplug events work once system is
+ * fully running: "modprobe $MODALIAS"
+ * (b) sysfs attribute lets new-style coldplug recover from hotplug events
+ * mishandled before system is fully running: "modprobe $(cat modalias)"
+ */
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute platform_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ add_uevent_var(env, "MODALIAS=platform:%s", pdev->name);
+ return 0;
+}
+
+/**
+ * platform_match - bind platform device to platform driver.
+ * @dev: device.
+ * @drv: driver.
+ *
+ * Platform device IDs are assumed to be encoded like this:
+ * "<name><instance>", where <name> is a short description of the type of
+ * device, like "pci" or "floppy", and <instance> is the enumerated
+ * instance of the device, like '0' or '42'. Driver IDs are simply
+ * "<name>". So, extract the <name> from the platform_device structure,
+ * and compare it against the name of the driver. Return whether they match
+ * or not.
+ */
+static int platform_match(struct device *dev, struct device_driver *drv)
+{
+ struct platform_device *pdev;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->suspend)
+ ret = dev->driver->suspend(dev, mesg);
+
+ return ret;
+}
+
+static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg)
+{
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct platform_device *pdev;
+ int ret = 0;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ if (dev->driver && drv->suspend_late)
+ ret = drv->suspend_late(pdev, mesg);
+
+ return ret;
+}
+
+static int platform_legacy_resume_early(struct device *dev)
+{
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct platform_device *pdev;
+ int ret = 0;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ if (dev->driver && drv->resume_early)
+ ret = drv->resume_early(pdev);
+
+ return ret;
+}
+
+static int platform_legacy_resume(struct device *dev)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->resume)
+ ret = dev->driver->resume(dev);
+
+ return ret;
+}
+
+static int platform_pm_prepare(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm && drv->pm->prepare)
+ ret = drv->pm->prepare(dev);
+
+ return ret;
+}
+
+static void platform_pm_complete(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv && drv->pm && drv->pm->complete)
+ drv->pm->complete(dev);
+}
+
+#ifdef CONFIG_SUSPEND
+
+static int platform_pm_suspend(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->suspend)
+ ret = drv->pm->suspend(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_SUSPEND);
+ }
+
+ return ret;
+}
+
+static int platform_pm_suspend_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->suspend_noirq)
+ ret = pdrv->pm->suspend_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND);
+ }
+
+ return ret;
+}
+
+static int platform_pm_resume(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->resume)
+ ret = drv->pm->resume(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_resume_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->resume_noirq)
+ ret = pdrv->pm->resume_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define platform_pm_suspend NULL
+#define platform_pm_resume NULL
+#define platform_pm_suspend_noirq NULL
+#define platform_pm_resume_noirq NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int platform_pm_freeze(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->freeze)
+ ret = drv->pm->freeze(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_FREEZE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_freeze_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->freeze_noirq)
+ ret = pdrv->pm->freeze_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_FREEZE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_thaw(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->thaw)
+ ret = drv->pm->thaw(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_thaw_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->thaw_noirq)
+ ret = pdrv->pm->thaw_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_poweroff(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->poweroff)
+ ret = drv->pm->poweroff(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_HIBERNATE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_poweroff_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->poweroff_noirq)
+ ret = pdrv->pm->poweroff_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_restore(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->restore)
+ ret = drv->pm->restore(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_restore_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->restore_noirq)
+ ret = pdrv->pm->restore_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+#else /* !CONFIG_HIBERNATION */
+
+#define platform_pm_freeze NULL
+#define platform_pm_thaw NULL
+#define platform_pm_poweroff NULL
+#define platform_pm_restore NULL
+#define platform_pm_freeze_noirq NULL
+#define platform_pm_thaw_noirq NULL
+#define platform_pm_poweroff_noirq NULL
+#define platform_pm_restore_noirq NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+static struct pm_ext_ops platform_pm_ops = {
+ .base = {
+ .prepare = platform_pm_prepare,
+ .complete = platform_pm_complete,
+ .suspend = platform_pm_suspend,
+ .resume = platform_pm_resume,
+ .freeze = platform_pm_freeze,
+ .thaw = platform_pm_thaw,
+ .poweroff = platform_pm_poweroff,
+ .restore = platform_pm_restore,
+ },
+ .suspend_noirq = platform_pm_suspend_noirq,
+ .resume_noirq = platform_pm_resume_noirq,
+ .freeze_noirq = platform_pm_freeze_noirq,
+ .thaw_noirq = platform_pm_thaw_noirq,
+ .poweroff_noirq = platform_pm_poweroff_noirq,
+ .restore_noirq = platform_pm_restore_noirq,
+};
+
+#define PLATFORM_PM_OPS_PTR &platform_pm_ops
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define PLATFORM_PM_OPS_PTR NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
+struct bus_type platform_bus_type = {
+ .name = "platform",
+ .dev_attrs = platform_dev_attrs,
+ .match = platform_match,
+ .uevent = platform_uevent,
+ .pm = PLATFORM_PM_OPS_PTR,
+};
+EXPORT_SYMBOL_GPL(platform_bus_type);
+
+int __init platform_bus_init(void)
+{
+ int error;
+
+ error = device_register(&platform_bus);
+ if (error)
+ return error;
+ error = bus_register(&platform_bus_type);
+ if (error)
+ device_unregister(&platform_bus);
+ return error;
+}
+
+#ifndef ARCH_HAS_DMA_GET_REQUIRED_MASK
+u64 dma_get_required_mask(struct device *dev)
+{
+ u32 low_totalram = ((max_pfn - 1) << PAGE_SHIFT);
+ u32 high_totalram = ((max_pfn - 1) >> (32 - PAGE_SHIFT));
+ u64 mask;
+
+ if (!high_totalram) {
+ /* convert to mask just covering totalram */
+ low_totalram = (1 << (fls(low_totalram) - 1));
+ low_totalram += low_totalram - 1;
+ mask = low_totalram;
+ } else {
+ high_totalram = (1 << (fls(high_totalram) - 1));
+ high_totalram += high_totalram - 1;
+ mask = (((u64)high_totalram) << 32) + 0xffffffff;
+ }
+ return mask;
+}
+EXPORT_SYMBOL_GPL(dma_get_required_mask);
+#endif
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
new file mode 100644
index 0000000..911208b
--- /dev/null
+++ b/drivers/base/power/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_PM) += sysfs.o
+obj-$(CONFIG_PM_SLEEP) += main.o
+obj-$(CONFIG_PM_TRACE_RTC) += trace.o
+
+ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
+ccflags-$(CONFIG_PM_VERBOSE) += -DDEBUG
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
new file mode 100644
index 0000000..692c20b
--- /dev/null
+++ b/drivers/base/power/main.c
@@ -0,0 +1,784 @@
+/*
+ * drivers/base/power/main.c - Where the driver meets power management.
+ *
+ * Copyright (c) 2003 Patrick Mochel
+ * Copyright (c) 2003 Open Source Development Lab
+ *
+ * This file is released under the GPLv2
+ *
+ *
+ * The driver model core calls device_pm_add() when a device is registered.
+ * This will intialize the embedded device_pm_info object in the device
+ * and add it to the list of power-controlled devices. sysfs entries for
+ * controlling device power management will also be added.
+ *
+ * A separate list is used for keeping track of power info, because the power
+ * domain dependencies may differ from the ancestral dependencies that the
+ * subsystem list maintains.
+ */
+
+#include <linux/device.h>
+#include <linux/kallsyms.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/resume-trace.h>
+#include <linux/rwsem.h>
+
+#include "../base.h"
+#include "power.h"
+
+/*
+ * The entries in the dpm_list list are in a depth first order, simply
+ * because children are guaranteed to be discovered after parents, and
+ * are inserted at the back of the list on discovery.
+ *
+ * Since device_pm_add() may be called with a device semaphore held,
+ * we must never try to acquire a device semaphore while holding
+ * dpm_list_mutex.
+ */
+
+LIST_HEAD(dpm_list);
+
+static DEFINE_MUTEX(dpm_list_mtx);
+
+/*
+ * Set once the preparation of devices for a PM transition has started, reset
+ * before starting to resume devices. Protected by dpm_list_mtx.
+ */
+static bool transition_started;
+
+/**
+ * device_pm_lock - lock the list of active devices used by the PM core
+ */
+void device_pm_lock(void)
+{
+ mutex_lock(&dpm_list_mtx);
+}
+
+/**
+ * device_pm_unlock - unlock the list of active devices used by the PM core
+ */
+void device_pm_unlock(void)
+{
+ mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ * device_pm_add - add a device to the list of active devices
+ * @dev: Device to be added to the list
+ */
+void device_pm_add(struct device *dev)
+{
+ pr_debug("PM: Adding info for %s:%s\n",
+ dev->bus ? dev->bus->name : "No Bus",
+ kobject_name(&dev->kobj));
+ mutex_lock(&dpm_list_mtx);
+ if (dev->parent) {
+ if (dev->parent->power.status >= DPM_SUSPENDING)
+ dev_warn(dev, "parent %s should not be sleeping\n",
+ dev->parent->bus_id);
+ } else if (transition_started) {
+ /*
+ * We refuse to register parentless devices while a PM
+ * transition is in progress in order to avoid leaving them
+ * unhandled down the road
+ */
+ dev_WARN(dev, "Parentless device registered during a PM transaction\n");
+ }
+
+ list_add_tail(&dev->power.entry, &dpm_list);
+ mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ * device_pm_remove - remove a device from the list of active devices
+ * @dev: Device to be removed from the list
+ *
+ * This function also removes the device's PM-related sysfs attributes.
+ */
+void device_pm_remove(struct device *dev)
+{
+ pr_debug("PM: Removing info for %s:%s\n",
+ dev->bus ? dev->bus->name : "No Bus",
+ kobject_name(&dev->kobj));
+ mutex_lock(&dpm_list_mtx);
+ list_del_init(&dev->power.entry);
+ mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ * pm_op - execute the PM operation appropiate for given PM event
+ * @dev: Device.
+ * @ops: PM operations to choose from.
+ * @state: PM transition of the system being carried out.
+ */
+static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state)
+{
+ int error = 0;
+
+ switch (state.event) {
+#ifdef CONFIG_SUSPEND
+ case PM_EVENT_SUSPEND:
+ if (ops->suspend) {
+ error = ops->suspend(dev);
+ suspend_report_result(ops->suspend, error);
+ }
+ break;
+ case PM_EVENT_RESUME:
+ if (ops->resume) {
+ error = ops->resume(dev);
+ suspend_report_result(ops->resume, error);
+ }
+ break;
+#endif /* CONFIG_SUSPEND */
+#ifdef CONFIG_HIBERNATION
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_QUIESCE:
+ if (ops->freeze) {
+ error = ops->freeze(dev);
+ suspend_report_result(ops->freeze, error);
+ }
+ break;
+ case PM_EVENT_HIBERNATE:
+ if (ops->poweroff) {
+ error = ops->poweroff(dev);
+ suspend_report_result(ops->poweroff, error);
+ }
+ break;
+ case PM_EVENT_THAW:
+ case PM_EVENT_RECOVER:
+ if (ops->thaw) {
+ error = ops->thaw(dev);
+ suspend_report_result(ops->thaw, error);
+ }
+ break;
+ case PM_EVENT_RESTORE:
+ if (ops->restore) {
+ error = ops->restore(dev);
+ suspend_report_result(ops->restore, error);
+ }
+ break;
+#endif /* CONFIG_HIBERNATION */
+ default:
+ error = -EINVAL;
+ }
+ return error;
+}
+
+/**
+ * pm_noirq_op - execute the PM operation appropiate for given PM event
+ * @dev: Device.
+ * @ops: PM operations to choose from.
+ * @state: PM transition of the system being carried out.
+ *
+ * The operation is executed with interrupts disabled by the only remaining
+ * functional CPU in the system.
+ */
+static int pm_noirq_op(struct device *dev, struct pm_ext_ops *ops,
+ pm_message_t state)
+{
+ int error = 0;
+
+ switch (state.event) {
+#ifdef CONFIG_SUSPEND
+ case PM_EVENT_SUSPEND:
+ if (ops->suspend_noirq) {
+ error = ops->suspend_noirq(dev);
+ suspend_report_result(ops->suspend_noirq, error);
+ }
+ break;
+ case PM_EVENT_RESUME:
+ if (ops->resume_noirq) {
+ error = ops->resume_noirq(dev);
+ suspend_report_result(ops->resume_noirq, error);
+ }
+ break;
+#endif /* CONFIG_SUSPEND */
+#ifdef CONFIG_HIBERNATION
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_QUIESCE:
+ if (ops->freeze_noirq) {
+ error = ops->freeze_noirq(dev);
+ suspend_report_result(ops->freeze_noirq, error);
+ }
+ break;
+ case PM_EVENT_HIBERNATE:
+ if (ops->poweroff_noirq) {
+ error = ops->poweroff_noirq(dev);
+ suspend_report_result(ops->poweroff_noirq, error);
+ }
+ break;
+ case PM_EVENT_THAW:
+ case PM_EVENT_RECOVER:
+ if (ops->thaw_noirq) {
+ error = ops->thaw_noirq(dev);
+ suspend_report_result(ops->thaw_noirq, error);
+ }
+ break;
+ case PM_EVENT_RESTORE:
+ if (ops->restore_noirq) {
+ error = ops->restore_noirq(dev);
+ suspend_report_result(ops->restore_noirq, error);
+ }
+ break;
+#endif /* CONFIG_HIBERNATION */
+ default:
+ error = -EINVAL;
+ }
+ return error;
+}
+
+static char *pm_verb(int event)
+{
+ switch (event) {
+ case PM_EVENT_SUSPEND:
+ return "suspend";
+ case PM_EVENT_RESUME:
+ return "resume";
+ case PM_EVENT_FREEZE:
+ return "freeze";
+ case PM_EVENT_QUIESCE:
+ return "quiesce";
+ case PM_EVENT_HIBERNATE:
+ return "hibernate";
+ case PM_EVENT_THAW:
+ return "thaw";
+ case PM_EVENT_RESTORE:
+ return "restore";
+ case PM_EVENT_RECOVER:
+ return "recover";
+ default:
+ return "(unknown PM event)";
+ }
+}
+
+static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info)
+{
+ dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event),
+ ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ?
+ ", may wakeup" : "");
+}
+
+static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
+ int error)
+{
+ printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n",
+ kobject_name(&dev->kobj), pm_verb(state.event), info, error);
+}
+
+/*------------------------- Resume routines -------------------------*/
+
+/**
+ * resume_device_noirq - Power on one device (early resume).
+ * @dev: Device.
+ * @state: PM transition of the system being carried out.
+ *
+ * Must be called with interrupts disabled.
+ */
+static int resume_device_noirq(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ TRACE_DEVICE(dev);
+ TRACE_RESUME(0);
+
+ if (!dev->bus)
+ goto End;
+
+ if (dev->bus->pm) {
+ pm_dev_dbg(dev, state, "EARLY ");
+ error = pm_noirq_op(dev, dev->bus->pm, state);
+ } else if (dev->bus->resume_early) {
+ pm_dev_dbg(dev, state, "legacy EARLY ");
+ error = dev->bus->resume_early(dev);
+ }
+ End:
+ TRACE_RESUME(error);
+ return error;
+}
+
+/**
+ * dpm_power_up - Power on all regular (non-sysdev) devices.
+ * @state: PM transition of the system being carried out.
+ *
+ * Execute the appropriate "noirq resume" callback for all devices marked
+ * as DPM_OFF_IRQ.
+ *
+ * Must be called with interrupts disabled and only one CPU running.
+ */
+static void dpm_power_up(pm_message_t state)
+{
+ struct device *dev;
+
+ list_for_each_entry(dev, &dpm_list, power.entry)
+ if (dev->power.status > DPM_OFF) {
+ int error;
+
+ dev->power.status = DPM_OFF;
+ error = resume_device_noirq(dev, state);
+ if (error)
+ pm_dev_err(dev, state, " early", error);
+ }
+}
+
+/**
+ * device_power_up - Turn on all devices that need special attention.
+ * @state: PM transition of the system being carried out.
+ *
+ * Power on system devices, then devices that required we shut them down
+ * with interrupts disabled.
+ *
+ * Must be called with interrupts disabled.
+ */
+void device_power_up(pm_message_t state)
+{
+ sysdev_resume();
+ dpm_power_up(state);
+}
+EXPORT_SYMBOL_GPL(device_power_up);
+
+/**
+ * resume_device - Restore state for one device.
+ * @dev: Device.
+ * @state: PM transition of the system being carried out.
+ */
+static int resume_device(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ TRACE_DEVICE(dev);
+ TRACE_RESUME(0);
+
+ down(&dev->sem);
+
+ if (dev->bus) {
+ if (dev->bus->pm) {
+ pm_dev_dbg(dev, state, "");
+ error = pm_op(dev, &dev->bus->pm->base, state);
+ } else if (dev->bus->resume) {
+ pm_dev_dbg(dev, state, "legacy ");
+ error = dev->bus->resume(dev);
+ }
+ if (error)
+ goto End;
+ }
+
+ if (dev->type) {
+ if (dev->type->pm) {
+ pm_dev_dbg(dev, state, "type ");
+ error = pm_op(dev, dev->type->pm, state);
+ } else if (dev->type->resume) {
+ pm_dev_dbg(dev, state, "legacy type ");
+ error = dev->type->resume(dev);
+ }
+ if (error)
+ goto End;
+ }
+
+ if (dev->class) {
+ if (dev->class->pm) {
+ pm_dev_dbg(dev, state, "class ");
+ error = pm_op(dev, dev->class->pm, state);
+ } else if (dev->class->resume) {
+ pm_dev_dbg(dev, state, "legacy class ");
+ error = dev->class->resume(dev);
+ }
+ }
+ End:
+ up(&dev->sem);
+
+ TRACE_RESUME(error);
+ return error;
+}
+
+/**
+ * dpm_resume - Resume every device.
+ * @state: PM transition of the system being carried out.
+ *
+ * Execute the appropriate "resume" callback for all devices the status of
+ * which indicates that they are inactive.
+ */
+static void dpm_resume(pm_message_t state)
+{
+ struct list_head list;
+
+ INIT_LIST_HEAD(&list);
+ mutex_lock(&dpm_list_mtx);
+ transition_started = false;
+ while (!list_empty(&dpm_list)) {
+ struct device *dev = to_device(dpm_list.next);
+
+ get_device(dev);
+ if (dev->power.status >= DPM_OFF) {
+ int error;
+
+ dev->power.status = DPM_RESUMING;
+ mutex_unlock(&dpm_list_mtx);
+
+ error = resume_device(dev, state);
+
+ mutex_lock(&dpm_list_mtx);
+ if (error)
+ pm_dev_err(dev, state, "", error);
+ } else if (dev->power.status == DPM_SUSPENDING) {
+ /* Allow new children of the device to be registered */
+ dev->power.status = DPM_RESUMING;
+ }
+ if (!list_empty(&dev->power.entry))
+ list_move_tail(&dev->power.entry, &list);
+ put_device(dev);
+ }
+ list_splice(&list, &dpm_list);
+ mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ * complete_device - Complete a PM transition for given device
+ * @dev: Device.
+ * @state: PM transition of the system being carried out.
+ */
+static void complete_device(struct device *dev, pm_message_t state)
+{
+ down(&dev->sem);
+
+ if (dev->class && dev->class->pm && dev->class->pm->complete) {
+ pm_dev_dbg(dev, state, "completing class ");
+ dev->class->pm->complete(dev);
+ }
+
+ if (dev->type && dev->type->pm && dev->type->pm->complete) {
+ pm_dev_dbg(dev, state, "completing type ");
+ dev->type->pm->complete(dev);
+ }
+
+ if (dev->bus && dev->bus->pm && dev->bus->pm->base.complete) {
+ pm_dev_dbg(dev, state, "completing ");
+ dev->bus->pm->base.complete(dev);
+ }
+
+ up(&dev->sem);
+}
+
+/**
+ * dpm_complete - Complete a PM transition for all devices.
+ * @state: PM transition of the system being carried out.
+ *
+ * Execute the ->complete() callbacks for all devices that are not marked
+ * as DPM_ON.
+ */
+static void dpm_complete(pm_message_t state)
+{
+ struct list_head list;
+
+ INIT_LIST_HEAD(&list);
+ mutex_lock(&dpm_list_mtx);
+ while (!list_empty(&dpm_list)) {
+ struct device *dev = to_device(dpm_list.prev);
+
+ get_device(dev);
+ if (dev->power.status > DPM_ON) {
+ dev->power.status = DPM_ON;
+ mutex_unlock(&dpm_list_mtx);
+
+ complete_device(dev, state);
+
+ mutex_lock(&dpm_list_mtx);
+ }
+ if (!list_empty(&dev->power.entry))
+ list_move(&dev->power.entry, &list);
+ put_device(dev);
+ }
+ list_splice(&list, &dpm_list);
+ mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ * device_resume - Restore state of each device in system.
+ * @state: PM transition of the system being carried out.
+ *
+ * Resume all the devices, unlock them all, and allow new
+ * devices to be registered once again.
+ */
+void device_resume(pm_message_t state)
+{
+ might_sleep();
+ dpm_resume(state);
+ dpm_complete(state);
+}
+EXPORT_SYMBOL_GPL(device_resume);
+
+
+/*------------------------- Suspend routines -------------------------*/
+
+/**
+ * resume_event - return a PM message representing the resume event
+ * corresponding to given sleep state.
+ * @sleep_state: PM message representing a sleep state.
+ */
+static pm_message_t resume_event(pm_message_t sleep_state)
+{
+ switch (sleep_state.event) {
+ case PM_EVENT_SUSPEND:
+ return PMSG_RESUME;
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_QUIESCE:
+ return PMSG_RECOVER;
+ case PM_EVENT_HIBERNATE:
+ return PMSG_RESTORE;
+ }
+ return PMSG_ON;
+}
+
+/**
+ * suspend_device_noirq - Shut down one device (late suspend).
+ * @dev: Device.
+ * @state: PM transition of the system being carried out.
+ *
+ * This is called with interrupts off and only a single CPU running.
+ */
+static int suspend_device_noirq(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ if (!dev->bus)
+ return 0;
+
+ if (dev->bus->pm) {
+ pm_dev_dbg(dev, state, "LATE ");
+ error = pm_noirq_op(dev, dev->bus->pm, state);
+ } else if (dev->bus->suspend_late) {
+ pm_dev_dbg(dev, state, "legacy LATE ");
+ error = dev->bus->suspend_late(dev, state);
+ suspend_report_result(dev->bus->suspend_late, error);
+ }
+ return error;
+}
+
+/**
+ * device_power_down - Shut down special devices.
+ * @state: PM transition of the system being carried out.
+ *
+ * Power down devices that require interrupts to be disabled.
+ * Then power down system devices.
+ *
+ * Must be called with interrupts disabled and only one CPU running.
+ */
+int device_power_down(pm_message_t state)
+{
+ struct device *dev;
+ int error = 0;
+
+ list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
+ error = suspend_device_noirq(dev, state);
+ if (error) {
+ pm_dev_err(dev, state, " late", error);
+ break;
+ }
+ dev->power.status = DPM_OFF_IRQ;
+ }
+ if (!error)
+ error = sysdev_suspend(state);
+ if (error)
+ dpm_power_up(resume_event(state));
+ return error;
+}
+EXPORT_SYMBOL_GPL(device_power_down);
+
+/**
+ * suspend_device - Save state of one device.
+ * @dev: Device.
+ * @state: PM transition of the system being carried out.
+ */
+static int suspend_device(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ down(&dev->sem);
+
+ if (dev->class) {
+ if (dev->class->pm) {
+ pm_dev_dbg(dev, state, "class ");
+ error = pm_op(dev, dev->class->pm, state);
+ } else if (dev->class->suspend) {
+ pm_dev_dbg(dev, state, "legacy class ");
+ error = dev->class->suspend(dev, state);
+ suspend_report_result(dev->class->suspend, error);
+ }
+ if (error)
+ goto End;
+ }
+
+ if (dev->type) {
+ if (dev->type->pm) {
+ pm_dev_dbg(dev, state, "type ");
+ error = pm_op(dev, dev->type->pm, state);
+ } else if (dev->type->suspend) {
+ pm_dev_dbg(dev, state, "legacy type ");
+ error = dev->type->suspend(dev, state);
+ suspend_report_result(dev->type->suspend, error);
+ }
+ if (error)
+ goto End;
+ }
+
+ if (dev->bus) {
+ if (dev->bus->pm) {
+ pm_dev_dbg(dev, state, "");
+ error = pm_op(dev, &dev->bus->pm->base, state);
+ } else if (dev->bus->suspend) {
+ pm_dev_dbg(dev, state, "legacy ");
+ error = dev->bus->suspend(dev, state);
+ suspend_report_result(dev->bus->suspend, error);
+ }
+ }
+ End:
+ up(&dev->sem);
+
+ return error;
+}
+
+/**
+ * dpm_suspend - Suspend every device.
+ * @state: PM transition of the system being carried out.
+ *
+ * Execute the appropriate "suspend" callbacks for all devices.
+ */
+static int dpm_suspend(pm_message_t state)
+{
+ struct list_head list;
+ int error = 0;
+
+ INIT_LIST_HEAD(&list);
+ mutex_lock(&dpm_list_mtx);
+ while (!list_empty(&dpm_list)) {
+ struct device *dev = to_device(dpm_list.prev);
+
+ get_device(dev);
+ mutex_unlock(&dpm_list_mtx);
+
+ error = suspend_device(dev, state);
+
+ mutex_lock(&dpm_list_mtx);
+ if (error) {
+ pm_dev_err(dev, state, "", error);
+ put_device(dev);
+ break;
+ }
+ dev->power.status = DPM_OFF;
+ if (!list_empty(&dev->power.entry))
+ list_move(&dev->power.entry, &list);
+ put_device(dev);
+ }
+ list_splice(&list, dpm_list.prev);
+ mutex_unlock(&dpm_list_mtx);
+ return error;
+}
+
+/**
+ * prepare_device - Execute the ->prepare() callback(s) for given device.
+ * @dev: Device.
+ * @state: PM transition of the system being carried out.
+ */
+static int prepare_device(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ down(&dev->sem);
+
+ if (dev->bus && dev->bus->pm && dev->bus->pm->base.prepare) {
+ pm_dev_dbg(dev, state, "preparing ");
+ error = dev->bus->pm->base.prepare(dev);
+ suspend_report_result(dev->bus->pm->base.prepare, error);
+ if (error)
+ goto End;
+ }
+
+ if (dev->type && dev->type->pm && dev->type->pm->prepare) {
+ pm_dev_dbg(dev, state, "preparing type ");
+ error = dev->type->pm->prepare(dev);
+ suspend_report_result(dev->type->pm->prepare, error);
+ if (error)
+ goto End;
+ }
+
+ if (dev->class && dev->class->pm && dev->class->pm->prepare) {
+ pm_dev_dbg(dev, state, "preparing class ");
+ error = dev->class->pm->prepare(dev);
+ suspend_report_result(dev->class->pm->prepare, error);
+ }
+ End:
+ up(&dev->sem);
+
+ return error;
+}
+
+/**
+ * dpm_prepare - Prepare all devices for a PM transition.
+ * @state: PM transition of the system being carried out.
+ *
+ * Execute the ->prepare() callback for all devices.
+ */
+static int dpm_prepare(pm_message_t state)
+{
+ struct list_head list;
+ int error = 0;
+
+ INIT_LIST_HEAD(&list);
+ mutex_lock(&dpm_list_mtx);
+ transition_started = true;
+ while (!list_empty(&dpm_list)) {
+ struct device *dev = to_device(dpm_list.next);
+
+ get_device(dev);
+ dev->power.status = DPM_PREPARING;
+ mutex_unlock(&dpm_list_mtx);
+
+ error = prepare_device(dev, state);
+
+ mutex_lock(&dpm_list_mtx);
+ if (error) {
+ dev->power.status = DPM_ON;
+ if (error == -EAGAIN) {
+ put_device(dev);
+ continue;
+ }
+ printk(KERN_ERR "PM: Failed to prepare device %s "
+ "for power transition: error %d\n",
+ kobject_name(&dev->kobj), error);
+ put_device(dev);
+ break;
+ }
+ dev->power.status = DPM_SUSPENDING;
+ if (!list_empty(&dev->power.entry))
+ list_move_tail(&dev->power.entry, &list);
+ put_device(dev);
+ }
+ list_splice(&list, &dpm_list);
+ mutex_unlock(&dpm_list_mtx);
+ return error;
+}
+
+/**
+ * device_suspend - Save state and stop all devices in system.
+ * @state: PM transition of the system being carried out.
+ *
+ * Prepare and suspend all devices.
+ */
+int device_suspend(pm_message_t state)
+{
+ int error;
+
+ might_sleep();
+ error = dpm_prepare(state);
+ if (!error)
+ error = dpm_suspend(state);
+ return error;
+}
+EXPORT_SYMBOL_GPL(device_suspend);
+
+void __suspend_report_result(const char *function, void *fn, int ret)
+{
+ if (ret)
+ printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret);
+}
+EXPORT_SYMBOL_GPL(__suspend_report_result);
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
new file mode 100644
index 0000000..41f51fa
--- /dev/null
+++ b/drivers/base/power/power.h
@@ -0,0 +1,49 @@
+static inline void device_pm_init(struct device *dev)
+{
+ dev->power.status = DPM_ON;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+/*
+ * main.c
+ */
+
+extern struct list_head dpm_list; /* The active device list */
+
+static inline struct device *to_device(struct list_head *entry)
+{
+ return container_of(entry, struct device, power.entry);
+}
+
+extern void device_pm_add(struct device *);
+extern void device_pm_remove(struct device *);
+
+#else /* CONFIG_PM_SLEEP */
+
+static inline void device_pm_add(struct device *dev) {}
+static inline void device_pm_remove(struct device *dev) {}
+
+#endif
+
+#ifdef CONFIG_PM
+
+/*
+ * sysfs.c
+ */
+
+extern int dpm_sysfs_add(struct device *);
+extern void dpm_sysfs_remove(struct device *);
+
+#else /* CONFIG_PM */
+
+static inline int dpm_sysfs_add(struct device *dev)
+{
+ return 0;
+}
+
+static inline void dpm_sysfs_remove(struct device *dev)
+{
+}
+
+#endif
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
new file mode 100644
index 0000000..596aeec
--- /dev/null
+++ b/drivers/base/power/sysfs.c
@@ -0,0 +1,98 @@
+/*
+ * drivers/base/power/sysfs.c - sysfs entries for device PM
+ */
+
+#include <linux/device.h>
+#include <linux/string.h>
+#include "power.h"
+
+/*
+ * wakeup - Report/change current wakeup option for device
+ *
+ * Some devices support "wakeup" events, which are hardware signals
+ * used to activate devices from suspended or low power states. Such
+ * devices have one of three values for the sysfs power/wakeup file:
+ *
+ * + "enabled\n" to issue the events;
+ * + "disabled\n" not to do so; or
+ * + "\n" for temporary or permanent inability to issue wakeup.
+ *
+ * (For example, unconfigured USB devices can't issue wakeups.)
+ *
+ * Familiar examples of devices that can issue wakeup events include
+ * keyboards and mice (both PS2 and USB styles), power buttons, modems,
+ * "Wake-On-LAN" Ethernet links, GPIO lines, and more. Some events
+ * will wake the entire system from a suspend state; others may just
+ * wake up the device (if the system as a whole is already active).
+ * Some wakeup events use normal IRQ lines; other use special out
+ * of band signaling.
+ *
+ * It is the responsibility of device drivers to enable (or disable)
+ * wakeup signaling as part of changing device power states, respecting
+ * the policy choices provided through the driver model.
+ *
+ * Devices may not be able to generate wakeup events from all power
+ * states. Also, the events may be ignored in some configurations;
+ * for example, they might need help from other devices that aren't
+ * active, or which may have wakeup disabled. Some drivers rely on
+ * wakeup events internally (unless they are disabled), keeping
+ * their hardware in low power modes whenever they're unused. This
+ * saves runtime power, without requiring system-wide sleep states.
+ */
+
+static const char enabled[] = "enabled";
+static const char disabled[] = "disabled";
+
+static ssize_t
+wake_show(struct device * dev, struct device_attribute *attr, char * buf)
+{
+ return sprintf(buf, "%s\n", device_can_wakeup(dev)
+ ? (device_may_wakeup(dev) ? enabled : disabled)
+ : "");
+}
+
+static ssize_t
+wake_store(struct device * dev, struct device_attribute *attr,
+ const char * buf, size_t n)
+{
+ char *cp;
+ int len = n;
+
+ if (!device_can_wakeup(dev))
+ return -EINVAL;
+
+ cp = memchr(buf, '\n', n);
+ if (cp)
+ len = cp - buf;
+ if (len == sizeof enabled - 1
+ && strncmp(buf, enabled, sizeof enabled - 1) == 0)
+ device_set_wakeup_enable(dev, 1);
+ else if (len == sizeof disabled - 1
+ && strncmp(buf, disabled, sizeof disabled - 1) == 0)
+ device_set_wakeup_enable(dev, 0);
+ else
+ return -EINVAL;
+ return n;
+}
+
+static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
+
+
+static struct attribute * power_attrs[] = {
+ &dev_attr_wakeup.attr,
+ NULL,
+};
+static struct attribute_group pm_attr_group = {
+ .name = "power",
+ .attrs = power_attrs,
+};
+
+int dpm_sysfs_add(struct device * dev)
+{
+ return sysfs_create_group(&dev->kobj, &pm_attr_group);
+}
+
+void dpm_sysfs_remove(struct device * dev)
+{
+ sysfs_remove_group(&dev->kobj, &pm_attr_group);
+}
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
new file mode 100644
index 0000000..2aa6e8f
--- /dev/null
+++ b/drivers/base/power/trace.c
@@ -0,0 +1,231 @@
+/*
+ * drivers/base/power/trace.c
+ *
+ * Copyright (C) 2006 Linus Torvalds
+ *
+ * Trace facility for suspend/resume problems, when none of the
+ * devices may be working.
+ */
+
+#include <linux/resume-trace.h>
+#include <linux/rtc.h>
+
+#include <asm/rtc.h>
+
+#include "power.h"
+
+/*
+ * Horrid, horrid, horrid.
+ *
+ * It turns out that the _only_ piece of hardware that actually
+ * keeps its value across a hard boot (and, more importantly, the
+ * POST init sequence) is literally the realtime clock.
+ *
+ * Never mind that an RTC chip has 114 bytes (and often a whole
+ * other bank of an additional 128 bytes) of nice SRAM that is
+ * _designed_ to keep data - the POST will clear it. So we literally
+ * can just use the few bytes of actual time data, which means that
+ * we're really limited.
+ *
+ * It means, for example, that we can't use the seconds at all
+ * (since the time between the hang and the boot might be more
+ * than a minute), and we'd better not depend on the low bits of
+ * the minutes either.
+ *
+ * There are the wday fields etc, but I wouldn't guarantee those
+ * are dependable either. And if the date isn't valid, either the
+ * hw or POST will do strange things.
+ *
+ * So we're left with:
+ * - year: 0-99
+ * - month: 0-11
+ * - day-of-month: 1-28
+ * - hour: 0-23
+ * - min: (0-30)*2
+ *
+ * Giving us a total range of 0-16128000 (0xf61800), ie less
+ * than 24 bits of actual data we can save across reboots.
+ *
+ * And if your box can't boot in less than three minutes,
+ * you're screwed.
+ *
+ * Now, almost 24 bits of data is pitifully small, so we need
+ * to be pretty dense if we want to use it for anything nice.
+ * What we do is that instead of saving off nice readable info,
+ * we save off _hashes_ of information that we can hopefully
+ * regenerate after the reboot.
+ *
+ * In particular, this means that we might be unlucky, and hit
+ * a case where we have a hash collision, and we end up not
+ * being able to tell for certain exactly which case happened.
+ * But that's hopefully unlikely.
+ *
+ * What we do is to take the bits we can fit, and split them
+ * into three parts (16*997*1009 = 16095568), and use the values
+ * for:
+ * - 0-15: user-settable
+ * - 0-996: file + line number
+ * - 0-1008: device
+ */
+#define USERHASH (16)
+#define FILEHASH (997)
+#define DEVHASH (1009)
+
+#define DEVSEED (7919)
+
+static unsigned int dev_hash_value;
+
+static int set_magic_time(unsigned int user, unsigned int file, unsigned int device)
+{
+ unsigned int n = user + USERHASH*(file + FILEHASH*device);
+
+ // June 7th, 2006
+ static struct rtc_time time = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 7,
+ .tm_mon = 5, // June - counting from zero
+ .tm_year = 106,
+ .tm_wday = 3,
+ .tm_yday = 160,
+ .tm_isdst = 1
+ };
+
+ time.tm_year = (n % 100);
+ n /= 100;
+ time.tm_mon = (n % 12);
+ n /= 12;
+ time.tm_mday = (n % 28) + 1;
+ n /= 28;
+ time.tm_hour = (n % 24);
+ n /= 24;
+ time.tm_min = (n % 20) * 3;
+ n /= 20;
+ set_rtc_time(&time);
+ return n ? -1 : 0;
+}
+
+static unsigned int read_magic_time(void)
+{
+ struct rtc_time time;
+ unsigned int val;
+
+ get_rtc_time(&time);
+ printk("Time: %2d:%02d:%02d Date: %02d/%02d/%02d\n",
+ time.tm_hour, time.tm_min, time.tm_sec,
+ time.tm_mon + 1, time.tm_mday, time.tm_year % 100);
+ val = time.tm_year; /* 100 years */
+ if (val > 100)
+ val -= 100;
+ val += time.tm_mon * 100; /* 12 months */
+ val += (time.tm_mday-1) * 100 * 12; /* 28 month-days */
+ val += time.tm_hour * 100 * 12 * 28; /* 24 hours */
+ val += (time.tm_min / 3) * 100 * 12 * 28 * 24; /* 20 3-minute intervals */
+ return val;
+}
+
+/*
+ * This is just the sdbm hash function with a user-supplied
+ * seed and final size parameter.
+ */
+static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
+{
+ unsigned char c;
+ while ((c = *data++) != 0) {
+ seed = (seed << 16) + (seed << 6) - seed + c;
+ }
+ return seed % mod;
+}
+
+void set_trace_device(struct device *dev)
+{
+ dev_hash_value = hash_string(DEVSEED, dev->bus_id, DEVHASH);
+}
+EXPORT_SYMBOL(set_trace_device);
+
+/*
+ * We could just take the "tracedata" index into the .tracedata
+ * section instead. Generating a hash of the data gives us a
+ * chance to work across kernel versions, and perhaps more
+ * importantly it also gives us valid/invalid check (ie we will
+ * likely not give totally bogus reports - if the hash matches,
+ * it's not any guarantee, but it's a high _likelihood_ that
+ * the match is valid).
+ */
+void generate_resume_trace(const void *tracedata, unsigned int user)
+{
+ unsigned short lineno = *(unsigned short *)tracedata;
+ const char *file = *(const char **)(tracedata + 2);
+ unsigned int user_hash_value, file_hash_value;
+
+ user_hash_value = user % USERHASH;
+ file_hash_value = hash_string(lineno, file, FILEHASH);
+ set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
+}
+EXPORT_SYMBOL(generate_resume_trace);
+
+extern char __tracedata_start, __tracedata_end;
+static int show_file_hash(unsigned int value)
+{
+ int match;
+ char *tracedata;
+
+ match = 0;
+ for (tracedata = &__tracedata_start ; tracedata < &__tracedata_end ;
+ tracedata += 2 + sizeof(unsigned long)) {
+ unsigned short lineno = *(unsigned short *)tracedata;
+ const char *file = *(const char **)(tracedata + 2);
+ unsigned int hash = hash_string(lineno, file, FILEHASH);
+ if (hash != value)
+ continue;
+ printk(" hash matches %s:%u\n", file, lineno);
+ match++;
+ }
+ return match;
+}
+
+static int show_dev_hash(unsigned int value)
+{
+ int match = 0;
+ struct list_head *entry = dpm_list.prev;
+
+ while (entry != &dpm_list) {
+ struct device * dev = to_device(entry);
+ unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH);
+ if (hash == value) {
+ dev_info(dev, "hash matches\n");
+ match++;
+ }
+ entry = entry->prev;
+ }
+ return match;
+}
+
+static unsigned int hash_value_early_read;
+
+static int early_resume_init(void)
+{
+ hash_value_early_read = read_magic_time();
+ return 0;
+}
+
+static int late_resume_init(void)
+{
+ unsigned int val = hash_value_early_read;
+ unsigned int user, file, dev;
+
+ user = val % USERHASH;
+ val = val / USERHASH;
+ file = val % FILEHASH;
+ val = val / FILEHASH;
+ dev = val /* % DEVHASH */;
+
+ printk(" Magic number: %d:%d:%d\n", user, file, dev);
+ show_file_hash(file);
+ show_dev_hash(dev);
+ return 0;
+}
+
+core_initcall(early_resume_init);
+late_initcall(late_resume_init);
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
new file mode 100644
index 0000000..c98c31e
--- /dev/null
+++ b/drivers/base/sys.c
@@ -0,0 +1,528 @@
+/*
+ * sys.c - pseudo-bus for system 'devices' (cpus, PICs, timers, etc)
+ *
+ * Copyright (c) 2002-3 Patrick Mochel
+ * 2002-3 Open Source Development Lab
+ *
+ * This file is released under the GPLv2
+ *
+ * This exports a 'system' bus type.
+ * By default, a 'sys' bus gets added to the root of the system. There will
+ * always be core system devices. Devices can use sysdev_register() to
+ * add themselves as children of the system bus.
+ */
+
+#include <linux/sysdev.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+#include "base.h"
+
+#define to_sysdev(k) container_of(k, struct sys_device, kobj)
+#define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr)
+
+
+static ssize_t
+sysdev_show(struct kobject * kobj, struct attribute * attr, char * buffer)
+{
+ struct sys_device * sysdev = to_sysdev(kobj);
+ struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr);
+
+ if (sysdev_attr->show)
+ return sysdev_attr->show(sysdev, sysdev_attr, buffer);
+ return -EIO;
+}
+
+
+static ssize_t
+sysdev_store(struct kobject * kobj, struct attribute * attr,
+ const char * buffer, size_t count)
+{
+ struct sys_device * sysdev = to_sysdev(kobj);
+ struct sysdev_attribute * sysdev_attr = to_sysdev_attr(attr);
+
+ if (sysdev_attr->store)
+ return sysdev_attr->store(sysdev, sysdev_attr, buffer, count);
+ return -EIO;
+}
+
+static struct sysfs_ops sysfs_ops = {
+ .show = sysdev_show,
+ .store = sysdev_store,
+};
+
+static struct kobj_type ktype_sysdev = {
+ .sysfs_ops = &sysfs_ops,
+};
+
+
+int sysdev_create_file(struct sys_device * s, struct sysdev_attribute * a)
+{
+ return sysfs_create_file(&s->kobj, &a->attr);
+}
+
+
+void sysdev_remove_file(struct sys_device * s, struct sysdev_attribute * a)
+{
+ sysfs_remove_file(&s->kobj, &a->attr);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_create_file);
+EXPORT_SYMBOL_GPL(sysdev_remove_file);
+
+#define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj)
+#define to_sysdev_class_attr(a) container_of(a, \
+ struct sysdev_class_attribute, attr)
+
+static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr,
+ char *buffer)
+{
+ struct sysdev_class * class = to_sysdev_class(kobj);
+ struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
+
+ if (class_attr->show)
+ return class_attr->show(class, buffer);
+ return -EIO;
+}
+
+static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct sysdev_class * class = to_sysdev_class(kobj);
+ struct sysdev_class_attribute * class_attr = to_sysdev_class_attr(attr);
+
+ if (class_attr->store)
+ return class_attr->store(class, buffer, count);
+ return -EIO;
+}
+
+static struct sysfs_ops sysfs_class_ops = {
+ .show = sysdev_class_show,
+ .store = sysdev_class_store,
+};
+
+static struct kobj_type ktype_sysdev_class = {
+ .sysfs_ops = &sysfs_class_ops,
+};
+
+int sysdev_class_create_file(struct sysdev_class *c,
+ struct sysdev_class_attribute *a)
+{
+ return sysfs_create_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_create_file);
+
+void sysdev_class_remove_file(struct sysdev_class *c,
+ struct sysdev_class_attribute *a)
+{
+ sysfs_remove_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
+
+static struct kset *system_kset;
+
+int sysdev_class_register(struct sysdev_class * cls)
+{
+ pr_debug("Registering sysdev class '%s'\n", cls->name);
+
+ INIT_LIST_HEAD(&cls->drivers);
+ memset(&cls->kset.kobj, 0x00, sizeof(struct kobject));
+ cls->kset.kobj.parent = &system_kset->kobj;
+ cls->kset.kobj.ktype = &ktype_sysdev_class;
+ cls->kset.kobj.kset = system_kset;
+ kobject_set_name(&cls->kset.kobj, cls->name);
+ return kset_register(&cls->kset);
+}
+
+void sysdev_class_unregister(struct sysdev_class * cls)
+{
+ pr_debug("Unregistering sysdev class '%s'\n",
+ kobject_name(&cls->kset.kobj));
+ kset_unregister(&cls->kset);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_class_register);
+EXPORT_SYMBOL_GPL(sysdev_class_unregister);
+
+static DEFINE_MUTEX(sysdev_drivers_lock);
+
+/**
+ * sysdev_driver_register - Register auxillary driver
+ * @cls: Device class driver belongs to.
+ * @drv: Driver.
+ *
+ * @drv is inserted into @cls->drivers to be
+ * called on each operation on devices of that class. The refcount
+ * of @cls is incremented.
+ */
+
+int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
+{
+ int err = 0;
+
+ if (!cls) {
+ WARN(1, KERN_WARNING "sysdev: invalid class passed to "
+ "sysdev_driver_register!\n");
+ return -EINVAL;
+ }
+
+ /* Check whether this driver has already been added to a class. */
+ if (drv->entry.next && !list_empty(&drv->entry))
+ WARN(1, KERN_WARNING "sysdev: class %s: driver (%p) has already"
+ " been registered to a class, something is wrong, but "
+ "will forge on!\n", cls->name, drv);
+
+ mutex_lock(&sysdev_drivers_lock);
+ if (cls && kset_get(&cls->kset)) {
+ list_add_tail(&drv->entry, &cls->drivers);
+
+ /* If devices of this class already exist, tell the driver */
+ if (drv->add) {
+ struct sys_device *dev;
+ list_for_each_entry(dev, &cls->kset.list, kobj.entry)
+ drv->add(dev);
+ }
+ } else {
+ err = -EINVAL;
+ WARN(1, KERN_ERR "%s: invalid device class\n", __func__);
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+ return err;
+}
+
+
+/**
+ * sysdev_driver_unregister - Remove an auxillary driver.
+ * @cls: Class driver belongs to.
+ * @drv: Driver.
+ */
+void sysdev_driver_unregister(struct sysdev_class * cls,
+ struct sysdev_driver * drv)
+{
+ mutex_lock(&sysdev_drivers_lock);
+ list_del_init(&drv->entry);
+ if (cls) {
+ if (drv->remove) {
+ struct sys_device *dev;
+ list_for_each_entry(dev, &cls->kset.list, kobj.entry)
+ drv->remove(dev);
+ }
+ kset_put(&cls->kset);
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+}
+
+EXPORT_SYMBOL_GPL(sysdev_driver_register);
+EXPORT_SYMBOL_GPL(sysdev_driver_unregister);
+
+
+
+/**
+ * sysdev_register - add a system device to the tree
+ * @sysdev: device in question
+ *
+ */
+int sysdev_register(struct sys_device * sysdev)
+{
+ int error;
+ struct sysdev_class * cls = sysdev->cls;
+
+ if (!cls)
+ return -EINVAL;
+
+ pr_debug("Registering sys device of class '%s'\n",
+ kobject_name(&cls->kset.kobj));
+
+ /* initialize the kobject to 0, in case it had previously been used */
+ memset(&sysdev->kobj, 0x00, sizeof(struct kobject));
+
+ /* Make sure the kset is set */
+ sysdev->kobj.kset = &cls->kset;
+
+ /* Register the object */
+ error = kobject_init_and_add(&sysdev->kobj, &ktype_sysdev, NULL,
+ "%s%d", kobject_name(&cls->kset.kobj),
+ sysdev->id);
+
+ if (!error) {
+ struct sysdev_driver * drv;
+
+ pr_debug("Registering sys device '%s'\n",
+ kobject_name(&sysdev->kobj));
+
+ mutex_lock(&sysdev_drivers_lock);
+ /* Generic notification is implicit, because it's that
+ * code that should have called us.
+ */
+
+ /* Notify class auxillary drivers */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->add)
+ drv->add(sysdev);
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+ }
+
+ kobject_uevent(&sysdev->kobj, KOBJ_ADD);
+ return error;
+}
+
+void sysdev_unregister(struct sys_device * sysdev)
+{
+ struct sysdev_driver * drv;
+
+ mutex_lock(&sysdev_drivers_lock);
+ list_for_each_entry(drv, &sysdev->cls->drivers, entry) {
+ if (drv->remove)
+ drv->remove(sysdev);
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+
+ kobject_put(&sysdev->kobj);
+}
+
+
+
+/**
+ * sysdev_shutdown - Shut down all system devices.
+ *
+ * Loop over each class of system devices, and the devices in each
+ * of those classes. For each device, we call the shutdown method for
+ * each driver registered for the device - the auxillaries,
+ * and the class driver.
+ *
+ * Note: The list is iterated in reverse order, so that we shut down
+ * child devices before we shut down thier parents. The list ordering
+ * is guaranteed by virtue of the fact that child devices are registered
+ * after their parents.
+ */
+
+void sysdev_shutdown(void)
+{
+ struct sysdev_class * cls;
+
+ pr_debug("Shutting Down System Devices\n");
+
+ mutex_lock(&sysdev_drivers_lock);
+ list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
+ struct sys_device * sysdev;
+
+ pr_debug("Shutting down type '%s':\n",
+ kobject_name(&cls->kset.kobj));
+
+ list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
+ struct sysdev_driver * drv;
+ pr_debug(" %s\n", kobject_name(&sysdev->kobj));
+
+ /* Call auxillary drivers first */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->shutdown)
+ drv->shutdown(sysdev);
+ }
+
+ /* Now call the generic one */
+ if (cls->shutdown)
+ cls->shutdown(sysdev);
+ }
+ }
+ mutex_unlock(&sysdev_drivers_lock);
+}
+
+static void __sysdev_resume(struct sys_device *dev)
+{
+ struct sysdev_class *cls = dev->cls;
+ struct sysdev_driver *drv;
+
+ /* First, call the class-specific one */
+ if (cls->resume)
+ cls->resume(dev);
+
+ /* Call auxillary drivers next. */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->resume)
+ drv->resume(dev);
+ }
+}
+
+/**
+ * sysdev_suspend - Suspend all system devices.
+ * @state: Power state to enter.
+ *
+ * We perform an almost identical operation as sysdev_shutdown()
+ * above, though calling ->suspend() instead. Interrupts are disabled
+ * when this called. Devices are responsible for both saving state and
+ * quiescing or powering down the device.
+ *
+ * This is only called by the device PM core, so we let them handle
+ * all synchronization.
+ */
+
+int sysdev_suspend(pm_message_t state)
+{
+ struct sysdev_class * cls;
+ struct sys_device *sysdev, *err_dev;
+ struct sysdev_driver *drv, *err_drv;
+ int ret;
+
+ pr_debug("Suspending System Devices\n");
+
+ list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) {
+ pr_debug("Suspending type '%s':\n",
+ kobject_name(&cls->kset.kobj));
+
+ list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
+ pr_debug(" %s\n", kobject_name(&sysdev->kobj));
+
+ /* Call auxillary drivers first */
+ list_for_each_entry(drv, &cls->drivers, entry) {
+ if (drv->suspend) {
+ ret = drv->suspend(sysdev, state);
+ if (ret)
+ goto aux_driver;
+ }
+ }
+
+ /* Now call the generic one */
+ if (cls->suspend) {
+ ret = cls->suspend(sysdev, state);
+ if (ret)
+ goto cls_driver;
+ }
+ }
+ }
+ return 0;
+ /* resume current sysdev */
+cls_driver:
+ drv = NULL;
+ printk(KERN_ERR "Class suspend failed for %s\n",
+ kobject_name(&sysdev->kobj));
+
+aux_driver:
+ if (drv)
+ printk(KERN_ERR "Class driver suspend failed for %s\n",
+ kobject_name(&sysdev->kobj));
+ list_for_each_entry(err_drv, &cls->drivers, entry) {
+ if (err_drv == drv)
+ break;
+ if (err_drv->resume)
+ err_drv->resume(sysdev);
+ }
+
+ /* resume other sysdevs in current class */
+ list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
+ if (err_dev == sysdev)
+ break;
+ pr_debug(" %s\n", kobject_name(&err_dev->kobj));
+ __sysdev_resume(err_dev);
+ }
+
+ /* resume other classes */
+ list_for_each_entry_continue(cls, &system_kset->list, kset.kobj.entry) {
+ list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) {
+ pr_debug(" %s\n", kobject_name(&err_dev->kobj));
+ __sysdev_resume(err_dev);
+ }
+ }
+ return ret;
+}
+
+
+/**
+ * sysdev_resume - Bring system devices back to life.
+ *
+ * Similar to sysdev_suspend(), but we iterate the list forwards
+ * to guarantee that parent devices are resumed before their children.
+ *
+ * Note: Interrupts are disabled when called.
+ */
+
+int sysdev_resume(void)
+{
+ struct sysdev_class * cls;
+
+ pr_debug("Resuming System Devices\n");
+
+ list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) {
+ struct sys_device * sysdev;
+
+ pr_debug("Resuming type '%s':\n",
+ kobject_name(&cls->kset.kobj));
+
+ list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) {
+ pr_debug(" %s\n", kobject_name(&sysdev->kobj));
+
+ __sysdev_resume(sysdev);
+ }
+ }
+ return 0;
+}
+
+
+int __init system_bus_init(void)
+{
+ system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
+ if (!system_kset)
+ return -ENOMEM;
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sysdev_register);
+EXPORT_SYMBOL_GPL(sysdev_unregister);
+
+#define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
+
+ssize_t sysdev_store_ulong(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ char *end;
+ unsigned long new = simple_strtoul(buf, &end, 0);
+ if (end == buf)
+ return -EINVAL;
+ *(unsigned long *)(ea->var) = new;
+ /* Always return full write size even if we didn't consume all */
+ return size;
+}
+EXPORT_SYMBOL_GPL(sysdev_store_ulong);
+
+ssize_t sysdev_show_ulong(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ return snprintf(buf, PAGE_SIZE, "%lx\n", *(unsigned long *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(sysdev_show_ulong);
+
+ssize_t sysdev_store_int(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ char *end;
+ long new = simple_strtol(buf, &end, 0);
+ if (end == buf || new > INT_MAX || new < INT_MIN)
+ return -EINVAL;
+ *(int *)(ea->var) = new;
+ /* Always return full write size even if we didn't consume all */
+ return size;
+}
+EXPORT_SYMBOL_GPL(sysdev_store_int);
+
+ssize_t sysdev_show_int(struct sys_device *sysdev,
+ struct sysdev_attribute *attr,
+ char *buf)
+{
+ struct sysdev_ext_attribute *ea = to_ext_attr(attr);
+ return snprintf(buf, PAGE_SIZE, "%d\n", *(int *)(ea->var));
+}
+EXPORT_SYMBOL_GPL(sysdev_show_int);
+
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
new file mode 100644
index 0000000..199cd97
--- /dev/null
+++ b/drivers/base/topology.c
@@ -0,0 +1,183 @@
+/*
+ * driver/base/topology.c - Populate sysfs with cpu topology information
+ *
+ * Written by: Zhang Yanmin, Intel Corporation
+ *
+ * Copyright (C) 2006, Intel Corp.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/sysdev.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/topology.h>
+
+#define define_one_ro(_name) \
+static SYSDEV_ATTR(_name, 0444, show_##_name, NULL)
+
+#define define_id_show_func(name) \
+static ssize_t show_##name(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
+{ \
+ unsigned int cpu = dev->id; \
+ return sprintf(buf, "%d\n", topology_##name(cpu)); \
+}
+
+#if defined(topology_thread_siblings) || defined(topology_core_siblings)
+static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf)
+{
+ ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf;
+ int n = 0;
+
+ if (len > 1) {
+ n = type?
+ cpulist_scnprintf(buf, len-2, *mask):
+ cpumask_scnprintf(buf, len-2, *mask);
+ buf[n++] = '\n';
+ buf[n] = '\0';
+ }
+ return n;
+}
+#endif
+
+#ifdef arch_provides_topology_pointers
+#define define_siblings_show_map(name) \
+static ssize_t show_##name(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
+{ \
+ unsigned int cpu = dev->id; \
+ return show_cpumap(0, &(topology_##name(cpu)), buf); \
+}
+
+#define define_siblings_show_list(name) \
+static ssize_t show_##name##_list(struct sys_device *dev, \
+ struct sysdev_attribute *attr, \
+ char *buf) \
+{ \
+ unsigned int cpu = dev->id; \
+ return show_cpumap(1, &(topology_##name(cpu)), buf); \
+}
+
+#else
+#define define_siblings_show_map(name) \
+static ssize_t show_##name(struct sys_device *dev, \
+ struct sysdev_attribute *attr, char *buf) \
+{ \
+ unsigned int cpu = dev->id; \
+ cpumask_t mask = topology_##name(cpu); \
+ return show_cpumap(0, &mask, buf); \
+}
+
+#define define_siblings_show_list(name) \
+static ssize_t show_##name##_list(struct sys_device *dev, \
+ struct sysdev_attribute *attr, \
+ char *buf) \
+{ \
+ unsigned int cpu = dev->id; \
+ cpumask_t mask = topology_##name(cpu); \
+ return show_cpumap(1, &mask, buf); \
+}
+#endif
+
+#define define_siblings_show_func(name) \
+ define_siblings_show_map(name); define_siblings_show_list(name)
+
+define_id_show_func(physical_package_id);
+define_one_ro(physical_package_id);
+
+define_id_show_func(core_id);
+define_one_ro(core_id);
+
+define_siblings_show_func(thread_siblings);
+define_one_ro(thread_siblings);
+define_one_ro(thread_siblings_list);
+
+define_siblings_show_func(core_siblings);
+define_one_ro(core_siblings);
+define_one_ro(core_siblings_list);
+
+static struct attribute *default_attrs[] = {
+ &attr_physical_package_id.attr,
+ &attr_core_id.attr,
+ &attr_thread_siblings.attr,
+ &attr_thread_siblings_list.attr,
+ &attr_core_siblings.attr,
+ &attr_core_siblings_list.attr,
+ NULL
+};
+
+static struct attribute_group topology_attr_group = {
+ .attrs = default_attrs,
+ .name = "topology"
+};
+
+/* Add/Remove cpu_topology interface for CPU device */
+static int __cpuinit topology_add_dev(unsigned int cpu)
+{
+ struct sys_device *sys_dev = get_cpu_sysdev(cpu);
+
+ return sysfs_create_group(&sys_dev->kobj, &topology_attr_group);
+}
+
+static void __cpuinit topology_remove_dev(unsigned int cpu)
+{
+ struct sys_device *sys_dev = get_cpu_sysdev(cpu);
+
+ sysfs_remove_group(&sys_dev->kobj, &topology_attr_group);
+}
+
+static int __cpuinit topology_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+ int rc = 0;
+
+ switch (action) {
+ case CPU_UP_PREPARE:
+ case CPU_UP_PREPARE_FROZEN:
+ rc = topology_add_dev(cpu);
+ break;
+ case CPU_UP_CANCELED:
+ case CPU_UP_CANCELED_FROZEN:
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ topology_remove_dev(cpu);
+ break;
+ }
+ return rc ? NOTIFY_BAD : NOTIFY_OK;
+}
+
+static int __cpuinit topology_sysfs_init(void)
+{
+ int cpu;
+ int rc;
+
+ for_each_online_cpu(cpu) {
+ rc = topology_add_dev(cpu);
+ if (rc)
+ return rc;
+ }
+ hotcpu_notifier(topology_cpu_callback, 0);
+
+ return 0;
+}
+
+device_initcall(topology_sysfs_init);
diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
new file mode 100644
index 0000000..84997ef
--- /dev/null
+++ b/drivers/base/transport_class.c
@@ -0,0 +1,279 @@
+/*
+ * transport_class.c - implementation of generic transport classes
+ * using attribute_containers
+ *
+ * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
+ *
+ * This file is licensed under GPLv2
+ *
+ * The basic idea here is to allow any "device controller" (which
+ * would most often be a Host Bus Adapter to use the services of one
+ * or more tranport classes for performing transport specific
+ * services. Transport specific services are things that the generic
+ * command layer doesn't want to know about (speed settings, line
+ * condidtioning, etc), but which the user might be interested in.
+ * Thus, the HBA's use the routines exported by the transport classes
+ * to perform these functions. The transport classes export certain
+ * values to the user via sysfs using attribute containers.
+ *
+ * Note: because not every HBA will care about every transport
+ * attribute, there's a many to one relationship that goes like this:
+ *
+ * transport class<-----attribute container<----class device
+ *
+ * Usually the attribute container is per-HBA, but the design doesn't
+ * mandate that. Although most of the services will be specific to
+ * the actual external storage connection used by the HBA, the generic
+ * transport class is framed entirely in terms of generic devices to
+ * allow it to be used by any physical HBA in the system.
+ */
+#include <linux/attribute_container.h>
+#include <linux/transport_class.h>
+
+/**
+ * transport_class_register - register an initial transport class
+ *
+ * @tclass: a pointer to the transport class structure to be initialised
+ *
+ * The transport class contains an embedded class which is used to
+ * identify it. The caller should initialise this structure with
+ * zeros and then generic class must have been initialised with the
+ * actual transport class unique name. There's a macro
+ * DECLARE_TRANSPORT_CLASS() to do this (declared classes still must
+ * be registered).
+ *
+ * Returns 0 on success or error on failure.
+ */
+int transport_class_register(struct transport_class *tclass)
+{
+ return class_register(&tclass->class);
+}
+EXPORT_SYMBOL_GPL(transport_class_register);
+
+/**
+ * transport_class_unregister - unregister a previously registered class
+ *
+ * @tclass: The transport class to unregister
+ *
+ * Must be called prior to deallocating the memory for the transport
+ * class.
+ */
+void transport_class_unregister(struct transport_class *tclass)
+{
+ class_unregister(&tclass->class);
+}
+EXPORT_SYMBOL_GPL(transport_class_unregister);
+
+static int anon_transport_dummy_function(struct transport_container *tc,
+ struct device *dev,
+ struct device *cdev)
+{
+ /* do nothing */
+ return 0;
+}
+
+/**
+ * anon_transport_class_register - register an anonymous class
+ *
+ * @atc: The anon transport class to register
+ *
+ * The anonymous transport class contains both a transport class and a
+ * container. The idea of an anonymous class is that it never
+ * actually has any device attributes associated with it (and thus
+ * saves on container storage). So it can only be used for triggering
+ * events. Use prezero and then use DECLARE_ANON_TRANSPORT_CLASS() to
+ * initialise the anon transport class storage.
+ */
+int anon_transport_class_register(struct anon_transport_class *atc)
+{
+ int error;
+ atc->container.class = &atc->tclass.class;
+ attribute_container_set_no_classdevs(&atc->container);
+ error = attribute_container_register(&atc->container);
+ if (error)
+ return error;
+ atc->tclass.setup = anon_transport_dummy_function;
+ atc->tclass.remove = anon_transport_dummy_function;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(anon_transport_class_register);
+
+/**
+ * anon_transport_class_unregister - unregister an anon class
+ *
+ * @atc: Pointer to the anon transport class to unregister
+ *
+ * Must be called prior to deallocating the memory for the anon
+ * transport class.
+ */
+void anon_transport_class_unregister(struct anon_transport_class *atc)
+{
+ if (unlikely(attribute_container_unregister(&atc->container)))
+ BUG();
+}
+EXPORT_SYMBOL_GPL(anon_transport_class_unregister);
+
+static int transport_setup_classdev(struct attribute_container *cont,
+ struct device *dev,
+ struct device *classdev)
+{
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+ struct transport_container *tcont = attribute_container_to_transport_container(cont);
+
+ if (tclass->setup)
+ tclass->setup(tcont, dev, classdev);
+
+ return 0;
+}
+
+/**
+ * transport_setup_device - declare a new dev for transport class association but don't make it visible yet.
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+ * the HBA itself or a device remote across the HBA bus). This
+ * routine is simply a trigger point to see if any set of transport
+ * classes wishes to associate with the added device. This allocates
+ * storage for the class device and initialises it, but does not yet
+ * add it to the system or add attributes to it (you do this with
+ * transport_add_device). If you have no need for a separate setup
+ * and add operations, use transport_register_device (see
+ * transport_class.h).
+ */
+
+void transport_setup_device(struct device *dev)
+{
+ attribute_container_add_device(dev, transport_setup_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_setup_device);
+
+static int transport_add_class_device(struct attribute_container *cont,
+ struct device *dev,
+ struct device *classdev)
+{
+ int error = attribute_container_add_class_device(classdev);
+ struct transport_container *tcont =
+ attribute_container_to_transport_container(cont);
+
+ if (!error && tcont->statistics)
+ error = sysfs_create_group(&classdev->kobj, tcont->statistics);
+
+ return error;
+}
+
+
+/**
+ * transport_add_device - declare a new dev for transport class association
+ *
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+ * the HBA itself or a device remote across the HBA bus). This
+ * routine is simply a trigger point used to add the device to the
+ * system and register attributes for it.
+ */
+
+void transport_add_device(struct device *dev)
+{
+ attribute_container_device_trigger(dev, transport_add_class_device);
+}
+EXPORT_SYMBOL_GPL(transport_add_device);
+
+static int transport_configure(struct attribute_container *cont,
+ struct device *dev,
+ struct device *cdev)
+{
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+ struct transport_container *tcont = attribute_container_to_transport_container(cont);
+
+ if (tclass->configure)
+ tclass->configure(tcont, dev, cdev);
+
+ return 0;
+}
+
+/**
+ * transport_configure_device - configure an already set up device
+ *
+ * @dev: generic device representing device to be configured
+ *
+ * The idea of configure is simply to provide a point within the setup
+ * process to allow the transport class to extract information from a
+ * device after it has been setup. This is used in SCSI because we
+ * have to have a setup device to begin using the HBA, but after we
+ * send the initial inquiry, we use configure to extract the device
+ * parameters. The device need not have been added to be configured.
+ */
+void transport_configure_device(struct device *dev)
+{
+ attribute_container_device_trigger(dev, transport_configure);
+}
+EXPORT_SYMBOL_GPL(transport_configure_device);
+
+static int transport_remove_classdev(struct attribute_container *cont,
+ struct device *dev,
+ struct device *classdev)
+{
+ struct transport_container *tcont =
+ attribute_container_to_transport_container(cont);
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->remove)
+ tclass->remove(tcont, dev, classdev);
+
+ if (tclass->remove != anon_transport_dummy_function) {
+ if (tcont->statistics)
+ sysfs_remove_group(&classdev->kobj, tcont->statistics);
+ attribute_container_class_device_del(classdev);
+ }
+
+ return 0;
+}
+
+
+/**
+ * transport_remove_device - remove the visibility of a device
+ *
+ * @dev: generic device to remove
+ *
+ * This call removes the visibility of the device (to the user from
+ * sysfs), but does not destroy it. To eliminate a device entirely
+ * you must also call transport_destroy_device. If you don't need to
+ * do remove and destroy as separate operations, use
+ * transport_unregister_device() (see transport_class.h) which will
+ * perform both calls for you.
+ */
+void transport_remove_device(struct device *dev)
+{
+ attribute_container_device_trigger(dev, transport_remove_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_remove_device);
+
+static void transport_destroy_classdev(struct attribute_container *cont,
+ struct device *dev,
+ struct device *classdev)
+{
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->remove != anon_transport_dummy_function)
+ put_device(classdev);
+}
+
+
+/**
+ * transport_destroy_device - destroy a removed device
+ *
+ * @dev: device to eliminate from the transport class.
+ *
+ * This call triggers the elimination of storage associated with the
+ * transport classdev. Note: all it really does is relinquish a
+ * reference to the classdev. The memory will not be freed until the
+ * last reference goes to zero. Note also that the classdev retains a
+ * reference count on dev, so dev too will remain for as long as the
+ * transport class device remains around.
+ */
+void transport_destroy_device(struct device *dev)
+{
+ attribute_container_remove_device(dev, transport_destroy_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_destroy_device);
OpenPOWER on IntegriCloud