10d06db5aSConstantine A. Murenin /* $OpenBSD: aps.c,v 1.19 2009/05/24 16:40:18 jsg Exp $ */
20d06db5aSConstantine A. Murenin /*
30d06db5aSConstantine A. Murenin * Copyright (c) 2005 Jonathan Gray <jsg@openbsd.org>
40d06db5aSConstantine A. Murenin * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
523e32507SConstantine A. Murenin * Copyright (c) 2010 Constantine A. Murenin <cnst++@dragonflybsd.org>
60d06db5aSConstantine A. Murenin *
70d06db5aSConstantine A. Murenin * Permission to use, copy, modify, and distribute this software for any
80d06db5aSConstantine A. Murenin * purpose with or without fee is hereby granted, provided that the above
90d06db5aSConstantine A. Murenin * copyright notice and this permission notice appear in all copies.
100d06db5aSConstantine A. Murenin *
110d06db5aSConstantine A. Murenin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
120d06db5aSConstantine A. Murenin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
130d06db5aSConstantine A. Murenin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
140d06db5aSConstantine A. Murenin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
150d06db5aSConstantine A. Murenin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
160d06db5aSConstantine A. Murenin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
170d06db5aSConstantine A. Murenin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
180d06db5aSConstantine A. Murenin */
190d06db5aSConstantine A. Murenin
200d06db5aSConstantine A. Murenin /*
210d06db5aSConstantine A. Murenin * A driver for the ThinkPad Active Protection System based on notes from
220d06db5aSConstantine A. Murenin * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
230d06db5aSConstantine A. Murenin */
240d06db5aSConstantine A. Murenin
250d06db5aSConstantine A. Murenin #include <sys/param.h>
260d06db5aSConstantine A. Murenin #include <sys/systm.h>
2723e32507SConstantine A. Murenin #include <sys/bus.h>
28017d4aa0SConstantine A. Murenin #include <sys/kernel.h>
29017d4aa0SConstantine A. Murenin #include <sys/module.h>
3023e32507SConstantine A. Murenin #include <sys/rman.h>
31017d4aa0SConstantine A. Murenin #include <sys/sensors.h>
3223e32507SConstantine A. Murenin
3323e32507SConstantine A. Murenin #include <bus/isa/isavar.h>
340d06db5aSConstantine A. Murenin
350d06db5aSConstantine A. Murenin #if defined(APSDEBUG)
3623e32507SConstantine A. Murenin #define DPRINTF(x) do { kprintf x; } while (0)
370d06db5aSConstantine A. Murenin #else
380d06db5aSConstantine A. Murenin #define DPRINTF(x)
390d06db5aSConstantine A. Murenin #endif
400d06db5aSConstantine A. Murenin
410d06db5aSConstantine A. Murenin
420d06db5aSConstantine A. Murenin /*
430d06db5aSConstantine A. Murenin * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
440d06db5aSConstantine A. Murenin * From Renesans H8S/2140B Group Hardware Manual
450d06db5aSConstantine A. Murenin * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
460d06db5aSConstantine A. Murenin *
470d06db5aSConstantine A. Murenin * EC uses LPC Channel 3 registers TWR0..15
480d06db5aSConstantine A. Murenin */
490d06db5aSConstantine A. Murenin
500d06db5aSConstantine A. Murenin /* STR3 status register */
510d06db5aSConstantine A. Murenin #define APS_STR3 0x04
520d06db5aSConstantine A. Murenin
530d06db5aSConstantine A. Murenin #define APS_STR3_IBF3B 0x80 /* Input buffer full (host->slave) */
540d06db5aSConstantine A. Murenin #define APS_STR3_OBF3B 0x40 /* Output buffer full (slave->host)*/
550d06db5aSConstantine A. Murenin #define APS_STR3_MWMF 0x20 /* Master write mode */
560d06db5aSConstantine A. Murenin #define APS_STR3_SWMF 0x10 /* Slave write mode */
570d06db5aSConstantine A. Murenin
580d06db5aSConstantine A. Murenin
590d06db5aSConstantine A. Murenin /* Base address of TWR registers */
600d06db5aSConstantine A. Murenin #define APS_TWR_BASE 0x10
610d06db5aSConstantine A. Murenin #define APS_TWR_RET 0x1f
620d06db5aSConstantine A. Murenin
630d06db5aSConstantine A. Murenin /* TWR registers */
640d06db5aSConstantine A. Murenin #define APS_CMD 0x00
650d06db5aSConstantine A. Murenin #define APS_ARG1 0x01
660d06db5aSConstantine A. Murenin #define APS_ARG2 0x02
670d06db5aSConstantine A. Murenin #define APS_ARG3 0x03
680d06db5aSConstantine A. Murenin #define APS_RET 0x0f
690d06db5aSConstantine A. Murenin
700d06db5aSConstantine A. Murenin /* Sensor values */
710d06db5aSConstantine A. Murenin #define APS_STATE 0x01
720d06db5aSConstantine A. Murenin #define APS_XACCEL 0x02
730d06db5aSConstantine A. Murenin #define APS_YACCEL 0x04
740d06db5aSConstantine A. Murenin #define APS_TEMP 0x06
750d06db5aSConstantine A. Murenin #define APS_XVAR 0x07
760d06db5aSConstantine A. Murenin #define APS_YVAR 0x09
770d06db5aSConstantine A. Murenin #define APS_TEMP2 0x0b
780d06db5aSConstantine A. Murenin #define APS_UNKNOWN 0x0c
790d06db5aSConstantine A. Murenin #define APS_INPUT 0x0d
800d06db5aSConstantine A. Murenin
810d06db5aSConstantine A. Murenin /* write masks for I/O, send command + 0-3 arguments*/
820d06db5aSConstantine A. Murenin #define APS_WRITE_0 0x0001
830d06db5aSConstantine A. Murenin #define APS_WRITE_1 0x0003
840d06db5aSConstantine A. Murenin #define APS_WRITE_2 0x0007
850d06db5aSConstantine A. Murenin #define APS_WRITE_3 0x000f
860d06db5aSConstantine A. Murenin
870d06db5aSConstantine A. Murenin /* read masks for I/O, read 0-3 values (skip command byte) */
880d06db5aSConstantine A. Murenin #define APS_READ_0 0x0000
890d06db5aSConstantine A. Murenin #define APS_READ_1 0x0002
900d06db5aSConstantine A. Murenin #define APS_READ_2 0x0006
910d06db5aSConstantine A. Murenin #define APS_READ_3 0x000e
920d06db5aSConstantine A. Murenin
930d06db5aSConstantine A. Murenin #define APS_READ_RET 0x8000
940d06db5aSConstantine A. Murenin #define APS_READ_ALL 0xffff
950d06db5aSConstantine A. Murenin
960d06db5aSConstantine A. Murenin /* Bit definitions for APS_INPUT value */
970d06db5aSConstantine A. Murenin #define APS_INPUT_KB (1 << 5)
980d06db5aSConstantine A. Murenin #define APS_INPUT_MS (1 << 6)
990d06db5aSConstantine A. Murenin #define APS_INPUT_LIDOPEN (1 << 7)
1000d06db5aSConstantine A. Murenin
10123e32507SConstantine A. Murenin #define APS_ADDR_BASE 0x1600
1020d06db5aSConstantine A. Murenin #define APS_ADDR_SIZE 0x1f
1030d06db5aSConstantine A. Murenin
10423e32507SConstantine A. Murenin struct aps_sensor_rec {
1050d06db5aSConstantine A. Murenin u_int8_t state;
1060d06db5aSConstantine A. Murenin u_int16_t x_accel;
1070d06db5aSConstantine A. Murenin u_int16_t y_accel;
1080d06db5aSConstantine A. Murenin u_int8_t temp1;
1090d06db5aSConstantine A. Murenin u_int16_t x_var;
1100d06db5aSConstantine A. Murenin u_int16_t y_var;
1110d06db5aSConstantine A. Murenin u_int8_t temp2;
1120d06db5aSConstantine A. Murenin u_int8_t unk;
1130d06db5aSConstantine A. Murenin u_int8_t input;
1140d06db5aSConstantine A. Murenin };
1150d06db5aSConstantine A. Murenin
1160d06db5aSConstantine A. Murenin #define APS_NUM_SENSORS 9
1170d06db5aSConstantine A. Murenin
1180d06db5aSConstantine A. Murenin #define APS_SENSOR_XACCEL 0
1190d06db5aSConstantine A. Murenin #define APS_SENSOR_YACCEL 1
1200d06db5aSConstantine A. Murenin #define APS_SENSOR_XVAR 2
1210d06db5aSConstantine A. Murenin #define APS_SENSOR_YVAR 3
1220d06db5aSConstantine A. Murenin #define APS_SENSOR_TEMP1 4
1230d06db5aSConstantine A. Murenin #define APS_SENSOR_TEMP2 5
1240d06db5aSConstantine A. Murenin #define APS_SENSOR_KBACT 6
1250d06db5aSConstantine A. Murenin #define APS_SENSOR_MSACT 7
1260d06db5aSConstantine A. Murenin #define APS_SENSOR_LIDOPEN 8
1270d06db5aSConstantine A. Murenin
1280d06db5aSConstantine A. Murenin struct aps_softc {
129*5d302545SFrançois Tigeot device_t sc_dev;
1300d06db5aSConstantine A. Murenin
13123e32507SConstantine A. Murenin struct resource *sc_iores;
13223e32507SConstantine A. Murenin int sc_iorid;
1330d06db5aSConstantine A. Murenin
1340d06db5aSConstantine A. Murenin struct ksensor sensors[APS_NUM_SENSORS];
1350d06db5aSConstantine A. Murenin struct ksensordev sensordev;
1360d06db5aSConstantine A. Murenin
13723e32507SConstantine A. Murenin struct aps_sensor_rec aps_data;
1380d06db5aSConstantine A. Murenin };
1390d06db5aSConstantine A. Murenin
140*5d302545SFrançois Tigeot static void aps_identify(driver_t *, device_t);
141*5d302545SFrançois Tigeot static int aps_probe(device_t);
142*5d302545SFrançois Tigeot static int aps_attach(device_t);
143*5d302545SFrançois Tigeot static int aps_detach(device_t);
1440d06db5aSConstantine A. Murenin
145*5d302545SFrançois Tigeot static int aps_resume(device_t);
146*5d302545SFrançois Tigeot static int aps_suspend(device_t);
1470d06db5aSConstantine A. Murenin
14823e32507SConstantine A. Murenin static int aps_init(struct resource *);
14923e32507SConstantine A. Murenin static int aps_read_data(struct aps_softc *);
15023e32507SConstantine A. Murenin static void aps_refresh_sensor_data(struct aps_softc *);
15123e32507SConstantine A. Murenin static void aps_refresh(void *);
15223e32507SConstantine A. Murenin static int aps_do_io(struct resource *, unsigned char *, int, int);
15323e32507SConstantine A. Murenin
15423e32507SConstantine A. Murenin static device_method_t aps_methods[] = {
155017d4aa0SConstantine A. Murenin DEVMETHOD(device_identify, aps_identify),
15623e32507SConstantine A. Murenin DEVMETHOD(device_probe, aps_probe),
15723e32507SConstantine A. Murenin DEVMETHOD(device_attach, aps_attach),
15823e32507SConstantine A. Murenin DEVMETHOD(device_detach, aps_detach),
15923e32507SConstantine A. Murenin
16023e32507SConstantine A. Murenin DEVMETHOD(device_resume, aps_resume),
16123e32507SConstantine A. Murenin DEVMETHOD(device_suspend, aps_suspend),
16223e32507SConstantine A. Murenin { NULL, NULL }
1630d06db5aSConstantine A. Murenin };
1640d06db5aSConstantine A. Murenin
16523e32507SConstantine A. Murenin static driver_t aps_driver = {
16623e32507SConstantine A. Murenin "aps",
16723e32507SConstantine A. Murenin aps_methods,
16823e32507SConstantine A. Murenin sizeof(struct aps_softc)
1690d06db5aSConstantine A. Murenin };
1700d06db5aSConstantine A. Murenin
17123e32507SConstantine A. Murenin static devclass_t aps_devclass;
17223e32507SConstantine A. Murenin
17323e32507SConstantine A. Murenin DRIVER_MODULE(aps, isa, aps_driver, aps_devclass, NULL, NULL);
1740d06db5aSConstantine A. Murenin
1750d06db5aSConstantine A. Murenin
1760d06db5aSConstantine A. Murenin
1770d06db5aSConstantine A. Murenin /* properly communicate with the controller, writing a set of memory
1780d06db5aSConstantine A. Murenin * locations and reading back another set */
17923e32507SConstantine A. Murenin static int
aps_do_io(struct resource * iores,unsigned char * buf,int wmask,int rmask)18023e32507SConstantine A. Murenin aps_do_io(struct resource *iores, unsigned char *buf, int wmask, int rmask)
1810d06db5aSConstantine A. Murenin {
18223e32507SConstantine A. Murenin bus_space_tag_t iot = rman_get_bustag(iores);
18323e32507SConstantine A. Murenin bus_space_handle_t ioh = rman_get_bushandle(iores);
1840d06db5aSConstantine A. Murenin int bp, stat, n;
1850d06db5aSConstantine A. Murenin
1860d06db5aSConstantine A. Murenin DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
1870d06db5aSConstantine A. Murenin buf[0], wmask, rmask));
1880d06db5aSConstantine A. Murenin
1890d06db5aSConstantine A. Murenin /* write init byte using arbitration */
1900d06db5aSConstantine A. Murenin for (n = 0; n < 100; n++) {
1910d06db5aSConstantine A. Murenin stat = bus_space_read_1(iot, ioh, APS_STR3);
1920d06db5aSConstantine A. Murenin if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
1930d06db5aSConstantine A. Murenin bus_space_read_1(iot, ioh, APS_TWR_RET);
1940d06db5aSConstantine A. Murenin continue;
1950d06db5aSConstantine A. Murenin }
1960d06db5aSConstantine A. Murenin bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
1970d06db5aSConstantine A. Murenin stat = bus_space_read_1(iot, ioh, APS_STR3);
1980d06db5aSConstantine A. Murenin if (stat & (APS_STR3_MWMF))
1990d06db5aSConstantine A. Murenin break;
200333f47f2SConstantine A. Murenin DRIVERSLEEP(1);
2010d06db5aSConstantine A. Murenin }
2020d06db5aSConstantine A. Murenin
2030d06db5aSConstantine A. Murenin if (n == 100) {
2040d06db5aSConstantine A. Murenin DPRINTF(("aps_do_io: Failed to get bus\n"));
2050d06db5aSConstantine A. Murenin return (1);
2060d06db5aSConstantine A. Murenin }
2070d06db5aSConstantine A. Murenin
2080d06db5aSConstantine A. Murenin /* write data bytes, init already sent */
2090d06db5aSConstantine A. Murenin /* make sure last bye is always written as this will trigger slave */
2100d06db5aSConstantine A. Murenin wmask |= APS_READ_RET;
2110d06db5aSConstantine A. Murenin buf[APS_RET] = 0x01;
2120d06db5aSConstantine A. Murenin
2130d06db5aSConstantine A. Murenin for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
2140d06db5aSConstantine A. Murenin if (wmask & bp) {
2150d06db5aSConstantine A. Murenin bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
2160d06db5aSConstantine A. Murenin DPRINTF(("aps_do_io: write %2d 0x%02x\n", n, buf[n]));
2170d06db5aSConstantine A. Murenin }
2180d06db5aSConstantine A. Murenin }
2190d06db5aSConstantine A. Murenin
2200d06db5aSConstantine A. Murenin for (n = 0; n < 100; n++) {
2210d06db5aSConstantine A. Murenin stat = bus_space_read_1(iot, ioh, APS_STR3);
2220d06db5aSConstantine A. Murenin if (stat & (APS_STR3_OBF3B))
2230d06db5aSConstantine A. Murenin break;
224333f47f2SConstantine A. Murenin DRIVERSLEEP(500);
2250d06db5aSConstantine A. Murenin }
2260d06db5aSConstantine A. Murenin
2270d06db5aSConstantine A. Murenin if (n == 100) {
2280d06db5aSConstantine A. Murenin DPRINTF(("aps_do_io: timeout waiting response\n"));
2290d06db5aSConstantine A. Murenin return (1);
2300d06db5aSConstantine A. Murenin }
2310d06db5aSConstantine A. Murenin /* wait for data available */
2320d06db5aSConstantine A. Murenin /* make sure to read the final byte to clear status */
2330d06db5aSConstantine A. Murenin rmask |= APS_READ_RET;
2340d06db5aSConstantine A. Murenin
2350d06db5aSConstantine A. Murenin /* read cmd and data bytes */
2360d06db5aSConstantine A. Murenin for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
2370d06db5aSConstantine A. Murenin if (rmask & bp) {
2380d06db5aSConstantine A. Murenin buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
2390d06db5aSConstantine A. Murenin DPRINTF(("aps_do_io: read %2d 0x%02x\n", n, buf[n]));
2400d06db5aSConstantine A. Murenin }
2410d06db5aSConstantine A. Murenin }
2420d06db5aSConstantine A. Murenin
2430d06db5aSConstantine A. Murenin return (0);
2440d06db5aSConstantine A. Murenin }
2450d06db5aSConstantine A. Murenin
246017d4aa0SConstantine A. Murenin /* for hints, see /sys/bus/isa/isahint.c */
247017d4aa0SConstantine A. Murenin
248017d4aa0SConstantine A. Murenin static void
aps_identify(driver_t * driver,device_t parent)249*5d302545SFrançois Tigeot aps_identify(driver_t *driver, device_t parent)
250017d4aa0SConstantine A. Murenin {
251*5d302545SFrançois Tigeot device_t child;
252017d4aa0SConstantine A. Murenin
253017d4aa0SConstantine A. Murenin child = device_find_child(parent, driver->name, -1);
254017d4aa0SConstantine A. Murenin if (child != NULL) {
255017d4aa0SConstantine A. Murenin if (isa_get_portsize(child) == 0) {
256017d4aa0SConstantine A. Murenin // aps(4) must have been compiled into the kernel
257017d4aa0SConstantine A. Murenin if (bootverbose)
258017d4aa0SConstantine A. Murenin kprintf("%s: will specify the port\n",
259017d4aa0SConstantine A. Murenin __func__);
260017d4aa0SConstantine A. Murenin } else if (isa_get_port(child) != APS_ADDR_BASE)
261017d4aa0SConstantine A. Murenin kprintf("%s: will overwrite specified port\n",
262017d4aa0SConstantine A. Murenin __func__);
263017d4aa0SConstantine A. Murenin else {
264017d4aa0SConstantine A. Murenin if (isa_get_portsize(child) == APS_ADDR_SIZE) {
265017d4aa0SConstantine A. Murenin // aps.ko must have been reloaded
266017d4aa0SConstantine A. Murenin kprintf("%s: already have been invoked\n",
267017d4aa0SConstantine A. Murenin __func__);
268017d4aa0SConstantine A. Murenin return;
269017d4aa0SConstantine A. Murenin } else
270017d4aa0SConstantine A. Murenin kprintf("%s: will amend the portsize\n",
271017d4aa0SConstantine A. Murenin __func__);
272017d4aa0SConstantine A. Murenin }
273017d4aa0SConstantine A. Murenin } else {
274017d4aa0SConstantine A. Murenin // first invocation of `kldload aps.ko`
275017d4aa0SConstantine A. Murenin kprintf("%s: creating a new %s\n",
276017d4aa0SConstantine A. Murenin __func__, driver->name);
277017d4aa0SConstantine A. Murenin child = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP,
278017d4aa0SConstantine A. Murenin driver->name, -1);
279017d4aa0SConstantine A. Murenin if (child == NULL) {
280017d4aa0SConstantine A. Murenin kprintf("%s: cannot add child\n", __func__);
281017d4aa0SConstantine A. Murenin return;
282017d4aa0SConstantine A. Murenin }
283017d4aa0SConstantine A. Murenin }
284017d4aa0SConstantine A. Murenin if (bus_set_resource(child, SYS_RES_IOPORT, 0,
285b47b3275SSepherosa Ziehau APS_ADDR_BASE, APS_ADDR_SIZE, -1))
286017d4aa0SConstantine A. Murenin kprintf("%s: cannot set resource\n", __func__);
287017d4aa0SConstantine A. Murenin }
288017d4aa0SConstantine A. Murenin
28923e32507SConstantine A. Murenin static int
aps_probe(device_t dev)290*5d302545SFrançois Tigeot aps_probe(device_t dev)
2910d06db5aSConstantine A. Murenin {
29223e32507SConstantine A. Murenin struct resource *iores;
29323e32507SConstantine A. Murenin int iorid = 0;
2940d06db5aSConstantine A. Murenin u_int8_t cr;
2950d06db5aSConstantine A. Murenin unsigned char iobuf[16];
2960d06db5aSConstantine A. Murenin
29723e32507SConstantine A. Murenin #if defined(APSDEBUG) || defined(KLD_MODULE)
298017d4aa0SConstantine A. Murenin device_printf(dev, "%s: port 0x%x\n", __func__, isa_get_port(dev));
29923e32507SConstantine A. Murenin #endif
3000d06db5aSConstantine A. Murenin
30123e32507SConstantine A. Murenin if (device_get_unit(dev) != 0)
30223e32507SConstantine A. Murenin return ENXIO;
30323e32507SConstantine A. Murenin
304017d4aa0SConstantine A. Murenin iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &iorid, RF_ACTIVE);
30523e32507SConstantine A. Murenin if (iores == NULL) {
3060d06db5aSConstantine A. Murenin DPRINTF(("aps: can't map i/o space\n"));
30723e32507SConstantine A. Murenin return ENXIO;
3080d06db5aSConstantine A. Murenin }
3090d06db5aSConstantine A. Murenin
3100d06db5aSConstantine A. Murenin
3110d06db5aSConstantine A. Murenin /* See if this machine has APS */
3120d06db5aSConstantine A. Murenin
3130d06db5aSConstantine A. Murenin /* get APS mode */
3140d06db5aSConstantine A. Murenin iobuf[APS_CMD] = 0x13;
31523e32507SConstantine A. Murenin if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1)) {
31623e32507SConstantine A. Murenin bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
31723e32507SConstantine A. Murenin return ENXIO;
3180d06db5aSConstantine A. Murenin }
3190d06db5aSConstantine A. Murenin
3200d06db5aSConstantine A. Murenin /*
3210d06db5aSConstantine A. Murenin * Observed values from Linux driver:
3220d06db5aSConstantine A. Murenin * 0x01: T42
3230d06db5aSConstantine A. Murenin * 0x02: chip already initialised
3240d06db5aSConstantine A. Murenin * 0x03: T41
3250d06db5aSConstantine A. Murenin * 0x05: T61
3260d06db5aSConstantine A. Murenin */
3270d06db5aSConstantine A. Murenin
3280d06db5aSConstantine A. Murenin cr = iobuf[APS_ARG1];
3290d06db5aSConstantine A. Murenin
33023e32507SConstantine A. Murenin bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
3310d06db5aSConstantine A. Murenin DPRINTF(("aps: state register 0x%x\n", cr));
3320d06db5aSConstantine A. Murenin
3330d06db5aSConstantine A. Murenin if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
33423e32507SConstantine A. Murenin DPRINTF(("aps: unsupported state %d\n", cr));
33523e32507SConstantine A. Murenin return ENXIO;
33623e32507SConstantine A. Murenin }
33723e32507SConstantine A. Murenin device_set_desc(dev, "ThinkPad Active Protection System");
33823e32507SConstantine A. Murenin return 0;
3390d06db5aSConstantine A. Murenin }
3400d06db5aSConstantine A. Murenin
34123e32507SConstantine A. Murenin static int
aps_attach(device_t dev)342*5d302545SFrançois Tigeot aps_attach(device_t dev)
3430d06db5aSConstantine A. Murenin {
344017d4aa0SConstantine A. Murenin struct aps_softc *sc = device_get_softc(dev);
3450d06db5aSConstantine A. Murenin
34623e32507SConstantine A. Murenin sc->sc_dev = dev;
347017d4aa0SConstantine A. Murenin sc->sc_iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
348017d4aa0SConstantine A. Murenin &sc->sc_iorid, RF_ACTIVE);
34923e32507SConstantine A. Murenin if (sc->sc_iores == NULL) {
35023e32507SConstantine A. Murenin device_printf(dev, "can't map i/o space\n");
35123e32507SConstantine A. Murenin return ENXIO;
3520d06db5aSConstantine A. Murenin }
3530d06db5aSConstantine A. Murenin
35423e32507SConstantine A. Murenin if (aps_init(sc->sc_iores)) {
35523e32507SConstantine A. Murenin device_printf(dev, "failed to initialise\n");
35623e32507SConstantine A. Murenin bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid, sc->sc_iores);
35723e32507SConstantine A. Murenin return ENXIO;
35823e32507SConstantine A. Murenin }
3590d06db5aSConstantine A. Murenin
3600d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_XACCEL].type = SENSOR_INTEGER;
36123e32507SConstantine A. Murenin ksnprintf(sc->sensors[APS_SENSOR_XACCEL].desc,
3620d06db5aSConstantine A. Murenin sizeof(sc->sensors[APS_SENSOR_XACCEL].desc), "X_ACCEL");
3630d06db5aSConstantine A. Murenin
3640d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_YACCEL].type = SENSOR_INTEGER;
36523e32507SConstantine A. Murenin ksnprintf(sc->sensors[APS_SENSOR_YACCEL].desc,
3660d06db5aSConstantine A. Murenin sizeof(sc->sensors[APS_SENSOR_YACCEL].desc), "Y_ACCEL");
3670d06db5aSConstantine A. Murenin
3680d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_TEMP1].type = SENSOR_TEMP;
3690d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_TEMP2].type = SENSOR_TEMP;
3700d06db5aSConstantine A. Murenin
3710d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_XVAR].type = SENSOR_INTEGER;
37223e32507SConstantine A. Murenin ksnprintf(sc->sensors[APS_SENSOR_XVAR].desc,
3730d06db5aSConstantine A. Murenin sizeof(sc->sensors[APS_SENSOR_XVAR].desc), "X_VAR");
3740d06db5aSConstantine A. Murenin
3750d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_YVAR].type = SENSOR_INTEGER;
37623e32507SConstantine A. Murenin ksnprintf(sc->sensors[APS_SENSOR_YVAR].desc,
3770d06db5aSConstantine A. Murenin sizeof(sc->sensors[APS_SENSOR_YVAR].desc), "Y_VAR");
3780d06db5aSConstantine A. Murenin
3790d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_KBACT].type = SENSOR_INDICATOR;
38023e32507SConstantine A. Murenin ksnprintf(sc->sensors[APS_SENSOR_KBACT].desc,
3810d06db5aSConstantine A. Murenin sizeof(sc->sensors[APS_SENSOR_KBACT].desc), "Keyboard Active");
3820d06db5aSConstantine A. Murenin
3830d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_MSACT].type = SENSOR_INDICATOR;
38423e32507SConstantine A. Murenin ksnprintf(sc->sensors[APS_SENSOR_MSACT].desc,
3850d06db5aSConstantine A. Murenin sizeof(sc->sensors[APS_SENSOR_MSACT].desc), "Mouse Active");
3860d06db5aSConstantine A. Murenin
3870d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_LIDOPEN].type = SENSOR_INDICATOR;
38823e32507SConstantine A. Murenin ksnprintf(sc->sensors[APS_SENSOR_LIDOPEN].desc,
3890d06db5aSConstantine A. Murenin sizeof(sc->sensors[APS_SENSOR_LIDOPEN].desc), "Lid Open");
3900d06db5aSConstantine A. Murenin
3910d06db5aSConstantine A. Murenin /* stop hiding and report to the authorities */
39223e32507SConstantine A. Murenin strlcpy(sc->sensordev.xname, device_get_nameunit(dev),
3930d06db5aSConstantine A. Murenin sizeof(sc->sensordev.xname));
3942b05a037SConstantine A. Murenin for (int i = 0; i < APS_NUM_SENSORS ; i++)
3950d06db5aSConstantine A. Murenin sensor_attach(&sc->sensordev, &sc->sensors[i]);
39623e32507SConstantine A. Murenin
39723e32507SConstantine A. Murenin /* Refresh sensor data every 1 second */
39823e32507SConstantine A. Murenin /* XXX: a more frequent refresh might be appropriate */
3991bedd63aSSepherosa Ziehau sensor_task_register(sc, aps_refresh, 1);
40023e32507SConstantine A. Murenin
4010d06db5aSConstantine A. Murenin sensordev_install(&sc->sensordev);
40223e32507SConstantine A. Murenin return 0;
4030d06db5aSConstantine A. Murenin }
4040d06db5aSConstantine A. Murenin
40523e32507SConstantine A. Murenin static int
aps_detach(device_t dev)406*5d302545SFrançois Tigeot aps_detach(device_t dev)
40723e32507SConstantine A. Murenin {
40823e32507SConstantine A. Murenin struct aps_softc *sc = device_get_softc(dev);
40923e32507SConstantine A. Murenin
41023e32507SConstantine A. Murenin sensordev_deinstall(&sc->sensordev);
41123e32507SConstantine A. Murenin sensor_task_unregister(sc);
41223e32507SConstantine A. Murenin return bus_release_resource(dev, SYS_RES_IOPORT,
41323e32507SConstantine A. Murenin sc->sc_iorid, sc->sc_iores);
41423e32507SConstantine A. Murenin }
41523e32507SConstantine A. Murenin
41623e32507SConstantine A. Murenin static int
aps_init(struct resource * iores)41723e32507SConstantine A. Murenin aps_init(struct resource *iores)
4180d06db5aSConstantine A. Murenin {
4190d06db5aSConstantine A. Murenin unsigned char iobuf[16];
4200d06db5aSConstantine A. Murenin
4210d06db5aSConstantine A. Murenin /* command 0x17/0x81: check EC */
4220d06db5aSConstantine A. Murenin iobuf[APS_CMD] = 0x17;
4230d06db5aSConstantine A. Murenin iobuf[APS_ARG1] = 0x81;
4240d06db5aSConstantine A. Murenin
42523e32507SConstantine A. Murenin if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_3))
4260d06db5aSConstantine A. Murenin return (1);
4270d06db5aSConstantine A. Murenin
4280d06db5aSConstantine A. Murenin if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
4290d06db5aSConstantine A. Murenin return (1);
4300d06db5aSConstantine A. Murenin
4310d06db5aSConstantine A. Murenin /* Test values from the Linux driver */
4320d06db5aSConstantine A. Murenin if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
4330d06db5aSConstantine A. Murenin (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
4340d06db5aSConstantine A. Murenin return (1);
4350d06db5aSConstantine A. Murenin
4360d06db5aSConstantine A. Murenin /* command 0x14: set power */
4370d06db5aSConstantine A. Murenin iobuf[APS_CMD] = 0x14;
4380d06db5aSConstantine A. Murenin iobuf[APS_ARG1] = 0x01;
4390d06db5aSConstantine A. Murenin
44023e32507SConstantine A. Murenin if (aps_do_io(iores, iobuf, APS_WRITE_1, APS_READ_0))
4410d06db5aSConstantine A. Murenin return (1);
4420d06db5aSConstantine A. Murenin
4430d06db5aSConstantine A. Murenin if (iobuf[APS_RET] != 0)
4440d06db5aSConstantine A. Murenin return (1);
4450d06db5aSConstantine A. Murenin
4460d06db5aSConstantine A. Murenin /* command 0x10: set config (sample rate and order) */
4470d06db5aSConstantine A. Murenin iobuf[APS_CMD] = 0x10;
4480d06db5aSConstantine A. Murenin iobuf[APS_ARG1] = 0xc8;
4490d06db5aSConstantine A. Murenin iobuf[APS_ARG2] = 0x00;
4500d06db5aSConstantine A. Murenin iobuf[APS_ARG3] = 0x02;
4510d06db5aSConstantine A. Murenin
45223e32507SConstantine A. Murenin if (aps_do_io(iores, iobuf, APS_WRITE_3, APS_READ_0))
4530d06db5aSConstantine A. Murenin return (1);
4540d06db5aSConstantine A. Murenin
4550d06db5aSConstantine A. Murenin if (iobuf[APS_RET] != 0)
4560d06db5aSConstantine A. Murenin return (1);
4570d06db5aSConstantine A. Murenin
4580d06db5aSConstantine A. Murenin /* command 0x11: refresh data */
4590d06db5aSConstantine A. Murenin iobuf[APS_CMD] = 0x11;
46023e32507SConstantine A. Murenin if (aps_do_io(iores, iobuf, APS_WRITE_0, APS_READ_1))
4610d06db5aSConstantine A. Murenin return (1);
4620d06db5aSConstantine A. Murenin if (iobuf[APS_ARG1] != 0)
4630d06db5aSConstantine A. Murenin return (1);
4640d06db5aSConstantine A. Murenin
4650d06db5aSConstantine A. Murenin return (0);
4660d06db5aSConstantine A. Murenin }
4670d06db5aSConstantine A. Murenin
46823e32507SConstantine A. Murenin static int
aps_read_data(struct aps_softc * sc)4690d06db5aSConstantine A. Murenin aps_read_data(struct aps_softc *sc)
4700d06db5aSConstantine A. Murenin {
4710d06db5aSConstantine A. Murenin unsigned char iobuf[16];
4720d06db5aSConstantine A. Murenin
4730d06db5aSConstantine A. Murenin /* command 0x11: refresh data */
4740d06db5aSConstantine A. Murenin iobuf[APS_CMD] = 0x11;
47523e32507SConstantine A. Murenin if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_ALL))
4760d06db5aSConstantine A. Murenin return (1);
4770d06db5aSConstantine A. Murenin
4780d06db5aSConstantine A. Murenin sc->aps_data.state = iobuf[APS_STATE];
4790d06db5aSConstantine A. Murenin sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
4800d06db5aSConstantine A. Murenin sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
4810d06db5aSConstantine A. Murenin sc->aps_data.temp1 = iobuf[APS_TEMP];
4820d06db5aSConstantine A. Murenin sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
4830d06db5aSConstantine A. Murenin sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
4840d06db5aSConstantine A. Murenin sc->aps_data.temp2 = iobuf[APS_TEMP2];
4850d06db5aSConstantine A. Murenin sc->aps_data.input = iobuf[APS_INPUT];
4860d06db5aSConstantine A. Murenin
4870d06db5aSConstantine A. Murenin return (0);
4880d06db5aSConstantine A. Murenin }
4890d06db5aSConstantine A. Murenin
49023e32507SConstantine A. Murenin static void
aps_refresh_sensor_data(struct aps_softc * sc)4910d06db5aSConstantine A. Murenin aps_refresh_sensor_data(struct aps_softc *sc)
4920d06db5aSConstantine A. Murenin {
4930d06db5aSConstantine A. Murenin int64_t temp;
4940d06db5aSConstantine A. Murenin
49523e32507SConstantine A. Murenin if (aps_read_data(sc)) {
4962b05a037SConstantine A. Murenin for (int i = 0; i < APS_NUM_SENSORS; i++)
49723e32507SConstantine A. Murenin sc->sensors[i].flags |= SENSOR_FINVALID;
4980d06db5aSConstantine A. Murenin return;
4990d06db5aSConstantine A. Murenin }
5000d06db5aSConstantine A. Murenin
5010d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_XACCEL].value = sc->aps_data.x_accel;
5020d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_YACCEL].value = sc->aps_data.y_accel;
5030d06db5aSConstantine A. Murenin
5040d06db5aSConstantine A. Murenin /* convert to micro (mu) degrees */
5050d06db5aSConstantine A. Murenin temp = sc->aps_data.temp1 * 1000000;
5060d06db5aSConstantine A. Murenin /* convert to kelvin */
5070d06db5aSConstantine A. Murenin temp += 273150000;
5080d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_TEMP1].value = temp;
5090d06db5aSConstantine A. Murenin
5100d06db5aSConstantine A. Murenin /* convert to micro (mu) degrees */
5110d06db5aSConstantine A. Murenin temp = sc->aps_data.temp2 * 1000000;
5120d06db5aSConstantine A. Murenin /* convert to kelvin */
5130d06db5aSConstantine A. Murenin temp += 273150000;
5140d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_TEMP2].value = temp;
5150d06db5aSConstantine A. Murenin
5160d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_XVAR].value = sc->aps_data.x_var;
5170d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_YVAR].value = sc->aps_data.y_var;
5180d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_KBACT].value =
5190d06db5aSConstantine A. Murenin (sc->aps_data.input & APS_INPUT_KB) ? 1 : 0;
5200d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_MSACT].value =
5210d06db5aSConstantine A. Murenin (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
5220d06db5aSConstantine A. Murenin sc->sensors[APS_SENSOR_LIDOPEN].value =
5230d06db5aSConstantine A. Murenin (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
52423e32507SConstantine A. Murenin
5252b05a037SConstantine A. Murenin for (int i = 0; i < APS_NUM_SENSORS; i++)
52623e32507SConstantine A. Murenin sc->sensors[i].flags &= ~SENSOR_FINVALID;
5270d06db5aSConstantine A. Murenin }
5280d06db5aSConstantine A. Murenin
52923e32507SConstantine A. Murenin static void
aps_refresh(void * arg)5300d06db5aSConstantine A. Murenin aps_refresh(void *arg)
5310d06db5aSConstantine A. Murenin {
5320d06db5aSConstantine A. Murenin struct aps_softc *sc = (struct aps_softc *)arg;
5330d06db5aSConstantine A. Murenin
5340d06db5aSConstantine A. Murenin aps_refresh_sensor_data(sc);
5350d06db5aSConstantine A. Murenin }
5360d06db5aSConstantine A. Murenin
53723e32507SConstantine A. Murenin static int
aps_resume(device_t dev)538*5d302545SFrançois Tigeot aps_resume(device_t dev)
5390d06db5aSConstantine A. Murenin {
54023e32507SConstantine A. Murenin struct aps_softc *sc = device_get_softc(dev);
5410d06db5aSConstantine A. Murenin unsigned char iobuf[16];
5420d06db5aSConstantine A. Murenin
5430d06db5aSConstantine A. Murenin /*
5440d06db5aSConstantine A. Murenin * Redo the init sequence on resume, because APS is
5450d06db5aSConstantine A. Murenin * as forgetful as it is deaf.
5460d06db5aSConstantine A. Murenin */
5470d06db5aSConstantine A. Murenin
5480d06db5aSConstantine A. Murenin /* get APS mode */
5490d06db5aSConstantine A. Murenin iobuf[APS_CMD] = 0x13;
55023e32507SConstantine A. Murenin if (aps_do_io(sc->sc_iores, iobuf, APS_WRITE_0, APS_READ_1)
55123e32507SConstantine A. Murenin || aps_init(sc->sc_iores)) {
55223e32507SConstantine A. Murenin device_printf(sc->sc_dev, "failed to wake up\n");
55323e32507SConstantine A. Murenin return EIO;
55423e32507SConstantine A. Murenin }
55523e32507SConstantine A. Murenin
55623e32507SConstantine A. Murenin sensor_task_register(sc, aps_refresh, 1);
55723e32507SConstantine A. Murenin return 0;
55823e32507SConstantine A. Murenin }
55923e32507SConstantine A. Murenin
56023e32507SConstantine A. Murenin static int
aps_suspend(device_t dev)561*5d302545SFrançois Tigeot aps_suspend(device_t dev)
56223e32507SConstantine A. Murenin {
56323e32507SConstantine A. Murenin struct aps_softc *sc = device_get_softc(dev);
56423e32507SConstantine A. Murenin
56523e32507SConstantine A. Murenin for (int i = 0; i < APS_NUM_SENSORS; i++)
56623e32507SConstantine A. Murenin sc->sensors[i].flags |= SENSOR_FINVALID;
56723e32507SConstantine A. Murenin sensor_task_unregister(sc);
56823e32507SConstantine A. Murenin return 0;
5690d06db5aSConstantine A. Murenin }
570