blob: 86d50514b16bc7fc39e943342504c8d7fd816aa6 [file] [log] [blame]
Kevin O'Connor95b2f782008-03-05 19:52:06 -05001// Basic support for apmbios callbacks.
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2005 Struan Bartlett
5// Copyright (C) 2004 Fabrice Bellard
6//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05007// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor95b2f782008-03-05 19:52:06 -05008
9#include "farptr.h" // GET_VAR
Kevin O'Connor9521e262008-07-04 13:04:29 -040010#include "bregs.h" // struct bregs
Kevin O'Connor95b2f782008-03-05 19:52:06 -050011#include "ioport.h" // outb
Kevin O'Connor6d768b52009-12-10 21:15:04 -050012#include "util.h" // wait_irq
Kevin O'Connor9521e262008-07-04 13:04:29 -040013#include "config.h" // CONFIG_*
Kevin O'Connor15157a32008-12-13 11:10:37 -050014#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor95b2f782008-03-05 19:52:06 -050015
16static void
17out_str(const char *str_cs)
18{
Kevin O'Connorf64f0db2008-05-18 02:42:58 -040019 if (CONFIG_COREBOOT) {
Kevin O'Connorac8df8c2008-05-24 23:46:33 -040020 dprintf(1, "APM request '%s'\n", str_cs);
Kevin O'Connorf64f0db2008-05-18 02:42:58 -040021 return;
22 }
23
Kevin O'Connor95b2f782008-03-05 19:52:06 -050024 u8 *s = (u8*)str_cs;
25 for (;;) {
Kevin O'Connor15157a32008-12-13 11:10:37 -050026 u8 c = GET_GLOBAL(*s);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050027 if (!c)
28 break;
Kevin O'Connor2ad37442008-05-06 19:49:01 -040029 outb(c, PORT_BIOS_APM);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050030 s++;
31 }
32}
33
34// APM installation check
35static void
36handle_155300(struct bregs *regs)
37{
38 regs->ah = 1; // APM major version
39 regs->al = 2; // APM minor version
40 regs->bh = 'P';
41 regs->bl = 'M';
42 // bit 0 : 16 bit interface supported
43 // bit 1 : 32 bit interface supported
44 regs->cx = 0x03;
Kevin O'Connor6c781222008-03-09 12:19:23 -040045 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050046}
47
48// APM real mode interface connect
49static void
50handle_155301(struct bregs *regs)
51{
Kevin O'Connor6c781222008-03-09 12:19:23 -040052 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050053}
54
Kevin O'Connor44d65302008-03-08 11:34:46 -050055// Assembler entry points defined in romlayout.S
56extern void apm16protected_entry();
57extern void apm32protected_entry();
58
Kevin O'Connor95b2f782008-03-05 19:52:06 -050059// APM 16 bit protected mode interface connect
60static void
61handle_155302(struct bregs *regs)
62{
Kevin O'Connor44d65302008-03-08 11:34:46 -050063 regs->bx = (u32)apm16protected_entry;
Kevin O'Connor95b2f782008-03-05 19:52:06 -050064 regs->ax = SEG_BIOS; // 16 bit code segment base
65 regs->si = 0xfff0; // 16 bit code segment size
66 regs->cx = SEG_BIOS; // data segment address
67 regs->di = 0xfff0; // data segment length
Kevin O'Connor6c781222008-03-09 12:19:23 -040068 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050069}
70
71// APM 32 bit protected mode interface connect
72static void
73handle_155303(struct bregs *regs)
74{
Kevin O'Connor44d65302008-03-08 11:34:46 -050075 regs->ax = SEG_BIOS; // 32 bit code segment base
76 regs->ebx = (u32)apm32protected_entry;
77 regs->cx = SEG_BIOS; // 16 bit code segment base
Kevin O'Connor95b2f782008-03-05 19:52:06 -050078 // 32 bit code segment size (low 16 bits)
79 // 16 bit code segment size (high 16 bits)
80 regs->esi = 0xfff0fff0;
Kevin O'Connor44d65302008-03-08 11:34:46 -050081 regs->dx = SEG_BIOS; // data segment address
Kevin O'Connor95b2f782008-03-05 19:52:06 -050082 regs->di = 0xfff0; // data segment length
Kevin O'Connor6c781222008-03-09 12:19:23 -040083 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050084}
85
86// APM interface disconnect
87static void
88handle_155304(struct bregs *regs)
89{
Kevin O'Connor6c781222008-03-09 12:19:23 -040090 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050091}
92
93// APM cpu idle
94static void
95handle_155305(struct bregs *regs)
96{
Kevin O'Connor6d768b52009-12-10 21:15:04 -050097 wait_irq();
Kevin O'Connor6c781222008-03-09 12:19:23 -040098 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -050099}
100
Kevin O'Connor0fae9e12008-06-07 14:51:14 -0400101// APM cpu busy
102static void
103handle_155306(struct bregs *regs)
104{
105 set_success(regs);
106}
107
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500108// APM Set Power State
109static void
110handle_155307(struct bregs *regs)
111{
112 if (regs->bx != 1) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400113 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500114 return;
115 }
116 switch (regs->cx) {
117 case 1:
118 out_str("Standby");
119 break;
120 case 2:
121 out_str("Suspend");
122 break;
123 case 3:
124 irq_disable();
125 out_str("Shutdown");
126 for (;;)
127 hlt();
128 break;
129 }
Kevin O'Connor6c781222008-03-09 12:19:23 -0400130 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500131}
132
133static void
134handle_155308(struct bregs *regs)
135{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400136 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500137}
138
139// Get Power Status
140static void
141handle_15530a(struct bregs *regs)
142{
143 regs->bh = 0x01; // on line
144 regs->bl = 0xff; // unknown battery status
145 regs->ch = 0x80; // no system battery
146 regs->cl = 0xff; // unknown remaining time
147 regs->dx = 0xffff; // unknown remaining time
148 regs->si = 0x00; // zero battery
Kevin O'Connor6c781222008-03-09 12:19:23 -0400149 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500150}
151
Kevin O'Connor0fae9e12008-06-07 14:51:14 -0400152#define RET_ENOEVENT 0x80
153
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500154// Get PM Event
155static void
156handle_15530b(struct bregs *regs)
157{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500158 set_code_invalid_silent(regs, RET_ENOEVENT);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500159}
160
161// APM Driver Version
162static void
163handle_15530e(struct bregs *regs)
164{
165 regs->ah = 1;
166 regs->al = 2;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400167 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500168}
169
170// APM Engage / Disengage
171static void
172handle_15530f(struct bregs *regs)
173{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400174 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500175}
176
177// APM Get Capabilities
178static void
179handle_155310(struct bregs *regs)
180{
181 regs->bl = 0;
182 regs->cx = 0;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400183 set_success(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500184}
185
186static void
187handle_1553XX(struct bregs *regs)
188{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500189 set_unimplemented(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500190}
191
Kevin O'Connor44d65302008-03-08 11:34:46 -0500192void VISIBLE16
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500193handle_1553(struct bregs *regs)
194{
Kevin O'Connor7ba6b302008-05-26 15:19:33 -0400195 if (! CONFIG_APMBIOS) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500196 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connor7ba6b302008-05-26 15:19:33 -0400197 return;
198 }
199
Kevin O'Connorc38e4802008-03-15 11:07:50 -0400200 //debug_stub(regs);
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500201 switch (regs->al) {
202 case 0x00: handle_155300(regs); break;
203 case 0x01: handle_155301(regs); break;
204 case 0x02: handle_155302(regs); break;
205 case 0x03: handle_155303(regs); break;
206 case 0x04: handle_155304(regs); break;
207 case 0x05: handle_155305(regs); break;
Kevin O'Connor0fae9e12008-06-07 14:51:14 -0400208 case 0x06: handle_155306(regs); break;
Kevin O'Connor95b2f782008-03-05 19:52:06 -0500209 case 0x07: handle_155307(regs); break;
210 case 0x08: handle_155308(regs); break;
211 case 0x0a: handle_15530a(regs); break;
212 case 0x0b: handle_15530b(regs); break;
213 case 0x0e: handle_15530e(regs); break;
214 case 0x0f: handle_15530f(regs); break;
215 case 0x10: handle_155310(regs); break;
216 default: handle_1553XX(regs); break;
217 }
218}