1151564Snjl/*-
2151564Snjl * Copyright (c) 2005 Hans Petter Selasky
3151564Snjl * All rights reserved.
4151564Snjl *
5151564Snjl * Redistribution and use in source and binary forms, with or without
6151564Snjl * modification, are permitted provided that the following conditions
7151564Snjl * are met:
8151564Snjl * 1. Redistributions of source code must retain the above copyright
9151564Snjl *    notice, this list of conditions and the following disclaimer.
10151564Snjl * 2. Redistributions in binary form must reproduce the above copyright
11151564Snjl *    notice, this list of conditions and the following disclaimer in the
12151564Snjl *    documentation and/or other materials provided with the distribution.
13151564Snjl *
14151564Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15151564Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16151564Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17151564Snjl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18151564Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19151564Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20151564Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21151564Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22151564Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23151564Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24151564Snjl * SUCH DAMAGE.
25151564Snjl */
26151564Snjl
27151564Snjl#include <sys/cdefs.h>
28151564Snjl__FBSDID("$FreeBSD$");
29151564Snjl
30151564Snjl#include "opt_acpi.h"
31151564Snjl#include <sys/param.h>
32151564Snjl#include <sys/kernel.h>
33151564Snjl#include <sys/module.h>
34151564Snjl#include <sys/bus.h>
35151564Snjl
36193530Sjkim#include <contrib/dev/acpica/include/acpi.h>
37193530Sjkim
38151564Snjl#include <dev/acpica/acpivar.h>
39151564Snjl#include <dev/acpica/acpiio.h>
40151564Snjl#include <dev/acpica/acpi_smbus.h>
41151564Snjl
42151564Snjl/* Transactions have failed after 500 ms. */
43151564Snjl#define SMBUS_TIMEOUT	50
44151564Snjl
45151564Snjlstruct acpi_smbat_softc {
46151564Snjl	uint8_t		sb_base_addr;
47151564Snjl	device_t	ec_dev;
48152677Sume
49152677Sume	struct acpi_bif	bif;
50152677Sume	struct acpi_bst	bst;
51152677Sume	struct timespec	bif_lastupdated;
52152677Sume	struct timespec	bst_lastupdated;
53151564Snjl};
54151564Snjl
55151564Snjlstatic int	acpi_smbat_probe(device_t dev);
56151564Snjlstatic int	acpi_smbat_attach(device_t dev);
57151564Snjlstatic int	acpi_smbat_shutdown(device_t dev);
58152677Sumestatic int	acpi_smbat_info_expired(struct timespec *lastupdated);
59152677Sumestatic void	acpi_smbat_info_updated(struct timespec *lastupdated);
60151564Snjlstatic int	acpi_smbat_get_bif(device_t dev, struct acpi_bif *bif);
61151564Snjlstatic int	acpi_smbat_get_bst(device_t dev, struct acpi_bst *bst);
62151564Snjl
63151564SnjlACPI_SERIAL_DECL(smbat, "ACPI Smart Battery");
64151564Snjl
65227309Sedstatic SYSCTL_NODE(_debug_acpi, OID_AUTO, batt, CTLFLAG_RD, NULL,
66227309Sed    "Battery debugging");
67186026Ssilby
68186026Ssilby/* On some laptops with smart batteries, enabling battery monitoring
69186026Ssilby * software causes keystrokes from atkbd to be lost.  This has also been
70186026Ssilby * reported on Linux, and is apparently due to the keyboard and I2C line
71186026Ssilby * for the battery being routed through the same chip.  Whether that's
72186026Ssilby * accurate or not, adding extra sleeps to the status checking code
73186026Ssilby * causes the problem to go away.
74186026Ssilby *
75186026Ssilby * If you experience that problem, try a value of 10ms and move up
76186026Ssilby * from there.
77186026Ssilby */
78186026Ssilbystatic int      batt_sleep_ms;
79186026SsilbySYSCTL_INT(_debug_acpi_batt, OID_AUTO, batt_sleep_ms, CTLFLAG_RW, &batt_sleep_ms, 0,
80186026Ssilby    "Sleep during battery status updates to prevent keystroke loss.");
81186026Ssilby
82151564Snjlstatic device_method_t acpi_smbat_methods[] = {
83151564Snjl	/* device interface */
84151564Snjl	DEVMETHOD(device_probe, acpi_smbat_probe),
85151564Snjl	DEVMETHOD(device_attach, acpi_smbat_attach),
86151564Snjl	DEVMETHOD(device_shutdown, acpi_smbat_shutdown),
87151564Snjl
88151564Snjl	/* ACPI battery interface */
89151564Snjl	DEVMETHOD(acpi_batt_get_status, acpi_smbat_get_bst),
90151564Snjl	DEVMETHOD(acpi_batt_get_info, acpi_smbat_get_bif),
91151564Snjl
92246128Ssbz	DEVMETHOD_END
93151564Snjl};
94151564Snjl
95151564Snjlstatic driver_t	acpi_smbat_driver = {
96151564Snjl	"battery",
97151564Snjl	acpi_smbat_methods,
98151564Snjl	sizeof(struct acpi_smbat_softc),
99151564Snjl};
100151564Snjl
101151564Snjlstatic devclass_t acpi_smbat_devclass;
102151564SnjlDRIVER_MODULE(acpi_smbat, acpi, acpi_smbat_driver, acpi_smbat_devclass, 0, 0);
103151564SnjlMODULE_DEPEND(acpi_smbat, acpi, 1, 1, 1);
104151564Snjl
105151564Snjlstatic int
106151564Snjlacpi_smbat_probe(device_t dev)
107151564Snjl{
108151564Snjl	static char *smbat_ids[] = {"ACPI0001", "ACPI0005", NULL};
109151564Snjl	ACPI_STATUS status;
110151564Snjl
111151564Snjl	if (acpi_disabled("smbat") ||
112151564Snjl	    ACPI_ID_PROBE(device_get_parent(dev), dev, smbat_ids) == NULL)
113151564Snjl		return (ENXIO);
114151564Snjl	status = AcpiEvaluateObject(acpi_get_handle(dev), "_EC", NULL, NULL);
115151564Snjl	if (ACPI_FAILURE(status))
116151564Snjl		return (ENXIO);
117151564Snjl
118151564Snjl	device_set_desc(dev, "ACPI Smart Battery");
119151564Snjl	return (0);
120151564Snjl}
121151564Snjl
122151564Snjlstatic int
123151564Snjlacpi_smbat_attach(device_t dev)
124151564Snjl{
125151564Snjl	struct acpi_smbat_softc *sc;
126151564Snjl	uint32_t base;
127151564Snjl
128151564Snjl	sc = device_get_softc(dev);
129151564Snjl	if (ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_EC", &base))) {
130151564Snjl		device_printf(dev, "cannot get EC base address\n");
131151564Snjl		return (ENXIO);
132151564Snjl	}
133151564Snjl	sc->sb_base_addr = (base >> 8) & 0xff;
134151564Snjl
135151564Snjl	/* XXX Only works with one EC, but nearly all systems only have one. */
136151564Snjl	sc->ec_dev = devclass_get_device(devclass_find("acpi_ec"), 0);
137151564Snjl	if (sc->ec_dev == NULL) {
138151564Snjl		device_printf(dev, "cannot find EC device\n");
139151564Snjl		return (ENXIO);
140151564Snjl	}
141151564Snjl
142152677Sume	timespecclear(&sc->bif_lastupdated);
143152677Sume	timespecclear(&sc->bst_lastupdated);
144152677Sume
145151564Snjl	if (acpi_battery_register(dev) != 0) {
146151564Snjl		device_printf(dev, "cannot register battery\n");
147151564Snjl		return (ENXIO);
148151564Snjl	}
149151564Snjl	return (0);
150151564Snjl}
151151564Snjl
152151564Snjlstatic int
153151564Snjlacpi_smbat_shutdown(device_t dev)
154151564Snjl{
155151564Snjl
156151564Snjl	acpi_battery_remove(dev);
157151564Snjl	return (0);
158151564Snjl}
159151564Snjl
160151564Snjlstatic int
161152677Sumeacpi_smbat_info_expired(struct timespec *lastupdated)
162152677Sume{
163152677Sume	struct timespec	curtime;
164152677Sume
165152677Sume	ACPI_SERIAL_ASSERT(smbat);
166152677Sume
167152677Sume	if (lastupdated == NULL)
168152677Sume		return (TRUE);
169152677Sume	if (!timespecisset(lastupdated))
170152677Sume		return (TRUE);
171152677Sume
172152677Sume	getnanotime(&curtime);
173152677Sume	timespecsub(&curtime, lastupdated);
174152677Sume	return (curtime.tv_sec < 0 ||
175152677Sume	    curtime.tv_sec > acpi_battery_get_info_expire());
176152677Sume}
177152677Sume
178152677Sumestatic void
179152677Sumeacpi_smbat_info_updated(struct timespec *lastupdated)
180152677Sume{
181152677Sume
182152677Sume	ACPI_SERIAL_ASSERT(smbat);
183152677Sume
184152677Sume	if (lastupdated != NULL)
185152677Sume		getnanotime(lastupdated);
186152677Sume}
187152677Sume
188152677Sumestatic int
189151564Snjlacpi_smbus_read_2(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd,
190151564Snjl    uint16_t *ptr)
191151564Snjl{
192151564Snjl	int error, to;
193202771Sjkim	UINT64 val;
194151564Snjl
195151564Snjl	ACPI_SERIAL_ASSERT(smbat);
196151564Snjl
197186026Ssilby	if (batt_sleep_ms)
198186026Ssilby	    AcpiOsSleep(batt_sleep_ms);
199186026Ssilby
200151564Snjl	val = addr;
201151564Snjl	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR,
202151564Snjl	    val, 1);
203151564Snjl	if (error)
204151564Snjl		goto out;
205151564Snjl
206151564Snjl	val = cmd;
207151564Snjl	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD,
208151564Snjl	    val, 1);
209151564Snjl	if (error)
210151564Snjl		goto out;
211151564Snjl
212151564Snjl	val = 0x09; /* | 0x80 if PEC */
213151564Snjl	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
214151564Snjl	    val, 1);
215151564Snjl	if (error)
216151564Snjl		goto out;
217151564Snjl
218186026Ssilby	if (batt_sleep_ms)
219186026Ssilby	    AcpiOsSleep(batt_sleep_ms);
220186026Ssilby
221151564Snjl	for (to = SMBUS_TIMEOUT; to != 0; to--) {
222151564Snjl		error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
223151564Snjl		    &val, 1);
224151564Snjl		if (error)
225151564Snjl			goto out;
226151564Snjl		if (val == 0)
227151564Snjl			break;
228151564Snjl		AcpiOsSleep(10);
229151564Snjl	}
230151564Snjl	if (to == 0) {
231151564Snjl		error = ETIMEDOUT;
232151564Snjl		goto out;
233151564Snjl	}
234151564Snjl
235151564Snjl	error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1);
236151564Snjl	if (error)
237151564Snjl		goto out;
238151564Snjl	if (val & SMBUS_STS_MASK) {
239151564Snjl		printf("%s: AE_ERROR 0x%x\n",
240151564Snjl		       __FUNCTION__, (int)(val & SMBUS_STS_MASK));
241151564Snjl		error = EIO;
242151564Snjl		goto out;
243151564Snjl	}
244151564Snjl
245151564Snjl	error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA,
246151564Snjl	    &val, 2);
247151564Snjl	if (error)
248151564Snjl		goto out;
249151564Snjl
250151564Snjl	*ptr = val;
251151564Snjl
252151564Snjlout:
253151564Snjl	return (error);
254151564Snjl}
255151564Snjl
256151564Snjlstatic int
257151564Snjlacpi_smbus_read_multi_1(struct acpi_smbat_softc *sc, uint8_t addr, uint8_t cmd,
258151564Snjl    uint8_t *ptr, uint16_t len)
259151564Snjl{
260202771Sjkim	UINT64 val;
261151564Snjl	uint8_t	to;
262151564Snjl	int error;
263151564Snjl
264151564Snjl	ACPI_SERIAL_ASSERT(smbat);
265151564Snjl
266186026Ssilby	if (batt_sleep_ms)
267186026Ssilby	    AcpiOsSleep(batt_sleep_ms);
268186026Ssilby
269151564Snjl	val = addr;
270151564Snjl	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_ADDR,
271151564Snjl	    val, 1);
272151564Snjl	if (error)
273151564Snjl		goto out;
274151564Snjl
275151564Snjl	val = cmd;
276151564Snjl	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_CMD,
277151564Snjl	    val, 1);
278151564Snjl	if (error)
279151564Snjl		goto out;
280151564Snjl
281151564Snjl	val = 0x0B /* | 0x80 if PEC */ ;
282151564Snjl	error = ACPI_EC_WRITE(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
283151564Snjl	    val, 1);
284151564Snjl	if (error)
285151564Snjl		goto out;
286151564Snjl
287186026Ssilby	if (batt_sleep_ms)
288186026Ssilby	    AcpiOsSleep(batt_sleep_ms);
289186026Ssilby
290151564Snjl	for (to = SMBUS_TIMEOUT; to != 0; to--) {
291151564Snjl		error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_PRTCL,
292151564Snjl		    &val, 1);
293151564Snjl		if (error)
294151564Snjl			goto out;
295151564Snjl		if (val == 0)
296151564Snjl			break;
297151564Snjl		AcpiOsSleep(10);
298151564Snjl	}
299151564Snjl	if (to == 0) {
300151564Snjl		error = ETIMEDOUT;
301151564Snjl		goto out;
302151564Snjl	}
303151564Snjl
304151564Snjl	error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_STS, &val, 1);
305151564Snjl	if (error)
306151564Snjl		goto out;
307151564Snjl	if (val & SMBUS_STS_MASK) {
308151564Snjl		printf("%s: AE_ERROR 0x%x\n",
309151564Snjl		       __FUNCTION__, (int)(val & SMBUS_STS_MASK));
310151564Snjl		error = EIO;
311151564Snjl		goto out;
312151564Snjl	}
313151564Snjl
314151564Snjl	/* get length */
315151564Snjl	error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_BCNT,
316151564Snjl	    &val, 1);
317151564Snjl	if (error)
318151564Snjl		goto out;
319151564Snjl	val = (val & 0x1f) + 1;
320151564Snjl
321151564Snjl	bzero(ptr, len);
322151564Snjl	if (len > val)
323151564Snjl		len = val;
324151564Snjl
325186026Ssilby	if (batt_sleep_ms)
326186026Ssilby	    AcpiOsSleep(batt_sleep_ms);
327186026Ssilby
328151564Snjl	while (len--) {
329151564Snjl		error = ACPI_EC_READ(sc->ec_dev, sc->sb_base_addr + SMBUS_DATA
330151564Snjl		    + len, &val, 1);
331151564Snjl		if (error)
332151564Snjl			goto out;
333151564Snjl
334151564Snjl		ptr[len] = val;
335186026Ssilby		if (batt_sleep_ms)
336186031Ssilby		    AcpiOsSleep(batt_sleep_ms);
337151564Snjl	}
338151564Snjl
339151564Snjlout:
340151564Snjl	return (error);
341151564Snjl}
342151564Snjl
343151564Snjlstatic int
344151564Snjlacpi_smbat_get_bst(device_t dev, struct acpi_bst *bst)
345151564Snjl{
346151564Snjl	struct acpi_smbat_softc *sc;
347151564Snjl	int error;
348151564Snjl	uint32_t cap_units, factor;
349151564Snjl	int16_t val;
350151564Snjl	uint8_t	addr;
351151564Snjl
352151564Snjl	ACPI_SERIAL_BEGIN(smbat);
353151564Snjl
354151564Snjl	addr = SMBATT_ADDRESS;
355151564Snjl	error = ENXIO;
356151564Snjl	sc = device_get_softc(dev);
357151564Snjl
358152677Sume	if (!acpi_smbat_info_expired(&sc->bst_lastupdated)) {
359152677Sume		error = 0;
360152677Sume		goto out;
361152677Sume	}
362152677Sume
363151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
364151564Snjl		goto out;
365151564Snjl	if (val & SMBATT_BM_CAPACITY_MODE) {
366151564Snjl		factor = 10;
367151564Snjl		cap_units = ACPI_BIF_UNITS_MW;
368151564Snjl	} else {
369151564Snjl		factor = 1;
370151564Snjl		cap_units = ACPI_BIF_UNITS_MA;
371151564Snjl	}
372151564Snjl
373151564Snjl	/* get battery status */
374151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_STATUS, &val))
375151564Snjl		goto out;
376151564Snjl
377154273Sbruno	sc->bst.state = 0;
378154273Sbruno	if (val & SMBATT_BS_DISCHARGING)
379152677Sume		sc->bst.state |= ACPI_BATT_STAT_DISCHARG;
380151564Snjl
381151564Snjl	if (val & SMBATT_BS_REMAINING_CAPACITY_ALARM)
382152677Sume		sc->bst.state |= ACPI_BATT_STAT_CRITICAL;
383151564Snjl
384154273Sbruno	/*
385154273Sbruno	 * If the rate is negative, it is discharging.  Otherwise,
386154273Sbruno	 * it is charging.
387154273Sbruno	 */
388154273Sbruno	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_CURRENT, &val))
389154273Sbruno		goto out;
390154273Sbruno
391154273Sbruno	if (val > 0) {
392154273Sbruno		sc->bst.rate = val * factor;
393216503Savg		sc->bst.state &= ~SMBATT_BS_DISCHARGING;
394154273Sbruno		sc->bst.state |= ACPI_BATT_STAT_CHARGING;
395154273Sbruno	} else if (val < 0)
396154273Sbruno		sc->bst.rate = (-val) * factor;
397154273Sbruno	else
398154273Sbruno		sc->bst.rate = 0;
399154273Sbruno
400151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_REMAINING_CAPACITY, &val))
401151564Snjl		goto out;
402152677Sume	sc->bst.cap = val * factor;
403151564Snjl
404151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_VOLTAGE, &val))
405151564Snjl		goto out;
406152677Sume	sc->bst.volt = val;
407152677Sume
408152677Sume	acpi_smbat_info_updated(&sc->bst_lastupdated);
409151564Snjl	error = 0;
410151564Snjl
411151564Snjlout:
412152744Snjl	if (error == 0)
413152744Snjl		memcpy(bst, &sc->bst, sizeof(sc->bst));
414151564Snjl	ACPI_SERIAL_END(smbat);
415151564Snjl	return (error);
416151564Snjl}
417151564Snjl
418151564Snjlstatic int
419151564Snjlacpi_smbat_get_bif(device_t dev, struct acpi_bif *bif)
420151564Snjl{
421151564Snjl	struct acpi_smbat_softc *sc;
422151564Snjl	int error;
423151564Snjl	uint32_t factor;
424151564Snjl	uint16_t val;
425151564Snjl	uint8_t addr;
426151564Snjl
427151564Snjl	ACPI_SERIAL_BEGIN(smbat);
428151564Snjl
429151564Snjl	addr = SMBATT_ADDRESS;
430151564Snjl	error = ENXIO;
431151564Snjl	sc = device_get_softc(dev);
432151564Snjl
433152677Sume	if (!acpi_smbat_info_expired(&sc->bif_lastupdated)) {
434152677Sume		error = 0;
435152677Sume		goto out;
436152677Sume	}
437152677Sume
438151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_BATTERY_MODE, &val))
439151564Snjl		goto out;
440151564Snjl	if (val & SMBATT_BM_CAPACITY_MODE) {
441151564Snjl		factor = 10;
442152677Sume		sc->bif.units = ACPI_BIF_UNITS_MW;
443151564Snjl	} else {
444151564Snjl		factor = 1;
445152677Sume		sc->bif.units = ACPI_BIF_UNITS_MA;
446151564Snjl	}
447151564Snjl
448151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_CAPACITY, &val))
449151564Snjl		goto out;
450152677Sume	sc->bif.dcap = val * factor;
451151564Snjl
452151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_FULL_CHARGE_CAPACITY, &val))
453151564Snjl		goto out;
454152677Sume	sc->bif.lfcap = val * factor;
455152677Sume	sc->bif.btech = 1;		/* secondary (rechargeable) */
456151564Snjl
457151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_DESIGN_VOLTAGE, &val))
458151564Snjl		goto out;
459152677Sume	sc->bif.dvol = val;
460151564Snjl
461152677Sume	sc->bif.wcap = sc->bif.dcap / 10;
462152677Sume	sc->bif.lcap = sc->bif.dcap / 10;
463151564Snjl
464152677Sume	sc->bif.gra1 = factor;	/* not supported */
465152677Sume	sc->bif.gra2 = factor;	/* not supported */
466151564Snjl
467151564Snjl	if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_NAME,
468152677Sume	    sc->bif.model, sizeof(sc->bif.model)))
469151564Snjl		goto out;
470151564Snjl
471151564Snjl	if (acpi_smbus_read_2(sc, addr, SMBATT_CMD_SERIAL_NUMBER, &val))
472151564Snjl		goto out;
473152677Sume	snprintf(sc->bif.serial, sizeof(sc->bif.serial), "0x%04x", val);
474151564Snjl
475151564Snjl	if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_DEVICE_CHEMISTRY,
476152677Sume	    sc->bif.type, sizeof(sc->bif.type)))
477151564Snjl		goto out;
478151564Snjl
479151564Snjl	if (acpi_smbus_read_multi_1(sc, addr, SMBATT_CMD_MANUFACTURER_DATA,
480152677Sume	    sc->bif.oeminfo, sizeof(sc->bif.oeminfo)))
481151564Snjl		goto out;
482151564Snjl
483152744Snjl	/* XXX check if device was replugged during read? */
484152744Snjl
485152677Sume	acpi_smbat_info_updated(&sc->bif_lastupdated);
486151564Snjl	error = 0;
487151564Snjl
488151564Snjlout:
489152744Snjl	if (error == 0)
490152744Snjl		memcpy(bif, &sc->bif, sizeof(sc->bif));
491151564Snjl	ACPI_SERIAL_END(smbat);
492151564Snjl	return (error);
493151564Snjl}
494