170271Stakawata/*-
2148352Snjl * Copyright (c) 2005 Nate Lawson
378662Siwasaki * Copyright (c) 2000 Munehiro Matsuda
470271Stakawata * Copyright (c) 2000 Takanori Watanabe
570271Stakawata * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
670271Stakawata * All rights reserved.
770271Stakawata *
870271Stakawata * Redistribution and use in source and binary forms, with or without
970271Stakawata * modification, are permitted provided that the following conditions
1070271Stakawata * are met:
1170271Stakawata * 1. Redistributions of source code must retain the above copyright
1270271Stakawata *    notice, this list of conditions and the following disclaimer.
1370271Stakawata * 2. Redistributions in binary form must reproduce the above copyright
1470271Stakawata *    notice, this list of conditions and the following disclaimer in the
1570271Stakawata *    documentation and/or other materials provided with the distribution.
1670271Stakawata *
1770271Stakawata * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1870271Stakawata * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1970271Stakawata * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2070271Stakawata * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2170271Stakawata * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2270271Stakawata * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2370271Stakawata * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2470271Stakawata * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2570271Stakawata * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2670271Stakawata * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2770271Stakawata * SUCH DAMAGE.
2870271Stakawata */
2970271Stakawata
30148352Snjl#include <sys/cdefs.h>
31148352Snjl__FBSDID("$FreeBSD: stable/10/sys/dev/acpica/acpi_cmbat.c 315264 2017-03-14 15:56:19Z hselasky $");
32148352Snjl
3370271Stakawata#include "opt_acpi.h"
3470271Stakawata#include <sys/param.h>
3570271Stakawata#include <sys/kernel.h>
36129879Sphk#include <sys/module.h>
3770271Stakawata#include <sys/bus.h>
3870271Stakawata#include <sys/ioccom.h>
3970271Stakawata
4070271Stakawata#include <machine/bus.h>
4170271Stakawata#include <sys/rman.h>
4270271Stakawata#include <sys/malloc.h>
4370271Stakawata
44193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
45193530Sjkim
4670271Stakawata#include <dev/acpica/acpivar.h>
4770271Stakawata#include <dev/acpica/acpiio.h>
4870271Stakawata
49227293Sedstatic MALLOC_DEFINE(M_ACPICMBAT, "acpicmbat",
50227293Sed    "ACPI control method battery data");
5185729Siwasaki
52118783Snjl/* Number of times to retry initialization before giving up. */
53118783Snjl#define ACPI_CMBAT_RETRY_MAX	6
54118783Snjl
55118783Snjl/* Check the battery once a minute. */
56133615Snjl#define	CMBAT_POLLRATE		(60 * hz)
5785325Siwasaki
58118783Snjl/* Hooks for the ACPI CA debugging infrastructure */
5985738Siwasaki#define	_COMPONENT	ACPI_BATTERY
6091121SmsmithACPI_MODULE_NAME("BATTERY")
6177432Smsmith
62133615Snjl#define	ACPI_BATTERY_BST_CHANGE	0x80
63133615Snjl#define	ACPI_BATTERY_BIF_CHANGE	0x81
6470340Siwasaki
6585738Siwasakistruct acpi_cmbat_softc {
66118783Snjl    device_t	    dev;
67148352Snjl    int		    flags;
6885738Siwasaki
69118783Snjl    struct acpi_bif bif;
70118783Snjl    struct acpi_bst bst;
71118783Snjl    struct timespec bst_lastupdated;
7285738Siwasaki};
7385738Siwasaki
74133615SnjlACPI_SERIAL_DECL(cmbat, "ACPI cmbat");
7585738Siwasaki
76148352Snjlstatic int		acpi_cmbat_probe(device_t dev);
77148352Snjlstatic int		acpi_cmbat_attach(device_t dev);
78148352Snjlstatic int		acpi_cmbat_detach(device_t dev);
79148352Snjlstatic int		acpi_cmbat_resume(device_t dev);
80148352Snjlstatic void		acpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify,
81148352Snjl			    void *context);
82148352Snjlstatic int		acpi_cmbat_info_expired(struct timespec *lastupdated);
83148352Snjlstatic void		acpi_cmbat_info_updated(struct timespec *lastupdated);
84152705Snjlstatic void		acpi_cmbat_get_bst(void *arg);
85152818Snjlstatic void		acpi_cmbat_get_bif_task(void *arg);
86152705Snjlstatic void		acpi_cmbat_get_bif(void *arg);
87148352Snjlstatic int		acpi_cmbat_bst(device_t dev, struct acpi_bst *bstp);
88148352Snjlstatic int		acpi_cmbat_bif(device_t dev, struct acpi_bif *bifp);
89148352Snjlstatic void		acpi_cmbat_init_battery(void *arg);
9085738Siwasaki
91118926Snjlstatic device_method_t acpi_cmbat_methods[] = {
92118926Snjl    /* Device interface */
93118926Snjl    DEVMETHOD(device_probe,	acpi_cmbat_probe),
94118926Snjl    DEVMETHOD(device_attach,	acpi_cmbat_attach),
95132049Snjl    DEVMETHOD(device_detach,	acpi_cmbat_detach),
96118926Snjl    DEVMETHOD(device_resume,	acpi_cmbat_resume),
97118926Snjl
98148352Snjl    /* ACPI battery interface */
99148352Snjl    DEVMETHOD(acpi_batt_get_info, acpi_cmbat_bif),
100148352Snjl    DEVMETHOD(acpi_batt_get_status, acpi_cmbat_bst),
101148352Snjl
102246128Ssbz    DEVMETHOD_END
103118926Snjl};
104118926Snjl
105118926Snjlstatic driver_t acpi_cmbat_driver = {
106148352Snjl    "battery",
107118926Snjl    acpi_cmbat_methods,
108118926Snjl    sizeof(struct acpi_cmbat_softc),
109118926Snjl};
110118926Snjl
111118926Snjlstatic devclass_t acpi_cmbat_devclass;
112118926SnjlDRIVER_MODULE(acpi_cmbat, acpi, acpi_cmbat_driver, acpi_cmbat_devclass, 0, 0);
113128071SnjlMODULE_DEPEND(acpi_cmbat, acpi, 1, 1, 1);
114118926Snjl
115118926Snjlstatic int
116148352Snjlacpi_cmbat_probe(device_t dev)
117148352Snjl{
118148352Snjl    static char *cmbat_ids[] = { "PNP0C0A", NULL };
119148352Snjl
120148352Snjl    if (acpi_disabled("cmbat") ||
121148352Snjl	ACPI_ID_PROBE(device_get_parent(dev), dev, cmbat_ids) == NULL)
122148352Snjl	return (ENXIO);
123148352Snjl
124148352Snjl    device_set_desc(dev, "ACPI Control Method Battery");
125148352Snjl    return (0);
126148352Snjl}
127148352Snjl
128148352Snjlstatic int
129148352Snjlacpi_cmbat_attach(device_t dev)
130148352Snjl{
131148352Snjl    int		error;
132148352Snjl    ACPI_HANDLE	handle;
133148352Snjl    struct acpi_cmbat_softc *sc;
134148352Snjl
135148352Snjl    sc = device_get_softc(dev);
136148352Snjl    handle = acpi_get_handle(dev);
137148352Snjl    sc->dev = dev;
138148352Snjl
139148352Snjl    timespecclear(&sc->bst_lastupdated);
140148352Snjl
141148352Snjl    error = acpi_battery_register(dev);
142148352Snjl    if (error != 0) {
143148352Snjl    	device_printf(dev, "registering battery failed\n");
144148352Snjl	return (error);
145148352Snjl    }
146148352Snjl
147148352Snjl    /*
148148352Snjl     * Install a system notify handler in addition to the device notify.
149148352Snjl     * Toshiba notebook uses this alternate notify for its battery.
150148352Snjl     */
151148352Snjl    AcpiInstallNotifyHandler(handle, ACPI_ALL_NOTIFY,
152148352Snjl	acpi_cmbat_notify_handler, dev);
153148352Snjl
154167814Sjkim    AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
155148352Snjl
156148352Snjl    return (0);
157148352Snjl}
158148352Snjl
159148352Snjlstatic int
160148352Snjlacpi_cmbat_detach(device_t dev)
161148352Snjl{
162157778Siwasaki    ACPI_HANDLE	handle;
163148352Snjl
164157778Siwasaki    handle = acpi_get_handle(dev);
165157778Siwasaki    AcpiRemoveNotifyHandler(handle, ACPI_ALL_NOTIFY, acpi_cmbat_notify_handler);
166148352Snjl    acpi_battery_remove(dev);
167315264Shselasky
168315264Shselasky    /*
169315264Shselasky     * Force any pending notification handler calls to complete by
170315264Shselasky     * requesting cmbat serialisation while freeing and clearing the
171315264Shselasky     * softc pointer:
172315264Shselasky     */
173315264Shselasky    ACPI_SERIAL_BEGIN(cmbat);
174315264Shselasky    device_set_softc(dev, NULL);
175315264Shselasky    ACPI_SERIAL_END(cmbat);
176315264Shselasky
177148352Snjl    return (0);
178148352Snjl}
179148352Snjl
180148352Snjlstatic int
181148352Snjlacpi_cmbat_resume(device_t dev)
182148352Snjl{
183148352Snjl
184167814Sjkim    AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_init_battery, dev);
185148352Snjl    return (0);
186148352Snjl}
187148352Snjl
188148352Snjlstatic void
189148352Snjlacpi_cmbat_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
190148352Snjl{
191148352Snjl    struct acpi_cmbat_softc *sc;
192148352Snjl    device_t dev;
193148352Snjl
194148352Snjl    dev = (device_t)context;
195148352Snjl    sc = device_get_softc(dev);
196148352Snjl
197148352Snjl    switch (notify) {
198148352Snjl    case ACPI_NOTIFY_DEVICE_CHECK:
199148352Snjl    case ACPI_BATTERY_BST_CHANGE:
200152705Snjl	/*
201152705Snjl	 * Clear the last updated time.  The next call to retrieve the
202152705Snjl	 * battery status will get the new value for us.
203152705Snjl	 */
204148352Snjl	timespecclear(&sc->bst_lastupdated);
205148352Snjl	break;
206148352Snjl    case ACPI_NOTIFY_BUS_CHECK:
207148352Snjl    case ACPI_BATTERY_BIF_CHANGE:
208152705Snjl	/*
209152705Snjl	 * Queue a callback to get the current battery info from thread
210152705Snjl	 * context.  It's not safe to block in a notify handler.
211152705Snjl	 */
212167814Sjkim	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cmbat_get_bif_task, dev);
213148352Snjl	break;
214148352Snjl    }
215148352Snjl
216148352Snjl    acpi_UserNotify("CMBAT", h, notify);
217148352Snjl}
218148352Snjl
219148352Snjlstatic int
22078662Siwasakiacpi_cmbat_info_expired(struct timespec *lastupdated)
22178662Siwasaki{
222118783Snjl    struct timespec	curtime;
22378662Siwasaki
224133615Snjl    ACPI_SERIAL_ASSERT(cmbat);
225133615Snjl
226118783Snjl    if (lastupdated == NULL)
227133615Snjl	return (TRUE);
228118783Snjl    if (!timespecisset(lastupdated))
229133615Snjl	return (TRUE);
23078662Siwasaki
231118783Snjl    getnanotime(&curtime);
232118783Snjl    timespecsub(&curtime, lastupdated);
233118783Snjl    return (curtime.tv_sec < 0 ||
234118783Snjl	    curtime.tv_sec > acpi_battery_get_info_expire());
23578662Siwasaki}
23678662Siwasaki
237118926Snjlstatic void
23878662Siwasakiacpi_cmbat_info_updated(struct timespec *lastupdated)
23978662Siwasaki{
240133615Snjl
241133615Snjl    ACPI_SERIAL_ASSERT(cmbat);
242133615Snjl
243118783Snjl    if (lastupdated != NULL)
244118783Snjl	getnanotime(lastupdated);
24578662Siwasaki}
24678662Siwasaki
24770271Stakawatastatic void
248152705Snjlacpi_cmbat_get_bst(void *arg)
24970271Stakawata{
250118783Snjl    struct acpi_cmbat_softc *sc;
251118783Snjl    ACPI_STATUS	as;
252123777Snjl    ACPI_OBJECT	*res;
253118783Snjl    ACPI_HANDLE	h;
254118783Snjl    ACPI_BUFFER	bst_buffer;
255152705Snjl    device_t dev;
25678662Siwasaki
257133615Snjl    ACPI_SERIAL_ASSERT(cmbat);
258133615Snjl
259152705Snjl    dev = arg;
260118783Snjl    sc = device_get_softc(dev);
261118783Snjl    h = acpi_get_handle(dev);
262133615Snjl    bst_buffer.Pointer = NULL;
263133615Snjl    bst_buffer.Length = ACPI_ALLOCATE_BUFFER;
26485738Siwasaki
265118783Snjl    if (!acpi_cmbat_info_expired(&sc->bst_lastupdated))
266133615Snjl	goto end;
26778662Siwasaki
268118783Snjl    as = AcpiEvaluateObject(h, "_BST", NULL, &bst_buffer);
269118783Snjl    if (ACPI_FAILURE(as)) {
270118783Snjl	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
27188420Siwasaki		    "error fetching current battery status -- %s\n",
27288420Siwasaki		    AcpiFormatException(as));
273118783Snjl	goto end;
274118783Snjl    }
27570271Stakawata
276118783Snjl    res = (ACPI_OBJECT *)bst_buffer.Pointer;
277123777Snjl    if (!ACPI_PKG_VALID(res, 4)) {
278118783Snjl	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
27986552Siwasaki		    "battery status corrupted\n");
280118783Snjl	goto end;
281118783Snjl    }
28270271Stakawata
283123777Snjl    if (acpi_PkgInt32(res, 0, &sc->bst.state) != 0)
284123777Snjl	goto end;
285123777Snjl    if (acpi_PkgInt32(res, 1, &sc->bst.rate) != 0)
286123777Snjl	goto end;
287123777Snjl    if (acpi_PkgInt32(res, 2, &sc->bst.cap) != 0)
288123777Snjl	goto end;
289123777Snjl    if (acpi_PkgInt32(res, 3, &sc->bst.volt) != 0)
290123777Snjl	goto end;
291118783Snjl    acpi_cmbat_info_updated(&sc->bst_lastupdated);
292118783Snjl
293216503Savg    /* Clear out undefined/extended bits that might be set by hardware. */
294216503Savg    sc->bst.state &= ACPI_BATT_STAT_BST_MASK;
295216503Savg    if ((sc->bst.state & ACPI_BATT_STAT_INVALID) == ACPI_BATT_STAT_INVALID)
296216503Savg	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
297216503Savg	    "battery reports simultaneous charging and discharging\n");
298216503Savg
299139057Snjl    /* XXX If all batteries are critical, perhaps we should suspend. */
300139057Snjl    if (sc->bst.state & ACPI_BATT_STAT_CRITICAL) {
301139057Snjl    	if ((sc->flags & ACPI_BATT_STAT_CRITICAL) == 0) {
302139057Snjl	    sc->flags |= ACPI_BATT_STAT_CRITICAL;
303139057Snjl	    device_printf(dev, "critically low charge!\n");
304139057Snjl	}
305139057Snjl    } else
306139057Snjl	sc->flags &= ~ACPI_BATT_STAT_CRITICAL;
307136369Snjl
30870271Stakawataend:
309118783Snjl    if (bst_buffer.Pointer != NULL)
310118783Snjl	AcpiOsFree(bst_buffer.Pointer);
31170271Stakawata}
31270271Stakawata
313152818Snjl/* XXX There should be a cleaner way to do this locking. */
31470271Stakawatastatic void
315152818Snjlacpi_cmbat_get_bif_task(void *arg)
316152818Snjl{
317152818Snjl
318152818Snjl    ACPI_SERIAL_BEGIN(cmbat);
319152818Snjl    acpi_cmbat_get_bif(arg);
320152818Snjl    ACPI_SERIAL_END(cmbat);
321152818Snjl}
322152818Snjl
323152818Snjlstatic void
324152705Snjlacpi_cmbat_get_bif(void *arg)
32570271Stakawata{
326118783Snjl    struct acpi_cmbat_softc *sc;
327118783Snjl    ACPI_STATUS	as;
328123777Snjl    ACPI_OBJECT	*res;
329118783Snjl    ACPI_HANDLE	h;
330118783Snjl    ACPI_BUFFER	bif_buffer;
331152705Snjl    device_t dev;
33270271Stakawata
333133615Snjl    ACPI_SERIAL_ASSERT(cmbat);
334133615Snjl
335152705Snjl    dev = arg;
336118783Snjl    sc = device_get_softc(dev);
337118783Snjl    h = acpi_get_handle(dev);
338133615Snjl    bif_buffer.Pointer = NULL;
339133615Snjl    bif_buffer.Length = ACPI_ALLOCATE_BUFFER;
34085738Siwasaki
341118783Snjl    as = AcpiEvaluateObject(h, "_BIF", NULL, &bif_buffer);
342118783Snjl    if (ACPI_FAILURE(as)) {
343118783Snjl	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
34488420Siwasaki		    "error fetching current battery info -- %s\n",
34588420Siwasaki		    AcpiFormatException(as));
346118783Snjl	goto end;
347118783Snjl    }
34870271Stakawata
349118783Snjl    res = (ACPI_OBJECT *)bif_buffer.Pointer;
350123777Snjl    if (!ACPI_PKG_VALID(res, 13)) {
351118783Snjl	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
35286552Siwasaki		    "battery info corrupted\n");
353118783Snjl	goto end;
354118783Snjl    }
35578662Siwasaki
356133615Snjl    if (acpi_PkgInt32(res, 0, &sc->bif.units) != 0)
357123777Snjl	goto end;
358133615Snjl    if (acpi_PkgInt32(res, 1, &sc->bif.dcap) != 0)
359123777Snjl	goto end;
360133615Snjl    if (acpi_PkgInt32(res, 2, &sc->bif.lfcap) != 0)
361123777Snjl	goto end;
362133615Snjl    if (acpi_PkgInt32(res, 3, &sc->bif.btech) != 0)
363123777Snjl	goto end;
364133615Snjl    if (acpi_PkgInt32(res, 4, &sc->bif.dvol) != 0)
365123777Snjl	goto end;
366133615Snjl    if (acpi_PkgInt32(res, 5, &sc->bif.wcap) != 0)
367123777Snjl	goto end;
368133615Snjl    if (acpi_PkgInt32(res, 6, &sc->bif.lcap) != 0)
369123777Snjl	goto end;
370133615Snjl    if (acpi_PkgInt32(res, 7, &sc->bif.gra1) != 0)
371123777Snjl	goto end;
372133615Snjl    if (acpi_PkgInt32(res, 8, &sc->bif.gra2) != 0)
373123777Snjl	goto end;
374123777Snjl    if (acpi_PkgStr(res,  9, sc->bif.model, ACPI_CMBAT_MAXSTRLEN) != 0)
375123777Snjl	goto end;
376123777Snjl    if (acpi_PkgStr(res, 10, sc->bif.serial, ACPI_CMBAT_MAXSTRLEN) != 0)
377123777Snjl	goto end;
378123777Snjl    if (acpi_PkgStr(res, 11, sc->bif.type, ACPI_CMBAT_MAXSTRLEN) != 0)
379123777Snjl	goto end;
380123777Snjl    if (acpi_PkgStr(res, 12, sc->bif.oeminfo, ACPI_CMBAT_MAXSTRLEN) != 0)
381123777Snjl	goto end;
382118783Snjl
38370271Stakawataend:
384118783Snjl    if (bif_buffer.Pointer != NULL)
385118783Snjl	AcpiOsFree(bif_buffer.Pointer);
38670271Stakawata}
38770271Stakawata
38870271Stakawatastatic int
389148352Snjlacpi_cmbat_bif(device_t dev, struct acpi_bif *bifp)
39070271Stakawata{
391118783Snjl    struct acpi_cmbat_softc *sc;
39278662Siwasaki
393133615Snjl    sc = device_get_softc(dev);
394119974Snjl
395152705Snjl    /*
396152705Snjl     * Just copy the data.  The only value that should change is the
397152705Snjl     * last-full capacity, so we only update when we get a notify that says
398152705Snjl     * the info has changed.  Many systems apparently take a long time to
399152705Snjl     * process a _BIF call so we avoid it if possible.
400152705Snjl     */
401133615Snjl    ACPI_SERIAL_BEGIN(cmbat);
402148352Snjl    bifp->units = sc->bif.units;
403148352Snjl    bifp->dcap = sc->bif.dcap;
404148352Snjl    bifp->lfcap = sc->bif.lfcap;
405148352Snjl    bifp->btech = sc->bif.btech;
406148352Snjl    bifp->dvol = sc->bif.dvol;
407148352Snjl    bifp->wcap = sc->bif.wcap;
408148352Snjl    bifp->lcap = sc->bif.lcap;
409148352Snjl    bifp->gra1 = sc->bif.gra1;
410148352Snjl    bifp->gra2 = sc->bif.gra2;
411148352Snjl    strncpy(bifp->model, sc->bif.model, sizeof(sc->bif.model));
412148352Snjl    strncpy(bifp->serial, sc->bif.serial, sizeof(sc->bif.serial));
413148352Snjl    strncpy(bifp->type, sc->bif.type, sizeof(sc->bif.type));
414148352Snjl    strncpy(bifp->oeminfo, sc->bif.oeminfo, sizeof(sc->bif.oeminfo));
415133615Snjl    ACPI_SERIAL_END(cmbat);
416133615Snjl
417118783Snjl    return (0);
41870271Stakawata}
41970271Stakawata
42085325Siwasakistatic int
421148352Snjlacpi_cmbat_bst(device_t dev, struct acpi_bst *bstp)
422132049Snjl{
423132049Snjl    struct acpi_cmbat_softc *sc;
424132049Snjl
425132049Snjl    sc = device_get_softc(dev);
426132049Snjl
427133615Snjl    ACPI_SERIAL_BEGIN(cmbat);
428148352Snjl    if (acpi_BatteryIsPresent(dev)) {
429148352Snjl	acpi_cmbat_get_bst(dev);
430148352Snjl	bstp->state = sc->bst.state;
431148352Snjl	bstp->rate = sc->bst.rate;
432148352Snjl	bstp->cap = sc->bst.cap;
433148352Snjl	bstp->volt = sc->bst.volt;
434148352Snjl    } else
435148352Snjl	bstp->state = ACPI_BATT_STAT_NOT_PRESENT;
436133615Snjl    ACPI_SERIAL_END(cmbat);
43778662Siwasaki
438118783Snjl    return (0);
43970340Siwasaki}
44078662Siwasaki
441106377Siwasakistatic void
442106377Siwasakiacpi_cmbat_init_battery(void *arg)
443106377Siwasaki{
444133615Snjl    struct acpi_cmbat_softc *sc;
445148352Snjl    int		retry, valid;
446133615Snjl    device_t	dev;
447106377Siwasaki
448133615Snjl    dev = (device_t)arg;
449118783Snjl    ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
450118783Snjl		"battery initialization start\n");
451106377Siwasaki
452148352Snjl    /*
453148352Snjl     * Try repeatedly to get valid data from the battery.  Since the
454148352Snjl     * embedded controller isn't always ready just after boot, we may have
455148352Snjl     * to wait a while.
456148352Snjl     */
457138300Smarks    for (retry = 0; retry < ACPI_CMBAT_RETRY_MAX; retry++, AcpiOsSleep(10000)) {
458315264Shselasky	/*
459315264Shselasky	 * Batteries on DOCK can be ejected w/ DOCK during retrying.
460315264Shselasky	 *
461315264Shselasky	 * If there is a valid softc pointer the device may be in
462315264Shselasky	 * attaching, attached or detaching state. If the state is
463315264Shselasky	 * different from attached retry getting the device state
464315264Shselasky	 * until it becomes stable. This solves a race if the ACPI
465315264Shselasky	 * notification handler is called during attach, because
466315264Shselasky	 * device_is_attached() doesn't return non-zero until after
467315264Shselasky	 * the attach code has been executed.
468315264Shselasky	 */
469315264Shselasky	ACPI_SERIAL_BEGIN(cmbat);
470315264Shselasky	sc = device_get_softc(dev);
471315264Shselasky	if (sc == NULL) {
472315264Shselasky	    ACPI_SERIAL_END(cmbat);
473157774Siwasaki	    return;
474315264Shselasky	}
475157774Siwasaki
476315264Shselasky	if (!acpi_BatteryIsPresent(dev) || !device_is_attached(dev)) {
477315264Shselasky	    ACPI_SERIAL_END(cmbat);
478118783Snjl	    continue;
479315264Shselasky	}
480106377Siwasaki
481152705Snjl	/*
482152705Snjl	 * Only query the battery if this is the first try or the specific
483152705Snjl	 * type of info is still invalid.
484152705Snjl	 */
485152705Snjl	if (retry == 0 || !acpi_battery_bst_valid(&sc->bst)) {
486152705Snjl	    timespecclear(&sc->bst_lastupdated);
487152705Snjl	    acpi_cmbat_get_bst(dev);
488152705Snjl	}
489152705Snjl	if (retry == 0 || !acpi_battery_bif_valid(&sc->bif))
490152705Snjl	    acpi_cmbat_get_bif(dev);
491152705Snjl
492148352Snjl	valid = acpi_battery_bst_valid(&sc->bst) &&
493148352Snjl	    acpi_battery_bif_valid(&sc->bif);
494133615Snjl	ACPI_SERIAL_END(cmbat);
495106377Siwasaki
496148352Snjl	if (valid)
497133615Snjl	    break;
498118783Snjl    }
499106377Siwasaki
500118783Snjl    if (retry == ACPI_CMBAT_RETRY_MAX) {
501118783Snjl	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
502118783Snjl		    "battery initialization failed, giving up\n");
503118783Snjl    } else {
504118783Snjl	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
505118783Snjl		    "battery initialization done, tried %d times\n", retry + 1);
506118783Snjl    }
507106377Siwasaki}
508