root/dev/isa/aps.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. aps_match
  2. aps_attach
  3. aps_init
  4. aps_mem_read_1
  5. aps_read_data
  6. aps_refresh_sensor_data
  7. aps_refresh
  8. aps_power

    1 /*      $OpenBSD: aps.c,v 1.15 2007/05/19 19:14:11 tedu Exp $   */
    2 /*
    3  * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
    4  *
    5  * Permission to use, copy, modify, and distribute this software for any
    6  * purpose with or without fee is hereby granted, provided that the above
    7  * copyright notice and this permission notice appear in all copies.
    8  *
    9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   16  */
   17 
   18 /*
   19  * A driver for the ThinkPad Active Protection System based on notes from
   20  * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
   21  */
   22 
   23 #include <sys/param.h>
   24 #include <sys/systm.h>
   25 #include <sys/device.h>
   26 #include <sys/kernel.h>
   27 #include <sys/sensors.h>
   28 #include <sys/timeout.h>
   29 #include <machine/bus.h>
   30 
   31 #include <dev/isa/isareg.h>
   32 #include <dev/isa/isavar.h>
   33 
   34 #if defined(APSDEBUG)
   35 #define DPRINTF(x)              do { printf x; } while (0)
   36 #else
   37 #define DPRINTF(x)
   38 #endif
   39 
   40 #define APS_ACCEL_STATE         0x04
   41 #define APS_INIT                0x10
   42 #define APS_STATE               0x11
   43 #define APS_XACCEL              0x12
   44 #define APS_YACCEL              0x14
   45 #define APS_TEMP                0x16
   46 #define APS_XVAR                0x17
   47 #define APS_YVAR                0x19
   48 #define APS_TEMP2               0x1b
   49 #define APS_UNKNOWN             0x1c
   50 #define APS_INPUT               0x1d
   51 #define APS_CMD                 0x1f
   52 
   53 #define APS_STATE_NEWDATA       0x50
   54 
   55 #define APS_CMD_START           0x01
   56 
   57 #define APS_INPUT_KB            (1 << 5)
   58 #define APS_INPUT_MS            (1 << 6)
   59 #define APS_INPUT_LIDOPEN       (1 << 7)
   60 
   61 #define APS_ADDR_SIZE           0x1f
   62 
   63 struct sensor_rec {
   64         u_int8_t        state;
   65         u_int16_t       x_accel;
   66         u_int16_t       y_accel;
   67         u_int8_t        temp1;
   68         u_int16_t       x_var;
   69         u_int16_t       y_var;
   70         u_int8_t        temp2;
   71         u_int8_t        unk;
   72         u_int8_t        input;
   73 };
   74 
   75 #define APS_NUM_SENSORS         9
   76 
   77 #define APS_SENSOR_XACCEL       0
   78 #define APS_SENSOR_YACCEL       1
   79 #define APS_SENSOR_XVAR         2
   80 #define APS_SENSOR_YVAR         3
   81 #define APS_SENSOR_TEMP1        4
   82 #define APS_SENSOR_TEMP2        5
   83 #define APS_SENSOR_KBACT        6
   84 #define APS_SENSOR_MSACT        7
   85 #define APS_SENSOR_LIDOPEN      8
   86 
   87 struct aps_softc {
   88         struct device sc_dev;
   89 
   90         bus_space_tag_t aps_iot;
   91         bus_space_handle_t aps_ioh;
   92 
   93         struct ksensor sensors[APS_NUM_SENSORS];
   94         struct ksensordev sensordev;
   95         void (*refresh_sensor_data)(struct aps_softc *);
   96 
   97         struct sensor_rec aps_data;
   98 };
   99 
  100 int      aps_match(struct device *, void *, void *);
  101 void     aps_attach(struct device *, struct device *, void *);
  102 
  103 int      aps_init(bus_space_tag_t, bus_space_handle_t);
  104 u_int8_t aps_mem_read_1(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
  105 int      aps_read_data(struct aps_softc *);
  106 void     aps_refresh_sensor_data(struct aps_softc *sc);
  107 void     aps_refresh(void *);
  108 void     aps_power(int, void *);
  109 
  110 struct cfattach aps_ca = {
  111         sizeof(struct aps_softc),
  112         aps_match,
  113         aps_attach
  114 };
  115 
  116 struct cfdriver aps_cd = {
  117         NULL, "aps", DV_DULL
  118 };
  119 
  120 struct timeout aps_timeout;
  121 
  122 int
  123 aps_match(struct device *parent, void *match, void *aux)
  124 {
  125         bus_space_tag_t iot;
  126         bus_space_handle_t ioh;
  127         struct isa_attach_args *ia = aux;
  128         int iobase, i;
  129         u_int8_t cr;
  130 
  131         iot = ia->ia_iot;
  132         iobase = ia->ipa_io[0].base;
  133 
  134         if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
  135                 DPRINTF(("aps: can't map i/o space\n"));
  136                 return (0);
  137         }
  138 
  139         /* See if this machine has APS */
  140         bus_space_write_1(iot, ioh, APS_INIT, 0x13);
  141         bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  142 
  143         /* ask again as the X40 is slightly deaf in one ear */
  144         bus_space_read_1(iot, ioh, APS_CMD);
  145         bus_space_write_1(iot, ioh, APS_INIT, 0x13);
  146         bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  147 
  148         if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00)) {
  149                 bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
  150                 return (0);
  151         }
  152 
  153         /*
  154          * Observed values from Linux driver:
  155          * 0x01: T42
  156          * 0x02: chip already initialised
  157          * 0x03: T41
  158          */
  159         for (i = 0; i < 10; i++) {
  160                 cr = bus_space_read_1(iot, ioh, APS_STATE);
  161                 if (cr > 0 && cr < 6)
  162                         break;
  163                 delay(5 * 1000);
  164         }
  165         
  166         bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
  167         DPRINTF(("aps: state register 0x%x\n", cr));
  168         if (cr < 1 || cr > 5) {
  169                 DPRINTF(("aps0: unsupported state %d\n", cr));
  170                 return (0);
  171         }
  172 
  173         ia->ipa_nio = 1;
  174         ia->ipa_io[0].length = APS_ADDR_SIZE;
  175         ia->ipa_nmem = 0;
  176         ia->ipa_nirq = 0;
  177         ia->ipa_ndrq = 0;
  178 
  179         return (1);
  180 }
  181 
  182 void
  183 aps_attach(struct device *parent, struct device *self, void *aux)
  184 {
  185         struct aps_softc *sc = (void *)self;
  186         int iobase, i;
  187         bus_space_tag_t iot;
  188         bus_space_handle_t ioh;
  189         struct isa_attach_args *ia = aux;
  190 
  191         iobase = ia->ipa_io[0].base;
  192         iot = sc->aps_iot = ia->ia_iot;
  193 
  194         if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &sc->aps_ioh)) {
  195                 printf(": can't map i/o space\n");
  196                 return;
  197         }
  198 
  199         ioh = sc->aps_ioh;
  200 
  201         printf("\n");
  202 
  203         if (!aps_init(iot, ioh))
  204                 goto out;
  205 
  206         sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
  207         snprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
  208             sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
  209 
  210         sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
  211         snprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
  212             sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
  213 
  214         sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
  215         sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
  216 
  217         sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
  218         snprintf(sc->sensors[APS_SENSOR_XVAR].desc,
  219             sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
  220 
  221         sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
  222         snprintf(sc->sensors[APS_SENSOR_YVAR].desc,
  223             sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
  224 
  225         sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
  226         snprintf(sc->sensors[APS_SENSOR_KBACT].desc,
  227             sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
  228 
  229         sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
  230         snprintf(sc->sensors[APS_SENSOR_MSACT].desc,
  231             sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
  232 
  233         sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
  234         snprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
  235             sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
  236 
  237         /* stop hiding and report to the authorities */
  238         strlcpy(sc->sensordev.xname, sc->sc_dev.dv_xname,
  239             sizeof(sc->sensordev.xname));
  240         for (i = 0; i < APS_NUM_SENSORS ; i++) {
  241                 sensor_attach(&sc->sensordev, &sc->sensors[i]);
  242         }
  243         sensordev_install(&sc->sensordev);
  244 
  245         powerhook_establish(aps_power, (void *)sc);
  246 
  247         /* Refresh sensor data every 0.5 seconds */
  248         timeout_set(&aps_timeout, aps_refresh, sc);
  249         timeout_add(&aps_timeout, (5 * hz) / 10);
  250         return;
  251 out:
  252         printf("%s: failed to initialise\n", sc->sc_dev.dv_xname);
  253         return;
  254 }
  255 
  256 int
  257 aps_init(bus_space_tag_t iot, bus_space_handle_t ioh)
  258 {
  259         bus_space_write_1(iot, ioh, APS_INIT, 0x17);
  260         bus_space_write_1(iot, ioh, APS_STATE, 0x81);
  261         bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  262         if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
  263                 return (0);
  264         if (!aps_mem_read_1(iot, ioh, APS_STATE, 0x00))
  265                 return (0);
  266         if (!aps_mem_read_1(iot, ioh, APS_XACCEL, 0x60))
  267                 return (0);
  268         if (!aps_mem_read_1(iot, ioh, APS_XACCEL + 1, 0x00))
  269                 return (0);
  270         bus_space_write_1(iot, ioh, APS_INIT, 0x14);
  271         bus_space_write_1(iot, ioh, APS_STATE, 0x01);
  272         bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  273         if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
  274                 return (0);
  275         bus_space_write_1(iot, ioh, APS_INIT, 0x10);
  276         bus_space_write_1(iot, ioh, APS_STATE, 0xc8);
  277         bus_space_write_1(iot, ioh, APS_XACCEL, 0x00);
  278         bus_space_write_1(iot, ioh, APS_XACCEL + 1, 0x02);
  279         bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  280         if (!aps_mem_read_1(iot, ioh, APS_CMD, 0x00))
  281                 return (0);
  282         /* refresh data */
  283         bus_space_write_1(iot, ioh, APS_INIT, 0x11);
  284         bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  285         if (!aps_mem_read_1(iot, ioh, APS_ACCEL_STATE, 0x50))
  286                 return (0);
  287         if (!aps_mem_read_1(iot, ioh, APS_STATE, 0x00))
  288                 return (0);
  289 
  290         return (1);
  291 }
  292 
  293 u_int8_t
  294 aps_mem_read_1(bus_space_tag_t iot, bus_space_handle_t ioh, int reg,
  295     u_int8_t val)
  296 {
  297         int i;
  298         u_int8_t cr;
  299         /* should take no longer than 50 microseconds */
  300         for (i = 0; i < 10; i++) {
  301                 cr = bus_space_read_1(iot, ioh, reg);
  302                 if (cr == val)
  303                         return (1);
  304                 delay(5 * 1000);
  305         }
  306         DPRINTF(("aps: reg 0x%x not val 0x%x!\n", reg, val));
  307         return (0);
  308 }
  309 
  310 int
  311 aps_read_data(struct aps_softc *sc)
  312 {
  313         bus_space_tag_t iot = sc->aps_iot;
  314         bus_space_handle_t ioh = sc->aps_ioh;
  315 
  316         sc->aps_data.state = bus_space_read_1(iot, ioh, APS_STATE);
  317         sc->aps_data.x_accel = bus_space_read_2(iot, ioh, APS_XACCEL);
  318         sc->aps_data.y_accel = bus_space_read_2(iot, ioh, APS_YACCEL);
  319         sc->aps_data.temp1 = bus_space_read_1(iot, ioh, APS_TEMP);
  320         sc->aps_data.x_var = bus_space_read_2(iot, ioh, APS_XVAR);
  321         sc->aps_data.y_var = bus_space_read_2(iot, ioh, APS_YVAR);
  322         sc->aps_data.temp2 = bus_space_read_1(iot, ioh, APS_TEMP2);
  323         sc->aps_data.input = bus_space_read_1(iot, ioh, APS_INPUT);
  324 
  325         return (1);
  326 }
  327 
  328 void
  329 aps_refresh_sensor_data(struct aps_softc *sc)
  330 {
  331         bus_space_tag_t iot = sc->aps_iot;
  332         bus_space_handle_t ioh = sc->aps_ioh;
  333         int64_t temp;
  334         int i;
  335 
  336         /* ask for new data */
  337         bus_space_write_1(iot, ioh, APS_INIT, 0x11);
  338         bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  339         if (!aps_mem_read_1(iot, ioh, APS_ACCEL_STATE, 0x50))
  340                 return;
  341         aps_read_data(sc);
  342         bus_space_write_1(iot, ioh, APS_INIT, 0x11);
  343         bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  344 
  345         /* tell accelerometer we're done reading from it */
  346         bus_space_read_1(iot, ioh, APS_CMD);
  347         bus_space_read_1(iot, ioh, APS_ACCEL_STATE);
  348 
  349         for (i = 0; i < APS_NUM_SENSORS; i++) {
  350                 sc->sensors[i].flags &= ~SENSOR_FINVALID;
  351         }
  352 
  353         sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
  354         sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
  355 
  356         /* convert to micro (mu) degrees */
  357         temp = sc->aps_data.temp1 * 1000000;    
  358         /* convert to kelvin */
  359         temp += 273150000; 
  360         sc->sensors[APS_SENSOR_TEMP1].value = temp;
  361 
  362         /* convert to micro (mu) degrees */
  363         temp = sc->aps_data.temp2 * 1000000;    
  364         /* convert to kelvin */
  365         temp += 273150000; 
  366         sc->sensors[APS_SENSOR_TEMP2].value = temp;
  367 
  368         sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
  369         sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
  370         sc->sensors[APS_SENSOR_KBACT].value =
  371             (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
  372         sc->sensors[APS_SENSOR_MSACT].value =
  373             (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
  374         sc->sensors[APS_SENSOR_LIDOPEN].value =
  375             (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
  376 }
  377 
  378 void
  379 aps_refresh(void *arg)
  380 {
  381         struct aps_softc *sc = (struct aps_softc *)arg;
  382 
  383         aps_refresh_sensor_data(sc);
  384         timeout_add(&aps_timeout, (5 * hz) / 10);
  385 }
  386 
  387 void
  388 aps_power(int why, void *arg)
  389 {
  390         struct aps_softc *sc = (struct aps_softc *)arg;
  391         bus_space_tag_t iot = sc->aps_iot;
  392         bus_space_handle_t ioh = sc->aps_ioh;
  393 
  394         if (why != PWR_RESUME) {
  395                 if (timeout_pending(&aps_timeout))
  396                         timeout_del(&aps_timeout);
  397         } else {
  398                 /*
  399                  * Redo the init sequence on resume, because APS is 
  400                  * as forgetful as it is deaf.
  401                  */
  402                 bus_space_write_1(iot, ioh, APS_INIT, 0x13);
  403                 bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  404                 bus_space_read_1(iot, ioh, APS_CMD);
  405                 bus_space_write_1(iot, ioh, APS_INIT, 0x13);
  406                 bus_space_write_1(iot, ioh, APS_CMD, 0x01);
  407         
  408                 if (aps_mem_read_1(iot, ioh, APS_CMD, 0x00) &&
  409                     aps_init(iot, ioh))
  410                         timeout_add(&aps_timeout, (5 * hz) / 10);
  411                 else
  412                         printf("aps: failed to wake up\n");
  413         }
  414 }
  415 

/* [<][>][^][v][top][bottom][index][help] */