The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/powerpc/powernv/opal_dev.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2015 Nathan Whitehorn
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include <sys/param.h>
   31 #include <sys/systm.h>
   32 #include <sys/module.h>
   33 #include <sys/bus.h>
   34 #include <sys/conf.h>
   35 #include <sys/clock.h>
   36 #include <sys/cpu.h>
   37 #include <sys/eventhandler.h>
   38 #include <sys/kernel.h>
   39 #include <sys/kthread.h>
   40 #include <sys/reboot.h>
   41 #include <sys/sysctl.h>
   42 #include <sys/endian.h>
   43 
   44 #include <vm/vm.h>
   45 #include <vm/pmap.h>
   46 
   47 #include <dev/ofw/ofw_bus.h>
   48 #include <dev/ofw/ofw_bus_subr.h>
   49 #include <dev/ofw/openfirm.h>
   50 
   51 #include "clock_if.h"
   52 #include "opal.h"
   53 
   54 static int      opaldev_probe(device_t);
   55 static int      opaldev_attach(device_t);
   56 /* clock interface */
   57 static int      opal_gettime(device_t dev, struct timespec *ts);
   58 static int      opal_settime(device_t dev, struct timespec *ts);
   59 /* ofw bus interface */
   60 static const struct ofw_bus_devinfo *opaldev_get_devinfo(device_t dev,
   61     device_t child);
   62 
   63 static void     opal_shutdown(void *arg, int howto);
   64 static void     opal_handle_shutdown_message(void *unused,
   65     struct opal_msg *msg);
   66 static void     opal_intr(void *);
   67 
   68 static device_method_t  opaldev_methods[] = {
   69         /* Device interface */
   70         DEVMETHOD(device_probe,         opaldev_probe),
   71         DEVMETHOD(device_attach,        opaldev_attach),
   72 
   73         /* clock interface */
   74         DEVMETHOD(clock_gettime,        opal_gettime),
   75         DEVMETHOD(clock_settime,        opal_settime),
   76 
   77         /* Bus interface */
   78         DEVMETHOD(bus_child_pnpinfo,    ofw_bus_gen_child_pnpinfo),
   79 
   80         /* ofw_bus interface */
   81         DEVMETHOD(ofw_bus_get_devinfo,  opaldev_get_devinfo),
   82         DEVMETHOD(ofw_bus_get_compat,   ofw_bus_gen_get_compat),
   83         DEVMETHOD(ofw_bus_get_model,    ofw_bus_gen_get_model),
   84         DEVMETHOD(ofw_bus_get_name,     ofw_bus_gen_get_name),
   85         DEVMETHOD(ofw_bus_get_node,     ofw_bus_gen_get_node),
   86         DEVMETHOD(ofw_bus_get_type,     ofw_bus_gen_get_type),
   87 
   88         DEVMETHOD_END
   89 };
   90 
   91 static driver_t opaldev_driver = {
   92         "opal",
   93         opaldev_methods,
   94         0
   95 };
   96 
   97 EARLY_DRIVER_MODULE(opaldev, ofwbus, opaldev_driver, 0, 0, BUS_PASS_BUS);
   98 
   99 static void opal_heartbeat(void);
  100 static void opal_handle_messages(void);
  101 
  102 static struct proc *opal_hb_proc;
  103 static struct kproc_desc opal_heartbeat_kp = {
  104         "opal_heartbeat",
  105         opal_heartbeat,
  106         &opal_hb_proc
  107 };
  108 
  109 SYSINIT(opal_heartbeat_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start,
  110     &opal_heartbeat_kp);
  111 
  112 static int opal_heartbeat_ms;
  113 EVENTHANDLER_LIST_DEFINE(OPAL_ASYNC_COMP);
  114 EVENTHANDLER_LIST_DEFINE(OPAL_EPOW);
  115 EVENTHANDLER_LIST_DEFINE(OPAL_SHUTDOWN);
  116 EVENTHANDLER_LIST_DEFINE(OPAL_HMI_EVT);
  117 EVENTHANDLER_LIST_DEFINE(OPAL_DPO);
  118 EVENTHANDLER_LIST_DEFINE(OPAL_OCC);
  119 
  120 #define OPAL_SOFT_OFF           0
  121 #define OPAL_SOFT_REBOOT        1
  122 
  123 static void
  124 opal_heartbeat(void)
  125 {
  126         uint64_t events;
  127 
  128         if (opal_heartbeat_ms == 0)
  129                 kproc_exit(0);
  130 
  131         while (1) {
  132                 events = 0;
  133                 /* Turn the OPAL state crank */
  134                 opal_call(OPAL_POLL_EVENTS, vtophys(&events));
  135                 if (be64toh(events) & OPAL_EVENT_MSG_PENDING)
  136                         opal_handle_messages();
  137                 tsleep(opal_hb_proc, 0, "opal",
  138                     MSEC_2_TICKS(opal_heartbeat_ms));
  139         }
  140 }
  141 
  142 static int
  143 opaldev_probe(device_t dev)
  144 {
  145         phandle_t iparent;
  146         pcell_t *irqs;
  147         int i, n_irqs;
  148 
  149         if (!ofw_bus_is_compatible(dev, "ibm,opal-v3"))
  150                 return (ENXIO);
  151         if (opal_check() != 0)
  152                 return (ENXIO);
  153 
  154         device_set_desc(dev, "OPAL Abstraction Firmware");
  155 
  156         /* Manually add IRQs before attaching */
  157         if (OF_hasprop(ofw_bus_get_node(dev), "opal-interrupts")) {
  158                 iparent = OF_finddevice("/interrupt-controller@0");
  159                 iparent = OF_xref_from_node(iparent);
  160 
  161                 n_irqs = OF_getproplen(ofw_bus_get_node(dev),
  162                     "opal-interrupts") / sizeof(*irqs);
  163                 irqs = malloc(n_irqs * sizeof(*irqs), M_DEVBUF, M_WAITOK);
  164                 OF_getencprop(ofw_bus_get_node(dev), "opal-interrupts", irqs,
  165                     n_irqs * sizeof(*irqs));
  166                 for (i = 0; i < n_irqs; i++)
  167                         bus_set_resource(dev, SYS_RES_IRQ, i,
  168                             ofw_bus_map_intr(dev, iparent, 1, &irqs[i]), 1);
  169                 free(irqs, M_DEVBUF);
  170         }
  171 
  172         return (BUS_PROBE_SPECIFIC);
  173 }
  174 
  175 static int
  176 opaldev_attach(device_t dev)
  177 {
  178         phandle_t child;
  179         device_t cdev;
  180         uint64_t junk;
  181         int i, rv;
  182         uint32_t async_count;
  183         struct ofw_bus_devinfo *dinfo;
  184         struct resource *irq;
  185 
  186         /* Test for RTC support and register clock if it works */
  187         rv = opal_call(OPAL_RTC_READ, vtophys(&junk), vtophys(&junk));
  188         do {
  189                 rv = opal_call(OPAL_RTC_READ, vtophys(&junk), vtophys(&junk));
  190                 if (rv == OPAL_BUSY_EVENT)
  191                         rv = opal_call(OPAL_POLL_EVENTS, 0);
  192         } while (rv == OPAL_BUSY_EVENT);
  193 
  194         if (rv == OPAL_SUCCESS)
  195                 clock_register(dev, 2000);
  196 
  197         EVENTHANDLER_REGISTER(OPAL_SHUTDOWN, opal_handle_shutdown_message,
  198             NULL, EVENTHANDLER_PRI_ANY);
  199         EVENTHANDLER_REGISTER(shutdown_final, opal_shutdown, NULL,
  200             SHUTDOWN_PRI_LAST);
  201 
  202         OF_getencprop(ofw_bus_get_node(dev), "ibm,heartbeat-ms",
  203             &opal_heartbeat_ms, sizeof(opal_heartbeat_ms));
  204         /* Bind to interrupts */
  205         for (i = 0; (irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i,
  206             RF_ACTIVE)) != NULL; i++)
  207                 bus_setup_intr(dev, irq, INTR_TYPE_TTY | INTR_MPSAFE |
  208                     INTR_ENTROPY, NULL, opal_intr, (void *)rman_get_start(irq),
  209                     NULL);
  210 
  211         OF_getencprop(ofw_bus_get_node(dev), "opal-msg-async-num",
  212             &async_count, sizeof(async_count));
  213         opal_init_async_tokens(async_count);
  214 
  215         for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
  216             child = OF_peer(child)) {
  217                 dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
  218                 if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {
  219                         free(dinfo, M_DEVBUF);
  220                         continue;
  221                 }
  222                 cdev = device_add_child(dev, NULL, -1);
  223                 if (cdev == NULL) {
  224                         device_printf(dev, "<%s>: device_add_child failed\n",
  225                             dinfo->obd_name);
  226                         ofw_bus_gen_destroy_devinfo(dinfo);
  227                         free(dinfo, M_DEVBUF);
  228                         continue;
  229                 }
  230                 device_set_ivars(cdev, dinfo);
  231         }
  232 
  233         return (bus_generic_attach(dev));
  234 }
  235 
  236 static int
  237 bcd2bin32(int bcd)
  238 {
  239         int out = 0;
  240 
  241         out += bcd2bin(bcd & 0xff);
  242         out += 100*bcd2bin((bcd & 0x0000ff00) >> 8);
  243         out += 10000*bcd2bin((bcd & 0x00ff0000) >> 16);
  244         out += 1000000*bcd2bin((bcd & 0xffff0000) >> 24);
  245 
  246         return (out);
  247 }
  248 
  249 static int
  250 bin2bcd32(int bin)
  251 {
  252         int out = 0;
  253         int tmp;
  254 
  255         tmp = bin % 100;
  256         out += bin2bcd(tmp) * 0x1;
  257         bin = bin / 100;
  258 
  259         tmp = bin % 100;
  260         out += bin2bcd(tmp) * 0x100;
  261         bin = bin / 100;
  262 
  263         tmp = bin % 100;
  264         out += bin2bcd(tmp) * 0x10000;
  265 
  266         return (out);
  267 }
  268 
  269 static int
  270 opal_gettime(device_t dev, struct timespec *ts)
  271 {
  272         int rv;
  273         struct clocktime ct;
  274         uint32_t ymd;
  275         uint64_t hmsm;
  276 
  277         rv = opal_call(OPAL_RTC_READ, vtophys(&ymd), vtophys(&hmsm));
  278         while (rv == OPAL_BUSY_EVENT)  {
  279                 opal_call(OPAL_POLL_EVENTS, 0);
  280                 pause("opalrtc", 1);
  281                 rv = opal_call(OPAL_RTC_READ, vtophys(&ymd), vtophys(&hmsm));
  282         }
  283 
  284         if (rv != OPAL_SUCCESS)
  285                 return (ENXIO);
  286 
  287         hmsm = be64toh(hmsm);
  288         ymd = be32toh(ymd);
  289 
  290         ct.nsec = bcd2bin32((hmsm & 0x000000ffffff0000) >> 16) * 1000;
  291         ct.sec  = bcd2bin((hmsm & 0x0000ff0000000000) >> 40);
  292         ct.min  = bcd2bin((hmsm & 0x00ff000000000000) >> 48);
  293         ct.hour = bcd2bin((hmsm & 0xff00000000000000) >> 56);
  294 
  295         ct.day  = bcd2bin((ymd & 0x000000ff) >> 0);
  296         ct.mon  = bcd2bin((ymd & 0x0000ff00) >> 8);
  297         ct.year = bcd2bin32((ymd & 0xffff0000) >> 16);
  298 
  299         return (clock_ct_to_ts(&ct, ts));
  300 }
  301 
  302 static int
  303 opal_settime(device_t dev, struct timespec *ts)
  304 {
  305         int rv;
  306         struct clocktime ct;
  307         uint32_t ymd = 0;
  308         uint64_t hmsm = 0;
  309 
  310         clock_ts_to_ct(ts, &ct);
  311 
  312         ymd |= (uint32_t)bin2bcd(ct.day);
  313         ymd |= ((uint32_t)bin2bcd(ct.mon) << 8);
  314         ymd |= ((uint32_t)bin2bcd32(ct.year) << 16);
  315 
  316         hmsm |= ((uint64_t)bin2bcd32(ct.nsec/1000) << 16);
  317         hmsm |= ((uint64_t)bin2bcd(ct.sec) << 40);
  318         hmsm |= ((uint64_t)bin2bcd(ct.min) << 48);
  319         hmsm |= ((uint64_t)bin2bcd(ct.hour) << 56);
  320 
  321         /*
  322          * We do NOT swap endian here, because the values are being sent
  323          * via registers instead of indirect via memory.
  324          */
  325         do {
  326                 rv = opal_call(OPAL_RTC_WRITE, ymd, hmsm);
  327                 if (rv == OPAL_BUSY_EVENT) {
  328                         rv = opal_call(OPAL_POLL_EVENTS, 0);
  329                         pause("opalrtc", 1);
  330                 }
  331         } while (rv == OPAL_BUSY_EVENT);
  332 
  333         if (rv != OPAL_SUCCESS)
  334                 return (ENXIO);
  335 
  336         return (0);
  337 }
  338 
  339 static const struct ofw_bus_devinfo *
  340 opaldev_get_devinfo(device_t dev, device_t child)
  341 {
  342         return (device_get_ivars(child));
  343 }
  344 
  345 static void
  346 opal_shutdown(void *arg, int howto)
  347 {
  348 
  349         if (howto & RB_HALT)
  350                 opal_call(OPAL_CEC_POWER_DOWN, 0 /* Normal power off */);
  351         else
  352                 opal_call(OPAL_CEC_REBOOT);
  353 
  354         opal_call(OPAL_RETURN_CPU);
  355 }
  356 
  357 static void
  358 opal_handle_shutdown_message(void *unused, struct opal_msg *msg)
  359 {
  360         int howto;
  361 
  362         switch (be64toh(msg->params[0])) {
  363         case OPAL_SOFT_OFF:
  364                 howto = RB_POWEROFF;
  365                 break;
  366         case OPAL_SOFT_REBOOT:
  367                 howto = RB_REROOT;
  368                 break;
  369         }
  370         shutdown_nice(howto);
  371 }
  372 
  373 static void
  374 opal_handle_messages(void)
  375 {
  376         static struct opal_msg msg;
  377         uint64_t rv;
  378         uint32_t type;
  379 
  380         rv = opal_call(OPAL_GET_MSG, vtophys(&msg), sizeof(msg));
  381 
  382         switch (rv) {
  383         case OPAL_SUCCESS:
  384                 break;
  385         case OPAL_RESOURCE:
  386                 /* no available messages - return */
  387                 return;
  388         case OPAL_PARAMETER:
  389                 printf("%s error: invalid buffer. Please file a bug report.\n", __func__);
  390                 return;
  391         case OPAL_PARTIAL:
  392                 printf("%s error: buffer is too small and messages was discarded. Please file a bug report.\n", __func__);
  393                 return;
  394         default:
  395                 printf("%s opal_call returned unknown result <%lu>\n", __func__, rv);
  396                 return;
  397         }
  398 
  399         type = be32toh(msg.msg_type);
  400         switch (type) {
  401         case OPAL_MSG_ASYNC_COMP:
  402                 EVENTHANDLER_DIRECT_INVOKE(OPAL_ASYNC_COMP, &msg);
  403                 break;
  404         case OPAL_MSG_EPOW:
  405                 EVENTHANDLER_DIRECT_INVOKE(OPAL_EPOW, &msg);
  406                 break;
  407         case OPAL_MSG_SHUTDOWN:
  408                 EVENTHANDLER_DIRECT_INVOKE(OPAL_SHUTDOWN, &msg);
  409                 break;
  410         case OPAL_MSG_HMI_EVT:
  411                 EVENTHANDLER_DIRECT_INVOKE(OPAL_HMI_EVT, &msg);
  412                 break;
  413         case OPAL_MSG_DPO:
  414                 EVENTHANDLER_DIRECT_INVOKE(OPAL_DPO, &msg);
  415                 break;
  416         case OPAL_MSG_OCC:
  417                 EVENTHANDLER_DIRECT_INVOKE(OPAL_OCC, &msg);
  418                 break;
  419         default:
  420                 printf("%s Unknown OPAL message type %d\n", __func__, type);
  421         }
  422 }
  423 
  424 static void
  425 opal_intr(void *xintr)
  426 {
  427         uint64_t events = 0;
  428 
  429         opal_call(OPAL_HANDLE_INTERRUPT, (uint32_t)(uint64_t)xintr,
  430             vtophys(&events));
  431         /* Wake up the heartbeat, if it's been setup. */
  432         if (be64toh(events) != 0 && opal_hb_proc != NULL)
  433                 wakeup(opal_hb_proc);
  434 
  435 }

Cache object: 486b547aa629719f0990842773e1a4a3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.