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 34 struct s3c64xx_pm_domain { 35 char *const name; 36 u32 ena; 37 u32 pwr_stat; 38 struct generic_pm_domain pd; 39 }; 40 41 static 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 55 static 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 84 static 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 93 static 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 103 static 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 113 static 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 123 static 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 133 static 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 143 static 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 152 static 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 162 static struct s3c64xx_pm_domain *s3c64xx_always_on_pm_domains[] = { 163 &s3c64xx_pm_irom, 164 }; 165 166 static 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 177 void 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 194 static struct sleep_save core_save[] = { 195 SAVE_ITEM(S3C64XX_MEM0DRVCON), 196 SAVE_ITEM(S3C64XX_MEM1DRVCON), 197 }; 198 199 static 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 218 void s3c_pm_configure_extint(void) 219 { 220 __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK); 221 } 222 223 void 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 233 void 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 245 static 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 */ 283 static 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 296 static 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 309 void 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 358 int __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 379 static __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 } 400 arch_initcall(s3c64xx_pm_initcall); 401