// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later /* * Everything to do with deep power saving (stop) states * SLeep/Winkle, Handle ChipTOD chip & configure core timebases * * Copyright 2013-2019 IBM Corp. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum wakeup_engine_states wakeup_engine_state = WAKEUP_ENGINE_NOT_PRESENT; bool has_deep_states = false; DEFINE_LOG_ENTRY(OPAL_RC_SLW_SET, OPAL_PLATFORM_ERR_EVT, OPAL_SLW, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_SLW_REG, OPAL_PLATFORM_ERR_EVT, OPAL_SLW, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); static bool slw_set_overrides_p10(struct proc_chip *chip, struct cpu_thread *c) { uint64_t tmp; int rc; uint32_t core = pir_to_core_id(c->pir); /* Special wakeup bits that could hold power mgt */ rc = xscom_read(chip->id, XSCOM_ADDR_P10_QME_CORE(core, P10_QME_SPWU_HYP), &tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to read P10_QME_SPWU_HYP\n"); return false; } if (tmp & P10_SPWU_REQ) prlog(PR_WARNING, "SLW: core %d P10_QME_SPWU_HYP requested 0x%016llx\n", core, tmp); return true; } static bool slw_set_overrides_p9(struct proc_chip *chip, struct cpu_thread *c) { uint64_t tmp; int rc; uint32_t core = pir_to_core_id(c->pir); /* Special wakeup bits that could hold power mgt */ rc = xscom_read(chip->id, XSCOM_ADDR_P9_EC_SLAVE(core, EC_PPM_SPECIAL_WKUP_HYP), &tmp); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_SET), "SLW: Failed to read EC_PPM_SPECIAL_WKUP_HYP\n"); return false; } if (tmp) prlog(PR_WARNING, "SLW: core %d EC_PPM_SPECIAL_WKUP_HYP read 0x%016llx\n", core, tmp); rc = xscom_read(chip->id, XSCOM_ADDR_P9_EC_SLAVE(core, EC_PPM_SPECIAL_WKUP_OTR), &tmp); if (tmp) prlog(PR_WARNING, "SLW: core %d EC_PPM_SPECIAL_WKUP_OTR read 0x%016llx\n", core, tmp); return true; } /* * cpu_idle_states for key idle states of POWER9 that we want to * exploit. * Note latency_ns and residency_ns are estimated values for now. */ static struct cpu_idle_states power9_cpu_idle_states[] = { { .name = "stop0_lite", /* Enter stop0 with no state loss */ .latency_ns = 1000, .residency_ns = 10000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 0*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(0) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3), .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop0", .latency_ns = 2000, .residency_ns = 20000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(0) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, /* stop1_lite has been removed since it adds no additional benefit over stop0_lite */ { .name = "stop1", .latency_ns = 5000, .residency_ns = 50000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(1) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, /* * stop2_lite has been removed since currently it adds minimal benefit over stop2. * However, the benefit is eclipsed by the time required to ungate the clocks */ { .name = "stop2", .latency_ns = 10000, .residency_ns = 100000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(2) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop4", .latency_ns = 100000, .residency_ns = 10000000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 1*OPAL_PM_LOSE_HYP_CONTEXT \ | 1*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_DEEP, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(4) \ | OPAL_PM_PSSCR_MTL(7) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop5", .latency_ns = 200000, .residency_ns = 20000000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 1*OPAL_PM_LOSE_HYP_CONTEXT \ | 1*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_DEEP, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(5) \ | OPAL_PM_PSSCR_MTL(7) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop8", .latency_ns = 2000000, .residency_ns = 20000000, .flags = 1*OPAL_PM_DEC_STOP \ | 1*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 1*OPAL_PM_LOSE_HYP_CONTEXT \ | 1*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_DEEP, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(8) \ | OPAL_PM_PSSCR_MTL(11) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop11", .latency_ns = 10000000, .residency_ns = 100000000, .flags = 1*OPAL_PM_DEC_STOP \ | 1*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 1*OPAL_PM_LOSE_HYP_CONTEXT \ | 1*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_DEEP, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(11) \ | OPAL_PM_PSSCR_MTL(11) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, }; /* * Prior to Mambo.7.8.21, mambo did set the MSR correctly for lite stop * states, so disable them for now. */ static struct cpu_idle_states power9_mambo_cpu_idle_states[] = { { .name = "stop0", .latency_ns = 2000, .residency_ns = 20000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(0) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop1", .latency_ns = 5000, .residency_ns = 50000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(1) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop2", .latency_ns = 10000, .residency_ns = 100000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(2) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop4", .latency_ns = 100000, .residency_ns = 1000000, .flags = 1*OPAL_PM_DEC_STOP \ | 1*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 1*OPAL_PM_LOSE_HYP_CONTEXT \ | 1*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_DEEP, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(4) \ | OPAL_PM_PSSCR_MTL(7) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop8", .latency_ns = 2000000, .residency_ns = 20000000, .flags = 1*OPAL_PM_DEC_STOP \ | 1*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 1*OPAL_PM_LOSE_HYP_CONTEXT \ | 1*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_DEEP, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(8) \ | OPAL_PM_PSSCR_MTL(11) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop11", .latency_ns = 10000000, .residency_ns = 100000000, .flags = 1*OPAL_PM_DEC_STOP \ | 1*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 1*OPAL_PM_LOSE_HYP_CONTEXT \ | 1*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_DEEP, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(11) \ | OPAL_PM_PSSCR_MTL(11) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, }; /* * cpu_idle_states for fused core configuration * These will be a subset of power9 idle states. */ static struct cpu_idle_states power9_fusedcore_cpu_idle_states[] = { { .name = "stop0_lite", /* Enter stop0 with no state loss */ .latency_ns = 1000, .residency_ns = 10000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 0*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(0) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3), .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop0", .latency_ns = 2000, .residency_ns = 20000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(0) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, /* stop1_lite has been removed since it adds no additional benefit over stop0_lite */ { .name = "stop1", .latency_ns = 5000, .residency_ns = 50000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(1) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, /* * stop2_lite has been removed since currently it adds minimal benefit over stop2. * However, the benefit is eclipsed by the time required to ungate the clocks */ { .name = "stop2", .latency_ns = 10000, .residency_ns = 100000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(2) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, }; /* * Note latency_ns and residency_ns are estimated values for now. */ static struct cpu_idle_states power10_cpu_idle_states[] = { { .name = "stop0_lite", /* Enter stop0 with no state loss */ .latency_ns = 1000, .residency_ns = 10000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 0*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(0) \ | OPAL_PM_PSSCR_MTL(0) \ | OPAL_PM_PSSCR_TR(3), .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop0", .latency_ns = 10000, .residency_ns = 100000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(0) \ | OPAL_PM_PSSCR_MTL(0) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop2", .latency_ns = 20000, .residency_ns = 200000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(2) \ | OPAL_PM_PSSCR_MTL(2) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, { .name = "stop3", .latency_ns = 45000, .residency_ns = 450000, .flags = 0*OPAL_PM_DEC_STOP \ | 0*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 0*OPAL_PM_LOSE_HYP_CONTEXT \ | 0*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_FAST, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(3) \ | OPAL_PM_PSSCR_MTL(3) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, #if 0 { .name = "stop11", .latency_ns = 10000000, .residency_ns = 100000000, .flags = 1*OPAL_PM_DEC_STOP \ | 1*OPAL_PM_TIMEBASE_STOP \ | 1*OPAL_PM_LOSE_USER_CONTEXT \ | 1*OPAL_PM_LOSE_HYP_CONTEXT \ | 1*OPAL_PM_LOSE_FULL_CONTEXT \ | 1*OPAL_PM_STOP_INST_DEEP, .pm_ctrl_reg_val = OPAL_PM_PSSCR_RL(11) \ | OPAL_PM_PSSCR_MTL(11) \ | OPAL_PM_PSSCR_TR(3) \ | OPAL_PM_PSSCR_ESL \ | OPAL_PM_PSSCR_EC, .pm_ctrl_reg_mask = OPAL_PM_PSSCR_MASK }, #endif }; static void slw_late_init_p9(struct proc_chip *chip) { struct cpu_thread *c; int rc; prlog(PR_INFO, "SLW: Configuring self-restore for HRMOR\n"); for_each_available_cpu(c) { if (c->chip_id != chip->id) continue; /* * Clear HRMOR. Need to update only for thread * 0 of each core. Doing it anyway for all threads */ rc = p9_stop_save_cpureg((void *)chip->homer_base, P9_STOP_SPR_HRMOR, 0, c->pir); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: Failed to set HRMOR for CPU %x,RC=0x%x\n", c->pir, rc); prlog(PR_ERR, "Disabling deep stop states\n"); } } } static void slw_late_init_p10(struct proc_chip *chip) { struct cpu_thread *c; int rc; prlog(PR_INFO, "SLW: Configuring self-restore for HRMOR\n"); for_each_available_cpu(c) { if (c->chip_id != chip->id) continue; /* * Clear HRMOR. Need to update only for thread * 0 of each core. Doing it anyway for all threads */ rc = proc_stop_save_cpureg((void *)chip->homer_base, PROC_STOP_SPR_HRMOR, 0, c->pir); if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: Failed to set HRMOR for CPU %x,RC=0x%x\n", c->pir, rc); prlog(PR_ERR, "Disabling deep stop states\n"); } } } /* Add device tree properties to describe idle states */ void add_cpu_idle_state_properties(void) { struct dt_node *power_mgt; struct cpu_idle_states *states = NULL; struct proc_chip *chip; int nr_states = 0; bool can_sleep = true; bool has_stop_inst = false; u8 i; fdt64_t *pm_ctrl_reg_val_buf; fdt64_t *pm_ctrl_reg_mask_buf; u32 supported_states_mask; u32 opal_disabled_states_mask = ~0xFC000000; /* all but stop11 */ const char* nvram_disable_str; u32 nvram_disabled_states_mask = 0x00; u32 stop_levels; /* Variables to track buffer length */ u8 name_buf_len; u8 num_supported_idle_states; /* Buffers to hold idle state properties */ char *name_buf, *alloced_name_buf; fdt32_t *latency_ns_buf; fdt32_t *residency_ns_buf; fdt32_t *flags_buf; prlog(PR_DEBUG, "CPU idle state device tree init\n"); /* Create /ibm,opal/power-mgt if it doesn't exist already */ power_mgt = dt_new_check(opal_node, "power-mgt"); if (!power_mgt) { /** * @fwts-label CreateDTPowerMgtNodeFail * @fwts-advice OPAL failed to add the power-mgt device tree * node. This could mean that firmware ran out of memory, * or there's a bug somewhere. */ prlog(PR_ERR, "creating dt node /ibm,opal/power-mgt failed\n"); return; } /* * Chose the right state table for the chip * * XXX We use the first chip version, we should probably look * for the smaller of all chips instead.. */ chip = next_chip(NULL); assert(chip); if (proc_gen >= proc_gen_p9) { if (chip->type == PROC_CHIP_P9_NIMBUS || chip->type == PROC_CHIP_P9_CUMULUS || chip->type == PROC_CHIP_P9P) { if (proc_chip_quirks & QUIRK_MAMBO_CALLOUTS) { states = power9_mambo_cpu_idle_states; nr_states = ARRAY_SIZE(power9_mambo_cpu_idle_states); } else if (this_cpu()->is_fused_core) { states = power9_fusedcore_cpu_idle_states; nr_states = ARRAY_SIZE(power9_fusedcore_cpu_idle_states); } else { states = power9_cpu_idle_states; nr_states = ARRAY_SIZE(power9_cpu_idle_states); } } else if (chip->type == PROC_CHIP_P10) { states = power10_cpu_idle_states; nr_states = ARRAY_SIZE(power10_cpu_idle_states); } else { prlog(PR_ERR, "determining chip type\n"); return; } has_stop_inst = true; stop_levels = dt_prop_get_u32_def(power_mgt, "ibm,enabled-stop-levels", 0); if (!stop_levels) { prerror("SLW: No stop levels available. Power saving is disabled!\n"); has_deep_states = false; } else { /* Iterate to see if we have deep states enabled */ for (i = 0; i < nr_states; i++) { u32 level = 31 - (states[i].pm_ctrl_reg_val & OPAL_PM_PSSCR_RL_MASK); if ((stop_levels & (1ul << level)) && (states[i].flags & OPAL_PM_STOP_INST_DEEP)) has_deep_states = true; } } if ((wakeup_engine_state == WAKEUP_ENGINE_PRESENT) && has_deep_states) { if (chip->type == PROC_CHIP_P9_NIMBUS || chip->type == PROC_CHIP_P9_CUMULUS) { slw_late_init_p9(chip); xive_late_init(); nx_p9_rng_late_init(); } else if (chip->type == PROC_CHIP_P10) { slw_late_init_p10(chip); xive2_late_init(); } } if (wakeup_engine_state != WAKEUP_ENGINE_PRESENT) has_deep_states = false; #ifdef CONFIG_P8 } else { find_cpu_idle_state_properties_p8(&states, &nr_states, &can_sleep); #endif } /* * Currently we can't append strings and cells to dt properties. * So create buffers to which you can append values, then create * dt properties with this buffer content. */ /* Allocate memory to idle state property buffers. */ alloced_name_buf= malloc(nr_states * sizeof(char) * MAX_NAME_LEN); name_buf = alloced_name_buf; latency_ns_buf = malloc(nr_states * sizeof(u32)); residency_ns_buf= malloc(nr_states * sizeof(u32)); flags_buf = malloc(nr_states * sizeof(u32)); pm_ctrl_reg_val_buf = malloc(nr_states * sizeof(u64)); pm_ctrl_reg_mask_buf = malloc(nr_states * sizeof(u64)); name_buf_len = 0; num_supported_idle_states = 0; /* * Create a mask with the flags of all supported idle states * set. Use this to only add supported idle states to the * device-tree */ if (has_stop_inst) { /* Power 9/10 / POWER ISA 3.0 and above */ supported_states_mask = OPAL_PM_STOP_INST_FAST; if (wakeup_engine_state == WAKEUP_ENGINE_PRESENT) supported_states_mask |= OPAL_PM_STOP_INST_DEEP; } else { /* Power 7 and Power 8 */ supported_states_mask = OPAL_PM_NAP_ENABLED; if (can_sleep) supported_states_mask |= OPAL_PM_SLEEP_ENABLED | OPAL_PM_SLEEP_ENABLED_ER1; if (wakeup_engine_state == WAKEUP_ENGINE_PRESENT) supported_states_mask |= OPAL_PM_WINKLE_ENABLED; } nvram_disable_str = nvram_query_dangerous("opal-stop-state-disable-mask"); if (nvram_disable_str) nvram_disabled_states_mask = strtol(nvram_disable_str, NULL, 0); prlog(PR_DEBUG, "NVRAM stop disable mask: %x\n", nvram_disabled_states_mask); for (i = 0; i < nr_states; i++) { /* For each state, check if it is one of the supported states. */ if (!(states[i].flags & supported_states_mask)) continue; /* We can only use the stop levels that HB has made available */ if (has_stop_inst) { u32 level = 31 - (states[i].pm_ctrl_reg_val & OPAL_PM_PSSCR_RL_MASK); if (!(stop_levels & (1ul << level))) continue; if ((opal_disabled_states_mask | nvram_disabled_states_mask) & (1ul << level)) { if (nvram_disable_str && !(nvram_disabled_states_mask & (1ul << level))) { prlog(PR_NOTICE, "SLW: Enabling: %s " "(disabled in OPAL, forced by " "NVRAM)\n",states[i].name); } else { prlog(PR_NOTICE, "SLW: Disabling: %s in OPAL\n", states[i].name); continue; } } } prlog(PR_INFO, "SLW: Enabling: %s\n", states[i].name); /* * If a state is supported add each of its property * to its corresponding property buffer. */ strncpy(name_buf, states[i].name, MAX_NAME_LEN); name_buf = name_buf + strlen(states[i].name) + 1; *latency_ns_buf = cpu_to_fdt32(states[i].latency_ns); latency_ns_buf++; *residency_ns_buf = cpu_to_fdt32(states[i].residency_ns); residency_ns_buf++; *flags_buf = cpu_to_fdt32(states[i].flags); flags_buf++; *pm_ctrl_reg_val_buf = cpu_to_fdt64(states[i].pm_ctrl_reg_val); pm_ctrl_reg_val_buf++; *pm_ctrl_reg_mask_buf = cpu_to_fdt64(states[i].pm_ctrl_reg_mask); pm_ctrl_reg_mask_buf++; /* Increment buffer length trackers */ name_buf_len += strlen(states[i].name) + 1; num_supported_idle_states++; } /* Point buffer pointers back to beginning of the buffer */ name_buf -= name_buf_len; latency_ns_buf -= num_supported_idle_states; residency_ns_buf -= num_supported_idle_states; flags_buf -= num_supported_idle_states; pm_ctrl_reg_val_buf -= num_supported_idle_states; pm_ctrl_reg_mask_buf -= num_supported_idle_states; /* Create dt properties with the buffer content */ dt_add_property(power_mgt, "ibm,cpu-idle-state-names", name_buf, name_buf_len* sizeof(char)); dt_add_property(power_mgt, "ibm,cpu-idle-state-latencies-ns", latency_ns_buf, num_supported_idle_states * sizeof(u32)); dt_add_property(power_mgt, "ibm,cpu-idle-state-residency-ns", residency_ns_buf, num_supported_idle_states * sizeof(u32)); dt_add_property(power_mgt, "ibm,cpu-idle-state-flags", flags_buf, num_supported_idle_states * sizeof(u32)); if (has_stop_inst) { dt_add_property(power_mgt, "ibm,cpu-idle-state-psscr", pm_ctrl_reg_val_buf, num_supported_idle_states * sizeof(u64)); dt_add_property(power_mgt, "ibm,cpu-idle-state-psscr-mask", pm_ctrl_reg_mask_buf, num_supported_idle_states * sizeof(u64)); } else { dt_add_property(power_mgt, "ibm,cpu-idle-state-pmicr", pm_ctrl_reg_val_buf, num_supported_idle_states * sizeof(u64)); dt_add_property(power_mgt, "ibm,cpu-idle-state-pmicr-mask", pm_ctrl_reg_mask_buf, num_supported_idle_states * sizeof(u64)); } assert(alloced_name_buf == name_buf); free(alloced_name_buf); free(latency_ns_buf); free(residency_ns_buf); free(flags_buf); free(pm_ctrl_reg_val_buf); free(pm_ctrl_reg_mask_buf); } static void slw_init_chip_p9(struct proc_chip *chip) { struct cpu_thread *c; prlog(PR_DEBUG, "SLW: Init chip 0x%x\n", chip->id); /* At power ON setup inits for power-mgt */ for_each_available_core_in_chip(c, chip->id) slw_set_overrides_p9(chip, c); } static void slw_init_chip_p10(struct proc_chip *chip) { struct cpu_thread *c; prlog(PR_DEBUG, "SLW: Init chip 0x%x\n", chip->id); /* At power ON setup inits for power-mgt */ for_each_available_core_in_chip(c, chip->id) slw_set_overrides_p10(chip, c); } static bool slw_image_check_p9(struct proc_chip *chip) { if (!chip->homer_base) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: HOMER base not set %x\n", chip->id); return false; } else return true; } int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val) { struct cpu_thread *c = find_cpu_by_pir(cpu_pir); struct proc_chip *chip; int rc; if (!c) { prerror("SLW: Unknown thread with pir %x\n", (u32) cpu_pir); return OPAL_PARAMETER; } chip = get_chip(c->chip_id); if (!chip) { prerror("SLW: Unknown chip for thread with pir %x\n", (u32) cpu_pir); return OPAL_PARAMETER; } if (proc_gen >= proc_gen_p9) { if (!has_deep_states) { prlog(PR_INFO, "SLW: Deep states not enabled\n"); return OPAL_SUCCESS; } if (wakeup_engine_state != WAKEUP_ENGINE_PRESENT) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: wakeup_engine in bad state=%d chip=%x\n", wakeup_engine_state,chip->id); return OPAL_INTERNAL_ERROR; } if (proc_gen == proc_gen_p9) { rc = p9_stop_save_cpureg((void *)chip->homer_base, sprn, val, cpu_pir); } else { rc = proc_stop_save_cpureg((void *)chip->homer_base, sprn, val, cpu_pir); } #ifdef CONFIG_P8 } else if (proc_gen == proc_gen_p8) { rc = opal_slw_set_reg_p8(c, chip, sprn, val); #endif } else { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: proc_gen not supported\n"); return OPAL_UNSUPPORTED; } if (rc) { log_simple_error(&e_info(OPAL_RC_SLW_REG), "SLW: Failed to set spr %llx for CPU %x, RC=0x%x\n", sprn, c->pir, rc); return OPAL_INTERNAL_ERROR; } prlog(PR_DEBUG, "SLW: restore spr:0x%llx on c:0x%x with 0x%llx\n", sprn, c->pir, val); return OPAL_SUCCESS; } opal_call(OPAL_SLW_SET_REG, opal_slw_set_reg, 3); void slw_init(void) { struct proc_chip *chip; wakeup_engine_state = WAKEUP_ENGINE_NOT_PRESENT; if (chip_quirk(QUIRK_AWAN)) return; if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) { add_cpu_idle_state_properties(); return; } #ifdef CONFIG_P8 if (proc_gen == proc_gen_p8) slw_p8_init(); #endif if (proc_gen == proc_gen_p9) { for_each_chip(chip) { slw_init_chip_p9(chip); if(slw_image_check_p9(chip)) wakeup_engine_state = WAKEUP_ENGINE_PRESENT; if (wakeup_engine_state == WAKEUP_ENGINE_PRESENT) slw_late_init_p9(chip); } } else if (proc_gen == proc_gen_p10) { for_each_chip(chip) { slw_init_chip_p10(chip); if(slw_image_check_p9(chip)) wakeup_engine_state = WAKEUP_ENGINE_PRESENT; if (wakeup_engine_state == WAKEUP_ENGINE_PRESENT) { slw_late_init_p10(chip); } } } add_cpu_idle_state_properties(); }