summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/arch/x86/acpigen.c116
-rw-r--r--src/arch/x86/include/arch/acpigen.h72
2 files changed, 188 insertions, 0 deletions
diff --git a/src/arch/x86/acpigen.c b/src/arch/x86/acpigen.c
index 42d42044af..0c5a10c5bf 100644
--- a/src/arch/x86/acpigen.c
+++ b/src/arch/x86/acpigen.c
@@ -351,6 +351,122 @@ void acpigen_write_processor(u8 cpuindex, u32 pblock_addr, u8 pblock_len)
acpigen_emit_byte(pblock_len);
}
+/*
+ * Generate ACPI AML code for OperationRegion
+ * Arg0: Pointer to struct opregion opreg = OPREGION(rname, space, offset, len)
+ * where rname is region name, space is region space, offset is region offset &
+ * len is region length.
+ * OperationRegion(regionname, regionspace, regionoffset, regionlength)
+ */
+void acpigen_write_opregion(struct opregion *opreg)
+{
+ /* OpregionOp */
+ acpigen_emit_ext_op(OPREGION_OP);
+ /* NameString 4 chars only */
+ acpigen_emit_simple_namestring(opreg->name);
+ /* RegionSpace */
+ acpigen_emit_byte(opreg->regionspace);
+ /* RegionOffset & RegionLen, it can be byte word or double word */
+ acpigen_write_integer(opreg->regionoffset);
+ acpigen_write_integer(opreg->regionlen);
+}
+
+static void acpigen_write_field_offset(uint32_t offset,
+ uint32_t current_bit_pos)
+{
+ uint32_t diff_bits;
+ uint8_t i, j;
+ uint8_t emit[4];
+
+ if (offset < current_bit_pos) {
+ printk(BIOS_WARNING, "%s: Cannot move offset backward",
+ __func__);
+ return;
+ }
+
+ diff_bits = offset - current_bit_pos;
+ /* Upper limit */
+ if (diff_bits > 0xFFFFFFF) {
+ printk(BIOS_WARNING, "%s: Offset very large to encode",
+ __func__);
+ return;
+ }
+
+ i = 1;
+ if (diff_bits < 0x40) {
+ emit[0] = diff_bits & 0x3F;
+ } else {
+ emit[0] = diff_bits & 0xF;
+ diff_bits >>= 4;
+ while (diff_bits) {
+ emit[i] = diff_bits & 0xFF;
+ i++;
+ diff_bits >>= 8;
+ }
+ }
+ /* Update bit 7:6 : Number of bytes followed by emit[0] */
+ emit[0] |= (i - 1) << 6;
+
+ acpigen_emit_byte(0);
+ for (j = 0; j < i; j++)
+ acpigen_emit_byte(emit[j]);
+}
+
+/*
+ * Generate ACPI AML code for Field
+ * Arg0: region name
+ * Arg1: Pointer to struct fieldlist.
+ * Arg2: no. of entries in Arg1
+ * Arg3: flags which indicate filed access type, lock rule & update rule.
+ * Example with fieldlist
+ * struct fieldlist l[] = {
+ * FIELDLIST_OFFSET(0x84),
+ * FIELDLIST_NAMESTR("PMCS", 2),
+ * };
+ * acpigen_write_field("UART", l ,ARRAY_SIZE(l), FIELD_ANYACC | FIELD_NOLOCK |
+ * FIELD_PRESERVE);
+ * Output:
+ * Field (UART, AnyAcc, NoLock, Preserve)
+ * {
+ * Offset (0x84),
+ * PMCS, 2
+ * }
+ */
+void acpigen_write_field(const char *name, struct fieldlist *l, size_t count,
+ uint8_t flags)
+{
+ uint16_t i;
+ uint32_t current_bit_pos = 0;
+
+ /* FieldOp */
+ acpigen_emit_ext_op(FIELD_OP);
+ /* Package Length */
+ acpigen_write_len_f();
+ /* NameString 4 chars only */
+ acpigen_emit_simple_namestring(name);
+ /* Field Flag */
+ acpigen_emit_byte(flags);
+
+ for (i = 0; i < count; i++) {
+ switch (l[i].type) {
+ case NAME_STRING:
+ acpigen_emit_simple_namestring(l[i].name);
+ acpigen_emit_byte(l[i].bits);
+ current_bit_pos += l[i].bits;
+ break;
+ case OFFSET:
+ acpigen_write_field_offset(l[i].bits, current_bit_pos);
+ current_bit_pos = l[i].bits;
+ break;
+ default:
+ printk(BIOS_ERR, "%s: Invalid field type 0x%X\n"
+ , __func__, l[i].type);
+ break;
+ }
+ }
+ acpigen_pop_len();
+}
+
void acpigen_write_empty_PCT(void)
{
/*
diff --git a/src/arch/x86/include/arch/acpigen.h b/src/arch/x86/include/arch/acpigen.h
index 7c15a31bdd..4169fdf070 100644
--- a/src/arch/x86/include/arch/acpigen.h
+++ b/src/arch/x86/include/arch/acpigen.h
@@ -53,6 +53,8 @@ enum {
EXT_OP_PREFIX = 0x5B,
SLEEP_OP = 0x22,
DEBUG_OP = 0x31,
+ OPREGION_OP = 0x80,
+ FIELD_OP = 0x81,
DEVICE_OP = 0x82,
PROCESSOR_OP = 0x83,
POWER_RES_OP = 0x84,
@@ -84,6 +86,64 @@ enum {
ONES_OP = 0xFF,
};
+#define FIELDLIST_OFFSET(X) { .type = OFFSET, \
+ .name = "",\
+ .bits = X * 8, \
+ }
+#define FIELDLIST_NAMESTR(X, Y) { .type = NAME_STRING, \
+ .name = X, \
+ .bits = Y, \
+ }
+
+#define FIELD_ANYACC 0
+#define FIELD_BYTEACC 1
+#define FIELD_WORDACC 2
+#define FIELD_DWORDACC 3
+#define FIELD_QWORDACC 4
+#define FIELD_BUFFERACC 5
+#define FIELD_NOLOCK (0<<4)
+#define FIELD_LOCK (1<<4)
+#define FIELD_PRESERVE (0<<5)
+#define FIELD_WRITEASONES (1<<5)
+#define FIELD_WRITEASZEROS (2<<5)
+
+enum field_type {
+ OFFSET,
+ NAME_STRING,
+ FIELD_TYPE_MAX,
+};
+
+struct fieldlist {
+ enum field_type type;
+ const char *name;
+ u32 bits;
+};
+
+#define OPREGION(rname, space, offset, len) {.name = rname, \
+ .regionspace = space, \
+ .regionoffset = offset, \
+ .regionlen = len, \
+ }
+
+enum region_space {
+ SYSTEMMEMORY,
+ SYSTEMIO,
+ PCI_CONFIG,
+ EMBEDDEDCONTROL,
+ SMBUS,
+ CMOS,
+ PCIBARTARGET,
+ IPMI,
+ REGION_SPACE_MAX,
+};
+
+struct opregion {
+ const char *name;
+ enum region_space regionspace;
+ unsigned long regionoffset;
+ unsigned long regionlen;
+};
+
void acpigen_write_len_f(void);
void acpigen_pop_len(void);
void acpigen_set_current(char *curr);
@@ -175,6 +235,18 @@ void acpigen_write_return_byte(uint8_t arg);
*/
void acpigen_write_dsm(const char *uuid, void (*callbacks[])(void *),
size_t count, void *arg);
+/*
+ * Generate ACPI AML code for OperationRegion
+ * This function takes input region name, region space, region offset & region
+ * length.
+ */
+void acpigen_write_opregion(struct opregion *opreg);
+/*
+ * Generate ACPI AML code for Field
+ * This function takes input region name, fieldlist, count & flags.
+ */
+void acpigen_write_field(const char *name, struct fieldlist *l, size_t count,
+ uint8_t flags);
int get_cst_entries(acpi_cstate_t **);