brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · b993269 Raw
481 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * R-Car SYSC Power management support4 *5 * Copyright (C) 2014  Magnus Damm6 * Copyright (C) 2015-2017 Glider bvba7 */8 9#include <linux/clk/renesas.h>10#include <linux/delay.h>11#include <linux/err.h>12#include <linux/mm.h>13#include <linux/of_address.h>14#include <linux/pm_domain.h>15#include <linux/slab.h>16#include <linux/spinlock.h>17#include <linux/io.h>18#include <linux/iopoll.h>19#include <linux/soc/renesas/rcar-sysc.h>20 21#include "rcar-sysc.h"22 23/* SYSC Common */24#define SYSCSR			0x00	/* SYSC Status Register */25#define SYSCISR			0x04	/* Interrupt Status Register */26#define SYSCISCR		0x08	/* Interrupt Status Clear Register */27#define SYSCIER			0x0c	/* Interrupt Enable Register */28#define SYSCIMR			0x10	/* Interrupt Mask Register */29 30/* SYSC Status Register */31#define SYSCSR_PONENB		1	/* Ready for power resume requests */32#define SYSCSR_POFFENB		0	/* Ready for power shutoff requests */33 34/*35 * Power Control Register Offsets inside the register block for each domain36 * Note: The "CR" registers for ARM cores exist on H1 only37 *	 Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen238 *	 Use PSCI on R-Car Gen339 */40#define PWRSR_OFFS		0x00	/* Power Status Register */41#define PWROFFCR_OFFS		0x04	/* Power Shutoff Control Register */42#define PWROFFSR_OFFS		0x08	/* Power Shutoff Status Register */43#define PWRONCR_OFFS		0x0c	/* Power Resume Control Register */44#define PWRONSR_OFFS		0x10	/* Power Resume Status Register */45#define PWRER_OFFS		0x14	/* Power Shutoff/Resume Error */46 47 48#define SYSCSR_TIMEOUT		100049#define SYSCSR_DELAY_US		150 51#define PWRER_RETRIES		100052#define PWRER_DELAY_US		153 54#define SYSCISR_TIMEOUT		100055#define SYSCISR_DELAY_US	156 57#define RCAR_PD_ALWAYS_ON	32	/* Always-on power area */58 59struct rcar_sysc_pd {60	struct generic_pm_domain genpd;61	u16 chan_offs;62	u8 chan_bit;63	u8 isr_bit;64	unsigned int flags;65	char name[];66};67 68static void __iomem *rcar_sysc_base;69static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */70static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val;71 72static int rcar_sysc_pwr_on_off(const struct rcar_sysc_pd *pd, bool on)73{74	unsigned int sr_bit, reg_offs;75	u32 val;76	int ret;77 78	if (on) {79		sr_bit = SYSCSR_PONENB;80		reg_offs = PWRONCR_OFFS;81	} else {82		sr_bit = SYSCSR_POFFENB;83		reg_offs = PWROFFCR_OFFS;84	}85 86	/* Wait until SYSC is ready to accept a power request */87	ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCSR, val,88					val & BIT(sr_bit), SYSCSR_DELAY_US,89					SYSCSR_TIMEOUT);90	if (ret)91		return -EAGAIN;92 93	/* Power-off delay quirk */94	if (!on && (pd->flags & PD_OFF_DELAY))95		udelay(1);96 97	/* Submit power shutoff or power resume request */98	iowrite32(BIT(pd->chan_bit), rcar_sysc_base + pd->chan_offs + reg_offs);99 100	return 0;101}102 103static int rcar_sysc_power(const struct rcar_sysc_pd *pd, bool on)104{105	unsigned int isr_mask = BIT(pd->isr_bit);106	unsigned int chan_mask = BIT(pd->chan_bit);107	unsigned int status, k;108	unsigned long flags;109	int ret;110 111	spin_lock_irqsave(&rcar_sysc_lock, flags);112 113	/*114	 * Mask external power requests for CPU or 3DG domains115	 */116	if (rcar_sysc_extmask_val) {117		iowrite32(rcar_sysc_extmask_val,118			  rcar_sysc_base + rcar_sysc_extmask_offs);119	}120 121	/*122	 * The interrupt source needs to be enabled, but masked, to prevent the123	 * CPU from receiving it.124	 */125	iowrite32(ioread32(rcar_sysc_base + SYSCIMR) | isr_mask,126		  rcar_sysc_base + SYSCIMR);127	iowrite32(ioread32(rcar_sysc_base + SYSCIER) | isr_mask,128		  rcar_sysc_base + SYSCIER);129 130	iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);131 132	/* Submit power shutoff or resume request until it was accepted */133	for (k = 0; k < PWRER_RETRIES; k++) {134		ret = rcar_sysc_pwr_on_off(pd, on);135		if (ret)136			goto out;137 138		status = ioread32(rcar_sysc_base + pd->chan_offs + PWRER_OFFS);139		if (!(status & chan_mask))140			break;141 142		udelay(PWRER_DELAY_US);143	}144 145	if (k == PWRER_RETRIES) {146		ret = -EIO;147		goto out;148	}149 150	/* Wait until the power shutoff or resume request has completed * */151	ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCISR, status,152					status & isr_mask, SYSCISR_DELAY_US,153					SYSCISR_TIMEOUT);154	if (ret)155		ret = -EIO;156 157	iowrite32(isr_mask, rcar_sysc_base + SYSCISCR);158 159 out:160	if (rcar_sysc_extmask_val)161		iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs);162 163	spin_unlock_irqrestore(&rcar_sysc_lock, flags);164 165	pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off",166		 pd->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret);167	return ret;168}169 170static bool rcar_sysc_power_is_off(const struct rcar_sysc_pd *pd)171{172	unsigned int st;173 174	st = ioread32(rcar_sysc_base + pd->chan_offs + PWRSR_OFFS);175	if (st & BIT(pd->chan_bit))176		return true;177 178	return false;179}180 181static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d)182{183	return container_of(d, struct rcar_sysc_pd, genpd);184}185 186static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd)187{188	struct rcar_sysc_pd *pd = to_rcar_pd(genpd);189 190	pr_debug("%s: %s\n", __func__, genpd->name);191	return rcar_sysc_power(pd, false);192}193 194static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd)195{196	struct rcar_sysc_pd *pd = to_rcar_pd(genpd);197 198	pr_debug("%s: %s\n", __func__, genpd->name);199	return rcar_sysc_power(pd, true);200}201 202static bool has_cpg_mstp;203 204static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd)205{206	struct generic_pm_domain *genpd = &pd->genpd;207	const char *name = pd->genpd.name;208	int error;209 210	if (pd->flags & PD_CPU) {211		/*212		 * This domain contains a CPU core and therefore it should213		 * only be turned off if the CPU is not in use.214		 */215		pr_debug("PM domain %s contains %s\n", name, "CPU");216		genpd->flags |= GENPD_FLAG_ALWAYS_ON;217	} else if (pd->flags & PD_SCU) {218		/*219		 * This domain contains an SCU and cache-controller, and220		 * therefore it should only be turned off if the CPU cores are221		 * not in use.222		 */223		pr_debug("PM domain %s contains %s\n", name, "SCU");224		genpd->flags |= GENPD_FLAG_ALWAYS_ON;225	} else if (pd->flags & PD_NO_CR) {226		/*227		 * This domain cannot be turned off.228		 */229		genpd->flags |= GENPD_FLAG_ALWAYS_ON;230	}231 232	if (!(pd->flags & (PD_CPU | PD_SCU))) {233		/* Enable Clock Domain for I/O devices */234		genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;235		if (has_cpg_mstp) {236			genpd->attach_dev = cpg_mstp_attach_dev;237			genpd->detach_dev = cpg_mstp_detach_dev;238		} else {239			genpd->attach_dev = cpg_mssr_attach_dev;240			genpd->detach_dev = cpg_mssr_detach_dev;241		}242	}243 244	genpd->power_off = rcar_sysc_pd_power_off;245	genpd->power_on = rcar_sysc_pd_power_on;246 247	if (pd->flags & (PD_CPU | PD_NO_CR)) {248		/* Skip CPUs (handled by SMP code) and areas without control */249		pr_debug("%s: Not touching %s\n", __func__, genpd->name);250		goto finalize;251	}252 253	if (!rcar_sysc_power_is_off(pd)) {254		pr_debug("%s: %s is already powered\n", __func__, genpd->name);255		goto finalize;256	}257 258	rcar_sysc_power(pd, true);259 260finalize:261	error = pm_genpd_init(genpd, &simple_qos_governor, false);262	if (error)263		pr_err("Failed to init PM domain %s: %d\n", name, error);264 265	return error;266}267 268static const struct of_device_id rcar_sysc_matches[] __initconst = {269#ifdef CONFIG_SYSC_R8A7742270	{ .compatible = "renesas,r8a7742-sysc", .data = &r8a7742_sysc_info },271#endif272#ifdef CONFIG_SYSC_R8A7743273	{ .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info },274	/* RZ/G1N is identical to RZ/G2M w.r.t. power domains. */275	{ .compatible = "renesas,r8a7744-sysc", .data = &r8a7743_sysc_info },276#endif277#ifdef CONFIG_SYSC_R8A7745278	{ .compatible = "renesas,r8a7745-sysc", .data = &r8a7745_sysc_info },279#endif280#ifdef CONFIG_SYSC_R8A77470281	{ .compatible = "renesas,r8a77470-sysc", .data = &r8a77470_sysc_info },282#endif283#ifdef CONFIG_SYSC_R8A774A1284	{ .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info },285#endif286#ifdef CONFIG_SYSC_R8A774B1287	{ .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info },288#endif289#ifdef CONFIG_SYSC_R8A774C0290	{ .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info },291#endif292#ifdef CONFIG_SYSC_R8A774E1293	{ .compatible = "renesas,r8a774e1-sysc", .data = &r8a774e1_sysc_info },294#endif295#ifdef CONFIG_SYSC_R8A7779296	{ .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info },297#endif298#ifdef CONFIG_SYSC_R8A7790299	{ .compatible = "renesas,r8a7790-sysc", .data = &r8a7790_sysc_info },300#endif301#ifdef CONFIG_SYSC_R8A7791302	{ .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info },303	/* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */304	{ .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info },305#endif306#ifdef CONFIG_SYSC_R8A7792307	{ .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info },308#endif309#ifdef CONFIG_SYSC_R8A7794310	{ .compatible = "renesas,r8a7794-sysc", .data = &r8a7794_sysc_info },311#endif312#ifdef CONFIG_SYSC_R8A7795313	{ .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info },314#endif315#ifdef CONFIG_SYSC_R8A77960316	{ .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info },317#endif318#ifdef CONFIG_SYSC_R8A77961319	{ .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info },320#endif321#ifdef CONFIG_SYSC_R8A77965322	{ .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info },323#endif324#ifdef CONFIG_SYSC_R8A77970325	{ .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info },326#endif327#ifdef CONFIG_SYSC_R8A77980328	{ .compatible = "renesas,r8a77980-sysc", .data = &r8a77980_sysc_info },329#endif330#ifdef CONFIG_SYSC_R8A77990331	{ .compatible = "renesas,r8a77990-sysc", .data = &r8a77990_sysc_info },332#endif333#ifdef CONFIG_SYSC_R8A77995334	{ .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info },335#endif336	{ /* sentinel */ }337};338 339struct rcar_pm_domains {340	struct genpd_onecell_data onecell_data;341	struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1];342};343 344static struct genpd_onecell_data *rcar_sysc_onecell_data;345 346static int __init rcar_sysc_pd_init(void)347{348	const struct rcar_sysc_info *info;349	const struct of_device_id *match;350	struct rcar_pm_domains *domains;351	struct device_node *np;352	void __iomem *base;353	unsigned int i;354	int error;355 356	np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match);357	if (!np)358		return -ENODEV;359 360	info = match->data;361 362	if (info->init) {363		error = info->init();364		if (error)365			goto out_put;366	}367 368	has_cpg_mstp = of_find_compatible_node(NULL, NULL,369					       "renesas,cpg-mstp-clocks");370 371	base = of_iomap(np, 0);372	if (!base) {373		pr_warn("%pOF: Cannot map regs\n", np);374		error = -ENOMEM;375		goto out_put;376	}377 378	rcar_sysc_base = base;379 380	/* Optional External Request Mask Register */381	rcar_sysc_extmask_offs = info->extmask_offs;382	rcar_sysc_extmask_val = info->extmask_val;383 384	domains = kzalloc(sizeof(*domains), GFP_KERNEL);385	if (!domains) {386		error = -ENOMEM;387		goto out_put;388	}389 390	domains->onecell_data.domains = domains->domains;391	domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains);392	rcar_sysc_onecell_data = &domains->onecell_data;393 394	for (i = 0; i < info->num_areas; i++) {395		const struct rcar_sysc_area *area = &info->areas[i];396		struct rcar_sysc_pd *pd;397		size_t n;398 399		if (!area->name) {400			/* Skip NULLified area */401			continue;402		}403 404		n = strlen(area->name) + 1;405		pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL);406		if (!pd) {407			error = -ENOMEM;408			goto out_put;409		}410 411		memcpy(pd->name, area->name, n);412		pd->genpd.name = pd->name;413		pd->chan_offs = area->chan_offs;414		pd->chan_bit = area->chan_bit;415		pd->isr_bit = area->isr_bit;416		pd->flags = area->flags;417 418		error = rcar_sysc_pd_setup(pd);419		if (error)420			goto out_put;421 422		domains->domains[area->isr_bit] = &pd->genpd;423 424		if (area->parent < 0)425			continue;426 427		error = pm_genpd_add_subdomain(domains->domains[area->parent],428					       &pd->genpd);429		if (error) {430			pr_warn("Failed to add PM subdomain %s to parent %u\n",431				area->name, area->parent);432			goto out_put;433		}434	}435 436	error = of_genpd_add_provider_onecell(np, &domains->onecell_data);437	if (!error)438		fwnode_dev_initialized(of_fwnode_handle(np), true);439 440out_put:441	of_node_put(np);442	return error;443}444early_initcall(rcar_sysc_pd_init);445 446#ifdef CONFIG_ARCH_R8A7779447static int rcar_sysc_power_cpu(unsigned int idx, bool on)448{449	struct generic_pm_domain *genpd;450	struct rcar_sysc_pd *pd;451	unsigned int i;452 453	if (!rcar_sysc_onecell_data)454		return -ENODEV;455 456	for (i = 0; i < rcar_sysc_onecell_data->num_domains; i++) {457		genpd = rcar_sysc_onecell_data->domains[i];458		if (!genpd)459			continue;460 461		pd = to_rcar_pd(genpd);462		if (!(pd->flags & PD_CPU) || pd->chan_bit != idx)463			continue;464 465		return rcar_sysc_power(pd, on);466	}467 468	return -ENOENT;469}470 471int rcar_sysc_power_down_cpu(unsigned int cpu)472{473	return rcar_sysc_power_cpu(cpu, false);474}475 476int rcar_sysc_power_up_cpu(unsigned int cpu)477{478	return rcar_sysc_power_cpu(cpu, true);479}480#endif /* CONFIG_ARCH_R8A7779 */481