blob: 37bcf9826fdb7dfe579dee9f7c8927f1b6a9cfdf [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
/*
* Directly control CPU cores/threads. SRESET, special wakeup, etc
*
* Copyright 2017-2019 IBM Corp.
*/
#include <direct-controls.h>
#include <skiboot.h>
#include <opal.h>
#include <cpu.h>
#include <xscom.h>
#include <xscom-p8-regs.h>
#include <xscom-p9-regs.h>
#include <xscom-p10-regs.h>
#include <timebase.h>
#include <chip.h>
/**************** mambo direct controls ****************/
extern unsigned long callthru_tcl(const char *str, int len);
static void mambo_sreset_cpu(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
char tcl_cmd[50];
snprintf(tcl_cmd, sizeof(tcl_cmd),
"mysim cpu %i:%i:%i start_thread 0x100",
chip_id, core_id, thread_id);
callthru_tcl(tcl_cmd, strlen(tcl_cmd));
}
static void mambo_stop_cpu(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
char tcl_cmd[50];
snprintf(tcl_cmd, sizeof(tcl_cmd),
"mysim cpu %i:%i:%i stop_thread",
chip_id, core_id, thread_id);
callthru_tcl(tcl_cmd, strlen(tcl_cmd));
}
/**************** POWER8 direct controls ****************/
static int p8_core_set_special_wakeup(struct cpu_thread *cpu)
{
uint64_t val, poll_target, stamp;
uint32_t core_id;
int rc;
/*
* Note: HWP checks for checkstops, but I assume we don't need to
* as we wouldn't be running if one was present
*/
/* Grab core ID once */
core_id = pir_to_core_id(cpu->pir);
prlog(PR_DEBUG, "RESET Waking up core 0x%x\n", core_id);
/*
* The original HWp reads the XSCOM first but ignores the result
* and error, let's do the same until I know for sure that is
* not necessary
*/
xscom_read(cpu->chip_id,
XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
&val);
/* Then we write special wakeup */
rc = xscom_write(cpu->chip_id,
XSCOM_ADDR_P8_EX_SLAVE(core_id,
EX_PM_SPECIAL_WAKEUP_PHYP),
PPC_BIT(0));
if (rc) {
prerror("RESET: XSCOM error %d asserting special"
" wakeup on 0x%x\n", rc, cpu->pir);
return rc;
}
/*
* HWP uses the history for Perf register here, dunno why it uses
* that one instead of the pHyp one, maybe to avoid clobbering it...
*
* In any case, it does that to check for run/nap vs.sleep/winkle/other
* to decide whether to poll on checkstop or not. Since we don't deal
* with checkstop conditions here, we ignore that part.
*/
/*
* Now poll for completion of special wakeup. The HWP is nasty here,
* it will poll at 5ms intervals for up to 200ms. This is not quite
* acceptable for us at runtime, at least not until we have the
* ability to "context switch" HBRT. In practice, because we don't
* winkle, it will never take that long, so we increase the polling
* frequency to 1us per poll. However we do have to keep the same
* timeout.
*
* We don't use time_wait_ms() either for now as we don't want to
* poll the FSP here.
*/
stamp = mftb();
poll_target = stamp + msecs_to_tb(200);
val = 0;
while (!(val & EX_PM_GP0_SPECIAL_WAKEUP_DONE)) {
/* Wait 1 us */
time_wait_us(1);
/* Read PM state */
rc = xscom_read(cpu->chip_id,
XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_GP0),
&val);
if (rc) {
prerror("RESET: XSCOM error %d reading PM state on"
" 0x%x\n", rc, cpu->pir);
return rc;
}
/* Check timeout */
if (mftb() > poll_target)
break;
}
/* Success ? */
if (val & EX_PM_GP0_SPECIAL_WAKEUP_DONE) {
uint64_t now = mftb();
prlog(PR_TRACE, "RESET: Special wakeup complete after %ld us\n",
tb_to_usecs(now - stamp));
return 0;
}
/*
* We timed out ...
*
* HWP has a complex workaround for HW255321 which affects
* Murano DD1 and Venice DD1. Ignore that for now
*
* Instead we just dump some XSCOMs for error logging
*/
prerror("RESET: Timeout on special wakeup of 0x%0x\n", cpu->pir);
prerror("RESET: PM0 = 0x%016llx\n", val);
val = -1;
xscom_read(cpu->chip_id,
XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
&val);
prerror("RESET: SPC_WKUP = 0x%016llx\n", val);
val = -1;
xscom_read(cpu->chip_id,
XSCOM_ADDR_P8_EX_SLAVE(core_id,
EX_PM_IDLE_STATE_HISTORY_PHYP),
&val);
prerror("RESET: HISTORY = 0x%016llx\n", val);
return OPAL_HARDWARE;
}
static int p8_core_clear_special_wakeup(struct cpu_thread *cpu)
{
uint64_t val;
uint32_t core_id;
int rc;
/*
* Note: HWP checks for checkstops, but I assume we don't need to
* as we wouldn't be running if one was present
*/
/* Grab core ID once */
core_id = pir_to_core_id(cpu->pir);
prlog(PR_DEBUG, "RESET: Releasing core 0x%x wakeup\n", core_id);
/*
* The original HWp reads the XSCOM first but ignores the result
* and error, let's do the same until I know for sure that is
* not necessary
*/
xscom_read(cpu->chip_id,
XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
&val);
/* Then we write special wakeup */
rc = xscom_write(cpu->chip_id,
XSCOM_ADDR_P8_EX_SLAVE(core_id,
EX_PM_SPECIAL_WAKEUP_PHYP), 0);
if (rc) {
prerror("RESET: XSCOM error %d deasserting"
" special wakeup on 0x%x\n", rc, cpu->pir);
return rc;
}
/*
* The original HWp reads the XSCOM again with the comment
* "This puts an inherent delay in the propagation of the reset
* transition"
*/
xscom_read(cpu->chip_id,
XSCOM_ADDR_P8_EX_SLAVE(core_id, EX_PM_SPECIAL_WAKEUP_PHYP),
&val);
return 0;
}
static int p8_stop_thread(struct cpu_thread *cpu)
{
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t xscom_addr;
xscom_addr = XSCOM_ADDR_P8_EX(core_id,
P8_EX_TCTL_DIRECT_CONTROLS(thread_id));
if (xscom_write(chip_id, xscom_addr, P8_DIRECT_CTL_STOP)) {
prlog(PR_ERR, "Could not stop thread %u:%u:%u:"
" Unable to write EX_TCTL_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
return OPAL_SUCCESS;
}
static int p8_sreset_thread(struct cpu_thread *cpu)
{
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t xscom_addr;
xscom_addr = XSCOM_ADDR_P8_EX(core_id,
P8_EX_TCTL_DIRECT_CONTROLS(thread_id));
if (xscom_write(chip_id, xscom_addr, P8_DIRECT_CTL_PRENAP)) {
prlog(PR_ERR, "Could not prenap thread %u:%u:%u:"
" Unable to write EX_TCTL_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
if (xscom_write(chip_id, xscom_addr, P8_DIRECT_CTL_SRESET)) {
prlog(PR_ERR, "Could not sreset thread %u:%u:%u:"
" Unable to write EX_TCTL_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
return OPAL_SUCCESS;
}
/**************** POWER9 direct controls ****************/
/* Long running instructions may take time to complete. Timeout 100ms */
#define P9_QUIESCE_POLL_INTERVAL 100
#define P9_QUIESCE_TIMEOUT 100000
/* Waking may take up to 5ms for deepest sleep states. Set timeout to 100ms */
#define P9_SPWKUP_POLL_INTERVAL 100
#define P9_SPWKUP_TIMEOUT 100000
/*
* This implements direct control facilities of processor cores and threads
* using scom registers.
*/
static int p9_core_is_gated(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t sshhyp_addr;
uint64_t val;
sshhyp_addr = XSCOM_ADDR_P9_EC_SLAVE(core_id, P9_EC_PPM_SSHHYP);
if (xscom_read(chip_id, sshhyp_addr, &val)) {
prlog(PR_ERR, "Could not query core gated on %u:%u:"
" Unable to read PPM_SSHHYP.\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
return !!(val & P9_CORE_GATED);
}
static int p9_core_set_special_wakeup(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t swake_addr;
uint32_t sshhyp_addr;
uint64_t val;
int i;
swake_addr = XSCOM_ADDR_P9_EC_SLAVE(core_id, EC_PPM_SPECIAL_WKUP_HYP);
sshhyp_addr = XSCOM_ADDR_P9_EC_SLAVE(core_id, P9_EC_PPM_SSHHYP);
if (xscom_write(chip_id, swake_addr, P9_SPWKUP_SET)) {
prlog(PR_ERR, "Could not set special wakeup on %u:%u:"
" Unable to write PPM_SPECIAL_WKUP_HYP.\n",
chip_id, core_id);
goto out_fail;
}
for (i = 0; i < P9_SPWKUP_TIMEOUT / P9_SPWKUP_POLL_INTERVAL; i++) {
if (xscom_read(chip_id, sshhyp_addr, &val)) {
prlog(PR_ERR, "Could not set special wakeup on %u:%u:"
" Unable to read PPM_SSHHYP.\n",
chip_id, core_id);
goto out_fail;
}
if (val & P9_SPECIAL_WKUP_DONE) {
/*
* CORE_GATED will be unset on a successful special
* wakeup of the core which indicates that the core is
* out of stop state. If CORE_GATED is still set then
* raise error.
*/
if (p9_core_is_gated(cpu)) {
/* Deassert spwu for this strange error */
xscom_write(chip_id, swake_addr, 0);
prlog(PR_ERR, "Failed special wakeup on %u:%u"
" as CORE_GATED is set\n",
chip_id, core_id);
goto out_fail;
} else {
return 0;
}
}
time_wait_us(P9_SPWKUP_POLL_INTERVAL);
}
prlog(PR_ERR, "Could not set special wakeup on %u:%u:"
" timeout waiting for SPECIAL_WKUP_DONE.\n",
chip_id, core_id);
out_fail:
/*
* As per the special wakeup protocol we should not de-assert
* the special wakeup on the core until WAKEUP_DONE is set.
* So even on error do not de-assert.
*/
return OPAL_HARDWARE;
}
static int p9_core_clear_special_wakeup(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t swake_addr;
swake_addr = XSCOM_ADDR_P9_EC_SLAVE(core_id, EC_PPM_SPECIAL_WKUP_HYP);
/*
* De-assert special wakeup after a small delay.
* The delay may help avoid problems setting and clearing special
* wakeup back-to-back. This should be confirmed.
*/
time_wait_us(1);
if (xscom_write(chip_id, swake_addr, 0)) {
prlog(PR_ERR, "Could not clear special wakeup on %u:%u:"
" Unable to write PPM_SPECIAL_WKUP_HYP.\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
/*
* Don't wait for de-assert to complete as other components
* could have requested for special wkeup. Wait for 10ms to
* avoid back-to-back asserts
*/
time_wait_us(10000);
return 0;
}
static int p9_thread_quiesced(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t ras_addr;
uint64_t ras_status;
ras_addr = XSCOM_ADDR_P9_EC(core_id, P9_RAS_STATUS);
if (xscom_read(chip_id, ras_addr, &ras_status)) {
prlog(PR_ERR, "Could not check thread state on %u:%u:"
" Unable to read RAS_STATUS.\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
/*
* This returns true when the thread is quiesced and all
* instructions completed. For sreset this may not be necessary,
* but we may want to use instruction ramming or stepping
* direct controls where it is important.
*/
if ((ras_status & P9_THREAD_QUIESCED(thread_id))
== P9_THREAD_QUIESCED(thread_id))
return 1;
return 0;
}
static int p9_cont_thread(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t cts_addr;
uint32_t ti_addr;
uint32_t dctl_addr;
uint64_t core_thread_state;
uint64_t thread_info;
bool active, stop;
int rc;
rc = p9_thread_quiesced(cpu);
if (rc < 0)
return rc;
if (!rc) {
prlog(PR_ERR, "Could not cont thread %u:%u:%u:"
" Thread is not quiesced.\n",
chip_id, core_id, thread_id);
return OPAL_BUSY;
}
cts_addr = XSCOM_ADDR_P9_EC(core_id, P9_CORE_THREAD_STATE);
ti_addr = XSCOM_ADDR_P9_EC(core_id, P9_THREAD_INFO);
dctl_addr = XSCOM_ADDR_P9_EC(core_id, P9_EC_DIRECT_CONTROLS);
if (xscom_read(chip_id, cts_addr, &core_thread_state)) {
prlog(PR_ERR, "Could not resume thread %u:%u:%u:"
" Unable to read CORE_THREAD_STATE.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
if (core_thread_state & PPC_BIT(56 + thread_id))
stop = true;
else
stop = false;
if (xscom_read(chip_id, ti_addr, &thread_info)) {
prlog(PR_ERR, "Could not resume thread %u:%u:%u:"
" Unable to read THREAD_INFO.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
if (thread_info & PPC_BIT(thread_id))
active = true;
else
active = false;
if (!active || stop) {
if (xscom_write(chip_id, dctl_addr, P9_THREAD_CLEAR_MAINT(thread_id))) {
prlog(PR_ERR, "Could not resume thread %u:%u:%u:"
" Unable to write EC_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
}
} else {
if (xscom_write(chip_id, dctl_addr, P9_THREAD_CONT(thread_id))) {
prlog(PR_ERR, "Could not resume thread %u:%u:%u:"
" Unable to write EC_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
}
}
return 0;
}
static int p9_stop_thread(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t dctl_addr;
int rc;
int i;
dctl_addr = XSCOM_ADDR_P9_EC(core_id, P9_EC_DIRECT_CONTROLS);
rc = p9_thread_quiesced(cpu);
if (rc < 0)
return rc;
if (rc) {
prlog(PR_ERR, "Could not stop thread %u:%u:%u:"
" Thread is quiesced already.\n",
chip_id, core_id, thread_id);
return OPAL_BUSY;
}
if (xscom_write(chip_id, dctl_addr, P9_THREAD_STOP(thread_id))) {
prlog(PR_ERR, "Could not stop thread %u:%u:%u:"
" Unable to write EC_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
for (i = 0; i < P9_QUIESCE_TIMEOUT / P9_QUIESCE_POLL_INTERVAL; i++) {
int rc = p9_thread_quiesced(cpu);
if (rc < 0)
break;
if (rc)
return 0;
time_wait_us(P9_QUIESCE_POLL_INTERVAL);
}
prlog(PR_ERR, "Could not stop thread %u:%u:%u:"
" Unable to quiesce thread.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
static int p9_sreset_thread(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t dctl_addr;
dctl_addr = XSCOM_ADDR_P9_EC(core_id, P9_EC_DIRECT_CONTROLS);
if (xscom_write(chip_id, dctl_addr, P9_THREAD_SRESET(thread_id))) {
prlog(PR_ERR, "Could not sreset thread %u:%u:%u:"
" Unable to write EC_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
return 0;
}
/**************** POWER10 direct controls ****************/
/* Long running instructions may take time to complete. Timeout 100ms */
#define P10_QUIESCE_POLL_INTERVAL 100
#define P10_QUIESCE_TIMEOUT 100000
/* Waking may take up to 5ms for deepest sleep states. Set timeout to 100ms */
#define P10_SPWU_POLL_INTERVAL 100
#define P10_SPWU_TIMEOUT 100000
/*
* This implements direct control facilities of processor cores and threads
* using scom registers.
*/
static int p10_core_is_gated(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t ssh_addr;
uint64_t val;
ssh_addr = XSCOM_ADDR_P10_QME_CORE(core_id, P10_QME_SSH_HYP);
if (xscom_read(chip_id, ssh_addr, &val)) {
prlog(PR_ERR, "Could not query core gated on %u:%u:"
" Unable to read QME_SSH_HYP.\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
return !!(val & P10_SSH_CORE_GATED);
}
static int p10_core_set_special_wakeup(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t spwu_addr, ssh_addr;
uint64_t val;
int i;
/* P10 could use SPWU_HYP done bit instead of SSH? */
spwu_addr = XSCOM_ADDR_P10_QME_CORE(core_id, P10_QME_SPWU_HYP);
ssh_addr = XSCOM_ADDR_P10_QME_CORE(core_id, P10_QME_SSH_HYP);
if (xscom_write(chip_id, spwu_addr, P10_SPWU_REQ)) {
prlog(PR_ERR, "Could not set special wakeup on %u:%u:"
" Unable to write QME_SPWU_HYP.\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
for (i = 0; i < P10_SPWU_TIMEOUT / P10_SPWU_POLL_INTERVAL; i++) {
if (xscom_read(chip_id, ssh_addr, &val)) {
prlog(PR_ERR, "Could not set special wakeup on %u:%u:"
" Unable to read QME_SSH_HYP.\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
if (val & P10_SSH_SPWU_DONE) {
/*
* CORE_GATED will be unset on a successful special
* wakeup of the core which indicates that the core is
* out of stop state. If CORE_GATED is still set then
* check SPWU register and raise error only if SPWU_DONE
* is not set, else print a warning and consider SPWU
* operation as successful.
* This is in conjunction with a micocode bug, which
* calls out the fact that SPW can succeed in the case
* the core is gated but SPWU_HYP bit is set.
*/
if (p10_core_is_gated(cpu)) {
if(xscom_read(chip_id, spwu_addr, &val)) {
prlog(PR_ERR, "Core %u:%u:"
" unable to read QME_SPWU_HYP\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
if (val & P10_SPWU_DONE) {
/*
* If SPWU DONE bit is set then
* SPWU operation is complete
*/
prlog(PR_DEBUG, "Special wakeup on "
"%u:%u: core remains gated while"
" SPWU_HYP DONE set\n",
chip_id, core_id);
return 0;
}
/* Deassert spwu for this strange error */
xscom_write(chip_id, spwu_addr, 0);
prlog(PR_ERR,
"Failed special wakeup on %u:%u"
" core remains gated.\n",
chip_id, core_id);
return OPAL_HARDWARE;
} else {
return 0;
}
}
time_wait_us(P10_SPWU_POLL_INTERVAL);
}
prlog(PR_ERR, "Could not set special wakeup on %u:%u:"
" operation timeout.\n",
chip_id, core_id);
/*
* As per the special wakeup protocol we should not de-assert
* the special wakeup on the core until WAKEUP_DONE is set.
* So even on error do not de-assert.
*/
return OPAL_HARDWARE;
}
static int p10_core_clear_special_wakeup(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t spwu_addr;
spwu_addr = XSCOM_ADDR_P10_QME_CORE(core_id, P10_QME_SPWU_HYP);
/* Add a small delay here if spwu problems time_wait_us(1); */
if (xscom_write(chip_id, spwu_addr, 0)) {
prlog(PR_ERR, "Could not clear special wakeup on %u:%u:"
" Unable to write QME_SPWU_HYP.\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
return 0;
}
static int p10_thread_quiesced(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t ras_addr;
uint64_t ras_status;
ras_addr = XSCOM_ADDR_P10_EC(core_id, P10_EC_RAS_STATUS);
if (xscom_read(chip_id, ras_addr, &ras_status)) {
prlog(PR_ERR, "Could not check thread state on %u:%u:"
" Unable to read EC_RAS_STATUS.\n",
chip_id, core_id);
return OPAL_HARDWARE;
}
/*
* p10_thread_stop for the purpose of sreset wants QUIESCED
* and MAINT bits set. Step, RAM, etc. need more, but we don't
* use those in skiboot.
*
* P10 could try wait for more here in case of errors.
*/
if (!(ras_status & P10_THREAD_QUIESCED(thread_id)))
return 0;
if (!(ras_status & P10_THREAD_MAINT(thread_id)))
return 0;
return 1;
}
static int p10_cont_thread(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t cts_addr;
uint32_t ti_addr;
uint32_t dctl_addr;
uint64_t core_thread_state;
uint64_t thread_info;
bool active, stop;
int rc;
int i;
rc = p10_thread_quiesced(cpu);
if (rc < 0)
return rc;
if (!rc) {
prlog(PR_ERR, "Could not cont thread %u:%u:%u:"
" Thread is not quiesced.\n",
chip_id, core_id, thread_id);
return OPAL_BUSY;
}
cts_addr = XSCOM_ADDR_P10_EC(core_id, P10_EC_CORE_THREAD_STATE);
ti_addr = XSCOM_ADDR_P10_EC(core_id, P10_EC_THREAD_INFO);
dctl_addr = XSCOM_ADDR_P10_EC(core_id, P10_EC_DIRECT_CONTROLS);
if (xscom_read(chip_id, cts_addr, &core_thread_state)) {
prlog(PR_ERR, "Could not resume thread %u:%u:%u:"
" Unable to read EC_CORE_THREAD_STATE.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
if (core_thread_state & P10_THREAD_STOPPED(thread_id))
stop = true;
else
stop = false;
if (xscom_read(chip_id, ti_addr, &thread_info)) {
prlog(PR_ERR, "Could not resume thread %u:%u:%u:"
" Unable to read EC_THREAD_INFO.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
if (thread_info & P10_THREAD_ACTIVE(thread_id))
active = true;
else
active = false;
if (!active || stop) {
if (xscom_write(chip_id, dctl_addr, P10_THREAD_CLEAR_MAINT(thread_id))) {
prlog(PR_ERR, "Could not resume thread %u:%u:%u:"
" Unable to write EC_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
}
} else {
if (xscom_write(chip_id, dctl_addr, P10_THREAD_START(thread_id))) {
prlog(PR_ERR, "Could not resume thread %u:%u:%u:"
" Unable to write EC_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
}
}
for (i = 0; i < P10_QUIESCE_TIMEOUT / P10_QUIESCE_POLL_INTERVAL; i++) {
int rc = p10_thread_quiesced(cpu);
if (rc < 0)
break;
if (!rc)
return 0;
time_wait_us(P10_QUIESCE_POLL_INTERVAL);
}
prlog(PR_ERR, "Could not start thread %u:%u:%u:"
" Unable to start thread.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
static int p10_stop_thread(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t dctl_addr;
int rc;
int i;
dctl_addr = XSCOM_ADDR_P10_EC(core_id, P10_EC_DIRECT_CONTROLS);
rc = p10_thread_quiesced(cpu);
if (rc < 0)
return rc;
if (rc) {
prlog(PR_ERR, "Could not stop thread %u:%u:%u:"
" Thread is quiesced already.\n",
chip_id, core_id, thread_id);
return OPAL_BUSY;
}
if (xscom_write(chip_id, dctl_addr, P10_THREAD_STOP(thread_id))) {
prlog(PR_ERR, "Could not stop thread %u:%u:%u:"
" Unable to write EC_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
for (i = 0; i < P10_QUIESCE_TIMEOUT / P10_QUIESCE_POLL_INTERVAL; i++) {
int rc = p10_thread_quiesced(cpu);
if (rc < 0)
break;
if (rc)
return 0;
time_wait_us(P10_QUIESCE_POLL_INTERVAL);
}
prlog(PR_ERR, "Could not stop thread %u:%u:%u:"
" Unable to quiesce thread.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
static int p10_sreset_thread(struct cpu_thread *cpu)
{
uint32_t chip_id = pir_to_chip_id(cpu->pir);
uint32_t core_id = pir_to_core_id(cpu->pir);
uint32_t thread_id = pir_to_thread_id(cpu->pir);
uint32_t dctl_addr;
dctl_addr = XSCOM_ADDR_P10_EC(core_id, P10_EC_DIRECT_CONTROLS);
if (xscom_write(chip_id, dctl_addr, P10_THREAD_SRESET(thread_id))) {
prlog(PR_ERR, "Could not sreset thread %u:%u:%u:"
" Unable to write EC_DIRECT_CONTROLS.\n",
chip_id, core_id, thread_id);
return OPAL_HARDWARE;
}
return 0;
}
/**************** generic direct controls ****************/
int dctl_set_special_wakeup(struct cpu_thread *t)
{
struct cpu_thread *c = t->ec_primary;
int rc = OPAL_SUCCESS;
if (proc_gen == proc_gen_unknown)
return OPAL_UNSUPPORTED;
lock(&c->dctl_lock);
if (c->special_wakeup_count == 0) {
if (proc_gen == proc_gen_p10)
rc = p10_core_set_special_wakeup(c);
else if (proc_gen == proc_gen_p9)
rc = p9_core_set_special_wakeup(c);
else /* (proc_gen == proc_gen_p8) */
rc = p8_core_set_special_wakeup(c);
}
if (!rc)
c->special_wakeup_count++;
unlock(&c->dctl_lock);
return rc;
}
int dctl_clear_special_wakeup(struct cpu_thread *t)
{
struct cpu_thread *c = t->ec_primary;
int rc = OPAL_SUCCESS;
if (proc_gen == proc_gen_unknown)
return OPAL_UNSUPPORTED;
lock(&c->dctl_lock);
if (!c->special_wakeup_count)
goto out;
if (c->special_wakeup_count == 1) {
if (proc_gen == proc_gen_p10)
rc = p10_core_clear_special_wakeup(c);
else if (proc_gen == proc_gen_p9)
rc = p9_core_clear_special_wakeup(c);
else /* (proc_gen == proc_gen_p8) */
rc = p8_core_clear_special_wakeup(c);
}
if (!rc)
c->special_wakeup_count--;
out:
unlock(&c->dctl_lock);
return rc;
}
int dctl_core_is_gated(struct cpu_thread *t)
{
struct cpu_thread *c = t->primary;
if (proc_gen == proc_gen_p10)
return p10_core_is_gated(c);
else if (proc_gen == proc_gen_p9)
return p9_core_is_gated(c);
else
return OPAL_UNSUPPORTED;
}
static int dctl_stop(struct cpu_thread *t)
{
struct cpu_thread *c = t->ec_primary;
int rc;
lock(&c->dctl_lock);
if (t->dctl_stopped) {
unlock(&c->dctl_lock);
return OPAL_BUSY;
}
if (proc_gen == proc_gen_p10)
rc = p10_stop_thread(t);
else if (proc_gen == proc_gen_p9)
rc = p9_stop_thread(t);
else /* (proc_gen == proc_gen_p8) */
rc = p8_stop_thread(t);
if (!rc)
t->dctl_stopped = true;
unlock(&c->dctl_lock);
return rc;
}
static int dctl_cont(struct cpu_thread *t)
{
struct cpu_thread *c = t->primary;
int rc;
if (proc_gen != proc_gen_p10 && proc_gen != proc_gen_p9)
return OPAL_UNSUPPORTED;
lock(&c->dctl_lock);
if (!t->dctl_stopped) {
unlock(&c->dctl_lock);
return OPAL_BUSY;
}
if (proc_gen == proc_gen_p10)
rc = p10_cont_thread(t);
else /* (proc_gen == proc_gen_p9) */
rc = p9_cont_thread(t);
if (!rc)
t->dctl_stopped = false;
unlock(&c->dctl_lock);
return rc;
}
/*
* NOTE:
* The POWER8 sreset does not provide SRR registers, so it can be used
* for fast reboot, but not OPAL_SIGNAL_SYSTEM_RESET or anywhere that is
* expected to return. For now, callers beware.
*/
static int dctl_sreset(struct cpu_thread *t)
{
struct cpu_thread *c = t->ec_primary;
int rc;
lock(&c->dctl_lock);
if (!t->dctl_stopped) {
unlock(&c->dctl_lock);
return OPAL_BUSY;
}
if (proc_gen == proc_gen_p10)
rc = p10_sreset_thread(t);
else if (proc_gen == proc_gen_p9)
rc = p9_sreset_thread(t);
else /* (proc_gen == proc_gen_p8) */
rc = p8_sreset_thread(t);
if (!rc)
t->dctl_stopped = false;
unlock(&c->dctl_lock);
return rc;
}
/**************** fast reboot API ****************/
int sreset_all_prepare(void)
{
struct cpu_thread *cpu;
if (proc_gen == proc_gen_unknown)
return OPAL_UNSUPPORTED;
prlog(PR_DEBUG, "RESET: Resetting from cpu: 0x%x (core 0x%x)\n",
this_cpu()->pir, pir_to_core_id(this_cpu()->pir));
if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) {
for_each_ungarded_cpu(cpu) {
if (cpu == this_cpu())
continue;
mambo_stop_cpu(cpu);
}
return OPAL_SUCCESS;
}
/* Assert special wakup on all cores. Only on operational cores. */
for_each_ungarded_primary(cpu) {
if (dctl_set_special_wakeup(cpu) != OPAL_SUCCESS)
return OPAL_HARDWARE;
}
prlog(PR_DEBUG, "RESET: Stopping the world...\n");
/* Put everybody in stop except myself */
for_each_ungarded_cpu(cpu) {
if (cpu == this_cpu())
continue;
if (dctl_stop(cpu) != OPAL_SUCCESS)
return OPAL_HARDWARE;
}
return OPAL_SUCCESS;
}
void sreset_all_finish(void)
{
struct cpu_thread *cpu;
if (chip_quirk(QUIRK_MAMBO_CALLOUTS))
return;
for_each_ungarded_primary(cpu)
dctl_clear_special_wakeup(cpu);
}
int sreset_all_others(void)
{
struct cpu_thread *cpu;
prlog(PR_DEBUG, "RESET: Resetting all threads but self...\n");
/*
* mambo should actually implement stop as well, and implement
* the dctl_ helpers properly. Currently it's racy just sresetting.
*/
if (chip_quirk(QUIRK_MAMBO_CALLOUTS)) {
for_each_ungarded_cpu(cpu) {
if (cpu == this_cpu())
continue;
mambo_sreset_cpu(cpu);
}
return OPAL_SUCCESS;
}
for_each_ungarded_cpu(cpu) {
if (cpu == this_cpu())
continue;
if (dctl_sreset(cpu) != OPAL_SUCCESS)
return OPAL_HARDWARE;
}
return OPAL_SUCCESS;
}
/**************** OPAL_SIGNAL_SYSTEM_RESET API ****************/
/*
* This provides a way for the host to raise system reset exceptions
* on other threads using direct control scoms on POWER9.
*
* We assert special wakeup on the core first.
* Then stop target thread and wait for it to quiesce.
* Then sreset the target thread, which resumes execution on that thread.
* Then de-assert special wakeup on the core.
*/
static int64_t do_sreset_cpu(struct cpu_thread *cpu)
{
int rc;
if (this_cpu() == cpu) {
prlog(PR_ERR, "SRESET: Unable to reset self\n");
return OPAL_PARAMETER;
}
rc = dctl_set_special_wakeup(cpu);
if (rc)
return rc;
rc = dctl_stop(cpu);
if (rc)
goto out_spwk;
rc = dctl_sreset(cpu);
if (rc)
goto out_cont;
dctl_clear_special_wakeup(cpu);
return 0;
out_cont:
dctl_cont(cpu);
out_spwk:
dctl_clear_special_wakeup(cpu);
return rc;
}
static struct lock sreset_lock = LOCK_UNLOCKED;
int64_t opal_signal_system_reset(int cpu_nr)
{
struct cpu_thread *cpu;
int64_t ret;
if (proc_gen != proc_gen_p9 && proc_gen != proc_gen_p10)
return OPAL_UNSUPPORTED;
/*
* Broadcasts unsupported. Not clear what threads should be
* signaled, so it's better for the OS to perform one-at-a-time
* for now.
*/
if (cpu_nr < 0)
return OPAL_CONSTRAINED;
/* Reset a single CPU */
cpu = find_cpu_by_server(cpu_nr);
if (!cpu) {
prlog(PR_ERR, "SRESET: could not find cpu by server %d\n", cpu_nr);
return OPAL_PARAMETER;
}
lock(&sreset_lock);
ret = do_sreset_cpu(cpu);
unlock(&sreset_lock);
return ret;
}
void direct_controls_init(void)
{
if (chip_quirk(QUIRK_MAMBO_CALLOUTS))
return;
if (proc_gen != proc_gen_p9 && proc_gen != proc_gen_p10)
return;
opal_register(OPAL_SIGNAL_SYSTEM_RESET, opal_signal_system_reset, 1);
}