Path: blob/main/sys/powerpc/powernv/opal_sensor.c
104197 views
/*-1* Copyright (C) 2018 Justin Hibbits2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6* 1. Redistributions of source code must retain the above copyright7* notice, this list of conditions and the following disclaimer.8* 2. Redistributions in binary form must reproduce the above copyright9* notice, this list of conditions and the following disclaimer in the10* documentation and/or other materials provided with the distribution.11*12* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR13* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES14* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.15* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,16* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,17* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;18* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,19* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR20* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF21* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.22*/2324#include <sys/param.h>25#include <sys/kernel.h>26#include <sys/lock.h>27#include <sys/module.h>28#include <sys/mutex.h>29#include <sys/proc.h>30#include <sys/sysctl.h>31#include <sys/systm.h>32#include <sys/endian.h>3334#include <vm/vm.h>35#include <vm/pmap.h>3637#include <machine/bus.h>3839#include <dev/ofw/openfirm.h>40#include <dev/ofw/ofw_bus.h>41#include <dev/ofw/ofw_bus_subr.h>4243#include "opal.h"4445struct opal_sensor_softc {46device_t sc_dev;47struct mtx sc_mtx;48uint32_t sc_handle;49uint32_t sc_min_handle;50uint32_t sc_max_handle;51char *sc_label;52int sc_type;53};5455#define SENSOR_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)56#define SENSOR_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)57#define SENSOR_LOCK_INIT(_sc) \58mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \59"opal-sensor", MTX_DEF)6061/*62* A bit confusing, maybe. There are two types of nodes with compatible strings63* of "ibm,opal-sensor". One hangs off /ibm,opal/, named "sensors", the other64* hangs off of this node. For newbus attachments, we have one node (opalsens)65* attach from opal0, and the others (opal_sensor) attach from opalsens. These66* are the real sensors.67*/68enum opal_sensor_type {69OPAL_SENSOR_TEMP = 0, /* From OPAL: degC */70OPAL_SENSOR_FAN = 1, /* From OPAL: RPM */71OPAL_SENSOR_POWER = 2, /* From OPAL: W */72OPAL_SENSOR_IN = 3, /* From OPAL: mV */73OPAL_SENSOR_ENERGY = 4, /* From OPAL: uJ */74OPAL_SENSOR_CURR = 5, /* From OPAL: mA */75OPAL_SENSOR_MAX76};7778/* This must be kept sorted with the enum above. */79const char *opal_sensor_types[] = {80"temp",81"fan",82"power",83"in",84"energy",85"curr"86};8788/*89* Retrieve the raw value from OPAL. This will be cooked by the sysctl handler.90*/91static int92opal_sensor_get_val(struct opal_sensor_softc *sc, uint32_t key, uint64_t *val)93{94struct opal_msg msg;95uint32_t val32;96int rv, token;9798token = opal_alloc_async_token();99SENSOR_LOCK(sc);100rv = opal_call(OPAL_SENSOR_READ, key, token, vtophys(&val32));101102if (rv == OPAL_ASYNC_COMPLETION) {103/* Sleep a little to let things settle. */104DELAY(100);105bzero(&msg, sizeof(msg));106rv = opal_wait_completion(&msg, sizeof(msg), token);107108if (rv == OPAL_SUCCESS)109val32 = msg.params[0];110}111SENSOR_UNLOCK(sc);112113if (rv == OPAL_SUCCESS)114*val = be32toh(val32);115else116rv = EIO;117118opal_free_async_token(token);119return (rv);120}121122static int123opal_sensor_sysctl(SYSCTL_HANDLER_ARGS)124{125struct opal_sensor_softc *sc;126int error, result;127uint32_t sensor;128uint64_t sensval;129130sc = arg1;131sensor = arg2;132133error = opal_sensor_get_val(sc, sensor, &sensval);134135if (error)136return (error);137138result = sensval;139140switch (sc->sc_type) {141case OPAL_SENSOR_TEMP:142result = result * 10 + 2731; /* Convert to K */143break;144case OPAL_SENSOR_POWER:145result = result * 1000; /* Convert to mW */146break;147}148149error = sysctl_handle_int(oidp, &result, 0, req);150151return (error);152}153154static int155opal_sensor_probe(device_t dev)156{157if (!ofw_bus_is_compatible(dev, "ibm,opal-sensor"))158return (ENXIO);159160device_set_desc(dev, "OPAL sensor");161return (BUS_PROBE_GENERIC);162}163164static int165opal_sensor_attach(device_t dev)166{167struct opal_sensor_softc *sc;168struct sysctl_ctx_list *ctx;169struct sysctl_oid *tree;170char type[8];171phandle_t node;172cell_t sensor_id;173int i;174175sc = device_get_softc(dev);176sc->sc_dev = dev;177178node = ofw_bus_get_node(dev);179180if (OF_getencprop(node, "sensor-data", &sensor_id, sizeof(sensor_id)) < 0) {181device_printf(dev, "Missing sensor ID\n");182return (ENXIO);183}184if (OF_getprop(node, "sensor-type", type, sizeof(type)) < 0) {185device_printf(dev, "Missing sensor type\n");186return (ENXIO);187}188189sc->sc_type = -1;190for (i = 0; i < OPAL_SENSOR_MAX; i++) {191if (strcmp(type, opal_sensor_types[i]) == 0) {192sc->sc_type = i;193break;194}195}196if (sc->sc_type == -1) {197device_printf(dev, "Unknown sensor type '%s'\n", type);198return (ENXIO);199}200201ctx = device_get_sysctl_ctx(dev);202tree = device_get_sysctl_tree(dev);203204sc->sc_handle = sensor_id;205SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,206"sensor", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc,207sensor_id, opal_sensor_sysctl,208(sc->sc_type == OPAL_SENSOR_TEMP) ? "IK" : "I", "current value");209210SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "type",211CTLFLAG_RD, __DECONST(char *, opal_sensor_types[sc->sc_type]),2120, "");213214OF_getprop_alloc(node, "label", (void **)&sc->sc_label);215SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "label",216CTLFLAG_RD, sc->sc_label, 0, "");217218if (OF_getencprop(node, "sensor-data-min",219&sensor_id, sizeof(sensor_id)) > 0) {220sc->sc_min_handle = sensor_id;221SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,222"sensor_min", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,223sc, sensor_id, opal_sensor_sysctl,224(sc->sc_type == OPAL_SENSOR_TEMP) ? "IK" : "I",225"minimum value");226}227228if (OF_getencprop(node, "sensor-data-max",229&sensor_id, sizeof(sensor_id)) > 0) {230sc->sc_max_handle = sensor_id;231SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,232"sensor_max", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,233sc, sensor_id, opal_sensor_sysctl,234(sc->sc_type == OPAL_SENSOR_TEMP) ? "IK" : "I",235"maximum value");236}237238SENSOR_LOCK_INIT(sc);239240return (0);241}242243static device_method_t opal_sensor_methods[] = {244DEVMETHOD(device_probe, opal_sensor_probe),245DEVMETHOD(device_attach, opal_sensor_attach),246247DEVMETHOD_END248};249250static driver_t opal_sensor_driver = {251"opal_sensor",252opal_sensor_methods,253sizeof(struct opal_sensor_softc)254};255256DRIVER_MODULE(opal_sensor, opalsens, opal_sensor_driver, NULL, NULL);257258static int259opalsens_probe(device_t dev)260{261262if (!ofw_bus_is_compatible(dev, "ibm,opal-sensor"))263return (ENXIO);264265device_set_desc(dev, "OPAL Sensors");266return (BUS_PROBE_GENERIC);267}268269static int270opalsens_attach(device_t dev)271{272phandle_t child;273device_t cdev;274struct ofw_bus_devinfo *dinfo;275276for (child = OF_child(ofw_bus_get_node(dev)); child != 0;277child = OF_peer(child)) {278dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);279if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {280free(dinfo, M_DEVBUF);281continue;282}283cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY);284if (cdev == NULL) {285device_printf(dev, "<%s>: device_add_child failed\n",286dinfo->obd_name);287ofw_bus_gen_destroy_devinfo(dinfo);288free(dinfo, M_DEVBUF);289continue;290}291device_set_ivars(cdev, dinfo);292}293294bus_attach_children(dev);295return (0);296}297298static const struct ofw_bus_devinfo *299opalsens_get_devinfo(device_t dev, device_t child)300{301return (device_get_ivars(child));302}303304static device_method_t opalsens_methods[] = {305/* Device interface */306DEVMETHOD(device_probe, opalsens_probe),307DEVMETHOD(device_attach, opalsens_attach),308309/* ofw_bus interface */310DEVMETHOD(ofw_bus_get_devinfo, opalsens_get_devinfo),311DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),312DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),313DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),314DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),315DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),316317DEVMETHOD_END318};319320static driver_t opalsens_driver = {321"opalsens",322opalsens_methods,3230324};325326DRIVER_MODULE(opalsens, opal, opalsens_driver, NULL, NULL);327328329