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