|
@@ -0,0 +1,147 @@
|
|
|
+/*
|
|
|
+ * omap_device implementation
|
|
|
+ *
|
|
|
+ * Copyright (C) 2009-2010 Nokia Corporation
|
|
|
+ * Paul Walmsley, Kevin Hilman
|
|
|
+ *
|
|
|
+ * Developed in collaboration with (alphabetical order): Benoit
|
|
|
+ * Cousson, Thara Gopinath, Tony Lindgren, Rajendra Nayak, Vikram
|
|
|
+ * Pandita, Sakari Poussa, Anand Sawant, Santosh Shilimkar, Richard
|
|
|
+ * Woodruff
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ *
|
|
|
+ * This code provides a consistent interface for OMAP device drivers
|
|
|
+ * to control power management and interconnect properties of their
|
|
|
+ * devices.
|
|
|
+ *
|
|
|
+ * In the medium- to long-term, this code should either be
|
|
|
+ * a) implemented via arch-specific pointers in platform_data
|
|
|
+ * or
|
|
|
+ * b) implemented as a proper omap_bus/omap_device in Linux, no more
|
|
|
+ * platform_data func pointers
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * Guidelines for usage by driver authors:
|
|
|
+ *
|
|
|
+ * 1. These functions are intended to be used by device drivers via
|
|
|
+ * function pointers in struct platform_data. As an example,
|
|
|
+ * omap_device_enable() should be passed to the driver as
|
|
|
+ *
|
|
|
+ * struct foo_driver_platform_data {
|
|
|
+ * ...
|
|
|
+ * int (*device_enable)(struct platform_device *pdev);
|
|
|
+ * ...
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * Note that the generic "device_enable" name is used, rather than
|
|
|
+ * "omap_device_enable". This is so other architectures can pass in their
|
|
|
+ * own enable/disable functions here.
|
|
|
+ *
|
|
|
+ * This should be populated during device setup:
|
|
|
+ *
|
|
|
+ * ...
|
|
|
+ * pdata->device_enable = omap_device_enable;
|
|
|
+ * ...
|
|
|
+ *
|
|
|
+ * 2. Drivers should first check to ensure the function pointer is not null
|
|
|
+ * before calling it, as in:
|
|
|
+ *
|
|
|
+ * if (pdata->device_enable)
|
|
|
+ * pdata->device_enable(pdev);
|
|
|
+ *
|
|
|
+ * This allows other architectures that don't use similar device_enable()/
|
|
|
+ * device_shutdown() functions to execute normally.
|
|
|
+ *
|
|
|
+ * ...
|
|
|
+ *
|
|
|
+ * Suggested usage by device drivers:
|
|
|
+ *
|
|
|
+ * During device initialization:
|
|
|
+ * device_enable()
|
|
|
+ *
|
|
|
+ * During device idle:
|
|
|
+ * (save remaining device context if necessary)
|
|
|
+ * device_idle();
|
|
|
+ *
|
|
|
+ * During device resume:
|
|
|
+ * device_enable();
|
|
|
+ * (restore context if necessary)
|
|
|
+ *
|
|
|
+ * During device shutdown:
|
|
|
+ * device_shutdown()
|
|
|
+ * (device must be reinitialized at this point to use it again)
|
|
|
+ *
|
|
|
+ */
|
|
|
+#undef DEBUG
|
|
|
+
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/export.h>
|
|
|
+#include <linux/platform_device.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/err.h>
|
|
|
+#include <linux/io.h>
|
|
|
+#include <linux/clk.h>
|
|
|
+#include <linux/clkdev.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
+#include <linux/of.h>
|
|
|
+#include <linux/notifier.h>
|
|
|
+
|
|
|
+#include "omap_device.h"
|
|
|
+#include "omap_hwmod.h"
|
|
|
+
|
|
|
+/* These parameters are passed to _omap_device_{de,}activate() */
|
|
|
+#define USE_WAKEUP_LAT 0
|
|
|
+#define IGNORE_WAKEUP_LAT 1
|
|
|
+
|
|
|
+static int omap_early_device_register(struct platform_device *pdev);
|
|
|
+
|
|
|
+static struct omap_device_pm_latency omap_default_latency[] = {
|
|
|
+ {
|
|
|
+ .deactivate_func = omap_device_idle_hwmods,
|
|
|
+ .activate_func = omap_device_enable_hwmods,
|
|
|
+ .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/* Private functions */
|
|
|
+
|
|
|
+/**
|
|
|
+ * _omap_device_activate - increase device readiness
|
|
|
+ * @od: struct omap_device *
|
|
|
+ * @ignore_lat: increase to latency target (0) or full readiness (1)?
|
|
|
+ *
|
|
|
+ * Increase readiness of omap_device @od (thus decreasing device
|
|
|
+ * wakeup latency, but consuming more power). If @ignore_lat is
|
|
|
+ * IGNORE_WAKEUP_LAT, make the omap_device fully active. Otherwise,
|
|
|
+ * if @ignore_lat is USE_WAKEUP_LAT, and the device's maximum wakeup
|
|
|
+ * latency is greater than the requested maximum wakeup latency, step
|
|
|
+ * backwards in the omap_device_pm_latency table to ensure the
|
|
|
+ * device's maximum wakeup latency is less than or equal to the
|
|
|
+ * requested maximum wakeup latency. Returns 0.
|
|
|
+ */
|
|
|
+static int _omap_device_activate(struct omap_device *od, u8 ignore_lat)
|
|
|
+{
|
|
|
+ struct timespec a, b, c;
|
|
|
+
|
|
|
+ dev_dbg(&od->pdev->dev, "omap_device: activating\n");
|
|
|
+
|
|
|
+ while (od->pm_lat_level > 0) {
|
|
|
+ struct omap_device_pm_latency *odpl;
|
|
|
+ unsigned long long act_lat = 0;
|
|
|
+
|
|
|
+ od->pm_lat_level--;
|
|
|
+
|
|
|
+ odpl = od->pm_lats + od->pm_lat_level;
|
|
|
+
|
|
|
+ if (!ignore_lat &&
|
|
|
+ (od->dev_wakeup_lat <= od->_dev_wakeup_lat_limit))
|
|
|
+ break;
|
|
|
+
|
|
|
+ read_persistent_clock(&a);
|
|
|
+
|
|
|
+ /* XXX check return code */
|
|
|
+ odpl->activate_func(od);
|
|
|
+
|