1/*-
2 * Copyright (c) 2005 Nate Lawson
3 * Copyright (c) 2000 Munehiro Matsuda
4 * Copyright (c) 2000 Takanori Watanabe
5 * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/11/sys/dev/acpica/acpi_cmbat.c 359076 2020-03-18 18:02:33Z hrs $");
32
33#include "opt_acpi.h"
34#include <sys/param.h>
35#include <sys/kernel.h>
36#include <sys/module.h>
37#include <sys/bus.h>
38#include <sys/ioccom.h>
39
40#include <machine/bus.h>
41#include <sys/rman.h>
42#include <sys/malloc.h>
43
44#include <contrib/dev/acpica/include/acpi.h>
45
46#include <dev/acpica/acpivar.h>
47#include <dev/acpica/acpiio.h>
48
49static MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat",
50    "ACPI control method battery data");
51
52/* Number of times to retry initialization before giving up. */
53#define ACPI_CMBAT_RETRY_MAX	6
54
55/* Check the battery once a minute. */
56#define	CMBAT_POLLRATE		(60 * hz)
57
58/* Hooks for the ACPI CA debugging infrastructure */
59#define	_COMPONENT	ACPI_BATTERY
60ACPI_MODULE_NAME("BATTERY")
61
62#define	ACPI_BATTERY_BST_CHANGE	0x80
63#define	ACPI_BATTERY_BIF_CHANGE	0x81
64#define	ACPI_BATTERY_BIX_CHANGE	ACPI_BATTERY_BIF_CHANGE
65
66struct acpi_cmbat_softc {
67    device_t	    dev;
68    int		    flags;
69
70    struct acpi_bix bix;
71    struct acpi_bst bst;
72    struct timespec bst_lastupdated;
73};
74
75ACPI_SERIAL_DECL(cmbat, "ACPI cmbat");
76
77static int		acpi_cmbat_probe(device_t dev);
78static int		acpi_cmbat_attach(device_t dev);
79static int		acpi_cmbat_detach(device_t dev);
80static int		acpi_cmbat_resume(device_t dev);
81static void		acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify,
82			    void *context);
83static int		acpi_cmbat_info_expired(struct timespec *lastupdated);
84static void		acpi_cmbat_info_updated(struct timespec *lastupdated);
85static void		acpi_cmbat_get_bst(void *arg);
86static void		acpi_cmbat_get_bix_task(void *arg);
87static void		acpi_cmbat_get_bix(void *arg);
88static int		acpi_cmbat_bst(device_t, struct acpi_bst *);
89static int		acpi_cmbat_bix(device_t, void *, size_t);
90static void		acpi_cmbat_init_battery(void *arg);
91
92static device_method_t acpi_cmbat_methods[] = {
93    /* Device interface */
94    DEVMETHOD(device_probe,	acpi_cmbat_probe),
95    DEVMETHOD(device_attach,	acpi_cmbat_attach),
96    DEVMETHOD(device_detach,	acpi_cmbat_detach),
97    DEVMETHOD(device_resume,	acpi_cmbat_resume),
98
99    /* ACPI battery interface */
100    DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bix),
101    DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst),
102
103    DEVMETHOD_END
104};
105
106static driver_t acpi_cmbat_driver = {
107    "battery",
108    acpi_cmbat_methods,
109    sizeof(struct acpi_cmbat_softc),
110};
111
112static devclass_t acpi_cmbat_devclass;
113DRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0);
114MODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1);
115
116static int
117acpi_cmbat_probe(device_t dev)
118{
119    static char *cmbat_ids[] = { "PNP0C0A", NULL };
120
121    if (acpi_disabled("cmbat") ||
122	ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL)
123	return (ENXIO);
124
125    device_set_desc(dev, "ACPI Control Method Battery");
126    return (0);
127}
128
129static int
130acpi_cmbat_attach(device_t dev)
131{
132    int		error;
133    ACPI_HANDLE	handle;
134    struct acpi_cmbat_softc *sc;
135
136    sc = device_get_softc(dev);
137    handle = acpi_get_handle(dev);
138    sc->dev = dev;
139
140    timespecclear(&sc->bst_lastupdated);
141
142    error = acpi_battery_register(dev);
143    if (error != 0) {
144    	device_printf(dev, "registering battery failed\n");
145	return (error);
146    }
147
148    /*
149     * Install a system notify handler in addition to the device notify.
150     * Toshiba notebook uses this alternate notify for its battery.
151     */
152    AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY,
153	acpi_cmbat_notify_handler, dev);
154
155    AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
156
157    return (0);
158}
159
160static int
161acpi_cmbat_detach(device_t dev)
162{
163    ACPI_HANDLE	handle;
164
165    handle = acpi_get_handle(dev);
166    AcpiRemoveNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler);
167    acpi_battery_remove(dev);
168
169    /*
170     * Force any pending notification handler calls to complete by
171     * requesting cmbat serialisation while freeing and clearing the
172     * softc pointer:
173     */
174    ACPI_SERIAL_BEGIN(cmbat);
175    device_set_softc(dev, NULL);
176    ACPI_SERIAL_END(cmbat);
177
178    return (0);
179}
180
181static int
182acpi_cmbat_resume(device_t dev)
183{
184
185    AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
186    return (0);
187}
188
189static void
190acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
191{
192    struct acpi_cmbat_softc *sc;
193    device_t dev;
194
195    dev = (device_t)context;
196    sc = device_get_softc(dev);
197
198    switch (notify) {
199    case ACPI_NOTIFY_DEVICE_CHECK:
200    case ACPI_BATTERY_BST_CHANGE:
201	/*
202	 * Clear the last updated time.  The next call to retrieve the
203	 * battery status will get the new value for us.
204	 */
205	timespecclear(&sc->bst_lastupdated);
206	break;
207    case ACPI_NOTIFY_BUS_CHECK:
208    case ACPI_BATTERY_BIX_CHANGE:
209	/*
210	 * Queue a callback to get the current battery info from thread
211	 * context.  It's not safe to block in a notify handler.
212	 */
213	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bix_task, dev);
214	break;
215    }
216
217    acpi_UserNotify("CMBAT", h, notify);
218}
219
220static int
221acpi_cmbat_info_expired(struct timespec *lastupdated)
222{
223    struct timespec	curtime;
224
225    ACPI_SERIAL_ASSERT(cmbat);
226
227    if (lastupdated == NULL)
228	return (TRUE);
229    if (!timespecisset(lastupdated))
230	return (TRUE);
231
232    getnanotime(&curtime);
233    timespecsub(&curtime, lastupdated);
234    return (curtime.tv_sec < 0 ||
235	    curtime.tv_sec > acpi_battery_get_info_expire());
236}
237
238static void
239acpi_cmbat_info_updated(struct timespec *lastupdated)
240{
241
242    ACPI_SERIAL_ASSERT(cmbat);
243
244    if (lastupdated != NULL)
245	getnanotime(lastupdated);
246}
247
248static void
249acpi_cmbat_get_bst(void *arg)
250{
251    struct acpi_cmbat_softc *sc;
252    ACPI_STATUS	as;
253    ACPI_OBJECT	*res;
254    ACPI_HANDLE	h;
255    ACPI_BUFFER	bst_buffer;
256    device_t dev;
257
258    ACPI_SERIAL_ASSERT(cmbat);
259
260    dev = arg;
261    sc = device_get_softc(dev);
262    h = acpi_get_handle(dev);
263    bst_buffer.Pointer = NULL;
264    bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
265
266    if (!acpi_cmbat_info_expired(&sc->bst_lastupdated))
267	goto end;
268
269    as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer);
270    if (ACPI_FAILURE(as)) {
271	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
272	    "error fetching current battery status -- %s\n",
273	    AcpiFormatException(as));
274	goto end;
275    }
276
277    res = (ACPI_OBJECT *)bst_buffer.Pointer;
278    if (!ACPI_PKG_VALID(res, 4)) {
279	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
280	    "battery status corrupted\n");
281	goto end;
282    }
283
284    if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0)
285	goto end;
286    if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0)
287	goto end;
288    if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0)
289	goto end;
290    if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0)
291	goto end;
292    acpi_cmbat_info_updated(&sc->bst_lastupdated);
293
294    /* Clear out undefined/extended bits that might be set by hardware. */
295    sc->bst.state &= ACPI_BATT_STAT_BST_MASK;
296    if ((sc->bst.state & ACPI_BATT_STAT_INVALID) == ACPI_BATT_STAT_INVALID)
297	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
298	    "battery reports simultaneous charging and discharging\n");
299
300    /* XXX If all batteries are critical, perhaps we should suspend. */
301    if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
302    	if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
303	    sc->flags |= ACPI_BATT_STAT_CRITICAL;
304	    device_printf(dev, "critically low charge!\n");
305	}
306    } else
307	sc->flags &= ~ACPI_BATT_STAT_CRITICAL;
308
309end:
310    AcpiOsFree(bst_buffer.Pointer);
311}
312
313/* XXX There should be a cleaner way to do this locking. */
314static void
315acpi_cmbat_get_bix_task(void *arg)
316{
317
318    ACPI_SERIAL_BEGIN(cmbat);
319    acpi_cmbat_get_bix(arg);
320    ACPI_SERIAL_END(cmbat);
321}
322
323static void
324acpi_cmbat_get_bix(void *arg)
325{
326    struct acpi_cmbat_softc *sc;
327    ACPI_STATUS	as;
328    ACPI_OBJECT	*res;
329    ACPI_HANDLE	h;
330    ACPI_BUFFER	bix_buffer;
331    device_t dev;
332    int i, n;
333    const struct {
334	    enum { _BIX, _BIF } type;
335	    char *name;
336    } bobjs[] = {
337	    { _BIX, "_BIX"},
338	    { _BIF, "_BIF"},
339    };
340
341    ACPI_SERIAL_ASSERT(cmbat);
342
343    dev = arg;
344    sc = device_get_softc(dev);
345    h = acpi_get_handle(dev);
346    bix_buffer.Pointer = NULL;
347    bix_buffer.Length = ACPI_ALLOCATE_BUFFER;
348
349    for (n = 0; n < sizeof(bobjs); n++) {
350	as = AcpiEvaluateObject(h, bobjs[n].name, NULL, &bix_buffer);
351	if (!ACPI_FAILURE(as)) {
352	    res = (ACPI_OBJECT *)bix_buffer.Pointer;
353	    break;
354	}
355	AcpiOsFree(bix_buffer.Pointer);
356        bix_buffer.Pointer = NULL;
357        bix_buffer.Length = ACPI_ALLOCATE_BUFFER;
358    }
359    /* Both _BIF and _BIX were not found. */
360    if (n == sizeof(bobjs)) {
361	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
362	    "error fetching current battery info -- %s\n",
363	    AcpiFormatException(as));
364	goto end;
365    }
366
367    /*
368     * ACPI _BIX and _BIF revision mismatch check:
369     *
370     * 1. _BIF has no revision field.  The number of fields must be 13.
371     *
372     * 2. _BIX has a revision field.  As of ACPI 6.3 it must be "0" or
373     *    "1".  The number of fields will be checked---20 and 21,
374     *    respectively.
375     *
376     *    If the revision number is grater than "1" and the number of
377     *    fields is grater than 21, it will be treated as compatible with
378     *    ACPI 6.0 _BIX.  If not, it will be ignored.
379     */
380    i = 0;
381    switch (bobjs[n].type) {
382    case _BIX:
383	if (acpi_PkgInt16(res, i++, &sc->bix.rev) != 0) {
384	    ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
385		"_BIX revision error\n");
386	    goto end;
387	}
388#define	ACPI_BIX_REV_MISMATCH_ERR(x, r) do {			\
389	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),	\
390	    "_BIX revision mismatch (%u != %u)\n", x, r);	\
391	goto end;						\
392	} while (0)
393
394	if (ACPI_PKG_VALID_EQ(res, 21)) {	/* ACPI 6.0 _BIX */
395	    /*
396	     * Some models have rev.0 _BIX with 21 members.
397	     * In that case, treat the first 20 members as rev.0 _BIX.
398	     */
399	    if (sc->bix.rev != ACPI_BIX_REV_0 &&
400	        sc->bix.rev != ACPI_BIX_REV_1)
401		ACPI_BIX_REV_MISMATCH_ERR(sc->bix.rev, ACPI_BIX_REV_1);
402	} else if (ACPI_PKG_VALID_EQ(res, 20)) {/* ACPI 4.0 _BIX */
403	    if (sc->bix.rev != ACPI_BIX_REV_0)
404		ACPI_BIX_REV_MISMATCH_ERR(sc->bix.rev, ACPI_BIX_REV_0);
405	} else if (ACPI_PKG_VALID(res, 22)) {
406	    /* _BIX with 22 or more members. */
407	    if (ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_1 + 1)) {
408		/*
409		 * Unknown revision number.
410		 * Assume 21 members are compatible with 6.0 _BIX.
411		 */
412		ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
413		    "Unknown _BIX revision(%u). "
414		    "Assuming compatible with revision %u.\n",
415		    sc->bix.rev, ACPI_BIX_REV_1);
416	    } else {
417		/*
418		 * Known revision number.  Ignore the extra members.
419		 */
420		ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
421		    "Extra objects found in _BIX were ignored.\n");
422	    }
423	} else {
424		/* Invalid _BIX.  Ignore it. */
425		ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
426		    "Invalid _BIX found (rev=%u, count=%u).  Ignored.\n",
427		    sc->bix.rev, res->Package.Count);
428		goto end;
429	}
430	break;
431#undef	ACPI_BIX_REV_MISMATCH_ERR
432    case _BIF:
433	if (ACPI_PKG_VALID_EQ(res, 13))	/* _BIF */
434	    sc->bix.rev = ACPI_BIX_REV_BIF;
435	else {
436		ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
437		    "Invalid _BIF found (count=%u).  Ignored.\n",
438		    res->Package.Count);
439		goto end;
440	}
441	break;
442    }
443
444    ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
445	"rev = %04x\n", sc->bix.rev);
446#define	BIX_GETU32(NAME)	do {			\
447    ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),	\
448	#NAME " = %u\n", sc->bix.NAME);			\
449    if (acpi_PkgInt32(res, i++, &sc->bix.NAME) != 0)	\
450	    goto end;					\
451    } while (0)
452
453    BIX_GETU32(units);
454    BIX_GETU32(dcap);
455    BIX_GETU32(lfcap);
456    BIX_GETU32(btech);
457    BIX_GETU32(dvol);
458    BIX_GETU32(wcap);
459    BIX_GETU32(lcap);
460    if (ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_0)) {
461	    BIX_GETU32(cycles);
462	    BIX_GETU32(accuracy);
463	    BIX_GETU32(stmax);
464	    BIX_GETU32(stmin);
465	    BIX_GETU32(aimax);
466	    BIX_GETU32(aimin);
467    }
468    BIX_GETU32(gra1);
469    BIX_GETU32(gra2);
470    if (acpi_PkgStr(res, i++, sc->bix.model, ACPI_CMBAT_MAXSTRLEN) != 0)
471	    goto end;
472    if (acpi_PkgStr(res, i++, sc->bix.serial, ACPI_CMBAT_MAXSTRLEN) != 0)
473	    goto end;
474    if (acpi_PkgStr(res, i++, sc->bix.type, ACPI_CMBAT_MAXSTRLEN) != 0)
475	    goto end;
476    if (acpi_PkgStr(res, i++, sc->bix.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0)
477	    goto end;
478    if (ACPI_BIX_REV_MIN_CHECK(sc->bix.rev, ACPI_BIX_REV_1))
479	    BIX_GETU32(scap);
480#undef	BIX_GETU32
481end:
482    AcpiOsFree(bix_buffer.Pointer);
483}
484
485static int
486acpi_cmbat_bix(device_t dev, void *bix, size_t len)
487{
488    struct acpi_cmbat_softc *sc;
489
490    if (len != sizeof(struct acpi_bix) &&
491	len != sizeof(struct acpi_bif))
492	    return (-1);
493
494    sc = device_get_softc(dev);
495
496    /*
497     * Just copy the data.  The only value that should change is the
498     * last-full capacity, so we only update when we get a notify that says
499     * the info has changed.  Many systems apparently take a long time to
500     * process a _BI[FX] call so we avoid it if possible.
501     */
502    ACPI_SERIAL_BEGIN(cmbat);
503    memcpy(bix, &sc->bix, len);
504    ACPI_SERIAL_END(cmbat);
505
506    return (0);
507}
508
509static int
510acpi_cmbat_bst(device_t dev, struct acpi_bst *bst)
511{
512    struct acpi_cmbat_softc *sc;
513
514    sc = device_get_softc(dev);
515
516    ACPI_SERIAL_BEGIN(cmbat);
517    if (acpi_BatteryIsPresent(dev)) {
518	acpi_cmbat_get_bst(dev);
519	memcpy(bst, &sc->bst, sizeof(*bst));
520    } else
521	bst->state = ACPI_BATT_STAT_NOT_PRESENT;
522    ACPI_SERIAL_END(cmbat);
523
524    return (0);
525}
526
527static void
528acpi_cmbat_init_battery(void *arg)
529{
530    struct acpi_cmbat_softc *sc;
531    int		retry, valid;
532    device_t	dev;
533
534    dev = (device_t)arg;
535    ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
536	"battery enitialization start\n");
537
538    /*
539     * Try repeatedly to get valid data from the battery.  Since the
540     * embedded controller isn't always ready just after boot, we may have
541     * to wait a while.
542     */
543    for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
544	/*
545	 * Batteries on DOCK can be ejected w/ DOCK during retrying.
546	 *
547	 * If there is a valid softc pointer the device may be in
548	 * attaching, attached or detaching state. If the state is
549	 * different from attached retry getting the device state
550	 * until it becomes stable. This solves a race if the ACPI
551	 * notification handler is called during attach, because
552	 * device_is_attached() doesn't return non-zero until after
553	 * the attach code has been executed.
554	 */
555	ACPI_SERIAL_BEGIN(cmbat);
556	sc = device_get_softc(dev);
557	if (sc == NULL) {
558	    ACPI_SERIAL_END(cmbat);
559	    return;
560	}
561
562	if (!acpi_BatteryIsPresent(dev) || !device_is_attached(dev)) {
563	    ACPI_SERIAL_END(cmbat);
564	    continue;
565	}
566
567	/*
568	 * Only query the battery if this is the first try or the specific
569	 * type of info is still invalid.
570	 */
571	if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) {
572	    timespecclear(&sc->bst_lastupdated);
573	    acpi_cmbat_get_bst(dev);
574	}
575	if (retry == 0 || !acpi_battery_bix_valid(&sc->bix))
576	    acpi_cmbat_get_bix(dev);
577
578	valid = acpi_battery_bst_valid(&sc->bst) &&
579	    acpi_battery_bix_valid(&sc->bix);
580	ACPI_SERIAL_END(cmbat);
581
582	if (valid)
583	    break;
584    }
585
586    if (retry == ACPI_CMBAT_RETRY_MAX) {
587	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
588	    "battery initialization failed, giving up\n");
589    } else {
590	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
591	    "battery initialization done, tried %d times\n", retry + 1);
592    }
593}
594