cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pm_domains.c (3966B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Exynos Generic power domain support.
      4//
      5// Copyright (c) 2012 Samsung Electronics Co., Ltd.
      6//		http://www.samsung.com
      7//
      8// Implementation of Exynos specific power domain control which is used in
      9// conjunction with runtime-pm. Support for both device-tree and non-device-tree
     10// based power domain support is included.
     11
     12#include <linux/io.h>
     13#include <linux/err.h>
     14#include <linux/slab.h>
     15#include <linux/pm_domain.h>
     16#include <linux/delay.h>
     17#include <linux/of_address.h>
     18#include <linux/of_platform.h>
     19#include <linux/pm_runtime.h>
     20
     21struct exynos_pm_domain_config {
     22	/* Value for LOCAL_PWR_CFG and STATUS fields for each domain */
     23	u32 local_pwr_cfg;
     24};
     25
     26/*
     27 * Exynos specific wrapper around the generic power domain
     28 */
     29struct exynos_pm_domain {
     30	void __iomem *base;
     31	struct generic_pm_domain pd;
     32	u32 local_pwr_cfg;
     33};
     34
     35static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
     36{
     37	struct exynos_pm_domain *pd;
     38	void __iomem *base;
     39	u32 timeout, pwr;
     40	char *op;
     41
     42	pd = container_of(domain, struct exynos_pm_domain, pd);
     43	base = pd->base;
     44
     45	pwr = power_on ? pd->local_pwr_cfg : 0;
     46	writel_relaxed(pwr, base);
     47
     48	/* Wait max 1ms */
     49	timeout = 10;
     50
     51	while ((readl_relaxed(base + 0x4) & pd->local_pwr_cfg) != pwr) {
     52		if (!timeout) {
     53			op = (power_on) ? "enable" : "disable";
     54			pr_err("Power domain %s %s failed\n", domain->name, op);
     55			return -ETIMEDOUT;
     56		}
     57		timeout--;
     58		cpu_relax();
     59		usleep_range(80, 100);
     60	}
     61
     62	return 0;
     63}
     64
     65static int exynos_pd_power_on(struct generic_pm_domain *domain)
     66{
     67	return exynos_pd_power(domain, true);
     68}
     69
     70static int exynos_pd_power_off(struct generic_pm_domain *domain)
     71{
     72	return exynos_pd_power(domain, false);
     73}
     74
     75static const struct exynos_pm_domain_config exynos4210_cfg = {
     76	.local_pwr_cfg		= 0x7,
     77};
     78
     79static const struct exynos_pm_domain_config exynos5433_cfg = {
     80	.local_pwr_cfg		= 0xf,
     81};
     82
     83static const struct of_device_id exynos_pm_domain_of_match[] = {
     84	{
     85		.compatible = "samsung,exynos4210-pd",
     86		.data = &exynos4210_cfg,
     87	}, {
     88		.compatible = "samsung,exynos5433-pd",
     89		.data = &exynos5433_cfg,
     90	},
     91	{ },
     92};
     93
     94static const char *exynos_get_domain_name(struct device_node *node)
     95{
     96	const char *name;
     97
     98	if (of_property_read_string(node, "label", &name) < 0)
     99		name = kbasename(node->full_name);
    100	return kstrdup_const(name, GFP_KERNEL);
    101}
    102
    103static int exynos_pd_probe(struct platform_device *pdev)
    104{
    105	const struct exynos_pm_domain_config *pm_domain_cfg;
    106	struct device *dev = &pdev->dev;
    107	struct device_node *np = dev->of_node;
    108	struct of_phandle_args child, parent;
    109	struct exynos_pm_domain *pd;
    110	int on, ret;
    111
    112	pm_domain_cfg = of_device_get_match_data(dev);
    113	pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
    114	if (!pd)
    115		return -ENOMEM;
    116
    117	pd->pd.name = exynos_get_domain_name(np);
    118	if (!pd->pd.name)
    119		return -ENOMEM;
    120
    121	pd->base = of_iomap(np, 0);
    122	if (!pd->base) {
    123		kfree_const(pd->pd.name);
    124		return -ENODEV;
    125	}
    126
    127	pd->pd.power_off = exynos_pd_power_off;
    128	pd->pd.power_on = exynos_pd_power_on;
    129	pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg;
    130
    131	on = readl_relaxed(pd->base + 0x4) & pd->local_pwr_cfg;
    132
    133	pm_genpd_init(&pd->pd, NULL, !on);
    134	ret = of_genpd_add_provider_simple(np, &pd->pd);
    135
    136	if (ret == 0 && of_parse_phandle_with_args(np, "power-domains",
    137				      "#power-domain-cells", 0, &parent) == 0) {
    138		child.np = np;
    139		child.args_count = 0;
    140
    141		if (of_genpd_add_subdomain(&parent, &child))
    142			pr_warn("%pOF failed to add subdomain: %pOF\n",
    143				parent.np, child.np);
    144		else
    145			pr_info("%pOF has as child subdomain: %pOF.\n",
    146				parent.np, child.np);
    147	}
    148
    149	pm_runtime_enable(dev);
    150	return ret;
    151}
    152
    153static struct platform_driver exynos_pd_driver = {
    154	.probe	= exynos_pd_probe,
    155	.driver	= {
    156		.name		= "exynos-pd",
    157		.of_match_table	= exynos_pm_domain_of_match,
    158		.suppress_bind_attrs = true,
    159	}
    160};
    161
    162static __init int exynos4_pm_init_power_domain(void)
    163{
    164	return platform_driver_register(&exynos_pd_driver);
    165}
    166core_initcall(exynos4_pm_init_power_domain);