acpi_apm.c revision 196037
1/*-
2 * Copyright (c) 2001 Mitsuru IWASAKI
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/i386/acpica/acpi_machdep.c 196037 2009-08-02 14:28:40Z attilio $");
29
30#include <sys/param.h>
31#include <sys/bus.h>
32#include <sys/condvar.h>
33#include <sys/conf.h>
34#include <sys/fcntl.h>
35#include <sys/kernel.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38#include <sys/poll.h>
39#include <sys/sysctl.h>
40#include <sys/uio.h>
41#include <vm/vm.h>
42#include <vm/pmap.h>
43
44#include <contrib/dev/acpica/include/acpi.h>
45
46#include <dev/acpica/acpivar.h>
47#include <dev/acpica/acpiio.h>
48
49#include <machine/nexusvar.h>
50
51/*
52 * APM driver emulation
53 */
54
55#include <machine/apm_bios.h>
56#include <machine/pc/bios.h>
57
58#include <i386/bios/apm.h>
59
60SYSCTL_DECL(_debug_acpi);
61
62uint32_t acpi_resume_beep;
63TUNABLE_INT("debug.acpi.resume_beep", &acpi_resume_beep);
64SYSCTL_UINT(_debug_acpi, OID_AUTO, resume_beep, CTLFLAG_RW, &acpi_resume_beep,
65    0, "Beep the PC speaker when resuming");
66uint32_t acpi_reset_video;
67TUNABLE_INT("hw.acpi.reset_video", &acpi_reset_video);
68
69static int intr_model = ACPI_INTR_PIC;
70static int apm_active;
71static struct clonedevs *apm_clones;
72
73MALLOC_DEFINE(M_APMDEV, "apmdev", "APM device emulation");
74
75static d_open_t		apmopen;
76static d_close_t	apmclose;
77static d_write_t	apmwrite;
78static d_ioctl_t	apmioctl;
79static d_poll_t		apmpoll;
80static d_kqfilter_t	apmkqfilter;
81static void		apmreadfiltdetach(struct knote *kn);
82static int		apmreadfilt(struct knote *kn, long hint);
83static struct filterops	apm_readfiltops =
84	{ 1, NULL, apmreadfiltdetach, apmreadfilt };
85
86static struct cdevsw apm_cdevsw = {
87	.d_version =	D_VERSION,
88	.d_flags =	D_TRACKCLOSE | D_NEEDMINOR,
89	.d_open =	apmopen,
90	.d_close =	apmclose,
91	.d_write =	apmwrite,
92	.d_ioctl =	apmioctl,
93	.d_poll =	apmpoll,
94	.d_name =	"apm",
95	.d_kqfilter =	apmkqfilter
96};
97
98static int
99acpi_capm_convert_battstate(struct  acpi_battinfo *battp)
100{
101	int	state;
102
103	state = APM_UNKNOWN;
104
105	if (battp->state & ACPI_BATT_STAT_DISCHARG) {
106		if (battp->cap >= 50)
107			state = 0;	/* high */
108		else
109			state = 1;	/* low */
110	}
111	if (battp->state & ACPI_BATT_STAT_CRITICAL)
112		state = 2;		/* critical */
113	if (battp->state & ACPI_BATT_STAT_CHARGING)
114		state = 3;		/* charging */
115
116	/* If still unknown, determine it based on the battery capacity. */
117	if (state == APM_UNKNOWN) {
118		if (battp->cap >= 50)
119			state = 0;	/* high */
120		else
121			state = 1;	/* low */
122	}
123
124	return (state);
125}
126
127static int
128acpi_capm_convert_battflags(struct  acpi_battinfo *battp)
129{
130	int	flags;
131
132	flags = 0;
133
134	if (battp->cap >= 50)
135		flags |= APM_BATT_HIGH;
136	else {
137		if (battp->state & ACPI_BATT_STAT_CRITICAL)
138			flags |= APM_BATT_CRITICAL;
139		else
140			flags |= APM_BATT_LOW;
141	}
142	if (battp->state & ACPI_BATT_STAT_CHARGING)
143		flags |= APM_BATT_CHARGING;
144	if (battp->state == ACPI_BATT_STAT_NOT_PRESENT)
145		flags = APM_BATT_NOT_PRESENT;
146
147	return (flags);
148}
149
150static int
151acpi_capm_get_info(apm_info_t aip)
152{
153	int	acline;
154	struct	acpi_battinfo batt;
155
156	aip->ai_infoversion = 1;
157	aip->ai_major       = 1;
158	aip->ai_minor       = 2;
159	aip->ai_status      = apm_active;
160	aip->ai_capabilities= 0xff00;	/* unknown */
161
162	if (acpi_acad_get_acline(&acline))
163		aip->ai_acline = APM_UNKNOWN;	/* unknown */
164	else
165		aip->ai_acline = acline;	/* on/off */
166
167	newbus_slock();
168	if (acpi_battery_get_battinfo(NULL, &batt) != 0) {
169		aip->ai_batt_stat = APM_UNKNOWN;
170		aip->ai_batt_life = APM_UNKNOWN;
171		aip->ai_batt_time = -1;		 /* unknown */
172		aip->ai_batteries = ~0U;	 /* unknown */
173	} else {
174		aip->ai_batt_stat = acpi_capm_convert_battstate(&batt);
175		aip->ai_batt_life = batt.cap;
176		aip->ai_batt_time = (batt.min == -1) ? -1 : batt.min * 60;
177		aip->ai_batteries = acpi_battery_get_units();
178	}
179	newbus_sunlock();
180
181	return (0);
182}
183
184static int
185acpi_capm_get_pwstatus(apm_pwstatus_t app)
186{
187	device_t dev;
188	int	acline, unit, error;
189	struct	acpi_battinfo batt;
190
191	if (app->ap_device != PMDV_ALLDEV &&
192	    (app->ap_device < PMDV_BATT0 || app->ap_device > PMDV_BATT_ALL))
193		return (1);
194
195	newbus_slock();
196	if (app->ap_device == PMDV_ALLDEV)
197		error = acpi_battery_get_battinfo(NULL, &batt);
198	else {
199		unit = app->ap_device - PMDV_BATT0;
200		dev = devclass_get_device(devclass_find("battery"), unit);
201		if (dev != NULL)
202			error = acpi_battery_get_battinfo(dev, &batt);
203		else
204			error = ENXIO;
205	}
206	newbus_sunlock();
207	if (error)
208		return (1);
209
210	app->ap_batt_stat = acpi_capm_convert_battstate(&batt);
211	app->ap_batt_flag = acpi_capm_convert_battflags(&batt);
212	app->ap_batt_life = batt.cap;
213	app->ap_batt_time = (batt.min == -1) ? -1 : batt.min * 60;
214
215	if (acpi_acad_get_acline(&acline))
216		app->ap_acline = APM_UNKNOWN;
217	else
218		app->ap_acline = acline;	/* on/off */
219
220	return (0);
221}
222
223/* Create single-use devices for /dev/apm and /dev/apmctl. */
224static void
225apm_clone(void *arg, struct ucred *cred, char *name, int namelen,
226    struct cdev **dev)
227{
228	int ctl_dev, unit;
229
230	if (*dev != NULL)
231		return;
232	if (strcmp(name, "apmctl") == 0)
233		ctl_dev = TRUE;
234	else if (strcmp(name, "apm") == 0)
235		ctl_dev = FALSE;
236	else
237		return;
238
239	/* Always create a new device and unit number. */
240	unit = -1;
241	if (clone_create(&apm_clones, &apm_cdevsw, &unit, dev, 0)) {
242		if (ctl_dev) {
243			*dev = make_dev(&apm_cdevsw, unit,
244			    UID_ROOT, GID_OPERATOR, 0660, "apmctl%d", unit);
245		} else {
246			*dev = make_dev(&apm_cdevsw, unit,
247			    UID_ROOT, GID_OPERATOR, 0664, "apm%d", unit);
248		}
249		if (*dev != NULL) {
250			dev_ref(*dev);
251			(*dev)->si_flags |= SI_CHEAPCLONE;
252		}
253	}
254}
255
256/* Create a struct for tracking per-device suspend notification. */
257static struct apm_clone_data *
258apm_create_clone(struct cdev *dev, struct acpi_softc *acpi_sc)
259{
260	struct apm_clone_data *clone;
261
262	clone = malloc(sizeof(*clone), M_APMDEV, M_WAITOK);
263	clone->cdev = dev;
264	clone->acpi_sc = acpi_sc;
265	clone->notify_status = APM_EV_NONE;
266	bzero(&clone->sel_read, sizeof(clone->sel_read));
267	knlist_init_mtx(&clone->sel_read.si_note, &acpi_mutex);
268
269	/*
270	 * The acpi device is always managed by devd(8) and is considered
271	 * writable (i.e., ack is required to allow suspend to proceed.)
272	 */
273	if (strcmp("acpi", devtoname(dev)) == 0)
274		clone->flags = ACPI_EVF_DEVD | ACPI_EVF_WRITE;
275	else
276		clone->flags = ACPI_EVF_NONE;
277
278	ACPI_LOCK(acpi);
279	STAILQ_INSERT_TAIL(&acpi_sc->apm_cdevs, clone, entries);
280	ACPI_UNLOCK(acpi);
281	return (clone);
282}
283
284static int
285apmopen(struct cdev *dev, int flag, int fmt, struct thread *td)
286{
287	struct	acpi_softc *acpi_sc;
288	struct 	apm_clone_data *clone;
289
290	newbus_slock();
291	acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
292	newbus_sunlock();
293	clone = apm_create_clone(dev, acpi_sc);
294	dev->si_drv1 = clone;
295
296	/* If the device is opened for write, record that. */
297	if ((flag & FWRITE) != 0)
298		clone->flags |= ACPI_EVF_WRITE;
299
300	return (0);
301}
302
303static int
304apmclose(struct cdev *dev, int flag, int fmt, struct thread *td)
305{
306	struct	apm_clone_data *clone;
307	struct	acpi_softc *acpi_sc;
308
309	clone = dev->si_drv1;
310	acpi_sc = clone->acpi_sc;
311
312	/* We are about to lose a reference so check if suspend should occur */
313	if (acpi_sc->acpi_next_sstate != 0 &&
314	    clone->notify_status != APM_EV_ACKED)
315		acpi_AckSleepState(clone, 0);
316
317	/* Remove this clone's data from the list and free it. */
318	ACPI_LOCK(acpi);
319	STAILQ_REMOVE(&acpi_sc->apm_cdevs, clone, apm_clone_data, entries);
320	knlist_destroy(&clone->sel_read.si_note);
321	ACPI_UNLOCK(acpi);
322	free(clone, M_APMDEV);
323	destroy_dev_sched(dev);
324	return (0);
325}
326
327static int
328apmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
329{
330	int	error;
331	struct	apm_clone_data *clone;
332	struct	acpi_softc *acpi_sc;
333	struct	apm_info info;
334	struct 	apm_event_info *ev_info;
335	apm_info_old_t aiop;
336
337	error = 0;
338	clone = dev->si_drv1;
339	acpi_sc = clone->acpi_sc;
340
341	switch (cmd) {
342	case APMIO_SUSPEND:
343		if ((flag & FWRITE) == 0)
344			return (EPERM);
345		if (acpi_sc->acpi_next_sstate == 0) {
346			if (acpi_sc->acpi_suspend_sx != ACPI_STATE_S5) {
347				error = acpi_ReqSleepState(acpi_sc,
348				    acpi_sc->acpi_suspend_sx);
349			} else {
350				printf(
351			"power off via apm suspend not supported\n");
352				error = ENXIO;
353			}
354		} else
355			error = acpi_AckSleepState(clone, 0);
356		break;
357	case APMIO_STANDBY:
358		if ((flag & FWRITE) == 0)
359			return (EPERM);
360		if (acpi_sc->acpi_next_sstate == 0) {
361			if (acpi_sc->acpi_standby_sx != ACPI_STATE_S5) {
362				error = acpi_ReqSleepState(acpi_sc,
363				    acpi_sc->acpi_standby_sx);
364			} else {
365				printf(
366			"power off via apm standby not supported\n");
367				error = ENXIO;
368			}
369		} else
370			error = acpi_AckSleepState(clone, 0);
371		break;
372	case APMIO_NEXTEVENT:
373		printf("apm nextevent start\n");
374		ACPI_LOCK(acpi);
375		if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status ==
376		    APM_EV_NONE) {
377			ev_info = (struct apm_event_info *)addr;
378			if (acpi_sc->acpi_next_sstate <= ACPI_STATE_S3)
379				ev_info->type = PMEV_STANDBYREQ;
380			else
381				ev_info->type = PMEV_SUSPENDREQ;
382			ev_info->index = 0;
383			clone->notify_status = APM_EV_NOTIFIED;
384			printf("apm event returning %d\n", ev_info->type);
385		} else
386			error = EAGAIN;
387		ACPI_UNLOCK(acpi);
388		break;
389	case APMIO_GETINFO_OLD:
390		if (acpi_capm_get_info(&info))
391			error = ENXIO;
392		aiop = (apm_info_old_t)addr;
393		aiop->ai_major = info.ai_major;
394		aiop->ai_minor = info.ai_minor;
395		aiop->ai_acline = info.ai_acline;
396		aiop->ai_batt_stat = info.ai_batt_stat;
397		aiop->ai_batt_life = info.ai_batt_life;
398		aiop->ai_status = info.ai_status;
399		break;
400	case APMIO_GETINFO:
401		if (acpi_capm_get_info((apm_info_t)addr))
402			error = ENXIO;
403		break;
404	case APMIO_GETPWSTATUS:
405		if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr))
406			error = ENXIO;
407		break;
408	case APMIO_ENABLE:
409		if ((flag & FWRITE) == 0)
410			return (EPERM);
411		apm_active = 1;
412		break;
413	case APMIO_DISABLE:
414		if ((flag & FWRITE) == 0)
415			return (EPERM);
416		apm_active = 0;
417		break;
418	case APMIO_HALTCPU:
419		break;
420	case APMIO_NOTHALTCPU:
421		break;
422	case APMIO_DISPLAY:
423		if ((flag & FWRITE) == 0)
424			return (EPERM);
425		break;
426	case APMIO_BIOS:
427		if ((flag & FWRITE) == 0)
428			return (EPERM);
429		bzero(addr, sizeof(struct apm_bios_arg));
430		break;
431	default:
432		error = EINVAL;
433		break;
434	}
435
436	return (error);
437}
438
439static int
440apmwrite(struct cdev *dev, struct uio *uio, int ioflag)
441{
442	return (uio->uio_resid);
443}
444
445static int
446apmpoll(struct cdev *dev, int events, struct thread *td)
447{
448	struct	apm_clone_data *clone;
449	int revents;
450
451	revents = 0;
452	ACPI_LOCK(acpi);
453	clone = dev->si_drv1;
454	if (clone->acpi_sc->acpi_next_sstate)
455		revents |= events & (POLLIN | POLLRDNORM);
456	else
457		selrecord(td, &clone->sel_read);
458	ACPI_UNLOCK(acpi);
459	return (revents);
460}
461
462static int
463apmkqfilter(struct cdev *dev, struct knote *kn)
464{
465	struct	apm_clone_data *clone;
466
467	ACPI_LOCK(acpi);
468	clone = dev->si_drv1;
469	kn->kn_hook = clone;
470	kn->kn_fop = &apm_readfiltops;
471	knlist_add(&clone->sel_read.si_note, kn, 0);
472	ACPI_UNLOCK(acpi);
473	return (0);
474}
475
476static void
477apmreadfiltdetach(struct knote *kn)
478{
479	struct	apm_clone_data *clone;
480
481	ACPI_LOCK(acpi);
482	clone = kn->kn_hook;
483	knlist_remove(&clone->sel_read.si_note, kn, 0);
484	ACPI_UNLOCK(acpi);
485}
486
487static int
488apmreadfilt(struct knote *kn, long hint)
489{
490	struct	apm_clone_data *clone;
491	int	sleeping;
492
493	ACPI_LOCK(acpi);
494	clone = kn->kn_hook;
495	sleeping = clone->acpi_sc->acpi_next_sstate ? 1 : 0;
496	ACPI_UNLOCK(acpi);
497	return (sleeping);
498}
499
500int
501acpi_machdep_init(device_t dev)
502{
503	struct	acpi_softc *acpi_sc;
504
505	acpi_sc = devclass_get_softc(devclass_find("acpi"), 0);
506
507	/* Create a clone for /dev/acpi also. */
508	STAILQ_INIT(&acpi_sc->apm_cdevs);
509	acpi_sc->acpi_clone = apm_create_clone(acpi_sc->acpi_dev_t, acpi_sc);
510	clone_setup(&apm_clones);
511	EVENTHANDLER_REGISTER(dev_clone, apm_clone, 0, 1000);
512	acpi_install_wakeup_handler(acpi_sc);
513
514	if (intr_model == ACPI_INTR_PIC)
515		BUS_CONFIG_INTR(dev, AcpiGbl_FADT.SciInterrupt,
516		    INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
517	else
518		acpi_SetIntrModel(intr_model);
519
520	SYSCTL_ADD_UINT(&acpi_sc->acpi_sysctl_ctx,
521	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
522	    "reset_video", CTLFLAG_RW, &acpi_reset_video, 0,
523	    "Call the VESA reset BIOS vector on the resume path");
524
525	return (0);
526}
527
528void
529acpi_SetDefaultIntrModel(int model)
530{
531
532	intr_model = model;
533}
534
535/* Check BIOS date.  If 1998 or older, disable ACPI. */
536int
537acpi_machdep_quirks(int *quirks)
538{
539	char *va;
540	int year;
541
542	/* BIOS address 0xffff5 contains the date in the format mm/dd/yy. */
543	va = pmap_mapbios(0xffff0, 16);
544	sscanf(va + 11, "%2d", &year);
545	pmap_unmapbios((vm_offset_t)va, 16);
546
547	/*
548	 * Date must be >= 1/1/1999 or we don't trust ACPI.  Note that this
549	 * check must be changed by my 114th birthday.
550	 */
551	if (year > 90 && year < 99)
552		*quirks = ACPI_Q_BROKEN;
553
554	return (0);
555}
556
557void
558acpi_cpu_c1()
559{
560	__asm __volatile("sti; hlt");
561}
562
563/*
564 * ACPI nexus(4) driver.
565 */
566static int
567nexus_acpi_probe(device_t dev)
568{
569	int error;
570
571	error = acpi_identify();
572	if (error)
573		return (error);
574
575	return (BUS_PROBE_DEFAULT);
576}
577
578static int
579nexus_acpi_attach(device_t dev)
580{
581
582	nexus_init_resources();
583	bus_generic_probe(dev);
584	if (BUS_ADD_CHILD(dev, 10, "acpi", 0) == NULL)
585		panic("failed to add acpi0 device");
586
587	return (bus_generic_attach(dev));
588}
589
590static device_method_t nexus_acpi_methods[] = {
591	/* Device interface */
592	DEVMETHOD(device_probe,		nexus_acpi_probe),
593	DEVMETHOD(device_attach,	nexus_acpi_attach),
594
595	{ 0, 0 }
596};
597
598DEFINE_CLASS_1(nexus, nexus_acpi_driver, nexus_acpi_methods, 1, nexus_driver);
599static devclass_t nexus_devclass;
600
601DRIVER_MODULE(nexus_acpi, root, nexus_acpi_driver, nexus_devclass, 0, 0);
602