83abd81c8a
This patch adds CC6 power save support to the AMD Family 15h support code. As CC6 is a complex power saving state that relies heavily on CPU, northbridge, and southbridge cooperation, this patch alters significant amounts of code throughout the tree simultaneously. Allowing the CPU to enter CC6 allows the second level of turbo boost to be reached, and also provides significant power savings when the system is idle due to the complete core shutdown. Change-Id: I44ce157cda97fb85f3e8f3d7262d4712b5410670 Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> Reviewed-on: http://review.coreboot.org/11979 Tested-by: build bot (Jenkins) Tested-by: Raptor Engineering Automated Test Stand <noreply@raptorengineeringinc.com> Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
742 lines
18 KiB
C
742 lines
18 KiB
C
/*
|
||
* This file is part of the coreboot project.
|
||
*
|
||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||
* Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz>
|
||
*
|
||
* This program is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; version 2 of the License.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*/
|
||
|
||
/* How much nesting do we support? */
|
||
#define ACPIGEN_LENSTACK_SIZE 10
|
||
|
||
/*
|
||
* If you need to change this, change acpigen_write_len_f and
|
||
* acpigen_pop_len
|
||
*/
|
||
|
||
#define ACPIGEN_MAXLEN 0xfffff
|
||
|
||
#include <string.h>
|
||
#include <arch/acpigen.h>
|
||
#include <console/console.h>
|
||
#include <device/device.h>
|
||
|
||
static char *gencurrent;
|
||
|
||
char *len_stack[ACPIGEN_LENSTACK_SIZE];
|
||
int ltop = 0;
|
||
|
||
void acpigen_write_len_f(void)
|
||
{
|
||
ASSERT(ltop < (ACPIGEN_LENSTACK_SIZE - 1))
|
||
len_stack[ltop++] = gencurrent;
|
||
acpigen_emit_byte(0);
|
||
acpigen_emit_byte(0);
|
||
acpigen_emit_byte(0);
|
||
}
|
||
|
||
void acpigen_pop_len(void)
|
||
{
|
||
int len;
|
||
ASSERT(ltop > 0)
|
||
char *p = len_stack[--ltop];
|
||
len = gencurrent - p;
|
||
ASSERT(len <= ACPIGEN_MAXLEN)
|
||
/* generate store length for 0xfffff max */
|
||
p[0] = (0x80 | (len & 0xf));
|
||
p[1] = (len >> 4 & 0xff);
|
||
p[2] = (len >> 12 & 0xff);
|
||
|
||
}
|
||
|
||
void acpigen_set_current(char *curr)
|
||
{
|
||
gencurrent = curr;
|
||
}
|
||
|
||
char *acpigen_get_current(void)
|
||
{
|
||
return gencurrent;
|
||
}
|
||
|
||
void acpigen_emit_byte(unsigned char b)
|
||
{
|
||
(*gencurrent++) = b;
|
||
}
|
||
|
||
void acpigen_write_package(int nr_el)
|
||
{
|
||
/* package op */
|
||
acpigen_emit_byte(0x12);
|
||
acpigen_write_len_f();
|
||
acpigen_emit_byte(nr_el);
|
||
}
|
||
|
||
void acpigen_write_byte(unsigned int data)
|
||
{
|
||
/* byte op */
|
||
acpigen_emit_byte(0xa);
|
||
acpigen_emit_byte(data & 0xff);
|
||
}
|
||
|
||
void acpigen_write_dword(unsigned int data)
|
||
{
|
||
/* dword op */
|
||
acpigen_emit_byte(0xc);
|
||
acpigen_emit_byte(data & 0xff);
|
||
acpigen_emit_byte((data >> 8) & 0xff);
|
||
acpigen_emit_byte((data >> 16) & 0xff);
|
||
acpigen_emit_byte((data >> 24) & 0xff);
|
||
}
|
||
|
||
void acpigen_write_qword(uint64_t data)
|
||
{
|
||
/* qword op */
|
||
acpigen_emit_byte(0xe);
|
||
acpigen_emit_byte(data & 0xff);
|
||
acpigen_emit_byte((data >> 8) & 0xff);
|
||
acpigen_emit_byte((data >> 16) & 0xff);
|
||
acpigen_emit_byte((data >> 24) & 0xff);
|
||
acpigen_emit_byte((data >> 32) & 0xff);
|
||
acpigen_emit_byte((data >> 40) & 0xff);
|
||
acpigen_emit_byte((data >> 48) & 0xff);
|
||
acpigen_emit_byte((data >> 56) & 0xff);
|
||
}
|
||
|
||
void acpigen_write_name_byte(const char *name, uint8_t val)
|
||
{
|
||
acpigen_write_name(name);
|
||
acpigen_write_byte(val);
|
||
}
|
||
|
||
void acpigen_write_name_dword(const char *name, uint32_t val)
|
||
{
|
||
acpigen_write_name(name);
|
||
acpigen_write_dword(val);
|
||
}
|
||
|
||
void acpigen_write_name_qword(const char *name, uint64_t val)
|
||
{
|
||
acpigen_write_name(name);
|
||
acpigen_write_qword(val);
|
||
}
|
||
|
||
void acpigen_emit_stream(const char *data, int size)
|
||
{
|
||
int i;
|
||
for (i = 0; i < size; i++) {
|
||
acpigen_emit_byte(data[i]);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* The naming conventions for ACPI namespace names are a bit tricky as
|
||
* each element has to be 4 chars wide (»All names are a fixed 32 bits.«)
|
||
* and »By convention, when an ASL compiler pads a name shorter than 4
|
||
* characters, it is done so with trailing underscores (‘_’).«.
|
||
*
|
||
* Check sections 5.3, 18.2.2 and 18.4 of ACPI spec 3.0 for details.
|
||
*/
|
||
|
||
static void acpigen_emit_simple_namestring(const char *name) {
|
||
int i;
|
||
char ud[] = "____";
|
||
for (i = 0; i < 4; i++) {
|
||
if ((name[i] == '\0') || (name[i] == '.')) {
|
||
acpigen_emit_stream(ud, 4 - i);
|
||
break;
|
||
} else {
|
||
acpigen_emit_byte(name[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void acpigen_emit_double_namestring(const char *name, int dotpos) {
|
||
/* mark dual name prefix */
|
||
acpigen_emit_byte(0x2e);
|
||
acpigen_emit_simple_namestring(name);
|
||
acpigen_emit_simple_namestring(&name[dotpos + 1]);
|
||
}
|
||
|
||
static void acpigen_emit_multi_namestring(const char *name) {
|
||
int count = 0;
|
||
unsigned char *pathlen;
|
||
/* mark multi name prefix */
|
||
acpigen_emit_byte(0x2f);
|
||
acpigen_emit_byte(0x0);
|
||
pathlen = ((unsigned char *) acpigen_get_current()) - 1;
|
||
|
||
while (name[0] != '\0') {
|
||
acpigen_emit_simple_namestring(name);
|
||
/* find end or next entity */
|
||
while ((name[0] != '.') && (name[0] != '\0'))
|
||
name++;
|
||
/* forward to next */
|
||
if (name[0] == '.')
|
||
name++;
|
||
count++;
|
||
}
|
||
|
||
pathlen[0] = count;
|
||
}
|
||
|
||
|
||
void acpigen_emit_namestring(const char *namepath) {
|
||
int dotcount = 0, i;
|
||
int dotpos = 0;
|
||
|
||
/* We can start with a '\'. */
|
||
if (namepath[0] == '\\') {
|
||
acpigen_emit_byte('\\');
|
||
namepath++;
|
||
}
|
||
|
||
/* And there can be any number of '^' */
|
||
while (namepath[0] == '^') {
|
||
acpigen_emit_byte('^');
|
||
namepath++;
|
||
}
|
||
|
||
/* If we have only \\ or only ^...^. Then we need to put a null
|
||
name (0x00). */
|
||
if(namepath[0] == '\0') {
|
||
acpigen_emit_byte(0x00);
|
||
return;
|
||
}
|
||
|
||
i = 0;
|
||
while (namepath[i] != '\0') {
|
||
if (namepath[i] == '.') {
|
||
dotcount++;
|
||
dotpos = i;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
if (dotcount == 0) {
|
||
acpigen_emit_simple_namestring(namepath);
|
||
} else if (dotcount == 1) {
|
||
acpigen_emit_double_namestring(namepath, dotpos);
|
||
} else {
|
||
acpigen_emit_multi_namestring(namepath);
|
||
}
|
||
}
|
||
|
||
void acpigen_write_name(const char *name)
|
||
{
|
||
/* name op */
|
||
acpigen_emit_byte(0x8);
|
||
acpigen_emit_namestring(name);
|
||
}
|
||
|
||
void acpigen_write_scope(const char *name)
|
||
{
|
||
/* scope op */
|
||
acpigen_emit_byte(0x10);
|
||
acpigen_write_len_f();
|
||
acpigen_emit_namestring(name);
|
||
}
|
||
|
||
void acpigen_write_processor(u8 cpuindex, u32 pblock_addr, u8 pblock_len)
|
||
{
|
||
/*
|
||
Processor (\_PR.CPUcpuindex, cpuindex, pblock_addr, pblock_len)
|
||
{
|
||
*/
|
||
char pscope[16];
|
||
/* processor op */
|
||
acpigen_emit_byte(0x5b);
|
||
acpigen_emit_byte(0x83);
|
||
acpigen_write_len_f();
|
||
|
||
snprintf(pscope, sizeof (pscope),
|
||
"\\_PR.CP%02d", (unsigned int) cpuindex);
|
||
acpigen_emit_namestring(pscope);
|
||
acpigen_emit_byte(cpuindex);
|
||
acpigen_emit_byte(pblock_addr & 0xff);
|
||
acpigen_emit_byte((pblock_addr >> 8) & 0xff);
|
||
acpigen_emit_byte((pblock_addr >> 16) & 0xff);
|
||
acpigen_emit_byte((pblock_addr >> 24) & 0xff);
|
||
acpigen_emit_byte(pblock_len);
|
||
}
|
||
|
||
void acpigen_write_empty_PCT(void)
|
||
{
|
||
/*
|
||
Name (_PCT, Package (0x02)
|
||
{
|
||
ResourceTemplate ()
|
||
{
|
||
Register (FFixedHW,
|
||
0x00, // Bit Width
|
||
0x00, // Bit Offset
|
||
0x0000000000000000, // Address
|
||
,)
|
||
},
|
||
|
||
ResourceTemplate ()
|
||
{
|
||
Register (FFixedHW,
|
||
0x00, // Bit Width
|
||
0x00, // Bit Offset
|
||
0x0000000000000000, // Address
|
||
,)
|
||
}
|
||
})
|
||
*/
|
||
static char stream[] = {
|
||
0x08, 0x5F, 0x50, 0x43, 0x54, 0x12, 0x2C, /* 00000030 "0._PCT.," */
|
||
0x02, 0x11, 0x14, 0x0A, 0x11, 0x82, 0x0C, 0x00, /* 00000038 "........" */
|
||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000040 "........" */
|
||
0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x11, 0x14, /* 00000048 "....y..." */
|
||
0x0A, 0x11, 0x82, 0x0C, 0x00, 0x7F, 0x00, 0x00, /* 00000050 "........" */
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000058 "........" */
|
||
0x00, 0x79, 0x00
|
||
};
|
||
acpigen_emit_stream(stream, ARRAY_SIZE(stream));
|
||
}
|
||
|
||
void acpigen_write_empty_PTC(void)
|
||
{
|
||
/*
|
||
Name (_PTC, Package (0x02)
|
||
{
|
||
ResourceTemplate ()
|
||
{
|
||
Register (FFixedHW,
|
||
0x00, // Bit Width
|
||
0x00, // Bit Offset
|
||
0x0000000000000000, // Address
|
||
,)
|
||
},
|
||
|
||
ResourceTemplate ()
|
||
{
|
||
Register (FFixedHW,
|
||
0x00, // Bit Width
|
||
0x00, // Bit Offset
|
||
0x0000000000000000, // Address
|
||
,)
|
||
}
|
||
})
|
||
*/
|
||
acpi_addr_t addr = {
|
||
.space_id = ACPI_ADDRESS_SPACE_FIXED,
|
||
.bit_width = 0,
|
||
.bit_offset = 0,
|
||
{
|
||
.resv = 0
|
||
},
|
||
.addrl = 0,
|
||
.addrh = 0,
|
||
};
|
||
|
||
acpigen_write_name("_PTC");
|
||
acpigen_write_package(2);
|
||
|
||
/* ControlRegister */
|
||
acpigen_write_resourcetemplate_header();
|
||
acpigen_write_register(&addr);
|
||
acpigen_write_resourcetemplate_footer();
|
||
|
||
/* StatusRegister */
|
||
acpigen_write_resourcetemplate_header();
|
||
acpigen_write_register(&addr);
|
||
acpigen_write_resourcetemplate_footer();
|
||
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
void acpigen_write_method(const char *name, int nargs)
|
||
{
|
||
/* method op */
|
||
acpigen_emit_byte(0x14);
|
||
acpigen_write_len_f();
|
||
acpigen_emit_namestring(name);
|
||
acpigen_emit_byte(nargs & 7);
|
||
}
|
||
|
||
void acpigen_write_device(const char *name)
|
||
{
|
||
/* method op */
|
||
acpigen_emit_byte(0x5b);
|
||
acpigen_emit_byte(0x82);
|
||
acpigen_write_len_f();
|
||
acpigen_emit_namestring(name);
|
||
}
|
||
|
||
/*
|
||
* Generates a func with max supported P-states.
|
||
*/
|
||
void acpigen_write_PPC(u8 nr)
|
||
{
|
||
/*
|
||
Method (_PPC, 0, NotSerialized)
|
||
{
|
||
Return (nr)
|
||
}
|
||
*/
|
||
acpigen_write_method("_PPC", 0);
|
||
/* return */
|
||
acpigen_emit_byte(0xa4);
|
||
/* arg */
|
||
acpigen_write_byte(nr);
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
/*
|
||
* Generates a func with max supported P-states saved
|
||
* in the variable PPCM.
|
||
*/
|
||
void acpigen_write_PPC_NVS(void)
|
||
{
|
||
/*
|
||
Method (_PPC, 0, NotSerialized)
|
||
{
|
||
Return (PPCM)
|
||
}
|
||
*/
|
||
acpigen_write_method("_PPC", 0);
|
||
/* return */
|
||
acpigen_emit_byte(0xa4);
|
||
/* arg */
|
||
acpigen_emit_namestring("PPCM");
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
void acpigen_write_TPC(const char *gnvs_tpc_limit)
|
||
{
|
||
/*
|
||
// Sample _TPC method
|
||
Method (_TPC, 0, NotSerialized)
|
||
{
|
||
Return (\TLVL)
|
||
}
|
||
*/
|
||
acpigen_write_method("_TPC", 0);
|
||
acpigen_emit_byte(0xa4); /* ReturnOp */
|
||
acpigen_emit_namestring(gnvs_tpc_limit);
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
void acpigen_write_PSS_package(u32 coreFreq, u32 power, u32 transLat,
|
||
u32 busmLat, u32 control, u32 status)
|
||
{
|
||
acpigen_write_package(6);
|
||
acpigen_write_dword(coreFreq);
|
||
acpigen_write_dword(power);
|
||
acpigen_write_dword(transLat);
|
||
acpigen_write_dword(busmLat);
|
||
acpigen_write_dword(control);
|
||
acpigen_write_dword(status);
|
||
acpigen_pop_len();
|
||
|
||
printk(BIOS_DEBUG, "PSS: %uMHz power %u control 0x%x status 0x%x\n",
|
||
coreFreq, power, control, status);
|
||
}
|
||
|
||
void acpigen_write_PSD_package(u32 domain, u32 numprocs, PSD_coord coordtype)
|
||
{
|
||
acpigen_write_name("_PSD");
|
||
acpigen_write_package(1);
|
||
acpigen_write_package(5);
|
||
acpigen_write_byte(5); // 5 values
|
||
acpigen_write_byte(0); // revision 0
|
||
acpigen_write_dword(domain);
|
||
acpigen_write_dword(coordtype);
|
||
acpigen_write_dword(numprocs);
|
||
acpigen_pop_len();
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
void acpigen_write_CST_package_entry(acpi_cstate_t *cstate)
|
||
{
|
||
acpigen_write_package(4);
|
||
acpigen_write_resourcetemplate_header();
|
||
acpigen_write_register(&cstate->resource);
|
||
acpigen_write_resourcetemplate_footer();
|
||
acpigen_write_dword(cstate->ctype);
|
||
acpigen_write_dword(cstate->latency);
|
||
acpigen_write_dword(cstate->power);
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
void acpigen_write_CST_package(acpi_cstate_t *cstate, int nentries)
|
||
{
|
||
int i;
|
||
acpigen_write_name("_CST");
|
||
acpigen_write_package(nentries+1);
|
||
acpigen_write_dword(nentries);
|
||
|
||
for (i = 0; i < nentries; i++)
|
||
acpigen_write_CST_package_entry(cstate + i);
|
||
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
void acpigen_write_CSD_package(u32 domain, u32 numprocs, CSD_coord coordtype, u32 index)
|
||
{
|
||
acpigen_write_name("_CSD");
|
||
acpigen_write_package(1);
|
||
acpigen_write_package(6);
|
||
acpigen_write_byte(6); // 6 values
|
||
acpigen_write_byte(0); // revision 0
|
||
acpigen_write_dword(domain);
|
||
acpigen_write_dword(coordtype);
|
||
acpigen_write_dword(numprocs);
|
||
acpigen_write_dword(index);
|
||
acpigen_pop_len();
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
void acpigen_write_TSS_package(int entries, acpi_tstate_t *tstate_list)
|
||
{
|
||
/*
|
||
Sample _TSS package with 100% and 50% duty cycles
|
||
Name (_TSS, Package (0x02)
|
||
{
|
||
Package(){100, 1000, 0, 0x00, 0)
|
||
Package(){50, 520, 0, 0x18, 0)
|
||
})
|
||
*/
|
||
int i;
|
||
acpi_tstate_t *tstate = tstate_list;
|
||
|
||
acpigen_write_name("_TSS");
|
||
acpigen_write_package(entries);
|
||
|
||
for (i = 0; i < entries; i++) {
|
||
acpigen_write_package(5);
|
||
acpigen_write_dword(tstate->percent);
|
||
acpigen_write_dword(tstate->power);
|
||
acpigen_write_dword(tstate->latency);
|
||
acpigen_write_dword(tstate->control);
|
||
acpigen_write_dword(tstate->status);
|
||
acpigen_pop_len();
|
||
tstate++;
|
||
}
|
||
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
void acpigen_write_TSD_package(u32 domain, u32 numprocs, PSD_coord coordtype)
|
||
{
|
||
acpigen_write_name("_TSD");
|
||
acpigen_write_package(1);
|
||
acpigen_write_package(5);
|
||
acpigen_write_byte(5); // 5 values
|
||
acpigen_write_byte(0); // revision 0
|
||
acpigen_write_dword(domain);
|
||
acpigen_write_dword(coordtype);
|
||
acpigen_write_dword(numprocs);
|
||
acpigen_pop_len();
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
|
||
|
||
void acpigen_write_mem32fixed(int readwrite, u32 base, u32 size)
|
||
{
|
||
/*
|
||
* acpi 4.0 section 6.4.3.4: 32-Bit Fixed Memory Range Descriptor
|
||
* Byte 0:
|
||
* Bit7 : 1 => big item
|
||
* Bit6-0: 0000110 (0x6) => 32-bit fixed memory
|
||
*/
|
||
acpigen_emit_byte(0x86);
|
||
/* Byte 1+2: length (0x0009) */
|
||
acpigen_emit_byte(0x09);
|
||
acpigen_emit_byte(0x00);
|
||
/* bit1-7 are ignored */
|
||
acpigen_emit_byte(readwrite ? 0x01 : 0x00);
|
||
acpigen_emit_byte(base & 0xff);
|
||
acpigen_emit_byte((base >> 8) & 0xff);
|
||
acpigen_emit_byte((base >> 16) & 0xff);
|
||
acpigen_emit_byte((base >> 24) & 0xff);
|
||
acpigen_emit_byte(size & 0xff);
|
||
acpigen_emit_byte((size >> 8) & 0xff);
|
||
acpigen_emit_byte((size >> 16) & 0xff);
|
||
acpigen_emit_byte((size >> 24) & 0xff);
|
||
}
|
||
|
||
void acpigen_write_register(acpi_addr_t *addr)
|
||
{
|
||
acpigen_emit_byte(0x82); /* Register Descriptor */
|
||
acpigen_emit_byte(0x0c); /* Register Length 7:0 */
|
||
acpigen_emit_byte(0x00); /* Register Length 15:8 */
|
||
acpigen_emit_byte(addr->space_id); /* Address Space ID */
|
||
acpigen_emit_byte(addr->bit_width); /* Register Bit Width */
|
||
acpigen_emit_byte(addr->bit_offset); /* Register Bit Offset */
|
||
acpigen_emit_byte(addr->resv); /* Register Access Size */
|
||
acpigen_emit_byte(addr->addrl & 0xff); /* Register Address Low */
|
||
acpigen_emit_byte((addr->addrl >> 8) & 0xff);
|
||
acpigen_emit_byte((addr->addrl >> 16) & 0xff);
|
||
acpigen_emit_byte((addr->addrl >> 24) & 0xff);
|
||
acpigen_emit_byte(addr->addrh & 0xff); /* Register Address High */
|
||
acpigen_emit_byte((addr->addrh >> 8) & 0xff);
|
||
acpigen_emit_byte((addr->addrh >> 16) & 0xff);
|
||
acpigen_emit_byte((addr->addrh >> 24) & 0xff);
|
||
}
|
||
|
||
void acpigen_write_irq(u16 mask)
|
||
{
|
||
/*
|
||
* acpi 3.0b section 6.4.2.1: IRQ Descriptor
|
||
* Byte 0:
|
||
* Bit7 : 0 => small item
|
||
* Bit6-3: 0100 (0x4) => IRQ port descriptor
|
||
* Bit2-0: 010 (0x2) => 2 Bytes long
|
||
*/
|
||
acpigen_emit_byte(0x22);
|
||
acpigen_emit_byte(mask & 0xff);
|
||
acpigen_emit_byte((mask >> 8) & 0xff);
|
||
}
|
||
|
||
void acpigen_write_io16(u16 min, u16 max, u8 align, u8 len, u8 decode16)
|
||
{
|
||
/*
|
||
* acpi 4.0 section 6.4.2.6: I/O Port Descriptor
|
||
* Byte 0:
|
||
* Bit7 : 0 => small item
|
||
* Bit6-3: 1000 (0x8) => I/O port descriptor
|
||
* Bit2-0: 111 (0x7) => 7 Bytes long
|
||
*/
|
||
acpigen_emit_byte(0x47);
|
||
/* Does the device decode all 16 or just 10 bits? */
|
||
/* bit1-7 are ignored */
|
||
acpigen_emit_byte(decode16 ? 0x01 : 0x00);
|
||
/* minimum base address the device may be configured for */
|
||
acpigen_emit_byte(min & 0xff);
|
||
acpigen_emit_byte((min >> 8) & 0xff);
|
||
/* maximum base address the device may be configured for */
|
||
acpigen_emit_byte(max & 0xff);
|
||
acpigen_emit_byte((max >> 8) & 0xff);
|
||
/* alignment for min base */
|
||
acpigen_emit_byte(align & 0xff);
|
||
acpigen_emit_byte(len & 0xff);
|
||
}
|
||
|
||
void acpigen_write_resourcetemplate_header(void)
|
||
{
|
||
/*
|
||
* A ResourceTemplate() is a Buffer() with a
|
||
* (Byte|Word|DWord) containing the length, followed by one or more
|
||
* resource items, terminated by the end tag.
|
||
* (small item 0xf, len 1)
|
||
*/
|
||
acpigen_emit_byte(0x11); /* Buffer opcode */
|
||
acpigen_write_len_f();
|
||
acpigen_emit_byte(0x0b); /* Word opcode */
|
||
len_stack[ltop++] = acpigen_get_current();
|
||
acpigen_emit_byte(0x00);
|
||
acpigen_emit_byte(0x00);
|
||
}
|
||
|
||
void acpigen_write_resourcetemplate_footer(void)
|
||
{
|
||
char *p = len_stack[--ltop];
|
||
int len;
|
||
/*
|
||
* end tag (acpi 4.0 Section 6.4.2.8)
|
||
* 0x79 <checksum>
|
||
* 0x00 is treated as a good checksum according to the spec
|
||
* and is what iasl generates.
|
||
*/
|
||
acpigen_emit_byte(0x79);
|
||
acpigen_emit_byte(0x00);
|
||
|
||
len = gencurrent - p;
|
||
|
||
/* patch len word */
|
||
p[0] = len & 0xff;
|
||
p[1] = (len >> 8) & 0xff;
|
||
/* patch len field */
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
static void acpigen_add_mainboard_rsvd_mem32(void *gp, struct device *dev,
|
||
struct resource *res)
|
||
{
|
||
acpigen_write_mem32fixed(0, res->base, res->size);
|
||
}
|
||
|
||
static void acpigen_add_mainboard_rsvd_io(void *gp, struct device *dev,
|
||
struct resource *res)
|
||
{
|
||
resource_t base = res->base;
|
||
resource_t size = res->size;
|
||
while (size > 0) {
|
||
resource_t sz = size > 255 ? 255 : size;
|
||
acpigen_write_io16(base, base, 0, sz, 1);
|
||
size -= sz;
|
||
base += sz;
|
||
}
|
||
}
|
||
|
||
void acpigen_write_mainboard_resource_template(void)
|
||
{
|
||
acpigen_write_resourcetemplate_header();
|
||
|
||
/* Add reserved memory ranges. */
|
||
search_global_resources(
|
||
IORESOURCE_MEM | IORESOURCE_RESERVE,
|
||
IORESOURCE_MEM | IORESOURCE_RESERVE,
|
||
acpigen_add_mainboard_rsvd_mem32, 0);
|
||
|
||
/* Add reserved io ranges. */
|
||
search_global_resources(
|
||
IORESOURCE_IO | IORESOURCE_RESERVE,
|
||
IORESOURCE_IO | IORESOURCE_RESERVE,
|
||
acpigen_add_mainboard_rsvd_io, 0);
|
||
|
||
acpigen_write_resourcetemplate_footer();
|
||
}
|
||
|
||
void acpigen_write_mainboard_resources(const char *scope, const char *name)
|
||
{
|
||
acpigen_write_scope(scope);
|
||
acpigen_write_name(name);
|
||
acpigen_write_mainboard_resource_template();
|
||
acpigen_pop_len();
|
||
}
|
||
|
||
static int hex2bin(const char c)
|
||
{
|
||
if (c >= 'A' && c <= 'F')
|
||
return c - 'A' + 10;
|
||
if (c >= 'a' && c <= 'f')
|
||
return c - 'a' + 10;
|
||
return c - '0';
|
||
}
|
||
|
||
void acpigen_emit_eisaid(const char *eisaid)
|
||
{
|
||
u32 compact = 0;
|
||
|
||
/* Clamping individual values would be better but
|
||
there is a disagreement over what is a valid
|
||
EISA id, so accept anything and don't clamp,
|
||
parent code should create a valid EISAid.
|
||
*/
|
||
compact |= (eisaid[0] - 'A' + 1) << 26;
|
||
compact |= (eisaid[1] - 'A' + 1) << 21;
|
||
compact |= (eisaid[2] - 'A' + 1) << 16;
|
||
compact |= hex2bin(eisaid[3]) << 12;
|
||
compact |= hex2bin(eisaid[4]) << 8;
|
||
compact |= hex2bin(eisaid[5]) << 4;
|
||
compact |= hex2bin(eisaid[6]);
|
||
|
||
acpigen_emit_byte(0xc);
|
||
acpigen_emit_byte((compact >> 24) & 0xff);
|
||
acpigen_emit_byte((compact >> 16) & 0xff);
|
||
acpigen_emit_byte((compact >> 8) & 0xff);
|
||
acpigen_emit_byte(compact & 0xff);
|
||
}
|