All of lore.kernel.org
 help / color / mirror / Atom feed
From: Simon Glass <sjg@chromium.org>
To: u-boot@lists.denx.de
Subject: [PATCH v2 30/39] acpi: Add functions to generate ACPI code
Date: Sun,  8 Mar 2020 21:44:54 -0600	[thread overview]
Message-ID: <20200308214442.v2.30.Ie25c3492416531983761521cbc51e188052e18b4@changeid> (raw)
In-Reply-To: <20200309034504.149659-1-sjg@chromium.org>

Sometimes we need to generate ACPI code on the fly based on things only
known at run time. Add a new 'acpigen' library to handle this. This code
comes from coreboot and has been modified to support the acpi_ctx struct.

Also add acpi_device.c to the build, since these files are co-dependent.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

Changes in v2: None

 include/acpigen.h  |  482 +++++++++++++
 include/dm/acpi.h  |    7 +
 include/irq.h      |    2 +
 lib/acpi/Makefile  |    2 +
 lib/acpi/acpigen.c | 1683 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 2176 insertions(+)
 create mode 100644 include/acpigen.h
 create mode 100644 lib/acpi/acpigen.c

diff --git a/include/acpigen.h b/include/acpigen.h
new file mode 100644
index 0000000000..08000831b9
--- /dev/null
+++ b/include/acpigen.h
@@ -0,0 +1,482 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Core ACPI (Advanced Configuration and Power Interface) support
+ *
+ * Copyright 2019 Google LLC
+ *
+ * Modified from coreboot file acpigen.h
+ */
+
+#ifndef _ACPIGEN_H
+#define _ACPIGEN_H
+
+#include <acpi_table.h>
+
+struct acpi_cstate;
+struct acpi_pld;
+struct acpi_gpio;
+struct acpi_tstate;
+
+/* Values that can be returned for ACPI Device _STA method */
+#define ACPI_STATUS_DEVICE_PRESENT	BIT(0)
+#define ACPI_STATUS_DEVICE_ENABLED	BIT(1)
+#define ACPI_STATUS_DEVICE_SHOW_IN_UI	BIT(2)
+#define ACPI_STATUS_DEVICE_STATE_OK	BIT(3)
+
+#define ACPI_STATUS_DEVICE_ALL_OFF	0
+#define ACPI_STATUS_DEVICE_ALL_ON	(ACPI_STATUS_DEVICE_PRESENT |\
+					 ACPI_STATUS_DEVICE_ENABLED |\
+					 ACPI_STATUS_DEVICE_SHOW_IN_UI |\
+					 ACPI_STATUS_DEVICE_STATE_OK)
+#define ACPI_STATUS_DEVICE_HIDDEN_ON	(ACPI_STATUS_DEVICE_PRESENT |\
+					 ACPI_STATUS_DEVICE_ENABLED |\
+					 ACPI_STATUS_DEVICE_STATE_OK)
+
+/* ACPI Op/Prefix Codes */
+enum {
+	ZERO_OP			= 0x00,
+	ONE_OP			= 0x01,
+	ALIAS_OP		= 0x06,
+	NAME_OP			= 0x08,
+	BYTE_PREFIX		= 0x0A,
+	WORD_PREFIX		= 0x0B,
+	DWORD_PREFIX		= 0x0C,
+	STRING_PREFIX		= 0x0D,
+	QWORD_PREFIX		= 0x0E,
+	SCOPE_OP		= 0x10,
+	BUFFER_OP		= 0x11,
+	PACKAGE_OP		= 0x12,
+	VARIABLE_PACKAGE_OP	= 0x13,
+	METHOD_OP		= 0x14,
+	EXTERNAL_OP		= 0x15,
+	DUAL_NAME_PREFIX	= 0x2E,
+	MULTI_NAME_PREFIX	= 0x2F,
+	EXT_OP_PREFIX		= 0x5B,
+
+	MUTEX_OP		= 0x01,
+	EVENT_OP		= 0x01,
+	SF_RIGHT_OP		= 0x10,
+	SF_LEFT_OP		= 0x11,
+	COND_REFOF_OP		= 0x12,
+	CREATEFIELD_OP		= 0x13,
+	LOAD_TABLE_OP		= 0x1f,
+	LOAD_OP		= 0x20,
+	STALL_OP		= 0x21,
+	SLEEP_OP		= 0x22,
+	ACQUIRE_OP		= 0x23,
+	SIGNAL_OP		= 0x24,
+	WAIT_OP		= 0x25,
+	RST_OP			= 0x26,
+	RELEASE_OP		= 0x27,
+	FROM_BCD_OP		= 0x28,
+	TO_BCD_OP		= 0x29,
+	UNLOAD_OP		= 0x2A,
+	REVISON_OP		= 0x30,
+	DEBUG_OP		= 0x31,
+	FATAL_OP		= 0x32,
+	TIMER_OP		= 0x33,
+	OPREGION_OP		= 0x80,
+	FIELD_OP		= 0x81,
+	DEVICE_OP		= 0x82,
+	PROCESSOR_OP		= 0x83,
+	POWER_RES_OP		= 0x84,
+	THERMAL_ZONE_OP	= 0x85,
+	INDEX_FIELD_OP		= 0x86,
+	BANK_FIELD_OP		= 0x87,
+	DATA_REGION_OP		= 0x88,
+
+	ROOT_PREFIX		= 0x5C,
+	PARENT_PREFIX		= 0x5D,
+	LOCAL0_OP		= 0x60,
+	LOCAL1_OP		= 0x61,
+	LOCAL2_OP		= 0x62,
+	LOCAL3_OP		= 0x63,
+	LOCAL4_OP		= 0x64,
+	LOCAL5_OP		= 0x65,
+	LOCAL6_OP		= 0x66,
+	LOCAL7_OP		= 0x67,
+	ARG0_OP			= 0x68,
+	ARG1_OP			= 0x69,
+	ARG2_OP			= 0x6A,
+	ARG3_OP			= 0x6B,
+	ARG4_OP			= 0x6C,
+	ARG5_OP			= 0x6D,
+	ARG6_OP			= 0x6E,
+	STORE_OP		= 0x70,
+	REF_OF_OP		= 0x71,
+	ADD_OP			= 0x72,
+	CONCATENATE_OP		= 0x73,
+	SUBTRACT_OP		= 0x74,
+	INCREMENT_OP		= 0x75,
+	DECREMENT_OP		= 0x76,
+	MULTIPLY_OP		= 0x77,
+	DIVIDE_OP		= 0x78,
+	SHIFT_LEFT_OP		= 0x79,
+	SHIFT_RIGHT_OP		= 0x7A,
+	AND_OP			= 0x7B,
+	NAND_OP			= 0x7C,
+	OR_OP			= 0x7D,
+	NOR_OP			= 0x7E,
+	XOR_OP			= 0x7F,
+	NOT_OP			= 0x80,
+	FD_SHIFT_LEFT_BIT_OR	= 0x81,
+	FD_SHIFT_RIGHT_BIT_OR	= 0x82,
+	DEREF_OP		= 0x83,
+	CONCATENATE_TEMP_OP	= 0x84,
+	MOD_OP			= 0x85,
+	NOTIFY_OP		= 0x86,
+	SIZEOF_OP		= 0x87,
+	INDEX_OP		= 0x88,
+	MATCH_OP		= 0x89,
+	CREATE_DWORD_OP		= 0x8A,
+	CREATE_WORD_OP		= 0x8B,
+	CREATE_BYTE_OP		= 0x8C,
+	CREATE_BIT_OP		= 0x8D,
+	OBJ_TYPE_OP		= 0x8E,
+	CREATE_QWORD_OP		= 0x8F,
+	LAND_OP			= 0x90,
+	LOR_OP			= 0x91,
+	LNOT_OP			= 0x92,
+	LEQUAL_OP		= 0x93,
+	LGREATER_OP		= 0x94,
+	LLESS_OP		= 0x95,
+	TO_BUFFER_OP		= 0x96,
+	TO_DEC_STRING_OP	= 0x97,
+	TO_HEX_STRING_OP	= 0x98,
+	TO_INTEGER_OP		= 0x99,
+	TO_STRING_OP		= 0x9C,
+	CP_OBJ_OP		= 0x9D,
+	MID_OP			= 0x9E,
+	CONTINUE_OP		= 0x9F,
+	IF_OP			= 0xA0,
+	ELSE_OP			= 0xA1,
+	WHILE_OP		= 0xA2,
+	NOOP_OP			= 0xA3,
+	RETURN_OP		= 0xA4,
+	BREAK_OP		= 0xA5,
+	COMMENT_OP		= 0xA9,
+	BREAKPIONT_OP		= 0xCC,
+	ONES_OP			= 0xFF,
+};
+
+#define FIELDLIST_OFFSET(_bits)		{ .type = OFFSET, \
+					  .name = "", \
+					  .bits = _bits * 8, \
+					}
+#define FIELDLIST_NAMESTR(_name, _bits)		{ .type = NAME_STRING, \
+					  .name = _name, \
+					  .bits = _bits, \
+					}
+
+#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,
+	GPIO_REGION,
+	GPSERIALBUS,
+	PCC,
+	FIXED_HARDWARE = 0x7F,
+	REGION_SPACE_MAX,
+};
+
+struct opregion {
+	const char *name;
+	enum region_space regionspace;
+	unsigned long regionoffset;
+	unsigned long regionlen;
+};
+
+#define DSM_UUID(DSM_UUID, DSM_CALLBACKS, DSM_COUNT, DSM_ARG) \
+	{ .uuid = DSM_UUID, \
+	.callbacks = DSM_CALLBACKS, \
+	.count = DSM_COUNT, \
+	.arg = DSM_ARG, \
+	}
+
+typedef void (*hid_callback_func)(struct acpi_ctx *ctx, void *arg);
+
+struct dsm_uuid {
+	const char *uuid;
+	hid_callback_func *callbacks;
+	size_t count;
+	void *arg;
+};
+
+/* version 1 has 15 fields, version 2 has 19, and version 3 has 21 */
+enum cppc_fields {
+	CPPC_HIGHEST_PERF, /* can be DWORD */
+	CPPC_NOMINAL_PERF, /* can be DWORD */
+	CPPC_LOWEST_NONL_PERF, /* can be DWORD */
+	CPPC_LOWEST_PERF, /* can be DWORD */
+	CPPC_GUARANTEED_PERF,
+	CPPC_DESIRED_PERF,
+	CPPC_MIN_PERF,
+	CPPC_MAX_PERF,
+	CPPC_PERF_REDUCE_TOLERANCE,
+	CPPC_TIME_WINDOW,
+	CPPC_COUNTER_WRAP, /* can be DWORD */
+	CPPC_REF_PERF_COUNTER,
+	CPPC_DELIVERED_PERF_COUNTER,
+	CPPC_PERF_LIMITED,
+	CPPC_ENABLE, /* can be System I/O */
+	CPPC_MAX_FIELDS_VER_1,
+	CPPC_AUTO_SELECT = /* can be DWORD */
+		CPPC_MAX_FIELDS_VER_1,
+	CPPC_AUTO_ACTIVITY_WINDOW,
+	CPPC_PERF_PREF,
+	CPPC_REF_PERF, /* can be DWORD */
+	CPPC_MAX_FIELDS_VER_2,
+	CPPC_LOWEST_FREQ = /* can be DWORD */
+		CPPC_MAX_FIELDS_VER_2,
+	CPPC_NOMINAL_FREQ, /* can be DWORD */
+	CPPC_MAX_FIELDS_VER_3,
+};
+
+struct cppc_config {
+	u32 version; /* must be 1, 2, or 3 */
+	/*
+	 * The generic struct acpi_gen_regaddr structure is being used, though
+	 * anything besides PPC or FFIXED generally requires checking
+	 * if the OS has advertised support for it (via _OSC).
+	 *
+	 * NOTE: some fields permit DWORDs to be used.  If you
+	 * provide a System Memory register with all zeros (which
+	 * represents unsupported) then this will be used as-is.
+	 * Otherwise, a System Memory register with a 32-bit
+	 * width will be converted into a DWORD field (the value
+	 * of which will be the value of 'addrl'.  Any other use
+	 * of System Memory register is currently undefined.
+	 * (i.e., if you have an actual need for System Memory
+	 * then you'll need to adjust this kludge).
+	 */
+	struct acpi_gen_regaddr regs[CPPC_MAX_FIELDS_VER_3];
+};
+
+void acpigen_write_return_integer(struct acpi_ctx *ctx, u64 arg);
+void acpigen_write_return_string(struct acpi_ctx *ctx, const char *arg);
+void acpigen_write_len_f(struct acpi_ctx *ctx);
+void acpigen_pop_len(struct acpi_ctx *ctx);
+void acpigen_set_current(struct acpi_ctx *ctx, char *curr);
+char *acpigen_get_current(struct acpi_ctx *ctx);
+char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el);
+void acpigen_write_zero(struct acpi_ctx *ctx);
+void acpigen_write_one(struct acpi_ctx *ctx);
+void acpigen_write_ones(struct acpi_ctx *ctx);
+void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data);
+void acpigen_emit_byte(struct acpi_ctx *ctx, unsigned char data);
+void acpigen_emit_ext_op(struct acpi_ctx *ctx, u8 op);
+void acpigen_emit_word(struct acpi_ctx *ctx, unsigned int data);
+void acpigen_emit_dword(struct acpi_ctx *ctx, unsigned int data);
+void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size);
+void acpigen_emit_string(struct acpi_ctx *ctx, const char *string);
+void acpigen_emit_namestring(struct acpi_ctx *ctx, const char *namepath);
+void acpigen_emit_eisaid(struct acpi_ctx *ctx, const char *eisaid);
+void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data);
+void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data);
+void acpigen_write_qword(struct acpi_ctx *ctx, u64 data);
+void acpigen_write_integer(struct acpi_ctx *ctx, u64 data);
+void acpigen_write_string(struct acpi_ctx *ctx, const char *string);
+void acpigen_write_coreboot_hid(struct acpi_ctx *ctx,
+				enum coreboot_acpi_ids id);
+void acpigen_write_name(struct acpi_ctx *ctx, const char *name);
+void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name);
+void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name);
+void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name,
+			       const char *string);
+void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, u32 val);
+void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val);
+void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, u8 val);
+void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name,
+				u64 val);
+void acpigen_write_scope(struct acpi_ctx *ctx, const char *name);
+void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs);
+void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name,
+				     int nargs);
+void acpigen_write_device(struct acpi_ctx *ctx, const char *name);
+void acpigen_write_ppc(struct acpi_ctx *ctx, u8 nr);
+void acpigen_write_ppc_nvs(struct acpi_ctx *ctx);
+void acpigen_write_empty_pct(struct acpi_ctx *ctx);
+void acpigen_write_empty_ptc(struct acpi_ctx *ctx);
+void acpigen_write_prw(struct acpi_ctx *ctx, u32 wake, u32 level);
+void acpigen_write_sta(struct acpi_ctx *ctx, u8 status);
+void acpigen_write_tpc(struct acpi_ctx *ctx, const char *gnvs_tpc_limit);
+void acpigen_write_pss_package(struct acpi_ctx *ctx, u32 corefreq, u32 power,
+			       u32 translat,
+			u32 busmlat, u32 control, u32 status);
+enum psd_coord {
+	SW_ALL = 0xfc,
+	SW_ANY = 0xfd,
+	HW_ALL = 0xfe
+};
+
+void acpigen_write_psd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs,
+			       enum psd_coord coordtype);
+void acpigen_write_cst_package_entry(struct acpi_ctx *ctx,
+				     struct acpi_cstate *cstate);
+void acpigen_write_cst_package(struct acpi_ctx *ctx, struct acpi_cstate *entry,
+			       int nentries);
+
+enum csd_coord {
+	CSD_HW_ALL = 0xfe,
+};
+
+void acpigen_write_CSD_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs,
+			       enum csd_coord coordtype, u32 index);
+void acpigen_write_processor(struct acpi_ctx *ctx, u8 cpuindex, u32 pblock_addr,
+			     u8 pblock_len);
+void acpigen_write_processor_package(struct acpi_ctx *ctx, const char *name,
+				     uint first_core, uint core_count);
+void acpigen_write_processor_cnot(struct acpi_ctx *ctx, const uint num_cores);
+void acpigen_write_tss_package(struct acpi_ctx *ctx, int entries,
+			       struct acpi_tstate *tstate_list);
+void acpigen_write_tsd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs,
+			       enum psd_coord coordtype);
+void acpigen_write_mem32fixed(struct acpi_ctx *ctx, int readwrite, u32 base,
+			      u32 size);
+void acpigen_write_register_resource(struct acpi_ctx *ctx,
+				     const struct acpi_gen_regaddr *addr);
+void acpigen_write_irq(struct acpi_ctx *ctx, u16 mask);
+void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx);
+void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx);
+int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid);
+void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, u8 level,
+			     u16 order, const char *const dev_states[],
+			     size_t dev_states_count);
+void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms);
+void acpigen_write_store(struct acpi_ctx *ctx);
+void acpigen_write_store_ops(struct acpi_ctx *ctx, u8 src, u8 dst);
+void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res);
+void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res);
+void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res);
+void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str);
+void acpigen_write_debug_integer(struct acpi_ctx *ctx, u64 val);
+void acpigen_write_debug_op(struct acpi_ctx *ctx, u8 op);
+void acpigen_write_if(struct acpi_ctx *ctx);
+void acpigen_write_if_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2);
+void acpigen_write_if_lequal_op_int(struct acpi_ctx *ctx, u8 op, u64 val);
+void acpigen_write_else(struct acpi_ctx *ctx);
+void acpigen_write_to_buffer(struct acpi_ctx *ctx, u8 src, u8 dst);
+void acpigen_write_to_integer(struct acpi_ctx *ctx, u8 src, u8 dst);
+void acpigen_write_byte_buffer(struct acpi_ctx *ctx, u8 *arr, size_t size);
+void acpigen_write_return_byte_buffer(struct acpi_ctx *ctx, u8 *arr,
+				      size_t size);
+void acpigen_write_return_singleton_buffer(struct acpi_ctx *ctx, u8 arg);
+void acpigen_write_return_byte(struct acpi_ctx *ctx, u8 arg);
+void acpigen_write_upc(struct acpi_ctx *ctx, enum acpi_upc_type type);
+
+/*
+ * Generate ACPI AML code for _DSM method.
+ * This function takes as input uuid for the device, set of callbacks and
+ * argument to pass into the callbacks. Callbacks should ensure that Local0 and
+ * Local1 are left untouched. Use of Local2-Local7 is permitted in callbacks.
+ */
+int acpigen_write_dsm(struct acpi_ctx *ctx, const char *uuid,
+		      hid_callback_func callbacks[], size_t count, void *arg);
+int acpigen_write_dsm_uuid_arr(struct acpi_ctx *ctx, struct dsm_uuid *ids,
+			       size_t count);
+
+/*
+ * Generate ACPI AML code for _CPC (struct acpi_ctx *ctx, Continuous Perfmance
+ * Control). Execute the package function once to create a global table, then
+ * execute the method function within each processor object to
+ * create a method that points to the global table.
+ */
+int acpigen_write_cppc_package(struct acpi_ctx *ctx,
+			       const struct cppc_config *config);
+void acpigen_write_cppc_method(struct acpi_ctx *ctx);
+
+/*
+ * Generate ACPI AML code for _ROM method.
+ * This function takes as input ROM data and ROM length.
+ * The ROM length has to be multiple of 4096 and has to be less
+ * than the current implementation limit of 0x40000.
+ */
+int acpigen_write_rom(struct acpi_ctx *ctx, void *bios, const size_t length);
+/*
+ * Generate ACPI AML code for OperationRegion
+ * This function takes input region name, region space, region offset & region
+ * length.
+ */
+void acpigen_write_opregion(struct acpi_ctx *ctx, struct opregion *opreg);
+/*
+ * Generate ACPI AML code for Field
+ * This function takes input region name, fieldlist, count & flags.
+ */
+int acpigen_write_field(struct acpi_ctx *ctx, const char *name,
+			struct fieldlist *l, size_t count, uint flags);
+/*
+ * Generate ACPI AML code for IndexField
+ * This function takes input index name, data name, fieldlist, count & flags.
+ */
+int acpigen_write_indexfield(struct acpi_ctx *ctx, const char *idx,
+			     const char *data, struct fieldlist *l,
+			     size_t count, uint flags);
+
+/*
+ * Soc-implemented functions for generating ACPI AML code for GPIO handling. All
+ * these functions are expected to use only Local5, Local6 and Local7
+ * variables. If the functions call into another ACPI method, then there is no
+ * restriction on the use of Local variables. In case of get/read functions,
+ * return value is expected to be stored in Local0 variable.
+ *
+ * All functions return 0 on success and -1 on error.
+ */
+
+/* Generate ACPI AML code to return Rx value of GPIO in Local0. */
+int acpigen_soc_read_rx_gpio(struct acpi_ctx *ctx, uint gpio_num);
+
+/* Generate ACPI AML code to return Tx value of GPIO in Local0. */
+int acpigen_soc_get_tx_gpio(struct acpi_ctx *ctx, uint gpio_num);
+
+/* Generate ACPI AML code to set Tx value of GPIO to 1. */
+int acpigen_soc_set_tx_gpio(struct acpi_ctx *ctx, uint gpio_num);
+
+/* Generate ACPI AML code to set Tx value of GPIO to 0. */
+int acpigen_soc_clear_tx_gpio(struct acpi_ctx *ctx, uint gpio_num);
+
+/*
+ * Helper functions for enabling/disabling Tx GPIOs based on the GPIO
+ * polarity. These functions end up calling acpigen_soc_{set,clear}_tx_gpio to
+ * make callbacks into SoC acpigen code.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int acpigen_enable_tx_gpio(struct acpi_ctx *ctx, struct acpi_gpio *gpio);
+int acpigen_disable_tx_gpio(struct acpi_ctx *ctx, struct acpi_gpio *gpio);
+
+#endif
diff --git a/include/dm/acpi.h b/include/dm/acpi.h
index f3e9d73b78..0f78a506da 100644
--- a/include/dm/acpi.h
+++ b/include/dm/acpi.h
@@ -22,6 +22,9 @@
 /* Length of an ACPI name string including nul terminator */
 #define ACPI_NAME_MAX	5
 
+/* Number of nested objects supported */
+#define ACPIGEN_LENSTACK_SIZE 10
+
 #if !defined(__ACPI__)
 
 /**
@@ -34,12 +37,16 @@
  *	adding a new table. The RSDP holds pointers to the RSDP and XSDT.
  * @rsdt: Pointer to the Root System Description Table
  * @xsdt: Pointer to the Extended System Description Table
+ * @len_stack: Stack of 'length' words to fix up later
+ * @ltop: Points to current top of stack (0 = empty)
  */
 struct acpi_ctx {
 	void *current;
 	struct acpi_rsdp *rsdp;
 	struct acpi_rsdt *rsdt;
 	struct acpi_xsdt *xsdt;
+	char *len_stack[ACPIGEN_LENSTACK_SIZE];
+	int ltop;
 };
 
 /**
diff --git a/include/irq.h b/include/irq.h
index b71afe9bee..d4948e6dc4 100644
--- a/include/irq.h
+++ b/include/irq.h
@@ -8,6 +8,8 @@
 #ifndef __irq_H
 #define __irq_H
 
+struct ofnode_phandle_args;
+
 /*
  * Interrupt controller types available. You can find a particular one with
  * irq_first_device_type()
diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile
index 660491ef71..85a1f774ad 100644
--- a/lib/acpi/Makefile
+++ b/lib/acpi/Makefile
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0+
 #
 
+obj-y += acpigen.o
+obj-y += acpi_device.o
 obj-y += acpi_table.o
diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c
new file mode 100644
index 0000000000..4e7d1ce51e
--- /dev/null
+++ b/lib/acpi/acpigen.c
@@ -0,0 +1,1683 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generation of ACPI (Advanced Configuration and Power Interface) tables
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot
+ */
+
+#define LOG_CATEGORY LOGC_ACPI
+
+#include <common.h>
+#include <dm.h>
+#include <hexdump.h>
+#include <uuid.h>
+#include <acpigen.h>
+#include <acpi_device.h>
+#include <acpi_table.h>
+#include <dm/acpi.h>
+#include <linux/ioport.h>
+
+/*
+ * Maximum length for an ACPI object generated by this code,
+ *
+ * If you need to change this, change acpigen_write_len_f(ctx) and
+ * acpigen_pop_len(ctx)
+ */
+#define ACPIGEN_MAXLEN 0xfffff
+
+/* CPU path format */
+#define ACPI_CPU_STRING "\\_PR.CP%02d"
+
+void acpigen_write_len_f(struct acpi_ctx *ctx)
+{
+	assert(ctx->ltop < (ACPIGEN_LENSTACK_SIZE - 1));
+	ctx->len_stack[ctx->ltop++] = ctx->current;
+	acpigen_emit_byte(ctx, 0);
+	acpigen_emit_byte(ctx, 0);
+	acpigen_emit_byte(ctx, 0);
+}
+
+void acpigen_pop_len(struct acpi_ctx *ctx)
+{
+	int len;
+	char *p;
+
+	assert(ctx->ltop > 0);
+	p = ctx->len_stack[--ctx->ltop];
+	len = ctx->current - (void *)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(struct acpi_ctx *ctx, char *curr)
+{
+	ctx->current = curr;
+}
+
+char *acpigen_get_current(struct acpi_ctx *ctx)
+{
+	return ctx->current;
+}
+
+void acpigen_emit_byte(struct acpi_ctx *ctx, unsigned char b)
+{
+	*(u8 *)ctx->current++ = b;
+}
+
+void acpigen_emit_ext_op(struct acpi_ctx *ctx, u8 op)
+{
+	acpigen_emit_byte(ctx, EXT_OP_PREFIX);
+	acpigen_emit_byte(ctx, op);
+}
+
+void acpigen_emit_word(struct acpi_ctx *ctx, unsigned int data)
+{
+	acpigen_emit_byte(ctx, data & 0xff);
+	acpigen_emit_byte(ctx, (data >> 8) & 0xff);
+}
+
+void acpigen_emit_dword(struct acpi_ctx *ctx, unsigned int data)
+{
+	acpigen_emit_byte(ctx, data & 0xff);
+	acpigen_emit_byte(ctx, (data >> 8) & 0xff);
+	acpigen_emit_byte(ctx, (data >> 16) & 0xff);
+	acpigen_emit_byte(ctx, (data >> 24) & 0xff);
+}
+
+char *acpigen_write_package(struct acpi_ctx *ctx, int nr_el)
+{
+	char *p;
+
+	acpigen_emit_byte(ctx, PACKAGE_OP);
+	acpigen_write_len_f(ctx);
+	p = ctx->current;
+	acpigen_emit_byte(ctx, nr_el);
+
+	return p;
+}
+
+void acpigen_write_byte(struct acpi_ctx *ctx, unsigned int data)
+{
+	acpigen_emit_byte(ctx, BYTE_PREFIX);
+	acpigen_emit_byte(ctx, data & 0xff);
+}
+
+void acpigen_write_word(struct acpi_ctx *ctx, unsigned int data)
+{
+	acpigen_emit_byte(ctx, WORD_PREFIX);
+	acpigen_emit_word(ctx, data);
+}
+
+void acpigen_write_dword(struct acpi_ctx *ctx, unsigned int data)
+{
+	acpigen_emit_byte(ctx, DWORD_PREFIX);
+	acpigen_emit_dword(ctx, data);
+}
+
+void acpigen_write_qword(struct acpi_ctx *ctx, u64 data)
+{
+	acpigen_emit_byte(ctx, QWORD_PREFIX);
+	acpigen_emit_dword(ctx, data & 0xffffffff);
+	acpigen_emit_dword(ctx, (data >> 32) & 0xffffffff);
+}
+
+void acpigen_write_zero(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, ZERO_OP);
+}
+
+void acpigen_write_one(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, ONE_OP);
+}
+
+void acpigen_write_ones(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, ONES_OP);
+}
+
+void acpigen_write_integer(struct acpi_ctx *ctx, u64 data)
+{
+	if (data == 0)
+		acpigen_write_zero(ctx);
+	else if (data == 1)
+		acpigen_write_one(ctx);
+	else if (data <= 0xff)
+		acpigen_write_byte(ctx, (unsigned char)data);
+	else if (data <= 0xffff)
+		acpigen_write_word(ctx, (unsigned int)data);
+	else if (data <= 0xffffffff)
+		acpigen_write_dword(ctx, (unsigned int)data);
+	else
+		acpigen_write_qword(ctx, data);
+}
+
+void acpigen_write_name_zero(struct acpi_ctx *ctx, const char *name)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_one(ctx);
+}
+
+void acpigen_write_name_one(struct acpi_ctx *ctx, const char *name)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_zero(ctx);
+}
+
+void acpigen_write_name_byte(struct acpi_ctx *ctx, const char *name, u8 val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_byte(ctx, val);
+}
+
+void acpigen_write_name_dword(struct acpi_ctx *ctx, const char *name, u32 val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_dword(ctx, val);
+}
+
+void acpigen_write_name_qword(struct acpi_ctx *ctx, const char *name, u64 val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_qword(ctx, val);
+}
+
+void acpigen_write_name_integer(struct acpi_ctx *ctx, const char *name, u64 val)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_integer(ctx, val);
+}
+
+void acpigen_write_name_string(struct acpi_ctx *ctx, const char *name,
+			       const char *string)
+{
+	acpigen_write_name(ctx, name);
+	acpigen_write_string(ctx, string);
+}
+
+void acpigen_emit_stream(struct acpi_ctx *ctx, const char *data, int size)
+{
+	int i;
+
+	for (i = 0; i < size; i++)
+		acpigen_emit_byte(ctx, data[i]);
+}
+
+void acpigen_emit_string(struct acpi_ctx *ctx, const char *string)
+{
+	acpigen_emit_stream(ctx, string, string ? strlen(string) : 0);
+	acpigen_emit_byte(ctx, '\0'); /* NUL */
+}
+
+void acpigen_write_string(struct acpi_ctx *ctx, const char *string)
+{
+	acpigen_emit_byte(ctx, STRING_PREFIX);
+	acpigen_emit_string(ctx, string);
+}
+
+void acpigen_write_coreboot_hid(struct acpi_ctx *ctx, enum coreboot_acpi_ids id)
+{
+	char hid[9]; /* BOOTxxxx */
+
+	snprintf(hid, sizeof(hid), "%.4s%04X", COREBOOT_ACPI_ID, id);
+	acpigen_write_name_string(ctx, "_HID", hid);
+}
+
+/*
+ * 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(struct acpi_ctx *ctx,
+					   const char *name)
+{
+	char ud[] = "____";
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		if ((name[i] == '\0') || (name[i] == '.')) {
+			acpigen_emit_stream(ctx, ud, 4 - i);
+			break;
+		}
+		acpigen_emit_byte(ctx, name[i]);
+	}
+}
+
+static void acpigen_emit_double_namestring(struct acpi_ctx *ctx,
+					   const char *name, int dotpos)
+{
+	acpigen_emit_byte(ctx, DUAL_NAME_PREFIX);
+	acpigen_emit_simple_namestring(ctx, name);
+	acpigen_emit_simple_namestring(ctx, &name[dotpos + 1]);
+}
+
+static void acpigen_emit_multi_namestring(struct acpi_ctx *ctx,
+					  const char *name)
+{
+	int count = 0;
+	unsigned char *pathlen;
+
+	acpigen_emit_byte(ctx, MULTI_NAME_PREFIX);
+	acpigen_emit_byte(ctx, ZERO_OP);
+	pathlen = ((unsigned char *)ctx->current) - 1;
+
+	while (name[0] != '\0') {
+		acpigen_emit_simple_namestring(ctx, 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(struct acpi_ctx *ctx, const char *namepath)
+{
+	int dotcount = 0, i;
+	int dotpos = 0;
+
+	/* We can start with a '\'. */
+	if (namepath[0] == '\\') {
+		acpigen_emit_byte(ctx, '\\');
+		namepath++;
+	}
+
+	/* And there can be any number of '^' */
+	while (namepath[0] == '^') {
+		acpigen_emit_byte(ctx, '^');
+		namepath++;
+	}
+
+	/*
+	 * If we have only \\ or only ^...^ then we need to add a null name
+	 * (0x00)
+	 */
+	if (namepath[0] == '\0') {
+		acpigen_emit_byte(ctx, ZERO_OP);
+		return;
+	}
+
+	i = 0;
+	while (namepath[i] != '\0') {
+		if (namepath[i] == '.') {
+			dotcount++;
+			dotpos = i;
+		}
+		i++;
+	}
+
+	if (dotcount == 0)
+		acpigen_emit_simple_namestring(ctx, namepath);
+	else if (dotcount == 1)
+		acpigen_emit_double_namestring(ctx, namepath, dotpos);
+	else
+		acpigen_emit_multi_namestring(ctx, namepath);
+}
+
+void acpigen_write_name(struct acpi_ctx *ctx, const char *name)
+{
+	acpigen_emit_byte(ctx, NAME_OP);
+	acpigen_emit_namestring(ctx, name);
+}
+
+void acpigen_write_scope(struct acpi_ctx *ctx, const char *name)
+{
+	acpigen_emit_byte(ctx, SCOPE_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_namestring(ctx, name);
+}
+
+void acpigen_write_processor(struct acpi_ctx *ctx, u8 cpuindex, u32 pblock_addr,
+			     u8 pblock_len)
+{
+	/*
+	 * Processor (\_PR.CPcpuindex, cpuindex, pblock_addr, pblock_len)
+	 * {
+	 */
+	char pscope[16];
+
+	acpigen_emit_ext_op(ctx, PROCESSOR_OP);
+	acpigen_write_len_f(ctx);
+
+	snprintf(pscope, sizeof(pscope), ACPI_CPU_STRING, (uint)cpuindex);
+	acpigen_emit_namestring(ctx, pscope);
+	acpigen_emit_byte(ctx, cpuindex);
+	acpigen_emit_dword(ctx, pblock_addr);
+	acpigen_emit_byte(ctx, pblock_len);
+}
+
+void acpigen_write_processor_package(struct acpi_ctx *ctx,
+				     const char *const name,
+				     const uint first_core,
+				     const uint core_count)
+{
+	uint i;
+	char pscope[16];
+
+	acpigen_write_name(ctx, name);
+	acpigen_write_package(ctx, core_count);
+	for (i = first_core; i < first_core + core_count; ++i) {
+		snprintf(pscope, sizeof(pscope), ACPI_CPU_STRING, i);
+		acpigen_emit_namestring(ctx, pscope);
+	}
+	acpigen_pop_len(ctx);
+}
+
+/* Method to notify all CPU cores */
+void acpigen_write_processor_cnot(struct acpi_ctx *ctx, const uint num_cores)
+{
+	int core_id;
+
+	acpigen_write_method(ctx, "\\_PR.CNOT", 1);
+	for (core_id = 0; core_id < num_cores; core_id++) {
+		char buffer[30];
+
+		snprintf(buffer, sizeof(buffer), ACPI_CPU_STRING, core_id);
+		acpigen_emit_byte(ctx, NOTIFY_OP);
+		acpigen_emit_namestring(ctx, buffer);
+		acpigen_emit_byte(ctx, ARG0_OP);
+	}
+	acpigen_pop_len(ctx);
+}
+
+/*
+ * 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 acpi_ctx *ctx, struct opregion *opreg)
+{
+	/* OpregionOp */
+	acpigen_emit_ext_op(ctx, OPREGION_OP);
+	/* NameString 4 chars only */
+	acpigen_emit_simple_namestring(ctx, opreg->name);
+	/* RegionSpace */
+	acpigen_emit_byte(ctx, opreg->regionspace);
+	/* RegionOffset & RegionLen, it can be byte word or double word */
+	acpigen_write_integer(ctx, opreg->regionoffset);
+	acpigen_write_integer(ctx, opreg->regionlen);
+}
+
+static void acpigen_write_field_length(struct acpi_ctx *ctx, u32 len)
+{
+	u8 i, j;
+	u8 emit[4];
+
+	i = 1;
+	if (len < 0x40) {
+		emit[0] = len & 0x3F;
+	} else {
+		emit[0] = len & 0xF;
+		len >>= 4;
+		while (len) {
+			emit[i] = len & 0xFF;
+			i++;
+			len >>= 8;
+		}
+	}
+	/* Update bit 7:6 : Number of bytes followed by emit[0] */
+	emit[0] |= (i - 1) << 6;
+
+	for (j = 0; j < i; j++)
+		acpigen_emit_byte(ctx, emit[j]);
+}
+
+static int acpigen_write_field_offset(struct acpi_ctx *ctx, u32 offset,
+				      u32 current_bit_pos)
+{
+	u32 diff_bits;
+
+	if (offset < current_bit_pos) {
+		log_warning("Cannot move offset backward");
+		return -ESPIPE;
+	}
+
+	diff_bits = offset - current_bit_pos;
+	/* Upper limit */
+	if (diff_bits > 0xFFFFFFF) {
+		log_warning("Offset very large to encode");
+		return E2BIG;
+	}
+
+	acpigen_emit_byte(ctx, 0);
+	acpigen_write_field_length(ctx, diff_bits);
+
+	return 0;
+}
+
+static void acpigen_write_field_name(struct acpi_ctx *ctx, const char *name,
+				     u32 size)
+{
+	acpigen_emit_simple_namestring(ctx, name);
+	acpigen_write_field_length(ctx, size);
+}
+
+/*
+ * 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
+ *	}
+ */
+int acpigen_write_field(struct acpi_ctx *ctx, const char *name,
+			struct fieldlist *l, size_t count, uint flags)
+{
+	int i;
+	u32 current_bit_pos = 0;
+	int ret;
+
+	/* FieldOp */
+	acpigen_emit_ext_op(ctx, FIELD_OP);
+	/* Package Length */
+	acpigen_write_len_f(ctx);
+	/* NameString 4 chars only */
+	acpigen_emit_simple_namestring(ctx, name);
+	/* Field Flag */
+	acpigen_emit_byte(ctx, flags);
+
+	for (i = 0; i < count; i++) {
+		switch (l[i].type) {
+		case NAME_STRING:
+			acpigen_write_field_name(ctx, l[i].name, l[i].bits);
+			current_bit_pos += l[i].bits;
+			break;
+		case OFFSET:
+			ret = acpigen_write_field_offset(ctx, l[i].bits,
+							 current_bit_pos);
+			if (ret)
+				return log_msg_ret("field", ret);
+			current_bit_pos = l[i].bits;
+			break;
+		default:
+			log_err("Invalid field type %#x\n", l[i].type);
+			return -EDOM;
+		}
+	}
+	acpigen_pop_len(ctx);
+
+	return 0;
+}
+
+/*
+ * Generate ACPI AML code for IndexField
+ * 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("IDX", "DATA" l, ARRAY_SIZE(l), FIELD_ANYACC |
+ *						       FIELD_NOLOCK |
+ *						       FIELD_PRESERVE);
+ * Output:
+ * IndexField (IDX, DATA, AnyAcc, NoLock, Preserve)
+ *	{
+ *		Offset (0x84),
+ *		PMCS,   2
+ *	}
+ */
+int acpigen_write_indexfield(struct acpi_ctx *ctx, const char *idx,
+			     const char *data, struct fieldlist *l,
+			     size_t count, uint flags)
+{
+	u16 i;
+	u32 current_bit_pos = 0;
+	int ret;
+
+	/* FieldOp */
+	acpigen_emit_ext_op(ctx, INDEX_FIELD_OP);
+	/* Package Length */
+	acpigen_write_len_f(ctx);
+	/* NameString 4 chars only */
+	acpigen_emit_simple_namestring(ctx, idx);
+	/* NameString 4 chars only */
+	acpigen_emit_simple_namestring(ctx, data);
+	/* Field Flag */
+	acpigen_emit_byte(ctx, flags);
+
+	for (i = 0; i < count; i++) {
+		switch (l[i].type) {
+		case NAME_STRING:
+			acpigen_write_field_name(ctx, l[i].name, l[i].bits);
+			current_bit_pos += l[i].bits;
+			break;
+		case OFFSET:
+			ret = acpigen_write_field_offset(ctx, l[i].bits,
+							 current_bit_pos);
+			if (ret)
+				return log_msg_ret("field", ret);
+			current_bit_pos = l[i].bits;
+			break;
+		default:
+			log_err("Invalid field type 0x%X\n", l[i].type);
+			return -EDOM;
+		}
+	}
+	acpigen_pop_len(ctx);
+
+	return 0;
+}
+
+void acpigen_write_empty_pct(struct acpi_ctx *ctx)
+{
+	/*
+	 * 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[] = {
+		/* 00000030    "0._PCT.," */
+		0x08, 0x5F, 0x50, 0x43, 0x54, 0x12, 0x2C,
+		/* 00000038    "........" */
+		0x02, 0x11, 0x14, 0x0A, 0x11, 0x82, 0x0C, 0x00,
+		/* 00000040    "........" */
+		0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		/* 00000048    "....y..." */
+		0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x11, 0x14,
+		/* 00000050    "........" */
+		0x0A, 0x11, 0x82, 0x0C, 0x00, 0x7F, 0x00, 0x00,
+		/* 00000058    "........" */
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x79, 0x00
+	};
+	acpigen_emit_stream(ctx, stream, ARRAY_SIZE(stream));
+}
+
+void acpigen_write_empty_ptc(struct acpi_ctx *ctx)
+{
+	/*
+	 * 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
+	 *			,)
+	 *	}
+	 * })
+	 */
+	struct acpi_gen_regaddr addr = {
+		.space_id    = ACPI_ADDRESS_SPACE_FIXED,
+		.bit_width   = 0,
+		.bit_offset  = 0,
+		.access_size = 0,
+		.addrl       = 0,
+		.addrh       = 0,
+	};
+
+	acpigen_write_name(ctx, "_PTC");
+	acpigen_write_package(ctx, 2);
+
+	/* ControlRegister */
+	acpigen_write_register_resource(ctx, &addr);
+
+	/* StatusRegister */
+	acpigen_write_register_resource(ctx, &addr);
+
+	acpigen_pop_len(ctx);
+}
+
+static void __acpigen_write_method(struct acpi_ctx *ctx, const char *name,
+				   u8 flags)
+{
+	acpigen_emit_byte(ctx, METHOD_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_namestring(ctx, name);
+	acpigen_emit_byte(ctx, flags);
+}
+
+/* Method (name, nargs, NotSerialized) */
+void acpigen_write_method(struct acpi_ctx *ctx, const char *name, int nargs)
+{
+	__acpigen_write_method(ctx, name, (nargs & 7));
+}
+
+/* Method (name, nargs, Serialized) */
+void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name,
+				     int nargs)
+{
+	__acpigen_write_method(ctx, name, (nargs & 7) | (1 << 3));
+}
+
+void acpigen_write_device(struct acpi_ctx *ctx, const char *name)
+{
+	acpigen_emit_ext_op(ctx, DEVICE_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_namestring(ctx, name);
+}
+
+void acpigen_write_sta(struct acpi_ctx *ctx, u8 status)
+{
+	/* Method (_STA, 0, NotSerialized) { Return (status) } */
+	acpigen_write_method(ctx, "_STA", 0);
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_write_byte(ctx, status);
+	acpigen_pop_len(ctx);
+}
+
+/*
+ * Generates a func with max supported P-states.
+ */
+void acpigen_write_ppc(struct acpi_ctx *ctx, u8 nr)
+{
+	/*
+	 * Method (_PPC, 0, NotSerialized)
+	 * {
+	 *	Return (nr)
+	 * }
+	 */
+	acpigen_write_method(ctx, "_PPC", 0);
+	acpigen_emit_byte(ctx, RETURN_OP);
+	/* arg */
+	acpigen_write_byte(ctx, nr);
+	acpigen_pop_len(ctx);
+}
+
+/*
+ * Generates a func with max supported P-states saved
+ * in the variable PPCM.
+ */
+void acpigen_write_ppc_nvs(struct acpi_ctx *ctx)
+{
+	/*
+	 * Method (_PPC, 0, NotSerialized)
+	 * {
+	 *	Return (PPCM)
+	 * }
+	 */
+	acpigen_write_method(ctx, "_PPC", 0);
+	acpigen_emit_byte(ctx, RETURN_OP);
+	/* arg */
+	acpigen_emit_namestring(ctx, "PPCM");
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_tpc(struct acpi_ctx *ctx, const char *gnvs_tpc_limit)
+{
+	/*
+	 * // Sample _TPC method
+	 * Method (_TPC, 0, NotSerialized)
+	 * {
+	 *	Return (\TLVL)
+	 * }
+	 */
+	acpigen_write_method(ctx, "_TPC", 0);
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_emit_namestring(ctx, gnvs_tpc_limit);
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_prw(struct acpi_ctx *ctx, u32 wake, u32 level)
+{
+	/*
+	 * Name (_PRW, Package () { wake, level }
+	 */
+	acpigen_write_name(ctx, "_PRW");
+	acpigen_write_package(ctx, 2);
+	acpigen_write_integer(ctx, wake);
+	acpigen_write_integer(ctx, level);
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_pss_package(struct acpi_ctx *ctx, u32 core_freq, u32 power,
+			       u32 trans_lat, u32 busm_lat, u32 control,
+			       u32 status)
+{
+	acpigen_write_package(ctx, 6);
+	acpigen_write_dword(ctx, core_freq);
+	acpigen_write_dword(ctx, power);
+	acpigen_write_dword(ctx, trans_lat);
+	acpigen_write_dword(ctx, busm_lat);
+	acpigen_write_dword(ctx, control);
+	acpigen_write_dword(ctx, status);
+	acpigen_pop_len(ctx);
+
+	log_debug("PSS: %uMHz power %u control 0x%x status 0x%x\n",
+		  core_freq, power, control, status);
+}
+
+void acpigen_write_psd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs,
+			       enum psd_coord coordtype)
+{
+	acpigen_write_name(ctx, "_PSD");
+	acpigen_write_package(ctx, 1);
+	acpigen_write_package(ctx, 5);
+	acpigen_write_byte(ctx, 5);	// 5 values
+	acpigen_write_byte(ctx, 0);	// revision 0
+	acpigen_write_dword(ctx, domain);
+	acpigen_write_dword(ctx, coordtype);
+	acpigen_write_dword(ctx, numprocs);
+	acpigen_pop_len(ctx);
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_cst_package_entry(struct acpi_ctx *ctx,
+				     struct acpi_cstate *cstate)
+{
+	acpigen_write_package(ctx, 4);
+	acpigen_write_register_resource(ctx, &cstate->resource);
+	acpigen_write_dword(ctx, cstate->ctype);
+	acpigen_write_dword(ctx, cstate->latency);
+	acpigen_write_dword(ctx, cstate->power);
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_cst_package(struct acpi_ctx *ctx, struct acpi_cstate *cstate,
+			       int nentries)
+{
+	int i;
+
+	acpigen_write_name(ctx, "_CST");
+	acpigen_write_package(ctx, nentries + 1);
+	acpigen_write_dword(ctx, nentries);
+
+	for (i = 0; i < nentries; i++)
+		acpigen_write_cst_package_entry(ctx, cstate + i);
+
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_csd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs,
+			       enum csd_coord coordtype, u32 index)
+{
+	acpigen_write_name(ctx, "_CSD");
+	acpigen_write_package(ctx, 1);
+	acpigen_write_package(ctx, 6);
+	acpigen_write_byte(ctx, 6);	// 6 values
+	acpigen_write_byte(ctx, 0);	// revision 0
+	acpigen_write_dword(ctx, domain);
+	acpigen_write_dword(ctx, coordtype);
+	acpigen_write_dword(ctx, numprocs);
+	acpigen_write_dword(ctx, index);
+	acpigen_pop_len(ctx);
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_tss_package(struct acpi_ctx *ctx, int entries,
+			       struct acpi_tstate *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;
+	struct acpi_tstate *tstate = tstate_list;
+
+	acpigen_write_name(ctx, "_TSS");
+	acpigen_write_package(ctx, entries);
+
+	for (i = 0; i < entries; i++) {
+		acpigen_write_package(ctx, 5);
+		acpigen_write_dword(ctx, tstate->percent);
+		acpigen_write_dword(ctx, tstate->power);
+		acpigen_write_dword(ctx, tstate->latency);
+		acpigen_write_dword(ctx, tstate->control);
+		acpigen_write_dword(ctx, tstate->status);
+		acpigen_pop_len(ctx);
+		tstate++;
+	}
+
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_tsd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs,
+			       enum psd_coord coordtype)
+{
+	acpigen_write_name(ctx, "_TSD");
+	acpigen_write_package(ctx, 1);
+	acpigen_write_package(ctx, 5);
+	acpigen_write_byte(ctx, 5);	// 5 values
+	acpigen_write_byte(ctx, 0);	// revision 0
+	acpigen_write_dword(ctx, domain);
+	acpigen_write_dword(ctx, coordtype);
+	acpigen_write_dword(ctx, numprocs);
+	acpigen_pop_len(ctx);
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_mem32fixed(struct acpi_ctx *ctx, 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(ctx, 0x86);
+	/* Byte 1+2: length (0x0009) */
+	acpigen_emit_byte(ctx, 0x09);
+	acpigen_emit_byte(ctx, 0x00);
+	/* bit1-7 are ignored */
+	acpigen_emit_byte(ctx, readwrite ? 0x01 : 0x00);
+	acpigen_emit_dword(ctx, base);
+	acpigen_emit_dword(ctx, size);
+}
+
+static void acpigen_write_register(struct acpi_ctx *ctx,
+				   const struct acpi_gen_regaddr *addr)
+{
+	acpigen_emit_byte(ctx, 0x82);		/* Register Descriptor */
+	acpigen_emit_byte(ctx, 0x0c);		/* Register Length 7:0 */
+	acpigen_emit_byte(ctx, 0x00);		/* Register Length 15:8 */
+	acpigen_emit_byte(ctx, addr->space_id);
+	acpigen_emit_byte(ctx, addr->bit_width);
+	acpigen_emit_byte(ctx, addr->bit_offset);
+	acpigen_emit_byte(ctx, addr->access_size);
+	acpigen_emit_dword(ctx, addr->addrl);
+	acpigen_emit_dword(ctx, addr->addrh);
+}
+
+void acpigen_write_register_resource(struct acpi_ctx *ctx,
+				     const struct acpi_gen_regaddr *addr)
+{
+	acpigen_write_resourcetemplate_header(ctx);
+	acpigen_write_register(ctx, addr);
+	acpigen_write_resourcetemplate_footer(ctx);
+}
+
+void acpigen_write_irq(struct acpi_ctx *ctx, 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(ctx, 0x22);
+	acpigen_emit_byte(ctx, mask & 0xff);
+	acpigen_emit_byte(ctx, (mask >> 8) & 0xff);
+}
+
+void acpigen_write_resourcetemplate_header(struct acpi_ctx *ctx)
+{
+	/*
+	 * 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(ctx, BUFFER_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_byte(ctx, WORD_PREFIX);
+	ctx->len_stack[ctx->ltop++] = ctx->current;
+	/*
+	 * Add two dummy bytes for the ACPI word (keep aligned with the
+	 * calclulation in acpigen_write_resourcetemplate() below)
+	 */
+	acpigen_emit_byte(ctx, 0x00);
+	acpigen_emit_byte(ctx, 0x00);
+}
+
+void acpigen_write_resourcetemplate_footer(struct acpi_ctx *ctx)
+{
+	char *p = ctx->len_stack[--ctx->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(ctx, 0x79);
+	acpigen_emit_byte(ctx, 0x00);
+
+	/*
+	 * Start counting past the 2-bytes length added in
+	 * acpigen_write_resourcetemplate() above
+	 */
+	len = (char *)ctx->current - (p + 2);
+
+	/* patch len word */
+	p[0] = len & 0xff;
+	p[1] = (len >> 8) & 0xff;
+	/* patch len field */
+	acpigen_pop_len(ctx);
+}
+
+/*
+ * ToUUID(uuid)
+ *
+ * ACPI 6.1 Section 19.6.136 table 19-385 defines a special output
+ * order for the bytes that make up a UUID Buffer object.
+ * UUID byte order for input:
+ *   aabbccdd-eeff-gghh-iijj-kkllmmnnoopp
+ * UUID byte order for output:
+ *   ddccbbaa-ffee-hhgg-iijj-kkllmmnnoopp
+ */
+#define UUID_LEN 16
+int acpigen_write_uuid(struct acpi_ctx *ctx, const char *uuid)
+{
+	u8 buf[UUID_LEN];
+	size_t i, order[UUID_LEN] = { 3, 2, 1, 0, 5, 4, 7, 6,
+				      8, 9, 10, 11, 12, 13, 14, 15 };
+	int ret;
+
+	/* Parse UUID string into bytes */
+	ret = uuid_str_to_bin(uuid, buf, UUID_STR_FORMAT_STD);
+	if (ret)
+		return log_msg_ret("bad hex", -EINVAL);
+
+	/* BufferOp */
+	acpigen_emit_byte(ctx, BUFFER_OP);
+	acpigen_write_len_f(ctx);
+
+	/* Buffer length in bytes */
+	acpigen_write_word(ctx, UUID_LEN);
+
+	/* Output UUID in expected order */
+	for (i = 0; i < UUID_LEN; i++)
+		acpigen_emit_byte(ctx, buf[order[i]]);
+
+	acpigen_pop_len(ctx);
+
+	return 0;
+}
+
+/*
+ * Name (_PRx, Package(One) { name })
+ * ...
+ * PowerResource (name, level, order)
+ */
+void acpigen_write_power_res(struct acpi_ctx *ctx, const char *name, u8 level,
+			     u16 order, const char *const dev_states[],
+			     size_t dev_states_count)
+{
+	size_t i;
+
+	for (i = 0; i < dev_states_count; i++) {
+		acpigen_write_name(ctx, dev_states[i]);
+		acpigen_write_package(ctx, 1);
+		acpigen_emit_simple_namestring(ctx, name);
+		acpigen_pop_len(ctx);		/* Package */
+	}
+
+	acpigen_emit_ext_op(ctx, POWER_RES_OP);
+
+	acpigen_write_len_f(ctx);
+
+	acpigen_emit_simple_namestring(ctx, name);
+	acpigen_emit_byte(ctx, level);
+	acpigen_emit_word(ctx, order);
+}
+
+/* Sleep (ms) */
+void acpigen_write_sleep(struct acpi_ctx *ctx, u64 sleep_ms)
+{
+	acpigen_emit_ext_op(ctx, SLEEP_OP);
+	acpigen_write_integer(ctx, sleep_ms);
+}
+
+void acpigen_write_store(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, STORE_OP);
+}
+
+/* Store (src, dst) */
+void acpigen_write_store_ops(struct acpi_ctx *ctx, u8 src, u8 dst)
+{
+	acpigen_write_store(ctx);
+	acpigen_emit_byte(ctx, src);
+	acpigen_emit_byte(ctx, dst);
+}
+
+/* Or (arg1, arg2, res) */
+void acpigen_write_or(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res)
+{
+	acpigen_emit_byte(ctx, OR_OP);
+	acpigen_emit_byte(ctx, arg1);
+	acpigen_emit_byte(ctx, arg2);
+	acpigen_emit_byte(ctx, res);
+}
+
+/* And (arg1, arg2, res) */
+void acpigen_write_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2, u8 res)
+{
+	acpigen_emit_byte(ctx, AND_OP);
+	acpigen_emit_byte(ctx, arg1);
+	acpigen_emit_byte(ctx, arg2);
+	acpigen_emit_byte(ctx, res);
+}
+
+/* Not (arg, res) */
+void acpigen_write_not(struct acpi_ctx *ctx, u8 arg, u8 res)
+{
+	acpigen_emit_byte(ctx, NOT_OP);
+	acpigen_emit_byte(ctx, arg);
+	acpigen_emit_byte(ctx, res);
+}
+
+/* Store (str, DEBUG) */
+void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str)
+{
+	acpigen_write_store(ctx);
+	acpigen_write_string(ctx, str);
+	acpigen_emit_ext_op(ctx, DEBUG_OP);
+}
+
+/* Store (val, DEBUG) */
+void acpigen_write_debug_integer(struct acpi_ctx *ctx, u64 val)
+{
+	acpigen_write_store(ctx);
+	acpigen_write_integer(ctx, val);
+	acpigen_emit_ext_op(ctx, DEBUG_OP);
+}
+
+/* Store (op, DEBUG) */
+void acpigen_write_debug_op(struct acpi_ctx *ctx, u8 op)
+{
+	acpigen_write_store(ctx);
+	acpigen_emit_byte(ctx, op);
+	acpigen_emit_ext_op(ctx, DEBUG_OP);
+}
+
+void acpigen_write_if(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, IF_OP);
+	acpigen_write_len_f(ctx);
+}
+
+/* If (And (arg1, arg2)) */
+void acpigen_write_if_and(struct acpi_ctx *ctx, u8 arg1, u8 arg2)
+{
+	acpigen_write_if(ctx);
+	acpigen_emit_byte(ctx, AND_OP);
+	acpigen_emit_byte(ctx, arg1);
+	acpigen_emit_byte(ctx, arg2);
+}
+
+/*
+ * Generates ACPI code for checking if operand1 and operand2 are equal, where,
+ * operand1 is ACPI op and operand2 is an integer.
+ *
+ * If (Lequal (op, val))
+ */
+void acpigen_write_if_lequal_op_int(struct acpi_ctx *ctx, u8 op, u64 val)
+{
+	acpigen_write_if(ctx);
+	acpigen_emit_byte(ctx, LEQUAL_OP);
+	acpigen_emit_byte(ctx, op);
+	acpigen_write_integer(ctx, val);
+}
+
+void acpigen_write_else(struct acpi_ctx *ctx)
+{
+	acpigen_emit_byte(ctx, ELSE_OP);
+	acpigen_write_len_f(ctx);
+}
+
+void acpigen_write_to_buffer(struct acpi_ctx *ctx, u8 src, u8 dst)
+{
+	acpigen_emit_byte(ctx, TO_BUFFER_OP);
+	acpigen_emit_byte(ctx, src);
+	acpigen_emit_byte(ctx, dst);
+}
+
+void acpigen_write_to_integer(struct acpi_ctx *ctx, u8 src, u8 dst)
+{
+	acpigen_emit_byte(ctx, TO_INTEGER_OP);
+	acpigen_emit_byte(ctx, src);
+	acpigen_emit_byte(ctx, dst);
+}
+
+void acpigen_write_byte_buffer(struct acpi_ctx *ctx, u8 *arr, size_t size)
+{
+	size_t i;
+
+	acpigen_emit_byte(ctx, BUFFER_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_write_integer(ctx, size);
+
+	for (i = 0; i < size; i++)
+		acpigen_emit_byte(ctx, arr[i]);
+
+	acpigen_pop_len(ctx);
+}
+
+void acpigen_write_return_byte_buffer(struct acpi_ctx *ctx, u8 *arr,
+				      size_t size)
+{
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_write_byte_buffer(ctx, arr, size);
+}
+
+void acpigen_write_return_singleton_buffer(struct acpi_ctx *ctx, u8 arg)
+{
+	acpigen_write_return_byte_buffer(ctx, &arg, 1);
+}
+
+void acpigen_write_return_byte(struct acpi_ctx *ctx, u8 arg)
+{
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_write_byte(ctx, arg);
+}
+
+void acpigen_write_return_integer(struct acpi_ctx *ctx, u64 arg)
+{
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_write_integer(ctx, arg);
+}
+
+void acpigen_write_return_string(struct acpi_ctx *ctx, const char *arg)
+{
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_write_string(ctx, arg);
+}
+
+void acpigen_write_upc(struct acpi_ctx *ctx, enum acpi_upc_type type)
+{
+	acpigen_write_name(ctx, "_UPC");
+	acpigen_write_package(ctx, 4);
+	/* Connectable */
+	acpigen_write_byte(ctx, type == UPC_TYPE_UNUSED ? 0 : 0xff);
+	/* Type */
+	acpigen_write_byte(ctx, type);
+	/* Reserved0 */
+	acpigen_write_zero(ctx);
+	/* Reserved1 */
+	acpigen_write_zero(ctx);
+	acpigen_pop_len(ctx);
+}
+
+int acpigen_write_dsm(struct acpi_ctx *ctx, const char *uuid,
+		      hid_callback_func callbacks[], size_t count, void *arg)
+{
+	struct dsm_uuid id = DSM_UUID(uuid, callbacks, count, arg);
+	int ret;
+
+	ret = acpigen_write_dsm_uuid_arr(ctx, &id, 1);
+	if (ret)
+		return log_msg_ret("uuid_arr", ret);
+
+	return 0;
+}
+
+static int acpigen_write_dsm_uuid(struct acpi_ctx *ctx, struct dsm_uuid *id)
+{
+	size_t i;
+	int ret;
+
+	/* If (LEqual (Local0, ToUUID(uuid))) */
+	acpigen_write_if(ctx);
+	acpigen_emit_byte(ctx, LEQUAL_OP);
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+	ret = acpigen_write_uuid(ctx, id->uuid);
+	if (ret)
+		return log_msg_ret("uuid", ret);
+
+	/* ToInteger (Arg2, Local1) */
+	acpigen_write_to_integer(ctx, ARG2_OP, LOCAL1_OP);
+
+	for (i = 0; i < id->count; i++) {
+		/* If (LEqual (Local1, i)) */
+		acpigen_write_if_lequal_op_int(ctx, LOCAL1_OP, i);
+
+		/* Callback to write if handler. */
+		if (id->callbacks[i])
+			id->callbacks[i](ctx, id->arg);
+
+		acpigen_pop_len(ctx);	/* If */
+	}
+
+	/* Default case: Return (Buffer (One) { 0x0 }) */
+	acpigen_write_return_singleton_buffer(ctx, 0x0);
+
+	acpigen_pop_len(ctx);	/* If (LEqual (Local0, ToUUID(uuid))) */
+
+	return 0;
+}
+
+/*
+ * Generate ACPI AML code for _DSM method.
+ * This function takes as input array of uuid for the device, set of callbacks
+ * and argument to pass into the callbacks. Callbacks should ensure that Local0
+ * and Local1 are left untouched. Use of Local2-Local7 is permitted in
+ * callbacks.
+ *
+ * Arguments passed into _DSM method:
+ * Arg0 = UUID
+ * Arg1 = Revision
+ * Arg2 = Function index
+ * Arg3 = Function specific arguments
+ *
+ * AML code generated would look like:
+ * Method (_DSM, 4, Serialized) {
+ *	ToBuffer (Arg0, Local0)
+ *	If (LEqual (Local0, ToUUID(uuid))) {
+ *		ToInteger (Arg2, Local1)
+ *		If (LEqual (Local1, 0)) {
+ *			<acpigen by callback[0]>
+ *		}
+ *		...
+ *		If (LEqual (Local1, n)) {
+ *			<acpigen by callback[n]>
+ *		}
+ *		Return (Buffer (One) { 0x0 })
+ *	}
+ *	...
+ *	If (LEqual (Local0, ToUUID(uuidn))) {
+ *	...
+ *	}
+ *	Return (Buffer (One) { 0x0 })
+ * }
+ */
+int acpigen_write_dsm_uuid_arr(struct acpi_ctx *ctx, struct dsm_uuid *ids,
+			       size_t count)
+{
+	size_t i;
+	int ret;
+
+	/* Method (_DSM, 4, Serialized) */
+	acpigen_write_method_serialized(ctx, "_DSM", 0x4);
+
+	/* ToBuffer (Arg0, Local0) */
+	acpigen_write_to_buffer(ctx, ARG0_OP, LOCAL0_OP);
+
+	for (i = 0; i < count; i++) {
+		ret = acpigen_write_dsm_uuid(ctx, &ids[i]);
+		if (ret)
+			return ret;
+	}
+
+	/* Return (Buffer (One) { 0x0 }) */
+	acpigen_write_return_singleton_buffer(ctx, 0x0);
+
+	acpigen_pop_len(ctx);	/* Method _DSM */
+
+	return 0;
+}
+
+#define CPPC_PACKAGE_NAME "\\GCPC"
+
+int acpigen_write_cppc_package(struct acpi_ctx *ctx,
+			       const struct cppc_config *config)
+{
+	u32 i;
+	u32 max;
+
+	switch (config->version) {
+	case 1:
+		max = CPPC_MAX_FIELDS_VER_1;
+		break;
+	case 2:
+		max = CPPC_MAX_FIELDS_VER_2;
+		break;
+	case 3:
+		max = CPPC_MAX_FIELDS_VER_3;
+		break;
+	default:
+		log_err("CPPC version %u is not implemented\n",
+			config->version);
+		return -EDOM;
+	}
+	acpigen_write_name(ctx, CPPC_PACKAGE_NAME);
+
+	/* Adding 2 to account for length and version fields */
+	acpigen_write_package(ctx, max + 2);
+	acpigen_write_dword(ctx, max + 2);
+
+	acpigen_write_byte(ctx, config->version);
+
+	for (i = 0; i < max; ++i) {
+		const struct acpi_gen_regaddr *reg = &config->regs[i];
+
+		if (reg->space_id == ACPI_ADDRESS_SPACE_MEMORY &&
+		    reg->bit_width == 32 && reg->access_size == 0) {
+			acpigen_write_dword(ctx, reg->addrl);
+		} else {
+			acpigen_write_register_resource(ctx, reg);
+		}
+	}
+	acpigen_pop_len(ctx);
+
+	return 0;
+}
+
+void acpigen_write_cppc_method(struct acpi_ctx *ctx)
+{
+	acpigen_write_method(ctx, "_CPC", 0);
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_emit_namestring(ctx, CPPC_PACKAGE_NAME);
+	acpigen_pop_len(ctx);
+}
+
+/*
+ * Generate ACPI AML code for _ROM method.
+ * This function takes as input ROM data and ROM length.
+ *
+ * The ACPI spec isn't clear about what should happen at the end of the
+ * ROM. Tests showed that it shouldn't truncate, but fill the remaining
+ * bytes in the returned buffer with zeros.
+ *
+ * Arguments passed into _DSM method:
+ * Arg0 = Offset in Bytes
+ * Arg1 = Bytes to return
+ *
+ * Example:
+ *   acpigen_write_rom(0xdeadbeef, 0x10000)
+ *
+ * AML code generated would look like:
+ * Method (_ROM, 2, NotSerialized) {
+ *
+ *	OperationRegion("ROMS", SYSTEMMEMORY, 0xdeadbeef, 0x10000)
+ *	Field (ROMS, AnyAcc, NoLock, Preserve)
+ *	{
+ *		Offset (0),
+ *		RBF0,   0x80000
+ *	}
+ *
+ *	Store (Arg0, Local0)
+ *	Store (Arg1, Local1)
+ *
+ *	If (LGreater (Local1, 0x1000))
+ *	{
+ *		Store (0x1000, Local1)
+ *	}
+ *
+ *	Store (Local1, Local3)
+ *
+ *	If (LGreater (Local0, 0x10000))
+ *	{
+ *		Return(Buffer(Local1){0})
+ *	}
+ *
+ *	If (LGreater (Local0, 0x0f000))
+ *	{
+ *		Subtract (0x10000, Local0, Local2)
+ *		If (LGreater (Local1, Local2))
+ *		{
+ *			Store (Local2, Local1)
+ *		}
+ *	}
+ *
+ *	Name (ROM1, Buffer (Local3) {0})
+ *
+ *	Multiply (Local0, 0x08, Local0)
+ *	Multiply (Local1, 0x08, Local1)
+ *
+ *	CreateField (RBF0, Local0, Local1, TMPB)
+ *	Store (TMPB, ROM1)
+ *	Return (ROM1)
+ * }
+ */
+int acpigen_write_rom(struct acpi_ctx *ctx, void *bios, const size_t length)
+{
+	int ret;
+
+	assert(bios);
+	assert(length);
+
+	/* Method (_ROM, 2, Serialized) */
+	acpigen_write_method_serialized(ctx, "_ROM", 2);
+
+	/* OperationRegion("ROMS", SYSTEMMEMORY, current, length) */
+	struct opregion opreg = OPREGION("ROMS", SYSTEMMEMORY,
+			(uintptr_t)bios, length);
+	acpigen_write_opregion(ctx, &opreg);
+
+	struct fieldlist l[] = {
+		FIELDLIST_OFFSET(0),
+		FIELDLIST_NAMESTR("RBF0", 8 * length),
+	};
+
+	/*
+	 * Field (ROMS, AnyAcc, NoLock, Preserve)
+	 * {
+	 *  Offset (0),
+	 *  RBF0,   0x80000
+	 * }
+	 */
+	ret = acpigen_write_field(ctx, opreg.name, l, 2, FIELD_ANYACC |
+				  FIELD_NOLOCK | FIELD_PRESERVE);
+	if (ret)
+		return log_msg_ret("field", ret);
+
+	/* Store (Arg0, Local0) */
+	acpigen_write_store(ctx);
+	acpigen_emit_byte(ctx, ARG0_OP);
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+
+	/* Store (Arg1, Local1) */
+	acpigen_write_store(ctx);
+	acpigen_emit_byte(ctx, ARG1_OP);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+
+	/* ACPI SPEC requires to return at maximum 4KiB */
+	/* If (LGreater (Local1, 0x1000)) */
+	acpigen_write_if(ctx);
+	acpigen_emit_byte(ctx, LGREATER_OP);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+	acpigen_write_integer(ctx, 0x1000);
+
+	/* Store (0x1000, Local1) */
+	acpigen_write_store(ctx);
+	acpigen_write_integer(ctx, 0x1000);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+
+	/* Pop if */
+	acpigen_pop_len(ctx);
+
+	/* Store (Local1, Local3) */
+	acpigen_write_store(ctx);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+	acpigen_emit_byte(ctx, LOCAL3_OP);
+
+	/* If (LGreater (Local0, length)) */
+	acpigen_write_if(ctx);
+	acpigen_emit_byte(ctx, LGREATER_OP);
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+	acpigen_write_integer(ctx, length);
+
+	/* Return(Buffer(Local1){0}) */
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_emit_byte(ctx, BUFFER_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+	acpigen_emit_byte(ctx, 0);
+	acpigen_pop_len(ctx);
+
+	/* Pop if */
+	acpigen_pop_len(ctx);
+
+	/* If (LGreater (Local0, length - 4096)) */
+	acpigen_write_if(ctx);
+	acpigen_emit_byte(ctx, LGREATER_OP);
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+	acpigen_write_integer(ctx, length - 4096);
+
+	/* Subtract (length, Local0, Local2) */
+	acpigen_emit_byte(ctx, SUBTRACT_OP);
+	acpigen_write_integer(ctx, length);
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+	acpigen_emit_byte(ctx, LOCAL2_OP);
+
+	/* If (LGreater (Local1, Local2)) */
+	acpigen_write_if(ctx);
+	acpigen_emit_byte(ctx, LGREATER_OP);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+	acpigen_emit_byte(ctx, LOCAL2_OP);
+
+	/* Store (Local2, Local1) */
+	acpigen_write_store(ctx);
+	acpigen_emit_byte(ctx, LOCAL2_OP);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+
+	/* Pop if */
+	acpigen_pop_len(ctx);
+
+	/* Pop if */
+	acpigen_pop_len(ctx);
+
+	/* Name (ROM1, Buffer (Local3) {0}) */
+	acpigen_write_name(ctx, "ROM1");
+	acpigen_emit_byte(ctx, BUFFER_OP);
+	acpigen_write_len_f(ctx);
+	acpigen_emit_byte(ctx, LOCAL3_OP);
+	acpigen_emit_byte(ctx, 0);
+	acpigen_pop_len(ctx);
+
+	/* Multiply (Local1, 0x08, Local1) */
+	acpigen_emit_byte(ctx, MULTIPLY_OP);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+	acpigen_write_integer(ctx, 0x08);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+
+	/* Multiply (Local0, 0x08, Local0) */
+	acpigen_emit_byte(ctx, MULTIPLY_OP);
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+	acpigen_write_integer(ctx, 0x08);
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+
+	/* CreateField (RBF0, Local0, Local1, TMPB) */
+	acpigen_emit_ext_op(ctx, CREATEFIELD_OP);
+	acpigen_emit_namestring(ctx, "RBF0");
+	acpigen_emit_byte(ctx, LOCAL0_OP);
+	acpigen_emit_byte(ctx, LOCAL1_OP);
+	acpigen_emit_namestring(ctx, "TMPB");
+
+	/* Store (TMPB, ROM1) */
+	acpigen_write_store(ctx);
+	acpigen_emit_namestring(ctx, "TMPB");
+	acpigen_emit_namestring(ctx, "ROM1");
+
+	/* Return (ROM1) */
+	acpigen_emit_byte(ctx, RETURN_OP);
+	acpigen_emit_namestring(ctx, "ROM1");
+
+	/* Pop method */
+	acpigen_pop_len(ctx);
+
+	return 0;
+}
+
+/* Soc-implemented functions -- weak definitions. */
+int __weak acpigen_soc_read_rx_gpio(struct acpi_ctx *ctx, unsigned int gpio_num)
+{
+	log_err("not implemented\n");
+	acpigen_write_debug_string(ctx, "read_rx_gpio not available");
+
+	return -ENOTSUPP;
+}
+
+int __weak acpigen_soc_get_tx_gpio(struct acpi_ctx *ctx, unsigned int gpio_num)
+{
+	log_err("not implemented\n");
+	acpigen_write_debug_string(ctx, "get_tx_gpio not available");
+
+	return -ENOTSUPP;
+}
+
+int __weak acpigen_soc_set_tx_gpio(struct acpi_ctx *ctx, unsigned int gpio_num)
+{
+	log_err("not implemented\n");
+	acpigen_write_debug_string(ctx, "set_tx_gpio not available");
+
+	return -ENOTSUPP;
+}
+
+int __weak acpigen_soc_clear_tx_gpio(struct acpi_ctx *ctx,
+				     unsigned int gpio_num)
+{
+	log_err("not implemented\n");
+	acpigen_write_debug_string(ctx, "clear_tx_gpio not available");
+
+	return -ENOTSUPP;
+}
+
+/*
+ * Helper functions for enabling/disabling Tx GPIOs based on the GPIO
+ * polarity. These functions end up calling acpigen_soc_{set,clear}_tx_gpio to
+ * make callbacks into SoC acpigen code.
+ *
+ * Returns 0 on success and -1 on error.
+ */
+int acpigen_enable_tx_gpio(struct acpi_ctx *ctx, struct acpi_gpio *gpio)
+{
+	int ret;
+
+	if (gpio->polarity == ACPI_GPIO_ACTIVE_HIGH)
+		ret = acpigen_soc_set_tx_gpio(ctx, gpio->pins[0]);
+	else
+		ret = acpigen_soc_clear_tx_gpio(ctx, gpio->pins[0]);
+	if (ret)
+		return log_msg_ret("call", ret);
+
+	return 0;
+}
+
+int acpigen_disable_tx_gpio(struct acpi_ctx *ctx, struct acpi_gpio *gpio)
+{
+	int ret;
+
+	if (gpio->polarity == ACPI_GPIO_ACTIVE_LOW)
+		return acpigen_soc_set_tx_gpio(ctx, gpio->pins[0]);
+	else
+		return acpigen_soc_clear_tx_gpio(ctx, gpio->pins[0]);
+	if (ret)
+		return log_msg_ret("call", ret);
+
+	return 0;
+}
-- 
2.25.1.481.gfbce0eb801-goog

  parent reply	other threads:[~2020-03-09  3:44 UTC|newest]

Thread overview: 86+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-03-09  3:44 [PATCH v2 00/39] dm: Add programmatic generation of ACPI tables (part A) Simon Glass
2020-03-09  3:44 ` [PATCH v2 01/39] cpu: Support querying the address width Simon Glass
2020-03-09  3:44 ` [PATCH v2 02/39] spi: Add SPI mode enums Simon Glass
2020-03-09  7:41   ` Andy Shevchenko
2020-03-09  3:44 ` [PATCH v2 03/39] tpm: cr50: Release locality on exit Simon Glass
2020-03-09  3:44 ` [PATCH v2 04/39] tpm: cr50: Add a comment for cr50_priv Simon Glass
2020-03-09  3:44 ` [PATCH v2 05/39] tpm: cr50: Use the correct GPIO binding Simon Glass
2020-03-09  3:44 ` [PATCH v2 06/39] tpm: Don't cleanup unless an error happens Simon Glass
2020-03-09  3:44 ` [PATCH v2 07/39] dm: pci: Allow disabling auto-config for a device Simon Glass
2020-03-09  7:43   ` Andy Shevchenko
2020-03-09  3:44 ` [PATCH v2 08/39] x86: Correct wording of coreboot source code Simon Glass
2020-03-09  7:44   ` Andy Shevchenko
2020-03-10 23:22     ` Simon Glass
2020-03-09  3:44 ` [PATCH v2 09/39] x86: apl: Move p2sb ofdata reading to the correct method Simon Glass
2020-03-10 14:39   ` Andy Shevchenko
2020-03-11 12:17     ` Simon Glass
2020-03-11 13:06       ` Andy Shevchenko
2020-03-09  3:44 ` [PATCH v2 10/39] pci: Adjust dm_pci_read_bar32() to return errors correctly Simon Glass
2020-03-09  3:44 ` [PATCH v2 11/39] x86: apl: Add Global NVS table header Simon Glass
2020-03-09  3:44 ` [PATCH v2 12/39] dm: core: Add basic ACPI support Simon Glass
2020-03-10 14:46   ` Andy Shevchenko
2020-03-11 12:17     ` Simon Glass
2020-03-09  3:44 ` [PATCH v2 13/39] acpi: Add a binding for ACPI settings in the device tree Simon Glass
2020-03-10 14:50   ` Andy Shevchenko
2020-03-12  3:22     ` Simon Glass
2020-03-09  3:44 ` [PATCH v2 14/39] acpi: Add a simple sandbox test Simon Glass
2020-03-09  3:44 ` [PATCH v2 15/39] x86: Move acpi_table header to main include/ directory Simon Glass
2020-03-09  3:44 ` [PATCH v2 16/39] acpi: Add an __ACPI__ preprocessor symbol Simon Glass
2020-03-09  3:44 ` [PATCH v2 17/39] acpi: Add a central location for table version numbers Simon Glass
2020-03-09  3:44 ` [PATCH v2 18/39] acpi: Add support for DMAR Simon Glass
2020-03-09  3:44 ` [PATCH v2 19/39] acpi: Move acpi_fill_header() to generic code Simon Glass
2020-03-09  3:44 ` [PATCH v2 20/39] acpi: Add a method to write tables for a device Simon Glass
2020-03-09  3:44 ` [PATCH v2 21/39] acpi: Convert part of acpi_table to use acpi_ctx Simon Glass
2020-03-09  3:44 ` [PATCH v2 22/39] x86: Allow devices to write ACPI tables Simon Glass
2020-03-09  3:44 ` [PATCH v2 23/39] acpi: Drop code for missing XSDT from acpi_write_rsdp() Simon Glass
2020-03-09  3:44 ` [PATCH v2 24/39] acpi: Move acpi_add_table() to generic code Simon Glass
2020-03-09  3:44 ` [PATCH v2 25/39] acpi: Put table-setup code in its own function Simon Glass
2020-03-09  3:44 ` [PATCH v2 26/39] acpi: Move the xsdt pointer to acpi_ctx Simon Glass
2020-03-09  3:44 ` [PATCH v2 27/39] acpi: Add an acpi command Simon Glass
2020-03-09  3:44 ` [PATCH v2 28/39] acpi: Add some tables required by the generation code Simon Glass
2020-03-09  3:44 ` [PATCH v2 29/39] acpi: Add generation code for devices Simon Glass
2020-03-09  3:44 ` Simon Glass [this message]
2020-03-09  3:44 ` [PATCH v2 31/39] gpio: Add a method to convert a GPIO to ACPI Simon Glass
2020-03-09  3:44 ` [PATCH v2 32/39] irq: Add a method to convert an interrupt " Simon Glass
2020-03-18 16:17   ` Antwort: " Wolfgang Wallner
2020-03-18 16:20   ` Wolfgang Wallner
2020-03-19 16:18     ` Simon Glass
2020-03-09  3:44 ` [PATCH v2 33/39] acpi: Add support for SSDT generation Simon Glass
2020-03-18 16:48   ` Antwort: " Wolfgang Wallner
2020-03-19  7:39   ` Wolfgang Wallner
2020-03-09  3:44 ` [PATCH v2 34/39] x86: acpi: Move MADT up a bit Simon Glass
2020-03-09  3:44 ` [PATCH v2 35/39] acpi: Record the items added to SSDT Simon Glass
2020-03-09  3:45 ` [PATCH v2 36/39] acpi: Support ordering SSDT data by device Simon Glass
2020-03-09  3:45 ` [PATCH v2 37/39] x86: Allow devices to write an SSDT Simon Glass
2020-03-09  3:45 ` [PATCH v2 38/39] acpi: Add support for DSDT generation Simon Glass
2020-03-09  3:45 ` [PATCH v2 39/39] x86: Allow devices to write to DSDT Simon Glass
2020-03-09  7:38 ` Antwort: [PATCH v2 02/39] spi: Add SPI mode enums Wolfgang Wallner
2020-03-09  7:38 ` Antwort: [PATCH v2 08/39] x86: Correct wording of coreboot source code Wolfgang Wallner
2020-03-09  7:38 ` Antwort: [PATCH v2 10/39] pci: Adjust dm_pci_read_bar32() to return errors correctly Wolfgang Wallner
2020-03-09  7:38 ` Antwort: [PATCH v2 11/39] x86: apl: Add Global NVS table header Wolfgang Wallner
2020-03-09  9:07 ` Antwort: [PATCH v2 12/39] dm: core: Add basic ACPI support Wolfgang Wallner
2020-03-10  9:15 ` Antwort: [PATCH v2 13/39] acpi: Add a binding for ACPI settings in the device tree Wolfgang Wallner
2020-03-12  3:24   ` Simon Glass
2020-03-12 12:44   ` Antwort: " Wolfgang Wallner
2020-03-13  0:36     ` Simon Glass
2020-03-10  9:16 ` Antwort: [PATCH v2 15/39] x86: Move acpi_table header to main include/ directory Wolfgang Wallner
2020-03-10  9:17 ` Antwort: [PATCH v2 16/39] acpi: Add an __ACPI__ preprocessor symbol Wolfgang Wallner
2020-03-10  9:26 ` Antwort: [PATCH v2 17/39] acpi: Add a central location for table version numbers Wolfgang Wallner
2020-03-12  3:22   ` Simon Glass
2020-03-10 12:32 ` Antwort: [PATCH v2 18/39] acpi: Add support for DMAR Wolfgang Wallner
2020-03-10 12:33 ` Antwort: [PATCH v2 19/39] acpi: Move acpi_fill_header() to generic code Wolfgang Wallner
2020-03-10 12:53 ` Antwort: [PATCH v2 14/39] acpi: Add a simple sandbox test Wolfgang Wallner
2020-03-11  9:04 ` Antwort: [PATCH v2 19/39] acpi: Move acpi_fill_header() to generic code Wolfgang Wallner
2020-03-11 11:36 ` Antwort: [PATCH v2 20/39] acpi: Add a method to write tables for a device Wolfgang Wallner
2020-03-11 11:37 ` Antwort: [PATCH v2 22/39] x86: Allow devices to write ACPI tables Wolfgang Wallner
2020-03-11 12:58 ` Antwort: [PATCH v2 21/39] acpi: Convert part of acpi_table to use acpi_ctx Wolfgang Wallner
2020-03-12  3:23   ` Simon Glass
2020-03-12 13:03   ` Antwort: " Wolfgang Wallner
2020-03-11 13:43 ` Antwort: [PATCH v2 24/39] acpi: Move acpi_add_table() to generic code Wolfgang Wallner
2020-03-11 14:33 ` Antwort: [PATCH v2 25/39] acpi: Put table-setup code in its own function Wolfgang Wallner
2020-03-11 14:34 ` Antwort: [PATCH v2 23/39] acpi: Drop code for missing XSDT from acpi_write_rsdp() Wolfgang Wallner
2020-03-11 14:57 ` Antwort: [PATCH v2 26/39] acpi: Move the xsdt pointer to acpi_ctx Wolfgang Wallner
2020-03-12 14:30 ` Antwort: [PATCH v2 27/39] acpi: Add an acpi command Wolfgang Wallner
2020-03-13  9:55 ` Antwort: [PATCH v2 30/39] acpi: Add functions to generate ACPI code Wolfgang Wallner
2020-03-14 20:34   ` Simon Glass
2020-03-13 11:16 ` Antwort: [PATCH v2 31/39] gpio: Add a method to convert a GPIO to ACPI Wolfgang Wallner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200308214442.v2.30.Ie25c3492416531983761521cbc51e188052e18b4@changeid \
    --to=sjg@chromium.org \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.