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