aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorTomasz Figa <tomasz.figa@gmail.com>2014-09-19 14:27:36 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-09-22 09:57:40 -0400
commitaa42240ab2544a8bcb2efb400193826f57f3175e (patch)
tree00ce21c5dfdf974340f0e0921dd1a91bc77f3292 /drivers/base
parent86f1e15f5646b4855bd77025c950239650c4843e (diff)
PM / Domains: Add generic OF-based PM domain look-up
This patch introduces generic code to perform PM domain look-up using device tree and automatically bind devices to their PM domains. Generic device tree bindings are introduced to specify PM domains of devices in their device tree nodes. Backwards compatibility with legacy Samsung-specific PM domain bindings is provided, but for now the new code is not compiled when CONFIG_ARCH_EXYNOS is selected to avoid collision with legacy code. This will change as soon as the Exynos PM domain code gets converted to use the generic framework in further patch. This patch was originally submitted by Tomasz Figa when he was employed by Samsung. Link: http://marc.info/?l=linux-pm&m=139955349702152&w=2 Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Acked-by: Rob Herring <robh@kernel.org> Tested-by: Philipp Zabel <p.zabel@pengutronix.de> Reviewed-by: Kevin Hilman <khilman@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/domain.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index e6a11ca3ce26..a3d41a883d83 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -8,6 +8,7 @@
8 8
9#include <linux/kernel.h> 9#include <linux/kernel.h>
10#include <linux/io.h> 10#include <linux/io.h>
11#include <linux/platform_device.h>
11#include <linux/pm_runtime.h> 12#include <linux/pm_runtime.h>
12#include <linux/pm_domain.h> 13#include <linux/pm_domain.h>
13#include <linux/pm_qos.h> 14#include <linux/pm_qos.h>
@@ -1933,3 +1934,291 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
1933 list_add(&genpd->gpd_list_node, &gpd_list); 1934 list_add(&genpd->gpd_list_node, &gpd_list);
1934 mutex_unlock(&gpd_list_lock); 1935 mutex_unlock(&gpd_list_lock);
1935} 1936}
1937
1938#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
1939/*
1940 * Device Tree based PM domain providers.
1941 *
1942 * The code below implements generic device tree based PM domain providers that
1943 * bind device tree nodes with generic PM domains registered in the system.
1944 *
1945 * Any driver that registers generic PM domains and needs to support binding of
1946 * devices to these domains is supposed to register a PM domain provider, which
1947 * maps a PM domain specifier retrieved from the device tree to a PM domain.
1948 *
1949 * Two simple mapping functions have been provided for convenience:
1950 * - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
1951 * - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by
1952 * index.
1953 */
1954
1955/**
1956 * struct of_genpd_provider - PM domain provider registration structure
1957 * @link: Entry in global list of PM domain providers
1958 * @node: Pointer to device tree node of PM domain provider
1959 * @xlate: Provider-specific xlate callback mapping a set of specifier cells
1960 * into a PM domain.
1961 * @data: context pointer to be passed into @xlate callback
1962 */
1963struct of_genpd_provider {
1964 struct list_head link;
1965 struct device_node *node;
1966 genpd_xlate_t xlate;
1967 void *data;
1968};
1969
1970/* List of registered PM domain providers. */
1971static LIST_HEAD(of_genpd_providers);
1972/* Mutex to protect the list above. */
1973static DEFINE_MUTEX(of_genpd_mutex);
1974
1975/**
1976 * __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping
1977 * @genpdspec: OF phandle args to map into a PM domain
1978 * @data: xlate function private data - pointer to struct generic_pm_domain
1979 *
1980 * This is a generic xlate function that can be used to model PM domains that
1981 * have their own device tree nodes. The private data of xlate function needs
1982 * to be a valid pointer to struct generic_pm_domain.
1983 */
1984struct generic_pm_domain *__of_genpd_xlate_simple(
1985 struct of_phandle_args *genpdspec,
1986 void *data)
1987{
1988 if (genpdspec->args_count != 0)
1989 return ERR_PTR(-EINVAL);
1990 return data;
1991}
1992EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple);
1993
1994/**
1995 * __of_genpd_xlate_onecell() - Xlate function using a single index.
1996 * @genpdspec: OF phandle args to map into a PM domain
1997 * @data: xlate function private data - pointer to struct genpd_onecell_data
1998 *
1999 * This is a generic xlate function that can be used to model simple PM domain
2000 * controllers that have one device tree node and provide multiple PM domains.
2001 * A single cell is used as an index into an array of PM domains specified in
2002 * the genpd_onecell_data struct when registering the provider.
2003 */
2004struct generic_pm_domain *__of_genpd_xlate_onecell(
2005 struct of_phandle_args *genpdspec,
2006 void *data)
2007{
2008 struct genpd_onecell_data *genpd_data = data;
2009 unsigned int idx = genpdspec->args[0];
2010
2011 if (genpdspec->args_count != 1)
2012 return ERR_PTR(-EINVAL);
2013
2014 if (idx >= genpd_data->num_domains) {
2015 pr_err("%s: invalid domain index %u\n", __func__, idx);
2016 return ERR_PTR(-EINVAL);
2017 }
2018
2019 if (!genpd_data->domains[idx])
2020 return ERR_PTR(-ENOENT);
2021
2022 return genpd_data->domains[idx];
2023}
2024EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell);
2025
2026/**
2027 * __of_genpd_add_provider() - Register a PM domain provider for a node
2028 * @np: Device node pointer associated with the PM domain provider.
2029 * @xlate: Callback for decoding PM domain from phandle arguments.
2030 * @data: Context pointer for @xlate callback.
2031 */
2032int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
2033 void *data)
2034{
2035 struct of_genpd_provider *cp;
2036
2037 cp = kzalloc(sizeof(*cp), GFP_KERNEL);
2038 if (!cp)
2039 return -ENOMEM;
2040
2041 cp->node = of_node_get(np);
2042 cp->data = data;
2043 cp->xlate = xlate;
2044
2045 mutex_lock(&of_genpd_mutex);
2046 list_add(&cp->link, &of_genpd_providers);
2047 mutex_unlock(&of_genpd_mutex);
2048 pr_debug("Added domain provider from %s\n", np->full_name);
2049
2050 return 0;
2051}
2052EXPORT_SYMBOL_GPL(__of_genpd_add_provider);
2053
2054/**
2055 * of_genpd_del_provider() - Remove a previously registered PM domain provider
2056 * @np: Device node pointer associated with the PM domain provider
2057 */
2058void of_genpd_del_provider(struct device_node *np)
2059{
2060 struct of_genpd_provider *cp;
2061
2062 mutex_lock(&of_genpd_mutex);
2063 list_for_each_entry(cp, &of_genpd_providers, link) {
2064 if (cp->node == np) {
2065 list_del(&cp->link);
2066 of_node_put(cp->node);
2067 kfree(cp);
2068 break;
2069 }
2070 }
2071 mutex_unlock(&of_genpd_mutex);
2072}
2073EXPORT_SYMBOL_GPL(of_genpd_del_provider);
2074
2075/**
2076 * of_genpd_get_from_provider() - Look-up PM domain
2077 * @genpdspec: OF phandle args to use for look-up
2078 *
2079 * Looks for a PM domain provider under the node specified by @genpdspec and if
2080 * found, uses xlate function of the provider to map phandle args to a PM
2081 * domain.
2082 *
2083 * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
2084 * on failure.
2085 */
2086static struct generic_pm_domain *of_genpd_get_from_provider(
2087 struct of_phandle_args *genpdspec)
2088{
2089 struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
2090 struct of_genpd_provider *provider;
2091
2092 mutex_lock(&of_genpd_mutex);
2093
2094 /* Check if we have such a provider in our array */
2095 list_for_each_entry(provider, &of_genpd_providers, link) {
2096 if (provider->node == genpdspec->np)
2097 genpd = provider->xlate(genpdspec, provider->data);
2098 if (!IS_ERR(genpd))
2099 break;
2100 }
2101
2102 mutex_unlock(&of_genpd_mutex);
2103
2104 return genpd;
2105}
2106
2107/**
2108 * genpd_dev_pm_detach - Detach a device from its PM domain.
2109 * @dev: Device to attach.
2110 * @power_off: Currently not used
2111 *
2112 * Try to locate a corresponding generic PM domain, which the device was
2113 * attached to previously. If such is found, the device is detached from it.
2114 */
2115static void genpd_dev_pm_detach(struct device *dev, bool power_off)
2116{
2117 struct generic_pm_domain *pd = NULL, *gpd;
2118 int ret = 0;
2119
2120 if (!dev->pm_domain)
2121 return;
2122
2123 mutex_lock(&gpd_list_lock);
2124 list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
2125 if (&gpd->domain == dev->pm_domain) {
2126 pd = gpd;
2127 break;
2128 }
2129 }
2130 mutex_unlock(&gpd_list_lock);
2131
2132 if (!pd)
2133 return;
2134
2135 dev_dbg(dev, "removing from PM domain %s\n", pd->name);
2136
2137 while (1) {
2138 ret = pm_genpd_remove_device(pd, dev);
2139 if (ret != -EAGAIN)
2140 break;
2141 cond_resched();
2142 }
2143
2144 if (ret < 0) {
2145 dev_err(dev, "failed to remove from PM domain %s: %d",
2146 pd->name, ret);
2147 return;
2148 }
2149
2150 /* Check if PM domain can be powered off after removing this device. */
2151 genpd_queue_power_off_work(pd);
2152}
2153
2154/**
2155 * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
2156 * @dev: Device to attach.
2157 *
2158 * Parse device's OF node to find a PM domain specifier. If such is found,
2159 * attaches the device to retrieved pm_domain ops.
2160 *
2161 * Both generic and legacy Samsung-specific DT bindings are supported to keep
2162 * backwards compatibility with existing DTBs.
2163 *
2164 * Returns 0 on successfully attached PM domain or negative error code.
2165 */
2166int genpd_dev_pm_attach(struct device *dev)
2167{
2168 struct of_phandle_args pd_args;
2169 struct generic_pm_domain *pd;
2170 int ret;
2171
2172 if (!dev->of_node)
2173 return -ENODEV;
2174
2175 if (dev->pm_domain)
2176 return -EEXIST;
2177
2178 ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
2179 "#power-domain-cells", 0, &pd_args);
2180 if (ret < 0) {
2181 if (ret != -ENOENT)
2182 return ret;
2183
2184 /*
2185 * Try legacy Samsung-specific bindings
2186 * (for backwards compatibility of DT ABI)
2187 */
2188 pd_args.args_count = 0;
2189 pd_args.np = of_parse_phandle(dev->of_node,
2190 "samsung,power-domain", 0);
2191 if (!pd_args.np)
2192 return -ENOENT;
2193 }
2194
2195 pd = of_genpd_get_from_provider(&pd_args);
2196 if (IS_ERR(pd)) {
2197 dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
2198 __func__, PTR_ERR(pd));
2199 of_node_put(dev->of_node);
2200 return PTR_ERR(pd);
2201 }
2202
2203 dev_dbg(dev, "adding to PM domain %s\n", pd->name);
2204
2205 while (1) {
2206 ret = pm_genpd_add_device(pd, dev);
2207 if (ret != -EAGAIN)
2208 break;
2209 cond_resched();
2210 }
2211
2212 if (ret < 0) {
2213 dev_err(dev, "failed to add to PM domain %s: %d",
2214 pd->name, ret);
2215 of_node_put(dev->of_node);
2216 return ret;
2217 }
2218
2219 dev->pm_domain->detach = genpd_dev_pm_detach;
2220
2221 return 0;
2222}
2223EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
2224#endif