diff --git a/arch/arm/include/asm/acpi_table.h b/arch/arm/include/asm/acpi_table.h
index e69de29bb2d..930e857edeb 100644
--- a/arch/arm/include/asm/acpi_table.h
+++ b/arch/arm/include/asm/acpi_table.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2019 Google LLC
+ */
+
+#ifndef __ASM_ACPI_TABLE_H__
+#define __ASM_ACPI_TABLE_H__
+
+#ifndef __ACPI__
+
+ulong write_acpi_tables(ulong start);
+
+#endif
+
+#endif /* __ASM_ACPI_TABLE_H__ */
diff --git a/board/raspberrypi/rpi/Makefile b/board/raspberrypi/rpi/Makefile
index b1186cdf100..67f6b05e654 100644
--- a/board/raspberrypi/rpi/Makefile
+++ b/board/raspberrypi/rpi/Makefile
@@ -4,3 +4,5 @@
 
 obj-y	:= rpi.o
 obj-y	+= lowlevel_init.o
+
+obj-$(CONFIG_GENERATE_ACPI_TABLE) += dsdt.o
diff --git a/board/raspberrypi/rpi/rpi.c b/board/raspberrypi/rpi/rpi.c
index 55afaa54d9f..3fe69c66da2 100644
--- a/board/raspberrypi/rpi/rpi.c
+++ b/board/raspberrypi/rpi/rpi.c
@@ -3,6 +3,8 @@
  * (C) Copyright 2012-2016 Stephen Warren
  */
 
+#define LOG_CATEGORY LOGC_BOARD
+
 #include <common.h>
 #include <config.h>
 #include <dm.h>
@@ -12,19 +14,29 @@
 #include <fdt_simplefb.h>
 #include <init.h>
 #include <lcd.h>
+#include <log.h>
 #include <memalign.h>
 #include <mmc.h>
+#include <signatures.h>
+#include <tables_csum.h>
+#include <acpi/acpi_table.h>
+#include <asm/acpi_table.h>
+#include <asm/global_data.h>
 #include <asm/gpio.h>
 #include <asm/arch/mbox.h>
 #include <asm/arch/msg.h>
 #include <asm/arch/sdhci.h>
-#include <asm/global_data.h>
+#include <asm/arch/acpi/bcm2836.h>
+#include <dm/acpi.h>
 #include <dm/platform_data/serial_bcm283x_mu.h>
 #ifdef CONFIG_ARM64
 #include <asm/armv8/mmu.h>
 #endif
 #include <watchdog.h>
 #include <dm/pinctrl.h>
+#ifdef CONFIG_GENERATE_ACPI_TABLE
+#include "acpitables.h"
+#endif
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -515,3 +527,449 @@ int ft_board_setup(void *blob, struct bd_info *bd)
 
 	return 0;
 }
+
+#ifdef CONFIG_GENERATE_ACPI_TABLE
+int last_stage_init(void)
+{
+	int ret;
+
+	ret = write_acpi_tables(0x10000);
+	if (ret < 0) {
+		log_err("Failed to write tables\n");
+		return log_msg_ret("table", ret);
+	}
+
+	return 0;
+}
+
+static int rpi_write_facp(struct acpi_ctx *ctx, const struct acpi_writer *entry)
+{
+	struct acpi_table_header *header;
+	struct acpi_fadt *fadt;
+
+	fadt = ctx->current;
+	header = &fadt->header;
+
+	memset(fadt, '\0', sizeof(struct acpi_fadt));
+
+	acpi_fill_header(header, "FACP");
+	header->length = sizeof(struct acpi_fadt);
+	header->revision = ACPI_FADT_REV_ACPI_6_0;
+
+	fadt->firmware_ctrl = (ulong)ctx->facs;
+	fadt->dsdt = (ulong)ctx->dsdt;
+	fadt->preferred_pm_profile = ACPI_PM_APPLIANCE_PC;
+
+	fadt->flags = ACPI_FADT_WBINVD | ACPI_FADT_SLEEP_BUTTON |
+		ACPI_FADT_HW_REDUCED_ACPI;
+	fadt->arm_boot_arch = ACPI_ARM_PSCI_COMPLIANT;
+	fadt->minor_revision = 3;
+
+	fadt->x_firmware_ctl_l = (ulong)ctx->facs;
+	fadt->x_firmware_ctl_h = (ulong)ctx->facs >> 32;
+	fadt->x_dsdt_l = (ulong)ctx->dsdt;
+	fadt->x_dsdt_h = (ulong)ctx->dsdt >> 32;
+
+	header->checksum = table_compute_checksum(fadt, header->length);
+
+	acpi_add_table(ctx, fadt);
+
+	acpi_inc(ctx, sizeof(struct acpi_fadt));
+
+	return 0;
+}
+ACPI_WRITER(5facp, "FACP", rpi_write_facp, 0);
+
+#define GTDT_FLAG_INT_ACTIVE_LOW	BIT(1)
+#define RPI_GTDT_GTIMER_FLAGS		GTDT_FLAG_INT_ACTIVE_LOW
+
+/* ARM Architectural Timer Interrupt(GIC PPI) numbers */
+#define PcdArmArchTimerSecIntrNum	29
+#define PcdArmArchTimerIntrNum		30
+#define PcdArmArchTimerHypIntrNum	26
+#define PcdArmArchTimerVirtIntrNum	27
+
+static int rpi_write_gtdt(struct acpi_ctx *ctx, const struct acpi_writer *entry)
+{
+	struct acpi_table_header *header;
+	struct acpi_gtdt *gtdt;
+
+	gtdt = ctx->current;
+	header = &gtdt->header;
+
+	memset(gtdt, '\0', sizeof(struct acpi_gtdt));
+
+	acpi_fill_header(header, "GTDT");
+	header->length = sizeof(struct acpi_gtdt);
+	header->revision = 3;
+
+	gtdt->cnt_ctrl_base = RPI_SYSTEM_TIMER_BASE_ADDRESS;
+	gtdt->sec_el1_gsiv = PcdArmArchTimerSecIntrNum;
+	gtdt->sec_el1_flags = RPI_GTDT_GTIMER_FLAGS;
+	gtdt->el1_gsiv = PcdArmArchTimerIntrNum;
+	gtdt->el1_flags = RPI_GTDT_GTIMER_FLAGS;
+	gtdt->virt_el1_gsiv = PcdArmArchTimerVirtIntrNum;
+	gtdt->virt_el1_flags = RPI_GTDT_GTIMER_FLAGS;
+	gtdt->el2_gsiv = PcdArmArchTimerHypIntrNum;
+	gtdt->el2_flags = RPI_GTDT_GTIMER_FLAGS;
+	gtdt->cnt_read_base = 0xffffffffffffffff;
+	acpi_add_table(ctx, gtdt);
+
+	acpi_inc(ctx, sizeof(struct acpi_gtdt));
+
+	return 0;
+};
+ACPI_WRITER(5gtdt, "GTDT", rpi_write_gtdt, 0);
+
+static int rpi_write_dbg2(struct acpi_ctx *ctx, const struct acpi_writer *entry)
+{
+	struct acpi_table_header *header;
+	struct acpi_dbg2_header *dbg2;
+	struct acpi_dbg2_device *ddev;
+	struct acpi_gen_regaddr *addr;
+	u32 *addr_size;
+	char *name;
+	void *end;
+
+	dbg2 = ctx->current;
+	header = &dbg2->header;
+
+	/* Note this doesn't zero everything */
+	memset(dbg2, '\0', sizeof(struct acpi_dbg2_header));
+
+	acpi_fill_header(header, "DBG2");
+	header->revision = 0;
+
+	dbg2->devices_offset = sizeof(*dbg2);
+	dbg2->devices_count = 1;
+
+	ddev = (void *)(dbg2 + 1);
+	ddev->revision = 0;
+	ddev->length = 0x35;
+	ddev->address_count = 1;
+	ddev->namespace_string_length = 0xf;
+	ddev->namespace_string_offset = 0x26;
+	ddev->oem_data_length = 0;
+	ddev->oem_data_offset = 0;
+	ddev->port_type = ACPI_DBG2_SERIAL_PORT;
+	ddev->port_subtype = ACPI_DBG2_ARM_PL011;
+
+	addr = (void *)(ddev + 1);
+	ddev->base_address_offset = sizeof(*ddev);
+	ddev->address_size_offset = sizeof(*ddev) + sizeof(*addr);
+
+	addr->space_id = ACPI_ADDRESS_SPACE_MEMORY;
+	addr->bit_width = 32;
+	addr->bit_offset = 0;
+	addr->access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+	addr->addrl = 0xfe201000;
+	addr->addrh = 0;
+
+	addr_size = (u32 *)(addr + 1);
+	*addr_size = 0x1000;
+
+	name = (char *)(addr_size + 1);
+	strcpy(name, "\\_SB.GDV0.URT0");
+	end = name + strlen(name) + 1;
+	header->length = end - ctx->current;
+	acpi_add_table(ctx, dbg2);
+
+	acpi_inc(ctx, header->length);
+
+	return 0;
+};
+ACPI_WRITER(5dbg2, "DBG2", rpi_write_dbg2, 0);
+
+#if 0
+/* Need to add a logo first */
+static int rpi_write_bgrt(struct acpi_ctx *ctx, const struct acpi_writer *entry)
+{
+	struct acpi_table_header *header;
+	struct acpi_bgrt_header *bgrt;
+
+	bgrt = ctx->current;
+	header = &dbg2->header;
+
+	memset(bgrt, '\0', sizeof(struct acpi_bgrt));
+
+	acpi_fill_header(header, "BGRT");
+	header->revision = 0;
+
+	acpi_inc(ctx, header->length);
+
+	return 0;
+};
+ACPI_WRITER(5bgrt, "BGRT", rpi_write_bgrt, 0);
+#endif
+
+static u32 *add_proc(struct acpi_ctx *ctx, int flags, int parent, int proc_id,
+		     int num_resources)
+{
+	struct acpi_pptt_proc *proc = ctx->current;
+	u32 *resource_list;
+
+	proc->hdr.type = ACPI_PPTT_TYPE_PROC;
+	proc->flags = flags;
+	proc->parent = parent;
+	proc->proc_id = proc_id;
+	proc->num_resources = num_resources;
+	proc->hdr.length = sizeof(struct acpi_pptt_proc) +
+		sizeof(u32) * num_resources;
+	resource_list = ctx->current + sizeof(struct acpi_pptt_proc);
+	acpi_inc(ctx, proc->hdr.length);
+
+	return resource_list;
+}
+
+static int add_cache(struct acpi_ctx *ctx, int flags, int size, int sets,
+		     int assoc, int attributes, int line_size)
+{
+	struct acpi_pptt_cache *cache = ctx->current;
+	int ofs;
+
+	ofs = ctx->current - ctx->tab_start;
+	cache->hdr.type = ACPI_PPTT_TYPE_CACHE;
+	cache->hdr.length = sizeof(struct acpi_pptt_cache);
+	cache->flags = flags;
+	cache->next_cache_level = 0;
+	cache->size = size;
+	cache->sets = sets;
+	cache->assoc = assoc;
+	cache->attributes = attributes;
+	cache->line_size = line_size;
+	acpi_inc(ctx, cache->hdr.length);
+
+	return ofs;
+}
+
+static int rpi_write_pptt(struct acpi_ctx *ctx, const struct acpi_writer *entry)
+{
+	struct acpi_table_header *header;
+	int proc_ofs;
+	u32 *proc_ptr;
+	int ofs, ofs0, ofs1, i;
+
+	header = ctx->current;
+	ctx->tab_start = ctx->current;
+
+	memset(header, '\0', sizeof(struct acpi_table_header));
+
+	acpi_fill_header(header, "PPTT");
+	header->revision = 0;
+	acpi_inc(ctx, sizeof(*header));
+
+	proc_ofs = ctx->current - ctx->tab_start;
+	proc_ptr = add_proc(ctx, ACPI_PPTT_PHYSICAL_PACKAGE |
+			    ACPI_PPTT_CHILDREN_IDENTICAL, 0, 0, 1);
+
+	ofs = add_cache(ctx, ACPI_PPTT_ALL_VALID, 0x100000, 0x400, 0x10,
+			ACPI_PPTT_WRITE_ALLOC |
+			(ACPI_PPTT_CACHE_TYPE_UNIFIED <<
+			 ACPI_PPTT_CACHE_TYPE_SHIFT), 0x40);
+	*proc_ptr = ofs;
+
+	for (i = 0; i < 4; i++) {
+		proc_ptr = add_proc(ctx, ACPI_PPTT_CHILDREN_IDENTICAL |
+				    ACPI_PPTT_NODE_IS_LEAF | ACPI_PPTT_PROC_ID_VALID,
+				    proc_ofs, i, 2);
+
+		ofs0 = add_cache(ctx, ACPI_PPTT_ALL_VALID, 0x8000, 0x100, 2,
+				 ACPI_PPTT_WRITE_ALLOC, 0x40);
+
+		ofs1 = add_cache(ctx, ACPI_PPTT_ALL_BUT_WRITE_POL, 0xc000, 0x100, 3,
+				 ACPI_PPTT_CACHE_TYPE_INSTR <<
+				 ACPI_PPTT_CACHE_TYPE_SHIFT, 0x40);
+		proc_ptr[0] = ofs0;
+		proc_ptr[1] = ofs1;
+	}
+
+	header->length = ctx->current - ctx->tab_start;
+	header->checksum = table_compute_checksum(header, header->length);
+
+	acpi_inc(ctx, header->length);
+	acpi_add_table(ctx, header);
+
+	return 0;
+};
+ACPI_WRITER(5pptt, "PPTT", rpi_write_pptt, 0);
+
+static void acpi_write_madt_gicc(struct acpi_ctx *ctx, uint cpu_num,
+				 uint perf_gsiv, ulong phys_base, ulong gicv,
+				 ulong gich, uint vgic_maint_irq, ulong mpidr,
+				 uint efficiency)
+{
+	struct acpi_madr_gicc *gicc = ctx->current;
+
+	memset(gicc, '\0', sizeof(struct acpi_madr_gicc));
+	gicc->type = ACPI_APIC_GICC;
+	gicc->length = sizeof(struct acpi_madr_gicc);
+	gicc->cpu_if_num = cpu_num;
+	gicc->processor_id = cpu_num;
+	gicc->flags = ACPI_MADRF_ENABLED;
+	gicc->perf_gsiv = perf_gsiv;
+	gicc->phys_base = phys_base;
+	gicc->gicv = gicv;
+	gicc->gich = gich;
+	gicc->vgic_maint_irq = vgic_maint_irq;
+	gicc->mpidr = mpidr;
+	gicc->efficiency = efficiency;
+	acpi_inc(ctx, gicc->length);
+}
+
+static void acpi_write_madt_gicd(struct acpi_ctx *ctx, uint gic_id,
+				 ulong phys_base, uint gic_version)
+{
+	struct acpi_madr_gicd *gicd = ctx->current;
+
+	memset(gicd, '\0', sizeof(struct acpi_madr_gicd));
+	gicd->type = ACPI_APIC_GICD;
+	gicd->length = sizeof(struct acpi_madr_gicd);
+	gicd->gic_id = gic_id;
+	gicd->phys_base = phys_base;
+	gicd->gic_version = gic_version;
+
+	acpi_inc(ctx, gicd->length);
+}
+
+static int rpi_write_madt(struct acpi_ctx *ctx, const struct acpi_writer *entry)
+{
+	struct acpi_table_header *header;
+	struct acpi_madt *madt;
+	int i;
+
+	ctx->tab_start = ctx->current;
+	madt = ctx->current;
+
+	memset(madt, '\0', sizeof(struct acpi_madt));
+	header = &madt->header;
+
+	/* Fill out header fields */
+	acpi_fill_header(header, "APIC");
+	header->length = sizeof(struct acpi_madt);
+	header->revision = ACPI_MADT_REV_ACPI_6_0;
+
+	madt->lapic_addr = 0;
+	madt->flags = 0;
+	acpi_inc(ctx, sizeof(*madt));
+
+	for (i = 0; i < 4; i++) {
+		acpi_write_madt_gicc(ctx, i, 0x30 + i, 0xff842000, 0xff846000,
+				     0xff844000, 0x19, i, 1);
+	}
+	acpi_write_madt_gicd(ctx, 0, 0xff841000, 2);
+
+	/* (Re)calculate length and checksum */
+	header->length = (u32)(ctx->current - ctx->tab_start);
+
+	header->checksum = table_compute_checksum((void *)madt, header->length);
+	acpi_add_table(ctx, madt);
+	acpi_inc(ctx, madt->header.length);
+
+	return 0;
+}
+ACPI_WRITER(5madt, "APIC", rpi_write_madt, 0);
+
+/* DMA Controller Vendor Data */
+struct __packed dma_ctlr_vendor_data {
+	u32 length;
+	u32 type;
+	u64 chan_base;
+	u32 chan_size;
+	u64 ctlr_base;
+	u32 ctlr_size;
+	u32 chan_count;
+	u32 ctlr_irq;
+	u32 min_req_line;
+	u32 max_req_line;
+	u8 cache_coherent;
+};
+
+/* DMA Controller */
+struct __packed rd_dma_ctlr {
+	struct acpi_csrt_descriptor hdr;
+	struct dma_ctlr_vendor_data data;
+};
+
+/* dma chan vendor data */
+struct __packed dma_chan_vendor_data {
+	u32 chan;
+	u32 chan_irq;
+	u16 is_reserved;
+	u16 addr_incr;
+};
+
+/* dma chan */
+struct __packed rd_dma_chan {
+	struct acpi_csrt_descriptor hdr;
+	struct dma_chan_vendor_data data;
+};
+
+/* dma resource group */
+struct __packed rg_dma {
+	struct acpi_csrt_group hdr;
+	struct rd_dma_ctlr ctlr;
+	struct rd_dma_chan chan[];
+};
+
+#define RPI_DMA_MAX_REQ_LINES 32
+
+static void add_cmd_chan(struct rd_dma_chan *dmac, uint uid, uint chan,
+			 uint chan_irq, bool is_reserved, int addr_incr)
+{
+	memset(dmac, '\0', sizeof(*dmac));
+	dmac->hdr.length = sizeof(struct rd_dma_chan);
+	dmac->hdr.type = EFI_ACPI_CSRT_RESOURCE_TYPE_DMA;
+	dmac->hdr.subtype = EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CHANNEL;
+	dmac->hdr.uid = uid;
+
+	dmac->data.chan = chan;
+	dmac->data.chan_irq = chan_irq;
+	dmac->data.is_reserved = is_reserved;
+	dmac->data.addr_incr = addr_incr;
+}
+
+int acpi_fill_csrt(struct acpi_ctx *ctx)
+{
+	struct dma_ctlr_vendor_data *data;
+	struct acpi_csrt_group *hdr;
+	struct rg_dma *dma;
+	int i;
+
+	dma = ctx->current;
+	hdr = &dma->hdr;
+	memset(hdr, '\0', sizeof(*hdr));
+	hdr->length = 0;
+	hdr->vendor_id = SIGNATURE_32('R', 'P', 'I', 'F');
+	hdr->device_id = EFI_ACPI_CSRT_DEVICE_ID_DMA;
+
+	dma->ctlr.hdr.length = sizeof(struct rd_dma_ctlr);
+	dma->ctlr.hdr.type = EFI_ACPI_CSRT_RESOURCE_TYPE_DMA;
+	dma->ctlr.hdr.subtype = EFI_ACPI_CSRT_RESOURCE_SUBTYPE_DMA_CONTROLLER;
+	dma->ctlr.hdr.uid = EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP;
+
+	data = &dma->ctlr.data;
+	data->length = sizeof(struct dma_ctlr_vendor_data);
+	data->type = 1;
+	data->chan_base = BCM2836_DMA0_BASE_ADDRESS;
+	data->chan_size = RPI_DMA_CHANNEL_COUNT * BCM2836_DMA_CHANNEL_LENGTH;
+	data->ctlr_base = BCM2836_DMA_CTRL_BASE_ADDRESS;
+	data->ctlr_size = 8;
+	data->chan_count = RPI_DMA_USED_CHANNEL_COUNT;
+	data->max_req_line = RPI_DMA_MAX_REQ_LINES - 1;
+
+	acpi_inc(ctx, sizeof(struct rg_dma));
+
+	for (i = 0; i < 10; i++) {
+		add_cmd_chan(&dma->chan[i],
+			     EFI_ACPI_CSRT_RESOURCE_ID_IN_DMA_GRP + 1 + i, i,
+			     0x30 + i,
+			     i == 1 || i == 2 || i == 3 || i == 6 || i == 7,
+			     i == 4);
+		acpi_inc(ctx, sizeof(struct rd_dma_chan));
+	}
+
+	hdr->length = (u32)(ctx->current - (void *)dma);
+
+	return 0;
+}
+#endif
diff --git a/configs/rpi_4_defconfig b/configs/rpi_4_defconfig
index 0720505c6a5..4a81f3e1f38 100644
--- a/configs/rpi_4_defconfig
+++ b/configs/rpi_4_defconfig
@@ -11,6 +11,7 @@ CONFIG_USE_PREBOOT=y
 CONFIG_PREBOOT="pci enum; usb start;"
 # CONFIG_DISPLAY_CPUINFO is not set
 # CONFIG_DISPLAY_BOARDINFO is not set
+CONFIG_LAST_STAGE_INIT=y
 CONFIG_MISC_INIT_R=y
 CONFIG_SYS_PROMPT="U-Boot> "
 CONFIG_CMD_DFU=y
@@ -60,4 +61,5 @@ CONFIG_DM_VIDEO=y
 CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
+CONFIG_GENERATE_ACPI_TABLE=y
 CONFIG_OF_LIBFDT_OVERLAY=y
