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

rmobile-sysc.c (8297B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * rmobile power management support
      4 *
      5 * Copyright (C) 2012  Renesas Solutions Corp.
      6 * Copyright (C) 2012  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
      7 * Copyright (C) 2014  Glider bvba
      8 *
      9 * based on pm-sh7372.c
     10 *  Copyright (C) 2011 Magnus Damm
     11 */
     12#include <linux/clk/renesas.h>
     13#include <linux/console.h>
     14#include <linux/delay.h>
     15#include <linux/of.h>
     16#include <linux/of_address.h>
     17#include <linux/pm.h>
     18#include <linux/pm_clock.h>
     19#include <linux/pm_domain.h>
     20#include <linux/slab.h>
     21
     22#include <asm/io.h>
     23
     24/* SYSC */
     25#define SPDCR		0x08	/* SYS Power Down Control Register */
     26#define SWUCR		0x14	/* SYS Wakeup Control Register */
     27#define PSTR		0x80	/* Power Status Register */
     28
     29#define PSTR_RETRIES	100
     30#define PSTR_DELAY_US	10
     31
     32struct rmobile_pm_domain {
     33	struct generic_pm_domain genpd;
     34	struct dev_power_governor *gov;
     35	int (*suspend)(void);
     36	void __iomem *base;
     37	unsigned int bit_shift;
     38};
     39
     40static inline
     41struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d)
     42{
     43	return container_of(d, struct rmobile_pm_domain, genpd);
     44}
     45
     46static int rmobile_pd_power_down(struct generic_pm_domain *genpd)
     47{
     48	struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd);
     49	unsigned int mask = BIT(rmobile_pd->bit_shift);
     50
     51	if (rmobile_pd->suspend) {
     52		int ret = rmobile_pd->suspend();
     53
     54		if (ret)
     55			return ret;
     56	}
     57
     58	if (readl(rmobile_pd->base + PSTR) & mask) {
     59		unsigned int retry_count;
     60		writel(mask, rmobile_pd->base + SPDCR);
     61
     62		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
     63			if (!(readl(rmobile_pd->base + SPDCR) & mask))
     64				break;
     65			cpu_relax();
     66		}
     67	}
     68
     69	pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", genpd->name, mask,
     70		 readl(rmobile_pd->base + PSTR));
     71
     72	return 0;
     73}
     74
     75static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd)
     76{
     77	unsigned int mask = BIT(rmobile_pd->bit_shift);
     78	unsigned int retry_count;
     79	int ret = 0;
     80
     81	if (readl(rmobile_pd->base + PSTR) & mask)
     82		return ret;
     83
     84	writel(mask, rmobile_pd->base + SWUCR);
     85
     86	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
     87		if (!(readl(rmobile_pd->base + SWUCR) & mask))
     88			break;
     89		if (retry_count > PSTR_RETRIES)
     90			udelay(PSTR_DELAY_US);
     91		else
     92			cpu_relax();
     93	}
     94	if (!retry_count)
     95		ret = -EIO;
     96
     97	pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n",
     98		 rmobile_pd->genpd.name, mask,
     99		 readl(rmobile_pd->base + PSTR));
    100
    101	return ret;
    102}
    103
    104static int rmobile_pd_power_up(struct generic_pm_domain *genpd)
    105{
    106	return __rmobile_pd_power_up(to_rmobile_pd(genpd));
    107}
    108
    109static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
    110{
    111	struct generic_pm_domain *genpd = &rmobile_pd->genpd;
    112	struct dev_power_governor *gov = rmobile_pd->gov;
    113
    114	genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
    115	genpd->attach_dev = cpg_mstp_attach_dev;
    116	genpd->detach_dev = cpg_mstp_detach_dev;
    117
    118	if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) {
    119		genpd->power_off = rmobile_pd_power_down;
    120		genpd->power_on = rmobile_pd_power_up;
    121		__rmobile_pd_power_up(rmobile_pd);
    122	}
    123
    124	pm_genpd_init(genpd, gov ? : &simple_qos_governor, false);
    125}
    126
    127static int rmobile_pd_suspend_console(void)
    128{
    129	/*
    130	 * Serial consoles make use of SCIF hardware located in this domain,
    131	 * hence keep the power domain on if "no_console_suspend" is set.
    132	 */
    133	return console_suspend_enabled ? 0 : -EBUSY;
    134}
    135
    136enum pd_types {
    137	PD_NORMAL,
    138	PD_CPU,
    139	PD_CONSOLE,
    140	PD_DEBUG,
    141	PD_MEMCTL,
    142};
    143
    144#define MAX_NUM_SPECIAL_PDS	16
    145
    146static struct special_pd {
    147	struct device_node *pd;
    148	enum pd_types type;
    149} special_pds[MAX_NUM_SPECIAL_PDS] __initdata;
    150
    151static unsigned int num_special_pds __initdata;
    152
    153static const struct of_device_id special_ids[] __initconst = {
    154	{ .compatible = "arm,coresight-etm3x", .data = (void *)PD_DEBUG },
    155	{ .compatible = "renesas,dbsc-r8a73a4", .data = (void *)PD_MEMCTL, },
    156	{ .compatible = "renesas,dbsc3-r8a7740", .data = (void *)PD_MEMCTL, },
    157	{ .compatible = "renesas,sbsc-sh73a0", .data = (void *)PD_MEMCTL, },
    158	{ /* sentinel */ },
    159};
    160
    161static void __init add_special_pd(struct device_node *np, enum pd_types type)
    162{
    163	unsigned int i;
    164	struct device_node *pd;
    165
    166	pd = of_parse_phandle(np, "power-domains", 0);
    167	if (!pd)
    168		return;
    169
    170	for (i = 0; i < num_special_pds; i++)
    171		if (pd == special_pds[i].pd && type == special_pds[i].type) {
    172			of_node_put(pd);
    173			return;
    174		}
    175
    176	if (num_special_pds == ARRAY_SIZE(special_pds)) {
    177		pr_warn("Too many special PM domains\n");
    178		of_node_put(pd);
    179		return;
    180	}
    181
    182	pr_debug("Special PM domain %pOFn type %d for %pOF\n", pd, type, np);
    183
    184	special_pds[num_special_pds].pd = pd;
    185	special_pds[num_special_pds].type = type;
    186	num_special_pds++;
    187}
    188
    189static void __init get_special_pds(void)
    190{
    191	struct device_node *np;
    192	const struct of_device_id *id;
    193
    194	/* PM domains containing CPUs */
    195	for_each_of_cpu_node(np)
    196		add_special_pd(np, PD_CPU);
    197
    198	/* PM domain containing console */
    199	if (of_stdout)
    200		add_special_pd(of_stdout, PD_CONSOLE);
    201
    202	/* PM domains containing other special devices */
    203	for_each_matching_node_and_match(np, special_ids, &id)
    204		add_special_pd(np, (enum pd_types)id->data);
    205}
    206
    207static void __init put_special_pds(void)
    208{
    209	unsigned int i;
    210
    211	for (i = 0; i < num_special_pds; i++)
    212		of_node_put(special_pds[i].pd);
    213}
    214
    215static enum pd_types __init pd_type(const struct device_node *pd)
    216{
    217	unsigned int i;
    218
    219	for (i = 0; i < num_special_pds; i++)
    220		if (pd == special_pds[i].pd)
    221			return special_pds[i].type;
    222
    223	return PD_NORMAL;
    224}
    225
    226static void __init rmobile_setup_pm_domain(struct device_node *np,
    227					   struct rmobile_pm_domain *pd)
    228{
    229	const char *name = pd->genpd.name;
    230
    231	switch (pd_type(np)) {
    232	case PD_CPU:
    233		/*
    234		 * This domain contains the CPU core and therefore it should
    235		 * only be turned off if the CPU is not in use.
    236		 */
    237		pr_debug("PM domain %s contains CPU\n", name);
    238		pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
    239		break;
    240
    241	case PD_CONSOLE:
    242		pr_debug("PM domain %s contains serial console\n", name);
    243		pd->gov = &pm_domain_always_on_gov;
    244		pd->suspend = rmobile_pd_suspend_console;
    245		break;
    246
    247	case PD_DEBUG:
    248		/*
    249		 * This domain contains the Coresight-ETM hardware block and
    250		 * therefore it should only be turned off if the debug module
    251		 * is not in use.
    252		 */
    253		pr_debug("PM domain %s contains Coresight-ETM\n", name);
    254		pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
    255		break;
    256
    257	case PD_MEMCTL:
    258		/*
    259		 * This domain contains a memory-controller and therefore it
    260		 * should only be turned off if memory is not in use.
    261		 */
    262		pr_debug("PM domain %s contains MEMCTL\n", name);
    263		pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
    264		break;
    265
    266	case PD_NORMAL:
    267		if (pd->bit_shift == ~0) {
    268			/* Top-level always-on domain */
    269			pr_debug("PM domain %s is always-on domain\n", name);
    270			pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
    271		}
    272		break;
    273	}
    274
    275	rmobile_init_pm_domain(pd);
    276}
    277
    278static int __init rmobile_add_pm_domains(void __iomem *base,
    279					 struct device_node *parent,
    280					 struct generic_pm_domain *genpd_parent)
    281{
    282	struct device_node *np;
    283
    284	for_each_child_of_node(parent, np) {
    285		struct rmobile_pm_domain *pd;
    286		u32 idx = ~0;
    287
    288		if (of_property_read_u32(np, "reg", &idx)) {
    289			/* always-on domain */
    290		}
    291
    292		pd = kzalloc(sizeof(*pd), GFP_KERNEL);
    293		if (!pd) {
    294			of_node_put(np);
    295			return -ENOMEM;
    296		}
    297
    298		pd->genpd.name = np->name;
    299		pd->base = base;
    300		pd->bit_shift = idx;
    301
    302		rmobile_setup_pm_domain(np, pd);
    303		if (genpd_parent)
    304			pm_genpd_add_subdomain(genpd_parent, &pd->genpd);
    305		of_genpd_add_provider_simple(np, &pd->genpd);
    306
    307		rmobile_add_pm_domains(base, np, &pd->genpd);
    308	}
    309	return 0;
    310}
    311
    312static int __init rmobile_init_pm_domains(void)
    313{
    314	struct device_node *np, *pmd;
    315	bool scanned = false;
    316	void __iomem *base;
    317	int ret = 0;
    318
    319	for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") {
    320		base = of_iomap(np, 0);
    321		if (!base) {
    322			pr_warn("%pOF cannot map reg 0\n", np);
    323			continue;
    324		}
    325
    326		pmd = of_get_child_by_name(np, "pm-domains");
    327		if (!pmd) {
    328			iounmap(base);
    329			pr_warn("%pOF lacks pm-domains node\n", np);
    330			continue;
    331		}
    332
    333		if (!scanned) {
    334			/* Find PM domains containing special blocks */
    335			get_special_pds();
    336			scanned = true;
    337		}
    338
    339		ret = rmobile_add_pm_domains(base, pmd, NULL);
    340		of_node_put(pmd);
    341		if (ret) {
    342			of_node_put(np);
    343			break;
    344		}
    345
    346		fwnode_dev_initialized(&np->fwnode, true);
    347	}
    348
    349	put_special_pds();
    350
    351	return ret;
    352}
    353
    354core_initcall(rmobile_init_pm_domains);