powerpc/powernv: Add OPAL heartbeat thread

Summary:
OPAL needs to be kicked periodically in order for the firmware to make
progress on its tasks.  To do so, create a heartbeat thread to perform this task
every N milliseconds, defined by the device tree.  This task is also a central
location to handle all messages received from OPAL.

Reviewed By: luporl
Differential Revision: https://reviews.freebsd.org/D19743
This commit is contained in:
Justin Hibbits 2019-04-02 04:00:01 +00:00
parent 8ac5aef8f3
commit 911a92603e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=345789
2 changed files with 134 additions and 1 deletions

View File

@ -31,6 +31,7 @@
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/eventhandler.h>
/* Check if OPAL is correctly instantiated. Will try to instantiate it. */
int opal_check(void);
@ -72,6 +73,7 @@ int opal_call(uint64_t token, ...);
#define OPAL_RETURN_CPU 69
#define OPAL_REINIT_CPUS 70
#define OPAL_CHECK_TOKEN 80
#define OPAL_GET_MSG 85
#define OPAL_CHECK_ASYNC_COMPLETION 86
#define OPAL_SENSOR_READ 88
#define OPAL_HANDLE_HMI 98
@ -140,6 +142,19 @@ int opal_call(uint64_t token, ...);
#define OPAL_TOKEN_ABSENT 0
#define OPAL_TOKEN_PRESENT 1
#define OPAL_EVENT_OPAL_INTERNAL 0x1
#define OPAL_EVENT_NVRAM 0x2
#define OPAL_EVENT_RTC 0x4
#define OPAL_EVENT_CONSOLE_INPUT 0x8
#define OPAL_EVENT_CONSOLE_OUTPUT 0x10
#define OPAL_EVENT_ERROR_LOG_AVAIL 0x20
#define OPAL_EVENT_ERROR_LOG 0x40
#define OPAL_EVENT_EPOW 0x80
#define OPAL_EVENT_LED_STATUS 0x100
#define OPAL_EVENT_PCI_ERROR 0x200
#define OPAL_EVENT_DUMP_AVAIL 0x400
#define OPAL_EVENT_MSG_PENDING 0x800
#define OPAL_HMI_FLAGS_TB_RESYNC (1ull << 0)
#define OPAL_HMI_FLAGS_DEC_LOST (1ull << 1)
#define OPAL_HMI_FLAGS_HDEC_LOST (1ull << 2)
@ -188,4 +203,17 @@ int opal_alloc_async_token(void);
void opal_free_async_token(int);
int opal_wait_completion(void *, uint64_t, uint64_t);
typedef void (*opal_msg_handler_fn)(void *, struct opal_msg *);
EVENTHANDLER_DECLARE(OPAL_ASYNC_COMP, opal_msg_handler_fn);
EVENTHANDLER_DECLARE(OPAL_EPOW, opal_msg_handler_fn);
EVENTHANDLER_DECLARE(OPAL_SHUTDOWN, opal_msg_handler_fn);
EVENTHANDLER_DECLARE(OPAL_HMI_EVT, opal_msg_handler_fn);
EVENTHANDLER_DECLARE(OPAL_DPO, opal_msg_handler_fn);
EVENTHANDLER_DECLARE(OPAL_OCC, opal_msg_handler_fn);
EVENTHANDLER_LIST_DECLARE(OPAL_ASYNC_COMP);
EVENTHANDLER_LIST_DECLARE(OPAL_EPOW);
EVENTHANDLER_LIST_DECLARE(OPAL_SHUTDOWN);
EVENTHANDLER_LIST_DECLARE(OPAL_HMI_EVT);
EVENTHANDLER_LIST_DECLARE(OPAL_DPO);
EVENTHANDLER_LIST_DECLARE(OPAL_OCC);
#endif

View File

@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/clock.h>
#include <sys/cpu.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/reboot.h>
#include <sys/sysctl.h>
#include <sys/endian.h>
@ -59,6 +60,8 @@ static const struct ofw_bus_devinfo *opaldev_get_devinfo(device_t dev,
device_t child);
static void opal_shutdown(void *arg, int howto);
static void opal_handle_shutdown_message(void *unused,
struct opal_msg *msg);
static void opal_intr(void *);
static device_method_t opaldev_methods[] = {
@ -94,6 +97,49 @@ static devclass_t opaldev_devclass;
DRIVER_MODULE(opaldev, ofwbus, opaldev_driver, opaldev_devclass, 0, 0);
static void opal_heartbeat(void);
static void opal_handle_messages(void);
static struct proc *opal_hb_proc;
static struct kproc_desc opal_heartbeat_kp = {
"opal_heartbeat",
opal_heartbeat,
&opal_hb_proc
};
SYSINIT(opal_heartbeat_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start,
&opal_heartbeat_kp);
static int opal_heartbeat_ms;
EVENTHANDLER_LIST_DEFINE(OPAL_ASYNC_COMP);
EVENTHANDLER_LIST_DEFINE(OPAL_EPOW);
EVENTHANDLER_LIST_DEFINE(OPAL_SHUTDOWN);
EVENTHANDLER_LIST_DEFINE(OPAL_HMI_EVT);
EVENTHANDLER_LIST_DEFINE(OPAL_DPO);
EVENTHANDLER_LIST_DEFINE(OPAL_OCC);
#define OPAL_SOFT_OFF 0
#define OPAL_SOFT_REBOOT 1
static void
opal_heartbeat(void)
{
uint64_t events;
if (opal_heartbeat_ms == 0)
kproc_exit(0);
while (1) {
events = 0;
/* Turn the OPAL state crank */
opal_call(OPAL_POLL_EVENTS, vtophys(&events));
if (events & OPAL_EVENT_MSG_PENDING)
opal_handle_messages();
tsleep(opal_hb_proc, 0, "opal",
MSEC_2_TICKS(opal_heartbeat_ms));
}
}
static int
opaldev_probe(device_t dev)
{
@ -150,9 +196,13 @@ opaldev_attach(device_t dev)
if (rv == OPAL_SUCCESS)
clock_register(dev, 2000);
EVENTHANDLER_REGISTER(OPAL_SHUTDOWN, opal_handle_shutdown_message,
NULL, EVENTHANDLER_PRI_ANY);
EVENTHANDLER_REGISTER(shutdown_final, opal_shutdown, NULL,
SHUTDOWN_PRI_LAST);
OF_getencprop(ofw_bus_get_node(dev), "ibm,heartbeat-ms",
&opal_heartbeat_ms, sizeof(opal_heartbeat_ms));
/* Bind to interrupts */
for (i = 0; (irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i,
RF_ACTIVE)) != NULL; i++)
@ -305,6 +355,59 @@ opal_shutdown(void *arg, int howto)
opal_call(OPAL_RETURN_CPU);
}
static void
opal_handle_shutdown_message(void *unused, struct opal_msg *msg)
{
int howto;
switch (be64toh(msg->params[0])) {
case OPAL_SOFT_OFF:
howto = RB_POWEROFF;
break;
case OPAL_SOFT_REBOOT:
howto = RB_REROOT;
break;
}
shutdown_nice(howto);
}
static void
opal_handle_messages(void)
{
static struct opal_msg msg;
uint64_t rv;
uint32_t type;
rv = opal_call(OPAL_GET_MSG, vtophys(&msg), sizeof(msg));
if (rv != OPAL_SUCCESS)
return;
type = be32toh(msg.msg_type);
switch (type) {
case OPAL_MSG_ASYNC_COMP:
EVENTHANDLER_DIRECT_INVOKE(OPAL_ASYNC_COMP, &msg);
break;
case OPAL_MSG_EPOW:
EVENTHANDLER_DIRECT_INVOKE(OPAL_EPOW, &msg);
break;
case OPAL_MSG_SHUTDOWN:
EVENTHANDLER_DIRECT_INVOKE(OPAL_SHUTDOWN, &msg);
break;
case OPAL_MSG_HMI_EVT:
EVENTHANDLER_DIRECT_INVOKE(OPAL_HMI_EVT, &msg);
break;
case OPAL_MSG_DPO:
EVENTHANDLER_DIRECT_INVOKE(OPAL_DPO, &msg);
break;
case OPAL_MSG_OCC:
EVENTHANDLER_DIRECT_INVOKE(OPAL_OCC, &msg);
break;
default:
printf("Unknown OPAL message type %d\n", type);
}
}
static void
opal_intr(void *xintr)
{
@ -312,7 +415,9 @@ opal_intr(void *xintr)
opal_call(OPAL_HANDLE_INTERRUPT, (uint32_t)(uint64_t)xintr,
vtophys(&events));
/* XXX: do something useful with this information */
/* Wake up the heartbeat, if it's been setup. */
if (events != 0 && opal_hb_proc != NULL)
wakeup(opal_hb_proc);
}