acpi_machdep.c revision 159409
1169689Skan/*-
2169689Skan * Copyright (c) 2001 Mitsuru IWASAKI
3132718Skan * All rights reserved.
4132718Skan *
5132718Skan * Redistribution and use in source and binary forms, with or without
6132718Skan * modification, are permitted provided that the following conditions
7132718Skan * are met:
8132718Skan * 1. Redistributions of source code must retain the above copyright
9132718Skan *    notice, this list of conditions and the following disclaimer.
10132718Skan * 2. Redistributions in binary form must reproduce the above copyright
11132718Skan *    notice, this list of conditions and the following disclaimer in the
12132718Skan *    documentation and/or other materials provided with the distribution.
13132718Skan *
14132718Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15132718Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16132718Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17132718Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18132718Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21132718Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24169689Skan * SUCH DAMAGE.
25169689Skan */
26169689Skan
27169689Skan#include <sys/cdefs.h>
28169689Skan__FBSDID("$FreeBSD: head/sys/i386/acpica/acpi_machdep.c 159409 2006-06-08 17:54:10Z njl $");
29169689Skan
30169689Skan#include <sys/param.h>
31169689Skan#include <sys/bus.h>
32169689Skan#include <sys/conf.h>
33169689Skan#include <sys/fcntl.h>
34169689Skan#include <sys/kernel.h>
35169689Skan#include <sys/sysctl.h>
36169689Skan#include <sys/uio.h>
37169689Skan#include <vm/vm.h>
38169689Skan#include <vm/pmap.h>
39169689Skan
40169689Skan#include <contrib/dev/acpica/acpi.h>
41169689Skan#include <dev/acpica/acpivar.h>
42169689Skan#include <dev/acpica/acpiio.h>
43169689Skan
44169689Skan/*
45169689Skan * APM driver emulation
46169689Skan */
47169689Skan
48169689Skan#include <sys/selinfo.h>
49169689Skan
50169689Skan#include <machine/apm_bios.h>
51169689Skan#include <machine/pc/bios.h>
52169689Skan
53169689Skan#include <i386/bios/apm.h>
54169689Skan
55169689Skanuint32_t acpi_resume_beep;
56169689SkanTUNABLE_INT("hw.acpi.resume_beep", &acpi_resume_beep);
57169689Skanuint32_t acpi_reset_video;
58169689SkanTUNABLE_INT("hw.acpi.reset_video", &acpi_reset_video);
59169689Skan
60169689Skanstatic int intr_model = ACPI_INTR_PIC;
61169689Skanstatic int apm_active;
62169689Skan
63169689Skanstatic d_open_t apmopen;
64169689Skanstatic d_close_t apmclose;
65169689Skanstatic d_write_t apmwrite;
66169689Skanstatic d_ioctl_t apmioctl;
67169689Skanstatic d_poll_t apmpoll;
68169689Skan
69169689Skanstatic struct cdevsw apm_cdevsw = {
70169689Skan	.d_version =	D_VERSION,
71169689Skan	.d_open =	apmopen,
72169689Skan	.d_close =	apmclose,
73169689Skan	.d_write =	apmwrite,
74169689Skan	.d_ioctl =	apmioctl,
75169689Skan	.d_poll =	apmpoll,
76169689Skan	.d_name =	"apm",
77169689Skan};
78169689Skan
79169689Skanstatic int
80169689Skanacpi_capm_convert_battstate(struct  acpi_battinfo *battp)
81169689Skan{
82169689Skan	int	state;
83169689Skan
84169689Skan	state = APM_UNKNOWN;
85169689Skan
86169689Skan	if (battp->state & ACPI_BATT_STAT_DISCHARG) {
87169689Skan		if (battp->cap >= 50)
88169689Skan			state = 0;	/* high */
89169689Skan		else
90169689Skan			state = 1;	/* low */
91169689Skan	}
92169689Skan	if (battp->state & ACPI_BATT_STAT_CRITICAL)
93169689Skan		state = 2;		/* critical */
94169689Skan	if (battp->state & ACPI_BATT_STAT_CHARGING)
95169689Skan		state = 3;		/* charging */
96169689Skan
97169689Skan	/* If still unknown, determine it based on the battery capacity. */
98169689Skan	if (state == APM_UNKNOWN) {
99169689Skan		if (battp->cap >= 50)
100169689Skan			state = 0;	/* high */
101169689Skan		else
102169689Skan			state = 1;	/* low */
103169689Skan	}
104169689Skan
105169689Skan	return (state);
106169689Skan}
107169689Skan
108169689Skanstatic int
109169689Skanacpi_capm_convert_battflags(struct  acpi_battinfo *battp)
110169689Skan{
111169689Skan	int	flags;
112169689Skan
113169689Skan	flags = 0;
114169689Skan
115169689Skan	if (battp->cap >= 50)
116169689Skan		flags |= APM_BATT_HIGH;
117169689Skan	else {
118169689Skan		if (battp->state & ACPI_BATT_STAT_CRITICAL)
119169689Skan			flags |= APM_BATT_CRITICAL;
120169689Skan		else
121169689Skan			flags |= APM_BATT_LOW;
122169689Skan	}
123169689Skan	if (battp->state & ACPI_BATT_STAT_CHARGING)
124169689Skan		flags |= APM_BATT_CHARGING;
125169689Skan	if (battp->state == ACPI_BATT_STAT_NOT_PRESENT)
126169689Skan		flags = APM_BATT_NOT_PRESENT;
127169689Skan
128169689Skan	return (flags);
129169689Skan}
130169689Skan
131169689Skanstatic int
132169689Skanacpi_capm_get_info(apm_info_t aip)
133169689Skan{
134169689Skan	int	acline;
135169689Skan	struct	acpi_battinfo batt;
136169689Skan
137169689Skan	aip->ai_infoversion = 1;
138169689Skan	aip->ai_major       = 1;
139169689Skan	aip->ai_minor       = 2;
140169689Skan	aip->ai_status      = apm_active;
141169689Skan	aip->ai_capabilities= 0xff00;	/* unknown */
142132718Skan
143132718Skan	if (acpi_acad_get_acline(&acline))
144132718Skan		aip->ai_acline = APM_UNKNOWN;	/* unknown */
145132718Skan	else
146132718Skan		aip->ai_acline = acline;	/* on/off */
147169689Skan
148169689Skan	if (acpi_battery_get_battinfo(NULL, &batt) != 0) {
149132718Skan		aip->ai_batt_stat = APM_UNKNOWN;
150132718Skan		aip->ai_batt_life = APM_UNKNOWN;
151169689Skan		aip->ai_batt_time = -1;		 /* unknown */
152132718Skan		aip->ai_batteries = ~0U;	 /* unknown */
153132718Skan	} else {
154132718Skan		aip->ai_batt_stat = acpi_capm_convert_battstate(&batt);
155132718Skan		aip->ai_batt_life = batt.cap;
156132718Skan		aip->ai_batt_time = (batt.min == -1) ? -1 : batt.min * 60;
157132718Skan		aip->ai_batteries = acpi_battery_get_units();
158132718Skan	}
159132718Skan
160132718Skan	return (0);
161132718Skan}
162132718Skan
163132718Skanstatic int
164132718Skanacpi_capm_get_pwstatus(apm_pwstatus_t app)
165169689Skan{
166169689Skan	device_t dev;
167169689Skan	int	acline, unit, error;
168169689Skan	struct	acpi_battinfo batt;
169132718Skan
170132718Skan	if (app->ap_device != PMDV_ALLDEV &&
171132718Skan	    (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL))
172132718Skan		return (1);
173169689Skan
174169689Skan	if (app->ap_device == PMDV_ALLDEV)
175169689Skan		error = acpi_battery_get_battinfo(NULL, &batt);
176132718Skan	else {
177169689Skan		unit = app->ap_device - PMDV_BATT0;
178169689Skan		dev = devclass_get_device(devclass_find("battery"), unit);
179132718Skan		if (dev != NULL)
180169689Skan			error = acpi_battery_get_battinfo(dev, &batt);
181132718Skan		else
182132718Skan			error = ENXIO;
183169689Skan	}
184169689Skan	if (error)
185132718Skan		return (1);
186169689Skan
187169689Skan	app->ap_batt_stat = acpi_capm_convert_battstate(&batt);
188132718Skan	app->ap_batt_flag = acpi_capm_convert_battflags(&batt);
189132718Skan	app->ap_batt_life = batt.cap;
190132718Skan	app->ap_batt_time = (batt.min == -1) ? -1 : batt.min * 60;
191132718Skan
192132718Skan	if (acpi_acad_get_acline(&acline))
193132718Skan		app->ap_acline = APM_UNKNOWN;
194132718Skan	else
195132718Skan		app->ap_acline = acline;	/* on/off */
196169689Skan
197169689Skan	return (0);
198169689Skan}
199169689Skan
200169689Skanstatic int
201169689Skanapmopen(struct cdev *dev, int flag, int fmt, d_thread_t *td)
202169689Skan{
203169689Skan	return (0);
204169689Skan}
205169689Skan
206169689Skanstatic int
207169689Skanapmclose(struct cdev *dev, int flag, int fmt, d_thread_t *td)
208169689Skan{
209169689Skan	return (0);
210169689Skan}
211169689Skan
212169689Skanstatic int
213169689Skanapmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, d_thread_t *td)
214169689Skan{
215169689Skan	int	error = 0;
216169689Skan	struct	acpi_softc *acpi_sc;
217169689Skan	struct apm_info info;
218132718Skan	apm_info_old_t aiop;
219132718Skan
220132718Skan	acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
221132718Skan
222132718Skan	switch (cmd) {
223132718Skan	case APMIO_SUSPEND:
224132718Skan		if ((flag & FWRITE) == 0)
225169689Skan			return (EPERM);
226169689Skan		if (apm_active)
227169689Skan			acpi_SetSleepState(acpi_sc, acpi_sc->acpi_suspend_sx);
228169689Skan		else
229169689Skan			error = EINVAL;
230169689Skan		break;
231169689Skan	case APMIO_STANDBY:
232169689Skan		if ((flag & FWRITE) == 0)
233169689Skan			return (EPERM);
234169689Skan		if (apm_active)
235169689Skan			acpi_SetSleepState(acpi_sc, acpi_sc->acpi_standby_sx);
236169689Skan		else
237169689Skan			error = EINVAL;
238132718Skan		break;
239132718Skan	case APMIO_GETINFO_OLD:
240132718Skan		if (acpi_capm_get_info(&info))
241132718Skan			error = ENXIO;
242132718Skan		aiop = (apm_info_old_t)addr;
243132718Skan		aiop->ai_major = info.ai_major;
244132718Skan		aiop->ai_minor = info.ai_minor;
245132718Skan		aiop->ai_acline = info.ai_acline;
246132718Skan		aiop->ai_batt_stat = info.ai_batt_stat;
247132718Skan		aiop->ai_batt_life = info.ai_batt_life;
248132718Skan		aiop->ai_status = info.ai_status;
249132718Skan		break;
250132718Skan	case APMIO_GETINFO:
251132718Skan		if (acpi_capm_get_info((apm_info_t)addr))
252132718Skan			error = ENXIO;
253132718Skan		break;
254169689Skan	case APMIO_GETPWSTATUS:
255169689Skan		if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr))
256169689Skan			error = ENXIO;
257169689Skan		break;
258169689Skan	case APMIO_ENABLE:
259169689Skan		if ((flag & FWRITE) == 0)
260132718Skan			return (EPERM);
261132718Skan		apm_active = 1;
262132718Skan		break;
263132718Skan	case APMIO_DISABLE:
264132718Skan		if ((flag & FWRITE) == 0)
265132718Skan			return (EPERM);
266132718Skan		apm_active = 0;
267169689Skan		break;
268169689Skan	case APMIO_HALTCPU:
269132718Skan		break;
270132718Skan	case APMIO_NOTHALTCPU:
271132718Skan		break;
272132718Skan	case APMIO_DISPLAY:
273132718Skan		if ((flag & FWRITE) == 0)
274169689Skan			return (EPERM);
275169689Skan		break;
276169689Skan	case APMIO_BIOS:
277169689Skan		if ((flag & FWRITE) == 0)
278169689Skan			return (EPERM);
279169689Skan		bzero(addr, sizeof(struct apm_bios_arg));
280169689Skan		break;
281169689Skan	default:
282169689Skan		error = EINVAL;
283169689Skan		break;
284169689Skan	}
285169689Skan
286169689Skan	return (error);
287169689Skan}
288169689Skan
289169689Skanstatic int
290169689Skanapmwrite(struct cdev *dev, struct uio *uio, int ioflag)
291169689Skan{
292169689Skan	return (uio->uio_resid);
293169689Skan}
294169689Skan
295169689Skanstatic int
296169689Skanapmpoll(struct cdev *dev, int events, d_thread_t *td)
297169689Skan{
298169689Skan	return (0);
299169689Skan}
300169689Skan
301169689Skanstatic void
302169689Skanacpi_capm_init(struct acpi_softc *sc)
303169689Skan{
304169689Skan        make_dev(&apm_cdevsw, 0, 0, 5, 0664, "apm");
305169689Skan}
306169689Skan
307169689Skanint
308169689Skanacpi_machdep_init(device_t dev)
309169689Skan{
310169689Skan	struct	acpi_softc *sc;
311169689Skan
312169689Skan	sc = devclass_get_softc(devclass_find("acpi"), 0);
313169689Skan	acpi_capm_init(sc);
314169689Skan
315169689Skan	acpi_install_wakeup_handler(sc);
316169689Skan
317169689Skan	if (intr_model == ACPI_INTR_PIC)
318169689Skan		BUS_CONFIG_INTR(dev, AcpiGbl_FADT->SciInt, INTR_TRIGGER_LEVEL,
319169689Skan		    INTR_POLARITY_LOW);
320169689Skan	else
321169689Skan		acpi_SetIntrModel(intr_model);
322169689Skan
323169689Skan	SYSCTL_ADD_UINT(&sc->acpi_sysctl_ctx,
324169689Skan	    SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO,
325169689Skan	    "resume_beep", CTLFLAG_RD | CTLFLAG_RW, &acpi_resume_beep, 0,
326169689Skan	    "Beep the PC speaker when resuming");
327169689Skan	SYSCTL_ADD_UINT(&sc->acpi_sysctl_ctx,
328169689Skan	    SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO,
329169689Skan	    "reset_video", CTLFLAG_RD | CTLFLAG_RW, &acpi_reset_video, 0,
330169689Skan	    "Call the VESA reset BIOS vector on the resume path");
331169689Skan
332169689Skan	return (0);
333169689Skan}
334169689Skan
335169689Skanvoid
336169689Skanacpi_SetDefaultIntrModel(int model)
337169689Skan{
338169689Skan
339169689Skan	intr_model = model;
340169689Skan}
341169689Skan
342169689Skan/* Check BIOS date.  If 1998 or older, disable ACPI. */
343169689Skanint
344169689Skanacpi_machdep_quirks(int *quirks)
345169689Skan{
346169689Skan	char *va;
347169689Skan	int year;
348169689Skan
349169689Skan	/* BIOS address 0xffff5 contains the date in the format mm/dd/yy. */
350169689Skan	va = pmap_mapdev(0xffff0, 16);
351132718Skan	sscanf(va + 11, "%2d", &year);
352132718Skan	pmap_unmapdev((vm_offset_t)va, 16);
353132718Skan
354132718Skan	/*
355132718Skan	 * Date must be >= 1/1/1999 or we don't trust ACPI.  Note that this
356132718Skan	 * check must be changed by my 114th birthday.
357132718Skan	 */
358132718Skan	if (year > 90 && year < 99)
359132718Skan		*quirks = ACPI_Q_BROKEN;
360132718Skan
361132718Skan	return (0);
362169689Skan}
363169689Skan
364132718Skanvoid
365132718Skanacpi_cpu_c1()
366132718Skan{
367132718Skan	__asm __volatile("sti; hlt");
368132718Skan}
369169689Skan