Author: avg
Date: Sat Oct 15 09:10:35 2016
New Revision: 307348
URL: https://svnweb.freebsd.org/changeset/base/307348

Log:
  aibs / atk0110: add support for querying sensors via GGRP and GITM
  
  Comparing to the Linux driver there is still one missing feature.
  The Linux driver finds and enables "Embedded Controller" item in
  the 0x11 group if it's not enabled yet.
  
  I tested the new method, Torfinn Ingolfsen tested the old method
  and helped to fix several bugs in the earlier versions of the patch.
  
  Tested by:    Torfinn Ingolfsen <torfinn.ingolf...@getmail.no>
  Reviewed by:  rpaulo
  MFC after:    3 weeks
  Differential Revision: https://reviews.freebsd.org/D8227

Modified:
  head/sys/dev/acpi_support/atk0110.c

Modified: head/sys/dev/acpi_support/atk0110.c
==============================================================================
--- head/sys/dev/acpi_support/atk0110.c Sat Oct 15 09:09:25 2016        
(r307347)
+++ head/sys/dev/acpi_support/atk0110.c Sat Oct 15 09:10:35 2016        
(r307348)
@@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/module.h>
 #include <sys/malloc.h>
 #include <sys/sysctl.h>
+#include <sys/stdint.h>
 
 #include <contrib/dev/acpica/include/acpi.h>
 #include <dev/acpica/acpivar.h>
@@ -51,18 +52,23 @@ ACPI_SERIAL_DECL(aibs, "aibs");
 #define AIBS_MORE_SENSORS
 #define AIBS_VERBOSE
 
-enum aibs_type {
-       AIBS_VOLT,
-       AIBS_TEMP,
-       AIBS_FAN
-};
+#define        AIBS_GROUP_SENSORS      0x06
+
+#define AIBS_SENS_TYPE(x)      (((x) >> 16) & 0xff)
+#define AIBS_SENS_TYPE_VOLT    2
+#define AIBS_SENS_TYPE_TEMP    3
+#define AIBS_SENS_TYPE_FAN     4
+
+#define        AIBS_SENS_TYPE_VOLT_NAME                "volt"
+#define        AIBS_SENS_TYPE_VOLT_TEMP                "temp"
+#define        AIBS_SENS_TYPE_VOLT_FAN         "fan"
 
 struct aibs_sensor {
        ACPI_INTEGER    v;
        ACPI_INTEGER    i;
        ACPI_INTEGER    l;
        ACPI_INTEGER    h;
-       enum aibs_type  t;
+       int             t;
 };
 
 struct aibs_softc {
@@ -72,14 +78,23 @@ struct aibs_softc {
        struct aibs_sensor      *sc_asens_volt;
        struct aibs_sensor      *sc_asens_temp;
        struct aibs_sensor      *sc_asens_fan;
+       struct aibs_sensor      *sc_asens_all;
+
+       struct sysctl_oid       *sc_volt_sysctl;
+       struct sysctl_oid       *sc_temp_sysctl;
+       struct sysctl_oid       *sc_fan_sysctl;
+
+       bool                    sc_ggrp_method;
 };
 
 static int aibs_probe(device_t);
 static int aibs_attach(device_t);
 static int aibs_detach(device_t);
 static int aibs_sysctl(SYSCTL_HANDLER_ARGS);
+static int aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS);
 
-static void aibs_attach_sif(struct aibs_softc *, enum aibs_type);
+static int aibs_attach_ggrp(struct aibs_softc *);
+static int aibs_attach_sif(struct aibs_softc *, int);
 
 static device_method_t aibs_methods[] = {
        DEVMETHOD(device_probe,         aibs_probe),
@@ -109,54 +124,240 @@ aibs_probe(device_t dev)
 {
        if (acpi_disabled("aibs") ||
            ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
-               return ENXIO;
+               return (ENXIO);
 
        device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
-       return 0;
+       return (0);
 }
 
 static int
 aibs_attach(device_t dev)
 {
        struct aibs_softc *sc = device_get_softc(dev);
+       int err;
 
        sc->sc_dev = dev;
        sc->sc_ah = acpi_get_handle(dev);
 
-       aibs_attach_sif(sc, AIBS_VOLT);
-       aibs_attach_sif(sc, AIBS_TEMP);
-       aibs_attach_sif(sc, AIBS_FAN);
+       sc->sc_ggrp_method = false;
+       err = aibs_attach_sif(sc, AIBS_SENS_TYPE_VOLT);
+       if (err == 0)
+               err = aibs_attach_sif(sc, AIBS_SENS_TYPE_TEMP);
+       if (err == 0)
+               err = aibs_attach_sif(sc, AIBS_SENS_TYPE_FAN);
+
+       if (err == 0)
+               return (0);
+
+       /* Clean up whatever was allocated earlier. */
+       if (sc->sc_volt_sysctl != NULL)
+               sysctl_remove_oid(sc->sc_volt_sysctl, true, true);
+       if (sc->sc_temp_sysctl != NULL)
+               sysctl_remove_oid(sc->sc_temp_sysctl, true, true);
+       if (sc->sc_fan_sysctl != NULL)
+               sysctl_remove_oid(sc->sc_fan_sysctl, true, true);
+       aibs_detach(dev);
+
+       sc->sc_ggrp_method = true;
+       err = aibs_attach_ggrp(sc);
+       return (err);
+}
+
+static int
+aibs_add_sensor(struct aibs_softc *sc, ACPI_OBJECT *o,
+    struct aibs_sensor* sensor, const char ** descr)
+{
+       int             off;
 
-       return 0;
+       /*
+        * Packages for the old and new methods are quite
+        * similar except that the new package has two
+        * new (unknown / unused) fields after the name field.
+        */
+       if (sc->sc_ggrp_method)
+               off = 4;
+       else
+               off = 2;
+
+       if (o->Type != ACPI_TYPE_PACKAGE) {
+               device_printf(sc->sc_dev,
+                   "sensor object is not a package: %i type\n",
+                    o->Type);
+               return (ENXIO);
+       }
+       if (o[0].Package.Count != (off + 3) ||
+           o->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
+           o->Package.Elements[1].Type != ACPI_TYPE_STRING ||
+           o->Package.Elements[off].Type != ACPI_TYPE_INTEGER ||
+           o->Package.Elements[off + 1].Type != ACPI_TYPE_INTEGER ||
+           o->Package.Elements[off + 2].Type != ACPI_TYPE_INTEGER) {
+               device_printf(sc->sc_dev, "unexpected package content\n");
+               return (ENXIO);
+       }
+
+       sensor->i = o->Package.Elements[0].Integer.Value;
+       *descr = o->Package.Elements[1].String.Pointer;
+       sensor->l = o->Package.Elements[off].Integer.Value;
+       sensor->h = o->Package.Elements[off + 1].Integer.Value;
+       /* For the new method the second value is a range size. */
+       if (sc->sc_ggrp_method)
+               sensor->h += sensor->l;
+       sensor->t = AIBS_SENS_TYPE(sensor->i);
+
+       switch (sensor->t) {
+       case AIBS_SENS_TYPE_VOLT:
+       case AIBS_SENS_TYPE_TEMP:
+       case AIBS_SENS_TYPE_FAN:
+               return (0);
+       default:
+               device_printf(sc->sc_dev, "unknown sensor type 0x%x",
+                   sensor->t);
+               return (ENXIO);
+       }
 }
 
 static void
-aibs_attach_sif(struct aibs_softc *sc, enum aibs_type st)
+aibs_sensor_added(struct aibs_softc *sc, struct sysctl_oid *so,
+    const char *type_name, int idx, struct aibs_sensor *sensor,
+    const char *descr)
+{
+       char    sysctl_name[8];
+
+       snprintf(sysctl_name, sizeof(sysctl_name), "%i", idx);
+#ifdef AIBS_VERBOSE
+       device_printf(sc->sc_dev, "%c%i: 0x%08jx %20s %5jd / %5jd\n",
+           type_name[0], idx,
+           (uintmax_t)sensor->i, descr, (intmax_t)sensor->l,
+           (intmax_t)sensor->h);
+#endif
+       SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
+           SYSCTL_CHILDREN(so), idx, sysctl_name,
+           CTLTYPE_INT | CTLFLAG_RD, sc, (uintptr_t)sensor,
+           sc->sc_ggrp_method ? aibs_sysctl_ggrp : aibs_sysctl,
+           sensor->t == AIBS_SENS_TYPE_TEMP ? "IK" : "I", descr);
+}
+
+static int
+aibs_attach_ggrp(struct aibs_softc *sc)
+{
+       ACPI_STATUS             s;
+       ACPI_BUFFER             buf;
+       ACPI_HANDLE             h;
+       ACPI_OBJECT             id;
+       ACPI_OBJECT             *bp;
+       ACPI_OBJECT_LIST        arg;
+       int                     i;
+       int                     t, v, f;
+       int                     err;
+       int                     *s_idx;
+       const char              *name;
+       const char              *descr;
+       struct aibs_sensor      *sensor;
+       struct sysctl_oid       **so;
+
+       /* First see if GITM is available. */
+       s = AcpiGetHandle(sc->sc_ah, "GITM", &h);
+       if (ACPI_FAILURE(s)) {
+               if (bootverbose)
+                       device_printf(sc->sc_dev, "GITM not found\n");
+               return (ENXIO);
+       }
+
+       /*
+        * Now call GGRP with the appropriate argument to list sensors.
+        * The method lists different groups of entities depending on
+        * the argument.
+        */
+       id.Integer.Value = AIBS_GROUP_SENSORS;
+       id.Type = ACPI_TYPE_INTEGER;
+       arg.Count = 1;
+       arg.Pointer = &id;
+       buf.Length = ACPI_ALLOCATE_BUFFER;
+       buf.Pointer = NULL;
+       s = AcpiEvaluateObjectTyped(sc->sc_ah, "GGRP", &arg, &buf,
+           ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(s)) {
+               device_printf(sc->sc_dev, "GGRP not found\n");
+               return (ENXIO);
+       }
+
+       bp = buf.Pointer;
+       sc->sc_asens_all = malloc(sizeof(*sc->sc_asens_all) * bp->Package.Count,
+           M_DEVBUF, M_WAITOK | M_ZERO);
+       v = t = f = 0;
+       for (i = 0; i < bp->Package.Count; i++) {
+               sensor = &sc->sc_asens_all[i];
+               err = aibs_add_sensor(sc, &bp->Package.Elements[i], sensor,
+                   &descr);
+               if (err != 0)
+                       continue;
+
+               switch (sensor->t) {
+               case AIBS_SENS_TYPE_VOLT:
+                       name = "volt";
+                       so = &sc->sc_volt_sysctl;
+                       s_idx = &v;
+                       break;
+               case AIBS_SENS_TYPE_TEMP:
+                       name = "temp";
+                       so = &sc->sc_temp_sysctl;
+                       s_idx = &t;
+                       break;
+               case AIBS_SENS_TYPE_FAN:
+                       name = "fan";
+                       so = &sc->sc_fan_sysctl;
+                       s_idx = &f;
+                       break;
+               default:
+                       panic("add_sensor succeeded for unknown sensor type %d",
+                           sensor->t);
+               }
+
+               if (*so == NULL) {
+                       /* sysctl subtree for sensors of this type */
+                       *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
+                           SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)),
+                           sensor->t, name, CTLFLAG_RD, NULL, NULL);
+               }
+               aibs_sensor_added(sc, *so, name, *s_idx, sensor, descr);
+               *s_idx += 1;
+       }
+
+       AcpiOsFree(buf.Pointer);
+       return (0);
+}
+
+static int
+aibs_attach_sif(struct aibs_softc *sc, int st)
 {
+       char                    name[] = "?SIF";
        ACPI_STATUS             s;
        ACPI_BUFFER             b;
        ACPI_OBJECT             *bp, *o;
-       int                     i, n;
        const char              *node;
-       char                    name[] = "?SIF";
        struct aibs_sensor      *as;
-       struct sysctl_oid       *so;
+       struct sysctl_oid       **so;
+       int                     i, n;
+       int err;
 
        switch (st) {
-       case AIBS_VOLT:
+       case AIBS_SENS_TYPE_VOLT:
                node = "volt";
                name[0] = 'V';
+               so = &sc->sc_volt_sysctl;
                break;
-       case AIBS_TEMP:
+       case AIBS_SENS_TYPE_TEMP:
                node = "temp";
                name[0] = 'T';
+               so = &sc->sc_temp_sysctl;
                break;
-       case AIBS_FAN:
+       case AIBS_SENS_TYPE_FAN:
                node = "fan";
                name[0] = 'F';
+               so = &sc->sc_fan_sysctl;
                break;
        default:
-               return;
+               panic("Unsupported sensor type %d", st);
        }
 
        b.Length = ACPI_ALLOCATE_BUFFER;
@@ -164,7 +365,7 @@ aibs_attach_sif(struct aibs_softc *sc, e
            ACPI_TYPE_PACKAGE);
        if (ACPI_FAILURE(s)) {
                device_printf(sc->sc_dev, "%s not found\n", name);
-               return;
+               return (ENXIO);
        }
 
        bp = b.Pointer;
@@ -172,14 +373,14 @@ aibs_attach_sif(struct aibs_softc *sc, e
        if (o[0].Type != ACPI_TYPE_INTEGER) {
                device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
                AcpiOsFree(b.Pointer);
-               return;
+               return (ENXIO);
        }
 
        n = o[0].Integer.Value;
        if (bp->Package.Count - 1 < n) {
                device_printf(sc->sc_dev, "%s: invalid package\n", name);
                AcpiOsFree(b.Pointer);
-               return;
+               return (ENXIO);
        } else if (bp->Package.Count - 1 > n) {
                int on = n;
 
@@ -193,76 +394,37 @@ aibs_attach_sif(struct aibs_softc *sc, e
                device_printf(sc->sc_dev, "%s: no members in the package\n",
                    name);
                AcpiOsFree(b.Pointer);
-               return;
+               return (ENXIO);
        }
 
-       as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO);
-       if (as == NULL) {
-               device_printf(sc->sc_dev, "%s: malloc fail\n", name);
-               AcpiOsFree(b.Pointer);
-               return;
-       }
+       as = malloc(sizeof(*as) * n, M_DEVBUF, M_WAITOK | M_ZERO);
        switch (st) {
-       case AIBS_VOLT:
+       case AIBS_SENS_TYPE_VOLT:
                sc->sc_asens_volt = as;
                break;
-       case AIBS_TEMP:
+       case AIBS_SENS_TYPE_TEMP:
                sc->sc_asens_temp = as;
                break;
-       case AIBS_FAN:
+       case AIBS_SENS_TYPE_FAN:
                sc->sc_asens_fan = as;
                break;
        }
 
        /* sysctl subtree for sensors of this type */
-       so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
+       *so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
            SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
            node, CTLFLAG_RD, NULL, NULL);
 
        for (i = 0, o++; i < n; i++, o++) {
-               ACPI_OBJECT     *oi;
-               char            si[3];
-               const char      *desc;
-
-               /* acpica5 automatically evaluates the referenced package */
-               if (o[0].Type != ACPI_TYPE_PACKAGE) {
-                       device_printf(sc->sc_dev,
-                           "%s: %i: not a package: %i type\n",
-                           name, i, o[0].Type);
-                       continue;
-               }
-               oi = o[0].Package.Elements;
-               if (o[0].Package.Count != 5 ||
-                   oi[0].Type != ACPI_TYPE_INTEGER ||
-                   oi[1].Type != ACPI_TYPE_STRING ||
-                   oi[2].Type != ACPI_TYPE_INTEGER ||
-                   oi[3].Type != ACPI_TYPE_INTEGER ||
-                   oi[4].Type != ACPI_TYPE_INTEGER) {
-                       device_printf(sc->sc_dev,
-                           "%s: %i: invalid package\n",
-                           name, i);
-                       continue;
-               }
-               as[i].i = oi[0].Integer.Value;
-               desc = oi[1].String.Pointer;
-               as[i].l = oi[2].Integer.Value;
-               as[i].h = oi[3].Integer.Value;
-               as[i].t = st;
-#ifdef AIBS_VERBOSE
-               device_printf(sc->sc_dev, "%c%i: "
-                   "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64"  "
-                   "0x%"PRIx64"\n",
-                   name[0], i,
-                   (uint64_t)as[i].i, desc, (int64_t)as[i].l,
-                   (int64_t)as[i].h, (uint64_t)oi[4].Integer.Value);
-#endif
-               snprintf(si, sizeof(si), "%i", i);
-               SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
-                   SYSCTL_CHILDREN(so), i, si, CTLTYPE_INT | CTLFLAG_RD,
-                   sc, st, aibs_sysctl, st == AIBS_TEMP ? "IK" : "I", desc);
+               const char      *descr;
+
+               err = aibs_add_sensor(sc, o, &as[i], &descr);
+               if (err == 0)
+                       aibs_sensor_added(sc, *so, node, i, &as[i], descr);
        }
 
        AcpiOsFree(b.Pointer);
+       return (0);
 }
 
 static int
@@ -276,7 +438,9 @@ aibs_detach(device_t dev)
                free(sc->sc_asens_temp, M_DEVBUF);
        if (sc->sc_asens_fan != NULL)
                free(sc->sc_asens_fan, M_DEVBUF);
-       return 0;
+       if (sc->sc_asens_all != NULL)
+               free(sc->sc_asens_all, M_DEVBUF);
+       return (0);
 }
 
 #ifdef AIBS_VERBOSE
@@ -289,39 +453,33 @@ static int
 aibs_sysctl(SYSCTL_HANDLER_ARGS)
 {
        struct aibs_softc       *sc = arg1;
-       enum aibs_type          st = arg2;
+       struct aibs_sensor      *sensor = (void *)arg2;
        int                     i = oidp->oid_number;
        ACPI_STATUS             rs;
        ACPI_OBJECT             p, *bp;
        ACPI_OBJECT_LIST        mp;
        ACPI_BUFFER             b;
        char                    *name;
-       struct aibs_sensor      *as;
        ACPI_INTEGER            v, l, h;
        int                     so[3];
 
-       switch (st) {
-       case AIBS_VOLT:
+       switch (sensor->t) {
+       case AIBS_SENS_TYPE_VOLT:
                name = "RVLT";
-               as = sc->sc_asens_volt;
                break;
-       case AIBS_TEMP:
+       case AIBS_SENS_TYPE_TEMP:
                name = "RTMP";
-               as = sc->sc_asens_temp;
                break;
-       case AIBS_FAN:
+       case AIBS_SENS_TYPE_FAN:
                name = "RFAN";
-               as = sc->sc_asens_fan;
                break;
        default:
-               return ENOENT;
+               return (ENOENT);
        }
-       if (as == NULL)
-               return ENOENT;
-       l = as[i].l;
-       h = as[i].h;
+       l = sensor->l;
+       h = sensor->h;
        p.Type = ACPI_TYPE_INTEGER;
-       p.Integer.Value = as[i].i;
+       p.Integer.Value = sensor->i;
        mp.Count = 1;
        mp.Pointer = &p;
        b.Length = ACPI_ALLOCATE_BUFFER;
@@ -333,26 +491,91 @@ aibs_sysctl(SYSCTL_HANDLER_ARGS)
                    "%s: %i: evaluation failed\n",
                    name, i);
                ACPI_SERIAL_END(aibs);
-               return EIO;
+               return (EIO);
        }
        bp = b.Pointer;
        v = bp->Integer.Value;
        AcpiOsFree(b.Pointer);
        ACPI_SERIAL_END(aibs);
 
-       switch (st) {
-       case AIBS_VOLT:
+       switch (sensor->t) {
+       case AIBS_SENS_TYPE_VOLT:
+               break;
+       case AIBS_SENS_TYPE_TEMP:
+               v += 2731;
+               l += 2731;
+               h += 2731;
+               break;
+       case AIBS_SENS_TYPE_FAN:
+               break;
+       }
+       so[0] = v;
+       so[1] = l;
+       so[2] = h;
+       return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
+}
+
+static int
+aibs_sysctl_ggrp(SYSCTL_HANDLER_ARGS)
+{
+       struct aibs_softc       *sc = arg1;
+       struct aibs_sensor      *sensor = (void *)arg2;
+       ACPI_STATUS             rs;
+       ACPI_OBJECT             p, *bp;
+       ACPI_OBJECT_LIST        arg;
+       ACPI_BUFFER             buf;
+       ACPI_INTEGER            v, l, h;
+       int                     so[3];
+       uint32_t                *ret;
+       uint32_t                cmd[3];
+
+       cmd[0] = sensor->i;
+       cmd[1] = 0;
+       cmd[2] = 0;
+       p.Type = ACPI_TYPE_BUFFER;
+       p.Buffer.Pointer = (void *)cmd;
+       p.Buffer.Length = sizeof(cmd);
+       arg.Count = 1;
+       arg.Pointer = &p;
+       buf.Pointer = NULL;
+       buf.Length = ACPI_ALLOCATE_BUFFER;
+       ACPI_SERIAL_BEGIN(aibs);
+       rs = AcpiEvaluateObjectTyped(sc->sc_ah, "GITM", &arg, &buf,
+           ACPI_TYPE_BUFFER);
+       ACPI_SERIAL_END(aibs);
+       if (ACPI_FAILURE(rs)) {
+               device_printf(sc->sc_dev, "GITM evaluation failed\n");
+               return (EIO);
+       }
+       bp = buf.Pointer;
+       if (bp->Buffer.Length < 8) {
+               device_printf(sc->sc_dev, "GITM returned short buffer\n");
+               return (EIO);
+       }
+       ret = (uint32_t *)bp->Buffer.Pointer;
+       if (ret[0] == 0) {
+               device_printf(sc->sc_dev, "GITM returned error status\n");
+               return (EINVAL);
+       }
+       v = ret[1];
+       AcpiOsFree(buf.Pointer);
+
+       l = sensor->l;
+       h = sensor->h;
+
+       switch (sensor->t) {
+       case AIBS_SENS_TYPE_VOLT:
                break;
-       case AIBS_TEMP:
+       case AIBS_SENS_TYPE_TEMP:
                v += 2731;
                l += 2731;
                h += 2731;
                break;
-       case AIBS_FAN:
+       case AIBS_SENS_TYPE_FAN:
                break;
        }
        so[0] = v;
        so[1] = l;
        so[2] = h;
-       return sysctl_handle_opaque(oidp, &so, sizeof(so), req);
+       return (sysctl_handle_opaque(oidp, &so, sizeof(so), req));
 }
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to