From 717e5d458e3bfca495a38dca61c64f274c049e46 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 10 Dec 2013 14:37:41 +0100 Subject: PM / Runtime: Implement the pm_generic_runtime functions for CONFIG_PM The pm_generic_runtime_suspend|resume functions were implemented within CONFIG_PM_RUNTIME. As we also may use runtime PM callbacks during system suspend, to put devices into low power state, we need to move the implementation of pm_generic_runtime_suspend|resume to CONFIG_PM. This change gives a power domain provision to invoke a platform driver's runtime PM callback from a power domain's system PM callback. This were earlier prevented by the platform bus, since it uses the pm_generic_runtime_suspend|resume functions as runtime PM callbacks. Cc: Kevin Hilman Cc: Alan Stern Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/generic_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index 5ee030a864f9..a2e55bfdf572 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -10,7 +10,7 @@ #include #include -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM /** * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems. * @dev: Device to suspend. @@ -48,7 +48,7 @@ int pm_generic_runtime_resume(struct device *dev) return ret; } EXPORT_SYMBOL_GPL(pm_generic_runtime_resume); -#endif /* CONFIG_PM_RUNTIME */ +#endif /* CONFIG_PM */ #ifdef CONFIG_PM_SLEEP /** -- cgit 1.4.1 From caa73ea158de9419f08e456f2716c71d1f06012a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 29 Dec 2013 15:25:48 +0100 Subject: ACPI / hotplug / driver core: Handle containers in a special way ACPI container devices require special hotplug handling, at least on some systems, since generally user space needs to carry out system-specific cleanup before it makes sense to offline devices in the container. However, the current ACPI hotplug code for containers first attempts to offline devices in the container and only then it notifies user space of the container offline. Moreover, after commit 202317a573b2 (ACPI / scan: Add acpi_device objects for all device nodes in the namespace), ACPI device objects representing containers are present as long as the ACPI namespace nodes corresponding to them are present, which may be forever, even if the container devices are physically detached from the system (the return values of the corresponding _STA methods change in those cases, but generally the namespace nodes themselves are still there). Thus it is useful to introduce entities representing containers that will go away during container hot-unplug. The goal of this change is to address both the above issues. The idea is to create a "companion" container system device for each of the ACPI container device objects during the initial namespace scan or on a hotplug event making the container present. That system device will be unregistered on container removal. A new bus type for container devices is added for this purpose, because device offline and online operations need to be defined for them. The online operation is a trivial function that is always successful and the offline uses a callback pointed to by the container device's offline member. For ACPI containers that callback simply walks the list of ACPI device objects right below the container object (its children) and checks if all of their physical companion devices are offline. If that's not the case, it returns -EBUSY and the container system devivce cannot be put offline. Consequently, to put the container system device offline, it is necessary to put all of the physical devices depending on its ACPI companion object offline beforehand. Container system devices created for ACPI container objects are initially online. They are created by the container ACPI scan handler whose hotplug.demand_offline flag is set. That causes acpi_scan_hot_remove() to check if the companion container system device is offline before attempting to remove an ACPI container or any devices below it. If the check fails, a KOBJ_CHANGE uevent is emitted for the container system device in question and user space is expected to offline all devices below the container and the container itself in response to it. Then, user space can finalize the removal of the container with the help of its ACPI device object's eject attribute in sysfs. Tested-by: Yasuaki Ishimatsu Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- drivers/acpi/container.c | 48 +++++++++++++++++++++++++++++++++++++++++++---- drivers/acpi/internal.h | 1 + drivers/acpi/scan.c | 8 +++++--- drivers/base/Makefile | 2 +- drivers/base/base.h | 1 + drivers/base/container.c | 44 +++++++++++++++++++++++++++++++++++++++++++ drivers/base/init.c | 1 + include/linux/container.h | 25 ++++++++++++++++++++++++ 8 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 drivers/base/container.c create mode 100644 include/linux/container.h (limited to 'drivers/base') diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 83d232c10f13..0b6ae6eb5c4a 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -27,8 +27,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include - -#include "internal.h" +#include #include "internal.h" @@ -44,16 +43,56 @@ static const struct acpi_device_id container_device_ids[] = { {"", 0}, }; +static int acpi_container_offline(struct container_dev *cdev) +{ + struct acpi_device *adev = ACPI_COMPANION(&cdev->dev); + struct acpi_device *child; + + /* Check all of the dependent devices' physical companions. */ + list_for_each_entry(child, &adev->children, node) + if (!acpi_scan_is_offline(child, false)) + return -EBUSY; + + return 0; +} + +static void acpi_container_release(struct device *dev) +{ + kfree(to_container_dev(dev)); +} + static int container_device_attach(struct acpi_device *adev, const struct acpi_device_id *not_used) { - kobject_uevent(&adev->dev.kobj, KOBJ_ONLINE); + struct container_dev *cdev; + struct device *dev; + int ret; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->offline = acpi_container_offline; + dev = &cdev->dev; + dev->bus = &container_subsys; + dev_set_name(dev, "%s", dev_name(&adev->dev)); + ACPI_COMPANION_SET(dev, adev); + dev->release = acpi_container_release; + ret = device_register(dev); + if (ret) + return ret; + + adev->driver_data = dev; return 1; } static void container_device_detach(struct acpi_device *adev) { - kobject_uevent(&adev->dev.kobj, KOBJ_OFFLINE); + struct device *dev = acpi_driver_data(adev); + + adev->driver_data = NULL; + if (dev) + device_unregister(dev); } static struct acpi_scan_handler container_handler = { @@ -62,6 +101,7 @@ static struct acpi_scan_handler container_handler = { .detach = container_device_detach, .hotplug = { .enabled = true, + .demand_offline = true, }, }; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index b125fdb0b30c..3375129bb5b7 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -73,6 +73,7 @@ static inline void acpi_lpss_init(void) {} #endif bool acpi_queue_hotplug_work(struct work_struct *work); +bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); /* -------------------------------------------------------------------------- Device Node Initialization / Removal diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 65243b9dd868..32b340171d41 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -126,7 +126,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static bool acpi_scan_is_offline(struct acpi_device *adev) +bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) { struct acpi_device_physical_node *pn; bool offline = true; @@ -135,7 +135,9 @@ static bool acpi_scan_is_offline(struct acpi_device *adev) list_for_each_entry(pn, &adev->physical_node_list, node) if (device_supports_offline(pn->dev) && !pn->dev->offline) { - kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); + if (uevent) + kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); + offline = false; break; } @@ -267,7 +269,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_status status; if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) { - if (!acpi_scan_is_offline(device)) + if (!acpi_scan_is_offline(device, true)) return -EBUSY; } else { int error = acpi_scan_try_to_offline(device); diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 94e8a80e87f8..d08c9d3b1d37 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -4,7 +4,7 @@ obj-y := core.o bus.o dd.o syscore.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o \ - topology.o + topology.o container.o obj-$(CONFIG_DEVTMPFS) += devtmpfs.o obj-$(CONFIG_DMA_CMA) += dma-contiguous.o obj-y += power/ diff --git a/drivers/base/base.h b/drivers/base/base.h index 2cbc6774f4cd..24f424249d9b 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -100,6 +100,7 @@ static inline int hypervisor_init(void) { return 0; } #endif extern int platform_bus_init(void); extern void cpu_dev_init(void); +extern void container_dev_init(void); struct kobject *virtual_device_parent(struct device *dev); diff --git a/drivers/base/container.c b/drivers/base/container.c new file mode 100644 index 000000000000..ecbfbe2e908f --- /dev/null +++ b/drivers/base/container.c @@ -0,0 +1,44 @@ +/* + * System bus type for containers. + * + * Copyright (C) 2013, Intel Corporation + * Author: Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include "base.h" + +#define CONTAINER_BUS_NAME "container" + +static int trivial_online(struct device *dev) +{ + return 0; +} + +static int container_offline(struct device *dev) +{ + struct container_dev *cdev = to_container_dev(dev); + + return cdev->offline ? cdev->offline(cdev) : 0; +} + +struct bus_type container_subsys = { + .name = CONTAINER_BUS_NAME, + .dev_name = CONTAINER_BUS_NAME, + .online = trivial_online, + .offline = container_offline, +}; + +void __init container_dev_init(void) +{ + int ret; + + ret = subsys_system_register(&container_subsys, NULL); + if (ret) + pr_err("%s() failed: %d\n", __func__, ret); +} diff --git a/drivers/base/init.c b/drivers/base/init.c index c16f0b808a17..da033d3bab3c 100644 --- a/drivers/base/init.c +++ b/drivers/base/init.c @@ -33,4 +33,5 @@ void __init driver_init(void) platform_bus_init(); cpu_dev_init(); memory_dev_init(); + container_dev_init(); } diff --git a/include/linux/container.h b/include/linux/container.h new file mode 100644 index 000000000000..3c03e6fd2035 --- /dev/null +++ b/include/linux/container.h @@ -0,0 +1,25 @@ +/* + * Definitions for container bus type. + * + * Copyright (C) 2013, Intel Corporation + * Author: Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +/* drivers/base/power/container.c */ +extern struct bus_type container_subsys; + +struct container_dev { + struct device dev; + int (*offline)(struct container_dev *cdev); +}; + +static inline struct container_dev *to_container_dev(struct device *dev) +{ + return container_of(dev, struct container_dev, dev); +} -- cgit 1.4.1 From 8a6720ec2020f01756154d9c272f88a6af76fb81 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 14 Jan 2014 12:23:40 +0000 Subject: PM / clock_ops: fix up clk prepare/unprepare count The drivers/base/power/clock_ops.c file is causing warnings from the clock driver (as shown below) due to failing to do a clk_prepare() call before enabling a clock. It also fails to check the balance of prepare/unprepare as __pm_clk_remove() do clk_disable_unprepare() call. This bug has probably been in since commit b2476490e ("clk: introduce the common clock framework") as the warning was part of the original commit. It is strange that it has not been noticed (although this has also been coupled with a failure for certain SH builds to not build the necessary glue to use this method of controlling the clocks). In summary, this is probably needed in several stable branches but need advice on which ones. On the Renesas Lager board, this causes numerous warnings of the following and even worse the clock system will not enable clocks, causing drivers that are in development to fail to work: WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:883 __clk_enable+0x2c/0xa0() Signed-off-by: Ben Dooks Reviewed-by: Ian Molton Signed-off-by: Rafael J. Wysocki --- drivers/base/power/clock_ops.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 9d8fde709390..b9dd8fac87d7 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -43,6 +43,7 @@ static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) if (IS_ERR(ce->clk)) { ce->status = PCE_STATUS_ERROR; } else { + clk_prepare(ce->clk); ce->status = PCE_STATUS_ACQUIRED; dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id); } @@ -99,10 +100,12 @@ static void __pm_clk_remove(struct pm_clock_entry *ce) if (ce->status < PCE_STATUS_ERROR) { if (ce->status == PCE_STATUS_ENABLED) - clk_disable_unprepare(ce->clk); + clk_disable(ce->clk); - if (ce->status >= PCE_STATUS_ACQUIRED) + if (ce->status >= PCE_STATUS_ACQUIRED) { + clk_unprepare(ce->clk); clk_put(ce->clk); + } } kfree(ce->con_id); -- cgit 1.4.1 From afdd3ab315a4454ccb7895ddade2c20bdf91f5c6 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 14 Jan 2014 12:23:41 +0000 Subject: PM / clock_ops: check return of clk_enable() in pm_clk_resume() The clk_enable() call in the pm_clk_resume() call returns an error that is not being checked. If clk_enable() fails then we should not set the state of the clock to PCE_STATUS_ENABLED. Note, the issue of warning the user if this fails has not been addressed in this patch as this is not the only place the driver calls clk_enable(). Signed-off-by: Ben Dooks Reviewed-by: Ian Molton Signed-off-by: Rafael J. Wysocki --- drivers/base/power/clock_ops.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index b9dd8fac87d7..cad7190465d6 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -252,6 +252,7 @@ int pm_clk_resume(struct device *dev) struct pm_subsys_data *psd = dev_to_psd(dev); struct pm_clock_entry *ce; unsigned long flags; + int ret; dev_dbg(dev, "%s()\n", __func__); @@ -262,8 +263,9 @@ int pm_clk_resume(struct device *dev) list_for_each_entry(ce, &psd->clock_list, node) { if (ce->status < PCE_STATUS_ERROR) { - clk_enable(ce->clk); - ce->status = PCE_STATUS_ENABLED; + ret = clk_enable(ce->clk); + if (!ret) + ce->status = PCE_STATUS_ENABLED; } } -- cgit 1.4.1 From 5cda3fbb155bff96c971d058ed040d5c85612fd8 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 14 Jan 2014 12:23:42 +0000 Subject: PM / clock_ops: report clock errors from clk_enable() If clk_enable() fails, then print a message so that the user can see what is happening instead of silently failing to enable the clock. Signed-off-by: Ben Dooks Reviewed-by: Ian Molton Signed-off-by: Rafael J. Wysocki --- drivers/base/power/clock_ops.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index cad7190465d6..e870bbe9ec4e 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -32,6 +32,21 @@ struct pm_clock_entry { enum pce_status status; }; +/** + * pm_clk_enable - Enable a clock, reporting any errors + * @dev: The device for the given clock + * @clk: The clock being enabled. + */ +static inline int __pm_clk_enable(struct device *dev, struct clk *clk) +{ + int ret = clk_enable(clk); + if (ret) + dev_err(dev, "%s: failed to enable clk %p, error %d\n", + __func__, clk, ret); + + return ret; +} + /** * pm_clk_acquire - Acquire a device clock. * @dev: Device whose clock is to be acquired. @@ -263,7 +278,7 @@ int pm_clk_resume(struct device *dev) list_for_each_entry(ce, &psd->clock_list, node) { if (ce->status < PCE_STATUS_ERROR) { - ret = clk_enable(ce->clk); + ret = __pm_clk_enable(dev, ce->clk); if (!ret) ce->status = PCE_STATUS_ENABLED; } @@ -381,7 +396,7 @@ int pm_clk_resume(struct device *dev) spin_lock_irqsave(&psd->lock, flags); list_for_each_entry(ce, &psd->clock_list, node) - clk_enable(ce->clk); + __pm_clk_enable(dev, ce->clk); spin_unlock_irqrestore(&psd->lock, flags); -- cgit 1.4.1 From 8c4ff6d0094a16809a7ecdd49d71cf06b0a51326 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 14 Jan 2014 16:46:37 +0800 Subject: ACPI: fix module autoloading for ACPI enumerated devices ACPI enumerated devices has ACPI style _HID and _CID strings, all of these strings can be used for both driver loading and matching. Currently, in Platform, I2C and SPI bus, the ACPI style driver matching is supported by invoking acpi_driver_match_device() in bus .match() callback. But, the module autoloading is still broken. For example, there is any ACPI device with _HID "INTABCD" that is enumerated to platform bus, and we have a driver that can probe it. The driver exports its module_alias as "acpi:INTABCD" use the following code static const struct acpi_device_id xxx_acpi_match[] = { { "INTABCD", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, xxx_acpi_match); But, unfortunately, the device' modalias is shown as "platform:INTABCD:00", please refer to modalias_show() and platform_uevent() in drivers/base/platform.c. This results in that the driver will not be loaded automatically when the device node is created, because their modalias do not match. This also applies to I2C and SPI bus. With this patch, the device' modalias will be shown as "acpi:INTABCD" as well. Signed-off-by: Zhang Rui Acked-by: Mark Brown Acked-by: Wolfram Sang Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 12 +++++++++++- drivers/i2c/i2c-core.c | 11 +++++++++++ drivers/spi/spi.c | 10 ++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 3a94b799f166..2f4aea2428b2 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -677,7 +677,13 @@ 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); + int len; + + len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); + if (len != -ENODEV) + return len; + + len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name); return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; } @@ -699,6 +705,10 @@ static int platform_uevent(struct device *dev, struct kobj_uevent_env *env) if (rc != -ENODEV) return rc; + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; + add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX, pdev->name); return 0; diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index d74c0b34248e..c4c5588ec0fb 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -104,6 +104,11 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv) static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) { struct i2c_client *client = to_i2c_client(dev); + int rc; + + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; if (add_uevent_var(env, "MODALIAS=%s%s", I2C_MODULE_PREFIX, client->name)) @@ -409,6 +414,12 @@ static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); + int len; + + len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); + if (len != -ENODEV) + return len; + return sprintf(buf, "%s%s\n", I2C_MODULE_PREFIX, client->name); } diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 349ebba4b199..827ff49d3d4f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -58,6 +58,11 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, char *buf) { const struct spi_device *spi = to_spi_device(dev); + int len; + + len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); + if (len != -ENODEV) + return len; return sprintf(buf, "%s%s\n", SPI_MODULE_PREFIX, spi->modalias); } @@ -114,6 +119,11 @@ static int spi_match_device(struct device *dev, struct device_driver *drv) static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) { const struct spi_device *spi = to_spi_device(dev); + int rc; + + rc = acpi_device_uevent_modalias(dev, env); + if (rc != -ENODEV) + return rc; add_uevent_var(env, "MODALIAS=%s%s", SPI_MODULE_PREFIX, spi->modalias); return 0; -- cgit 1.4.1 From b9f73067f32531db608e469a9ad20ce631e34550 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 14 Jan 2014 16:46:38 +0800 Subject: platform: introduce OF style 'modalias' support for platform bus Fix a problem that, the platform bus supports the OF style modalias in .uevent() call, but not in its device 'modalias' sysfs attribute. Signed-off-by: Zhang Rui Acked-by: Rob Herring Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 4 ++++ drivers/of/device.c | 3 +++ include/linux/of_device.h | 6 ++++++ 3 files changed, 13 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 2f4aea2428b2..bc78848dd59a 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -679,6 +679,10 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, struct platform_device *pdev = to_platform_device(dev); int len; + len = of_device_get_modalias(dev, buf, PAGE_SIZE -1); + if (len != -ENODEV) + return len; + len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); if (len != -ENODEV) return len; diff --git a/drivers/of/device.c b/drivers/of/device.c index f685e55e0717..dafb9736ab9b 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -85,6 +85,9 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len) int cplen, i; ssize_t tsize, csize, repend; + if ((!dev) || (!dev->of_node)) + return -ENODEV; + /* Name & Type */ csize = snprintf(str, len, "of:N%sT%s", dev->of_node->name, dev->of_node->type); diff --git a/include/linux/of_device.h b/include/linux/of_device.h index 82ce324fdce7..8d7dd6768cb7 100644 --- a/include/linux/of_device.h +++ b/include/linux/of_device.h @@ -64,6 +64,12 @@ static inline int of_driver_match_device(struct device *dev, static inline void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) { } +static inline int of_device_get_modalias(struct device *dev, + char *str, ssize_t len) +{ + return -ENODEV; +} + static inline int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) { -- cgit 1.4.1