19c6ba29dSJustin Hibbits /*- 29c6ba29dSJustin Hibbits * Copyright (C) 2018 Justin Hibbits 39c6ba29dSJustin Hibbits * 49c6ba29dSJustin Hibbits * Redistribution and use in source and binary forms, with or without 59c6ba29dSJustin Hibbits * modification, are permitted provided that the following conditions 69c6ba29dSJustin Hibbits * are met: 79c6ba29dSJustin Hibbits * 1. Redistributions of source code must retain the above copyright 89c6ba29dSJustin Hibbits * notice, this list of conditions and the following disclaimer. 99c6ba29dSJustin Hibbits * 2. Redistributions in binary form must reproduce the above copyright 109c6ba29dSJustin Hibbits * notice, this list of conditions and the following disclaimer in the 119c6ba29dSJustin Hibbits * documentation and/or other materials provided with the distribution. 129c6ba29dSJustin Hibbits * 139c6ba29dSJustin Hibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 149c6ba29dSJustin Hibbits * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 159c6ba29dSJustin Hibbits * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 169c6ba29dSJustin Hibbits * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 179c6ba29dSJustin Hibbits * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 189c6ba29dSJustin Hibbits * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 199c6ba29dSJustin Hibbits * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 209c6ba29dSJustin Hibbits * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 219c6ba29dSJustin Hibbits * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 229c6ba29dSJustin Hibbits * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 239c6ba29dSJustin Hibbits */ 249c6ba29dSJustin Hibbits 259c6ba29dSJustin Hibbits #include <sys/param.h> 269c6ba29dSJustin Hibbits #include <sys/kernel.h> 27e2e050c8SConrad Meyer #include <sys/lock.h> 289c6ba29dSJustin Hibbits #include <sys/module.h> 29e2e050c8SConrad Meyer #include <sys/mutex.h> 309c6ba29dSJustin Hibbits #include <sys/proc.h> 319c6ba29dSJustin Hibbits #include <sys/sysctl.h> 32e2e050c8SConrad Meyer #include <sys/systm.h> 336d2254bcSAlfredo Dal'Ava Junior #include <sys/endian.h> 349c6ba29dSJustin Hibbits 359c6ba29dSJustin Hibbits #include <vm/vm.h> 369c6ba29dSJustin Hibbits #include <vm/pmap.h> 379c6ba29dSJustin Hibbits 389c6ba29dSJustin Hibbits #include <machine/bus.h> 399c6ba29dSJustin Hibbits 409c6ba29dSJustin Hibbits #include <dev/ofw/openfirm.h> 419c6ba29dSJustin Hibbits #include <dev/ofw/ofw_bus.h> 429c6ba29dSJustin Hibbits #include <dev/ofw/ofw_bus_subr.h> 439c6ba29dSJustin Hibbits 449c6ba29dSJustin Hibbits #include "opal.h" 459c6ba29dSJustin Hibbits 469c6ba29dSJustin Hibbits struct opal_sensor_softc { 479c6ba29dSJustin Hibbits device_t sc_dev; 489c6ba29dSJustin Hibbits struct mtx sc_mtx; 499c6ba29dSJustin Hibbits uint32_t sc_handle; 509c6ba29dSJustin Hibbits uint32_t sc_min_handle; 519c6ba29dSJustin Hibbits uint32_t sc_max_handle; 529c6ba29dSJustin Hibbits char *sc_label; 539c6ba29dSJustin Hibbits int sc_type; 549c6ba29dSJustin Hibbits }; 559c6ba29dSJustin Hibbits 569c6ba29dSJustin Hibbits #define SENSOR_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 579c6ba29dSJustin Hibbits #define SENSOR_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 589c6ba29dSJustin Hibbits #define SENSOR_LOCK_INIT(_sc) \ 599c6ba29dSJustin Hibbits mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ 609c6ba29dSJustin Hibbits "opal-sensor", MTX_DEF) 619c6ba29dSJustin Hibbits 629c6ba29dSJustin Hibbits /* 639c6ba29dSJustin Hibbits * A bit confusing, maybe. There are two types of nodes with compatible strings 649c6ba29dSJustin Hibbits * of "ibm,opal-sensor". One hangs off /ibm,opal/, named "sensors", the other 659c6ba29dSJustin Hibbits * hangs off of this node. For newbus attachments, we have one node (opalsens) 669c6ba29dSJustin Hibbits * attach from opal0, and the others (opal_sensor) attach from opalsens. These 679c6ba29dSJustin Hibbits * are the real sensors. 689c6ba29dSJustin Hibbits */ 699c6ba29dSJustin Hibbits enum opal_sensor_type { 709c6ba29dSJustin Hibbits OPAL_SENSOR_TEMP = 0, /* From OPAL: degC */ 719c6ba29dSJustin Hibbits OPAL_SENSOR_FAN = 1, /* From OPAL: RPM */ 729c6ba29dSJustin Hibbits OPAL_SENSOR_POWER = 2, /* From OPAL: W */ 739c6ba29dSJustin Hibbits OPAL_SENSOR_IN = 3, /* From OPAL: mV */ 749c6ba29dSJustin Hibbits OPAL_SENSOR_ENERGY = 4, /* From OPAL: uJ */ 759c6ba29dSJustin Hibbits OPAL_SENSOR_CURR = 5, /* From OPAL: mA */ 769c6ba29dSJustin Hibbits OPAL_SENSOR_MAX 779c6ba29dSJustin Hibbits }; 789c6ba29dSJustin Hibbits 799c6ba29dSJustin Hibbits /* This must be kept sorted with the enum above. */ 809c6ba29dSJustin Hibbits const char *opal_sensor_types[] = { 819c6ba29dSJustin Hibbits "temp", 829c6ba29dSJustin Hibbits "fan", 839c6ba29dSJustin Hibbits "power", 849c6ba29dSJustin Hibbits "in", 859c6ba29dSJustin Hibbits "energy", 869c6ba29dSJustin Hibbits "curr" 879c6ba29dSJustin Hibbits }; 889c6ba29dSJustin Hibbits 899c6ba29dSJustin Hibbits /* 909c6ba29dSJustin Hibbits * Retrieve the raw value from OPAL. This will be cooked by the sysctl handler. 919c6ba29dSJustin Hibbits */ 929c6ba29dSJustin Hibbits static int 93f433dab2SJustin Hibbits opal_sensor_get_val(struct opal_sensor_softc *sc, uint32_t key, uint64_t *val) 949c6ba29dSJustin Hibbits { 959c6ba29dSJustin Hibbits struct opal_msg msg; 969c6ba29dSJustin Hibbits uint32_t val32; 97dac618a6SJustin Hibbits int rv, token; 989c6ba29dSJustin Hibbits 99dac618a6SJustin Hibbits token = opal_alloc_async_token(); 100f433dab2SJustin Hibbits SENSOR_LOCK(sc); 101dac618a6SJustin Hibbits rv = opal_call(OPAL_SENSOR_READ, key, token, vtophys(&val32)); 1029c6ba29dSJustin Hibbits 1039c6ba29dSJustin Hibbits if (rv == OPAL_ASYNC_COMPLETION) { 1049c6ba29dSJustin Hibbits /* Sleep a little to let things settle. */ 1059c6ba29dSJustin Hibbits DELAY(100); 1069c6ba29dSJustin Hibbits bzero(&msg, sizeof(msg)); 107dac618a6SJustin Hibbits rv = opal_wait_completion(&msg, sizeof(msg), token); 108dac618a6SJustin Hibbits 109dac618a6SJustin Hibbits if (rv == OPAL_SUCCESS) 1109c6ba29dSJustin Hibbits val32 = msg.params[0]; 1119c6ba29dSJustin Hibbits } 112f433dab2SJustin Hibbits SENSOR_UNLOCK(sc); 1139c6ba29dSJustin Hibbits 114dac618a6SJustin Hibbits if (rv == OPAL_SUCCESS) 1156d2254bcSAlfredo Dal'Ava Junior *val = be32toh(val32); 116dac618a6SJustin Hibbits else 117dac618a6SJustin Hibbits rv = EIO; 1189c6ba29dSJustin Hibbits 119dac618a6SJustin Hibbits opal_free_async_token(token); 120dac618a6SJustin Hibbits return (rv); 1219c6ba29dSJustin Hibbits } 1229c6ba29dSJustin Hibbits 1239c6ba29dSJustin Hibbits static int 1249c6ba29dSJustin Hibbits opal_sensor_sysctl(SYSCTL_HANDLER_ARGS) 1259c6ba29dSJustin Hibbits { 1269c6ba29dSJustin Hibbits struct opal_sensor_softc *sc; 1279c6ba29dSJustin Hibbits int error, result; 1289c6ba29dSJustin Hibbits uint32_t sensor; 1299c6ba29dSJustin Hibbits uint64_t sensval; 1309c6ba29dSJustin Hibbits 1319c6ba29dSJustin Hibbits sc = arg1; 1329c6ba29dSJustin Hibbits sensor = arg2; 1339c6ba29dSJustin Hibbits 134f433dab2SJustin Hibbits error = opal_sensor_get_val(sc, sensor, &sensval); 1359c6ba29dSJustin Hibbits 1369c6ba29dSJustin Hibbits if (error) 1379c6ba29dSJustin Hibbits return (error); 1389c6ba29dSJustin Hibbits 1399c6ba29dSJustin Hibbits result = sensval; 1409c6ba29dSJustin Hibbits 1419c6ba29dSJustin Hibbits switch (sc->sc_type) { 1429c6ba29dSJustin Hibbits case OPAL_SENSOR_TEMP: 1439c6ba29dSJustin Hibbits result = result * 10 + 2731; /* Convert to K */ 1449c6ba29dSJustin Hibbits break; 1459c6ba29dSJustin Hibbits case OPAL_SENSOR_POWER: 1469c6ba29dSJustin Hibbits result = result * 1000; /* Convert to mW */ 1479c6ba29dSJustin Hibbits break; 1489c6ba29dSJustin Hibbits } 1499c6ba29dSJustin Hibbits 1509c6ba29dSJustin Hibbits error = sysctl_handle_int(oidp, &result, 0, req); 1519c6ba29dSJustin Hibbits 1529c6ba29dSJustin Hibbits return (error); 1539c6ba29dSJustin Hibbits } 1549c6ba29dSJustin Hibbits 1559c6ba29dSJustin Hibbits static int 1569c6ba29dSJustin Hibbits opal_sensor_probe(device_t dev) 1579c6ba29dSJustin Hibbits { 1589c6ba29dSJustin Hibbits if (!ofw_bus_is_compatible(dev, "ibm,opal-sensor")) 1599c6ba29dSJustin Hibbits return (ENXIO); 1609c6ba29dSJustin Hibbits 1619c6ba29dSJustin Hibbits device_set_desc(dev, "OPAL sensor"); 1629c6ba29dSJustin Hibbits return (BUS_PROBE_GENERIC); 1639c6ba29dSJustin Hibbits } 1649c6ba29dSJustin Hibbits 1659c6ba29dSJustin Hibbits static int 1669c6ba29dSJustin Hibbits opal_sensor_attach(device_t dev) 1679c6ba29dSJustin Hibbits { 1689c6ba29dSJustin Hibbits struct opal_sensor_softc *sc; 1699c6ba29dSJustin Hibbits struct sysctl_ctx_list *ctx; 1709c6ba29dSJustin Hibbits struct sysctl_oid *tree; 1719c6ba29dSJustin Hibbits char type[8]; 1729c6ba29dSJustin Hibbits phandle_t node; 1739c6ba29dSJustin Hibbits cell_t sensor_id; 1749c6ba29dSJustin Hibbits int i; 1759c6ba29dSJustin Hibbits 1769c6ba29dSJustin Hibbits sc = device_get_softc(dev); 1779c6ba29dSJustin Hibbits sc->sc_dev = dev; 1789c6ba29dSJustin Hibbits 1799c6ba29dSJustin Hibbits node = ofw_bus_get_node(dev); 1809c6ba29dSJustin Hibbits 1819c6ba29dSJustin Hibbits if (OF_getencprop(node, "sensor-data", &sensor_id, sizeof(sensor_id)) < 0) { 1829c6ba29dSJustin Hibbits device_printf(dev, "Missing sensor ID\n"); 1839c6ba29dSJustin Hibbits return (ENXIO); 1849c6ba29dSJustin Hibbits } 1859c6ba29dSJustin Hibbits if (OF_getprop(node, "sensor-type", type, sizeof(type)) < 0) { 1869c6ba29dSJustin Hibbits device_printf(dev, "Missing sensor type\n"); 1879c6ba29dSJustin Hibbits return (ENXIO); 1889c6ba29dSJustin Hibbits } 1899c6ba29dSJustin Hibbits 1909c6ba29dSJustin Hibbits sc->sc_type = -1; 1919c6ba29dSJustin Hibbits for (i = 0; i < OPAL_SENSOR_MAX; i++) { 1929c6ba29dSJustin Hibbits if (strcmp(type, opal_sensor_types[i]) == 0) { 1939c6ba29dSJustin Hibbits sc->sc_type = i; 1949c6ba29dSJustin Hibbits break; 1959c6ba29dSJustin Hibbits } 1969c6ba29dSJustin Hibbits } 1979c6ba29dSJustin Hibbits if (sc->sc_type == -1) { 1989c6ba29dSJustin Hibbits device_printf(dev, "Unknown sensor type '%s'\n", type); 1999c6ba29dSJustin Hibbits return (ENXIO); 2009c6ba29dSJustin Hibbits } 2019c6ba29dSJustin Hibbits 2029c6ba29dSJustin Hibbits ctx = device_get_sysctl_ctx(dev); 2039c6ba29dSJustin Hibbits tree = device_get_sysctl_tree(dev); 2049c6ba29dSJustin Hibbits 2059c6ba29dSJustin Hibbits sc->sc_handle = sensor_id; 2069c6ba29dSJustin Hibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 2077029da5cSPawel Biernacki "sensor", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 2087029da5cSPawel Biernacki sensor_id, opal_sensor_sysctl, 2097029da5cSPawel Biernacki (sc->sc_type == OPAL_SENSOR_TEMP) ? "IK" : "I", "current value"); 2109c6ba29dSJustin Hibbits 2119c6ba29dSJustin Hibbits SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "type", 2129c6ba29dSJustin Hibbits CTLFLAG_RD, __DECONST(char *, opal_sensor_types[sc->sc_type]), 2139c6ba29dSJustin Hibbits 0, ""); 2149c6ba29dSJustin Hibbits 2159c6ba29dSJustin Hibbits OF_getprop_alloc(node, "label", (void **)&sc->sc_label); 2169c6ba29dSJustin Hibbits SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "label", 2179c6ba29dSJustin Hibbits CTLFLAG_RD, sc->sc_label, 0, ""); 2189c6ba29dSJustin Hibbits 2196d2254bcSAlfredo Dal'Ava Junior if (OF_getencprop(node, "sensor-data-min", 2209c6ba29dSJustin Hibbits &sensor_id, sizeof(sensor_id)) > 0) { 2219c6ba29dSJustin Hibbits sc->sc_min_handle = sensor_id; 2229c6ba29dSJustin Hibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 2237029da5cSPawel Biernacki "sensor_min", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 2247029da5cSPawel Biernacki sc, sensor_id, opal_sensor_sysctl, 225459e54f9SJustin Hibbits (sc->sc_type == OPAL_SENSOR_TEMP) ? "IK" : "I", 2269c6ba29dSJustin Hibbits "minimum value"); 2279c6ba29dSJustin Hibbits } 2289c6ba29dSJustin Hibbits 2296d2254bcSAlfredo Dal'Ava Junior if (OF_getencprop(node, "sensor-data-max", 2309c6ba29dSJustin Hibbits &sensor_id, sizeof(sensor_id)) > 0) { 2319c6ba29dSJustin Hibbits sc->sc_max_handle = sensor_id; 2329c6ba29dSJustin Hibbits SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 2337029da5cSPawel Biernacki "sensor_max", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 2347029da5cSPawel Biernacki sc, sensor_id, opal_sensor_sysctl, 235459e54f9SJustin Hibbits (sc->sc_type == OPAL_SENSOR_TEMP) ? "IK" : "I", 2369c6ba29dSJustin Hibbits "maximum value"); 2379c6ba29dSJustin Hibbits } 2389c6ba29dSJustin Hibbits 2399c6ba29dSJustin Hibbits SENSOR_LOCK_INIT(sc); 2409c6ba29dSJustin Hibbits 2419c6ba29dSJustin Hibbits return (0); 2429c6ba29dSJustin Hibbits } 2439c6ba29dSJustin Hibbits 2449c6ba29dSJustin Hibbits static device_method_t opal_sensor_methods[] = { 2459c6ba29dSJustin Hibbits DEVMETHOD(device_probe, opal_sensor_probe), 2469c6ba29dSJustin Hibbits DEVMETHOD(device_attach, opal_sensor_attach), 247885f3adfSJustin Hibbits 248885f3adfSJustin Hibbits DEVMETHOD_END 2499c6ba29dSJustin Hibbits }; 2509c6ba29dSJustin Hibbits 2519c6ba29dSJustin Hibbits static driver_t opal_sensor_driver = { 2529c6ba29dSJustin Hibbits "opal_sensor", 2539c6ba29dSJustin Hibbits opal_sensor_methods, 2549c6ba29dSJustin Hibbits sizeof(struct opal_sensor_softc) 2559c6ba29dSJustin Hibbits }; 2569c6ba29dSJustin Hibbits 2575edf159fSJohn Baldwin DRIVER_MODULE(opal_sensor, opalsens, opal_sensor_driver, NULL, NULL); 2589c6ba29dSJustin Hibbits 2599c6ba29dSJustin Hibbits static int 2609c6ba29dSJustin Hibbits opalsens_probe(device_t dev) 2619c6ba29dSJustin Hibbits { 2629c6ba29dSJustin Hibbits 2639c6ba29dSJustin Hibbits if (!ofw_bus_is_compatible(dev, "ibm,opal-sensor")) 2649c6ba29dSJustin Hibbits return (ENXIO); 2659c6ba29dSJustin Hibbits 2669c6ba29dSJustin Hibbits device_set_desc(dev, "OPAL Sensors"); 2679c6ba29dSJustin Hibbits return (BUS_PROBE_GENERIC); 2689c6ba29dSJustin Hibbits } 2699c6ba29dSJustin Hibbits 2709c6ba29dSJustin Hibbits static int 2719c6ba29dSJustin Hibbits opalsens_attach(device_t dev) 2729c6ba29dSJustin Hibbits { 2739c6ba29dSJustin Hibbits phandle_t child; 2749c6ba29dSJustin Hibbits device_t cdev; 2759c6ba29dSJustin Hibbits struct ofw_bus_devinfo *dinfo; 2769c6ba29dSJustin Hibbits 2779c6ba29dSJustin Hibbits for (child = OF_child(ofw_bus_get_node(dev)); child != 0; 2789c6ba29dSJustin Hibbits child = OF_peer(child)) { 2799c6ba29dSJustin Hibbits dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); 2809c6ba29dSJustin Hibbits if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) { 2819c6ba29dSJustin Hibbits free(dinfo, M_DEVBUF); 2829c6ba29dSJustin Hibbits continue; 2839c6ba29dSJustin Hibbits } 2845b56413dSWarner Losh cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY); 2859c6ba29dSJustin Hibbits if (cdev == NULL) { 2869c6ba29dSJustin Hibbits device_printf(dev, "<%s>: device_add_child failed\n", 2879c6ba29dSJustin Hibbits dinfo->obd_name); 2889c6ba29dSJustin Hibbits ofw_bus_gen_destroy_devinfo(dinfo); 2899c6ba29dSJustin Hibbits free(dinfo, M_DEVBUF); 2909c6ba29dSJustin Hibbits continue; 2919c6ba29dSJustin Hibbits } 2929c6ba29dSJustin Hibbits device_set_ivars(cdev, dinfo); 2939c6ba29dSJustin Hibbits } 2949c6ba29dSJustin Hibbits 295*18250ec6SJohn Baldwin bus_attach_children(dev); 296*18250ec6SJohn Baldwin return (0); 2979c6ba29dSJustin Hibbits } 2989c6ba29dSJustin Hibbits 2999c6ba29dSJustin Hibbits static const struct ofw_bus_devinfo * 3009c6ba29dSJustin Hibbits opalsens_get_devinfo(device_t dev, device_t child) 3019c6ba29dSJustin Hibbits { 3029c6ba29dSJustin Hibbits return (device_get_ivars(child)); 3039c6ba29dSJustin Hibbits } 3049c6ba29dSJustin Hibbits 3059c6ba29dSJustin Hibbits static device_method_t opalsens_methods[] = { 3069c6ba29dSJustin Hibbits /* Device interface */ 3079c6ba29dSJustin Hibbits DEVMETHOD(device_probe, opalsens_probe), 3089c6ba29dSJustin Hibbits DEVMETHOD(device_attach, opalsens_attach), 3099c6ba29dSJustin Hibbits 3109c6ba29dSJustin Hibbits /* ofw_bus interface */ 3119c6ba29dSJustin Hibbits DEVMETHOD(ofw_bus_get_devinfo, opalsens_get_devinfo), 3129c6ba29dSJustin Hibbits DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 3139c6ba29dSJustin Hibbits DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 3149c6ba29dSJustin Hibbits DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 3159c6ba29dSJustin Hibbits DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 3169c6ba29dSJustin Hibbits DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 3179c6ba29dSJustin Hibbits 3189c6ba29dSJustin Hibbits DEVMETHOD_END 3199c6ba29dSJustin Hibbits }; 3209c6ba29dSJustin Hibbits 3219c6ba29dSJustin Hibbits static driver_t opalsens_driver = { 3229c6ba29dSJustin Hibbits "opalsens", 3239c6ba29dSJustin Hibbits opalsens_methods, 3249c6ba29dSJustin Hibbits 0 3259c6ba29dSJustin Hibbits }; 3269c6ba29dSJustin Hibbits 3275edf159fSJohn Baldwin DRIVER_MODULE(opalsens, opal, opalsens_driver, NULL, NULL); 328