diff options
Diffstat (limited to 'board/raspberrypi/rpi/rpi.c')
| -rw-r--r-- | board/raspberrypi/rpi/rpi.c | 183 | 
1 files changed, 183 insertions, 0 deletions
| diff --git a/board/raspberrypi/rpi/rpi.c b/board/raspberrypi/rpi/rpi.c index ab5ea85cf9f..9122f33d88d 100644 --- a/board/raspberrypi/rpi/rpi.c +++ b/board/raspberrypi/rpi/rpi.c @@ -23,6 +23,11 @@  #endif  #include <watchdog.h>  #include <dm/pinctrl.h> +#include <dm/ofnode.h> +#include <acpi/acpi_table.h> +#include <acpi/acpigen.h> +#include <dm/lists.h> +#include <tables_csum.h>  DECLARE_GLOBAL_DATA_PTR; @@ -583,3 +588,181 @@ int ft_board_setup(void *blob, struct bd_info *bd)  	return 0;  } + +#if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) +static bool is_rpi4(void) +{ +	return of_machine_is_compatible("brcm,bcm2711") || +	       of_machine_is_compatible("brcm,bcm2712"); +} + +static bool is_rpi3(void) +{ +	return of_machine_is_compatible("brcm,bcm2837"); +} + +static int acpi_rpi_board_fill_ssdt(struct acpi_ctx *ctx) +{ +	int node, ret, uart_in_use, mini_clock_rate; +	bool enabled; +	struct udevice *dev; +	struct { +		const char *fdt_compatible; +		const char *acpi_scope; +		bool on_rpi4; +		bool on_rpi3; +		u32 mmio_address; +	} map[] = { +		{"brcm,bcm2711-pcie", "\\_SB.PCI0", true, false}, +		{"brcm,bcm2711-emmc2", "\\_SB.GDV1.SDC3", true, false}, +		{"brcm,bcm2835-pwm", "\\_SB.GDV0.PWM0", true, true}, +		{"brcm,bcm2711-genet-v5",  "\\_SB.ETH0", true, false}, +		{"brcm,bcm2711-thermal", "\\_SB.EC00", true, true}, +		{"brcm,bcm2835-sdhci", "\\_SB.SDC1", true, true}, +		{"brcm,bcm2835-sdhost", "\\_SB.SDC2", false, true}, +		{"brcm,bcm2835-mbox", "\\_SB.GDV0.RPIQ", true, true}, +		{"brcm,bcm2835-i2c", "\\_SB.GDV0.I2C1", true, true, 0xfe205000}, +		{"brcm,bcm2835-i2c", "\\_SB.GDV0.I2C2", true, true, 0xfe804000}, +		{"brcm,bcm2835-spi", "\\_SB.GDV0.SPI0", true, true}, +		{"brcm,bcm2835-aux-spi", "\\_SB.GDV0.SPI1", true, true, 0xfe215080}, +		{"arm,pl011", "\\_SB.URT0", true, true}, +		{"brcm,bcm2835-aux-uart", "\\_SB.URTM", true, true}, +		{ /* Sentinel */ } +	}; + +	/* Device enable */ +	for (int i = 0; map[i].fdt_compatible; i++) { +		if ((is_rpi4() && !map[i].on_rpi4) || +		    (is_rpi3() && !map[i].on_rpi3)) { +			enabled = false; +		} else { +			node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, +							     map[i].fdt_compatible); +			while (node != -FDT_ERR_NOTFOUND && map[i].mmio_address) { +				struct fdt_resource r; + +				ret = fdt_get_resource(gd->fdt_blob, node, "reg", 0, &r); +				if (ret) { +					node = -FDT_ERR_NOTFOUND; +					break; +				} + +				if (r.start == map[i].mmio_address) +					break; + +				node = fdt_node_offset_by_compatible(gd->fdt_blob, node, +								     map[i].fdt_compatible); +			} + +			enabled = (node > 0) ? fdtdec_get_is_enabled(gd->fdt_blob, node) : 0; +		} +		acpigen_write_scope(ctx, map[i].acpi_scope); +		acpigen_write_name_integer(ctx, "_STA", enabled ? 0xf : 0); +		acpigen_pop_len(ctx); +	} + +	/* GPIO quirks */ +	node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "brcm,bcm2835-gpio"); +	if (node <= 0) +		node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "brcm,bcm2711-gpio"); + +	acpigen_write_scope(ctx, "\\_SB.GDV0.GPI0"); +	enabled = (node > 0) ? fdtdec_get_is_enabled(gd->fdt_blob, node) : 0; +	acpigen_write_name_integer(ctx, "_STA", enabled ? 0xf : 0); +	acpigen_pop_len(ctx); + +	if (is_rpi4()) { +		/* eMMC quirks */ +		node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "brcm,bcm2711-emmc2"); +		if (node) { +			phys_addr_t cpu; +			dma_addr_t bus; +			u64 size; + +			ret = fdt_get_dma_range(gd->fdt_blob, node, &cpu, &bus, &size); + +			acpigen_write_scope(ctx, "\\_SB.GDV1"); +			acpigen_write_method_serialized(ctx, "_DMA", 0); +			acpigen_emit_byte(ctx, RETURN_OP); + +			if (!ret && bus != cpu)		/* Translated DMA range */ +				acpigen_emit_namestring(ctx, "\\_SB.GDV1.DMTR"); +			else if (!ret && bus == cpu)	/* Non translated DMA */ +				acpigen_emit_namestring(ctx, "\\_SB.GDV1.DMNT"); +			else	/* Silicon revisions older than C0: Translated DMA range */ +				acpigen_emit_namestring(ctx, "\\_SB.GDV1.DMTR"); +			acpigen_pop_len(ctx); +		} +	} + +	/* Serial */ +	uart_in_use = ~0; +	mini_clock_rate = 0x1000000; + +	ret = uclass_get_device_by_driver(UCLASS_SERIAL, +					  DM_DRIVER_GET(bcm283x_pl011_uart), +					  &dev); +	if (!ret) +		uart_in_use = 0; + +	ret = uclass_get_device_by_driver(UCLASS_SERIAL, +					  DM_DRIVER_GET(serial_bcm283x_mu), +					  &dev); +	if (!ret) { +		if (uart_in_use == 0) +			log_err("Invalid config: PL011 and MiniUART are both enabled."); +		else +			uart_in_use = 1; + +		mini_clock_rate = dev_read_u32_default(dev, "clock", 0x1000000); +	} +	if (uart_in_use > 1) +		log_err("No working serial: PL011 and MiniUART are both disabled."); + +	acpigen_write_scope(ctx, "\\_SB.BTH0"); +	acpigen_write_name_integer(ctx, "URIU", uart_in_use); +	acpigen_pop_len(ctx); + +	acpigen_write_scope(ctx, "\\_SB.URTM"); +	acpigen_write_name_integer(ctx, "MUCR", mini_clock_rate); +	acpigen_pop_len(ctx); + +	return 0; +} + +static int rpi_acpi_write_ssdt(struct acpi_ctx *ctx, const struct acpi_writer *entry) +{ +	struct acpi_table_header *ssdt; +	int ret; + +	ssdt = ctx->current; +	memset(ssdt, '\0', sizeof(struct acpi_table_header)); + +	acpi_fill_header(ssdt, "SSDT"); +	ssdt->revision = acpi_get_table_revision(ACPITAB_SSDT); +	ssdt->creator_revision = 1; +	ssdt->length = sizeof(struct acpi_table_header); + +	acpi_inc(ctx, sizeof(struct acpi_table_header)); + +	ret = acpi_rpi_board_fill_ssdt(ctx); +	if (ret) { +		ctx->current = ssdt; +		return log_msg_ret("fill", ret); +	} + +	/* (Re)calculate length and checksum */ +	ssdt->length = ctx->current - (void *)ssdt; +	ssdt->checksum = table_compute_checksum((void *)ssdt, ssdt->length); +	log_debug("SSDT at %p, length %x\n", ssdt, ssdt->length); + +	/* Drop the table if it is empty */ +	if (ssdt->length == sizeof(struct acpi_table_header)) +		return log_msg_ret("fill", -ENOENT); +	acpi_add_table(ctx, ssdt); + +	return 0; +} + +ACPI_WRITER(5ssdt, "SSDT", rpi_acpi_write_ssdt, 0); +#endif | 
