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