xref: /openbsd-src/sys/dev/acpi/acpisbs.c (revision 6321bc9409fa776a747e5be1befd2f9e5ac2dce2)
1*6321bc94Skn /* $OpenBSD: acpisbs.c,v 1.11 2022/10/26 16:06:42 kn Exp $ */
2a7e2cbadSjcs /*
373488085Sjcs  * Smart Battery subsystem device driver
4a7e2cbadSjcs  * ACPI 5.0 spec section 10
5a7e2cbadSjcs  *
6a7e2cbadSjcs  * Copyright (c) 2016-2017 joshua stein <jcs@openbsd.org>
7a7e2cbadSjcs  *
8a7e2cbadSjcs  * Permission to use, copy, modify, and distribute this software for any
9a7e2cbadSjcs  * purpose with or without fee is hereby granted, provided that the above
10a7e2cbadSjcs  * copyright notice and this permission notice appear in all copies.
11a7e2cbadSjcs  *
12a7e2cbadSjcs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13a7e2cbadSjcs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14a7e2cbadSjcs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15a7e2cbadSjcs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16a7e2cbadSjcs  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17a7e2cbadSjcs  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18a7e2cbadSjcs  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19a7e2cbadSjcs  */
20a7e2cbadSjcs 
2174aedba5Sjcs /*
2274aedba5Sjcs  * TODO: support multiple batteries based on _SBS, make sc_battery an array and
2374aedba5Sjcs  * poll each battery independently
2474aedba5Sjcs  */
2574aedba5Sjcs 
26a7e2cbadSjcs #include <sys/param.h>
27a7e2cbadSjcs #include <sys/systm.h>
28a7e2cbadSjcs #include <sys/device.h>
29a7e2cbadSjcs #include <sys/malloc.h>
30a7e2cbadSjcs 
316ee917c2Sjca #include <machine/apmvar.h>
326ee917c2Sjca 
33a7e2cbadSjcs #include <dev/acpi/acpireg.h>
34a7e2cbadSjcs #include <dev/acpi/acpivar.h>
35a7e2cbadSjcs #include <dev/acpi/acpidev.h>
36a7e2cbadSjcs #include <dev/acpi/amltypes.h>
37a7e2cbadSjcs #include <dev/acpi/dsdt.h>
38a7e2cbadSjcs 
39a7e2cbadSjcs #include <sys/sensors.h>
40a7e2cbadSjcs 
41a7e2cbadSjcs /* #define ACPISBS_DEBUG */
42a7e2cbadSjcs 
43a7e2cbadSjcs #ifdef ACPISBS_DEBUG
44a7e2cbadSjcs #define DPRINTF(x) printf x
45a7e2cbadSjcs #else
46a7e2cbadSjcs #define DPRINTF(x)
47a7e2cbadSjcs #endif
48a7e2cbadSjcs 
49a7e2cbadSjcs /* how often (in seconds) to re-poll data */
50a7e2cbadSjcs #define ACPISBS_POLL_FREQ	30
51a7e2cbadSjcs 
52a7e2cbadSjcs /* number of polls for reading data */
53a7e2cbadSjcs #define SMBUS_TIMEOUT		50
54a7e2cbadSjcs 
55a7e2cbadSjcs #define CHECK(kind, cmd, val, senst, sens) { \
56a7e2cbadSjcs 	SMBUS_READ_##kind, SMBATT_CMD_##cmd, \
57a7e2cbadSjcs 	offsetof(struct acpisbs_battery, val), \
58a7e2cbadSjcs 	(SMBUS_READ_##kind == SMBUS_READ_BLOCK ? SMBUS_DATA_SIZE : 2), \
59a7e2cbadSjcs 	#val, senst, sens }
60a7e2cbadSjcs 
61*6321bc94Skn const struct acpisbs_battery_check {
62a7e2cbadSjcs 	uint8_t	mode;
63a7e2cbadSjcs 	uint8_t command;
64a7e2cbadSjcs 	size_t	offset;
65a7e2cbadSjcs 	int	len;
66a7e2cbadSjcs 	char	*name;
67a7e2cbadSjcs 	int	sensor_type;
68a7e2cbadSjcs 	char	*sensor_desc;
69a7e2cbadSjcs } acpisbs_battery_checks[] = {
70a7e2cbadSjcs 	/* mode must be checked first */
71a7e2cbadSjcs 	CHECK(WORD, BATTERY_MODE, mode, -1,
72a7e2cbadSjcs 	    "mode flags"),
73a7e2cbadSjcs 	CHECK(WORD, TEMPERATURE, temperature, SENSOR_TEMP,
74a7e2cbadSjcs 	    "internal temperature"),
75a7e2cbadSjcs 	CHECK(WORD, VOLTAGE, voltage, SENSOR_VOLTS_DC,
76a7e2cbadSjcs 	    "voltage"),
77a7e2cbadSjcs 	CHECK(WORD, CURRENT, current, SENSOR_AMPS,
78a7e2cbadSjcs 	    "current being supplied"),
79a7e2cbadSjcs 	CHECK(WORD, AVERAGE_CURRENT, avg_current, SENSOR_AMPS,
80a7e2cbadSjcs 	    "average current supplied"),
81a7e2cbadSjcs 	CHECK(WORD, RELATIVE_STATE_OF_CHARGE, rel_charge, SENSOR_PERCENT,
82a7e2cbadSjcs 	    "remaining capacity"),
83a7e2cbadSjcs 	CHECK(WORD, ABSOLUTE_STATE_OF_CHARGE, abs_charge, SENSOR_PERCENT,
84a7e2cbadSjcs 	    "remaining of design capacity"),
85a7e2cbadSjcs 	CHECK(WORD, REMAINING_CAPACITY, capacity, SENSOR_AMPHOUR,
86a7e2cbadSjcs 	    "remaining capacity"),
87a7e2cbadSjcs 	CHECK(WORD, FULL_CHARGE_CAPACITY, full_capacity, SENSOR_AMPHOUR,
88a7e2cbadSjcs 	    "capacity when fully charged"),
89a7e2cbadSjcs 	CHECK(WORD, RUN_TIME_TO_EMPTY, run_time, SENSOR_INTEGER,
90a7e2cbadSjcs 	    "remaining run time minutes"),
91a7e2cbadSjcs 	CHECK(WORD, AVERAGE_TIME_TO_EMPTY, avg_empty_time, SENSOR_INTEGER,
92a7e2cbadSjcs 	    "avg remaining minutes"),
93a7e2cbadSjcs 	CHECK(WORD, AVERAGE_TIME_TO_FULL, avg_full_time, SENSOR_INTEGER,
94a7e2cbadSjcs 	    "avg minutes until full charge"),
95a7e2cbadSjcs 	CHECK(WORD, CHARGING_CURRENT, charge_current, SENSOR_AMPS,
96a7e2cbadSjcs 	    "desired charging rate"),
97a7e2cbadSjcs 	CHECK(WORD, CHARGING_VOLTAGE, charge_voltage, SENSOR_VOLTS_DC,
98a7e2cbadSjcs 	    "desired charging voltage"),
99a7e2cbadSjcs 	CHECK(WORD, BATTERY_STATUS, status, -1,
100a7e2cbadSjcs 	    "status"),
101a7e2cbadSjcs 	CHECK(WORD, CYCLE_COUNT, cycle_count, SENSOR_INTEGER,
102a7e2cbadSjcs 	    "charge and discharge cycles"),
103a7e2cbadSjcs 	CHECK(WORD, DESIGN_CAPACITY, design_capacity, SENSOR_AMPHOUR,
104a7e2cbadSjcs 	    "capacity of new battery"),
105a7e2cbadSjcs 	CHECK(WORD, DESIGN_VOLTAGE, design_voltage, SENSOR_VOLTS_DC,
106a7e2cbadSjcs 	    "voltage of new battery"),
107a7e2cbadSjcs 	CHECK(WORD, SERIAL_NUMBER, serial, -1,
108a7e2cbadSjcs 	    "serial number"),
109a7e2cbadSjcs 
110a7e2cbadSjcs 	CHECK(BLOCK, MANUFACTURER_NAME, manufacturer, -1,
111a7e2cbadSjcs 	    "manufacturer name"),
112a7e2cbadSjcs 	CHECK(BLOCK, DEVICE_NAME, device_name, -1,
113a7e2cbadSjcs 	    "battery model number"),
114a7e2cbadSjcs 	CHECK(BLOCK, DEVICE_CHEMISTRY, device_chemistry, -1,
115a7e2cbadSjcs 	    "battery chemistry"),
116a7e2cbadSjcs #if 0
117a7e2cbadSjcs 	CHECK(WORD, SPECIFICATION_INFO, spec, -1,
118a7e2cbadSjcs 	    NULL),
119a7e2cbadSjcs 	CHECK(WORD, MANUFACTURE_DATE, manufacture_date, -1,
120a7e2cbadSjcs 	    "date battery was manufactured"),
121a7e2cbadSjcs 	CHECK(BLOCK, MANUFACTURER_DATA, oem_data, -1,
122a7e2cbadSjcs 	    "manufacturer-specific data"),
123a7e2cbadSjcs #endif
124a7e2cbadSjcs };
125a7e2cbadSjcs 
126d2eaebe9Skettenis extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
127d2eaebe9Skettenis extern void acpiec_write(struct acpiec_softc *, uint8_t, int, uint8_t *);
128a7e2cbadSjcs 
129a7e2cbadSjcs int	acpisbs_match(struct device *, void *, void *);
130a7e2cbadSjcs void	acpisbs_attach(struct device *, struct device *, void *);
131dda5942aSjca int	acpisbs_activate(struct device *, int);
132a7e2cbadSjcs void	acpisbs_setup_sensors(struct acpisbs_softc *);
133a7e2cbadSjcs void	acpisbs_refresh_sensors(struct acpisbs_softc *);
134a7e2cbadSjcs void	acpisbs_read(struct acpisbs_softc *);
135a7e2cbadSjcs int	acpisbs_notify(struct aml_node *, int, void *);
136a7e2cbadSjcs 
137a7e2cbadSjcs int	acpi_smbus_read(struct acpisbs_softc *, uint8_t, uint8_t, int, void *);
138a7e2cbadSjcs 
139f1a94324Sjung const struct cfattach acpisbs_ca = {
140a7e2cbadSjcs 	sizeof(struct acpisbs_softc),
141a7e2cbadSjcs 	acpisbs_match,
142a7e2cbadSjcs 	acpisbs_attach,
143dda5942aSjca 	NULL,
144dda5942aSjca 	acpisbs_activate,
145a7e2cbadSjcs };
146a7e2cbadSjcs 
147a7e2cbadSjcs struct cfdriver acpisbs_cd = {
148a7e2cbadSjcs 	NULL, "acpisbs", DV_DULL
149a7e2cbadSjcs };
150a7e2cbadSjcs 
151a7e2cbadSjcs const char *acpisbs_hids[] = {
1526715140aSjcs 	ACPI_DEV_SBS,
153a7e2cbadSjcs 	NULL
154a7e2cbadSjcs };
155a7e2cbadSjcs 
156a7e2cbadSjcs int
acpisbs_match(struct device * parent,void * match,void * aux)157a7e2cbadSjcs acpisbs_match(struct device *parent, void *match, void *aux)
158a7e2cbadSjcs {
159a7e2cbadSjcs 	struct acpi_attach_args *aa = aux;
160a7e2cbadSjcs 	struct cfdata *cf = match;
161a7e2cbadSjcs 
162a7e2cbadSjcs 	return (acpi_matchhids(aa, acpisbs_hids, cf->cf_driver->cd_name));
163a7e2cbadSjcs }
164a7e2cbadSjcs 
165a7e2cbadSjcs void
acpisbs_attach(struct device * parent,struct device * self,void * aux)166a7e2cbadSjcs acpisbs_attach(struct device *parent, struct device *self, void *aux)
167a7e2cbadSjcs {
168a7e2cbadSjcs 	struct acpisbs_softc *sc = (struct acpisbs_softc *)self;
169a7e2cbadSjcs 	struct acpi_attach_args *aa = aux;
170a7e2cbadSjcs 	int64_t sbs, val;
171a7e2cbadSjcs 
172a7e2cbadSjcs 	sc->sc_acpi = (struct acpi_softc *)parent;
173a7e2cbadSjcs 	sc->sc_devnode = aa->aaa_node;
174a7e2cbadSjcs 	sc->sc_batteries_present = 0;
175a7e2cbadSjcs 
176a7e2cbadSjcs 	memset(&sc->sc_battery, 0, sizeof(sc->sc_battery));
177a7e2cbadSjcs 
178c7b18a16Scheloha 	getmicrouptime(&sc->sc_lastpoll);
179a7e2cbadSjcs 
180a7e2cbadSjcs 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_SBS", 0, NULL, &sbs))
181a7e2cbadSjcs 		return;
182a7e2cbadSjcs 
183a7e2cbadSjcs 	/*
184a7e2cbadSjcs 	 * The parent node of the device block containing the _HID must also
185a7e2cbadSjcs 	 * have an _EC node, which contains the base address and query value.
186a7e2cbadSjcs 	 */
187a7e2cbadSjcs 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode->parent, "_EC", 0,
188a7e2cbadSjcs 	    NULL, &val))
189a7e2cbadSjcs 		return;
190a7e2cbadSjcs 	sc->sc_ec_base = (val >> 8) & 0xff;
191a7e2cbadSjcs 
192a7e2cbadSjcs 	if (!sc->sc_acpi->sc_ec)
193a7e2cbadSjcs 		return;
194a7e2cbadSjcs 	sc->sc_ec = sc->sc_acpi->sc_ec;
195a7e2cbadSjcs 
196a7e2cbadSjcs 	printf(": %s", sc->sc_devnode->name);
197a7e2cbadSjcs 
198a7e2cbadSjcs 	if (sbs > 0)
199a7e2cbadSjcs 		acpisbs_read(sc);
200a7e2cbadSjcs 
201a7e2cbadSjcs 	if (sc->sc_batteries_present) {
202a7e2cbadSjcs 		if (sc->sc_battery.device_name[0])
203a7e2cbadSjcs 			printf(" model \"%s\"", sc->sc_battery.device_name);
204a7e2cbadSjcs 		if (sc->sc_battery.serial)
205a7e2cbadSjcs 			printf(" serial %d", sc->sc_battery.serial);
206a7e2cbadSjcs 		if (sc->sc_battery.device_chemistry[0])
207a7e2cbadSjcs 			printf(" type %s", sc->sc_battery.device_chemistry);
208a7e2cbadSjcs 		if (sc->sc_battery.manufacturer[0])
209a7e2cbadSjcs 			printf(" oem \"%s\"", sc->sc_battery.manufacturer);
210a7e2cbadSjcs 	}
211a7e2cbadSjcs 
212a7e2cbadSjcs 	printf("\n");
213a7e2cbadSjcs 
214a7e2cbadSjcs 	acpisbs_setup_sensors(sc);
215a7e2cbadSjcs 	acpisbs_refresh_sensors(sc);
216a7e2cbadSjcs 
21773488085Sjcs 	/*
21873488085Sjcs 	 * Request notification of SCI events on the subsystem itself, but also
21973488085Sjcs 	 * periodically poll as a fallback in case those events never arrive.
22073488085Sjcs 	 */
22173488085Sjcs 	aml_register_notify(sc->sc_devnode->parent, aa->aaa_dev,
22273488085Sjcs 	    acpisbs_notify, sc, ACPIDEV_POLL);
2236715140aSjcs 
2246715140aSjcs 	sc->sc_acpi->sc_havesbs = 1;
225a7e2cbadSjcs }
226a7e2cbadSjcs 
227a7e2cbadSjcs void
acpisbs_read(struct acpisbs_softc * sc)228a7e2cbadSjcs acpisbs_read(struct acpisbs_softc *sc)
229a7e2cbadSjcs {
230a7e2cbadSjcs 	int i;
231a7e2cbadSjcs 
232a7e2cbadSjcs 	for (i = 0; i < nitems(acpisbs_battery_checks); i++) {
233*6321bc94Skn 		const struct acpisbs_battery_check check =
234*6321bc94Skn 		    acpisbs_battery_checks[i];
235a7e2cbadSjcs 		void *p = (void *)&sc->sc_battery + check.offset;
236a7e2cbadSjcs 
237a7e2cbadSjcs 		acpi_smbus_read(sc, check.mode, check.command, check.len, p);
238a7e2cbadSjcs 
239a7e2cbadSjcs 		if (check.mode == SMBUS_READ_BLOCK)
240a7e2cbadSjcs 			DPRINTF(("%s: %s: %s\n", sc->sc_dev.dv_xname,
241a7e2cbadSjcs 			    check.name, (char *)p));
242a7e2cbadSjcs 		else
243a7e2cbadSjcs 			DPRINTF(("%s: %s: %u\n", sc->sc_dev.dv_xname,
244a7e2cbadSjcs 			    check.name, *(uint16_t *)p));
245a7e2cbadSjcs 
246a7e2cbadSjcs 		if (check.command == SMBATT_CMD_BATTERY_MODE) {
247a7e2cbadSjcs 			uint16_t *ival = (uint16_t *)p;
248a7e2cbadSjcs 			if (*ival == 0) {
249a7e2cbadSjcs 				/* battery not present, skip further checks */
250a7e2cbadSjcs 				sc->sc_batteries_present = 0;
251a7e2cbadSjcs 				break;
252a7e2cbadSjcs 			}
253a7e2cbadSjcs 
254a7e2cbadSjcs 			sc->sc_batteries_present = 1;
255a7e2cbadSjcs 
256a7e2cbadSjcs 			if (*ival & SMBATT_BM_CAPACITY_MODE)
257a7e2cbadSjcs 				sc->sc_battery.units = ACPISBS_UNITS_MW;
258a7e2cbadSjcs 			else
259a7e2cbadSjcs 				sc->sc_battery.units = ACPISBS_UNITS_MA;
260a7e2cbadSjcs 		}
261a7e2cbadSjcs 	}
262a7e2cbadSjcs }
263a7e2cbadSjcs 
264a7e2cbadSjcs void
acpisbs_setup_sensors(struct acpisbs_softc * sc)265a7e2cbadSjcs acpisbs_setup_sensors(struct acpisbs_softc *sc)
266a7e2cbadSjcs {
267a7e2cbadSjcs 	int i;
268a7e2cbadSjcs 
269a7e2cbadSjcs 	memset(&sc->sc_sensordev, 0, sizeof(sc->sc_sensordev));
270a7e2cbadSjcs 	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
271a7e2cbadSjcs 	    sizeof(sc->sc_sensordev.xname));
272a7e2cbadSjcs 
273a7e2cbadSjcs 	sc->sc_sensors = mallocarray(sizeof(struct ksensor),
274a7e2cbadSjcs 	    nitems(acpisbs_battery_checks), M_DEVBUF, M_WAITOK | M_ZERO);
275a7e2cbadSjcs 
276a7e2cbadSjcs 	for (i = 0; i < nitems(acpisbs_battery_checks); i++) {
277*6321bc94Skn 		const struct acpisbs_battery_check check =
278*6321bc94Skn 		    acpisbs_battery_checks[i];
279a7e2cbadSjcs 
280a7e2cbadSjcs 		if (check.sensor_type < 0)
281a7e2cbadSjcs 			continue;
282a7e2cbadSjcs 
283a7e2cbadSjcs 		strlcpy(sc->sc_sensors[i].desc, check.sensor_desc,
284a7e2cbadSjcs 		    sizeof(sc->sc_sensors[i].desc));
285a7e2cbadSjcs 
286a7e2cbadSjcs 		if (check.sensor_type == SENSOR_AMPHOUR &&
287a7e2cbadSjcs 		    sc->sc_battery.units == ACPISBS_UNITS_MW)
288a7e2cbadSjcs 			/* translate to watt-hours */
289a7e2cbadSjcs 			sc->sc_sensors[i].type = SENSOR_WATTHOUR;
290a7e2cbadSjcs 		else
291a7e2cbadSjcs 			sc->sc_sensors[i].type = check.sensor_type;
292a7e2cbadSjcs 
293a7e2cbadSjcs 		sc->sc_sensors[i].value = 0;
294a7e2cbadSjcs 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
295a7e2cbadSjcs 	}
296a7e2cbadSjcs 
297a7e2cbadSjcs 	sensordev_install(&sc->sc_sensordev);
298a7e2cbadSjcs }
299a7e2cbadSjcs 
300a7e2cbadSjcs void
acpisbs_refresh_sensors(struct acpisbs_softc * sc)301a7e2cbadSjcs acpisbs_refresh_sensors(struct acpisbs_softc *sc)
302a7e2cbadSjcs {
303a7e2cbadSjcs 	int i;
304a7e2cbadSjcs 
305a7e2cbadSjcs 	for (i = 0; i < nitems(acpisbs_battery_checks); i++) {
306*6321bc94Skn 		const struct acpisbs_battery_check check =
307*6321bc94Skn 		    acpisbs_battery_checks[i];
308a7e2cbadSjcs 		void *p = (void *)&sc->sc_battery + check.offset;
309a7e2cbadSjcs 		uint16_t *ival = (uint16_t *)p;
310a7e2cbadSjcs 
311a7e2cbadSjcs 		if (check.sensor_type < 0)
312a7e2cbadSjcs 			continue;
313a7e2cbadSjcs 
314a7e2cbadSjcs 		if (sc->sc_batteries_present) {
315a7e2cbadSjcs 			sc->sc_sensors[i].flags = 0;
316a7e2cbadSjcs 			sc->sc_sensors[i].status = SENSOR_S_OK;
317a7e2cbadSjcs 
318a7e2cbadSjcs 			switch (check.sensor_type) {
319a7e2cbadSjcs 			case SENSOR_AMPS:
320a7e2cbadSjcs 				sc->sc_sensors[i].value = *ival * 100;
321a7e2cbadSjcs 				break;
322a7e2cbadSjcs 
323a7e2cbadSjcs 			case SENSOR_AMPHOUR:
324a7e2cbadSjcs 			case SENSOR_WATTHOUR:
325a7e2cbadSjcs 				sc->sc_sensors[i].value = *ival * 10000;
326a7e2cbadSjcs 				break;
327a7e2cbadSjcs 
328a7e2cbadSjcs 			case SENSOR_PERCENT:
329a7e2cbadSjcs 				sc->sc_sensors[i].value = *ival * 1000;
330a7e2cbadSjcs 				break;
331a7e2cbadSjcs 
332a7e2cbadSjcs #if 0
333a7e2cbadSjcs 			case SENSOR_STRING:
334a7e2cbadSjcs 				strlcpy(sc->sc_sensors[i].string, (char *)p,
335a7e2cbadSjcs 				    sizeof(sc->sc_sensors[i].string));
336a7e2cbadSjcs 				break;
337a7e2cbadSjcs #endif
338a7e2cbadSjcs 			case SENSOR_TEMP:
339a7e2cbadSjcs 				/* .1 degK */
340a7e2cbadSjcs 				sc->sc_sensors[i].value = (*ival * 10000) +
341a7e2cbadSjcs 				    273150000;
342a7e2cbadSjcs 				break;
343a7e2cbadSjcs 
344a7e2cbadSjcs 			case SENSOR_VOLTS_DC:
345a7e2cbadSjcs 				sc->sc_sensors[i].value = *ival * 1000;
346a7e2cbadSjcs 				break;
347a7e2cbadSjcs 
348a7e2cbadSjcs 			default:
349a7e2cbadSjcs 				if (*ival == ACPISBS_VALUE_UNKNOWN) {
350a7e2cbadSjcs 					sc->sc_sensors[i].value = 0;
351a7e2cbadSjcs 					sc->sc_sensors[i].status =
352a7e2cbadSjcs 					    SENSOR_S_UNKNOWN;
353a7e2cbadSjcs 					sc->sc_sensors[i].flags =
354a7e2cbadSjcs 					    SENSOR_FUNKNOWN;
355a7e2cbadSjcs 				} else
356a7e2cbadSjcs 					sc->sc_sensors[i].value = *ival;
357a7e2cbadSjcs 			}
358a7e2cbadSjcs 		} else {
359a7e2cbadSjcs 			sc->sc_sensors[i].value = 0;
360a7e2cbadSjcs 			sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
361a7e2cbadSjcs 			sc->sc_sensors[i].flags = SENSOR_FUNKNOWN;
362a7e2cbadSjcs 		}
363a7e2cbadSjcs 	}
364a7e2cbadSjcs }
365a7e2cbadSjcs 
366a7e2cbadSjcs int
acpisbs_activate(struct device * self,int act)367dda5942aSjca acpisbs_activate(struct device *self, int act)
368dda5942aSjca {
369dda5942aSjca 	struct acpisbs_softc *sc = (struct acpisbs_softc *)self;
370dda5942aSjca 
371dda5942aSjca 	switch (act) {
372dda5942aSjca 	case DVACT_WAKEUP:
373dda5942aSjca 		acpisbs_read(sc);
374dda5942aSjca 		acpisbs_refresh_sensors(sc);
375dda5942aSjca 		break;
376dda5942aSjca 	}
377dda5942aSjca 
378dda5942aSjca 	return 0;
379dda5942aSjca }
380dda5942aSjca 
381dda5942aSjca int
acpisbs_notify(struct aml_node * node,int notify_type,void * arg)382a7e2cbadSjcs acpisbs_notify(struct aml_node *node, int notify_type, void *arg)
383a7e2cbadSjcs {
384a7e2cbadSjcs 	struct acpisbs_softc *sc = arg;
385c7b18a16Scheloha 	struct timeval diff, now;
386a7e2cbadSjcs 
387a7e2cbadSjcs 	DPRINTF(("%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, notify_type));
388a7e2cbadSjcs 
389c7b18a16Scheloha 	getmicrouptime(&now);
390a7e2cbadSjcs 
39173488085Sjcs 	switch (notify_type) {
39273488085Sjcs 	case 0x00:
39373488085Sjcs 		/* fallback poll */
39473488085Sjcs 	case 0x80:
39573488085Sjcs 		/*
39673488085Sjcs 		 * EC SCI will come for every data point, so only run once in a
39773488085Sjcs 		 * while
39873488085Sjcs 		 */
399c7b18a16Scheloha 		timersub(&now, &sc->sc_lastpoll, &diff);
400c7b18a16Scheloha 		if (diff.tv_sec > ACPISBS_POLL_FREQ) {
401a7e2cbadSjcs 			acpisbs_read(sc);
40273488085Sjcs 			acpisbs_refresh_sensors(sc);
4036ee917c2Sjca 			acpi_record_event(sc->sc_acpi, APM_POWER_CHANGE);
404c7b18a16Scheloha 			getmicrouptime(&sc->sc_lastpoll);
405a7e2cbadSjcs 		}
40673488085Sjcs 		break;
40773488085Sjcs 	default:
40873488085Sjcs 		break;
40973488085Sjcs 	}
410a7e2cbadSjcs 
411a7e2cbadSjcs 	return 0;
412a7e2cbadSjcs }
413a7e2cbadSjcs 
414a7e2cbadSjcs int
acpi_smbus_read(struct acpisbs_softc * sc,uint8_t type,uint8_t cmd,int len,void * buf)415a7e2cbadSjcs acpi_smbus_read(struct acpisbs_softc *sc, uint8_t type, uint8_t cmd, int len,
416a7e2cbadSjcs     void *buf)
417a7e2cbadSjcs {
418a7e2cbadSjcs 	int j;
419a7e2cbadSjcs 	uint8_t addr = SMBATT_ADDRESS;
420a7e2cbadSjcs 	uint8_t val;
421a7e2cbadSjcs 
422a7e2cbadSjcs 	acpiec_write(sc->sc_ec, sc->sc_ec_base + SMBUS_ADDR, 1, &addr);
423a7e2cbadSjcs 	acpiec_write(sc->sc_ec, sc->sc_ec_base + SMBUS_CMD, 1, &cmd);
424a7e2cbadSjcs 	acpiec_write(sc->sc_ec, sc->sc_ec_base + SMBUS_PRTCL, 1, &type);
425a7e2cbadSjcs 
426cbf40926Sjcs 	for (j = SMBUS_TIMEOUT; j > 0; j--) {
427a7e2cbadSjcs 		acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_PRTCL, 1, &val);
428a7e2cbadSjcs 		if (val == 0)
429a7e2cbadSjcs 			break;
430a7e2cbadSjcs 	}
431a7e2cbadSjcs 	if (j == 0) {
432a7e2cbadSjcs 		printf("%s: %s: timeout reading 0x%x\n", sc->sc_dev.dv_xname,
433a7e2cbadSjcs 		    __func__, addr);
434a7e2cbadSjcs 		return 1;
435a7e2cbadSjcs 	}
436a7e2cbadSjcs 
437a7e2cbadSjcs 	acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_STS, 1, &val);
438a7e2cbadSjcs 	if (val & SMBUS_STS_MASK) {
439a7e2cbadSjcs 		printf("%s: %s: error reading status: 0x%x\n",
440a7e2cbadSjcs 		    sc->sc_dev.dv_xname, __func__, addr);
441a7e2cbadSjcs 		return 1;
442a7e2cbadSjcs 	}
443a7e2cbadSjcs 
444a7e2cbadSjcs 	switch (type) {
445a7e2cbadSjcs         case SMBUS_READ_WORD: {
446a7e2cbadSjcs 		uint8_t word[2];
447a7e2cbadSjcs 		acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_DATA, 2,
448a7e2cbadSjcs 		    (uint8_t *)&word);
449a7e2cbadSjcs 
450a7e2cbadSjcs 		*(uint16_t *)buf = (word[1] << 8) | word[0];
451a7e2cbadSjcs 
452a7e2cbadSjcs 		break;
453a7e2cbadSjcs 	}
454a7e2cbadSjcs 	case SMBUS_READ_BLOCK:
455a7e2cbadSjcs 		bzero(buf, len);
456a7e2cbadSjcs 
457a7e2cbadSjcs 		/* find number of bytes to read */
458a7e2cbadSjcs 		acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_BCNT, 1, &val);
459a7e2cbadSjcs 		val &= 0x1f;
460a7e2cbadSjcs 		if (len > val)
461a7e2cbadSjcs 			len = val;
462a7e2cbadSjcs 
463a7e2cbadSjcs 		for (j = 0; j < len; j++) {
464a7e2cbadSjcs 			acpiec_read(sc->sc_ec, sc->sc_ec_base + SMBUS_DATA + j,
465a7e2cbadSjcs 			    1, &val);
466a7e2cbadSjcs 			((char *)buf)[j] = val;
467a7e2cbadSjcs 		}
468a7e2cbadSjcs 		break;
469a7e2cbadSjcs 	default:
470a7e2cbadSjcs 		printf("%s: %s: unknown mode 0x%x\n", sc->sc_dev.dv_xname,
471a7e2cbadSjcs 		    __func__, type);
472a7e2cbadSjcs 		return 1;
473a7e2cbadSjcs 	}
474a7e2cbadSjcs 
475a7e2cbadSjcs 	return 0;
476a7e2cbadSjcs }
477