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/dev/isa/aps.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 /*      $OpenBSD: aps.c,v 1.28 2022/04/06 18:59:28 naddy Exp $  */
    2 /*
    3  * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
    4  * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 /*
   20  * A driver for the ThinkPad Active Protection System based on notes from
   21  * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
   22  */
   23 
   24 #include <sys/param.h>
   25 #include <sys/systm.h>
   26 #include <sys/device.h>
   27 #include <sys/kernel.h>
   28 #include <sys/sensors.h>
   29 #include <sys/timeout.h>
   30 #include <machine/bus.h>
   31 #include <sys/event.h>
   32 
   33 #include <dev/isa/isavar.h>
   34 
   35 #ifdef __i386__
   36 #include "apm.h"
   37 #include <machine/acpiapm.h>
   38 #include <machine/biosvar.h>
   39 #include <machine/apmvar.h>
   40 #endif
   41 
   42 #if defined(APSDEBUG)
   43 #define DPRINTF(x)              do { printf x; } while (0)
   44 #else
   45 #define DPRINTF(x)
   46 #endif
   47 
   48 
   49 /*
   50  * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
   51  * From Renesas H8S/2140B Group Hardware Manual
   52  * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
   53  *
   54  * EC uses LPC Channel 3 registers TWR0..15
   55  */
   56 
   57 /* STR3 status register */
   58 #define APS_STR3                0x04
   59 
   60 #define APS_STR3_IBF3B  0x80    /* Input buffer full (host->slave) */
   61 #define APS_STR3_OBF3B  0x40    /* Output buffer full (slave->host)*/
   62 #define APS_STR3_MWMF   0x20    /* Master write mode */
   63 #define APS_STR3_SWMF   0x10    /* Slave write mode */
   64 
   65 
   66 /* Base address of TWR registers */
   67 #define APS_TWR_BASE            0x10
   68 #define APS_TWR_RET             0x1f
   69 
   70 /* TWR registers */
   71 #define APS_CMD                 0x00
   72 #define APS_ARG1                0x01
   73 #define APS_ARG2                0x02
   74 #define APS_ARG3                0x03
   75 #define APS_RET                 0x0f
   76 
   77 /* Sensor values */
   78 #define APS_STATE               0x01
   79 #define APS_XACCEL              0x02
   80 #define APS_YACCEL              0x04
   81 #define APS_TEMP                0x06
   82 #define APS_XVAR                0x07
   83 #define APS_YVAR                0x09
   84 #define APS_TEMP2               0x0b
   85 #define APS_UNKNOWN             0x0c
   86 #define APS_INPUT               0x0d
   87 
   88 /* write masks for I/O, send command + 0-3 arguments*/
   89 #define APS_WRITE_0             0x0001
   90 #define APS_WRITE_1             0x0003
   91 #define APS_WRITE_2             0x0007
   92 #define APS_WRITE_3             0x000f
   93 
   94 /* read masks for I/O, read 0-3 values (skip command byte) */
   95 #define APS_READ_0              0x0000
   96 #define APS_READ_1              0x0002
   97 #define APS_READ_2              0x0006
   98 #define APS_READ_3              0x000e
   99 
  100 #define APS_READ_RET            0x8000
  101 #define APS_READ_ALL            0xffff
  102 
  103 /* Bit definitions for APS_INPUT value */
  104 #define APS_INPUT_KB            (1 << 5)
  105 #define APS_INPUT_MS            (1 << 6)
  106 #define APS_INPUT_LIDOPEN       (1 << 7)
  107 
  108 #define APS_ADDR_SIZE           0x1f
  109 
  110 struct sensor_rec {
  111         u_int8_t        state;
  112         u_int16_t       x_accel;
  113         u_int16_t       y_accel;
  114         u_int8_t        temp1;
  115         u_int16_t       x_var;
  116         u_int16_t       y_var;
  117         u_int8_t        temp2;
  118         u_int8_t        unk;
  119         u_int8_t        input;
  120 };
  121 
  122 #define APS_NUM_SENSORS         9
  123 
  124 #define APS_SENSOR_XACCEL       0
  125 #define APS_SENSOR_YACCEL       1
  126 #define APS_SENSOR_XVAR         2
  127 #define APS_SENSOR_YVAR         3
  128 #define APS_SENSOR_TEMP1        4
  129 #define APS_SENSOR_TEMP2        5
  130 #define APS_SENSOR_KBACT        6
  131 #define APS_SENSOR_MSACT        7
  132 #define APS_SENSOR_LIDOPEN      8
  133 
  134 struct aps_softc {
  135         struct device sc_dev;
  136 
  137         bus_space_tag_t aps_iot;
  138         bus_space_handle_t aps_ioh;
  139 
  140         struct ksensor sensors[APS_NUM_SENSORS];
  141         struct ksensordev sensordev;
  142         void (*refresh_sensor_data)(struct aps_softc *);
  143 
  144         struct sensor_rec aps_data;
  145 };
  146 
  147 int      aps_match(struct device *, void *, void *);
  148 void     aps_attach(struct device *, struct device *, void *);
  149 int      aps_activate(struct device *, int);
  150 
  151 int      aps_init(bus_space_tag_t, bus_space_handle_t);
  152 int      aps_read_data(struct aps_softc *);
  153 void     aps_refresh_sensor_data(struct aps_softc *);
  154 void     aps_refresh(void *);
  155 int      aps_do_io(bus_space_tag_t, bus_space_handle_t,
  156                    unsigned char *, int, int);
  157 
  158 const struct cfattach aps_ca = {
  159         sizeof(struct aps_softc),
  160         aps_match, aps_attach, NULL, aps_activate
  161 };
  162 
  163 struct cfdriver aps_cd = {
  164         NULL, "aps", DV_DULL
  165 };
  166 
  167 struct timeout aps_timeout;
  168 
  169 
  170 
  171 /* properly communicate with the controller, writing a set of memory
  172  * locations and reading back another set  */
  173 int
  174 aps_do_io(bus_space_tag_t iot, bus_space_handle_t ioh,
  175           unsigned char *buf, int wmask, int rmask)
  176 {
  177         int bp, stat, n;
  178 
  179         DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
  180                buf[0], wmask, rmask));
  181 
  182         /* write init byte using arbitration */     
  183         for (n = 0; n < 100; n++) {
  184                 stat = bus_space_read_1(iot, ioh, APS_STR3);
  185                 if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
  186                         bus_space_read_1(iot, ioh, APS_TWR_RET);
  187                         continue;
  188                 }
  189                 bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
  190                 stat = bus_space_read_1(iot, ioh, APS_STR3);
  191                 if (stat & (APS_STR3_MWMF))
  192                         break;
  193                 delay(1);
  194         }
  195 
  196         if (n == 100) {
  197                 DPRINTF(("aps_do_io: Failed to get bus\n"));
  198                 return (1);
  199         }
  200 
  201         /* write data bytes, init already sent */
  202         /* make sure last bye is always written as this will trigger slave */
  203         wmask |= APS_READ_RET;
  204         buf[APS_RET] = 0x01;
  205 
  206         for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
  207                 if (wmask & bp) {
  208                         bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
  209                         DPRINTF(("aps_do_io:  write %2d 0x%02x\n", n, buf[n]));
  210                 }
  211         }
  212 
  213         for (n = 0; n < 100; n++) {
  214                 stat = bus_space_read_1(iot, ioh, APS_STR3);
  215                 if (stat & (APS_STR3_OBF3B))
  216                         break;
  217                 delay(5 * 100);
  218         }
  219 
  220         if (n == 100) {
  221                 DPRINTF(("aps_do_io: timeout waiting response\n"));
  222                 return (1);
  223         }
  224         /* wait for data available */
  225         /* make sure to read the final byte to clear status */
  226         rmask |= APS_READ_RET;
  227 
  228         /* read cmd and data bytes */
  229         for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
  230                 if (rmask & bp) {
  231                         buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
  232                         DPRINTF(("aps_do_io:  read %2d 0x%02x\n", n, buf[n]));
  233                 }
  234         }
  235 
  236         return (0);
  237 }
  238 
  239 int
  240 aps_match(struct device *parent, void *match, void *aux)
  241 {
  242         struct isa_attach_args *ia = aux;
  243         bus_space_tag_t iot = ia->ia_iot;
  244         bus_space_handle_t ioh;
  245         int iobase = ia->ipa_io[0].base;
  246         u_int8_t cr;
  247         unsigned char iobuf[16];
  248 
  249         if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
  250                 DPRINTF(("aps: can't map i/o space\n"));
  251                 return (0);
  252         }
  253         /* get APS mode */
  254         iobuf[APS_CMD] = 0x13;
  255         if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) {
  256                 bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
  257                 return (0);
  258         }
  259 
  260         /*
  261          * Observed values from Linux driver:
  262          * 0x01: T42
  263          * 0x02: chip already initialised
  264          * 0x03: T41
  265          * 0x05: T61
  266          */
  267 
  268         cr = iobuf[APS_ARG1];
  269         DPRINTF(("aps: state register 0x%x\n", cr));
  270 
  271         if (aps_init(iot, ioh)) {
  272                 bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
  273                 return (0);
  274         }
  275 
  276         bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
  277 
  278         if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
  279                 DPRINTF(("aps0: unsupported state %d\n", cr));
  280                 return (0);
  281         }
  282 
  283         ia->ipa_nio = 1;
  284         ia->ipa_io[0].length = APS_ADDR_SIZE;
  285         ia->ipa_nmem = 0;
  286         ia->ipa_nirq = 0;
  287         ia->ipa_ndrq = 0;
  288         return (1);
  289 }
  290 
  291 void
  292 aps_attach(struct device *parent, struct device *self, void *aux)
  293 {
  294         struct aps_softc *sc = (void *)self;
  295         int iobase, i;
  296         bus_space_tag_t iot;
  297         bus_space_handle_t ioh;
  298         struct isa_attach_args *ia = aux;
  299 
  300         iobase = ia->ipa_io[0].base;
  301         iot = sc->aps_iot = ia->ia_iot;
  302 
  303         if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) {
  304                 printf(": can't map i/o space\n");
  305                 return;
  306         }
  307 
  308         ioh = sc->aps_ioh;
  309 
  310         printf("\n");
  311 
  312         sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
  313         snprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
  314             sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
  315 
  316         sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
  317         snprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
  318             sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
  319 
  320         sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
  321         sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
  322 
  323         sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
  324         snprintf(sc->sensors[APS_SENSOR_XVAR].desc,
  325             sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
  326 
  327         sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
  328         snprintf(sc->sensors[APS_SENSOR_YVAR].desc,
  329             sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
  330 
  331         sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
  332         snprintf(sc->sensors[APS_SENSOR_KBACT].desc,
  333             sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
  334 
  335         sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
  336         snprintf(sc->sensors[APS_SENSOR_MSACT].desc,
  337             sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
  338 
  339         sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
  340         snprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
  341             sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
  342 
  343         /* stop hiding and report to the authorities */
  344         strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
  345             sizeof(sc->sensordev.xname));
  346         for (i = 0; i < APS_NUM_SENSORS ; i++) {
  347                 sensor_attach(&sc->sensordev, &sc->sensors[i]);
  348         }
  349         sensordev_install(&sc->sensordev);
  350 
  351         /* Refresh sensor data every 0.5 seconds */
  352         timeout_set(&aps_timeout, aps_refresh, sc);
  353         timeout_add_msec(&aps_timeout, 500);
  354 }
  355 
  356 int
  357 aps_init(bus_space_tag_t iot, bus_space_handle_t ioh)
  358 {
  359         unsigned char iobuf[16];
  360 
  361 
  362         /* command 0x17/0x81: check EC */
  363         iobuf[APS_CMD] = 0x17;
  364         iobuf[APS_ARG1] = 0x81;
  365 
  366         if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_3))
  367                 return (1);
  368 
  369         if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
  370                 return (2);
  371 
  372         /* Test values from the Linux driver */
  373         if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
  374             (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
  375                 return (3);
  376 
  377         /* command 0x14: set power */
  378         iobuf[APS_CMD] = 0x14;
  379         iobuf[APS_ARG1] = 0x01;
  380 
  381         if (aps_do_io(iot, ioh, iobuf, APS_WRITE_1, APS_READ_0))
  382                 return (4);
  383 
  384         if (iobuf[APS_RET] != 0)
  385                 return (5);
  386 
  387         /* command 0x10: set config (sample rate and order) */
  388         iobuf[APS_CMD] = 0x10;
  389         iobuf[APS_ARG1] = 0xc8;
  390         iobuf[APS_ARG2] = 0x00;
  391         iobuf[APS_ARG3] = 0x02;
  392 
  393         if (aps_do_io(iot, ioh, iobuf, APS_WRITE_3, APS_READ_0))
  394                 return (6);
  395 
  396         if (iobuf[APS_RET] != 0)
  397                 return (7);
  398 
  399         /* command 0x11: refresh data */
  400         iobuf[APS_CMD] = 0x11;
  401         if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1))
  402                 return (8);
  403 
  404         return (0);
  405 }
  406 
  407 int
  408 aps_read_data(struct aps_softc *sc)
  409 {
  410         bus_space_tag_t iot = sc->aps_iot;
  411         bus_space_handle_t ioh = sc->aps_ioh;
  412         unsigned char iobuf[16];
  413 
  414         /* command 0x11: refresh data */
  415         iobuf[APS_CMD] = 0x11;
  416         if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_ALL))
  417                 return (1);
  418 
  419         sc->aps_data.state = iobuf[APS_STATE];
  420         sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
  421         sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
  422         sc->aps_data.temp1 = iobuf[APS_TEMP];
  423         sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
  424         sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
  425         sc->aps_data.temp2 = iobuf[APS_TEMP2];
  426         sc->aps_data.input = iobuf[APS_INPUT];
  427 
  428         return (0);
  429 }
  430 
  431 void
  432 aps_refresh_sensor_data(struct aps_softc *sc)
  433 {
  434         int64_t temp;
  435         int i;
  436 #if NAPM > 0
  437         extern int lid_action;
  438         extern int apm_lidclose;
  439 #endif
  440 
  441         if (aps_read_data(sc))
  442                 return;
  443 
  444         for (i = 0; i < APS_NUM_SENSORS; i++) {
  445                 sc->sensors[i].flags &= ~SENSOR_FINVALID;
  446         }
  447 
  448         sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
  449         sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
  450 
  451         /* convert to micro (mu) degrees */
  452         temp = sc->aps_data.temp1 * 1000000;    
  453         /* convert to kelvin */
  454         temp += 273150000; 
  455         sc->sensors[APS_SENSOR_TEMP1].value = temp;
  456 
  457         /* convert to micro (mu) degrees */
  458         temp = sc->aps_data.temp2 * 1000000;    
  459         /* convert to kelvin */
  460         temp += 273150000; 
  461         sc->sensors[APS_SENSOR_TEMP2].value = temp;
  462 
  463         sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
  464         sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
  465         sc->sensors[APS_SENSOR_KBACT].value =
  466             (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
  467         sc->sensors[APS_SENSOR_MSACT].value =
  468             (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
  469 #if NAPM > 0
  470         if (lid_action &&
  471             (sc->sensors[APS_SENSOR_LIDOPEN].value == 1) &&
  472             (sc->aps_data.input & APS_INPUT_LIDOPEN) == 0)
  473                 /* Inform APM that the lid has closed */
  474                 apm_lidclose = 1;
  475 #endif
  476         sc->sensors[APS_SENSOR_LIDOPEN].value =
  477             (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
  478 }
  479 
  480 void
  481 aps_refresh(void *arg)
  482 {
  483         struct aps_softc *sc = (struct aps_softc *)arg;
  484 
  485         aps_refresh_sensor_data(sc);
  486         timeout_add_msec(&aps_timeout, 500);
  487 }
  488 
  489 int
  490 aps_activate(struct device *self, int act)
  491 {
  492         struct aps_softc *sc = (struct aps_softc *)self;
  493         bus_space_tag_t iot = sc->aps_iot;
  494         bus_space_handle_t ioh = sc->aps_ioh;
  495         unsigned char iobuf[16];
  496 
  497         /* check if we bombed during attach */
  498         if (!timeout_initialized(&aps_timeout))
  499                 return (0);
  500 
  501         switch (act) {
  502         case DVACT_SUSPEND:
  503                 timeout_del(&aps_timeout);
  504                 break;
  505         case DVACT_RESUME:
  506                 /*
  507                  * Redo the init sequence on resume, because APS is 
  508                  * as forgetful as it is deaf.
  509                  */
  510 
  511                 /* get APS mode */
  512                 iobuf[APS_CMD] = 0x13;
  513                 aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1);
  514 
  515                 aps_init(iot, ioh);
  516                 timeout_add_msec(&aps_timeout, 500);
  517                 break;
  518         }
  519         return (0);
  520 }

Cache object: e0a78e18f1a3c2e139ca47123fe7fbd6


[ 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.