mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-21 19:53:59 +08:00
greybus: interface: add runtime pm support
Configure and enable runtime pm support for the Interface. Refer to the 12.2. The Interface Lifecycle of the Greybus specification for details on the requirements for transitioning from ENUMERATED to SUSPEND and vice versa. All the Bundles for the Interface have to be either OFF or SUSPENDED before the Interface can be autosuspended. Testing Done: - Check the runtime_status of an interface driver and validate the suspend current of a module. Signed-off-by: David Lin <dtwlin@google.com> Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org>
This commit is contained in:
parent
2d48b5b4a8
commit
30a3bf7b30
@ -18,6 +18,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include "kernel_ver.h"
|
||||
|
@ -7,6 +7,8 @@
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include "greybus_trace.h"
|
||||
|
||||
@ -14,6 +16,11 @@
|
||||
|
||||
#define GB_INTERFACE_DEVICE_ID_BAD 0xff
|
||||
|
||||
#define GB_INTERFACE_AUTOSUSPEND_MS 3000
|
||||
|
||||
/* Time required for interface to enter standby before disabling REFCLK */
|
||||
#define GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS 20
|
||||
|
||||
/* Don't-care selector index */
|
||||
#define DME_SELECTOR_INDEX_NULL 0
|
||||
|
||||
@ -36,6 +43,8 @@
|
||||
#define TOSHIBA_ES3_APBRIDGE_DPID 0x1001
|
||||
#define TOSHIBA_ES3_GBPHY_DPID 0x1002
|
||||
|
||||
static int gb_interface_hibernate_link(struct gb_interface *intf);
|
||||
static int gb_interface_refclk_set(struct gb_interface *intf, bool enable);
|
||||
|
||||
static int gb_interface_dme_attr_get(struct gb_interface *intf,
|
||||
u16 attr, u32 *val)
|
||||
@ -505,9 +514,92 @@ static void gb_interface_release(struct device *dev)
|
||||
kfree(intf);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int gb_interface_suspend(struct device *dev)
|
||||
{
|
||||
struct gb_interface *intf = to_gb_interface(dev);
|
||||
int ret, timesync_ret;
|
||||
|
||||
ret = gb_control_interface_suspend_prepare(intf->control);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gb_timesync_interface_remove(intf);
|
||||
|
||||
ret = gb_control_suspend(intf->control);
|
||||
if (ret)
|
||||
goto err_hibernate_abort;
|
||||
|
||||
ret = gb_interface_hibernate_link(intf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Delay to allow interface to enter standby before disabling refclk */
|
||||
msleep(GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS);
|
||||
|
||||
ret = gb_interface_refclk_set(intf, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
|
||||
err_hibernate_abort:
|
||||
gb_control_interface_hibernate_abort(intf->control);
|
||||
|
||||
timesync_ret = gb_timesync_interface_add(intf);
|
||||
if (timesync_ret) {
|
||||
dev_err(dev, "failed to add to timesync: %d\n", timesync_ret);
|
||||
return timesync_ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gb_interface_resume(struct device *dev)
|
||||
{
|
||||
struct gb_interface *intf = to_gb_interface(dev);
|
||||
struct gb_svc *svc = intf->hd->svc;
|
||||
int ret;
|
||||
|
||||
ret = gb_interface_refclk_set(intf, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gb_svc_intf_resume(svc, intf->interface_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gb_control_resume(intf->control);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gb_timesync_interface_add(intf);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add to timesync: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gb_interface_runtime_idle(struct device *dev)
|
||||
{
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_request_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops gb_interface_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(gb_interface_suspend, gb_interface_resume,
|
||||
gb_interface_runtime_idle)
|
||||
};
|
||||
|
||||
struct device_type greybus_interface_type = {
|
||||
.name = "greybus_interface",
|
||||
.release = gb_interface_release,
|
||||
.pm = &gb_interface_pm_ops,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -553,6 +645,9 @@ struct gb_interface *gb_interface_create(struct gb_module *module,
|
||||
dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev),
|
||||
interface_id);
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&intf->dev,
|
||||
GB_INTERFACE_AUTOSUSPEND_MS);
|
||||
|
||||
trace_gb_interface_create(intf);
|
||||
|
||||
return intf;
|
||||
@ -809,6 +904,11 @@ int gb_interface_enable(struct gb_interface *intf)
|
||||
goto err_destroy_bundles;
|
||||
}
|
||||
|
||||
pm_runtime_use_autosuspend(&intf->dev);
|
||||
pm_runtime_get_noresume(&intf->dev);
|
||||
pm_runtime_set_active(&intf->dev);
|
||||
pm_runtime_enable(&intf->dev);
|
||||
|
||||
list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) {
|
||||
ret = gb_bundle_add(bundle);
|
||||
if (ret) {
|
||||
@ -821,6 +921,8 @@ int gb_interface_enable(struct gb_interface *intf)
|
||||
|
||||
intf->enabled = true;
|
||||
|
||||
pm_runtime_put(&intf->dev);
|
||||
|
||||
trace_gb_interface_enable(intf);
|
||||
|
||||
return 0;
|
||||
@ -854,6 +956,8 @@ void gb_interface_disable(struct gb_interface *intf)
|
||||
|
||||
trace_gb_interface_disable(intf);
|
||||
|
||||
pm_runtime_get_sync(&intf->dev);
|
||||
|
||||
/* Set disconnected flag to avoid I/O during connection tear down. */
|
||||
if (intf->quirks & GB_INTERFACE_QUIRK_FORCED_DISABLE)
|
||||
intf->disconnected = true;
|
||||
@ -871,6 +975,11 @@ void gb_interface_disable(struct gb_interface *intf)
|
||||
intf->control = NULL;
|
||||
|
||||
intf->enabled = false;
|
||||
|
||||
pm_runtime_disable(&intf->dev);
|
||||
pm_runtime_set_suspended(&intf->dev);
|
||||
pm_runtime_dont_use_autosuspend(&intf->dev);
|
||||
pm_runtime_put_noidle(&intf->dev);
|
||||
}
|
||||
|
||||
/* Enable TimeSync on an Interface control connection. */
|
||||
|
@ -389,4 +389,17 @@ static inline int kstrtobool(const char *s, bool *res)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||
/*
|
||||
* After commit b2b49ccbdd54 (PM: Kconfig: Set PM_RUNTIME if PM_SLEEP is
|
||||
* selected) PM_RUNTIME is always set if PM is set, so files that are build
|
||||
* conditionally if CONFIG_PM_RUNTIME is set may now be build if CONFIG_PM is
|
||||
* set.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#define CONFIG_PM_RUNTIME
|
||||
#endif /* CONFIG_PM */
|
||||
#endif
|
||||
|
||||
#endif /* __GREYBUS_KERNEL_VER_H */
|
||||
|
Loading…
Reference in New Issue
Block a user