Add acpi poweroff
[minix3.git] / kernel / arch / i386 / acpi.c
blob7770f336cf75eb7ad48008c573b2454d9c7dec2b
2 #include <string.h>
4 #include "kernel/kernel.h"
5 #include "acpi.h"
6 #include "arch_proto.h"
8 typedef int ((* acpi_read_t)(phys_bytes addr, void * buff, size_t size));
10 struct acpi_rsdp acpi_rsdp;
12 static acpi_read_t read_func;
14 #define MAX_RSDT 35 /* ACPI defines 35 signatures */
15 #define SLP_EN_CODE (1 << 13) /* ACPI SLP_EN_CODE code */
16 #define AMI_PACKAGE_OP_CODE (0x12)
17 #define AMI_NAME_OP_CODE (0x8)
18 #define AMI_BYTE_PREFIX_CODE (0xA)
19 #define AMI_PACKAGE_LENGTH_ENCODING_BITS_MASK (0xC0)
20 #define AMI_PACKAGE_LENGTH_ENCODING_BITS_SHIFT (6)
21 #define AMI_MIN_PACKAGE_LENGTH (1)
22 #define AMI_NUM_ELEMENTS_LENGTH (1)
23 #define AMI_SLP_TYPA_SHIFT (10)
24 #define AMI_SLP_TYPB_SHIFT (10)
25 #define AMI_S5_NAME_OP_OFFSET_1 (-1)
26 #define AMI_S5_NAME_OP_OFFSET_2 (-2)
27 #define AMI_S5_PACKAGE_OP_OFFSET (4)
28 #define AMI_S5_PACKET_LENGTH_OFFSET (5)
30 static struct acpi_rsdt {
31 struct acpi_sdt_header hdr;
32 u32_t data[MAX_RSDT];
33 } rsdt;
35 static struct {
36 char signature [ACPI_SDT_SIGNATURE_LEN + 1];
37 size_t length;
38 } sdt_trans[MAX_RSDT];
40 static int sdt_count;
41 static u16_t pm1a_cnt_blk = 0;
42 static u16_t pm1b_cnt_blk = 0;
43 static u16_t slp_typa = 0;
44 static u16_t slp_typb = 0;
46 static int acpi_check_csum(struct acpi_sdt_header * tb, size_t size)
48 u8_t total = 0;
49 int i;
50 for (i = 0; i < size; i++)
51 total += ((unsigned char *)tb)[i];
52 return total == 0 ? 0 : -1;
55 static int acpi_check_signature(const char * orig, const char * match)
57 return strncmp(orig, match, ACPI_SDT_SIGNATURE_LEN);
60 static u32_t acpi_phys2vir(u32_t p)
62 if(!vm_running) {
63 printf("acpi: returning 0x%lx as vir addr\n", p);
64 return p;
66 panic("acpi: can't get virtual address of arbitrary physical address");
69 static int acpi_phys_copy(phys_bytes phys, void *target, size_t len)
71 if(!vm_running) {
72 memcpy(target, (void *) phys, len);
73 return 0;
75 panic("can't acpi_phys_copy with vm");
78 static int acpi_read_sdt_at(phys_bytes addr,
79 struct acpi_sdt_header * tb,
80 size_t size,
81 const char * name)
83 struct acpi_sdt_header hdr;
85 /* if NULL is supplied, we only return the size of the table */
86 if (tb == NULL) {
87 if (read_func(addr, &hdr, sizeof(struct acpi_sdt_header))) {
88 printf("ERROR acpi cannot read %s header\n", name);
89 return -1;
92 return hdr.length;
95 if (read_func(addr, tb, sizeof(struct acpi_sdt_header))) {
96 printf("ERROR acpi cannot read %s header\n", name);
97 return -1;
100 if (acpi_check_signature(tb->signature, name)) {
101 printf("ERROR acpi %s signature does not match\n", name);
102 return -1;
105 if (size < tb->length) {
106 printf("ERROR acpi buffer too small for %s\n", name);
107 return -1;
110 if (read_func(addr, tb, size)) {
111 printf("ERROR acpi cannot read %s\n", name);
112 return -1;
115 if (acpi_check_csum(tb, tb->length)) {
116 printf("ERROR acpi %s checksum does not match\n", name);
117 return -1;
120 return tb->length;
123 phys_bytes acpi_get_table_base(const char * name)
125 int i;
127 for(i = 0; i < sdt_count; i++) {
128 if (strncmp(name, sdt_trans[i].signature,
129 ACPI_SDT_SIGNATURE_LEN) == 0)
130 return (phys_bytes) rsdt.data[i];
133 return (phys_bytes) NULL;
136 size_t acpi_get_table_length(const char * name)
138 int i;
140 for(i = 0; i < sdt_count; i++) {
141 if (strncmp(name, sdt_trans[i].signature,
142 ACPI_SDT_SIGNATURE_LEN) == 0)
143 return sdt_trans[i].length;
146 return 0;
149 static void * acpi_madt_get_typed_item(struct acpi_madt_hdr * hdr,
150 unsigned char type,
151 unsigned idx)
153 u8_t * t, * end;
154 int i;
156 t = (u8_t *) hdr + sizeof(struct acpi_madt_hdr);
157 end = (u8_t *) hdr + hdr->hdr.length;
159 i = 0;
160 while(t < end) {
161 if (type == ((struct acpi_madt_item_hdr *) t)->type) {
162 if (i == idx)
163 return t;
164 else
165 i++;
167 t += ((struct acpi_madt_item_hdr *) t)->length;
170 return NULL;
173 #if 0
174 static void * acpi_madt_get_item(struct acpi_madt_hdr * hdr,
175 unsigned idx)
177 u8_t * t, * end;
178 int i;
180 t = (u8_t *) hdr + sizeof(struct acpi_madt_hdr);
181 end = (u8_t *) hdr + hdr->hdr.length;
183 for(i = 0 ; i <= idx && t < end; i++) {
184 if (i == idx)
185 return t;
186 t += ((struct acpi_madt_item_hdr *) t)->length;
189 return NULL;
191 #endif
193 static int acpi_rsdp_test(void * buff)
195 struct acpi_rsdp * rsdp = (struct acpi_rsdp *) buff;
197 if (!platform_tbl_checksum_ok(buff, 20))
198 return 0;
199 if (strncmp(rsdp->signature, "RSD PTR ", 8))
200 return 0;
202 return 1;
205 static int get_acpi_rsdp(void)
207 u16_t ebda;
209 * Read 40:0Eh - to find the starting address of the EBDA.
211 acpi_phys_copy (0x40E, &ebda, sizeof(ebda));
212 if (ebda) {
213 ebda <<= 4;
214 if(platform_tbl_ptr(ebda, ebda + 0x400, 16, &acpi_rsdp,
215 sizeof(acpi_rsdp), &machine.acpi_rsdp,
216 acpi_rsdp_test))
217 return 1;
220 /* try BIOS read only mem space */
221 if(platform_tbl_ptr(0xE0000, 0x100000, 16, &acpi_rsdp,
222 sizeof(acpi_rsdp), &machine.acpi_rsdp,
223 acpi_rsdp_test))
224 return 1;
226 machine.acpi_rsdp = 0; /* RSDP cannot be found at this address therefore
227 it is a valid negative value */
228 return 0;
231 static void acpi_init_poweroff(void)
233 u8_t *ptr = NULL;
234 u8_t *start = NULL;
235 u8_t *end = NULL;
236 struct acpi_fadt_header *fadt_header = NULL;
237 struct acpi_rsdt * dsdt_header = NULL;
238 char *msg = NULL;
240 /* Everything used here existed since ACPI spec 1.0 */
241 /* So we can safely use them */
242 fadt_header = (struct acpi_fadt_header *)
243 acpi_phys2vir(acpi_get_table_base("FACP"));
244 if (fadt_header == NULL) {
245 msg = "Could not load FACP";
246 goto exit;
249 dsdt_header = (struct acpi_rsdt *)
250 acpi_phys2vir((phys_bytes) fadt_header->dsdt);
251 if (dsdt_header == NULL) {
252 msg = "Could not load DSDT";
253 goto exit;
256 pm1a_cnt_blk = fadt_header->pm1a_cnt_blk;
257 pm1b_cnt_blk = fadt_header->pm1b_cnt_blk;
259 ptr = start = (u8_t *) dsdt_header->data;
260 end = start + dsdt_header->hdr.length - 4;
262 /* See http://forum.osdev.org/viewtopic.php?t=16990 */
263 /* for layout of \_S5 */
264 while (ptr < end && memcmp(ptr, "_S5_", 4) != 0)
265 ptr++;
267 msg = "Could not read S5 data. Use default SLP_TYPa and SLP_TYPb";
268 if (ptr >= end || ptr == start)
269 goto exit;
271 /* validate AML structure */
272 if (*(ptr + AMI_S5_PACKAGE_OP_OFFSET) != AMI_PACKAGE_OP_CODE)
273 goto exit;
275 if ((ptr < start + (-AMI_S5_NAME_OP_OFFSET_2) ||
276 (*(ptr + AMI_S5_NAME_OP_OFFSET_2) != AMI_NAME_OP_CODE ||
277 *(ptr + AMI_S5_NAME_OP_OFFSET_2 + 1) != '\\')) &&
278 *(ptr + AMI_S5_NAME_OP_OFFSET_1) != AMI_NAME_OP_CODE)
279 goto exit;
281 ptr += AMI_S5_PACKET_LENGTH_OFFSET;
282 if (ptr >= end)
283 goto exit;
285 /* package length */
286 ptr += ((*ptr & AMI_PACKAGE_LENGTH_ENCODING_BITS_MASK) >>
287 AMI_PACKAGE_LENGTH_ENCODING_BITS_SHIFT) +
288 AMI_MIN_PACKAGE_LENGTH + AMI_NUM_ELEMENTS_LENGTH;
289 if (ptr >= end)
290 goto exit;
292 if (*ptr == AMI_BYTE_PREFIX_CODE)
293 ptr++; /* skip byte prefix */
295 slp_typa = (*ptr) << AMI_SLP_TYPA_SHIFT;
297 ptr++; /* move to SLP_TYPb */
298 if (*ptr == AMI_BYTE_PREFIX_CODE)
299 ptr++; /* skip byte prefix */
301 slp_typb = (*ptr) << AMI_SLP_TYPB_SHIFT;
303 msg = "poweroff initialized";
305 exit:
306 if (msg) {
307 printf("acpi: %s\n", msg);
311 void acpi_init(void)
313 int s, i;
314 read_func = acpi_phys_copy;
316 if (!get_acpi_rsdp()) {
317 printf("WARNING : Cannot configure ACPI\n");
318 return;
321 s = acpi_read_sdt_at(acpi_rsdp.rsdt_addr, (struct acpi_sdt_header *) &rsdt,
322 sizeof(struct acpi_rsdt), ACPI_SDT_SIGNATURE(RSDT));
324 sdt_count = (s - sizeof(struct acpi_sdt_header)) / sizeof(u32_t);
326 for (i = 0; i < sdt_count; i++) {
327 struct acpi_sdt_header hdr;
328 int j;
329 if (read_func(rsdt.data[i], &hdr, sizeof(struct acpi_sdt_header))) {
330 printf("ERROR acpi cannot read header at 0x%x\n",
331 rsdt.data[i]);
332 return;
335 for (j = 0 ; j < ACPI_SDT_SIGNATURE_LEN; j++)
336 sdt_trans[i].signature[j] = hdr.signature[j];
337 sdt_trans[i].signature[ACPI_SDT_SIGNATURE_LEN] = '\0';
338 sdt_trans[i].length = hdr.length;
341 acpi_init_poweroff();
344 struct acpi_madt_ioapic * acpi_get_ioapic_next(void)
346 static unsigned idx = 0;
347 static struct acpi_madt_hdr * madt_hdr;
349 struct acpi_madt_ioapic * ret;
351 if (idx == 0) {
352 madt_hdr = (struct acpi_madt_hdr *)
353 acpi_phys2vir(acpi_get_table_base("APIC"));
354 if (madt_hdr == NULL)
355 return NULL;
358 ret = (struct acpi_madt_ioapic *)
359 acpi_madt_get_typed_item(madt_hdr, ACPI_MADT_TYPE_IOAPIC, idx);
360 if (ret)
361 idx++;
363 return ret;
366 struct acpi_madt_lapic * acpi_get_lapic_next(void)
368 static unsigned idx = 0;
369 static struct acpi_madt_hdr * madt_hdr;
371 struct acpi_madt_lapic * ret;
373 if (idx == 0) {
374 madt_hdr = (struct acpi_madt_hdr *)
375 acpi_phys2vir(acpi_get_table_base("APIC"));
376 if (madt_hdr == NULL)
377 return NULL;
380 for (;;) {
381 ret = (struct acpi_madt_lapic *)
382 acpi_madt_get_typed_item(madt_hdr,
383 ACPI_MADT_TYPE_LAPIC, idx);
384 if (!ret)
385 break;
387 idx++;
389 /* report only usable CPUs */
390 if (ret->flags & 1)
391 break;
394 return ret;
397 void __k_unpaged_acpi_poweroff(void)
399 /* NO OP poweroff symbol*/
402 void acpi_poweroff(void)
404 if (pm1a_cnt_blk == 0) {
405 return;
407 outw(pm1a_cnt_blk, slp_typa | SLP_EN_CODE);
408 if (pm1b_cnt_blk != 0) {
409 outw(pm1b_cnt_blk, slp_typb | SLP_EN_CODE);