Home | History | Annotate | Line # | Download | only in isa
      1  1.20    andvar /*	$NetBSD: aps.c,v 1.20 2025/02/17 22:56:46 andvar 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.20    andvar __KERNEL_RCSID(0, "$NetBSD: aps.c,v 1.20 2025/02/17 22:56:46 andvar 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.19    andvar  * From Renesas 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.20    andvar 	/* make sure last byte 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.18   mlelstv 			sc->sc_sme = NULL;
    352   1.8   xtraeme 			goto out;
    353   1.3   xtraeme 		}
    354   1.3   xtraeme 	}
    355   1.1   xtraeme         /*
    356   1.1   xtraeme          * Register with the sysmon_envsys(9) framework.
    357   1.1   xtraeme          */
    358   1.8   xtraeme 	sc->sc_sme->sme_name = device_xname(self);
    359   1.8   xtraeme 	sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
    360   1.1   xtraeme 
    361   1.3   xtraeme 	if ((i = sysmon_envsys_register(sc->sc_sme))) {
    362   1.8   xtraeme 		aprint_error_dev(self,
    363   1.8   xtraeme 		    "unable to register with sysmon (%d)\n", i);
    364   1.3   xtraeme 		sysmon_envsys_destroy(sc->sc_sme);
    365  1.18   mlelstv 		sc->sc_sme = NULL;
    366   1.8   xtraeme 		goto out;
    367   1.1   xtraeme 	}
    368   1.1   xtraeme 
    369   1.5  jmcneill 	if (!pmf_device_register(self, aps_suspend, aps_resume))
    370   1.5  jmcneill 		aprint_error_dev(self, "couldn't establish power handler\n");
    371   1.1   xtraeme 
    372   1.1   xtraeme 	/* Refresh sensor data every 0.5 seconds */
    373   1.1   xtraeme 	callout_schedule(&sc->sc_callout, (hz) / 2);
    374   1.1   xtraeme 
    375   1.8   xtraeme 	return;
    376   1.8   xtraeme 
    377   1.8   xtraeme out:
    378   1.8   xtraeme 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
    379   1.1   xtraeme }
    380   1.1   xtraeme 
    381   1.1   xtraeme static int
    382   1.1   xtraeme aps_init(struct aps_softc *sc)
    383   1.1   xtraeme {
    384  1.11  jmcneill 	unsigned char iobuf[16];
    385  1.11  jmcneill 
    386  1.11  jmcneill 	/* command 0x17/0x81: check EC */
    387  1.11  jmcneill 	iobuf[APS_CMD] = 0x17;
    388  1.11  jmcneill 	iobuf[APS_ARG1] = 0x81;
    389  1.11  jmcneill 
    390  1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_1, APS_READ_3))
    391  1.11  jmcneill 		return 1;
    392  1.11  jmcneill 	if (iobuf[APS_RET] != 0 ||iobuf[APS_ARG3] != 0)
    393  1.11  jmcneill 		return 1;
    394  1.11  jmcneill 
    395  1.11  jmcneill 	/* Test values from the Linux driver */
    396  1.11  jmcneill 	if ((iobuf[APS_ARG1] != 0 || iobuf[APS_ARG2] != 0x60) &&
    397  1.11  jmcneill 	    (iobuf[APS_ARG1] != 1 || iobuf[APS_ARG2] != 0))
    398  1.11  jmcneill 		return 1;
    399  1.11  jmcneill 
    400  1.11  jmcneill 	/* command 0x14: set power */
    401  1.11  jmcneill 	iobuf[APS_CMD] = 0x14;
    402  1.11  jmcneill 	iobuf[APS_ARG1] = 0x01;
    403  1.11  jmcneill 
    404  1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_1, APS_READ_0))
    405  1.11  jmcneill 		return 1;
    406  1.11  jmcneill 
    407  1.11  jmcneill 	if (iobuf[APS_RET] != 0)
    408  1.11  jmcneill 		return 1;
    409  1.11  jmcneill 
    410  1.11  jmcneill 	/* command 0x10: set config (sample rate and order) */
    411  1.11  jmcneill 	iobuf[APS_CMD] = 0x10;
    412  1.11  jmcneill 	iobuf[APS_ARG1] = 0xc8;
    413  1.11  jmcneill 	iobuf[APS_ARG2] = 0x00;
    414  1.11  jmcneill 	iobuf[APS_ARG3] = 0x02;
    415  1.11  jmcneill 
    416  1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_3, APS_READ_0))
    417  1.11  jmcneill 		return 1;
    418  1.11  jmcneill 
    419  1.11  jmcneill 	/* command 0x11: refresh data */
    420  1.11  jmcneill 	iobuf[APS_CMD] = 0x11;
    421  1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_1))
    422  1.11  jmcneill 		return 1;
    423  1.11  jmcneill 	if (iobuf[APS_ARG1] != 0)
    424  1.11  jmcneill 		return 1;
    425   1.1   xtraeme 
    426  1.11  jmcneill 	return 0;
    427   1.1   xtraeme }
    428   1.1   xtraeme 
    429   1.1   xtraeme static int
    430   1.8   xtraeme aps_detach(device_t self, int flags)
    431   1.1   xtraeme {
    432   1.1   xtraeme 	struct aps_softc *sc = device_private(self);
    433   1.1   xtraeme 
    434  1.16     ozaki         callout_halt(&sc->sc_callout, NULL);
    435   1.1   xtraeme         callout_destroy(&sc->sc_callout);
    436  1.11  jmcneill 
    437  1.11  jmcneill 	if (sc->sc_sme)
    438  1.11  jmcneill 		sysmon_envsys_unregister(sc->sc_sme);
    439  1.11  jmcneill 	if (sc->sc_bus_space_valid == true)
    440  1.11  jmcneill 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, APS_ADDR_SIZE);
    441   1.1   xtraeme 
    442   1.1   xtraeme 	return 0;
    443   1.1   xtraeme }
    444   1.1   xtraeme 
    445  1.11  jmcneill static int
    446  1.11  jmcneill aps_read_data(struct aps_softc *sc)
    447   1.1   xtraeme {
    448  1.11  jmcneill 	unsigned char iobuf[16];
    449  1.11  jmcneill 
    450  1.11  jmcneill 	iobuf[APS_CMD] = 0x11;
    451  1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_ALL))
    452  1.11  jmcneill 		return 1;
    453  1.11  jmcneill 
    454  1.11  jmcneill 	sc->aps_data.state = iobuf[APS_STATE];
    455  1.11  jmcneill 	sc->aps_data.x_accel = iobuf[APS_XACCEL] + 256 * iobuf[APS_XACCEL + 1];
    456  1.11  jmcneill 	sc->aps_data.y_accel = iobuf[APS_YACCEL] + 256 * iobuf[APS_YACCEL + 1];
    457  1.11  jmcneill 	sc->aps_data.temp1 = iobuf[APS_TEMP];
    458  1.11  jmcneill 	sc->aps_data.x_var = iobuf[APS_XVAR] + 256 * iobuf[APS_XVAR + 1];
    459  1.11  jmcneill 	sc->aps_data.y_var = iobuf[APS_YVAR] + 256 * iobuf[APS_YVAR + 1];
    460  1.11  jmcneill 	sc->aps_data.temp2 = iobuf[APS_TEMP2];
    461  1.11  jmcneill 	sc->aps_data.input = iobuf[APS_INPUT];
    462   1.1   xtraeme 
    463   1.1   xtraeme 	return 0;
    464   1.1   xtraeme }
    465   1.1   xtraeme 
    466   1.1   xtraeme static void
    467   1.1   xtraeme aps_refresh_sensor_data(struct aps_softc *sc)
    468   1.1   xtraeme {
    469   1.1   xtraeme 	int64_t temp;
    470   1.1   xtraeme 
    471  1.13  jmcneill 	if (aps_read_data(sc)) {
    472  1.13  jmcneill 		printf("aps0: read data failed\n");
    473   1.1   xtraeme 		return;
    474  1.13  jmcneill 	}
    475   1.1   xtraeme 
    476   1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_XACCEL].value_cur = sc->aps_data.x_accel;
    477   1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_YACCEL].value_cur = sc->aps_data.y_accel;
    478   1.1   xtraeme 
    479  1.13  jmcneill 	if (sc->aps_data.temp1 == 0xff)
    480  1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP1].state = ENVSYS_SINVALID;
    481  1.13  jmcneill 	else {
    482  1.13  jmcneill 		/* convert to micro (mu) degrees */
    483  1.13  jmcneill 		temp = sc->aps_data.temp1 * 1000000;
    484  1.13  jmcneill 		/* convert to kelvin */
    485  1.13  jmcneill 		temp += 273150000;
    486  1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP1].value_cur = temp;
    487  1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP1].state = ENVSYS_SVALID;
    488  1.13  jmcneill 	}
    489  1.13  jmcneill 
    490  1.13  jmcneill 	if (sc->aps_data.temp2 == 0xff)
    491  1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP2].state = ENVSYS_SINVALID;
    492  1.13  jmcneill 	else {
    493  1.13  jmcneill 		/* convert to micro (mu) degrees */
    494  1.13  jmcneill 		temp = sc->aps_data.temp2 * 1000000;
    495  1.13  jmcneill 		/* convert to kelvin */
    496  1.13  jmcneill 		temp += 273150000;
    497  1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP2].value_cur = temp;
    498  1.13  jmcneill 		sc->sc_sensor[APS_SENSOR_TEMP2].state = ENVSYS_SVALID;
    499  1.13  jmcneill 	}
    500   1.1   xtraeme 
    501   1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_XVAR].value_cur = sc->aps_data.x_var;
    502   1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_YVAR].value_cur = sc->aps_data.y_var;
    503   1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_KBACT].value_cur =
    504   1.1   xtraeme 	    (sc->aps_data.input &  APS_INPUT_KB) ? 1 : 0;
    505   1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_MSACT].value_cur =
    506   1.1   xtraeme 	    (sc->aps_data.input & APS_INPUT_MS) ? 1 : 0;
    507   1.3   xtraeme 	sc->sc_sensor[APS_SENSOR_LIDOPEN].value_cur =
    508   1.1   xtraeme 	    (sc->aps_data.input & APS_INPUT_LIDOPEN) ? 1 : 0;
    509   1.1   xtraeme }
    510   1.1   xtraeme 
    511   1.1   xtraeme static void
    512   1.1   xtraeme aps_refresh(void *arg)
    513   1.1   xtraeme {
    514   1.8   xtraeme 	struct aps_softc *sc = arg;
    515   1.1   xtraeme 
    516   1.1   xtraeme 	aps_refresh_sensor_data(sc);
    517   1.1   xtraeme 	callout_schedule(&sc->sc_callout, (hz) / 2);
    518   1.1   xtraeme }
    519   1.1   xtraeme 
    520   1.5  jmcneill static bool
    521  1.10    dyoung aps_suspend(device_t dv, const pmf_qual_t *qual)
    522   1.5  jmcneill {
    523   1.5  jmcneill 	struct aps_softc *sc = device_private(dv);
    524   1.5  jmcneill 
    525   1.5  jmcneill 	callout_stop(&sc->sc_callout);
    526   1.5  jmcneill 
    527   1.5  jmcneill 	return true;
    528   1.5  jmcneill }
    529   1.5  jmcneill 
    530   1.5  jmcneill static bool
    531  1.10    dyoung aps_resume(device_t dv, const pmf_qual_t *qual)
    532   1.1   xtraeme {
    533   1.5  jmcneill 	struct aps_softc *sc = device_private(dv);
    534  1.11  jmcneill 	unsigned char iobuf[16];
    535   1.5  jmcneill 
    536   1.5  jmcneill 	/*
    537   1.5  jmcneill 	 * Redo the init sequence on resume, because APS is
    538   1.5  jmcneill 	 * as forgetful as it is deaf.
    539   1.5  jmcneill 	 */
    540   1.5  jmcneill 
    541  1.11  jmcneill 	/* get APS mode */
    542  1.11  jmcneill 	iobuf[APS_CMD] = 0x13;
    543  1.11  jmcneill 	if (aps_do_io(sc->sc_iot, sc->sc_ioh, iobuf, APS_WRITE_0, APS_READ_1)
    544  1.11  jmcneill 	    || aps_init(sc))
    545  1.11  jmcneill 		aprint_error_dev(dv, "failed to wake up\n");
    546  1.11  jmcneill 	else
    547   1.5  jmcneill 		callout_schedule(&sc->sc_callout, (hz) / 2);
    548   1.1   xtraeme 
    549   1.5  jmcneill 	return true;
    550   1.1   xtraeme }
    551  1.11  jmcneill 
    552  1.17  pgoyette MODULE(MODULE_CLASS_DRIVER, aps, "sysmon_envsys");
    553  1.11  jmcneill 
    554  1.11  jmcneill #ifdef _MODULE
    555  1.11  jmcneill #include "ioconf.c"
    556  1.11  jmcneill #endif
    557  1.11  jmcneill 
    558  1.11  jmcneill static int
    559  1.11  jmcneill aps_modcmd(modcmd_t cmd, void *opaque)
    560  1.11  jmcneill {
    561  1.11  jmcneill 	switch (cmd) {
    562  1.11  jmcneill 	case MODULE_CMD_INIT:
    563  1.11  jmcneill #ifdef _MODULE
    564  1.11  jmcneill 		return config_init_component(cfdriver_ioconf_aps,
    565  1.11  jmcneill 		    cfattach_ioconf_aps, cfdata_ioconf_aps);
    566  1.11  jmcneill #else
    567  1.11  jmcneill 		return 0;
    568  1.11  jmcneill #endif
    569  1.11  jmcneill 	case MODULE_CMD_FINI:
    570  1.11  jmcneill #ifdef _MODULE
    571  1.11  jmcneill 		return config_fini_component(cfdriver_ioconf_aps,
    572  1.11  jmcneill 		    cfattach_ioconf_aps, cfdata_ioconf_aps);
    573  1.11  jmcneill #else
    574  1.11  jmcneill 		return 0;
    575  1.11  jmcneill #endif
    576  1.11  jmcneill 	default:
    577  1.11  jmcneill 		return ENOTTY;
    578  1.11  jmcneill 	}
    579  1.11  jmcneill }
    580