Home | History | Annotate | Line # | Download | only in isa
aps.c revision 1.16.2.1
      1  1.16.2.1     skrll /*	$NetBSD: aps.c,v 1.16.2.1 2015/06/06 14:40:08 skrll Exp $	*/
      2       1.1   xtraeme /*	$OpenBSD: aps.c,v 1.15 2007/05/19 19:14:11 tedu Exp $	*/
      3      1.11  jmcneill /*	$OpenBSD: aps.c,v 1.17 2008/06/27 06:08:43 canacar Exp $	*/
      4       1.1   xtraeme /*
      5       1.1   xtraeme  * Copyright (c) 2005 Jonathan Gray <jsg (at) openbsd.org>
      6      1.11  jmcneill  * Copyright (c) 2008 Can Erkin Acar <canacar (at) openbsd.org>
      7       1.1   xtraeme  *
      8       1.1   xtraeme  * Permission to use, copy, modify, and distribute this software for any
      9       1.1   xtraeme  * purpose with or without fee is hereby granted, provided that the above
     10       1.1   xtraeme  * copyright notice and this permission notice appear in all copies.
     11       1.1   xtraeme  *
     12       1.1   xtraeme  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     13       1.1   xtraeme  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     14       1.1   xtraeme  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     15       1.1   xtraeme  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     16       1.1   xtraeme  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     17       1.1   xtraeme  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     18       1.1   xtraeme  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     19       1.1   xtraeme  */
     20       1.1   xtraeme 
     21       1.1   xtraeme /*
     22       1.1   xtraeme  * A driver for the ThinkPad Active Protection System based on notes from
     23       1.1   xtraeme  * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html
     24       1.1   xtraeme  */
     25       1.1   xtraeme 
     26       1.1   xtraeme #include <sys/cdefs.h>
     27  1.16.2.1     skrll __KERNEL_RCSID(0, "$NetBSD: aps.c,v 1.16.2.1 2015/06/06 14:40:08 skrll Exp $");
     28       1.1   xtraeme 
     29       1.1   xtraeme #include <sys/param.h>
     30       1.1   xtraeme #include <sys/systm.h>
     31       1.1   xtraeme #include <sys/device.h>
     32       1.1   xtraeme #include <sys/kernel.h>
     33       1.1   xtraeme #include <sys/callout.h>
     34      1.11  jmcneill #include <sys/module.h>
     35       1.1   xtraeme 
     36       1.2        ad #include <sys/bus.h>
     37       1.1   xtraeme 
     38       1.1   xtraeme #include <dev/sysmon/sysmonvar.h>
     39       1.1   xtraeme 
     40       1.1   xtraeme #include <dev/isa/isareg.h>
     41       1.1   xtraeme #include <dev/isa/isavar.h>
     42       1.1   xtraeme 
     43       1.1   xtraeme #if defined(APSDEBUG)
     44       1.1   xtraeme #define DPRINTF(x)		do { printf x; } while (0)
     45       1.1   xtraeme #else
     46       1.1   xtraeme #define DPRINTF(x)
     47       1.1   xtraeme #endif
     48       1.1   xtraeme 
     49       1.1   xtraeme 
     50      1.11  jmcneill /*
     51      1.11  jmcneill  * EC interface on Thinkpad Laptops, from Linux HDAPS driver notes.
     52      1.11  jmcneill  * From Renesans H8S/2140B Group Hardware Manual
     53      1.11  jmcneill  * http://documentation.renesas.com/eng/products/mpumcu/rej09b0300_2140bhm.pdf
     54      1.11  jmcneill  *
     55      1.11  jmcneill  * EC uses LPC Channel 3 registers TWR0..15
     56      1.11  jmcneill  */
     57      1.11  jmcneill 
     58      1.11  jmcneill /* STR3 status register */
     59      1.11  jmcneill #define APS_STR3		0x04
     60      1.11  jmcneill 
     61      1.11  jmcneill #define APS_STR3_IBF3B	0x80	/* Input buffer full (host->slave) */
     62      1.11  jmcneill #define APS_STR3_OBF3B	0x40	/* Output buffer full (slave->host)*/
     63      1.11  jmcneill #define APS_STR3_MWMF	0x20	/* Master write mode */
     64      1.11  jmcneill #define APS_STR3_SWMF	0x10	/* Slave write mode */
     65      1.11  jmcneill 
     66      1.11  jmcneill 
     67      1.11  jmcneill /* Base address of TWR registers */
     68      1.11  jmcneill #define APS_TWR_BASE		0x10
     69      1.11  jmcneill #define APS_TWR_RET		0x1f
     70      1.11  jmcneill 
     71      1.11  jmcneill /* TWR registers */
     72      1.11  jmcneill #define APS_CMD			0x00
     73      1.11  jmcneill #define APS_ARG1		0x01
     74      1.11  jmcneill #define APS_ARG2		0x02
     75      1.11  jmcneill #define APS_ARG3		0x03
     76      1.11  jmcneill #define APS_RET			0x0f
     77      1.11  jmcneill 
     78      1.11  jmcneill /* Sensor values */
     79      1.11  jmcneill #define APS_STATE		0x01
     80      1.11  jmcneill #define	APS_XACCEL		0x02
     81      1.11  jmcneill #define APS_YACCEL		0x04
     82      1.11  jmcneill #define APS_TEMP		0x06
     83      1.11  jmcneill #define	APS_XVAR		0x07
     84      1.11  jmcneill #define APS_YVAR		0x09
     85      1.11  jmcneill #define APS_TEMP2		0x0b
     86      1.11  jmcneill #define APS_UNKNOWN		0x0c
     87      1.11  jmcneill #define APS_INPUT		0x0d
     88      1.11  jmcneill 
     89      1.11  jmcneill /* write masks for I/O, send command + 0-3 arguments*/
     90      1.11  jmcneill #define APS_WRITE_0		0x0001
     91      1.11  jmcneill #define APS_WRITE_1		0x0003
     92      1.11  jmcneill #define APS_WRITE_2		0x0007
     93      1.11  jmcneill #define APS_WRITE_3		0x000f
     94      1.11  jmcneill 
     95      1.11  jmcneill /* read masks for I/O, read 0-3 values (skip command byte) */
     96      1.11  jmcneill #define APS_READ_0		0x0000
     97      1.11  jmcneill #define APS_READ_1		0x0002
     98      1.11  jmcneill #define APS_READ_2		0x0006
     99      1.11  jmcneill #define APS_READ_3		0x000e
    100       1.1   xtraeme 
    101      1.11  jmcneill #define APS_READ_RET		0x8000
    102      1.11  jmcneill #define APS_READ_ALL		0xffff
    103       1.1   xtraeme 
    104      1.11  jmcneill /* Bit definitions for APS_INPUT value */
    105       1.1   xtraeme #define APS_INPUT_KB		(1 << 5)
    106       1.1   xtraeme #define APS_INPUT_MS		(1 << 6)
    107       1.1   xtraeme #define APS_INPUT_LIDOPEN	(1 << 7)
    108       1.1   xtraeme 
    109       1.1   xtraeme #define APS_ADDR_SIZE		0x1f
    110       1.1   xtraeme 
    111       1.1   xtraeme struct sensor_rec {
    112       1.1   xtraeme 	uint8_t 	state;
    113       1.1   xtraeme 	uint16_t	x_accel;
    114       1.1   xtraeme 	uint16_t	y_accel;
    115       1.1   xtraeme 	uint8_t 	temp1;
    116       1.1   xtraeme 	uint16_t	x_var;
    117       1.1   xtraeme 	uint16_t	y_var;
    118       1.1   xtraeme 	uint8_t 	temp2;
    119       1.1   xtraeme 	uint8_t 	unk;
    120       1.1   xtraeme 	uint8_t 	input;
    121       1.1   xtraeme };
    122       1.1   xtraeme 
    123       1.1   xtraeme enum aps_sensors {
    124       1.1   xtraeme         APS_SENSOR_XACCEL = 0,
    125       1.1   xtraeme         APS_SENSOR_YACCEL,
    126       1.1   xtraeme         APS_SENSOR_XVAR,
    127       1.1   xtraeme         APS_SENSOR_YVAR,
    128       1.1   xtraeme         APS_SENSOR_TEMP1,
    129       1.1   xtraeme         APS_SENSOR_TEMP2,
    130       1.1   xtraeme         APS_SENSOR_KBACT,
    131       1.1   xtraeme         APS_SENSOR_MSACT,
    132       1.1   xtraeme         APS_SENSOR_LIDOPEN,
    133       1.1   xtraeme         APS_NUM_SENSORS
    134       1.1   xtraeme };
    135       1.1   xtraeme 
    136       1.1   xtraeme struct aps_softc {
    137       1.1   xtraeme 	bus_space_tag_t sc_iot;
    138       1.1   xtraeme 	bus_space_handle_t sc_ioh;
    139      1.11  jmcneill 	bool sc_bus_space_valid;
    140       1.1   xtraeme 
    141       1.3   xtraeme 	struct sysmon_envsys *sc_sme;
    142       1.3   xtraeme 	envsys_data_t sc_sensor[APS_NUM_SENSORS];
    143       1.1   xtraeme 	struct callout sc_callout;
    144       1.1   xtraeme 
    145       1.1   xtraeme 	struct sensor_rec aps_data;
    146       1.1   xtraeme };
    147       1.1   xtraeme 
    148       1.8   xtraeme static int 	aps_match(device_t, cfdata_t, void *);
    149       1.8   xtraeme static void 	aps_attach(device_t, device_t, void *);
    150       1.8   xtraeme static int	aps_detach(device_t, int);
    151       1.1   xtraeme 
    152       1.1   xtraeme static int 	aps_init(struct aps_softc *);
    153      1.11  jmcneill static int	aps_read_data(struct aps_softc *);
    154      1.11  jmcneill static void 	aps_refresh_sensor_data(struct aps_softc *);
    155       1.1   xtraeme static void 	aps_refresh(void *);
    156      1.11  jmcneill static int	aps_do_io(bus_space_tag_t, bus_space_handle_t,
    157      1.11  jmcneill 			  unsigned char *, int, int);
    158      1.10    dyoung static bool 	aps_suspend(device_t, const pmf_qual_t *);
    159      1.10    dyoung static bool 	aps_resume(device_t, const pmf_qual_t *);
    160       1.1   xtraeme 
    161       1.8   xtraeme CFATTACH_DECL_NEW(aps, sizeof(struct aps_softc),
    162       1.1   xtraeme 	      aps_match, aps_attach, aps_detach, NULL);
    163       1.1   xtraeme 
    164      1.11  jmcneill /* properly communicate with the controller, writing a set of memory
    165      1.11  jmcneill  * locations and reading back another set  */
    166      1.11  jmcneill static int
    167      1.11  jmcneill aps_do_io(bus_space_tag_t iot, bus_space_handle_t ioh,
    168      1.11  jmcneill 	  unsigned char *buf, int wmask, int rmask)
    169      1.11  jmcneill {
    170      1.11  jmcneill 	int bp, stat, n;
    171      1.11  jmcneill 
    172      1.11  jmcneill 	DPRINTF(("aps_do_io: CMD: 0x%02x, wmask: 0x%04x, rmask: 0x%04x\n",
    173      1.11  jmcneill 	    buf[0], wmask, rmask));
    174      1.11  jmcneill 
    175      1.11  jmcneill 	/* write init byte using arbitration */
    176      1.11  jmcneill 	for (n = 0; n < 100; n++) {
    177      1.11  jmcneill 		stat = bus_space_read_1(iot, ioh, APS_STR3);
    178      1.11  jmcneill 		if (stat & (APS_STR3_OBF3B | APS_STR3_SWMF)) {
    179      1.11  jmcneill 			bus_space_read_1(iot, ioh, APS_TWR_RET);
    180      1.11  jmcneill 			continue;
    181      1.11  jmcneill 		}
    182      1.11  jmcneill 		bus_space_write_1(iot, ioh, APS_TWR_BASE, buf[0]);
    183      1.11  jmcneill 		stat = bus_space_read_1(iot, ioh, APS_STR3);
    184      1.11  jmcneill 		if (stat & (APS_STR3_MWMF))
    185      1.11  jmcneill 			break;
    186      1.11  jmcneill 		delay(1);
    187      1.11  jmcneill 	}
    188      1.11  jmcneill 
    189      1.11  jmcneill 	if (n == 100) {
    190      1.11  jmcneill 		DPRINTF(("aps_do_io: Failed to get bus\n"));
    191      1.11  jmcneill 		return 1;
    192      1.11  jmcneill 	}
    193      1.11  jmcneill 
    194      1.11  jmcneill 	/* write data bytes, init already sent */
    195      1.11  jmcneill 	/* make sure last bye is always written as this will trigger slave */
    196      1.11  jmcneill 	wmask |= APS_READ_RET;
    197      1.11  jmcneill 	buf[APS_RET] = 0x01;
    198      1.11  jmcneill 
    199      1.11  jmcneill 	for (n = 1, bp = 2; n < 16; bp <<= 1, n++) {
    200      1.11  jmcneill 		if (wmask & bp) {
    201      1.11  jmcneill 			bus_space_write_1(iot, ioh, APS_TWR_BASE + n, buf[n]);
    202      1.11  jmcneill 			DPRINTF(("aps_do_io:  write %2d 0x%02x\n", n, buf[n]));
    203      1.11  jmcneill 		}
    204      1.11  jmcneill 	}
    205      1.11  jmcneill 
    206      1.11  jmcneill 	for (n = 0; n < 100; n++) {
    207      1.11  jmcneill 		stat = bus_space_read_1(iot, ioh, APS_STR3);
    208      1.11  jmcneill 		if (stat & (APS_STR3_OBF3B))
    209      1.11  jmcneill 			break;
    210      1.11  jmcneill 		delay(5 * 100);
    211      1.11  jmcneill 	}
    212      1.11  jmcneill 
    213      1.11  jmcneill 	if (n == 100) {
    214      1.11  jmcneill 		DPRINTF(("aps_do_io: timeout waiting response\n"));
    215      1.11  jmcneill 		return 1;
    216      1.11  jmcneill 	}
    217      1.11  jmcneill 	/* wait for data available */
    218      1.11  jmcneill 	/* make sure to read the final byte to clear status */
    219      1.11  jmcneill 	rmask |= APS_READ_RET;
    220      1.11  jmcneill 
    221      1.11  jmcneill 	/* read cmd and data bytes */
    222      1.11  jmcneill 	for (n = 0, bp = 1; n < 16; bp <<= 1, n++) {
    223      1.11  jmcneill 		if (rmask & bp) {
    224      1.11  jmcneill 			buf[n] = bus_space_read_1(iot, ioh, APS_TWR_BASE + n);
    225      1.11  jmcneill 			DPRINTF(("aps_do_io:  read %2d 0x%02x\n", n, buf[n]));
    226      1.11  jmcneill 		}
    227      1.11  jmcneill 	}
    228      1.11  jmcneill 
    229      1.11  jmcneill 	return 0;
    230      1.11  jmcneill }
    231      1.11  jmcneill 
    232       1.8   xtraeme static int
    233       1.8   xtraeme aps_match(device_t parent, cfdata_t match, void *aux)
    234       1.1   xtraeme {
    235       1.1   xtraeme 	struct isa_attach_args *ia = aux;
    236       1.1   xtraeme 	bus_space_tag_t iot = ia->ia_iot;
    237       1.1   xtraeme 	bus_space_handle_t ioh;
    238      1.11  jmcneill 	unsigned char iobuf[16];
    239      1.11  jmcneill 	int iobase;
    240       1.1   xtraeme 	uint8_t cr;
    241       1.1   xtraeme 
    242       1.1   xtraeme 	/* Must supply an address */
    243       1.1   xtraeme 	if (ia->ia_nio < 1)
    244       1.1   xtraeme 		return 0;
    245       1.1   xtraeme 
    246       1.1   xtraeme 	if (ISA_DIRECT_CONFIG(ia))
    247       1.1   xtraeme 		return 0;
    248       1.1   xtraeme 
    249       1.1   xtraeme 	if (ia->ia_io[0].ir_addr == ISA_UNKNOWN_PORT)
    250       1.1   xtraeme 		return 0;
    251       1.1   xtraeme 
    252       1.1   xtraeme 	iobase = ia->ia_io[0].ir_addr;
    253       1.1   xtraeme 
    254       1.1   xtraeme 	if (bus_space_map(iot, iobase, APS_ADDR_SIZE, 0, &ioh)) {
    255       1.1   xtraeme 		aprint_error("aps: can't map i/o space\n");
    256       1.1   xtraeme 		return 0;
    257       1.1   xtraeme 	}
    258       1.1   xtraeme 
    259      1.11  jmcneill 
    260       1.1   xtraeme 	/* See if this machine has APS */
    261       1.1   xtraeme 
    262      1.11  jmcneill 	/* get APS mode */
    263      1.11  jmcneill 	iobuf[APS_CMD] = 0x13;
    264      1.11  jmcneill 	if (aps_do_io(iot, ioh, iobuf, APS_WRITE_0, APS_READ_1)) {
    265       1.1   xtraeme 		bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
    266       1.1   xtraeme 		return 0;
    267       1.1   xtraeme 	}
    268       1.1   xtraeme 
    269       1.1   xtraeme 	/*
    270       1.1   xtraeme 	 * Observed values from Linux driver:
    271       1.1   xtraeme 	 * 0x01: T42
    272       1.1   xtraeme 	 * 0x02: chip already initialised
    273       1.1   xtraeme 	 * 0x03: T41
    274      1.11  jmcneill 	 * 0x05: T61
    275       1.1   xtraeme 	 */
    276      1.11  jmcneill 
    277      1.11  jmcneill 	cr = iobuf[APS_ARG1];
    278      1.11  jmcneill 
    279       1.1   xtraeme 	bus_space_unmap(iot, ioh, APS_ADDR_SIZE);
    280       1.1   xtraeme 	DPRINTF(("aps: state register 0x%x\n", cr));
    281      1.11  jmcneill 
    282      1.11  jmcneill 	if (iobuf[APS_RET] != 0 || cr < 1 || cr > 5) {
    283       1.1   xtraeme 		DPRINTF(("aps0: unsupported state %d\n", cr));
    284       1.1   xtraeme 		return 0;
    285       1.1   xtraeme 	}
    286       1.1   xtraeme 
    287       1.1   xtraeme 	ia->ia_nio = 1;
    288       1.1   xtraeme 	ia->ia_io[0].ir_size = APS_ADDR_SIZE;
    289       1.1   xtraeme 	ia->ia_niomem = 0;
    290       1.1   xtraeme 	ia->ia_nirq = 0;
    291       1.1   xtraeme 	ia->ia_ndrq = 0;
    292       1.1   xtraeme 
    293       1.1   xtraeme 	return 1;
    294       1.1   xtraeme }
    295       1.1   xtraeme 
    296       1.8   xtraeme static void
    297       1.8   xtraeme aps_attach(device_t parent, device_t self, void *aux)
    298       1.1   xtraeme {
    299       1.8   xtraeme 	struct aps_softc *sc = device_private(self);
    300       1.1   xtraeme 	struct isa_attach_args *ia = aux;
    301       1.1   xtraeme 	int iobase, i;
    302       1.1   xtraeme 
    303       1.1   xtraeme 	sc->sc_iot = ia->ia_iot;
    304       1.1   xtraeme 	iobase = ia->ia_io[0].ir_addr;
    305       1.1   xtraeme 
    306      1.11  jmcneill 	callout_init(&sc->sc_callout, 0);
    307      1.11  jmcneill 	callout_setfunc(&sc->sc_callout, aps_refresh, sc);
    308      1.11  jmcneill 
    309       1.1   xtraeme 	if (bus_space_map(sc->sc_iot, iobase, APS_ADDR_SIZE, 0, &sc->sc_ioh)) {
    310       1.1   xtraeme 		aprint_error(": can't map i/o space\n");
    311       1.1   xtraeme 		return;
    312       1.1   xtraeme 	}
    313      1.11  jmcneill 	sc->sc_bus_space_valid = true;
    314       1.1   xtraeme 
    315       1.1   xtraeme 	aprint_naive("\n");
    316      1.12  jmcneill 	aprint_normal(": Thinkpad Active Protection System\n");
    317       1.1   xtraeme 
    318      1.11  jmcneill 	if (aps_init(sc)) {
    319      1.11  jmcneill 		aprint_error_dev(self, "failed to initialize\n");
    320       1.8   xtraeme 		goto out;
    321       1.1   xtraeme 	}
    322       1.1   xtraeme 
    323       1.1   xtraeme 	/* Initialize sensors */
    324       1.1   xtraeme #define INITDATA(idx, unit, string)					\
    325       1.6   xtraeme 	sc->sc_sensor[idx].units = unit;				\
    326       1.6   xtraeme 	strlcpy(sc->sc_sensor[idx].desc, string,			\
    327       1.6   xtraeme 	    sizeof(sc->sc_sensor[idx].desc));
    328       1.1   xtraeme 
    329      1.14    jruoho 	INITDATA(APS_SENSOR_XACCEL, ENVSYS_INTEGER, "x-acceleration");
    330      1.14    jruoho 	INITDATA(APS_SENSOR_YACCEL, ENVSYS_INTEGER, "y-acceleration");
    331      1.14    jruoho 	INITDATA(APS_SENSOR_TEMP1, ENVSYS_STEMP, "temperature 1");
    332      1.14    jruoho 	INITDATA(APS_SENSOR_TEMP2, ENVSYS_STEMP, "temperature 2");
    333      1.14    jruoho 	INITDATA(APS_SENSOR_XVAR, ENVSYS_INTEGER, "x-variable");
    334      1.14    jruoho 	INITDATA(APS_SENSOR_YVAR, ENVSYS_INTEGER, "y-variable");
    335      1.14    jruoho 	INITDATA(APS_SENSOR_KBACT, ENVSYS_INDICATOR, "keyboard active");
    336      1.14    jruoho 	INITDATA(APS_SENSOR_MSACT, ENVSYS_INDICATOR, "mouse active");
    337      1.14    jruoho 	INITDATA(APS_SENSOR_LIDOPEN, ENVSYS_INDICATOR, "lid open");
    338       1.1   xtraeme 
    339       1.3   xtraeme 	sc->sc_sme = sysmon_envsys_create();
    340      1.15    jruoho 
    341       1.3   xtraeme 	for (i = 0; i < APS_NUM_SENSORS; i++) {
    342      1.15    jruoho 
    343       1.4    kefren 		sc->sc_sensor[i].state = ENVSYS_SVALID;
    344      1.15    jruoho 
    345      1.15    jruoho 		if (sc->sc_sensor[i].units == ENVSYS_INTEGER)
    346      1.15    jruoho 			sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
    347      1.15    jruoho 
    348       1.3   xtraeme 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
    349      1.15    jruoho 			&sc->sc_sensor[i])) {
    350       1.3   xtraeme 			sysmon_envsys_destroy(sc->sc_sme);
    351       1.8   xtraeme 			goto out;
    352       1.3   xtraeme 		}
    353       1.3   xtraeme 	}
    354       1.1   xtraeme         /*
    355       1.1   xtraeme          * Register with the sysmon_envsys(9) framework.
    356       1.1   xtraeme          */
    357       1.8   xtraeme 	sc->sc_sme->sme_name = device_xname(self);
    358       1.8   xtraeme 	sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
    359       1.1   xtraeme 
    360       1.3   xtraeme 	if ((i = sysmon_envsys_register(sc->sc_sme))) {
    361       1.8   xtraeme 		aprint_error_dev(self,
    362       1.8   xtraeme 		    "unable to register with sysmon (%d)\n", i);
    363       1.3   xtraeme 		sysmon_envsys_destroy(sc->sc_sme);
    364       1.8   xtraeme 		goto out;
    365       1.1   xtraeme 	}
    366       1.1   xtraeme 
    367       1.5  jmcneill 	if (!pmf_device_register(self, aps_suspend, aps_resume))
    368       1.5  jmcneill 		aprint_error_dev(self, "couldn't establish power handler\n");
    369       1.1   xtraeme 
    370       1.1   xtraeme 	/* Refresh sensor data every 0.5 seconds */
    371       1.1   xtraeme 	callout_schedule(&sc->sc_callout, (hz) / 2);
    372       1.1   xtraeme 
    373       1.8   xtraeme 	return;
    374       1.8   xtraeme 
    375       1.8   xtraeme out:
    376       1.8   xtraeme 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
    377       1.1   xtraeme }
    378       1.1   xtraeme 
    379       1.1   xtraeme static int
    380       1.1   xtraeme aps_init(struct aps_softc *sc)
    381       1.1   xtraeme {
    382      1.11  jmcneill 	unsigned char iobuf[16];
    383      1.11  jmcneill 
    384      1.11  jmcneill 	/* command 0x17/0x81: check EC */
    385      1.11  jmcneill 	iobuf[APS_CMD] = 0x17;
    386      1.11  jmcneill 	iobuf[APS_ARG1] = 0x81;
    387      1.11  jmcneill 
    388      1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_1, APS_READ_3))
    389      1.11  jmcneill 		return 1;
    390      1.11  jmcneill 	if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
    391      1.11  jmcneill 		return 1;
    392      1.11  jmcneill 
    393      1.11  jmcneill 	/* Test values from the Linux driver */
    394      1.11  jmcneill 	if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
    395      1.11  jmcneill 	    (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
    396      1.11  jmcneill 		return 1;
    397      1.11  jmcneill 
    398      1.11  jmcneill 	/* command 0x14: set power */
    399      1.11  jmcneill 	iobuf[APS_CMD] = 0x14;
    400      1.11  jmcneill 	iobuf[APS_ARG1] = 0x01;
    401      1.11  jmcneill 
    402      1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_1, APS_READ_0))
    403      1.11  jmcneill 		return 1;
    404      1.11  jmcneill 
    405      1.11  jmcneill 	if (iobuf[APS_RET] != 0)
    406      1.11  jmcneill 		return 1;
    407      1.11  jmcneill 
    408      1.11  jmcneill 	/* command 0x10: set config (sample rate and order) */
    409      1.11  jmcneill 	iobuf[APS_CMD] = 0x10;
    410      1.11  jmcneill 	iobuf[APS_ARG1] = 0xc8;
    411      1.11  jmcneill 	iobuf[APS_ARG2] = 0x00;
    412      1.11  jmcneill 	iobuf[APS_ARG3] = 0x02;
    413      1.11  jmcneill 
    414      1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_3, APS_READ_0))
    415      1.11  jmcneill 		return 1;
    416      1.11  jmcneill 
    417      1.11  jmcneill 	/* command 0x11: refresh data */
    418      1.11  jmcneill 	iobuf[APS_CMD] = 0x11;
    419      1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_1))
    420      1.11  jmcneill 		return 1;
    421      1.11  jmcneill 	if (iobuf[APS_ARG1] != 0)
    422      1.11  jmcneill 		return 1;
    423       1.1   xtraeme 
    424      1.11  jmcneill 	return 0;
    425       1.1   xtraeme }
    426       1.1   xtraeme 
    427       1.1   xtraeme static int
    428       1.8   xtraeme aps_detach(device_t self, int flags)
    429       1.1   xtraeme {
    430       1.1   xtraeme 	struct aps_softc *sc = device_private(self);
    431       1.1   xtraeme 
    432      1.16     ozaki         callout_halt(&sc->sc_callout, NULL);
    433       1.1   xtraeme         callout_destroy(&sc->sc_callout);
    434      1.11  jmcneill 
    435      1.11  jmcneill 	if (sc->sc_sme)
    436      1.11  jmcneill 		sysmon_envsys_unregister(sc->sc_sme);
    437      1.11  jmcneill 	if (sc->sc_bus_space_valid == true)
    438      1.11  jmcneill 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
    439       1.1   xtraeme 
    440       1.1   xtraeme 	return 0;
    441       1.1   xtraeme }
    442       1.1   xtraeme 
    443      1.11  jmcneill static int
    444      1.11  jmcneill aps_read_data(struct aps_softc *sc)
    445       1.1   xtraeme {
    446      1.11  jmcneill 	unsigned char iobuf[16];
    447      1.11  jmcneill 
    448      1.11  jmcneill 	iobuf[APS_CMD] = 0x11;
    449      1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_ALL))
    450      1.11  jmcneill 		return 1;
    451      1.11  jmcneill 
    452      1.11  jmcneill 	sc->aps_data.state = iobuf[APS_STATE];
    453      1.11  jmcneill 	sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
    454      1.11  jmcneill 	sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
    455      1.11  jmcneill 	sc->aps_data.temp1 = iobuf[APS_TEMP];
    456      1.11  jmcneill 	sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
    457      1.11  jmcneill 	sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
    458      1.11  jmcneill 	sc->aps_data.temp2 = iobuf[APS_TEMP2];
    459      1.11  jmcneill 	sc->aps_data.input = iobuf[APS_INPUT];
    460       1.1   xtraeme 
    461       1.1   xtraeme 	return 0;
    462       1.1   xtraeme }
    463       1.1   xtraeme 
    464       1.1   xtraeme static void
    465       1.1   xtraeme aps_refresh_sensor_data(struct aps_softc *sc)
    466       1.1   xtraeme {
    467       1.1   xtraeme 	int64_t temp;
    468       1.1   xtraeme 
    469      1.13  jmcneill 	if (aps_read_data(sc)) {
    470      1.13  jmcneill 		printf("aps0: read data failed\n");
    471       1.1   xtraeme 		return;
    472      1.13  jmcneill 	}
    473       1.1   xtraeme 
    474       1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_XACCEL].value_cur = sc->aps_data.x_accel;
    475       1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_YACCEL].value_cur = sc->aps_data.y_accel;
    476       1.1   xtraeme 
    477      1.13  jmcneill 	if (sc->aps_data.temp1 == 0xff)
    478      1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP1].state = ENVSYS_SINVALID;
    479      1.13  jmcneill 	else {
    480      1.13  jmcneill 		/* convert to micro (mu) degrees */
    481      1.13  jmcneill 		temp = sc->aps_data.temp1 * 1000000;
    482      1.13  jmcneill 		/* convert to kelvin */
    483      1.13  jmcneill 		temp += 273150000;
    484      1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP1].value_cur = temp;
    485      1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP1].state = ENVSYS_SVALID;
    486      1.13  jmcneill 	}
    487      1.13  jmcneill 
    488      1.13  jmcneill 	if (sc->aps_data.temp2 == 0xff)
    489      1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP2].state = ENVSYS_SINVALID;
    490      1.13  jmcneill 	else {
    491      1.13  jmcneill 		/* convert to micro (mu) degrees */
    492      1.13  jmcneill 		temp = sc->aps_data.temp2 * 1000000;
    493      1.13  jmcneill 		/* convert to kelvin */
    494      1.13  jmcneill 		temp += 273150000;
    495      1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP2].value_cur = temp;
    496      1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP2].state = ENVSYS_SVALID;
    497      1.13  jmcneill 	}
    498       1.1   xtraeme 
    499       1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_XVAR].value_cur = sc->aps_data.x_var;
    500       1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_YVAR].value_cur = sc->aps_data.y_var;
    501       1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_KBACT].value_cur =
    502       1.1   xtraeme 	    (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
    503       1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_MSACT].value_cur =
    504       1.1   xtraeme 	    (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
    505       1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_LIDOPEN].value_cur =
    506       1.1   xtraeme 	    (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
    507       1.1   xtraeme }
    508       1.1   xtraeme 
    509       1.1   xtraeme static void
    510       1.1   xtraeme aps_refresh(void *arg)
    511       1.1   xtraeme {
    512       1.8   xtraeme 	struct aps_softc *sc = arg;
    513       1.1   xtraeme 
    514       1.1   xtraeme 	aps_refresh_sensor_data(sc);
    515       1.1   xtraeme 	callout_schedule(&sc->sc_callout, (hz) / 2);
    516       1.1   xtraeme }
    517       1.1   xtraeme 
    518       1.5  jmcneill static bool
    519      1.10    dyoung aps_suspend(device_t dv, const pmf_qual_t *qual)
    520       1.5  jmcneill {
    521       1.5  jmcneill 	struct aps_softc *sc = device_private(dv);
    522       1.5  jmcneill 
    523       1.5  jmcneill 	callout_stop(&sc->sc_callout);
    524       1.5  jmcneill 
    525       1.5  jmcneill 	return true;
    526       1.5  jmcneill }
    527       1.5  jmcneill 
    528       1.5  jmcneill static bool
    529      1.10    dyoung aps_resume(device_t dv, const pmf_qual_t *qual)
    530       1.1   xtraeme {
    531       1.5  jmcneill 	struct aps_softc *sc = device_private(dv);
    532      1.11  jmcneill 	unsigned char iobuf[16];
    533       1.5  jmcneill 
    534       1.5  jmcneill 	/*
    535       1.5  jmcneill 	 * Redo the init sequence on resume, because APS is
    536       1.5  jmcneill 	 * as forgetful as it is deaf.
    537       1.5  jmcneill 	 */
    538       1.5  jmcneill 
    539      1.11  jmcneill 	/* get APS mode */
    540      1.11  jmcneill 	iobuf[APS_CMD] = 0x13;
    541      1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_1)
    542      1.11  jmcneill 	    || aps_init(sc))
    543      1.11  jmcneill 		aprint_error_dev(dv, "failed to wake up\n");
    544      1.11  jmcneill 	else
    545       1.5  jmcneill 		callout_schedule(&sc->sc_callout, (hz) / 2);
    546       1.1   xtraeme 
    547       1.5  jmcneill 	return true;
    548       1.1   xtraeme }
    549      1.11  jmcneill 
    550  1.16.2.1     skrll MODULE(MODULE_CLASS_DRIVER, aps, "sysmon_envsys");
    551      1.11  jmcneill 
    552      1.11  jmcneill #ifdef _MODULE
    553      1.11  jmcneill #include "ioconf.c"
    554      1.11  jmcneill #endif
    555      1.11  jmcneill 
    556      1.11  jmcneill static int
    557      1.11  jmcneill aps_modcmd(modcmd_t cmd, void *opaque)
    558      1.11  jmcneill {
    559      1.11  jmcneill 	switch (cmd) {
    560      1.11  jmcneill 	case MODULE_CMD_INIT:
    561      1.11  jmcneill #ifdef _MODULE
    562      1.11  jmcneill 		return config_init_component(cfdriver_ioconf_aps,
    563      1.11  jmcneill 		    cfattach_ioconf_aps, cfdata_ioconf_aps);
    564      1.11  jmcneill #else
    565      1.11  jmcneill 		return 0;
    566      1.11  jmcneill #endif
    567      1.11  jmcneill 	case MODULE_CMD_FINI:
    568      1.11  jmcneill #ifdef _MODULE
    569      1.11  jmcneill 		return config_fini_component(cfdriver_ioconf_aps,
    570      1.11  jmcneill 		    cfattach_ioconf_aps, cfdata_ioconf_aps);
    571      1.11  jmcneill #else
    572      1.11  jmcneill 		return 0;
    573      1.11  jmcneill #endif
    574      1.11  jmcneill 	default:
    575      1.11  jmcneill 		return ENOTTY;
    576      1.11  jmcneill 	}
    577      1.11  jmcneill }
    578