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

fsl_rcpm.c (8740B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * RCPM(Run Control/Power Management) support
      4 *
      5 * Copyright 2012-2015 Freescale Semiconductor Inc.
      6 *
      7 * Author: Chenhui Zhao <chenhui.zhao@freescale.com>
      8 */
      9
     10#define pr_fmt(fmt) "%s: " fmt, __func__
     11
     12#include <linux/types.h>
     13#include <linux/errno.h>
     14#include <linux/of_address.h>
     15#include <linux/export.h>
     16
     17#include <asm/io.h>
     18#include <linux/fsl/guts.h>
     19#include <asm/cputhreads.h>
     20#include <asm/fsl_pm.h>
     21#include <asm/smp.h>
     22
     23static struct ccsr_rcpm_v1 __iomem *rcpm_v1_regs;
     24static struct ccsr_rcpm_v2 __iomem *rcpm_v2_regs;
     25static unsigned int fsl_supported_pm_modes;
     26
     27static void rcpm_v1_irq_mask(int cpu)
     28{
     29	int hw_cpu = get_hard_smp_processor_id(cpu);
     30	unsigned int mask = 1 << hw_cpu;
     31
     32	setbits32(&rcpm_v1_regs->cpmimr, mask);
     33	setbits32(&rcpm_v1_regs->cpmcimr, mask);
     34	setbits32(&rcpm_v1_regs->cpmmcmr, mask);
     35	setbits32(&rcpm_v1_regs->cpmnmimr, mask);
     36}
     37
     38static void rcpm_v2_irq_mask(int cpu)
     39{
     40	int hw_cpu = get_hard_smp_processor_id(cpu);
     41	unsigned int mask = 1 << hw_cpu;
     42
     43	setbits32(&rcpm_v2_regs->tpmimr0, mask);
     44	setbits32(&rcpm_v2_regs->tpmcimr0, mask);
     45	setbits32(&rcpm_v2_regs->tpmmcmr0, mask);
     46	setbits32(&rcpm_v2_regs->tpmnmimr0, mask);
     47}
     48
     49static void rcpm_v1_irq_unmask(int cpu)
     50{
     51	int hw_cpu = get_hard_smp_processor_id(cpu);
     52	unsigned int mask = 1 << hw_cpu;
     53
     54	clrbits32(&rcpm_v1_regs->cpmimr, mask);
     55	clrbits32(&rcpm_v1_regs->cpmcimr, mask);
     56	clrbits32(&rcpm_v1_regs->cpmmcmr, mask);
     57	clrbits32(&rcpm_v1_regs->cpmnmimr, mask);
     58}
     59
     60static void rcpm_v2_irq_unmask(int cpu)
     61{
     62	int hw_cpu = get_hard_smp_processor_id(cpu);
     63	unsigned int mask = 1 << hw_cpu;
     64
     65	clrbits32(&rcpm_v2_regs->tpmimr0, mask);
     66	clrbits32(&rcpm_v2_regs->tpmcimr0, mask);
     67	clrbits32(&rcpm_v2_regs->tpmmcmr0, mask);
     68	clrbits32(&rcpm_v2_regs->tpmnmimr0, mask);
     69}
     70
     71static void rcpm_v1_set_ip_power(bool enable, u32 mask)
     72{
     73	if (enable)
     74		setbits32(&rcpm_v1_regs->ippdexpcr, mask);
     75	else
     76		clrbits32(&rcpm_v1_regs->ippdexpcr, mask);
     77}
     78
     79static void rcpm_v2_set_ip_power(bool enable, u32 mask)
     80{
     81	if (enable)
     82		setbits32(&rcpm_v2_regs->ippdexpcr[0], mask);
     83	else
     84		clrbits32(&rcpm_v2_regs->ippdexpcr[0], mask);
     85}
     86
     87static void rcpm_v1_cpu_enter_state(int cpu, int state)
     88{
     89	int hw_cpu = get_hard_smp_processor_id(cpu);
     90	unsigned int mask = 1 << hw_cpu;
     91
     92	switch (state) {
     93	case E500_PM_PH10:
     94		setbits32(&rcpm_v1_regs->cdozcr, mask);
     95		break;
     96	case E500_PM_PH15:
     97		setbits32(&rcpm_v1_regs->cnapcr, mask);
     98		break;
     99	default:
    100		pr_warn("Unknown cpu PM state (%d)\n", state);
    101		break;
    102	}
    103}
    104
    105static void rcpm_v2_cpu_enter_state(int cpu, int state)
    106{
    107	int hw_cpu = get_hard_smp_processor_id(cpu);
    108	u32 mask = 1 << cpu_core_index_of_thread(cpu);
    109
    110	switch (state) {
    111	case E500_PM_PH10:
    112		/* one bit corresponds to one thread for PH10 of 6500 */
    113		setbits32(&rcpm_v2_regs->tph10setr0, 1 << hw_cpu);
    114		break;
    115	case E500_PM_PH15:
    116		setbits32(&rcpm_v2_regs->pcph15setr, mask);
    117		break;
    118	case E500_PM_PH20:
    119		setbits32(&rcpm_v2_regs->pcph20setr, mask);
    120		break;
    121	case E500_PM_PH30:
    122		setbits32(&rcpm_v2_regs->pcph30setr, mask);
    123		break;
    124	default:
    125		pr_warn("Unknown cpu PM state (%d)\n", state);
    126	}
    127}
    128
    129static void rcpm_v1_cpu_die(int cpu)
    130{
    131	rcpm_v1_cpu_enter_state(cpu, E500_PM_PH15);
    132}
    133
    134#ifdef CONFIG_PPC64
    135static void qoriq_disable_thread(int cpu)
    136{
    137	int thread = cpu_thread_in_core(cpu);
    138
    139	book3e_stop_thread(thread);
    140}
    141#endif
    142
    143static void rcpm_v2_cpu_die(int cpu)
    144{
    145#ifdef CONFIG_PPC64
    146	int primary;
    147
    148	if (threads_per_core == 2) {
    149		primary = cpu_first_thread_sibling(cpu);
    150		if (cpu_is_offline(primary) && cpu_is_offline(primary + 1)) {
    151			/* if both threads are offline, put the cpu in PH20 */
    152			rcpm_v2_cpu_enter_state(cpu, E500_PM_PH20);
    153		} else {
    154			/* if only one thread is offline, disable the thread */
    155			qoriq_disable_thread(cpu);
    156		}
    157	}
    158#endif
    159
    160	if (threads_per_core == 1)
    161		rcpm_v2_cpu_enter_state(cpu, E500_PM_PH20);
    162}
    163
    164static void rcpm_v1_cpu_exit_state(int cpu, int state)
    165{
    166	int hw_cpu = get_hard_smp_processor_id(cpu);
    167	unsigned int mask = 1 << hw_cpu;
    168
    169	switch (state) {
    170	case E500_PM_PH10:
    171		clrbits32(&rcpm_v1_regs->cdozcr, mask);
    172		break;
    173	case E500_PM_PH15:
    174		clrbits32(&rcpm_v1_regs->cnapcr, mask);
    175		break;
    176	default:
    177		pr_warn("Unknown cpu PM state (%d)\n", state);
    178		break;
    179	}
    180}
    181
    182static void rcpm_v1_cpu_up_prepare(int cpu)
    183{
    184	rcpm_v1_cpu_exit_state(cpu, E500_PM_PH15);
    185	rcpm_v1_irq_unmask(cpu);
    186}
    187
    188static void rcpm_v2_cpu_exit_state(int cpu, int state)
    189{
    190	int hw_cpu = get_hard_smp_processor_id(cpu);
    191	u32 mask = 1 << cpu_core_index_of_thread(cpu);
    192
    193	switch (state) {
    194	case E500_PM_PH10:
    195		setbits32(&rcpm_v2_regs->tph10clrr0, 1 << hw_cpu);
    196		break;
    197	case E500_PM_PH15:
    198		setbits32(&rcpm_v2_regs->pcph15clrr, mask);
    199		break;
    200	case E500_PM_PH20:
    201		setbits32(&rcpm_v2_regs->pcph20clrr, mask);
    202		break;
    203	case E500_PM_PH30:
    204		setbits32(&rcpm_v2_regs->pcph30clrr, mask);
    205		break;
    206	default:
    207		pr_warn("Unknown cpu PM state (%d)\n", state);
    208	}
    209}
    210
    211static void rcpm_v2_cpu_up_prepare(int cpu)
    212{
    213	rcpm_v2_cpu_exit_state(cpu, E500_PM_PH20);
    214	rcpm_v2_irq_unmask(cpu);
    215}
    216
    217static int rcpm_v1_plat_enter_state(int state)
    218{
    219	u32 *pmcsr_reg = &rcpm_v1_regs->powmgtcsr;
    220	int ret = 0;
    221	int result;
    222
    223	switch (state) {
    224	case PLAT_PM_SLEEP:
    225		setbits32(pmcsr_reg, RCPM_POWMGTCSR_SLP);
    226
    227		/* Upon resume, wait for RCPM_POWMGTCSR_SLP bit to be clear. */
    228		result = spin_event_timeout(
    229		  !(in_be32(pmcsr_reg) & RCPM_POWMGTCSR_SLP), 10000, 10);
    230		if (!result) {
    231			pr_err("timeout waiting for SLP bit to be cleared\n");
    232			ret = -ETIMEDOUT;
    233		}
    234		break;
    235	default:
    236		pr_warn("Unknown platform PM state (%d)", state);
    237		ret = -EINVAL;
    238	}
    239
    240	return ret;
    241}
    242
    243static int rcpm_v2_plat_enter_state(int state)
    244{
    245	u32 *pmcsr_reg = &rcpm_v2_regs->powmgtcsr;
    246	int ret = 0;
    247	int result;
    248
    249	switch (state) {
    250	case PLAT_PM_LPM20:
    251		/* clear previous LPM20 status */
    252		setbits32(pmcsr_reg, RCPM_POWMGTCSR_P_LPM20_ST);
    253		/* enter LPM20 status */
    254		setbits32(pmcsr_reg, RCPM_POWMGTCSR_LPM20_RQ);
    255
    256		/* At this point, the device is in LPM20 status. */
    257
    258		/* resume ... */
    259		result = spin_event_timeout(
    260		  !(in_be32(pmcsr_reg) & RCPM_POWMGTCSR_LPM20_ST), 10000, 10);
    261		if (!result) {
    262			pr_err("timeout waiting for LPM20 bit to be cleared\n");
    263			ret = -ETIMEDOUT;
    264		}
    265		break;
    266	default:
    267		pr_warn("Unknown platform PM state (%d)\n", state);
    268		ret = -EINVAL;
    269	}
    270
    271	return ret;
    272}
    273
    274static int rcpm_v1_plat_enter_sleep(void)
    275{
    276	return rcpm_v1_plat_enter_state(PLAT_PM_SLEEP);
    277}
    278
    279static int rcpm_v2_plat_enter_sleep(void)
    280{
    281	return rcpm_v2_plat_enter_state(PLAT_PM_LPM20);
    282}
    283
    284static void rcpm_common_freeze_time_base(u32 *tben_reg, int freeze)
    285{
    286	static u32 mask;
    287
    288	if (freeze) {
    289		mask = in_be32(tben_reg);
    290		clrbits32(tben_reg, mask);
    291	} else {
    292		setbits32(tben_reg, mask);
    293	}
    294
    295	/* read back to push the previous write */
    296	in_be32(tben_reg);
    297}
    298
    299static void rcpm_v1_freeze_time_base(bool freeze)
    300{
    301	rcpm_common_freeze_time_base(&rcpm_v1_regs->ctbenr, freeze);
    302}
    303
    304static void rcpm_v2_freeze_time_base(bool freeze)
    305{
    306	rcpm_common_freeze_time_base(&rcpm_v2_regs->pctbenr, freeze);
    307}
    308
    309static unsigned int rcpm_get_pm_modes(void)
    310{
    311	return fsl_supported_pm_modes;
    312}
    313
    314static const struct fsl_pm_ops qoriq_rcpm_v1_ops = {
    315	.irq_mask = rcpm_v1_irq_mask,
    316	.irq_unmask = rcpm_v1_irq_unmask,
    317	.cpu_enter_state = rcpm_v1_cpu_enter_state,
    318	.cpu_exit_state = rcpm_v1_cpu_exit_state,
    319	.cpu_up_prepare = rcpm_v1_cpu_up_prepare,
    320	.cpu_die = rcpm_v1_cpu_die,
    321	.plat_enter_sleep = rcpm_v1_plat_enter_sleep,
    322	.set_ip_power = rcpm_v1_set_ip_power,
    323	.freeze_time_base = rcpm_v1_freeze_time_base,
    324	.get_pm_modes = rcpm_get_pm_modes,
    325};
    326
    327static const struct fsl_pm_ops qoriq_rcpm_v2_ops = {
    328	.irq_mask = rcpm_v2_irq_mask,
    329	.irq_unmask = rcpm_v2_irq_unmask,
    330	.cpu_enter_state = rcpm_v2_cpu_enter_state,
    331	.cpu_exit_state = rcpm_v2_cpu_exit_state,
    332	.cpu_up_prepare = rcpm_v2_cpu_up_prepare,
    333	.cpu_die = rcpm_v2_cpu_die,
    334	.plat_enter_sleep = rcpm_v2_plat_enter_sleep,
    335	.set_ip_power = rcpm_v2_set_ip_power,
    336	.freeze_time_base = rcpm_v2_freeze_time_base,
    337	.get_pm_modes = rcpm_get_pm_modes,
    338};
    339
    340static const struct of_device_id rcpm_matches[] = {
    341	{
    342		.compatible = "fsl,qoriq-rcpm-1.0",
    343		.data = &qoriq_rcpm_v1_ops,
    344	},
    345	{
    346		.compatible = "fsl,qoriq-rcpm-2.0",
    347		.data = &qoriq_rcpm_v2_ops,
    348	},
    349	{
    350		.compatible = "fsl,qoriq-rcpm-2.1",
    351		.data = &qoriq_rcpm_v2_ops,
    352	},
    353	{},
    354};
    355
    356int __init fsl_rcpm_init(void)
    357{
    358	struct device_node *np;
    359	const struct of_device_id *match;
    360	void __iomem *base;
    361
    362	np = of_find_matching_node_and_match(NULL, rcpm_matches, &match);
    363	if (!np)
    364		return 0;
    365
    366	base = of_iomap(np, 0);
    367	of_node_put(np);
    368	if (!base) {
    369		pr_err("of_iomap() error.\n");
    370		return -ENOMEM;
    371	}
    372
    373	rcpm_v1_regs = base;
    374	rcpm_v2_regs = base;
    375
    376	/* support sleep by default */
    377	fsl_supported_pm_modes = FSL_PM_SLEEP;
    378
    379	qoriq_pm_ops = match->data;
    380
    381	return 0;
    382}