132d1354aSWojciech Macek /*- 232d1354aSWojciech Macek * Copyright (c) 2015 Nathan Whitehorn 332d1354aSWojciech Macek * All rights reserved. 432d1354aSWojciech Macek * 532d1354aSWojciech Macek * Redistribution and use in source and binary forms, with or without 632d1354aSWojciech Macek * modification, are permitted provided that the following conditions 732d1354aSWojciech Macek * are met: 832d1354aSWojciech Macek * 1. Redistributions of source code must retain the above copyright 932d1354aSWojciech Macek * notice, this list of conditions and the following disclaimer. 1032d1354aSWojciech Macek * 2. Redistributions in binary form must reproduce the above copyright 1132d1354aSWojciech Macek * notice, this list of conditions and the following disclaimer in the 1232d1354aSWojciech Macek * documentation and/or other materials provided with the distribution. 1332d1354aSWojciech Macek * 1432d1354aSWojciech Macek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1532d1354aSWojciech Macek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1632d1354aSWojciech Macek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1732d1354aSWojciech Macek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1832d1354aSWojciech Macek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1932d1354aSWojciech Macek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2032d1354aSWojciech Macek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2132d1354aSWojciech Macek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2232d1354aSWojciech Macek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2332d1354aSWojciech Macek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2432d1354aSWojciech Macek * SUCH DAMAGE. 2532d1354aSWojciech Macek */ 2632d1354aSWojciech Macek 2732d1354aSWojciech Macek #include <sys/cdefs.h> 2832d1354aSWojciech Macek __FBSDID("$FreeBSD$"); 2932d1354aSWojciech Macek 3032d1354aSWojciech Macek #include <sys/param.h> 3132d1354aSWojciech Macek #include <sys/systm.h> 3232d1354aSWojciech Macek #include <sys/module.h> 3332d1354aSWojciech Macek #include <sys/bus.h> 3432d1354aSWojciech Macek #include <sys/conf.h> 3532d1354aSWojciech Macek #include <sys/clock.h> 3632d1354aSWojciech Macek #include <sys/cpu.h> 37e2e050c8SConrad Meyer #include <sys/eventhandler.h> 3832d1354aSWojciech Macek #include <sys/kernel.h> 39911a9260SJustin Hibbits #include <sys/kthread.h> 4032d1354aSWojciech Macek #include <sys/reboot.h> 4132d1354aSWojciech Macek #include <sys/sysctl.h> 42504d9b60SWojciech Macek #include <sys/endian.h> 43504d9b60SWojciech Macek 44504d9b60SWojciech Macek #include <vm/vm.h> 45504d9b60SWojciech Macek #include <vm/pmap.h> 4632d1354aSWojciech Macek 4732d1354aSWojciech Macek #include <dev/ofw/ofw_bus.h> 4832d1354aSWojciech Macek #include <dev/ofw/ofw_bus_subr.h> 4932d1354aSWojciech Macek #include <dev/ofw/openfirm.h> 5032d1354aSWojciech Macek 5132d1354aSWojciech Macek #include "clock_if.h" 5232d1354aSWojciech Macek #include "opal.h" 5332d1354aSWojciech Macek 5432d1354aSWojciech Macek static int opaldev_probe(device_t); 5532d1354aSWojciech Macek static int opaldev_attach(device_t); 5632d1354aSWojciech Macek /* clock interface */ 5732d1354aSWojciech Macek static int opal_gettime(device_t dev, struct timespec *ts); 5832d1354aSWojciech Macek static int opal_settime(device_t dev, struct timespec *ts); 5932d1354aSWojciech Macek /* ofw bus interface */ 6032d1354aSWojciech Macek static const struct ofw_bus_devinfo *opaldev_get_devinfo(device_t dev, 6132d1354aSWojciech Macek device_t child); 6232d1354aSWojciech Macek 6332d1354aSWojciech Macek static void opal_shutdown(void *arg, int howto); 64911a9260SJustin Hibbits static void opal_handle_shutdown_message(void *unused, 65911a9260SJustin Hibbits struct opal_msg *msg); 66504d9b60SWojciech Macek static void opal_intr(void *); 6732d1354aSWojciech Macek 6832d1354aSWojciech Macek static device_method_t opaldev_methods[] = { 6932d1354aSWojciech Macek /* Device interface */ 7032d1354aSWojciech Macek DEVMETHOD(device_probe, opaldev_probe), 7132d1354aSWojciech Macek DEVMETHOD(device_attach, opaldev_attach), 7232d1354aSWojciech Macek 7332d1354aSWojciech Macek /* clock interface */ 7432d1354aSWojciech Macek DEVMETHOD(clock_gettime, opal_gettime), 7532d1354aSWojciech Macek DEVMETHOD(clock_settime, opal_settime), 7632d1354aSWojciech Macek 7727ef2ca8SJustin Hibbits /* Bus interface */ 78*ddfc9c4cSWarner Losh DEVMETHOD(bus_child_pnpinfo, ofw_bus_gen_child_pnpinfo), 7927ef2ca8SJustin Hibbits 8032d1354aSWojciech Macek /* ofw_bus interface */ 8132d1354aSWojciech Macek DEVMETHOD(ofw_bus_get_devinfo, opaldev_get_devinfo), 8232d1354aSWojciech Macek DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 8332d1354aSWojciech Macek DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 8432d1354aSWojciech Macek DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 8532d1354aSWojciech Macek DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 8632d1354aSWojciech Macek DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 8732d1354aSWojciech Macek 8832d1354aSWojciech Macek DEVMETHOD_END 8932d1354aSWojciech Macek }; 9032d1354aSWojciech Macek 9132d1354aSWojciech Macek static driver_t opaldev_driver = { 9232d1354aSWojciech Macek "opal", 9332d1354aSWojciech Macek opaldev_methods, 9432d1354aSWojciech Macek 0 9532d1354aSWojciech Macek }; 9632d1354aSWojciech Macek 9732d1354aSWojciech Macek static devclass_t opaldev_devclass; 9832d1354aSWojciech Macek 99e2e3e7d2SJustin Hibbits EARLY_DRIVER_MODULE(opaldev, ofwbus, opaldev_driver, opaldev_devclass, 0, 0, 100e2e3e7d2SJustin Hibbits BUS_PASS_BUS); 10132d1354aSWojciech Macek 102911a9260SJustin Hibbits static void opal_heartbeat(void); 103911a9260SJustin Hibbits static void opal_handle_messages(void); 104911a9260SJustin Hibbits 105911a9260SJustin Hibbits static struct proc *opal_hb_proc; 106911a9260SJustin Hibbits static struct kproc_desc opal_heartbeat_kp = { 107911a9260SJustin Hibbits "opal_heartbeat", 108911a9260SJustin Hibbits opal_heartbeat, 109911a9260SJustin Hibbits &opal_hb_proc 110911a9260SJustin Hibbits }; 111911a9260SJustin Hibbits 112911a9260SJustin Hibbits SYSINIT(opal_heartbeat_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start, 113911a9260SJustin Hibbits &opal_heartbeat_kp); 114911a9260SJustin Hibbits 115911a9260SJustin Hibbits static int opal_heartbeat_ms; 116911a9260SJustin Hibbits EVENTHANDLER_LIST_DEFINE(OPAL_ASYNC_COMP); 117911a9260SJustin Hibbits EVENTHANDLER_LIST_DEFINE(OPAL_EPOW); 118911a9260SJustin Hibbits EVENTHANDLER_LIST_DEFINE(OPAL_SHUTDOWN); 119911a9260SJustin Hibbits EVENTHANDLER_LIST_DEFINE(OPAL_HMI_EVT); 120911a9260SJustin Hibbits EVENTHANDLER_LIST_DEFINE(OPAL_DPO); 121911a9260SJustin Hibbits EVENTHANDLER_LIST_DEFINE(OPAL_OCC); 122911a9260SJustin Hibbits 123911a9260SJustin Hibbits #define OPAL_SOFT_OFF 0 124911a9260SJustin Hibbits #define OPAL_SOFT_REBOOT 1 125911a9260SJustin Hibbits 126911a9260SJustin Hibbits static void 127911a9260SJustin Hibbits opal_heartbeat(void) 128911a9260SJustin Hibbits { 129911a9260SJustin Hibbits uint64_t events; 130911a9260SJustin Hibbits 131911a9260SJustin Hibbits if (opal_heartbeat_ms == 0) 132911a9260SJustin Hibbits kproc_exit(0); 133911a9260SJustin Hibbits 134911a9260SJustin Hibbits while (1) { 135911a9260SJustin Hibbits events = 0; 136911a9260SJustin Hibbits /* Turn the OPAL state crank */ 137911a9260SJustin Hibbits opal_call(OPAL_POLL_EVENTS, vtophys(&events)); 138bf933a83SBrandon Bergren if (be64toh(events) & OPAL_EVENT_MSG_PENDING) 139911a9260SJustin Hibbits opal_handle_messages(); 140911a9260SJustin Hibbits tsleep(opal_hb_proc, 0, "opal", 141911a9260SJustin Hibbits MSEC_2_TICKS(opal_heartbeat_ms)); 142911a9260SJustin Hibbits } 143911a9260SJustin Hibbits } 144911a9260SJustin Hibbits 14532d1354aSWojciech Macek static int 14632d1354aSWojciech Macek opaldev_probe(device_t dev) 14732d1354aSWojciech Macek { 148504d9b60SWojciech Macek phandle_t iparent; 149504d9b60SWojciech Macek pcell_t *irqs; 150504d9b60SWojciech Macek int i, n_irqs; 15132d1354aSWojciech Macek 15232d1354aSWojciech Macek if (!ofw_bus_is_compatible(dev, "ibm,opal-v3")) 15332d1354aSWojciech Macek return (ENXIO); 15432d1354aSWojciech Macek if (opal_check() != 0) 15532d1354aSWojciech Macek return (ENXIO); 15632d1354aSWojciech Macek 15732d1354aSWojciech Macek device_set_desc(dev, "OPAL Abstraction Firmware"); 158504d9b60SWojciech Macek 159504d9b60SWojciech Macek /* Manually add IRQs before attaching */ 160504d9b60SWojciech Macek if (OF_hasprop(ofw_bus_get_node(dev), "opal-interrupts")) { 161504d9b60SWojciech Macek iparent = OF_finddevice("/interrupt-controller@0"); 162504d9b60SWojciech Macek iparent = OF_xref_from_node(iparent); 163504d9b60SWojciech Macek 164504d9b60SWojciech Macek n_irqs = OF_getproplen(ofw_bus_get_node(dev), 165504d9b60SWojciech Macek "opal-interrupts") / sizeof(*irqs); 166504d9b60SWojciech Macek irqs = malloc(n_irqs * sizeof(*irqs), M_DEVBUF, M_WAITOK); 167504d9b60SWojciech Macek OF_getencprop(ofw_bus_get_node(dev), "opal-interrupts", irqs, 168504d9b60SWojciech Macek n_irqs * sizeof(*irqs)); 169504d9b60SWojciech Macek for (i = 0; i < n_irqs; i++) 170504d9b60SWojciech Macek bus_set_resource(dev, SYS_RES_IRQ, i, 171504d9b60SWojciech Macek ofw_bus_map_intr(dev, iparent, 1, &irqs[i]), 1); 172504d9b60SWojciech Macek free(irqs, M_DEVBUF); 173504d9b60SWojciech Macek } 174504d9b60SWojciech Macek 17532d1354aSWojciech Macek return (BUS_PROBE_SPECIFIC); 17632d1354aSWojciech Macek } 17732d1354aSWojciech Macek 17832d1354aSWojciech Macek static int 17932d1354aSWojciech Macek opaldev_attach(device_t dev) 18032d1354aSWojciech Macek { 18132d1354aSWojciech Macek phandle_t child; 18232d1354aSWojciech Macek device_t cdev; 183504d9b60SWojciech Macek uint64_t junk; 184504d9b60SWojciech Macek int i, rv; 185dac618a6SJustin Hibbits uint32_t async_count; 18632d1354aSWojciech Macek struct ofw_bus_devinfo *dinfo; 187504d9b60SWojciech Macek struct resource *irq; 18832d1354aSWojciech Macek 189504d9b60SWojciech Macek /* Test for RTC support and register clock if it works */ 190504d9b60SWojciech Macek rv = opal_call(OPAL_RTC_READ, vtophys(&junk), vtophys(&junk)); 191504d9b60SWojciech Macek do { 192504d9b60SWojciech Macek rv = opal_call(OPAL_RTC_READ, vtophys(&junk), vtophys(&junk)); 193504d9b60SWojciech Macek if (rv == OPAL_BUSY_EVENT) 194504d9b60SWojciech Macek rv = opal_call(OPAL_POLL_EVENTS, 0); 195504d9b60SWojciech Macek } while (rv == OPAL_BUSY_EVENT); 196504d9b60SWojciech Macek 197504d9b60SWojciech Macek if (rv == OPAL_SUCCESS) 19832d1354aSWojciech Macek clock_register(dev, 2000); 19932d1354aSWojciech Macek 200911a9260SJustin Hibbits EVENTHANDLER_REGISTER(OPAL_SHUTDOWN, opal_handle_shutdown_message, 201911a9260SJustin Hibbits NULL, EVENTHANDLER_PRI_ANY); 20232d1354aSWojciech Macek EVENTHANDLER_REGISTER(shutdown_final, opal_shutdown, NULL, 20332d1354aSWojciech Macek SHUTDOWN_PRI_LAST); 20432d1354aSWojciech Macek 205911a9260SJustin Hibbits OF_getencprop(ofw_bus_get_node(dev), "ibm,heartbeat-ms", 206911a9260SJustin Hibbits &opal_heartbeat_ms, sizeof(opal_heartbeat_ms)); 207504d9b60SWojciech Macek /* Bind to interrupts */ 208504d9b60SWojciech Macek for (i = 0; (irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, 209504d9b60SWojciech Macek RF_ACTIVE)) != NULL; i++) 210504d9b60SWojciech Macek bus_setup_intr(dev, irq, INTR_TYPE_TTY | INTR_MPSAFE | 211504d9b60SWojciech Macek INTR_ENTROPY, NULL, opal_intr, (void *)rman_get_start(irq), 212504d9b60SWojciech Macek NULL); 213504d9b60SWojciech Macek 214dac618a6SJustin Hibbits OF_getencprop(ofw_bus_get_node(dev), "opal-msg-async-num", 215dac618a6SJustin Hibbits &async_count, sizeof(async_count)); 216dac618a6SJustin Hibbits opal_init_async_tokens(async_count); 217dac618a6SJustin Hibbits 21832d1354aSWojciech Macek for (child = OF_child(ofw_bus_get_node(dev)); child != 0; 21932d1354aSWojciech Macek child = OF_peer(child)) { 22032d1354aSWojciech Macek dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); 22132d1354aSWojciech Macek if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) { 22232d1354aSWojciech Macek free(dinfo, M_DEVBUF); 22332d1354aSWojciech Macek continue; 22432d1354aSWojciech Macek } 22532d1354aSWojciech Macek cdev = device_add_child(dev, NULL, -1); 22632d1354aSWojciech Macek if (cdev == NULL) { 22732d1354aSWojciech Macek device_printf(dev, "<%s>: device_add_child failed\n", 22832d1354aSWojciech Macek dinfo->obd_name); 22932d1354aSWojciech Macek ofw_bus_gen_destroy_devinfo(dinfo); 23032d1354aSWojciech Macek free(dinfo, M_DEVBUF); 23132d1354aSWojciech Macek continue; 23232d1354aSWojciech Macek } 23332d1354aSWojciech Macek device_set_ivars(cdev, dinfo); 23432d1354aSWojciech Macek } 23532d1354aSWojciech Macek 23632d1354aSWojciech Macek return (bus_generic_attach(dev)); 23732d1354aSWojciech Macek } 23832d1354aSWojciech Macek 23932d1354aSWojciech Macek static int 240504d9b60SWojciech Macek bcd2bin32(int bcd) 241504d9b60SWojciech Macek { 242504d9b60SWojciech Macek int out = 0; 243504d9b60SWojciech Macek 244504d9b60SWojciech Macek out += bcd2bin(bcd & 0xff); 245504d9b60SWojciech Macek out += 100*bcd2bin((bcd & 0x0000ff00) >> 8); 246504d9b60SWojciech Macek out += 10000*bcd2bin((bcd & 0x00ff0000) >> 16); 247504d9b60SWojciech Macek out += 1000000*bcd2bin((bcd & 0xffff0000) >> 24); 248504d9b60SWojciech Macek 249504d9b60SWojciech Macek return (out); 250504d9b60SWojciech Macek } 251504d9b60SWojciech Macek 252504d9b60SWojciech Macek static int 253eb96cc13SWojciech Macek bin2bcd32(int bin) 254eb96cc13SWojciech Macek { 255eb96cc13SWojciech Macek int out = 0; 256eb96cc13SWojciech Macek int tmp; 257eb96cc13SWojciech Macek 258eb96cc13SWojciech Macek tmp = bin % 100; 2595c74d551SBrandon Bergren out += bin2bcd(tmp) * 0x1; 260eb96cc13SWojciech Macek bin = bin / 100; 261eb96cc13SWojciech Macek 262eb96cc13SWojciech Macek tmp = bin % 100; 2635c74d551SBrandon Bergren out += bin2bcd(tmp) * 0x100; 264eb96cc13SWojciech Macek bin = bin / 100; 265eb96cc13SWojciech Macek 266eb96cc13SWojciech Macek tmp = bin % 100; 2675c74d551SBrandon Bergren out += bin2bcd(tmp) * 0x10000; 268eb96cc13SWojciech Macek 269eb96cc13SWojciech Macek return (out); 270eb96cc13SWojciech Macek } 271eb96cc13SWojciech Macek 272eb96cc13SWojciech Macek static int 273504d9b60SWojciech Macek opal_gettime(device_t dev, struct timespec *ts) 274504d9b60SWojciech Macek { 275504d9b60SWojciech Macek int rv; 276504d9b60SWojciech Macek struct clocktime ct; 277504d9b60SWojciech Macek uint32_t ymd; 278504d9b60SWojciech Macek uint64_t hmsm; 279504d9b60SWojciech Macek 280504d9b60SWojciech Macek rv = opal_call(OPAL_RTC_READ, vtophys(&ymd), vtophys(&hmsm)); 28178f4e2feSBreno Leitao while (rv == OPAL_BUSY_EVENT) { 28278f4e2feSBreno Leitao opal_call(OPAL_POLL_EVENTS, 0); 283504d9b60SWojciech Macek pause("opalrtc", 1); 28478f4e2feSBreno Leitao rv = opal_call(OPAL_RTC_READ, vtophys(&ymd), vtophys(&hmsm)); 285504d9b60SWojciech Macek } 286504d9b60SWojciech Macek 287504d9b60SWojciech Macek if (rv != OPAL_SUCCESS) 28832d1354aSWojciech Macek return (ENXIO); 289504d9b60SWojciech Macek 290504d9b60SWojciech Macek hmsm = be64toh(hmsm); 291504d9b60SWojciech Macek ymd = be32toh(ymd); 292504d9b60SWojciech Macek 293504d9b60SWojciech Macek ct.nsec = bcd2bin32((hmsm & 0x000000ffffff0000) >> 16) * 1000; 294504d9b60SWojciech Macek ct.sec = bcd2bin((hmsm & 0x0000ff0000000000) >> 40); 295504d9b60SWojciech Macek ct.min = bcd2bin((hmsm & 0x00ff000000000000) >> 48); 296504d9b60SWojciech Macek ct.hour = bcd2bin((hmsm & 0xff00000000000000) >> 56); 297504d9b60SWojciech Macek 298504d9b60SWojciech Macek ct.day = bcd2bin((ymd & 0x000000ff) >> 0); 299504d9b60SWojciech Macek ct.mon = bcd2bin((ymd & 0x0000ff00) >> 8); 300504d9b60SWojciech Macek ct.year = bcd2bin32((ymd & 0xffff0000) >> 16); 301504d9b60SWojciech Macek 302504d9b60SWojciech Macek return (clock_ct_to_ts(&ct, ts)); 30332d1354aSWojciech Macek } 30432d1354aSWojciech Macek 30532d1354aSWojciech Macek static int 30632d1354aSWojciech Macek opal_settime(device_t dev, struct timespec *ts) 30732d1354aSWojciech Macek { 308eb96cc13SWojciech Macek int rv; 309eb96cc13SWojciech Macek struct clocktime ct; 310eb96cc13SWojciech Macek uint32_t ymd = 0; 311eb96cc13SWojciech Macek uint64_t hmsm = 0; 312eb96cc13SWojciech Macek 313eb96cc13SWojciech Macek clock_ts_to_ct(ts, &ct); 314eb96cc13SWojciech Macek 315eb96cc13SWojciech Macek ymd |= (uint32_t)bin2bcd(ct.day); 316eb96cc13SWojciech Macek ymd |= ((uint32_t)bin2bcd(ct.mon) << 8); 317eb96cc13SWojciech Macek ymd |= ((uint32_t)bin2bcd32(ct.year) << 16); 318eb96cc13SWojciech Macek 319eb96cc13SWojciech Macek hmsm |= ((uint64_t)bin2bcd32(ct.nsec/1000) << 16); 320eb96cc13SWojciech Macek hmsm |= ((uint64_t)bin2bcd(ct.sec) << 40); 321eb96cc13SWojciech Macek hmsm |= ((uint64_t)bin2bcd(ct.min) << 48); 322eb96cc13SWojciech Macek hmsm |= ((uint64_t)bin2bcd(ct.hour) << 56); 323eb96cc13SWojciech Macek 3245c74d551SBrandon Bergren /* 3255c74d551SBrandon Bergren * We do NOT swap endian here, because the values are being sent 3265c74d551SBrandon Bergren * via registers instead of indirect via memory. 3275c74d551SBrandon Bergren */ 328eb96cc13SWojciech Macek do { 3295c74d551SBrandon Bergren rv = opal_call(OPAL_RTC_WRITE, ymd, hmsm); 330eb96cc13SWojciech Macek if (rv == OPAL_BUSY_EVENT) { 331eb96cc13SWojciech Macek rv = opal_call(OPAL_POLL_EVENTS, 0); 332eb96cc13SWojciech Macek pause("opalrtc", 1); 333eb96cc13SWojciech Macek } 334eb96cc13SWojciech Macek } while (rv == OPAL_BUSY_EVENT); 335eb96cc13SWojciech Macek 336eb96cc13SWojciech Macek if (rv != OPAL_SUCCESS) 337eb96cc13SWojciech Macek return (ENXIO); 338504d9b60SWojciech Macek 33932d1354aSWojciech Macek return (0); 34032d1354aSWojciech Macek } 34132d1354aSWojciech Macek 34232d1354aSWojciech Macek static const struct ofw_bus_devinfo * 34332d1354aSWojciech Macek opaldev_get_devinfo(device_t dev, device_t child) 34432d1354aSWojciech Macek { 34532d1354aSWojciech Macek return (device_get_ivars(child)); 34632d1354aSWojciech Macek } 34732d1354aSWojciech Macek 34832d1354aSWojciech Macek static void 34932d1354aSWojciech Macek opal_shutdown(void *arg, int howto) 35032d1354aSWojciech Macek { 35132d1354aSWojciech Macek 35232d1354aSWojciech Macek if (howto & RB_HALT) 35332d1354aSWojciech Macek opal_call(OPAL_CEC_POWER_DOWN, 0 /* Normal power off */); 35432d1354aSWojciech Macek else 35532d1354aSWojciech Macek opal_call(OPAL_CEC_REBOOT); 356504d9b60SWojciech Macek 357504d9b60SWojciech Macek opal_call(OPAL_RETURN_CPU); 358504d9b60SWojciech Macek } 359504d9b60SWojciech Macek 360504d9b60SWojciech Macek static void 361911a9260SJustin Hibbits opal_handle_shutdown_message(void *unused, struct opal_msg *msg) 362911a9260SJustin Hibbits { 363911a9260SJustin Hibbits int howto; 364911a9260SJustin Hibbits 365911a9260SJustin Hibbits switch (be64toh(msg->params[0])) { 366911a9260SJustin Hibbits case OPAL_SOFT_OFF: 367911a9260SJustin Hibbits howto = RB_POWEROFF; 368911a9260SJustin Hibbits break; 369911a9260SJustin Hibbits case OPAL_SOFT_REBOOT: 370911a9260SJustin Hibbits howto = RB_REROOT; 371911a9260SJustin Hibbits break; 372911a9260SJustin Hibbits } 373911a9260SJustin Hibbits shutdown_nice(howto); 374911a9260SJustin Hibbits } 375911a9260SJustin Hibbits 376911a9260SJustin Hibbits static void 377911a9260SJustin Hibbits opal_handle_messages(void) 378911a9260SJustin Hibbits { 379911a9260SJustin Hibbits static struct opal_msg msg; 380911a9260SJustin Hibbits uint64_t rv; 381911a9260SJustin Hibbits uint32_t type; 382911a9260SJustin Hibbits 383911a9260SJustin Hibbits rv = opal_call(OPAL_GET_MSG, vtophys(&msg), sizeof(msg)); 384911a9260SJustin Hibbits 3856d2254bcSAlfredo Dal'Ava Junior switch (rv) { 3866d2254bcSAlfredo Dal'Ava Junior case OPAL_SUCCESS: 3876d2254bcSAlfredo Dal'Ava Junior break; 3886d2254bcSAlfredo Dal'Ava Junior case OPAL_RESOURCE: 3896d2254bcSAlfredo Dal'Ava Junior /* no available messages - return */ 390911a9260SJustin Hibbits return; 3916d2254bcSAlfredo Dal'Ava Junior case OPAL_PARAMETER: 3926d2254bcSAlfredo Dal'Ava Junior printf("%s error: invalid buffer. Please file a bug report.\n", __func__); 3936d2254bcSAlfredo Dal'Ava Junior return; 3946d2254bcSAlfredo Dal'Ava Junior case OPAL_PARTIAL: 3956d2254bcSAlfredo Dal'Ava Junior printf("%s error: buffer is too small and messages was discarded. Please file a bug report.\n", __func__); 3966d2254bcSAlfredo Dal'Ava Junior return; 3976d2254bcSAlfredo Dal'Ava Junior default: 3986d2254bcSAlfredo Dal'Ava Junior printf("%s opal_call returned unknown result <%lu>\n", __func__, rv); 3996d2254bcSAlfredo Dal'Ava Junior return; 4006d2254bcSAlfredo Dal'Ava Junior } 401911a9260SJustin Hibbits 402911a9260SJustin Hibbits type = be32toh(msg.msg_type); 403911a9260SJustin Hibbits switch (type) { 404911a9260SJustin Hibbits case OPAL_MSG_ASYNC_COMP: 405911a9260SJustin Hibbits EVENTHANDLER_DIRECT_INVOKE(OPAL_ASYNC_COMP, &msg); 406911a9260SJustin Hibbits break; 407911a9260SJustin Hibbits case OPAL_MSG_EPOW: 408911a9260SJustin Hibbits EVENTHANDLER_DIRECT_INVOKE(OPAL_EPOW, &msg); 409911a9260SJustin Hibbits break; 410911a9260SJustin Hibbits case OPAL_MSG_SHUTDOWN: 411911a9260SJustin Hibbits EVENTHANDLER_DIRECT_INVOKE(OPAL_SHUTDOWN, &msg); 412911a9260SJustin Hibbits break; 413911a9260SJustin Hibbits case OPAL_MSG_HMI_EVT: 414911a9260SJustin Hibbits EVENTHANDLER_DIRECT_INVOKE(OPAL_HMI_EVT, &msg); 415911a9260SJustin Hibbits break; 416911a9260SJustin Hibbits case OPAL_MSG_DPO: 417911a9260SJustin Hibbits EVENTHANDLER_DIRECT_INVOKE(OPAL_DPO, &msg); 418911a9260SJustin Hibbits break; 419911a9260SJustin Hibbits case OPAL_MSG_OCC: 420911a9260SJustin Hibbits EVENTHANDLER_DIRECT_INVOKE(OPAL_OCC, &msg); 421911a9260SJustin Hibbits break; 422911a9260SJustin Hibbits default: 4236d2254bcSAlfredo Dal'Ava Junior printf("%s Unknown OPAL message type %d\n", __func__, type); 424911a9260SJustin Hibbits } 425911a9260SJustin Hibbits } 426911a9260SJustin Hibbits 427911a9260SJustin Hibbits static void 428504d9b60SWojciech Macek opal_intr(void *xintr) 429504d9b60SWojciech Macek { 430504d9b60SWojciech Macek uint64_t events = 0; 431504d9b60SWojciech Macek 432504d9b60SWojciech Macek opal_call(OPAL_HANDLE_INTERRUPT, (uint32_t)(uint64_t)xintr, 433504d9b60SWojciech Macek vtophys(&events)); 434911a9260SJustin Hibbits /* Wake up the heartbeat, if it's been setup. */ 4356d2254bcSAlfredo Dal'Ava Junior if (be64toh(events) != 0 && opal_hb_proc != NULL) 436911a9260SJustin Hibbits wakeup(opal_hb_proc); 437504d9b60SWojciech Macek 43832d1354aSWojciech Macek } 439