1*214ffae0SHongbo Zhang /* 2*214ffae0SHongbo Zhang * Copyright 2016 Freescale Semiconductor, Inc. 3*214ffae0SHongbo Zhang * Author: Hongbo Zhang <hongbo.zhang@nxp.com> 4*214ffae0SHongbo Zhang * 5*214ffae0SHongbo Zhang * SPDX-License-Identifier: GPL-2.0+ 6*214ffae0SHongbo Zhang * This file implements LS102X platform PSCI SYSTEM-SUSPEND function 7*214ffae0SHongbo Zhang */ 8*214ffae0SHongbo Zhang 9*214ffae0SHongbo Zhang #include <config.h> 10*214ffae0SHongbo Zhang #include <asm/io.h> 11*214ffae0SHongbo Zhang #include <asm/psci.h> 12*214ffae0SHongbo Zhang #include <asm/arch/immap_ls102xa.h> 13*214ffae0SHongbo Zhang #include <fsl_immap.h> 14*214ffae0SHongbo Zhang #include "fsl_epu.h" 15*214ffae0SHongbo Zhang 16*214ffae0SHongbo Zhang #define __secure __attribute__((section("._secure.text"))) 17*214ffae0SHongbo Zhang 18*214ffae0SHongbo Zhang #define CCSR_GICD_CTLR 0x1000 19*214ffae0SHongbo Zhang #define CCSR_GICC_CTLR 0x2000 20*214ffae0SHongbo Zhang #define DCSR_RCPM_CG1CR0 0x31c 21*214ffae0SHongbo Zhang #define DCSR_RCPM_CSTTACR0 0xb00 22*214ffae0SHongbo Zhang #define DCFG_CRSTSR_WDRFR 0x8 23*214ffae0SHongbo Zhang #define DDR_RESV_LEN 128 24*214ffae0SHongbo Zhang 25*214ffae0SHongbo Zhang #ifdef CONFIG_LS1_DEEP_SLEEP 26*214ffae0SHongbo Zhang /* 27*214ffae0SHongbo Zhang * DDR controller initialization training breaks the first 128 bytes of DDR, 28*214ffae0SHongbo Zhang * save them so that the bootloader can restore them while resuming. 29*214ffae0SHongbo Zhang */ 30*214ffae0SHongbo Zhang static void __secure ls1_save_ddr_head(void) 31*214ffae0SHongbo Zhang { 32*214ffae0SHongbo Zhang const char *src = (const char *)CONFIG_SYS_SDRAM_BASE; 33*214ffae0SHongbo Zhang char *dest = (char *)(OCRAM_BASE_S_ADDR + OCRAM_S_SIZE - DDR_RESV_LEN); 34*214ffae0SHongbo Zhang struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR; 35*214ffae0SHongbo Zhang int i; 36*214ffae0SHongbo Zhang 37*214ffae0SHongbo Zhang out_le32(&scfg->sparecr[2], dest); 38*214ffae0SHongbo Zhang 39*214ffae0SHongbo Zhang for (i = 0; i < DDR_RESV_LEN; i++) 40*214ffae0SHongbo Zhang *dest++ = *src++; 41*214ffae0SHongbo Zhang } 42*214ffae0SHongbo Zhang 43*214ffae0SHongbo Zhang static void __secure ls1_fsm_setup(void) 44*214ffae0SHongbo Zhang { 45*214ffae0SHongbo Zhang void *dcsr_epu_base = (void *)(CONFIG_SYS_DCSRBAR + EPU_BLOCK_OFFSET); 46*214ffae0SHongbo Zhang void *dcsr_rcpm_base = (void *)CONFIG_SYS_DCSR_RCPM_ADDR; 47*214ffae0SHongbo Zhang 48*214ffae0SHongbo Zhang out_be32(dcsr_rcpm_base + DCSR_RCPM_CSTTACR0, 0x00001001); 49*214ffae0SHongbo Zhang out_be32(dcsr_rcpm_base + DCSR_RCPM_CG1CR0, 0x00000001); 50*214ffae0SHongbo Zhang 51*214ffae0SHongbo Zhang fsl_epu_setup((void *)dcsr_epu_base); 52*214ffae0SHongbo Zhang 53*214ffae0SHongbo Zhang /* Pull MCKE signal low before enabling deep sleep signal in FPGA */ 54*214ffae0SHongbo Zhang out_be32(dcsr_epu_base + EPECR0, 0x5); 55*214ffae0SHongbo Zhang out_be32(dcsr_epu_base + EPSMCR15, 0x76300000); 56*214ffae0SHongbo Zhang } 57*214ffae0SHongbo Zhang 58*214ffae0SHongbo Zhang static void __secure ls1_deepsleep_irq_cfg(void) 59*214ffae0SHongbo Zhang { 60*214ffae0SHongbo Zhang struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR; 61*214ffae0SHongbo Zhang struct ccsr_rcpm __iomem *rcpm = (void *)CONFIG_SYS_FSL_RCPM_ADDR; 62*214ffae0SHongbo Zhang u32 ippdexpcr0, ippdexpcr1, pmcintecr = 0; 63*214ffae0SHongbo Zhang 64*214ffae0SHongbo Zhang /* Mask interrupts from GIC */ 65*214ffae0SHongbo Zhang out_be32(&rcpm->nfiqoutr, 0x0ffffffff); 66*214ffae0SHongbo Zhang out_be32(&rcpm->nirqoutr, 0x0ffffffff); 67*214ffae0SHongbo Zhang /* Mask deep sleep wake-up interrupts while entering deep sleep */ 68*214ffae0SHongbo Zhang out_be32(&rcpm->dsimskr, 0x0ffffffff); 69*214ffae0SHongbo Zhang 70*214ffae0SHongbo Zhang ippdexpcr0 = in_be32(&rcpm->ippdexpcr0); 71*214ffae0SHongbo Zhang /* 72*214ffae0SHongbo Zhang * Workaround: There is bug of register ippdexpcr1, when read it always 73*214ffae0SHongbo Zhang * returns zero, so its value is saved to a scrachpad register to be 74*214ffae0SHongbo Zhang * read, that is why we don't read it from register ippdexpcr1 itself. 75*214ffae0SHongbo Zhang */ 76*214ffae0SHongbo Zhang ippdexpcr1 = in_le32(&scfg->sparecr[7]); 77*214ffae0SHongbo Zhang 78*214ffae0SHongbo Zhang if (ippdexpcr0 & RCPM_IPPDEXPCR0_ETSEC) 79*214ffae0SHongbo Zhang pmcintecr |= SCFG_PMCINTECR_ETSECRXG0 | 80*214ffae0SHongbo Zhang SCFG_PMCINTECR_ETSECRXG1 | 81*214ffae0SHongbo Zhang SCFG_PMCINTECR_ETSECERRG0 | 82*214ffae0SHongbo Zhang SCFG_PMCINTECR_ETSECERRG1; 83*214ffae0SHongbo Zhang 84*214ffae0SHongbo Zhang if (ippdexpcr0 & RCPM_IPPDEXPCR0_GPIO) 85*214ffae0SHongbo Zhang pmcintecr |= SCFG_PMCINTECR_GPIO; 86*214ffae0SHongbo Zhang 87*214ffae0SHongbo Zhang if (ippdexpcr1 & RCPM_IPPDEXPCR1_LPUART) 88*214ffae0SHongbo Zhang pmcintecr |= SCFG_PMCINTECR_LPUART; 89*214ffae0SHongbo Zhang 90*214ffae0SHongbo Zhang if (ippdexpcr1 & RCPM_IPPDEXPCR1_FLEXTIMER) 91*214ffae0SHongbo Zhang pmcintecr |= SCFG_PMCINTECR_FTM; 92*214ffae0SHongbo Zhang 93*214ffae0SHongbo Zhang /* Always set external IRQ pins as wakeup source */ 94*214ffae0SHongbo Zhang pmcintecr |= SCFG_PMCINTECR_IRQ0 | SCFG_PMCINTECR_IRQ1; 95*214ffae0SHongbo Zhang 96*214ffae0SHongbo Zhang out_be32(&scfg->pmcintlecr, 0); 97*214ffae0SHongbo Zhang /* Clear PMC interrupt status */ 98*214ffae0SHongbo Zhang out_be32(&scfg->pmcintsr, 0xffffffff); 99*214ffae0SHongbo Zhang /* Enable wakeup interrupt during deep sleep */ 100*214ffae0SHongbo Zhang out_be32(&scfg->pmcintecr, pmcintecr); 101*214ffae0SHongbo Zhang } 102*214ffae0SHongbo Zhang 103*214ffae0SHongbo Zhang static void __secure ls1_delay(unsigned int loop) 104*214ffae0SHongbo Zhang { 105*214ffae0SHongbo Zhang while (loop--) { 106*214ffae0SHongbo Zhang int i = 1000; 107*214ffae0SHongbo Zhang while (i--) 108*214ffae0SHongbo Zhang ; 109*214ffae0SHongbo Zhang } 110*214ffae0SHongbo Zhang } 111*214ffae0SHongbo Zhang 112*214ffae0SHongbo Zhang static void __secure ls1_start_fsm(void) 113*214ffae0SHongbo Zhang { 114*214ffae0SHongbo Zhang void *dcsr_epu_base = (void *)(CONFIG_SYS_DCSRBAR + EPU_BLOCK_OFFSET); 115*214ffae0SHongbo Zhang void *ccsr_gic_base = (void *)CONFIG_SYS_GIC_ADDR; 116*214ffae0SHongbo Zhang struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR; 117*214ffae0SHongbo Zhang struct ccsr_ddr __iomem *ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR; 118*214ffae0SHongbo Zhang 119*214ffae0SHongbo Zhang /* Set HRSTCR */ 120*214ffae0SHongbo Zhang setbits_be32(&scfg->hrstcr, 0x80000000); 121*214ffae0SHongbo Zhang 122*214ffae0SHongbo Zhang /* Place DDR controller in self refresh mode */ 123*214ffae0SHongbo Zhang setbits_be32(&ddr->sdram_cfg_2, 0x80000000); 124*214ffae0SHongbo Zhang 125*214ffae0SHongbo Zhang ls1_delay(2000); 126*214ffae0SHongbo Zhang 127*214ffae0SHongbo Zhang /* Set EVT4_B to lock the signal MCKE down */ 128*214ffae0SHongbo Zhang out_be32(dcsr_epu_base + EPECR0, 0x0); 129*214ffae0SHongbo Zhang 130*214ffae0SHongbo Zhang ls1_delay(2000); 131*214ffae0SHongbo Zhang 132*214ffae0SHongbo Zhang out_be32(ccsr_gic_base + CCSR_GICD_CTLR, 0x0); 133*214ffae0SHongbo Zhang out_be32(ccsr_gic_base + CCSR_GICC_CTLR, 0x0); 134*214ffae0SHongbo Zhang 135*214ffae0SHongbo Zhang /* Enable all EPU Counters */ 136*214ffae0SHongbo Zhang setbits_be32(dcsr_epu_base + EPGCR, 0x80000000); 137*214ffae0SHongbo Zhang 138*214ffae0SHongbo Zhang /* Enable SCU15 */ 139*214ffae0SHongbo Zhang setbits_be32(dcsr_epu_base + EPECR15, 0x90000004); 140*214ffae0SHongbo Zhang 141*214ffae0SHongbo Zhang /* Enter WFI mode, and EPU FSM will start */ 142*214ffae0SHongbo Zhang __asm__ __volatile__ ("wfi" : : : "memory"); 143*214ffae0SHongbo Zhang 144*214ffae0SHongbo Zhang /* NEVER ENTER HERE */ 145*214ffae0SHongbo Zhang while (1) 146*214ffae0SHongbo Zhang ; 147*214ffae0SHongbo Zhang } 148*214ffae0SHongbo Zhang 149*214ffae0SHongbo Zhang static void __secure ls1_deep_sleep(u32 entry_point) 150*214ffae0SHongbo Zhang { 151*214ffae0SHongbo Zhang struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR; 152*214ffae0SHongbo Zhang struct ccsr_gur __iomem *gur = (void *)CONFIG_SYS_FSL_GUTS_ADDR; 153*214ffae0SHongbo Zhang struct ccsr_rcpm __iomem *rcpm = (void *)CONFIG_SYS_FSL_RCPM_ADDR; 154*214ffae0SHongbo Zhang #ifdef QIXIS_BASE 155*214ffae0SHongbo Zhang u32 tmp; 156*214ffae0SHongbo Zhang void *qixis_base = (void *)QIXIS_BASE; 157*214ffae0SHongbo Zhang #endif 158*214ffae0SHongbo Zhang 159*214ffae0SHongbo Zhang /* Enable cluster to enter the PCL10 state */ 160*214ffae0SHongbo Zhang out_be32(&scfg->clusterpmcr, SCFG_CLUSTERPMCR_WFIL2EN); 161*214ffae0SHongbo Zhang 162*214ffae0SHongbo Zhang /* Save the first 128 bytes of DDR data */ 163*214ffae0SHongbo Zhang ls1_save_ddr_head(); 164*214ffae0SHongbo Zhang 165*214ffae0SHongbo Zhang /* Save the kernel resume entry */ 166*214ffae0SHongbo Zhang out_le32(&scfg->sparecr[3], entry_point); 167*214ffae0SHongbo Zhang 168*214ffae0SHongbo Zhang /* Request to put cluster 0 in PCL10 state */ 169*214ffae0SHongbo Zhang setbits_be32(&rcpm->clpcl10setr, RCPM_CLPCL10SETR_C0); 170*214ffae0SHongbo Zhang 171*214ffae0SHongbo Zhang /* Setup the registers of the EPU FSM for deep sleep */ 172*214ffae0SHongbo Zhang ls1_fsm_setup(); 173*214ffae0SHongbo Zhang 174*214ffae0SHongbo Zhang #ifdef QIXIS_BASE 175*214ffae0SHongbo Zhang /* Connect the EVENT button to IRQ in FPGA */ 176*214ffae0SHongbo Zhang tmp = in_8(qixis_base + QIXIS_CTL_SYS); 177*214ffae0SHongbo Zhang tmp &= ~QIXIS_CTL_SYS_EVTSW_MASK; 178*214ffae0SHongbo Zhang tmp |= QIXIS_CTL_SYS_EVTSW_IRQ; 179*214ffae0SHongbo Zhang out_8(qixis_base + QIXIS_CTL_SYS, tmp); 180*214ffae0SHongbo Zhang 181*214ffae0SHongbo Zhang /* Enable deep sleep signals in FPGA */ 182*214ffae0SHongbo Zhang tmp = in_8(qixis_base + QIXIS_PWR_CTL2); 183*214ffae0SHongbo Zhang tmp |= QIXIS_PWR_CTL2_PCTL; 184*214ffae0SHongbo Zhang out_8(qixis_base + QIXIS_PWR_CTL2, tmp); 185*214ffae0SHongbo Zhang 186*214ffae0SHongbo Zhang /* Pull down PCIe RST# */ 187*214ffae0SHongbo Zhang tmp = in_8(qixis_base + QIXIS_RST_FORCE_3); 188*214ffae0SHongbo Zhang tmp |= QIXIS_RST_FORCE_3_PCIESLOT1; 189*214ffae0SHongbo Zhang out_8(qixis_base + QIXIS_RST_FORCE_3, tmp); 190*214ffae0SHongbo Zhang #endif 191*214ffae0SHongbo Zhang 192*214ffae0SHongbo Zhang /* Enable Warm Device Reset */ 193*214ffae0SHongbo Zhang setbits_be32(&scfg->dpslpcr, SCFG_DPSLPCR_WDRR_EN); 194*214ffae0SHongbo Zhang setbits_be32(&gur->crstsr, DCFG_CRSTSR_WDRFR); 195*214ffae0SHongbo Zhang 196*214ffae0SHongbo Zhang ls1_deepsleep_irq_cfg(); 197*214ffae0SHongbo Zhang 198*214ffae0SHongbo Zhang psci_v7_flush_dcache_all(); 199*214ffae0SHongbo Zhang 200*214ffae0SHongbo Zhang ls1_start_fsm(); 201*214ffae0SHongbo Zhang } 202*214ffae0SHongbo Zhang 203*214ffae0SHongbo Zhang #else 204*214ffae0SHongbo Zhang static void __secure ls1_sleep(void) 205*214ffae0SHongbo Zhang { 206*214ffae0SHongbo Zhang struct ccsr_scfg __iomem *scfg = (void *)CONFIG_SYS_FSL_SCFG_ADDR; 207*214ffae0SHongbo Zhang struct ccsr_rcpm __iomem *rcpm = (void *)CONFIG_SYS_FSL_RCPM_ADDR; 208*214ffae0SHongbo Zhang 209*214ffae0SHongbo Zhang #ifdef QIXIS_BASE 210*214ffae0SHongbo Zhang u32 tmp; 211*214ffae0SHongbo Zhang void *qixis_base = (void *)QIXIS_BASE; 212*214ffae0SHongbo Zhang 213*214ffae0SHongbo Zhang /* Connect the EVENT button to IRQ in FPGA */ 214*214ffae0SHongbo Zhang tmp = in_8(qixis_base + QIXIS_CTL_SYS); 215*214ffae0SHongbo Zhang tmp &= ~QIXIS_CTL_SYS_EVTSW_MASK; 216*214ffae0SHongbo Zhang tmp |= QIXIS_CTL_SYS_EVTSW_IRQ; 217*214ffae0SHongbo Zhang out_8(qixis_base + QIXIS_CTL_SYS, tmp); 218*214ffae0SHongbo Zhang #endif 219*214ffae0SHongbo Zhang 220*214ffae0SHongbo Zhang /* Enable cluster to enter the PCL10 state */ 221*214ffae0SHongbo Zhang out_be32(&scfg->clusterpmcr, SCFG_CLUSTERPMCR_WFIL2EN); 222*214ffae0SHongbo Zhang 223*214ffae0SHongbo Zhang setbits_be32(&rcpm->powmgtcsr, RCPM_POWMGTCSR_LPM20_REQ); 224*214ffae0SHongbo Zhang 225*214ffae0SHongbo Zhang __asm__ __volatile__ ("wfi" : : : "memory"); 226*214ffae0SHongbo Zhang } 227*214ffae0SHongbo Zhang #endif 228*214ffae0SHongbo Zhang 229*214ffae0SHongbo Zhang void __secure ls1_system_suspend(u32 fn, u32 entry_point, u32 context_id) 230*214ffae0SHongbo Zhang { 231*214ffae0SHongbo Zhang #ifdef CONFIG_LS1_DEEP_SLEEP 232*214ffae0SHongbo Zhang ls1_deep_sleep(entry_point); 233*214ffae0SHongbo Zhang #else 234*214ffae0SHongbo Zhang ls1_sleep(); 235*214ffae0SHongbo Zhang #endif 236*214ffae0SHongbo Zhang } 237