]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/base/platform.c
Merge branch 'linus' into core/urgent
[linux-2.6-omap-h63xx.git] / drivers / base / platform.c
index 911ec600fe71dec287cf922441a8cf53855ecf3f..dfcbfe504867e9db43ea944c55cda9eae9328556 100644 (file)
@@ -42,10 +42,8 @@ struct resource *platform_get_resource(struct platform_device *dev,
        for (i = 0; i < dev->num_resources; i++) {
                struct resource *r = &dev->resource[i];
 
-               if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
-                                IORESOURCE_IRQ|IORESOURCE_DMA)) == type)
-                       if (num-- == 0)
-                               return r;
+               if (type == resource_type(r) && num-- == 0)
+                       return r;
        }
        return NULL;
 }
@@ -78,10 +76,8 @@ struct resource *platform_get_resource_byname(struct platform_device *dev,
        for (i = 0; i < dev->num_resources; i++) {
                struct resource *r = &dev->resource[i];
 
-               if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
-                                IORESOURCE_IRQ|IORESOURCE_DMA)) == type)
-                       if (!strcmp(r->name, name))
-                               return r;
+               if (type == resource_type(r) && !strcmp(r->name, name))
+                       return r;
        }
        return NULL;
 }
@@ -259,9 +255,9 @@ int platform_device_add(struct platform_device *pdev)
 
                p = r->parent;
                if (!p) {
-                       if (r->flags & IORESOURCE_MEM)
+                       if (resource_type(r) == IORESOURCE_MEM)
                                p = &iomem_resource;
-                       else if (r->flags & IORESOURCE_IO)
+                       else if (resource_type(r) == IORESOURCE_IO)
                                p = &ioport_resource;
                }
 
@@ -282,9 +278,14 @@ int platform_device_add(struct platform_device *pdev)
                return ret;
 
  failed:
-       while (--i >= 0)
-               if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
-                       release_resource(&pdev->resource[i]);
+       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);
@@ -306,7 +307,9 @@ void platform_device_del(struct platform_device *pdev)
 
                for (i = 0; i < pdev->num_resources; i++) {
                        struct resource *r = &pdev->resource[i];
-                       if (r->flags & (IORESOURCE_MEM|IORESOURCE_IO))
+                       unsigned long type = resource_type(r);
+
+                       if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
                                release_resource(r);
                }
        }
@@ -391,6 +394,53 @@ error:
 }
 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);
@@ -453,6 +503,8 @@ int platform_driver_register(struct platform_driver *drv)
                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);
@@ -560,7 +612,9 @@ static int platform_match(struct device *dev, struct device_driver *drv)
        return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
 }
 
-static int platform_suspend(struct device *dev, pm_message_t mesg)
+#ifdef CONFIG_PM_SLEEP
+
+static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
 {
        int ret = 0;
 
@@ -570,7 +624,7 @@ static int platform_suspend(struct device *dev, pm_message_t mesg)
        return ret;
 }
 
-static int platform_suspend_late(struct device *dev, pm_message_t mesg)
+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;
@@ -583,7 +637,7 @@ static int platform_suspend_late(struct device *dev, pm_message_t mesg)
        return ret;
 }
 
-static int platform_resume_early(struct device *dev)
+static int platform_legacy_resume_early(struct device *dev)
 {
        struct platform_driver *drv = to_platform_driver(dev->driver);
        struct platform_device *pdev;
@@ -596,7 +650,7 @@ static int platform_resume_early(struct device *dev)
        return ret;
 }
 
-static int platform_resume(struct device *dev)
+static int platform_legacy_resume(struct device *dev)
 {
        int ret = 0;
 
@@ -606,15 +660,291 @@ static int platform_resume(struct device *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,
-       .suspend        = platform_suspend,
-       .suspend_late   = platform_suspend_late,
-       .resume_early   = platform_resume_early,
-       .resume         = platform_resume,
+       .pm             = PLATFORM_PM_OPS_PTR,
 };
 EXPORT_SYMBOL_GPL(platform_bus_type);