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