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-s3c64xx.c (9553B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Copyright 2008 Openmoko, Inc.
      4// Copyright 2008 Simtec Electronics
      5//	Ben Dooks <ben@simtec.co.uk>
      6//	http://armlinux.simtec.co.uk/
      7//
      8// S3C64XX CPU PM support.
      9
     10#include <linux/init.h>
     11#include <linux/suspend.h>
     12#include <linux/serial_core.h>
     13#include <linux/io.h>
     14#include <linux/gpio.h>
     15#include <linux/pm_domain.h>
     16
     17#include "map.h"
     18#include "irqs.h"
     19
     20#include "cpu.h"
     21#include "devs.h"
     22#include "pm.h"
     23#include "wakeup-mask.h"
     24
     25#include "regs-gpio.h"
     26#include "regs-clock.h"
     27#include "gpio-samsung.h"
     28
     29#include "regs-gpio-memport-s3c64xx.h"
     30#include "regs-modem-s3c64xx.h"
     31#include "regs-sys-s3c64xx.h"
     32#include "regs-syscon-power-s3c64xx.h"
     33
     34struct s3c64xx_pm_domain {
     35	char *const name;
     36	u32 ena;
     37	u32 pwr_stat;
     38	struct generic_pm_domain pd;
     39};
     40
     41static int s3c64xx_pd_off(struct generic_pm_domain *domain)
     42{
     43	struct s3c64xx_pm_domain *pd;
     44	u32 val;
     45
     46	pd = container_of(domain, struct s3c64xx_pm_domain, pd);
     47
     48	val = __raw_readl(S3C64XX_NORMAL_CFG);
     49	val &= ~(pd->ena);
     50	__raw_writel(val, S3C64XX_NORMAL_CFG);
     51
     52	return 0;
     53}
     54
     55static int s3c64xx_pd_on(struct generic_pm_domain *domain)
     56{
     57	struct s3c64xx_pm_domain *pd;
     58	u32 val;
     59	long retry = 1000000L;
     60
     61	pd = container_of(domain, struct s3c64xx_pm_domain, pd);
     62
     63	val = __raw_readl(S3C64XX_NORMAL_CFG);
     64	val |= pd->ena;
     65	__raw_writel(val, S3C64XX_NORMAL_CFG);
     66
     67	/* Not all domains provide power status readback */
     68	if (pd->pwr_stat) {
     69		do {
     70			cpu_relax();
     71			if (__raw_readl(S3C64XX_BLK_PWR_STAT) & pd->pwr_stat)
     72				break;
     73		} while (retry--);
     74
     75		if (!retry) {
     76			pr_err("Failed to start domain %s\n", pd->name);
     77			return -EBUSY;
     78		}
     79	}
     80
     81	return 0;
     82}
     83
     84static struct s3c64xx_pm_domain s3c64xx_pm_irom = {
     85	.name = "IROM",
     86	.ena = S3C64XX_NORMALCFG_IROM_ON,
     87	.pd = {
     88		.power_off = s3c64xx_pd_off,
     89		.power_on = s3c64xx_pd_on,
     90	},
     91};
     92
     93static struct s3c64xx_pm_domain s3c64xx_pm_etm = {
     94	.name = "ETM",
     95	.ena = S3C64XX_NORMALCFG_DOMAIN_ETM_ON,
     96	.pwr_stat = S3C64XX_BLKPWRSTAT_ETM,
     97	.pd = {
     98		.power_off = s3c64xx_pd_off,
     99		.power_on = s3c64xx_pd_on,
    100	},
    101};
    102
    103static struct s3c64xx_pm_domain s3c64xx_pm_s = {
    104	.name = "S",
    105	.ena = S3C64XX_NORMALCFG_DOMAIN_S_ON,
    106	.pwr_stat = S3C64XX_BLKPWRSTAT_S,
    107	.pd = {
    108		.power_off = s3c64xx_pd_off,
    109		.power_on = s3c64xx_pd_on,
    110	},
    111};
    112
    113static struct s3c64xx_pm_domain s3c64xx_pm_f = {
    114	.name = "F",
    115	.ena = S3C64XX_NORMALCFG_DOMAIN_F_ON,
    116	.pwr_stat = S3C64XX_BLKPWRSTAT_F,
    117	.pd = {
    118		.power_off = s3c64xx_pd_off,
    119		.power_on = s3c64xx_pd_on,
    120	},
    121};
    122
    123static struct s3c64xx_pm_domain s3c64xx_pm_p = {
    124	.name = "P",
    125	.ena = S3C64XX_NORMALCFG_DOMAIN_P_ON,
    126	.pwr_stat = S3C64XX_BLKPWRSTAT_P,
    127	.pd = {
    128		.power_off = s3c64xx_pd_off,
    129		.power_on = s3c64xx_pd_on,
    130	},
    131};
    132
    133static struct s3c64xx_pm_domain s3c64xx_pm_i = {
    134	.name = "I",
    135	.ena = S3C64XX_NORMALCFG_DOMAIN_I_ON,
    136	.pwr_stat = S3C64XX_BLKPWRSTAT_I,
    137	.pd = {
    138		.power_off = s3c64xx_pd_off,
    139		.power_on = s3c64xx_pd_on,
    140	},
    141};
    142
    143static struct s3c64xx_pm_domain s3c64xx_pm_g = {
    144	.name = "G",
    145	.ena = S3C64XX_NORMALCFG_DOMAIN_G_ON,
    146	.pd = {
    147		.power_off = s3c64xx_pd_off,
    148		.power_on = s3c64xx_pd_on,
    149	},
    150};
    151
    152static struct s3c64xx_pm_domain s3c64xx_pm_v = {
    153	.name = "V",
    154	.ena = S3C64XX_NORMALCFG_DOMAIN_V_ON,
    155	.pwr_stat = S3C64XX_BLKPWRSTAT_V,
    156	.pd = {
    157		.power_off = s3c64xx_pd_off,
    158		.power_on = s3c64xx_pd_on,
    159	},
    160};
    161
    162static struct s3c64xx_pm_domain *s3c64xx_always_on_pm_domains[] = {
    163	&s3c64xx_pm_irom,
    164};
    165
    166static struct s3c64xx_pm_domain *s3c64xx_pm_domains[] = {
    167	&s3c64xx_pm_etm,
    168	&s3c64xx_pm_g,
    169	&s3c64xx_pm_v,
    170	&s3c64xx_pm_i,
    171	&s3c64xx_pm_p,
    172	&s3c64xx_pm_s,
    173	&s3c64xx_pm_f,
    174};
    175
    176#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
    177void s3c_pm_debug_smdkled(u32 set, u32 clear)
    178{
    179	unsigned long flags;
    180	int i;
    181
    182	local_irq_save(flags);
    183	for (i = 0; i < 4; i++) {
    184		if (clear & (1 << i))
    185			gpio_set_value(S3C64XX_GPN(12 + i), 0);
    186		if (set & (1 << i))
    187			gpio_set_value(S3C64XX_GPN(12 + i), 1);
    188	}
    189	local_irq_restore(flags);
    190}
    191#endif
    192
    193#ifdef CONFIG_PM_SLEEP
    194static struct sleep_save core_save[] = {
    195	SAVE_ITEM(S3C64XX_MEM0DRVCON),
    196	SAVE_ITEM(S3C64XX_MEM1DRVCON),
    197};
    198
    199static struct sleep_save misc_save[] = {
    200	SAVE_ITEM(S3C64XX_AHB_CON0),
    201	SAVE_ITEM(S3C64XX_AHB_CON1),
    202	SAVE_ITEM(S3C64XX_AHB_CON2),
    203	
    204	SAVE_ITEM(S3C64XX_SPCON),
    205
    206	SAVE_ITEM(S3C64XX_MEM0CONSTOP),
    207	SAVE_ITEM(S3C64XX_MEM1CONSTOP),
    208	SAVE_ITEM(S3C64XX_MEM0CONSLP0),
    209	SAVE_ITEM(S3C64XX_MEM0CONSLP1),
    210	SAVE_ITEM(S3C64XX_MEM1CONSLP),
    211
    212	SAVE_ITEM(S3C64XX_SDMA_SEL),
    213	SAVE_ITEM(S3C64XX_MODEM_MIFPCON),
    214
    215	SAVE_ITEM(S3C64XX_NORMAL_CFG),
    216};
    217
    218void s3c_pm_configure_extint(void)
    219{
    220	__raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK);
    221}
    222
    223void s3c_pm_restore_core(void)
    224{
    225	__raw_writel(0, S3C64XX_EINT_MASK);
    226
    227	s3c_pm_debug_smdkled(1 << 2, 0);
    228
    229	s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
    230	s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
    231}
    232
    233void s3c_pm_save_core(void)
    234{
    235	s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
    236	s3c_pm_do_save(core_save, ARRAY_SIZE(core_save));
    237}
    238#endif
    239
    240/* since both s3c6400 and s3c6410 share the same sleep pm calls, we
    241 * put the per-cpu code in here until any new cpu comes along and changes
    242 * this.
    243 */
    244
    245static int s3c64xx_cpu_suspend(unsigned long arg)
    246{
    247	unsigned long tmp;
    248
    249	/* set our standby method to sleep */
    250
    251	tmp = __raw_readl(S3C64XX_PWR_CFG);
    252	tmp &= ~S3C64XX_PWRCFG_CFG_WFI_MASK;
    253	tmp |= S3C64XX_PWRCFG_CFG_WFI_SLEEP;
    254	__raw_writel(tmp, S3C64XX_PWR_CFG);
    255
    256	/* clear any old wakeup */
    257
    258	__raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT),
    259		     S3C64XX_WAKEUP_STAT);
    260
    261	/* set the LED state to 0110 over sleep */
    262	s3c_pm_debug_smdkled(3 << 1, 0xf);
    263
    264	/* issue the standby signal into the pm unit. Note, we
    265	 * issue a write-buffer drain just in case */
    266
    267	tmp = 0;
    268
    269	asm("b 1f\n\t"
    270	    ".align 5\n\t"
    271	    "1:\n\t"
    272	    "mcr p15, 0, %0, c7, c10, 5\n\t"
    273	    "mcr p15, 0, %0, c7, c10, 4\n\t"
    274	    "mcr p15, 0, %0, c7, c0, 4" :: "r" (tmp));
    275
    276	/* we should never get past here */
    277
    278	pr_info("Failed to suspend the system\n");
    279	return 1; /* Aborting suspend */
    280}
    281
    282/* mapping of interrupts to parts of the wakeup mask */
    283static const struct samsung_wakeup_mask wake_irqs[] = {
    284	{ .irq = IRQ_RTC_ALARM,	.bit = S3C64XX_PWRCFG_RTC_ALARM_DISABLE, },
    285	{ .irq = IRQ_RTC_TIC,	.bit = S3C64XX_PWRCFG_RTC_TICK_DISABLE, },
    286	{ .irq = IRQ_PENDN,	.bit = S3C64XX_PWRCFG_TS_DISABLE, },
    287	{ .irq = IRQ_HSMMC0,	.bit = S3C64XX_PWRCFG_MMC0_DISABLE, },
    288	{ .irq = IRQ_HSMMC1,	.bit = S3C64XX_PWRCFG_MMC1_DISABLE, },
    289	{ .irq = IRQ_HSMMC2,	.bit = S3C64XX_PWRCFG_MMC2_DISABLE, },
    290	{ .irq = NO_WAKEUP_IRQ,	.bit = S3C64XX_PWRCFG_BATF_DISABLE},
    291	{ .irq = NO_WAKEUP_IRQ,	.bit = S3C64XX_PWRCFG_MSM_DISABLE },
    292	{ .irq = NO_WAKEUP_IRQ,	.bit = S3C64XX_PWRCFG_HSI_DISABLE },
    293	{ .irq = NO_WAKEUP_IRQ,	.bit = S3C64XX_PWRCFG_MSM_DISABLE },
    294};
    295
    296static void s3c64xx_pm_prepare(void)
    297{
    298	samsung_sync_wakemask(S3C64XX_PWR_CFG,
    299			      wake_irqs, ARRAY_SIZE(wake_irqs));
    300
    301	/* store address of resume. */
    302	__raw_writel(__pa_symbol(s3c_cpu_resume), S3C64XX_INFORM0);
    303
    304	/* ensure previous wakeup state is cleared before sleeping */
    305	__raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), S3C64XX_WAKEUP_STAT);
    306}
    307
    308#ifdef CONFIG_SAMSUNG_PM_DEBUG
    309void s3c_pm_arch_update_uart(void __iomem *regs, struct pm_uart_save *save)
    310{
    311	u32 ucon;
    312	u32 ucon_clk
    313	u32 save_clk;
    314	u32 new_ucon;
    315	u32 delta;
    316
    317	if (!soc_is_s3c64xx())
    318		return;
    319
    320	ucon = __raw_readl(regs + S3C2410_UCON);
    321	ucon_clk = ucon & S3C6400_UCON_CLKMASK;
    322	sav_clk = save->ucon & S3C6400_UCON_CLKMASK;
    323
    324	/* S3C64XX UART blocks only support level interrupts, so ensure that
    325	 * when we restore unused UART blocks we force the level interrupt
    326	 * settings. */
    327	save->ucon |= S3C2410_UCON_TXILEVEL | S3C2410_UCON_RXILEVEL;
    328
    329	/* We have a constraint on changing the clock type of the UART
    330	 * between UCLKx and PCLK, so ensure that when we restore UCON
    331	 * that the CLK field is correctly modified if the bootloader
    332	 * has changed anything.
    333	 */
    334	if (ucon_clk != save_clk) {
    335		new_ucon = save->ucon;
    336		delta = ucon_clk ^ save_clk;
    337
    338		/* change from UCLKx => wrong PCLK,
    339		 * either UCLK can be tested for by a bit-test
    340		 * with UCLK0 */
    341		if (ucon_clk & S3C6400_UCON_UCLK0 &&
    342		    !(save_clk & S3C6400_UCON_UCLK0) &&
    343		    delta & S3C6400_UCON_PCLK2) {
    344			new_ucon &= ~S3C6400_UCON_UCLK0;
    345		} else if (delta == S3C6400_UCON_PCLK2) {
    346			/* as an precaution, don't change from
    347			 * PCLK2 => PCLK or vice-versa */
    348			new_ucon ^= S3C6400_UCON_PCLK2;
    349		}
    350
    351		S3C_PMDBG("ucon change %04x => %04x (save=%04x)\n",
    352			  ucon, new_ucon, save->ucon);
    353		save->ucon = new_ucon;
    354	}
    355}
    356#endif
    357
    358int __init s3c64xx_pm_init(void)
    359{
    360	int i;
    361
    362	s3c_pm_init();
    363
    364	for (i = 0; i < ARRAY_SIZE(s3c64xx_always_on_pm_domains); i++)
    365		pm_genpd_init(&s3c64xx_always_on_pm_domains[i]->pd,
    366			      &pm_domain_always_on_gov, false);
    367
    368	for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++)
    369		pm_genpd_init(&s3c64xx_pm_domains[i]->pd, NULL, false);
    370
    371#ifdef CONFIG_S3C_DEV_FB
    372	if (dev_get_platdata(&s3c_device_fb.dev))
    373		pm_genpd_add_device(&s3c64xx_pm_f.pd, &s3c_device_fb.dev);
    374#endif
    375
    376	return 0;
    377}
    378
    379static __init int s3c64xx_pm_initcall(void)
    380{
    381	if (!soc_is_s3c64xx())
    382		return 0;
    383
    384	pm_cpu_prep = s3c64xx_pm_prepare;
    385	pm_cpu_sleep = s3c64xx_cpu_suspend;
    386
    387#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
    388	gpio_request(S3C64XX_GPN(12), "DEBUG_LED0");
    389	gpio_request(S3C64XX_GPN(13), "DEBUG_LED1");
    390	gpio_request(S3C64XX_GPN(14), "DEBUG_LED2");
    391	gpio_request(S3C64XX_GPN(15), "DEBUG_LED3");
    392	gpio_direction_output(S3C64XX_GPN(12), 0);
    393	gpio_direction_output(S3C64XX_GPN(13), 0);
    394	gpio_direction_output(S3C64XX_GPN(14), 0);
    395	gpio_direction_output(S3C64XX_GPN(15), 0);
    396#endif
    397
    398	return 0;
    399}
    400arch_initcall(s3c64xx_pm_initcall);