diff --git a/arch/sw_64/Kconfig b/arch/sw_64/Kconfig index 17b48c7e5fc1ce5e7191824c23e3a738e3102a50..b37a4f4e093e7675366ee85964c8656cfce294b8 100644 --- a/arch/sw_64/Kconfig +++ b/arch/sw_64/Kconfig @@ -876,6 +876,12 @@ config SW64_SUSPEND_DEEPSLEEP_BOOTCORE bool "SW64 bootcore suspend into deep sleep mode" default n +config SW64_SUPPORT_S3_SLEEPING_STATE + depends on SUSPEND + bool "SW64 support S3 sleeping state" + default n + help + Only SW831 support S3 sleep option and needs SROM, HMCode and BIOS support. source "drivers/cpuidle/Kconfig" diff --git a/arch/sw_64/chip/chip3/chip.c b/arch/sw_64/chip/chip3/chip.c index 105389d5989fe7d2f81ad5da3e229cf122f1885e..d0b1c1c1c6df7ac3a95c1a78832477aee58e2c11 100644 --- a/arch/sw_64/chip/chip3/chip.c +++ b/arch/sw_64/chip/chip3/chip.c @@ -493,6 +493,172 @@ static void chip3_device_interrupt(unsigned long irq_info) } } +static void chip3_i2c_srst(void) +{ + sw64_io_write(0, I2C0_SRST_L, 0x0); + sw64_io_write(0, I2C0_SRST_L, 0x1); + + sw64_io_write(0, I2C1_SRST_L, 0x0); + sw64_io_write(0, I2C1_SRST_L, 0x1); + + sw64_io_write(0, I2C2_SRST_L, 0x0); + sw64_io_write(0, I2C2_SRST_L, 0x1); +} + +static void chip3_pcie_save(void) +{ + struct pci_controller *hose; + struct piu_saved *piu_save; + unsigned long node, index; + unsigned long i; + + for (hose = hose_head; hose; hose = hose->next) { + piu_save = kzalloc(sizeof(*piu_save), GFP_KERNEL); + + node = hose->node; + index = hose->index; + hose->sysdata = piu_save; + + piu_save->piuconfig0 = read_piu_ior0(node, index, PIUCONFIG0); + piu_save->piuconfig1 = read_piu_ior1(node, index, PIUCONFIG1); + piu_save->epdmabar = read_piu_ior0(node, index, EPDMABAR); + piu_save->msiaddr = read_piu_ior0(node, index, MSIADDR); + + for (i = 0; i < 256; i++) { + piu_save->msiconfig[i] = read_piu_ior0(node, index, + MSICONFIG0 + (i << 7)); + } + } +} + +static void chip3_pcie_restore(void) +{ + struct pci_controller *hose; + struct piu_saved *piu_save; + unsigned long node, index; + u32 rc_misc_ctrl; + unsigned int value; + unsigned long i; + + for (hose = hose_head; hose; hose = hose->next) { + node = hose->node; + index = hose->index; + piu_save = hose->sysdata; + + write_piu_ior0(node, index, PIUCONFIG0, piu_save->piuconfig0); + write_piu_ior1(node, index, PIUCONFIG1, piu_save->piuconfig1); + write_piu_ior0(node, index, EPDMABAR, piu_save->epdmabar); + write_piu_ior0(node, index, MSIADDR, piu_save->msiaddr); + + for (i = 0; i < 256; i++) { + write_piu_ior0(node, index, MSICONFIG0 + (i << 7), + piu_save->msiconfig[i]); + } + + /* Enable DBI_RO_WR_EN */ + rc_misc_ctrl = read_rc_conf(node, index, RC_MISC_CONTROL_1); + write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl | 0x1); + + /* Fix up DEVICE_ID_VENDOR_ID register */ + value = (PCI_DEVICE_ID_CHIP3 << 16) | PCI_VENDOR_ID_JN; + write_rc_conf(node, index, RC_VENDOR_ID, value); + + /* Set PCI-E root class code */ + value = read_rc_conf(node, index, RC_REVISION_ID); + write_rc_conf(node, index, RC_REVISION_ID, (PCI_CLASS_BRIDGE_HOST << 16) | value); + + /* Disable DBI_RO_WR_EN */ + write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl); + } + +} + +static unsigned long saved_dvc_int, saved_long_time; + +static inline void chip3_intpu_save(void) +{ + saved_long_time = sw64_io_read(0, LONG_TIME); +} + +static inline void chip3_intpu_restore(void) +{ + switch (cpu_desc.model) { + case CPU_SW831: + sw64_io_write(0, LONG_TIME, saved_long_time); + sw64_io_write(0, LONG_TIME_START_EN, 0x1); + break; + default: + pr_info("long time start is disable!"); + break; + } +} + +static inline void chip3_spbu_save(void) +{ + saved_dvc_int = sw64_io_read(0, MCU_DVC_INT_EN); +} + +static inline void chip3_spbu_restore(void) +{ + chip3_i2c_srst(); + sw64_io_write(0, MCU_DVC_INT_EN, saved_dvc_int); +} + +#define BIOS_SECBIN 0x2F00000UL +#define BIOS_SECSIZE 0x40000UL +#define BOUNCE_BUFFER ((1UL<<32) - BIOS_SECSIZE) +#define BIOS_MEMSAVE ((1UL<<32) - 2 * BIOS_SECSIZE) + +/* + * Due to specific architecture PCI MEM32 addressing, we reserve 512M memory + * size at PCI_32BIT_MEMIO (0xE000_0000) on SW64 platform. + * + * Since this memory region is still usable by OS, we implement a interface + * contract between BIOS and kernel: + * + * Firstly BIOS should back up SEC relative code segment to BIOS_MEMSAVE region + * with the length BIOS_SECSIZE in order to restore BIOS SEC phase binary during + * S3 sleep. + * + * Secondly kernel should use a bounce buffer to save memory region which may be + * overwritten by BIOS on resume from S3 sleep. + */ +static void chip3_mem_restore(void) +{ + void *dst, *src; + unsigned long size = BIOS_SECSIZE; + + /* Firstly kernel back up to a bounce buffer */ + src = __va(BIOS_SECBIN); + dst = __va(BOUNCE_BUFFER); + memcpy(dst, src, size); + + /* Secondly restore BIOS SEC phase binary */ + src = __va(BIOS_MEMSAVE); + dst = __va(BIOS_SECBIN); + memcpy(dst, src, size); +} + +extern void cpld_write(uint8_t slave_addr, uint8_t reg, uint8_t data); + +static void chip3_suspend(bool wakeup) +{ + + if (wakeup) { + chip3_pcie_restore(); + chip3_intpu_restore(); + chip3_spbu_restore(); + } else { + /* Set S3 flag */ + cpld_write(0x64, 0x34, 0x33); + + chip3_spbu_save(); + chip3_intpu_save(); + chip3_pcie_save(); + chip3_mem_restore(); + } +} + static void chip3_hose_init(struct pci_controller *hose) { unsigned long pci_io_base; @@ -574,6 +740,7 @@ static struct sw64_chip_init_ops chip3_chip_init_ops = { static struct sw64_chip_ops chip3_chip_ops = { .get_cpu_num = chip3_get_cpu_nums, + .suspend = chip3_suspend, .fixup = chip3_ops_fixup, }; diff --git a/arch/sw_64/include/asm/chip3_io.h b/arch/sw_64/include/asm/chip3_io.h index 3bfbc2bdafe7f967f4e79d2f504861a4800e24f3..18e79cf2a36ba9aac5bd7dff9f93e3af59025c1f 100644 --- a/arch/sw_64/include/asm/chip3_io.h +++ b/arch/sw_64/include/asm/chip3_io.h @@ -165,6 +165,9 @@ enum { MC_CAP_CFG = MCU_BASE | 0x1180UL, IO_START = MCU_BASE | 0x1300UL, UART_ONLINE = MCU_BASE | 0x1780UL, + I2C0_SRST_L = MCU_BASE | 0x1900UL, + I2C1_SRST_L = MCU_BASE | 0x1980UL, + I2C2_SRST_L = MCU_BASE | 0x1a00UL, MCU_DVC_INT = MCU_BASE | 0x3000UL, MCU_DVC_INT_EN = MCU_BASE | 0x3080UL, SI_FAULT_STAT = MCU_BASE | 0x3100UL, diff --git a/arch/sw_64/include/asm/pci.h b/arch/sw_64/include/asm/pci.h index a90f80152470911af9b6c3d2080fb0c3956982ea..ab79d503b84daff89f18ca06771bcc8893f96b0c 100644 --- a/arch/sw_64/include/asm/pci.h +++ b/arch/sw_64/include/asm/pci.h @@ -18,6 +18,13 @@ struct resource; struct sunway_iommu; struct page; +struct piu_saved { + unsigned long piuconfig0; + unsigned long piuconfig1; + unsigned long epdmabar; + unsigned long msiaddr; + unsigned long msiconfig[256]; +}; /* A controller. Used to manage multiple PCI busses. */ diff --git a/arch/sw_64/include/asm/sw64_init.h b/arch/sw_64/include/asm/sw64_init.h index aae82f4163e00c1236205c02636968b7d92ee987..893bac1c621b499d0fabeb495097616606d34fae 100644 --- a/arch/sw_64/include/asm/sw64_init.h +++ b/arch/sw_64/include/asm/sw64_init.h @@ -32,7 +32,7 @@ struct sw64_chip_init_ops { struct sw64_chip_ops { int (*get_cpu_num)(void); void (*device_interrupt)(unsigned long irq_info); - void (*suspend)(int wake); + void (*suspend)(bool wake); void (*fixup)(void); }; diff --git a/arch/sw_64/kernel/smp.c b/arch/sw_64/kernel/smp.c index c0936d119c4ed62c19b43fe37a295a72a27271b3..b467562bce9ec9fca35981033655cc8300be6c8b 100644 --- a/arch/sw_64/kernel/smp.c +++ b/arch/sw_64/kernel/smp.c @@ -616,6 +616,7 @@ void native_cpu_die(unsigned int cpu) if (per_cpu(cpu_state, cpu) == CPU_DEAD) { if (system_state == SYSTEM_RUNNING) pr_info("CPU %u is now offline\n", cpu); + smp_rcb->ready = 0; return; } msleep(100); diff --git a/arch/sw_64/kernel/suspend.c b/arch/sw_64/kernel/suspend.c index 994d8e245878797917f75a1e1630cc0ff1d09529..b9798baa246745c68bcd21ce98f2d4a8fdaac66e 100644 --- a/arch/sw_64/kernel/suspend.c +++ b/arch/sw_64/kernel/suspend.c @@ -23,6 +23,8 @@ void disable_local_timer(void) wrtimer(0); } +extern struct pci_controller *hose_head; + /* * Boot Core will enter suspend stat here. */ @@ -32,6 +34,11 @@ void sw64_suspend_enter(void) * After wake up boot processor, pc will go here */ +#ifdef CONFIG_SW64_SUPPORT_S3_SLEEPING_STATE + if (sw64_chip->suspend) + sw64_chip->suspend(false); +#endif + disable_local_timer(); current_thread_info()->pcb.tp = rtid(); @@ -43,6 +50,11 @@ void sw64_suspend_enter(void) #endif wrtp(current_thread_info()->pcb.tp); +#ifdef CONFIG_SW64_SUPPORT_S3_SLEEPING_STATE + if (sw64_chip->suspend) + sw64_chip->suspend(true); +#endif + disable_local_timer(); } @@ -57,6 +69,7 @@ static const struct platform_suspend_ops native_suspend_ops = { .valid = native_suspend_state_valid, .enter = native_suspend_enter, }; + static int __init sw64_pm_init(void) { suspend_set_ops(&native_suspend_ops);