GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2002-2006
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
|
||||
ifndef CONFIG_X86_64
|
||||
obj-y += bios.o
|
||||
obj-y += bios_asm.o
|
||||
obj-y += bios_interrupts.o
|
||||
obj-y += string.o
|
||||
endif
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_CMD_BOOTM) += bootm.o
|
||||
endif
|
||||
obj-y += cmd_boot.o
|
||||
obj-$(CONFIG_SEABIOS) += coreboot_table.o
|
||||
obj-y += early_cmos.o
|
||||
obj-y += e820.o
|
||||
obj-y += init_helpers.o
|
||||
obj-y += interrupts.o
|
||||
obj-y += lpc-uclass.o
|
||||
obj-y += mpspec.o
|
||||
obj-$(CONFIG_ENABLE_MRC_CACHE) += mrccache.o
|
||||
obj-y += northbridge-uclass.o
|
||||
obj-$(CONFIG_I8259_PIC) += i8259.o
|
||||
obj-$(CONFIG_I8254_TIMER) += i8254.o
|
||||
obj-$(CONFIG_PINCTRL_ICH6) += pinctrl_ich6.o
|
||||
obj-y += pirq_routing.o
|
||||
obj-y += relocate.o
|
||||
obj-y += physmem.o
|
||||
obj-$(CONFIG_INTEL_MID) += pmu.o
|
||||
obj-$(CONFIG_X86_RAMTEST) += ramtest.o
|
||||
obj-$(CONFIG_INTEL_MID) += scu.o
|
||||
obj-y += sections.o
|
||||
obj-y += sfi.o
|
||||
obj-y += acpi.o
|
||||
obj-$(CONFIG_HAVE_ACPI_RESUME) += acpi_s3.o
|
||||
ifndef CONFIG_QEMU
|
||||
obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi_table.o
|
||||
endif
|
||||
obj-y += tables.o
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
obj-$(CONFIG_CMD_ZBOOT) += zimage.o
|
||||
endif
|
||||
obj-$(CONFIG_USE_HOB) += hob.o
|
||||
obj-$(CONFIG_HAVE_FSP) += fsp/
|
||||
obj-$(CONFIG_FSP_VERSION1) += fsp1/
|
||||
obj-$(CONFIG_FSP_VERSION2) += fsp2/
|
||||
|
||||
ifdef CONFIG_SPL_BUILD
|
||||
ifdef CONFIG_TPL_BUILD
|
||||
obj-y += tpl.o
|
||||
else
|
||||
obj-y += spl.o
|
||||
endif
|
||||
endif
|
||||
|
||||
lib-$(CONFIG_USE_PRIVATE_LIBGCC) += div64.o
|
||||
|
||||
ifeq ($(CONFIG_$(SPL_)X86_64),)
|
||||
obj-$(CONFIG_EFI_APP) += crt0_ia32_efi.o reloc_ia32_efi.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_EFI_STUB),)
|
||||
|
||||
CFLAGS_REMOVE_reloc_ia32_efi.o += -mregparm=3
|
||||
CFLAGS_reloc_ia32_efi.o += -fpic -fshort-wchar
|
||||
|
||||
# When building for 64-bit we must remove the i386-specific flags
|
||||
CFLAGS_REMOVE_reloc_x86_64_efi.o += -mregparm=3 -march=i386 -m32
|
||||
CFLAGS_reloc_x86_64_efi.o += -fpic -fshort-wchar -m64
|
||||
|
||||
AFLAGS_REMOVE_crt0_x86_64_efi.o += -mregparm=3 -march=i386 -m32
|
||||
AFLAGS_crt0_x86_64_efi.o += -fpic -fshort-wchar -m64
|
||||
|
||||
extra-$(CONFIG_EFI_STUB_32BIT) += crt0_ia32_efi.o reloc_ia32_efi.o
|
||||
extra-$(CONFIG_EFI_STUB_64BIT) += crt0_x86_64_efi.o reloc_x86_64_efi.o
|
||||
|
||||
endif
|
||||
|
||||
ifdef CONFIG_EFI_STUB
|
||||
|
||||
ifeq ($(CONFIG_$(SPL_)X86_64),)
|
||||
extra-y += $(EFI_CRT0) $(EFI_RELOC)
|
||||
endif
|
||||
|
||||
else
|
||||
|
||||
ifndef CONFIG_SPL_BUILD
|
||||
ifneq ($(CONFIG_CMD_BOOTEFI_SELFTEST)$(CONFIG_CMD_BOOTEFI_HELLO_COMPILE),)
|
||||
extra-y += $(EFI_CRT0) $(EFI_RELOC)
|
||||
endif
|
||||
endif
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/acpi_table.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/tables.h>
|
||||
|
||||
static struct acpi_rsdp *acpi_valid_rsdp(struct acpi_rsdp *rsdp)
|
||||
{
|
||||
if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0)
|
||||
return NULL;
|
||||
|
||||
debug("Looking on %p for valid checksum\n", rsdp);
|
||||
|
||||
if (table_compute_checksum((void *)rsdp, 20) != 0)
|
||||
return NULL;
|
||||
debug("acpi rsdp checksum 1 passed\n");
|
||||
|
||||
if ((rsdp->revision > 1) &&
|
||||
(table_compute_checksum((void *)rsdp, rsdp->length) != 0))
|
||||
return NULL;
|
||||
debug("acpi rsdp checksum 2 passed\n");
|
||||
|
||||
return rsdp;
|
||||
}
|
||||
|
||||
struct acpi_fadt *acpi_find_fadt(void)
|
||||
{
|
||||
char *p, *end;
|
||||
struct acpi_rsdp *rsdp = NULL;
|
||||
struct acpi_rsdt *rsdt;
|
||||
struct acpi_fadt *fadt = NULL;
|
||||
int i;
|
||||
|
||||
/* Find RSDP */
|
||||
for (p = (char *)ROM_TABLE_ADDR; p < (char *)ROM_TABLE_END; p += 16) {
|
||||
rsdp = acpi_valid_rsdp((struct acpi_rsdp *)p);
|
||||
if (rsdp)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rsdp)
|
||||
return NULL;
|
||||
|
||||
debug("RSDP found at %p\n", rsdp);
|
||||
rsdt = (struct acpi_rsdt *)(uintptr_t)rsdp->rsdt_address;
|
||||
|
||||
end = (char *)rsdt + rsdt->header.length;
|
||||
debug("RSDT found at %p ends at %p\n", rsdt, end);
|
||||
|
||||
for (i = 0; ((char *)&rsdt->entry[i]) < end; i++) {
|
||||
fadt = (struct acpi_fadt *)(uintptr_t)rsdt->entry[i];
|
||||
if (strncmp((char *)fadt, "FACP", 4) == 0)
|
||||
break;
|
||||
fadt = NULL;
|
||||
}
|
||||
|
||||
if (!fadt)
|
||||
return NULL;
|
||||
|
||||
debug("FADT found at %p\n", fadt);
|
||||
return fadt;
|
||||
}
|
||||
|
||||
void *acpi_find_wakeup_vector(struct acpi_fadt *fadt)
|
||||
{
|
||||
struct acpi_facs *facs;
|
||||
void *wake_vec;
|
||||
|
||||
debug("Trying to find the wakeup vector...\n");
|
||||
|
||||
facs = (struct acpi_facs *)(uintptr_t)fadt->firmware_ctrl;
|
||||
|
||||
if (!facs) {
|
||||
debug("No FACS found, wake up from S3 not possible.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
debug("FACS found at %p\n", facs);
|
||||
wake_vec = (void *)(uintptr_t)facs->firmware_waking_vector;
|
||||
debug("OS waking vector is %p\n", wake_vec);
|
||||
|
||||
return wake_vec;
|
||||
}
|
||||
|
||||
void enter_acpi_mode(int pm1_cnt)
|
||||
{
|
||||
u16 val = inw(pm1_cnt);
|
||||
|
||||
/*
|
||||
* PM1_CNT register bit0 selects the power management event to be
|
||||
* either an SCI or SMI interrupt. When this bit is set, then power
|
||||
* management events will generate an SCI interrupt. When this bit
|
||||
* is reset power management events will generate an SMI interrupt.
|
||||
*
|
||||
* Per ACPI spec, it is the responsibility of the hardware to set
|
||||
* or reset this bit. OSPM always preserves this bit position.
|
||||
*
|
||||
* U-Boot does not support SMI. And we don't have plan to support
|
||||
* anything running in SMM within U-Boot. To create a legacy-free
|
||||
* system, and expose ourselves to OSPM as working under ACPI mode
|
||||
* already, turn this bit on.
|
||||
*/
|
||||
outw(val | PM1_CNT_SCI_EN, pm1_cnt);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <acpi_s3.h>
|
||||
#include <asm/acpi.h>
|
||||
#include <asm/acpi_table.h>
|
||||
#include <asm/post.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static void asmlinkage (*acpi_do_wakeup)(void *vector) = (void *)WAKEUP_BASE;
|
||||
|
||||
static void acpi_jump_to_wakeup(void *vector)
|
||||
{
|
||||
/* Copy wakeup trampoline in place */
|
||||
memcpy((void *)WAKEUP_BASE, __wakeup, __wakeup_size);
|
||||
|
||||
printf("Jumping to OS waking vector %p\n", vector);
|
||||
acpi_do_wakeup(vector);
|
||||
}
|
||||
|
||||
void acpi_resume(struct acpi_fadt *fadt)
|
||||
{
|
||||
void *wake_vec;
|
||||
|
||||
/* Turn on ACPI mode for S3 */
|
||||
enter_acpi_mode(fadt->pm1a_cnt_blk);
|
||||
|
||||
wake_vec = acpi_find_wakeup_vector(fadt);
|
||||
|
||||
/*
|
||||
* Restore the memory content starting from address 0x1000 which is
|
||||
* used for the real mode interrupt handler stubs.
|
||||
*/
|
||||
memcpy((void *)0x1000, (const void *)gd->arch.backup_mem,
|
||||
S3_RESERVE_SIZE);
|
||||
|
||||
post_code(POST_OS_RESUME);
|
||||
acpi_jump_to_wakeup(wake_vec);
|
||||
}
|
||||
|
||||
int acpi_s3_reserve(void)
|
||||
{
|
||||
/* adjust stack pointer for ACPI S3 resume backup memory */
|
||||
gd->start_addr_sp -= S3_RESERVE_SIZE;
|
||||
gd->arch.backup_mem = gd->start_addr_sp;
|
||||
|
||||
gd->start_addr_sp &= ~0xf;
|
||||
|
||||
/*
|
||||
* U-Boot sets up the real mode interrupt handler stubs starting from
|
||||
* address 0x1000. In most cases, the first 640K (0x00000 - 0x9ffff)
|
||||
* system memory is reported as system RAM in E820 table to the OS.
|
||||
* (see install_e820_map() implementation for each platform). So OS
|
||||
* can use these memories whatever it wants.
|
||||
*
|
||||
* If U-Boot is in an S3 resume path, care must be taken not to corrupt
|
||||
* these memorie otherwise OS data gets lost. Testing shows that, on
|
||||
* Microsoft Windows 10 on Intel Baytrail its wake up vector happens to
|
||||
* be installed at the same address 0x1000. While on Linux its wake up
|
||||
* vector does not overlap this memory range, but after resume kernel
|
||||
* checks low memory range per config option CONFIG_X86_RESERVE_LOW
|
||||
* which is 64K by default to see whether a memory corruption occurs
|
||||
* during the suspend/resume (it's harmless, but warnings are shown
|
||||
* in the kernel dmesg logs).
|
||||
*
|
||||
* We cannot simply mark the these memory as reserved in E820 table
|
||||
* because such configuration makes GRUB complain: unable to allocate
|
||||
* real mode page. Hence we choose to back up these memories to the
|
||||
* place where we reserved on our stack for our S3 resume work.
|
||||
* Before jumping to OS wake up vector, we need restore the original
|
||||
* content there (see acpi_resume() above).
|
||||
*/
|
||||
if (gd->arch.prev_sleep_state == ACPI_S3)
|
||||
memcpy((void *)gd->arch.backup_mem, (const void *)0x1000,
|
||||
S3_RESERVE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Based on acpi.c from coreboot
|
||||
*
|
||||
* Copyright (C) 2015, Saket Sinha <saket.sinha89@gmail.com>
|
||||
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu.h>
|
||||
#include <dm.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <serial.h>
|
||||
#include <version.h>
|
||||
#include <asm/acpi/global_nvs.h>
|
||||
#include <asm/acpi_table.h>
|
||||
#include <asm/ioapic.h>
|
||||
#include <asm/lapic.h>
|
||||
#include <asm/mpspec.h>
|
||||
#include <asm/tables.h>
|
||||
#include <asm/arch/global_nvs.h>
|
||||
|
||||
/*
|
||||
* IASL compiles the dsdt entries and writes the hex values
|
||||
* to a C array AmlCode[] (see dsdt.c).
|
||||
*/
|
||||
extern const unsigned char AmlCode[];
|
||||
|
||||
/* ACPI RSDP address to be used in boot parameters */
|
||||
static ulong acpi_rsdp_addr;
|
||||
|
||||
static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt,
|
||||
struct acpi_xsdt *xsdt)
|
||||
{
|
||||
memset(rsdp, 0, sizeof(struct acpi_rsdp));
|
||||
|
||||
memcpy(rsdp->signature, RSDP_SIG, 8);
|
||||
memcpy(rsdp->oem_id, OEM_ID, 6);
|
||||
|
||||
rsdp->length = sizeof(struct acpi_rsdp);
|
||||
rsdp->rsdt_address = (u32)rsdt;
|
||||
|
||||
/*
|
||||
* Revision: ACPI 1.0: 0, ACPI 2.0/3.0/4.0: 2
|
||||
*
|
||||
* Some OSes expect an XSDT to be present for RSD PTR revisions >= 2.
|
||||
* If we don't have an ACPI XSDT, force ACPI 1.0 (and thus RSD PTR
|
||||
* revision 0)
|
||||
*/
|
||||
if (xsdt == NULL) {
|
||||
rsdp->revision = ACPI_RSDP_REV_ACPI_1_0;
|
||||
} else {
|
||||
rsdp->xsdt_address = (u64)(u32)xsdt;
|
||||
rsdp->revision = ACPI_RSDP_REV_ACPI_2_0;
|
||||
}
|
||||
|
||||
/* Calculate checksums */
|
||||
rsdp->checksum = table_compute_checksum((void *)rsdp, 20);
|
||||
rsdp->ext_checksum = table_compute_checksum((void *)rsdp,
|
||||
sizeof(struct acpi_rsdp));
|
||||
}
|
||||
|
||||
void acpi_fill_header(struct acpi_table_header *header, char *signature)
|
||||
{
|
||||
memcpy(header->signature, signature, 4);
|
||||
memcpy(header->oem_id, OEM_ID, 6);
|
||||
memcpy(header->oem_table_id, OEM_TABLE_ID, 8);
|
||||
header->oem_revision = U_BOOT_BUILD_DATE;
|
||||
memcpy(header->aslc_id, ASLC_ID, 4);
|
||||
}
|
||||
|
||||
static void acpi_write_rsdt(struct acpi_rsdt *rsdt)
|
||||
{
|
||||
struct acpi_table_header *header = &(rsdt->header);
|
||||
|
||||
/* Fill out header fields */
|
||||
acpi_fill_header(header, "RSDT");
|
||||
header->length = sizeof(struct acpi_rsdt);
|
||||
header->revision = 1;
|
||||
|
||||
/* Entries are filled in later, we come with an empty set */
|
||||
|
||||
/* Fix checksum */
|
||||
header->checksum = table_compute_checksum((void *)rsdt,
|
||||
sizeof(struct acpi_rsdt));
|
||||
}
|
||||
|
||||
static void acpi_write_xsdt(struct acpi_xsdt *xsdt)
|
||||
{
|
||||
struct acpi_table_header *header = &(xsdt->header);
|
||||
|
||||
/* Fill out header fields */
|
||||
acpi_fill_header(header, "XSDT");
|
||||
header->length = sizeof(struct acpi_xsdt);
|
||||
header->revision = 1;
|
||||
|
||||
/* Entries are filled in later, we come with an empty set */
|
||||
|
||||
/* Fix checksum */
|
||||
header->checksum = table_compute_checksum((void *)xsdt,
|
||||
sizeof(struct acpi_xsdt));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an ACPI table to the RSDT (and XSDT) structure, recalculate length
|
||||
* and checksum.
|
||||
*/
|
||||
static void acpi_add_table(struct acpi_rsdp *rsdp, void *table)
|
||||
{
|
||||
int i, entries_num;
|
||||
struct acpi_rsdt *rsdt;
|
||||
struct acpi_xsdt *xsdt = NULL;
|
||||
|
||||
/* The RSDT is mandatory while the XSDT is not */
|
||||
rsdt = (struct acpi_rsdt *)rsdp->rsdt_address;
|
||||
|
||||
if (rsdp->xsdt_address)
|
||||
xsdt = (struct acpi_xsdt *)((u32)rsdp->xsdt_address);
|
||||
|
||||
/* This should always be MAX_ACPI_TABLES */
|
||||
entries_num = ARRAY_SIZE(rsdt->entry);
|
||||
|
||||
for (i = 0; i < entries_num; i++) {
|
||||
if (rsdt->entry[i] == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= entries_num) {
|
||||
debug("ACPI: Error: too many tables\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add table to the RSDT */
|
||||
rsdt->entry[i] = (u32)table;
|
||||
|
||||
/* Fix RSDT length or the kernel will assume invalid entries */
|
||||
rsdt->header.length = sizeof(struct acpi_table_header) +
|
||||
(sizeof(u32) * (i + 1));
|
||||
|
||||
/* Re-calculate checksum */
|
||||
rsdt->header.checksum = 0;
|
||||
rsdt->header.checksum = table_compute_checksum((u8 *)rsdt,
|
||||
rsdt->header.length);
|
||||
|
||||
/*
|
||||
* And now the same thing for the XSDT. We use the same index as for
|
||||
* now we want the XSDT and RSDT to always be in sync in U-Boot
|
||||
*/
|
||||
if (xsdt) {
|
||||
/* Add table to the XSDT */
|
||||
xsdt->entry[i] = (u64)(u32)table;
|
||||
|
||||
/* Fix XSDT length */
|
||||
xsdt->header.length = sizeof(struct acpi_table_header) +
|
||||
(sizeof(u64) * (i + 1));
|
||||
|
||||
/* Re-calculate checksum */
|
||||
xsdt->header.checksum = 0;
|
||||
xsdt->header.checksum = table_compute_checksum((u8 *)xsdt,
|
||||
xsdt->header.length);
|
||||
}
|
||||
}
|
||||
|
||||
static void acpi_create_facs(struct acpi_facs *facs)
|
||||
{
|
||||
memset((void *)facs, 0, sizeof(struct acpi_facs));
|
||||
|
||||
memcpy(facs->signature, "FACS", 4);
|
||||
facs->length = sizeof(struct acpi_facs);
|
||||
facs->hardware_signature = 0;
|
||||
facs->firmware_waking_vector = 0;
|
||||
facs->global_lock = 0;
|
||||
facs->flags = 0;
|
||||
facs->x_firmware_waking_vector_l = 0;
|
||||
facs->x_firmware_waking_vector_h = 0;
|
||||
facs->version = 1;
|
||||
}
|
||||
|
||||
static int acpi_create_madt_lapic(struct acpi_madt_lapic *lapic,
|
||||
u8 cpu, u8 apic)
|
||||
{
|
||||
lapic->type = ACPI_APIC_LAPIC;
|
||||
lapic->length = sizeof(struct acpi_madt_lapic);
|
||||
lapic->flags = LOCAL_APIC_FLAG_ENABLED;
|
||||
lapic->processor_id = cpu;
|
||||
lapic->apic_id = apic;
|
||||
|
||||
return lapic->length;
|
||||
}
|
||||
|
||||
int acpi_create_madt_lapics(u32 current)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int total_length = 0;
|
||||
|
||||
for (uclass_find_first_device(UCLASS_CPU, &dev);
|
||||
dev;
|
||||
uclass_find_next_device(&dev)) {
|
||||
struct cpu_platdata *plat = dev_get_parent_platdata(dev);
|
||||
int length = acpi_create_madt_lapic(
|
||||
(struct acpi_madt_lapic *)current,
|
||||
plat->cpu_id, plat->cpu_id);
|
||||
current += length;
|
||||
total_length += length;
|
||||
}
|
||||
|
||||
return total_length;
|
||||
}
|
||||
|
||||
int acpi_create_madt_ioapic(struct acpi_madt_ioapic *ioapic, u8 id,
|
||||
u32 addr, u32 gsi_base)
|
||||
{
|
||||
ioapic->type = ACPI_APIC_IOAPIC;
|
||||
ioapic->length = sizeof(struct acpi_madt_ioapic);
|
||||
ioapic->reserved = 0x00;
|
||||
ioapic->gsi_base = gsi_base;
|
||||
ioapic->ioapic_id = id;
|
||||
ioapic->ioapic_addr = addr;
|
||||
|
||||
return ioapic->length;
|
||||
}
|
||||
|
||||
int acpi_create_madt_irqoverride(struct acpi_madt_irqoverride *irqoverride,
|
||||
u8 bus, u8 source, u32 gsirq, u16 flags)
|
||||
{
|
||||
irqoverride->type = ACPI_APIC_IRQ_SRC_OVERRIDE;
|
||||
irqoverride->length = sizeof(struct acpi_madt_irqoverride);
|
||||
irqoverride->bus = bus;
|
||||
irqoverride->source = source;
|
||||
irqoverride->gsirq = gsirq;
|
||||
irqoverride->flags = flags;
|
||||
|
||||
return irqoverride->length;
|
||||
}
|
||||
|
||||
int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi,
|
||||
u8 cpu, u16 flags, u8 lint)
|
||||
{
|
||||
lapic_nmi->type = ACPI_APIC_LAPIC_NMI;
|
||||
lapic_nmi->length = sizeof(struct acpi_madt_lapic_nmi);
|
||||
lapic_nmi->flags = flags;
|
||||
lapic_nmi->processor_id = cpu;
|
||||
lapic_nmi->lint = lint;
|
||||
|
||||
return lapic_nmi->length;
|
||||
}
|
||||
|
||||
static int acpi_create_madt_irq_overrides(u32 current)
|
||||
{
|
||||
struct acpi_madt_irqoverride *irqovr;
|
||||
u16 sci_flags = MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_HIGH;
|
||||
int length = 0;
|
||||
|
||||
irqovr = (void *)current;
|
||||
length += acpi_create_madt_irqoverride(irqovr, 0, 0, 2, 0);
|
||||
|
||||
irqovr = (void *)(current + length);
|
||||
length += acpi_create_madt_irqoverride(irqovr, 0, 9, 9, sci_flags);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
__weak u32 acpi_fill_madt(u32 current)
|
||||
{
|
||||
current += acpi_create_madt_lapics(current);
|
||||
|
||||
current += acpi_create_madt_ioapic((struct acpi_madt_ioapic *)current,
|
||||
io_apic_read(IO_APIC_ID) >> 24, IO_APIC_ADDR, 0);
|
||||
|
||||
current += acpi_create_madt_irq_overrides(current);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
static void acpi_create_madt(struct acpi_madt *madt)
|
||||
{
|
||||
struct acpi_table_header *header = &(madt->header);
|
||||
u32 current = (u32)madt + sizeof(struct acpi_madt);
|
||||
|
||||
memset((void *)madt, 0, sizeof(struct acpi_madt));
|
||||
|
||||
/* Fill out header fields */
|
||||
acpi_fill_header(header, "APIC");
|
||||
header->length = sizeof(struct acpi_madt);
|
||||
header->revision = 4;
|
||||
|
||||
madt->lapic_addr = LAPIC_DEFAULT_BASE;
|
||||
madt->flags = ACPI_MADT_PCAT_COMPAT;
|
||||
|
||||
current = acpi_fill_madt(current);
|
||||
|
||||
/* (Re)calculate length and checksum */
|
||||
header->length = current - (u32)madt;
|
||||
|
||||
header->checksum = table_compute_checksum((void *)madt, header->length);
|
||||
}
|
||||
|
||||
int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base,
|
||||
u16 seg_nr, u8 start, u8 end)
|
||||
{
|
||||
memset(mmconfig, 0, sizeof(*mmconfig));
|
||||
mmconfig->base_address_l = base;
|
||||
mmconfig->base_address_h = 0;
|
||||
mmconfig->pci_segment_group_number = seg_nr;
|
||||
mmconfig->start_bus_number = start;
|
||||
mmconfig->end_bus_number = end;
|
||||
|
||||
return sizeof(struct acpi_mcfg_mmconfig);
|
||||
}
|
||||
|
||||
__weak u32 acpi_fill_mcfg(u32 current)
|
||||
{
|
||||
current += acpi_create_mcfg_mmconfig
|
||||
((struct acpi_mcfg_mmconfig *)current,
|
||||
CONFIG_PCIE_ECAM_BASE, 0x0, 0x0, 255);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/* MCFG is defined in the PCI Firmware Specification 3.0 */
|
||||
static void acpi_create_mcfg(struct acpi_mcfg *mcfg)
|
||||
{
|
||||
struct acpi_table_header *header = &(mcfg->header);
|
||||
u32 current = (u32)mcfg + sizeof(struct acpi_mcfg);
|
||||
|
||||
memset((void *)mcfg, 0, sizeof(struct acpi_mcfg));
|
||||
|
||||
/* Fill out header fields */
|
||||
acpi_fill_header(header, "MCFG");
|
||||
header->length = sizeof(struct acpi_mcfg);
|
||||
header->revision = 1;
|
||||
|
||||
current = acpi_fill_mcfg(current);
|
||||
|
||||
/* (Re)calculate length and checksum */
|
||||
header->length = current - (u32)mcfg;
|
||||
header->checksum = table_compute_checksum((void *)mcfg, header->length);
|
||||
}
|
||||
|
||||
__weak u32 acpi_fill_csrt(u32 current)
|
||||
{
|
||||
return current;
|
||||
}
|
||||
|
||||
static void acpi_create_csrt(struct acpi_csrt *csrt)
|
||||
{
|
||||
struct acpi_table_header *header = &(csrt->header);
|
||||
u32 current = (u32)csrt + sizeof(struct acpi_csrt);
|
||||
|
||||
memset((void *)csrt, 0, sizeof(struct acpi_csrt));
|
||||
|
||||
/* Fill out header fields */
|
||||
acpi_fill_header(header, "CSRT");
|
||||
header->length = sizeof(struct acpi_csrt);
|
||||
header->revision = 0;
|
||||
|
||||
current = acpi_fill_csrt(current);
|
||||
|
||||
/* (Re)calculate length and checksum */
|
||||
header->length = current - (u32)csrt;
|
||||
header->checksum = table_compute_checksum((void *)csrt, header->length);
|
||||
}
|
||||
|
||||
static void acpi_create_spcr(struct acpi_spcr *spcr)
|
||||
{
|
||||
struct acpi_table_header *header = &(spcr->header);
|
||||
struct serial_device_info serial_info = {0};
|
||||
ulong serial_address, serial_offset;
|
||||
struct udevice *dev;
|
||||
uint serial_config;
|
||||
uint serial_width;
|
||||
int access_size;
|
||||
int space_id;
|
||||
int ret = -ENODEV;
|
||||
|
||||
/* Fill out header fields */
|
||||
acpi_fill_header(header, "SPCR");
|
||||
header->length = sizeof(struct acpi_spcr);
|
||||
header->revision = 2;
|
||||
|
||||
/* Read the device once, here. It is reused below */
|
||||
dev = gd->cur_serial_dev;
|
||||
if (dev)
|
||||
ret = serial_getinfo(dev, &serial_info);
|
||||
if (ret)
|
||||
serial_info.type = SERIAL_CHIP_UNKNOWN;
|
||||
|
||||
/* Encode chip type */
|
||||
switch (serial_info.type) {
|
||||
case SERIAL_CHIP_16550_COMPATIBLE:
|
||||
spcr->interface_type = ACPI_DBG2_16550_COMPATIBLE;
|
||||
break;
|
||||
case SERIAL_CHIP_UNKNOWN:
|
||||
default:
|
||||
spcr->interface_type = ACPI_DBG2_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Encode address space */
|
||||
switch (serial_info.addr_space) {
|
||||
case SERIAL_ADDRESS_SPACE_MEMORY:
|
||||
space_id = ACPI_ADDRESS_SPACE_MEMORY;
|
||||
break;
|
||||
case SERIAL_ADDRESS_SPACE_IO:
|
||||
default:
|
||||
space_id = ACPI_ADDRESS_SPACE_IO;
|
||||
break;
|
||||
}
|
||||
|
||||
serial_width = serial_info.reg_width * 8;
|
||||
serial_offset = serial_info.reg_offset << serial_info.reg_shift;
|
||||
serial_address = serial_info.addr + serial_offset;
|
||||
|
||||
/* Encode register access size */
|
||||
switch (serial_info.reg_shift) {
|
||||
case 0:
|
||||
access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
|
||||
break;
|
||||
case 1:
|
||||
access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
|
||||
break;
|
||||
case 2:
|
||||
access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
|
||||
break;
|
||||
case 3:
|
||||
access_size = ACPI_ACCESS_SIZE_QWORD_ACCESS;
|
||||
break;
|
||||
default:
|
||||
access_size = ACPI_ACCESS_SIZE_UNDEFINED;
|
||||
break;
|
||||
}
|
||||
|
||||
debug("UART type %u @ %lx\n", spcr->interface_type, serial_address);
|
||||
|
||||
/* Fill GAS */
|
||||
spcr->serial_port.space_id = space_id;
|
||||
spcr->serial_port.bit_width = serial_width;
|
||||
spcr->serial_port.bit_offset = 0;
|
||||
spcr->serial_port.access_size = access_size;
|
||||
spcr->serial_port.addrl = lower_32_bits(serial_address);
|
||||
spcr->serial_port.addrh = upper_32_bits(serial_address);
|
||||
|
||||
/* Encode baud rate */
|
||||
switch (serial_info.baudrate) {
|
||||
case 9600:
|
||||
spcr->baud_rate = 3;
|
||||
break;
|
||||
case 19200:
|
||||
spcr->baud_rate = 4;
|
||||
break;
|
||||
case 57600:
|
||||
spcr->baud_rate = 6;
|
||||
break;
|
||||
case 115200:
|
||||
spcr->baud_rate = 7;
|
||||
break;
|
||||
default:
|
||||
spcr->baud_rate = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
serial_config = SERIAL_DEFAULT_CONFIG;
|
||||
if (dev)
|
||||
ret = serial_getconfig(dev, &serial_config);
|
||||
|
||||
spcr->parity = SERIAL_GET_PARITY(serial_config);
|
||||
spcr->stop_bits = SERIAL_GET_STOP(serial_config);
|
||||
|
||||
/* No PCI devices for now */
|
||||
spcr->pci_device_id = 0xffff;
|
||||
spcr->pci_vendor_id = 0xffff;
|
||||
|
||||
/* Fix checksum */
|
||||
header->checksum = table_compute_checksum((void *)spcr, header->length);
|
||||
}
|
||||
|
||||
/*
|
||||
* QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
|
||||
*/
|
||||
ulong write_acpi_tables(ulong start)
|
||||
{
|
||||
u32 current;
|
||||
struct acpi_rsdp *rsdp;
|
||||
struct acpi_rsdt *rsdt;
|
||||
struct acpi_xsdt *xsdt;
|
||||
struct acpi_facs *facs;
|
||||
struct acpi_table_header *dsdt;
|
||||
struct acpi_fadt *fadt;
|
||||
struct acpi_mcfg *mcfg;
|
||||
struct acpi_madt *madt;
|
||||
struct acpi_csrt *csrt;
|
||||
struct acpi_spcr *spcr;
|
||||
int i;
|
||||
|
||||
current = start;
|
||||
|
||||
/* Align ACPI tables to 16 byte */
|
||||
current = ALIGN(current, 16);
|
||||
|
||||
debug("ACPI: Writing ACPI tables at %lx\n", start);
|
||||
|
||||
/* We need at least an RSDP and an RSDT Table */
|
||||
rsdp = (struct acpi_rsdp *)current;
|
||||
current += sizeof(struct acpi_rsdp);
|
||||
current = ALIGN(current, 16);
|
||||
rsdt = (struct acpi_rsdt *)current;
|
||||
current += sizeof(struct acpi_rsdt);
|
||||
current = ALIGN(current, 16);
|
||||
xsdt = (struct acpi_xsdt *)current;
|
||||
current += sizeof(struct acpi_xsdt);
|
||||
/*
|
||||
* Per ACPI spec, the FACS table address must be aligned to a 64 byte
|
||||
* boundary (Windows checks this, but Linux does not).
|
||||
*/
|
||||
current = ALIGN(current, 64);
|
||||
|
||||
/* clear all table memory */
|
||||
memset((void *)start, 0, current - start);
|
||||
|
||||
acpi_write_rsdp(rsdp, rsdt, xsdt);
|
||||
acpi_write_rsdt(rsdt);
|
||||
acpi_write_xsdt(xsdt);
|
||||
|
||||
debug("ACPI: * FACS\n");
|
||||
facs = (struct acpi_facs *)current;
|
||||
current += sizeof(struct acpi_facs);
|
||||
current = ALIGN(current, 16);
|
||||
|
||||
acpi_create_facs(facs);
|
||||
|
||||
debug("ACPI: * DSDT\n");
|
||||
dsdt = (struct acpi_table_header *)current;
|
||||
memcpy(dsdt, &AmlCode, sizeof(struct acpi_table_header));
|
||||
current += sizeof(struct acpi_table_header);
|
||||
memcpy((char *)current,
|
||||
(char *)&AmlCode + sizeof(struct acpi_table_header),
|
||||
dsdt->length - sizeof(struct acpi_table_header));
|
||||
current += dsdt->length - sizeof(struct acpi_table_header);
|
||||
current = ALIGN(current, 16);
|
||||
|
||||
/* Pack GNVS into the ACPI table area */
|
||||
for (i = 0; i < dsdt->length; i++) {
|
||||
u32 *gnvs = (u32 *)((u32)dsdt + i);
|
||||
if (*gnvs == ACPI_GNVS_ADDR) {
|
||||
debug("Fix up global NVS in DSDT to 0x%08x\n", current);
|
||||
*gnvs = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update DSDT checksum since we patched the GNVS address */
|
||||
dsdt->checksum = 0;
|
||||
dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length);
|
||||
|
||||
/* Fill in platform-specific global NVS variables */
|
||||
acpi_create_gnvs((struct acpi_global_nvs *)current);
|
||||
current += sizeof(struct acpi_global_nvs);
|
||||
current = ALIGN(current, 16);
|
||||
|
||||
debug("ACPI: * FADT\n");
|
||||
fadt = (struct acpi_fadt *)current;
|
||||
current += sizeof(struct acpi_fadt);
|
||||
current = ALIGN(current, 16);
|
||||
acpi_create_fadt(fadt, facs, dsdt);
|
||||
acpi_add_table(rsdp, fadt);
|
||||
|
||||
debug("ACPI: * MADT\n");
|
||||
madt = (struct acpi_madt *)current;
|
||||
acpi_create_madt(madt);
|
||||
current += madt->header.length;
|
||||
acpi_add_table(rsdp, madt);
|
||||
current = ALIGN(current, 16);
|
||||
|
||||
debug("ACPI: * MCFG\n");
|
||||
mcfg = (struct acpi_mcfg *)current;
|
||||
acpi_create_mcfg(mcfg);
|
||||
current += mcfg->header.length;
|
||||
acpi_add_table(rsdp, mcfg);
|
||||
current = ALIGN(current, 16);
|
||||
|
||||
debug("ACPI: * CSRT\n");
|
||||
csrt = (struct acpi_csrt *)current;
|
||||
acpi_create_csrt(csrt);
|
||||
current += csrt->header.length;
|
||||
acpi_add_table(rsdp, csrt);
|
||||
current = ALIGN(current, 16);
|
||||
|
||||
debug("ACPI: * SPCR\n");
|
||||
spcr = (struct acpi_spcr *)current;
|
||||
acpi_create_spcr(spcr);
|
||||
current += spcr->header.length;
|
||||
acpi_add_table(rsdp, spcr);
|
||||
current = ALIGN(current, 16);
|
||||
|
||||
debug("current = %x\n", current);
|
||||
|
||||
acpi_rsdp_addr = (unsigned long)rsdp;
|
||||
debug("ACPI: done\n");
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
ulong acpi_get_rsdp_addr(void)
|
||||
{
|
||||
return acpi_rsdp_addr;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Adapted from Linux v2.6.36 kernel: arch/powerpc/kernel/asm-offsets.c
|
||||
*
|
||||
* This program is used to generate definitions needed by
|
||||
* assembly language modules.
|
||||
*
|
||||
* We use the technique used in the OSF Mach kernel code:
|
||||
* generate asm statements containing #defines,
|
||||
* compile this file to assembler, and then extract the
|
||||
* #defines from the assembly-language output.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <linux/kbuild.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
DEFINE(GD_BIST, offsetof(gd_t, arch.bist));
|
||||
#ifdef CONFIG_USE_HOB
|
||||
DEFINE(GD_HOB_LIST, offsetof(gd_t, arch.hob_list));
|
||||
#endif
|
||||
DEFINE(GD_TABLE, offsetof(gd_t, arch.table));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* From Coreboot file device/oprom/realmode/x86.c
|
||||
*
|
||||
* Copyright (C) 2007 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2009-2010 coresystems GmbH
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <bios_emul.h>
|
||||
#include <irq_func.h>
|
||||
#include <vbe.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/i8259.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/post.h>
|
||||
#include "bios.h"
|
||||
|
||||
/* Interrupt handlers for each interrupt the ROM can call */
|
||||
static int (*int_handler[256])(void);
|
||||
|
||||
/* to have a common register file for interrupt handlers */
|
||||
X86EMU_sysEnv _X86EMU_env;
|
||||
|
||||
asmlinkage void (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx, u32 edx,
|
||||
u32 esi, u32 edi);
|
||||
|
||||
asmlinkage void (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx,
|
||||
u32 edx, u32 esi, u32 edi);
|
||||
|
||||
static void setup_realmode_code(void)
|
||||
{
|
||||
memcpy((void *)REALMODE_BASE, &asm_realmode_code,
|
||||
asm_realmode_code_size);
|
||||
|
||||
/* Ensure the global pointers are relocated properly. */
|
||||
realmode_call = PTR_TO_REAL_MODE(asm_realmode_call);
|
||||
realmode_interrupt = PTR_TO_REAL_MODE(__realmode_interrupt);
|
||||
|
||||
debug("Real mode stub @%x: %d bytes\n", REALMODE_BASE,
|
||||
asm_realmode_code_size);
|
||||
}
|
||||
|
||||
static void setup_rombios(void)
|
||||
{
|
||||
const char date[] = "06/11/99";
|
||||
memcpy((void *)0xffff5, &date, 8);
|
||||
|
||||
const char ident[] = "PCI_ISA";
|
||||
memcpy((void *)0xfffd9, &ident, 7);
|
||||
|
||||
/* system model: IBM-AT */
|
||||
writeb(0xfc, 0xffffe);
|
||||
}
|
||||
|
||||
static int int_exception_handler(void)
|
||||
{
|
||||
/* compatibility shim */
|
||||
struct eregs reg_info = {
|
||||
.eax = M.x86.R_EAX,
|
||||
.ecx = M.x86.R_ECX,
|
||||
.edx = M.x86.R_EDX,
|
||||
.ebx = M.x86.R_EBX,
|
||||
.esp = M.x86.R_ESP,
|
||||
.ebp = M.x86.R_EBP,
|
||||
.esi = M.x86.R_ESI,
|
||||
.edi = M.x86.R_EDI,
|
||||
.vector = M.x86.intno,
|
||||
.error_code = 0,
|
||||
.eip = M.x86.R_EIP,
|
||||
.cs = M.x86.R_CS,
|
||||
.eflags = M.x86.R_EFLG
|
||||
};
|
||||
struct eregs *regs = ®_info;
|
||||
|
||||
debug("Oops, exception %d while executing option rom\n", regs->vector);
|
||||
cpu_hlt();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int_unknown_handler(void)
|
||||
{
|
||||
debug("Unsupported software interrupt #0x%x eax 0x%x\n",
|
||||
M.x86.intno, M.x86.R_EAX);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* setup interrupt handlers for mainboard */
|
||||
void bios_set_interrupt_handler(int intnum, int (*int_func)(void))
|
||||
{
|
||||
int_handler[intnum] = int_func;
|
||||
}
|
||||
|
||||
static void setup_interrupt_handlers(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The first 16 int_handler functions are not BIOS services,
|
||||
* but the CPU-generated exceptions ("hardware interrupts")
|
||||
*/
|
||||
for (i = 0; i < 0x10; i++)
|
||||
int_handler[i] = &int_exception_handler;
|
||||
|
||||
/* Mark all other int_handler calls as unknown first */
|
||||
for (i = 0x10; i < 0x100; i++) {
|
||||
/* Skip if bios_set_interrupt_handler() isn't called first */
|
||||
if (int_handler[i])
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Now set the default functions that are actually needed
|
||||
* to initialize the option roms. The board may override
|
||||
* these with bios_set_interrupt_handler()
|
||||
*/
|
||||
switch (i) {
|
||||
case 0x10:
|
||||
int_handler[0x10] = &int10_handler;
|
||||
break;
|
||||
case 0x12:
|
||||
int_handler[0x12] = &int12_handler;
|
||||
break;
|
||||
case 0x16:
|
||||
int_handler[0x16] = &int16_handler;
|
||||
break;
|
||||
case 0x1a:
|
||||
int_handler[0x1a] = &int1a_handler;
|
||||
break;
|
||||
default:
|
||||
int_handler[i] = &int_unknown_handler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_idt_stub(void *target, u8 intnum)
|
||||
{
|
||||
unsigned char *codeptr;
|
||||
|
||||
codeptr = (unsigned char *)target;
|
||||
memcpy(codeptr, &__idt_handler, __idt_handler_size);
|
||||
codeptr[3] = intnum; /* modify int# in the code stub. */
|
||||
}
|
||||
|
||||
static void setup_realmode_idt(void)
|
||||
{
|
||||
struct realmode_idt *idts = NULL;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Copy IDT stub code for each interrupt. This might seem wasteful
|
||||
* but it is really simple
|
||||
*/
|
||||
for (i = 0; i < 256; i++) {
|
||||
idts[i].cs = 0;
|
||||
idts[i].offset = 0x1000 + (i * __idt_handler_size);
|
||||
write_idt_stub((void *)((ulong)idts[i].offset), i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Many option ROMs use the hard coded interrupt entry points in the
|
||||
* system bios. So install them at the known locations.
|
||||
*/
|
||||
|
||||
/* int42 is the relocated int10 */
|
||||
write_idt_stub((void *)0xff065, 0x42);
|
||||
/* BIOS Int 11 Handler F000:F84D */
|
||||
write_idt_stub((void *)0xff84d, 0x11);
|
||||
/* BIOS Int 12 Handler F000:F841 */
|
||||
write_idt_stub((void *)0xff841, 0x12);
|
||||
/* BIOS Int 13 Handler F000:EC59 */
|
||||
write_idt_stub((void *)0xfec59, 0x13);
|
||||
/* BIOS Int 14 Handler F000:E739 */
|
||||
write_idt_stub((void *)0xfe739, 0x14);
|
||||
/* BIOS Int 15 Handler F000:F859 */
|
||||
write_idt_stub((void *)0xff859, 0x15);
|
||||
/* BIOS Int 16 Handler F000:E82E */
|
||||
write_idt_stub((void *)0xfe82e, 0x16);
|
||||
/* BIOS Int 17 Handler F000:EFD2 */
|
||||
write_idt_stub((void *)0xfefd2, 0x17);
|
||||
/* ROM BIOS Int 1A Handler F000:FE6E */
|
||||
write_idt_stub((void *)0xffe6e, 0x1a);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
|
||||
static u8 vbe_get_mode_info(struct vbe_mode_info *mi)
|
||||
{
|
||||
u16 buffer_seg;
|
||||
u16 buffer_adr;
|
||||
char *buffer;
|
||||
|
||||
debug("VBE: Getting information about VESA mode %04x\n",
|
||||
mi->video_mode);
|
||||
buffer = PTR_TO_REAL_MODE(asm_realmode_buffer);
|
||||
buffer_seg = (((unsigned long)buffer) >> 4) & 0xff00;
|
||||
buffer_adr = ((unsigned long)buffer) & 0xffff;
|
||||
|
||||
realmode_interrupt(0x10, VESA_GET_MODE_INFO, 0x0000, mi->video_mode,
|
||||
0x0000, buffer_seg, buffer_adr);
|
||||
memcpy(mi->mode_info_block, buffer, sizeof(struct vbe_mode_info));
|
||||
mi->valid = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 vbe_set_mode(struct vbe_mode_info *mi)
|
||||
{
|
||||
int video_mode = mi->video_mode;
|
||||
|
||||
debug("VBE: Setting VESA mode %#04x\n", video_mode);
|
||||
/* request linear framebuffer mode */
|
||||
video_mode |= (1 << 14);
|
||||
/* don't clear the framebuffer, we do that later */
|
||||
video_mode |= (1 << 15);
|
||||
realmode_interrupt(0x10, VESA_SET_MODE, video_mode,
|
||||
0x0000, 0x0000, 0x0000, 0x0000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vbe_set_graphics(int vesa_mode, struct vbe_mode_info *mode_info)
|
||||
{
|
||||
unsigned char *framebuffer;
|
||||
|
||||
mode_info->video_mode = (1 << 14) | vesa_mode;
|
||||
vbe_get_mode_info(mode_info);
|
||||
|
||||
framebuffer = (unsigned char *)(ulong)mode_info->vesa.phys_base_ptr;
|
||||
debug("VBE: resolution: %dx%d@%d\n",
|
||||
le16_to_cpu(mode_info->vesa.x_resolution),
|
||||
le16_to_cpu(mode_info->vesa.y_resolution),
|
||||
mode_info->vesa.bits_per_pixel);
|
||||
debug("VBE: framebuffer: %p\n", framebuffer);
|
||||
if (!framebuffer) {
|
||||
debug("VBE: Mode does not support linear framebuffer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mode_info->video_mode &= 0x3ff;
|
||||
vbe_set_mode(mode_info);
|
||||
}
|
||||
#endif /* CONFIG_FRAMEBUFFER_SET_VESA_MODE */
|
||||
|
||||
void bios_run_on_x86(struct udevice *dev, unsigned long addr, int vesa_mode,
|
||||
struct vbe_mode_info *mode_info)
|
||||
{
|
||||
pci_dev_t pcidev = dm_pci_get_bdf(dev);
|
||||
u32 num_dev;
|
||||
|
||||
num_dev = PCI_BUS(pcidev) << 8 | PCI_DEV(pcidev) << 3 |
|
||||
PCI_FUNC(pcidev);
|
||||
|
||||
/* Needed to avoid exceptions in some ROMs */
|
||||
interrupt_init();
|
||||
|
||||
/* Set up some legacy information in the F segment */
|
||||
setup_rombios();
|
||||
|
||||
/* Set up C interrupt handlers */
|
||||
setup_interrupt_handlers();
|
||||
|
||||
/* Set up real-mode IDT */
|
||||
setup_realmode_idt();
|
||||
|
||||
/* Make sure the code is placed. */
|
||||
setup_realmode_code();
|
||||
|
||||
debug("Calling Option ROM at %lx, pci device %#x...", addr, num_dev);
|
||||
|
||||
/* Option ROM entry point is at OPROM start + 3 */
|
||||
realmode_call(addr + 0x0003, num_dev, 0xffff, 0x0000, 0xffff, 0x0,
|
||||
0x0);
|
||||
debug("done\n");
|
||||
|
||||
#ifdef CONFIG_FRAMEBUFFER_SET_VESA_MODE
|
||||
if (vesa_mode != -1)
|
||||
vbe_set_graphics(vesa_mode, mode_info);
|
||||
#endif
|
||||
}
|
||||
|
||||
asmlinkage int interrupt_handler(u32 intnumber, u32 gsfs, u32 dses,
|
||||
u32 edi, u32 esi, u32 ebp, u32 esp,
|
||||
u32 ebx, u32 edx, u32 ecx, u32 eax,
|
||||
u32 cs_ip, u16 stackflags)
|
||||
{
|
||||
u32 ip;
|
||||
u32 cs;
|
||||
u32 flags;
|
||||
int ret = 0;
|
||||
|
||||
ip = cs_ip & 0xffff;
|
||||
cs = cs_ip >> 16;
|
||||
flags = stackflags;
|
||||
|
||||
#ifdef CONFIG_REALMODE_DEBUG
|
||||
debug("oprom: INT# 0x%x\n", intnumber);
|
||||
debug("oprom: eax: %08x ebx: %08x ecx: %08x edx: %08x\n",
|
||||
eax, ebx, ecx, edx);
|
||||
debug("oprom: ebp: %08x esp: %08x edi: %08x esi: %08x\n",
|
||||
ebp, esp, edi, esi);
|
||||
debug("oprom: ip: %04x cs: %04x flags: %08x\n",
|
||||
ip, cs, flags);
|
||||
debug("oprom: stackflags = %04x\n", stackflags);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fetch arguments from the stack and put them to a place
|
||||
* suitable for the interrupt handlers
|
||||
*/
|
||||
M.x86.R_EAX = eax;
|
||||
M.x86.R_ECX = ecx;
|
||||
M.x86.R_EDX = edx;
|
||||
M.x86.R_EBX = ebx;
|
||||
M.x86.R_ESP = esp;
|
||||
M.x86.R_EBP = ebp;
|
||||
M.x86.R_ESI = esi;
|
||||
M.x86.R_EDI = edi;
|
||||
M.x86.intno = intnumber;
|
||||
M.x86.R_EIP = ip;
|
||||
M.x86.R_CS = cs;
|
||||
M.x86.R_EFLG = flags;
|
||||
|
||||
/* Call the interrupt handler for this interrupt number */
|
||||
ret = int_handler[intnumber]();
|
||||
|
||||
/*
|
||||
* This code is quite strange...
|
||||
*
|
||||
* Put registers back on the stack. The assembler code will pop them
|
||||
* later. We force (volatile!) changing the values of the parameters
|
||||
* of this function. We know that they stay alive on the stack after
|
||||
* we leave this function.
|
||||
*/
|
||||
*(volatile u32 *)&eax = M.x86.R_EAX;
|
||||
*(volatile u32 *)&ecx = M.x86.R_ECX;
|
||||
*(volatile u32 *)&edx = M.x86.R_EDX;
|
||||
*(volatile u32 *)&ebx = M.x86.R_EBX;
|
||||
*(volatile u32 *)&esi = M.x86.R_ESI;
|
||||
*(volatile u32 *)&edi = M.x86.R_EDI;
|
||||
flags = M.x86.R_EFLG;
|
||||
|
||||
/* Pass success or error back to our caller via the CARRY flag */
|
||||
if (ret) {
|
||||
flags &= ~1; /* no error: clear carry */
|
||||
} else {
|
||||
debug("int%02x call returned error\n", intnumber);
|
||||
flags |= 1; /* error: set carry */
|
||||
}
|
||||
*(volatile u16 *)&stackflags = flags;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* From Coreboot file device/oprom/realmode/x86.h
|
||||
*
|
||||
* Copyright (C) 2007 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2009-2010 coresystems GmbH
|
||||
*/
|
||||
|
||||
#ifndef _X86_LIB_BIOS_H
|
||||
#define _X86_LIB_BIOS_H
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#define REALMODE_BASE 0x600
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
#define PTR_TO_REAL_MODE(x) (x - asm_realmode_code + REALMODE_BASE)
|
||||
|
||||
#else
|
||||
|
||||
/* Convert a symbol address to our real mode area */
|
||||
#define PTR_TO_REAL_MODE(sym)\
|
||||
(void *)(REALMODE_BASE + ((char *)&(sym) - (char *)&asm_realmode_code))
|
||||
|
||||
/*
|
||||
* The following symbols cannot be used directly. They need to be fixed up
|
||||
* to point to the correct address location after the code has been copied
|
||||
* to REALMODE_BASE. Absolute symbols are not used because those symbols are
|
||||
* relocated by U-Boot.
|
||||
*/
|
||||
extern unsigned char asm_realmode_call, __realmode_interrupt;
|
||||
extern unsigned char asm_realmode_buffer;
|
||||
|
||||
#define DOWNTO8(A) \
|
||||
union { \
|
||||
struct { \
|
||||
union { \
|
||||
struct { \
|
||||
uint8_t A##l; \
|
||||
uint8_t A##h; \
|
||||
} __packed; \
|
||||
uint16_t A##x; \
|
||||
} __packed; \
|
||||
uint16_t h##A##x; \
|
||||
} __packed; \
|
||||
uint32_t e##A##x; \
|
||||
} __packed;
|
||||
|
||||
#define DOWNTO16(A) \
|
||||
union { \
|
||||
struct { \
|
||||
uint16_t A; \
|
||||
uint16_t h##A; \
|
||||
} __packed; \
|
||||
uint32_t e##A; \
|
||||
} __packed;
|
||||
|
||||
struct eregs {
|
||||
DOWNTO8(a);
|
||||
DOWNTO8(c);
|
||||
DOWNTO8(d);
|
||||
DOWNTO8(b);
|
||||
DOWNTO16(sp);
|
||||
DOWNTO16(bp);
|
||||
DOWNTO16(si);
|
||||
DOWNTO16(di);
|
||||
uint32_t vector;
|
||||
uint32_t error_code;
|
||||
uint32_t eip;
|
||||
uint32_t cs;
|
||||
uint32_t eflags;
|
||||
};
|
||||
|
||||
struct realmode_idt {
|
||||
u16 offset, cs;
|
||||
};
|
||||
|
||||
void x86_exception(struct eregs *info);
|
||||
|
||||
/* From x86_asm.S */
|
||||
extern unsigned char __idt_handler;
|
||||
extern unsigned int __idt_handler_size;
|
||||
extern unsigned char asm_realmode_code;
|
||||
extern unsigned int asm_realmode_code_size;
|
||||
|
||||
asmlinkage void (*realmode_call)(u32 addr, u32 eax, u32 ebx, u32 ecx, u32 edx,
|
||||
u32 esi, u32 edi);
|
||||
|
||||
asmlinkage void (*realmode_interrupt)(u32 intno, u32 eax, u32 ebx, u32 ecx,
|
||||
u32 edx, u32 esi, u32 edi);
|
||||
|
||||
int int10_handler(void);
|
||||
int int12_handler(void);
|
||||
int int16_handler(void);
|
||||
int int1a_handler(void);
|
||||
#endif /*__ASSEMBLY__ */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,303 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* From coreboot x86_asm.S, cleaned up substantially
|
||||
*
|
||||
* Copyright (C) 2009-2010 coresystems GmbH
|
||||
*/
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/processor-flags.h>
|
||||
#include "bios.h"
|
||||
|
||||
#define SEG(segment) $segment * X86_GDT_ENTRY_SIZE
|
||||
|
||||
/*
|
||||
* This is the interrupt handler stub code. It gets copied to the IDT and
|
||||
* to some fixed addresses in the F segment. Before the code can used,
|
||||
* it gets patched up by the C function copying it: byte 3 (the $0 in
|
||||
* movb $0, %al) is overwritten with the interrupt numbers.
|
||||
*/
|
||||
|
||||
.code16
|
||||
.globl __idt_handler
|
||||
__idt_handler:
|
||||
pushal
|
||||
movb $0, %al /* This instruction gets modified */
|
||||
ljmp $0, $__interrupt_handler_16bit
|
||||
.globl __idt_handler_size
|
||||
__idt_handler_size:
|
||||
.long . - __idt_handler
|
||||
|
||||
.macro setup_registers
|
||||
/* initial register values */
|
||||
movl 44(%ebp), %eax
|
||||
movl %eax, __registers + 0 /* eax */
|
||||
movl 48(%ebp), %eax
|
||||
movl %eax, __registers + 4 /* ebx */
|
||||
movl 52(%ebp), %eax
|
||||
movl %eax, __registers + 8 /* ecx */
|
||||
movl 56(%ebp), %eax
|
||||
movl %eax, __registers + 12 /* edx */
|
||||
movl 60(%ebp), %eax
|
||||
movl %eax, __registers + 16 /* esi */
|
||||
movl 64(%ebp), %eax
|
||||
movl %eax, __registers + 20 /* edi */
|
||||
.endm
|
||||
|
||||
.macro enter_real_mode
|
||||
/* Activate the right segment descriptor real mode. */
|
||||
ljmp SEG(X86_GDT_ENTRY_16BIT_CS), $PTR_TO_REAL_MODE(1f)
|
||||
1:
|
||||
.code16
|
||||
/*
|
||||
* Load the segment registers with properly configured segment
|
||||
* descriptors. They will retain these configurations (limits,
|
||||
* writability, etc.) once protected mode is turned off.
|
||||
*/
|
||||
mov SEG(X86_GDT_ENTRY_16BIT_DS), %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
mov %ax, %ss
|
||||
|
||||
/* Turn off protection */
|
||||
movl %cr0, %eax
|
||||
andl $~X86_CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
/* Now really going into real mode */
|
||||
ljmp $0, $PTR_TO_REAL_MODE(1f)
|
||||
1:
|
||||
/*
|
||||
* Set up a stack: Put the stack at the end of page zero. That way
|
||||
* we can easily share it between real and protected, since the
|
||||
* 16-bit ESP at segment 0 will work for any case.
|
||||
*/
|
||||
mov $0x0, %ax
|
||||
mov %ax, %ss
|
||||
|
||||
/* Load 16 bit IDT */
|
||||
xor %ax, %ax
|
||||
mov %ax, %ds
|
||||
lidt __realmode_idt
|
||||
|
||||
.endm
|
||||
|
||||
.macro prepare_for_irom
|
||||
movl $0x1000, %eax
|
||||
movl %eax, %esp
|
||||
|
||||
/* Initialise registers for option rom lcall */
|
||||
movl __registers + 0, %eax
|
||||
movl __registers + 4, %ebx
|
||||
movl __registers + 8, %ecx
|
||||
movl __registers + 12, %edx
|
||||
movl __registers + 16, %esi
|
||||
movl __registers + 20, %edi
|
||||
|
||||
/* Set all segments to 0x0000, ds to 0x0040 */
|
||||
push %ax
|
||||
xor %ax, %ax
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
mov SEG(X86_GDT_ENTRY_16BIT_FLAT_DS), %ax
|
||||
mov %ax, %ds
|
||||
pop %ax
|
||||
|
||||
.endm
|
||||
|
||||
.macro enter_protected_mode
|
||||
/* Go back to protected mode */
|
||||
movl %cr0, %eax
|
||||
orl $X86_CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
/* Now that we are in protected mode jump to a 32 bit code segment */
|
||||
data32 ljmp SEG(X86_GDT_ENTRY_32BIT_CS), $PTR_TO_REAL_MODE(1f)
|
||||
1:
|
||||
.code32
|
||||
mov SEG(X86_GDT_ENTRY_32BIT_DS), %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %gs
|
||||
mov %ax, %ss
|
||||
mov SEG(X86_GDT_ENTRY_32BIT_FS), %ax
|
||||
mov %ax, %fs
|
||||
|
||||
/* restore proper idt */
|
||||
lidt idt_ptr
|
||||
.endm
|
||||
|
||||
/*
|
||||
* In order to be independent of U-Boot's position in RAM we relocate a part
|
||||
* of the code to the first megabyte of RAM, so the CPU can use it in
|
||||
* real-mode. This code lives at asm_realmode_code.
|
||||
*/
|
||||
.globl asm_realmode_code
|
||||
asm_realmode_code:
|
||||
|
||||
/* Realmode IDT pointer structure. */
|
||||
__realmode_idt = PTR_TO_REAL_MODE(.)
|
||||
.word 1023 /* 16 bit limit */
|
||||
.long 0 /* 24 bit base */
|
||||
.word 0
|
||||
|
||||
/* Preserve old stack */
|
||||
__stack = PTR_TO_REAL_MODE(.)
|
||||
.long 0
|
||||
|
||||
/* Register store for realmode_call and realmode_interrupt */
|
||||
__registers = PTR_TO_REAL_MODE(.)
|
||||
.long 0 /* 0 - EAX */
|
||||
.long 0 /* 4 - EBX */
|
||||
.long 0 /* 8 - ECX */
|
||||
.long 0 /* 12 - EDX */
|
||||
.long 0 /* 16 - ESI */
|
||||
.long 0 /* 20 - EDI */
|
||||
|
||||
/* 256 byte buffer, used by int10 */
|
||||
.globl asm_realmode_buffer
|
||||
asm_realmode_buffer:
|
||||
.skip 256
|
||||
|
||||
.code32
|
||||
.globl asm_realmode_call
|
||||
asm_realmode_call:
|
||||
/* save all registers to the stack */
|
||||
pusha
|
||||
pushf
|
||||
movl %esp, __stack
|
||||
movl %esp, %ebp
|
||||
|
||||
/*
|
||||
* This function is called with regparm=0 and we have to skip the
|
||||
* 36 bytes from pushf+pusha. Hence start at 40.
|
||||
* Set up our call instruction.
|
||||
*/
|
||||
movl 40(%ebp), %eax
|
||||
mov %ax, __lcall_instr + 1
|
||||
andl $0xffff0000, %eax
|
||||
shrl $4, %eax
|
||||
mov %ax, __lcall_instr + 3
|
||||
|
||||
wbinvd
|
||||
|
||||
setup_registers
|
||||
enter_real_mode
|
||||
prepare_for_irom
|
||||
|
||||
__lcall_instr = PTR_TO_REAL_MODE(.)
|
||||
.byte 0x9a
|
||||
.word 0x0000, 0x0000
|
||||
|
||||
enter_protected_mode
|
||||
|
||||
/* restore stack pointer, eflags and register values and exit */
|
||||
movl __stack, %esp
|
||||
popf
|
||||
popa
|
||||
ret
|
||||
|
||||
.globl __realmode_interrupt
|
||||
__realmode_interrupt:
|
||||
/* save all registers to the stack and store the stack pointer */
|
||||
pusha
|
||||
pushf
|
||||
movl %esp, __stack
|
||||
movl %esp, %ebp
|
||||
|
||||
/*
|
||||
* This function is called with regparm=0 and we have to skip the
|
||||
* 36 bytes from pushf+pusha. Hence start at 40.
|
||||
* Prepare interrupt calling code.
|
||||
*/
|
||||
movl 40(%ebp), %eax
|
||||
movb %al, __intXX_instr + 1 /* intno */
|
||||
|
||||
setup_registers
|
||||
enter_real_mode
|
||||
prepare_for_irom
|
||||
|
||||
__intXX_instr = PTR_TO_REAL_MODE(.)
|
||||
.byte 0xcd, 0x00 /* This becomes intXX */
|
||||
|
||||
enter_protected_mode
|
||||
|
||||
/* restore stack pointer, eflags and register values and exit */
|
||||
movl __stack, %esp
|
||||
popf
|
||||
popa
|
||||
ret
|
||||
|
||||
/*
|
||||
* This is the 16-bit interrupt entry point called by the IDT stub code.
|
||||
*
|
||||
* Before this code code is called, %eax is pushed to the stack, and the
|
||||
* interrupt number is loaded into %al. On return this function cleans up
|
||||
* for its caller.
|
||||
*/
|
||||
.code16
|
||||
__interrupt_handler_16bit = PTR_TO_REAL_MODE(.)
|
||||
push %ds
|
||||
push %es
|
||||
push %fs
|
||||
push %gs
|
||||
|
||||
/* Save real mode SS */
|
||||
movw %ss, %cs:__realmode_ss
|
||||
|
||||
/* Clear DF to not break ABI assumptions */
|
||||
cld
|
||||
|
||||
/*
|
||||
* Clean up the interrupt number. We could do this in the stub, but
|
||||
* it would cost two more bytes per stub entry.
|
||||
*/
|
||||
andl $0xff, %eax
|
||||
pushl %eax /* ... and make it the first parameter */
|
||||
|
||||
enter_protected_mode
|
||||
|
||||
/*
|
||||
* Now we are in protected mode. We need compute the right ESP based
|
||||
* on saved real mode SS otherwise interrupt_handler() won't get
|
||||
* correct parameters from the stack.
|
||||
*/
|
||||
movzwl %cs:__realmode_ss, %ecx
|
||||
shll $4, %ecx
|
||||
addl %ecx, %esp
|
||||
|
||||
/* Call the C interrupt handler */
|
||||
movl $interrupt_handler, %eax
|
||||
call *%eax
|
||||
|
||||
/* Restore real mode ESP based on saved SS */
|
||||
movzwl %cs:__realmode_ss, %ecx
|
||||
shll $4, %ecx
|
||||
subl %ecx, %esp
|
||||
|
||||
enter_real_mode
|
||||
|
||||
/* Restore real mode SS */
|
||||
movw %cs:__realmode_ss, %ss
|
||||
|
||||
/*
|
||||
* Restore all registers, including those manipulated by the C
|
||||
* handler
|
||||
*/
|
||||
popl %eax
|
||||
pop %gs
|
||||
pop %fs
|
||||
pop %es
|
||||
pop %ds
|
||||
popal
|
||||
iret
|
||||
|
||||
__realmode_ss = PTR_TO_REAL_MODE(.)
|
||||
.word 0
|
||||
|
||||
.globl asm_realmode_code_size
|
||||
asm_realmode_code_size:
|
||||
.long . - asm_realmode_code
|
||||
@@ -0,0 +1,218 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* From Coreboot
|
||||
*
|
||||
* Copyright (C) 2001 Ronald G. Minnich
|
||||
* Copyright (C) 2005 Nick.Barker9@btinternet.com
|
||||
* Copyright (C) 2007-2009 coresystems GmbH
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/pci.h>
|
||||
#include "bios_emul.h"
|
||||
|
||||
/* errors go in AH. Just set these up so that word assigns will work */
|
||||
enum {
|
||||
PCIBIOS_SUCCESSFUL = 0x0000,
|
||||
PCIBIOS_UNSUPPORTED = 0x8100,
|
||||
PCIBIOS_BADVENDOR = 0x8300,
|
||||
PCIBIOS_NODEV = 0x8600,
|
||||
PCIBIOS_BADREG = 0x8700
|
||||
};
|
||||
|
||||
int int10_handler(void)
|
||||
{
|
||||
static u8 cursor_row, cursor_col;
|
||||
int res = 0;
|
||||
|
||||
switch ((M.x86.R_EAX & 0xff00) >> 8) {
|
||||
case 0x01: /* Set cursor shape */
|
||||
res = 1;
|
||||
break;
|
||||
case 0x02: /* Set cursor position */
|
||||
if (cursor_row != ((M.x86.R_EDX >> 8) & 0xff) ||
|
||||
cursor_col >= (M.x86.R_EDX & 0xff)) {
|
||||
debug("\n");
|
||||
}
|
||||
cursor_row = (M.x86.R_EDX >> 8) & 0xff;
|
||||
cursor_col = M.x86.R_EDX & 0xff;
|
||||
res = 1;
|
||||
break;
|
||||
case 0x03: /* Get cursor position */
|
||||
M.x86.R_EAX &= 0x00ff;
|
||||
M.x86.R_ECX = 0x0607;
|
||||
M.x86.R_EDX = (cursor_row << 8) | cursor_col;
|
||||
res = 1;
|
||||
break;
|
||||
case 0x06: /* Scroll up */
|
||||
debug("\n");
|
||||
res = 1;
|
||||
break;
|
||||
case 0x08: /* Get Character and Mode at Cursor Position */
|
||||
M.x86.R_EAX = 0x0f00 | 'A'; /* White on black 'A' */
|
||||
res = 1;
|
||||
break;
|
||||
case 0x09: /* Write Character and attribute */
|
||||
case 0x0e: /* Write Character */
|
||||
debug("%c", M.x86.R_EAX & 0xff);
|
||||
res = 1;
|
||||
break;
|
||||
case 0x0f: /* Get video mode */
|
||||
M.x86.R_EAX = 0x5002; /*80 x 25 */
|
||||
M.x86.R_EBX &= 0x00ff;
|
||||
res = 1;
|
||||
break;
|
||||
default:
|
||||
printf("Unknown INT10 function %04x\n", M.x86.R_EAX & 0xffff);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int int12_handler(void)
|
||||
{
|
||||
M.x86.R_EAX = 64 * 1024;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int int16_handler(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
switch ((M.x86.R_EAX & 0xff00) >> 8) {
|
||||
case 0x00: /* Check for Keystroke */
|
||||
M.x86.R_EAX = 0x6120; /* Space Bar, Space */
|
||||
res = 1;
|
||||
break;
|
||||
case 0x01: /* Check for Keystroke */
|
||||
M.x86.R_EFLG |= 1 << 6; /* Zero Flag set (no key available) */
|
||||
res = 1;
|
||||
break;
|
||||
default:
|
||||
printf("Unknown INT16 function %04x\n", M.x86.R_EAX & 0xffff);
|
||||
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define PCI_CONFIG_SPACE_TYPE1 (1 << 0)
|
||||
#define PCI_SPECIAL_CYCLE_TYPE1 (1 << 4)
|
||||
|
||||
int int1a_handler(void)
|
||||
{
|
||||
unsigned short func = (unsigned short)M.x86.R_EAX;
|
||||
int retval = 1;
|
||||
unsigned short devid, vendorid, devfn;
|
||||
struct udevice *dev;
|
||||
/* Use short to get rid of gabage in upper half of 32-bit register */
|
||||
short devindex;
|
||||
unsigned char bus;
|
||||
pci_dev_t bdf;
|
||||
u32 dword;
|
||||
u16 word;
|
||||
u8 byte, reg;
|
||||
int ret;
|
||||
|
||||
switch (func) {
|
||||
case 0xb101: /* PCIBIOS Check */
|
||||
M.x86.R_EDX = 0x20494350; /* ' ICP' */
|
||||
M.x86.R_EAX &= 0xffff0000; /* Clear AH / AL */
|
||||
M.x86.R_EAX |= PCI_CONFIG_SPACE_TYPE1 |
|
||||
PCI_SPECIAL_CYCLE_TYPE1;
|
||||
/*
|
||||
* last bus in the system. Hard code to 255 for now.
|
||||
* dev_enumerate() does not seem to tell us (publically)
|
||||
*/
|
||||
M.x86.R_ECX = 0xff;
|
||||
M.x86.R_EDI = 0x00000000; /* protected mode entry */
|
||||
retval = 1;
|
||||
break;
|
||||
case 0xb102: /* Find Device */
|
||||
devid = M.x86.R_ECX;
|
||||
vendorid = M.x86.R_EDX;
|
||||
devindex = M.x86.R_ESI;
|
||||
bdf = -1;
|
||||
ret = dm_pci_find_device(vendorid, devid, devindex, &dev);
|
||||
if (!ret) {
|
||||
unsigned short busdevfn;
|
||||
|
||||
bdf = dm_pci_get_bdf(dev);
|
||||
M.x86.R_EAX &= 0xffff00ff; /* Clear AH */
|
||||
M.x86.R_EAX |= PCIBIOS_SUCCESSFUL;
|
||||
/*
|
||||
* busnum is an unsigned char;
|
||||
* devfn is an int, so we mask it off.
|
||||
*/
|
||||
busdevfn = (PCI_BUS(bdf) << 8) | PCI_DEV(bdf) << 3 |
|
||||
PCI_FUNC(bdf);
|
||||
debug("0x%x: return 0x%x\n", func, busdevfn);
|
||||
M.x86.R_EBX = busdevfn;
|
||||
retval = 1;
|
||||
} else {
|
||||
M.x86.R_EAX &= 0xffff00ff; /* Clear AH */
|
||||
M.x86.R_EAX |= PCIBIOS_NODEV;
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
case 0xb10a: /* Read Config Dword */
|
||||
case 0xb109: /* Read Config Word */
|
||||
case 0xb108: /* Read Config Byte */
|
||||
case 0xb10d: /* Write Config Dword */
|
||||
case 0xb10c: /* Write Config Word */
|
||||
case 0xb10b: /* Write Config Byte */
|
||||
devfn = M.x86.R_EBX & 0xff;
|
||||
bus = M.x86.R_EBX >> 8;
|
||||
reg = M.x86.R_EDI;
|
||||
bdf = PCI_BDF(bus, devfn >> 3, devfn & 7);
|
||||
|
||||
ret = dm_pci_bus_find_bdf(bdf, &dev);
|
||||
if (ret) {
|
||||
debug("%s: Device %x not found\n", __func__, bdf);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (func) {
|
||||
case 0xb108: /* Read Config Byte */
|
||||
dm_pci_read_config8(dev, reg, &byte);
|
||||
M.x86.R_ECX = byte;
|
||||
break;
|
||||
case 0xb109: /* Read Config Word */
|
||||
dm_pci_read_config16(dev, reg, &word);
|
||||
M.x86.R_ECX = word;
|
||||
break;
|
||||
case 0xb10a: /* Read Config Dword */
|
||||
dm_pci_read_config32(dev, reg, &dword);
|
||||
M.x86.R_ECX = dword;
|
||||
break;
|
||||
case 0xb10b: /* Write Config Byte */
|
||||
byte = M.x86.R_ECX;
|
||||
dm_pci_write_config8(dev, reg, byte);
|
||||
break;
|
||||
case 0xb10c: /* Write Config Word */
|
||||
word = M.x86.R_ECX;
|
||||
dm_pci_write_config16(dev, reg, word);
|
||||
break;
|
||||
case 0xb10d: /* Write Config Dword */
|
||||
dword = M.x86.R_ECX;
|
||||
dm_pci_write_config32(dev, reg, dword);
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_REALMODE_DEBUG
|
||||
debug("0x%x: bus %d devfn 0x%x reg 0x%x val 0x%x\n", func,
|
||||
bus, devfn, reg, M.x86.R_ECX);
|
||||
#endif
|
||||
M.x86.R_EAX &= 0xffff00ff; /* Clear AH */
|
||||
M.x86.R_EAX |= PCIBIOS_SUCCESSFUL;
|
||||
retval = 1;
|
||||
break;
|
||||
default:
|
||||
printf("UNSUPPORTED PCIBIOS FUNCTION 0x%x\n", func);
|
||||
M.x86.R_EAX &= 0xffff00ff; /* Clear AH */
|
||||
M.x86.R_EAX |= PCIBIOS_UNSUPPORTED;
|
||||
retval = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2002
|
||||
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
* Marius Groeger <mgroeger@sysgo.de>
|
||||
*
|
||||
* Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <dm/device.h>
|
||||
#include <dm/root.h>
|
||||
#include <errno.h>
|
||||
#include <fdt_support.h>
|
||||
#include <image.h>
|
||||
#include <u-boot/zlib.h>
|
||||
#include <asm/bootparam.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/zimage.h>
|
||||
#ifdef CONFIG_SYS_COREBOOT
|
||||
#include <asm/arch/timestamp.h>
|
||||
#endif
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define COMMAND_LINE_OFFSET 0x9000
|
||||
|
||||
void bootm_announce_and_cleanup(void)
|
||||
{
|
||||
printf("\nStarting kernel ...\n\n");
|
||||
|
||||
#ifdef CONFIG_SYS_COREBOOT
|
||||
timestamp_add_now(TS_U_BOOT_START_KERNEL);
|
||||
#endif
|
||||
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
|
||||
#if CONFIG_IS_ENABLED(BOOTSTAGE_REPORT)
|
||||
bootstage_report();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Call remove function of all devices with a removal flag set.
|
||||
* This may be useful for last-stage operations, like cancelling
|
||||
* of DMA operation or releasing device internal buffers.
|
||||
*/
|
||||
dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL)
|
||||
int arch_fixup_memory_node(void *blob)
|
||||
{
|
||||
bd_t *bd = gd->bd;
|
||||
int bank;
|
||||
u64 start[CONFIG_NR_DRAM_BANKS];
|
||||
u64 size[CONFIG_NR_DRAM_BANKS];
|
||||
|
||||
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
|
||||
start[bank] = bd->bi_dram[bank].start;
|
||||
size[bank] = bd->bi_dram[bank].size;
|
||||
}
|
||||
|
||||
return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Subcommand: PREP */
|
||||
static int boot_prep_linux(bootm_headers_t *images)
|
||||
{
|
||||
char *cmd_line_dest = NULL;
|
||||
image_header_t *hdr;
|
||||
int is_zimage = 0;
|
||||
void *data = NULL;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_OF_LIBFDT
|
||||
if (images->ft_len) {
|
||||
debug("using: FDT\n");
|
||||
if (image_setup_linux(images)) {
|
||||
puts("FDT creation failed! hanging...");
|
||||
hang();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (images->legacy_hdr_valid) {
|
||||
hdr = images->legacy_hdr_os;
|
||||
if (image_check_type(hdr, IH_TYPE_MULTI)) {
|
||||
ulong os_data, os_len;
|
||||
|
||||
/* if multi-part image, we need to get first subimage */
|
||||
image_multi_getimg(hdr, 0, &os_data, &os_len);
|
||||
data = (void *)os_data;
|
||||
len = os_len;
|
||||
} else {
|
||||
/* otherwise get image data */
|
||||
data = (void *)image_get_data(hdr);
|
||||
len = image_get_data_size(hdr);
|
||||
}
|
||||
is_zimage = 1;
|
||||
#if defined(CONFIG_FIT)
|
||||
} else if (images->fit_uname_os && is_zimage) {
|
||||
ret = fit_image_get_data(images->fit_hdr_os,
|
||||
images->fit_noffset_os,
|
||||
(const void **)&data, &len);
|
||||
if (ret) {
|
||||
puts("Can't get image data/size!\n");
|
||||
goto error;
|
||||
}
|
||||
is_zimage = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (is_zimage) {
|
||||
ulong load_address;
|
||||
char *base_ptr;
|
||||
|
||||
base_ptr = (char *)load_zimage(data, len, &load_address);
|
||||
if (!base_ptr) {
|
||||
puts("## Kernel loading failed ...\n");
|
||||
goto error;
|
||||
}
|
||||
images->os.load = load_address;
|
||||
cmd_line_dest = base_ptr + COMMAND_LINE_OFFSET;
|
||||
images->ep = (ulong)base_ptr;
|
||||
} else if (images->ep) {
|
||||
cmd_line_dest = (void *)images->ep + COMMAND_LINE_OFFSET;
|
||||
} else {
|
||||
printf("## Kernel loading failed (missing x86 kernel setup) ...\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
printf("Setup at %#08lx\n", images->ep);
|
||||
ret = setup_zimage((void *)images->ep, cmd_line_dest,
|
||||
0, images->rd_start,
|
||||
images->rd_end - images->rd_start);
|
||||
|
||||
if (ret) {
|
||||
printf("## Setting up boot parameters failed ...\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int boot_linux_kernel(ulong setup_base, ulong load_address, bool image_64bit)
|
||||
{
|
||||
bootm_announce_and_cleanup();
|
||||
|
||||
#ifdef CONFIG_SYS_COREBOOT
|
||||
timestamp_add_now(TS_U_BOOT_START_KERNEL);
|
||||
#endif
|
||||
if (image_64bit) {
|
||||
if (!cpu_has_64bit()) {
|
||||
puts("Cannot boot 64-bit kernel on 32-bit machine\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
/* At present 64-bit U-Boot does not support booting a
|
||||
* kernel.
|
||||
* TODO(sjg@chromium.org): Support booting both 32-bit and
|
||||
* 64-bit kernels from 64-bit U-Boot.
|
||||
*/
|
||||
#if !CONFIG_IS_ENABLED(X86_64)
|
||||
return cpu_jump_to_64bit(setup_base, load_address);
|
||||
#endif
|
||||
} else {
|
||||
/*
|
||||
* Set %ebx, %ebp, and %edi to 0, %esi to point to the
|
||||
* boot_params structure, and then jump to the kernel. We
|
||||
* assume that %cs is 0x10, 4GB flat, and read/execute, and
|
||||
* the data segments are 0x18, 4GB flat, and read/write.
|
||||
* U-Boot is setting them up that way for itself in
|
||||
* arch/i386/cpu/cpu.c.
|
||||
*
|
||||
* Note that we cannot currently boot a kernel while running as
|
||||
* an EFI application. Please use the payload option for that.
|
||||
*/
|
||||
#ifndef CONFIG_EFI_APP
|
||||
__asm__ __volatile__ (
|
||||
"movl $0, %%ebp\n"
|
||||
"cli\n"
|
||||
"jmp *%[kernel_entry]\n"
|
||||
:: [kernel_entry]"a"(load_address),
|
||||
[boot_params] "S"(setup_base),
|
||||
"b"(0), "D"(0)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* We can't get to here */
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Subcommand: GO */
|
||||
static int boot_jump_linux(bootm_headers_t *images)
|
||||
{
|
||||
debug("## Transferring control to Linux (at address %08lx, kernel %08lx) ...\n",
|
||||
images->ep, images->os.load);
|
||||
|
||||
return boot_linux_kernel(images->ep, images->os.load,
|
||||
images->os.arch == IH_ARCH_X86_64);
|
||||
}
|
||||
|
||||
int do_bootm_linux(int flag, int argc, char * const argv[],
|
||||
bootm_headers_t *images)
|
||||
{
|
||||
/* No need for those on x86 */
|
||||
if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
|
||||
return -1;
|
||||
|
||||
if (flag & BOOTM_STATE_OS_PREP)
|
||||
return boot_prep_linux(images);
|
||||
|
||||
if (flag & BOOTM_STATE_OS_GO)
|
||||
return boot_jump_linux(images);
|
||||
|
||||
return boot_jump_linux(images);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2008-2011
|
||||
* Graeme Russ, <graeme.russ@gmail.com>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
* Marius Groeger <mgroeger@sysgo.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <command.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/u-boot-x86.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
unsigned long do_go_exec(ulong (*entry)(int, char * const []),
|
||||
int argc, char * const argv[])
|
||||
{
|
||||
unsigned long ret = 0;
|
||||
char **argv_tmp;
|
||||
|
||||
/*
|
||||
* x86 does not use a dedicated register to pass the pointer to
|
||||
* the global_data, so it is instead passed as argv[-1]. By using
|
||||
* argv[-1], the called 'Application' can use the contents of
|
||||
* argv natively. However, to safely use argv[-1] a new copy of
|
||||
* argv is needed with the extra element
|
||||
*/
|
||||
argv_tmp = malloc(sizeof(char *) * (argc + 1));
|
||||
|
||||
if (argv_tmp) {
|
||||
argv_tmp[0] = (char *)gd;
|
||||
|
||||
memcpy(&argv_tmp[1], argv, (size_t)(sizeof(char *) * argc));
|
||||
|
||||
ret = (entry) (argc, &argv_tmp[1]);
|
||||
free(argv_tmp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <acpi_s3.h>
|
||||
#include <vbe.h>
|
||||
#include <asm/coreboot_tables.h>
|
||||
#include <asm/e820.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int high_table_reserve(void)
|
||||
{
|
||||
/* adjust stack pointer to reserve space for configuration tables */
|
||||
gd->arch.high_table_limit = gd->start_addr_sp;
|
||||
gd->start_addr_sp -= CONFIG_HIGH_TABLE_SIZE;
|
||||
gd->arch.high_table_ptr = gd->start_addr_sp;
|
||||
|
||||
/* clear the memory */
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
if (gd->arch.prev_sleep_state != ACPI_S3)
|
||||
#endif
|
||||
memset((void *)gd->arch.high_table_ptr, 0,
|
||||
CONFIG_HIGH_TABLE_SIZE);
|
||||
|
||||
gd->start_addr_sp &= ~0xf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *high_table_malloc(size_t bytes)
|
||||
{
|
||||
u32 new_ptr;
|
||||
void *ptr;
|
||||
|
||||
new_ptr = gd->arch.high_table_ptr + bytes;
|
||||
if (new_ptr >= gd->arch.high_table_limit)
|
||||
return NULL;
|
||||
ptr = (void *)gd->arch.high_table_ptr;
|
||||
gd->arch.high_table_ptr = new_ptr;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* cb_table_init() - initialize a coreboot table header
|
||||
*
|
||||
* This fills in the coreboot table header signature and the header bytes.
|
||||
* Other fields are set to zero.
|
||||
*
|
||||
* @cbh: coreboot table header address
|
||||
*/
|
||||
static void cb_table_init(struct cb_header *cbh)
|
||||
{
|
||||
memset(cbh, 0, sizeof(struct cb_header));
|
||||
memcpy(cbh->signature, "LBIO", 4);
|
||||
cbh->header_bytes = sizeof(struct cb_header);
|
||||
}
|
||||
|
||||
/**
|
||||
* cb_table_add_entry() - add a coreboot table entry
|
||||
*
|
||||
* This increases the coreboot table entry size with added table entry length
|
||||
* and increases entry count by 1.
|
||||
*
|
||||
* @cbh: coreboot table header address
|
||||
* @cbr: to be added table entry address
|
||||
* @return: pointer to next table entry address
|
||||
*/
|
||||
static u32 cb_table_add_entry(struct cb_header *cbh, struct cb_record *cbr)
|
||||
{
|
||||
cbh->table_bytes += cbr->size;
|
||||
cbh->table_entries++;
|
||||
|
||||
return (u32)cbr + cbr->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* cb_table_finalize() - finalize the coreboot table
|
||||
*
|
||||
* This calculates the checksum for all coreboot table entries as well as
|
||||
* the checksum for the coreboot header itself.
|
||||
*
|
||||
* @cbh: coreboot table header address
|
||||
*/
|
||||
static void cb_table_finalize(struct cb_header *cbh)
|
||||
{
|
||||
struct cb_record *cbr = (struct cb_record *)(cbh + 1);
|
||||
|
||||
cbh->table_checksum = compute_ip_checksum(cbr, cbh->table_bytes);
|
||||
cbh->header_checksum = compute_ip_checksum(cbh, cbh->header_bytes);
|
||||
}
|
||||
|
||||
void write_coreboot_table(u32 addr, struct memory_area *cfg_tables)
|
||||
{
|
||||
struct cb_header *cbh = (struct cb_header *)addr;
|
||||
struct cb_record *cbr;
|
||||
struct cb_memory *mem;
|
||||
struct cb_memory_range *map;
|
||||
struct e820_entry e820[32];
|
||||
struct cb_framebuffer *fb;
|
||||
struct vesa_mode_info *vesa;
|
||||
int i, num;
|
||||
|
||||
cb_table_init(cbh);
|
||||
cbr = (struct cb_record *)(cbh + 1);
|
||||
|
||||
/*
|
||||
* Two type of coreboot table entries are generated by us.
|
||||
* They are 'struct cb_memory' and 'struct cb_framebuffer'.
|
||||
*/
|
||||
|
||||
/* populate memory map table */
|
||||
mem = (struct cb_memory *)cbr;
|
||||
mem->tag = CB_TAG_MEMORY;
|
||||
map = mem->map;
|
||||
|
||||
/* first install e820 defined memory maps */
|
||||
num = install_e820_map(ARRAY_SIZE(e820), e820);
|
||||
for (i = 0; i < num; i++) {
|
||||
map->start.lo = e820[i].addr & 0xffffffff;
|
||||
map->start.hi = e820[i].addr >> 32;
|
||||
map->size.lo = e820[i].size & 0xffffffff;
|
||||
map->size.hi = e820[i].size >> 32;
|
||||
map->type = e820[i].type;
|
||||
map++;
|
||||
}
|
||||
|
||||
/* then install all configuration tables */
|
||||
while (cfg_tables->size) {
|
||||
map->start.lo = cfg_tables->start & 0xffffffff;
|
||||
map->start.hi = cfg_tables->start >> 32;
|
||||
map->size.lo = cfg_tables->size & 0xffffffff;
|
||||
map->size.hi = cfg_tables->size >> 32;
|
||||
map->type = CB_MEM_TABLE;
|
||||
map++;
|
||||
num++;
|
||||
cfg_tables++;
|
||||
}
|
||||
mem->size = num * sizeof(struct cb_memory_range) +
|
||||
sizeof(struct cb_record);
|
||||
cbr = (struct cb_record *)cb_table_add_entry(cbh, cbr);
|
||||
|
||||
/* populate framebuffer table if we have sane vesa info */
|
||||
vesa = &mode_info.vesa;
|
||||
if (vesa->x_resolution && vesa->y_resolution) {
|
||||
fb = (struct cb_framebuffer *)cbr;
|
||||
fb->tag = CB_TAG_FRAMEBUFFER;
|
||||
fb->size = sizeof(struct cb_framebuffer);
|
||||
|
||||
fb->x_resolution = vesa->x_resolution;
|
||||
fb->y_resolution = vesa->y_resolution;
|
||||
fb->bits_per_pixel = vesa->bits_per_pixel;
|
||||
fb->bytes_per_line = vesa->bytes_per_scanline;
|
||||
fb->physical_address = vesa->phys_base_ptr;
|
||||
fb->red_mask_size = vesa->red_mask_size;
|
||||
fb->red_mask_pos = vesa->red_mask_pos;
|
||||
fb->green_mask_size = vesa->green_mask_size;
|
||||
fb->green_mask_pos = vesa->green_mask_pos;
|
||||
fb->blue_mask_size = vesa->blue_mask_size;
|
||||
fb->blue_mask_pos = vesa->blue_mask_pos;
|
||||
fb->reserved_mask_size = vesa->reserved_mask_size;
|
||||
fb->reserved_mask_pos = vesa->reserved_mask_pos;
|
||||
|
||||
cbr = (struct cb_record *)cb_table_add_entry(cbh, cbr);
|
||||
}
|
||||
|
||||
cb_table_finalize(cbh);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
/*
|
||||
* crt0-efi-ia32.S - x86 EFI startup code.
|
||||
*
|
||||
* Copyright (C) 1999 Hewlett-Packard Co.
|
||||
* Contributed by David Mosberger <davidm@hpl.hp.com>.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
.text
|
||||
.align 4
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
pushl %ebp
|
||||
movl %esp,%ebp
|
||||
|
||||
pushl 12(%ebp) # copy "image" argument
|
||||
pushl 8(%ebp) # copy "systab" argument
|
||||
|
||||
call 0f
|
||||
0: popl %eax
|
||||
movl %eax,%ebx
|
||||
|
||||
addl $image_base-0b,%eax # %eax = ldbase
|
||||
addl $_DYNAMIC-0b,%ebx # %ebx = _DYNAMIC
|
||||
|
||||
pushl %ebx # pass _DYNAMIC as second argument
|
||||
pushl %eax # pass ldbase as first argument
|
||||
call _relocate
|
||||
popl %ebx
|
||||
popl %ebx
|
||||
testl %eax,%eax
|
||||
jne .exit
|
||||
call efi_main # call app with "image" and "systab" argument
|
||||
|
||||
.exit: leave
|
||||
ret
|
||||
|
||||
/*
|
||||
* hand-craft a dummy .reloc section so EFI knows it's a relocatable
|
||||
* executable:
|
||||
*/
|
||||
.data
|
||||
dummy: .long 0
|
||||
|
||||
#define IMAGE_REL_ABSOLUTE 0
|
||||
.section .reloc
|
||||
.long dummy /* Page RVA */
|
||||
.long 10 /* Block Size (2*4+2) */
|
||||
.word (IMAGE_REL_ABSOLUTE << 12) + 0 /* reloc for dummy */
|
||||
@@ -0,0 +1,50 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
/*
|
||||
* crt0-efi-x86_64.S - x86_64 EFI startup code.
|
||||
* Copyright (C) 1999 Hewlett-Packard Co.
|
||||
* Contributed by David Mosberger <davidm@hpl.hp.com>.
|
||||
* Copyright (C) 2005 Intel Corporation
|
||||
* Contributed by Fenghua Yu <fenghua.yu@intel.com>.
|
||||
*
|
||||
* All rights reserved.
|
||||
*/
|
||||
.text
|
||||
.align 4
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
subq $8, %rsp
|
||||
|
||||
pushq %rcx
|
||||
pushq %rdx
|
||||
|
||||
lea image_base(%rip), %rcx
|
||||
lea _DYNAMIC(%rip), %rdx
|
||||
|
||||
call _relocate
|
||||
|
||||
popq %rdx
|
||||
popq %rcx
|
||||
|
||||
testq %rax, %rax
|
||||
jnz .exit
|
||||
|
||||
call efi_main
|
||||
.exit:
|
||||
addq $8, %rsp
|
||||
|
||||
ret
|
||||
|
||||
/*
|
||||
* hand-craft a dummy .reloc section so EFI knows it's a relocatable
|
||||
* executable:
|
||||
*/
|
||||
.data
|
||||
dummy: .long 0
|
||||
|
||||
#define IMAGE_REL_ABSOLUTE 0
|
||||
.section .reloc, "a"
|
||||
label1:
|
||||
.long dummy-label1 /* Page RVA */
|
||||
.long 10 /* Block Size (2*4+2) */
|
||||
.word (IMAGE_REL_ABSOLUTE << 12) + 0 /* reloc for dummy */
|
||||
@@ -0,0 +1,112 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* This file is copied from the coreboot repository as part of
|
||||
* the libpayload project:
|
||||
*
|
||||
* Copyright 2014 Google Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
union overlay64 {
|
||||
u64 longw;
|
||||
struct {
|
||||
u32 lower;
|
||||
u32 higher;
|
||||
} words;
|
||||
};
|
||||
|
||||
u64 __ashldi3(u64 num, unsigned int shift)
|
||||
{
|
||||
union overlay64 output;
|
||||
|
||||
output.longw = num;
|
||||
if (shift >= 32) {
|
||||
output.words.higher = output.words.lower << (shift - 32);
|
||||
output.words.lower = 0;
|
||||
} else {
|
||||
if (!shift)
|
||||
return num;
|
||||
output.words.higher = (output.words.higher << shift) |
|
||||
(output.words.lower >> (32 - shift));
|
||||
output.words.lower = output.words.lower << shift;
|
||||
}
|
||||
return output.longw;
|
||||
}
|
||||
|
||||
u64 __lshrdi3(u64 num, unsigned int shift)
|
||||
{
|
||||
union overlay64 output;
|
||||
|
||||
output.longw = num;
|
||||
if (shift >= 32) {
|
||||
output.words.lower = output.words.higher >> (shift - 32);
|
||||
output.words.higher = 0;
|
||||
} else {
|
||||
if (!shift)
|
||||
return num;
|
||||
output.words.lower = output.words.lower >> shift |
|
||||
(output.words.higher << (32 - shift));
|
||||
output.words.higher = output.words.higher >> shift;
|
||||
}
|
||||
return output.longw;
|
||||
}
|
||||
|
||||
#define MAX_32BIT_UINT ((((u64)1) << 32) - 1)
|
||||
|
||||
static u64 _64bit_divide(u64 dividend, u64 divider, u64 *rem_p)
|
||||
{
|
||||
u64 result = 0;
|
||||
|
||||
/*
|
||||
* If divider is zero - let the rest of the system care about the
|
||||
* exception.
|
||||
*/
|
||||
if (!divider)
|
||||
return 1 / (u32)divider;
|
||||
|
||||
/* As an optimization, let's not use 64 bit division unless we must. */
|
||||
if (dividend <= MAX_32BIT_UINT) {
|
||||
if (divider > MAX_32BIT_UINT) {
|
||||
result = 0;
|
||||
if (rem_p)
|
||||
*rem_p = divider;
|
||||
} else {
|
||||
result = (u32)dividend / (u32)divider;
|
||||
if (rem_p)
|
||||
*rem_p = (u32)dividend % (u32)divider;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
while (divider <= dividend) {
|
||||
u64 locald = divider;
|
||||
u64 limit = __lshrdi3(dividend, 1);
|
||||
int shifts = 0;
|
||||
|
||||
while (locald <= limit) {
|
||||
shifts++;
|
||||
locald = locald + locald;
|
||||
}
|
||||
result |= __ashldi3(1, shifts);
|
||||
dividend -= locald;
|
||||
}
|
||||
|
||||
if (rem_p)
|
||||
*rem_p = dividend;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u64 __udivdi3(u64 num, u64 den)
|
||||
{
|
||||
return _64bit_divide(num, den, NULL);
|
||||
}
|
||||
|
||||
u64 __umoddi3(u64 num, u64 den)
|
||||
{
|
||||
u64 v = 0;
|
||||
|
||||
_64bit_divide(num, den, &v);
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi_loader.h>
|
||||
#include <asm/e820.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* Install a default e820 table with 4 entries as follows:
|
||||
*
|
||||
* 0x000000-0x0a0000 Useable RAM
|
||||
* 0x0a0000-0x100000 Reserved for ISA
|
||||
* 0x100000-gd->ram_size Useable RAM
|
||||
* CONFIG_PCIE_ECAM_BASE PCIe ECAM
|
||||
*/
|
||||
__weak unsigned int install_e820_map(unsigned int max_entries,
|
||||
struct e820_entry *entries)
|
||||
{
|
||||
entries[0].addr = 0;
|
||||
entries[0].size = ISA_START_ADDRESS;
|
||||
entries[0].type = E820_RAM;
|
||||
entries[1].addr = ISA_START_ADDRESS;
|
||||
entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS;
|
||||
entries[1].type = E820_RESERVED;
|
||||
entries[2].addr = ISA_END_ADDRESS;
|
||||
entries[2].size = gd->ram_size - ISA_END_ADDRESS;
|
||||
entries[2].type = E820_RAM;
|
||||
entries[3].addr = CONFIG_PCIE_ECAM_BASE;
|
||||
entries[3].size = CONFIG_PCIE_ECAM_SIZE;
|
||||
entries[3].type = E820_RESERVED;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(EFI_LOADER)
|
||||
void efi_add_known_memory(void)
|
||||
{
|
||||
struct e820_entry e820[E820MAX];
|
||||
unsigned int i, num;
|
||||
u64 start, pages, ram_top;
|
||||
int type;
|
||||
|
||||
num = install_e820_map(ARRAY_SIZE(e820), e820);
|
||||
|
||||
ram_top = (u64)gd->ram_top & ~EFI_PAGE_MASK;
|
||||
if (!ram_top)
|
||||
ram_top = 0x100000000ULL;
|
||||
|
||||
for (i = 0; i < num; ++i) {
|
||||
start = e820[i].addr;
|
||||
|
||||
switch (e820[i].type) {
|
||||
case E820_RAM:
|
||||
type = EFI_CONVENTIONAL_MEMORY;
|
||||
break;
|
||||
case E820_RESERVED:
|
||||
type = EFI_RESERVED_MEMORY_TYPE;
|
||||
break;
|
||||
case E820_ACPI:
|
||||
type = EFI_ACPI_RECLAIM_MEMORY;
|
||||
break;
|
||||
case E820_NVS:
|
||||
type = EFI_ACPI_MEMORY_NVS;
|
||||
break;
|
||||
case E820_UNUSABLE:
|
||||
default:
|
||||
type = EFI_UNUSABLE_MEMORY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == EFI_CONVENTIONAL_MEMORY) {
|
||||
efi_add_conventional_memory_map(start,
|
||||
start + e820[i].size,
|
||||
ram_top);
|
||||
} else {
|
||||
pages = ALIGN(e820[i].size, EFI_PAGE_SIZE)
|
||||
>> EFI_PAGE_SHIFT;
|
||||
efi_add_memory_map(start, pages, type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_IS_ENABLED(EFI_LOADER) */
|
||||
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This library provides CMOS (inside RTC SRAM) access routines at a very
|
||||
* early stage when driver model is not available yet. Only read access is
|
||||
* provided. The 16-bit/32-bit read are compatible with driver model RTC
|
||||
* uclass write ops, that data is stored in little-endian mode.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/early_cmos.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
u8 cmos_read8(u8 addr)
|
||||
{
|
||||
outb(addr, CMOS_IO_PORT);
|
||||
|
||||
return inb(CMOS_IO_PORT + 1);
|
||||
}
|
||||
|
||||
u16 cmos_read16(u8 addr)
|
||||
{
|
||||
u16 value = 0;
|
||||
u16 data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(value); i++) {
|
||||
data = cmos_read8(addr + i);
|
||||
value |= data << (i << 3);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
u32 cmos_read32(u8 addr)
|
||||
{
|
||||
u32 value = 0;
|
||||
u32 data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(value); i++) {
|
||||
data = cmos_read8(addr + i);
|
||||
value |= data << (i << 3);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* U-Boot EFI linker script
|
||||
*
|
||||
* Modified from usr/lib32/elf_ia32_efi.lds in gnu-efi
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
image_base = .;
|
||||
.hash : { *(.hash) } /* this MUST come first, EFI expects it */
|
||||
. = ALIGN(4096);
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
.sdata :
|
||||
{
|
||||
*(.got.plt)
|
||||
*(.got)
|
||||
*(.srodata)
|
||||
*(.sdata)
|
||||
*(.sbss)
|
||||
*(.scommon)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
.data :
|
||||
{
|
||||
*(.rodata*)
|
||||
*(.data)
|
||||
*(.data1)
|
||||
*(.data.*)
|
||||
*(.sdata)
|
||||
*(.got.plt)
|
||||
*(.got)
|
||||
/*
|
||||
* the EFI loader doesn't seem to like a .bss section, so we
|
||||
* stick it all into .data:
|
||||
*/
|
||||
*(.sbss)
|
||||
*(.scommon)
|
||||
*(.dynbss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
|
||||
/* U-Boot lists and device tree */
|
||||
. = ALIGN(8);
|
||||
*(SORT(.u_boot_list*));
|
||||
. = ALIGN(8);
|
||||
*(.dtb*);
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
.dynamic : { *(.dynamic) }
|
||||
. = ALIGN(4096);
|
||||
.rel :
|
||||
{
|
||||
*(.rel.data)
|
||||
*(.rel.data.*)
|
||||
*(.rel.got)
|
||||
*(.rel.stab)
|
||||
*(.data.rel.ro.local)
|
||||
*(.data.rel.local)
|
||||
*(.data.rel.ro)
|
||||
*(.data.rel*)
|
||||
*(.rel.u_boot_list*)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
.reloc : /* This is the PECOFF .reloc section! */
|
||||
{
|
||||
*(.reloc)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
.dynsym : { *(.dynsym) }
|
||||
. = ALIGN(4096);
|
||||
.dynstr : { *(.dynstr) }
|
||||
. = ALIGN(4096);
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.rel.reloc)
|
||||
*(.eh_frame)
|
||||
*(.note.GNU-stack)
|
||||
}
|
||||
.comment 0 : { *(.comment) }
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* U-Boot EFI linker script
|
||||
*
|
||||
* Modified from usr/lib32/elf_x86_64_efi.lds in gnu-efi
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
|
||||
OUTPUT_ARCH(i386:x86-64)
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
image_base = .;
|
||||
.hash : { *(.hash) } /* this MUST come first, EFI expects it */
|
||||
. = ALIGN(4096);
|
||||
.eh_frame : {
|
||||
*(.eh_frame)
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
|
||||
.text : {
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
|
||||
.reloc : {
|
||||
*(.reloc)
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
|
||||
.data : {
|
||||
*(.rodata*)
|
||||
*(.got.plt)
|
||||
*(.got)
|
||||
*(.data*)
|
||||
*(.sdata)
|
||||
/* the EFI loader doesn't seem to like a .bss section, so we stick
|
||||
* it all into .data: */
|
||||
*(.sbss)
|
||||
*(.scommon)
|
||||
*(.dynbss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
*(.rel.local)
|
||||
|
||||
/* U-Boot lists and device tree */
|
||||
. = ALIGN(8);
|
||||
*(SORT(.u_boot_list*));
|
||||
. = ALIGN(8);
|
||||
*(.dtb*);
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
.dynamic : { *(.dynamic) }
|
||||
. = ALIGN(4096);
|
||||
|
||||
.rela : {
|
||||
*(.rela.data*)
|
||||
*(.rela.got)
|
||||
*(.rela.stab)
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
.dynsym : { *(.dynsym) }
|
||||
. = ALIGN(4096);
|
||||
.dynstr : { *(.dynstr) }
|
||||
. = ALIGN(4096);
|
||||
.ignored.reloc : {
|
||||
*(.rela.reloc)
|
||||
*(.eh_frame)
|
||||
*(.note.GNU-stack)
|
||||
}
|
||||
|
||||
.comment 0 : { *(.comment) }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright 2019 Google LLC
|
||||
|
||||
obj-y += fsp_common.o
|
||||
obj-y += fsp_dram.o
|
||||
obj-y += fsp_support.o
|
||||
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <acpi_s3.h>
|
||||
#include <cpu_func.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <rtc.h>
|
||||
#include <asm/cmos_layout.h>
|
||||
#include <asm/early_cmos.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mrccache.h>
|
||||
#include <asm/post.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/fsp/fsp_support.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int checkcpu(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int print_cpuinfo(void)
|
||||
{
|
||||
post_code(POST_CPU_INFO);
|
||||
return default_print_cpuinfo();
|
||||
}
|
||||
|
||||
int fsp_init_phase_pci(void)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
/* call into FspNotify */
|
||||
debug("Calling into FSP (notify phase INIT_PHASE_PCI): ");
|
||||
status = fsp_notify(NULL, INIT_PHASE_PCI);
|
||||
if (status)
|
||||
debug("fail, error code %x\n", status);
|
||||
else
|
||||
debug("OK\n");
|
||||
|
||||
return status ? -EPERM : 0;
|
||||
}
|
||||
|
||||
void board_final_cleanup(void)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
/* call into FspNotify */
|
||||
debug("Calling into FSP (notify phase INIT_PHASE_BOOT): ");
|
||||
status = fsp_notify(NULL, INIT_PHASE_BOOT);
|
||||
if (status)
|
||||
debug("fail, error code %x\n", status);
|
||||
else
|
||||
debug("OK\n");
|
||||
}
|
||||
|
||||
void *fsp_prepare_mrc_cache(void)
|
||||
{
|
||||
struct mrc_data_container *cache;
|
||||
struct mrc_region entry;
|
||||
int ret;
|
||||
|
||||
ret = mrccache_get_region(NULL, &entry);
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
cache = mrccache_find_current(&entry);
|
||||
if (!cache)
|
||||
return NULL;
|
||||
|
||||
debug("%s: mrc cache at %p, size %x checksum %04x\n", __func__,
|
||||
cache->data, cache->data_size, cache->checksum);
|
||||
|
||||
return cache->data;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
int fsp_save_s3_stack(void)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
if (gd->arch.prev_sleep_state == ACPI_S3)
|
||||
return 0;
|
||||
|
||||
ret = uclass_get_device(UCLASS_RTC, 0, &dev);
|
||||
if (ret) {
|
||||
debug("Cannot find RTC: err=%d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Save the stack address to CMOS */
|
||||
ret = rtc_write32(dev, CMOS_FSP_STACK_ADDR, gd->start_addr_sp);
|
||||
if (ret) {
|
||||
debug("Save stack address to CMOS: err=%d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <handoff.h>
|
||||
#include <init.h>
|
||||
#include <asm/fsp/fsp_support.h>
|
||||
#include <asm/e820.h>
|
||||
#include <asm/mrccache.h>
|
||||
#include <asm/post.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int fsp_scan_for_ram_size(void)
|
||||
{
|
||||
phys_size_t ram_size = 0;
|
||||
const struct hob_header *hdr;
|
||||
struct hob_res_desc *res_desc;
|
||||
|
||||
hdr = gd->arch.hob_list;
|
||||
while (!end_of_hob(hdr)) {
|
||||
if (hdr->type == HOB_TYPE_RES_DESC) {
|
||||
res_desc = (struct hob_res_desc *)hdr;
|
||||
if (res_desc->type == RES_SYS_MEM ||
|
||||
res_desc->type == RES_MEM_RESERVED)
|
||||
ram_size += res_desc->len;
|
||||
}
|
||||
hdr = get_next_hob(hdr);
|
||||
}
|
||||
|
||||
gd->ram_size = ram_size;
|
||||
post_code(POST_DRAM);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
int dram_init_banksize(void)
|
||||
{
|
||||
gd->bd->bi_dram[0].start = 0;
|
||||
gd->bd->bi_dram[0].size = gd->ram_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int install_e820_map(unsigned int max_entries,
|
||||
struct e820_entry *entries)
|
||||
{
|
||||
unsigned int num_entries = 0;
|
||||
const struct hob_header *hdr;
|
||||
struct hob_res_desc *res_desc;
|
||||
|
||||
hdr = gd->arch.hob_list;
|
||||
|
||||
while (!end_of_hob(hdr)) {
|
||||
if (hdr->type == HOB_TYPE_RES_DESC) {
|
||||
res_desc = (struct hob_res_desc *)hdr;
|
||||
entries[num_entries].addr = res_desc->phys_start;
|
||||
entries[num_entries].size = res_desc->len;
|
||||
|
||||
if (res_desc->type == RES_SYS_MEM)
|
||||
entries[num_entries].type = E820_RAM;
|
||||
else if (res_desc->type == RES_MEM_RESERVED)
|
||||
entries[num_entries].type = E820_RESERVED;
|
||||
|
||||
num_entries++;
|
||||
}
|
||||
hdr = get_next_hob(hdr);
|
||||
}
|
||||
|
||||
/* Mark PCIe ECAM address range as reserved */
|
||||
entries[num_entries].addr = CONFIG_PCIE_ECAM_BASE;
|
||||
entries[num_entries].size = CONFIG_PCIE_ECAM_SIZE;
|
||||
entries[num_entries].type = E820_RESERVED;
|
||||
num_entries++;
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
/*
|
||||
* Everything between U-Boot's stack and ram top needs to be
|
||||
* reserved in order for ACPI S3 resume to work.
|
||||
*/
|
||||
entries[num_entries].addr = gd->start_addr_sp - CONFIG_STACK_SIZE;
|
||||
entries[num_entries].size = gd->ram_top - gd->start_addr_sp +
|
||||
CONFIG_STACK_SIZE;
|
||||
entries[num_entries].type = E820_RESERVED;
|
||||
num_entries++;
|
||||
#endif
|
||||
|
||||
return num_entries;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(HANDOFF) && IS_ENABLED(CONFIG_USE_HOB)
|
||||
int handoff_arch_save(struct spl_handoff *ho)
|
||||
{
|
||||
ho->arch.usable_ram_top = fsp_get_usable_lowmem_top(gd->arch.hob_list);
|
||||
ho->arch.hob_list = gd->arch.hob_list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,183 @@
|
||||
// SPDX-License-Identifier: Intel
|
||||
/*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/fsp1/fsp_support.h>
|
||||
#include <asm/post.h>
|
||||
|
||||
u32 fsp_get_usable_lowmem_top(const void *hob_list)
|
||||
{
|
||||
const struct hob_header *hdr;
|
||||
struct hob_res_desc *res_desc;
|
||||
phys_addr_t phys_start;
|
||||
u32 top;
|
||||
#ifdef CONFIG_FSP_BROKEN_HOB
|
||||
struct hob_mem_alloc *res_mem;
|
||||
phys_addr_t mem_base = 0;
|
||||
#endif
|
||||
|
||||
/* Get the HOB list for processing */
|
||||
hdr = hob_list;
|
||||
|
||||
/* * Collect memory ranges */
|
||||
top = FSP_LOWMEM_BASE;
|
||||
while (!end_of_hob(hdr)) {
|
||||
if (hdr->type == HOB_TYPE_RES_DESC) {
|
||||
res_desc = (struct hob_res_desc *)hdr;
|
||||
if (res_desc->type == RES_SYS_MEM) {
|
||||
phys_start = res_desc->phys_start;
|
||||
/* Need memory above 1MB to be collected here */
|
||||
if (phys_start >= FSP_LOWMEM_BASE &&
|
||||
phys_start < (phys_addr_t)FSP_HIGHMEM_BASE)
|
||||
top += (u32)(res_desc->len);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FSP_BROKEN_HOB
|
||||
/*
|
||||
* Find out the lowest memory base address allocated by FSP
|
||||
* for the boot service data
|
||||
*/
|
||||
if (hdr->type == HOB_TYPE_MEM_ALLOC) {
|
||||
res_mem = (struct hob_mem_alloc *)hdr;
|
||||
if (!mem_base)
|
||||
mem_base = res_mem->mem_base;
|
||||
if (res_mem->mem_base < mem_base)
|
||||
mem_base = res_mem->mem_base;
|
||||
}
|
||||
#endif
|
||||
|
||||
hdr = get_next_hob(hdr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FSP_BROKEN_HOB
|
||||
/*
|
||||
* Check whether the memory top address is below the FSP HOB list.
|
||||
* If not, use the lowest memory base address allocated by FSP as
|
||||
* the memory top address. This is to prevent U-Boot relocation
|
||||
* overwrites the important boot service data which is used by FSP,
|
||||
* otherwise the subsequent call to fsp_notify() will fail.
|
||||
*/
|
||||
if (top > (u32)hob_list) {
|
||||
debug("Adjust memory top address due to a buggy FSP\n");
|
||||
top = (u32)mem_base;
|
||||
}
|
||||
#endif
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
u64 fsp_get_usable_highmem_top(const void *hob_list)
|
||||
{
|
||||
const struct hob_header *hdr;
|
||||
struct hob_res_desc *res_desc;
|
||||
phys_addr_t phys_start;
|
||||
u64 top;
|
||||
|
||||
/* Get the HOB list for processing */
|
||||
hdr = hob_list;
|
||||
|
||||
/* Collect memory ranges */
|
||||
top = FSP_HIGHMEM_BASE;
|
||||
while (!end_of_hob(hdr)) {
|
||||
if (hdr->type == HOB_TYPE_RES_DESC) {
|
||||
res_desc = (struct hob_res_desc *)hdr;
|
||||
if (res_desc->type == RES_SYS_MEM) {
|
||||
phys_start = res_desc->phys_start;
|
||||
/* Need memory above 4GB to be collected here */
|
||||
if (phys_start >= (phys_addr_t)FSP_HIGHMEM_BASE)
|
||||
top += (u32)(res_desc->len);
|
||||
}
|
||||
}
|
||||
hdr = get_next_hob(hdr);
|
||||
}
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
u64 fsp_get_reserved_mem_from_guid(const void *hob_list, u64 *len,
|
||||
const efi_guid_t *guid)
|
||||
{
|
||||
const struct hob_header *hdr;
|
||||
struct hob_res_desc *res_desc;
|
||||
|
||||
/* Get the HOB list for processing */
|
||||
hdr = hob_list;
|
||||
|
||||
/* Collect memory ranges */
|
||||
while (!end_of_hob(hdr)) {
|
||||
if (hdr->type == HOB_TYPE_RES_DESC) {
|
||||
res_desc = (struct hob_res_desc *)hdr;
|
||||
if (res_desc->type == RES_MEM_RESERVED) {
|
||||
if (!guidcmp(&res_desc->owner, guid)) {
|
||||
if (len)
|
||||
*len = (u32)(res_desc->len);
|
||||
|
||||
return (u64)(res_desc->phys_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
hdr = get_next_hob(hdr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 fsp_get_fsp_reserved_mem(const void *hob_list, u32 *len)
|
||||
{
|
||||
const efi_guid_t guid = FSP_HOB_RESOURCE_OWNER_FSP_GUID;
|
||||
u64 length;
|
||||
u32 base;
|
||||
|
||||
base = (u32)fsp_get_reserved_mem_from_guid(hob_list,
|
||||
&length, &guid);
|
||||
if (len && base)
|
||||
*len = (u32)length;
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
u32 fsp_get_tseg_reserved_mem(const void *hob_list, u32 *len)
|
||||
{
|
||||
const efi_guid_t guid = FSP_HOB_RESOURCE_OWNER_TSEG_GUID;
|
||||
u64 length;
|
||||
u32 base;
|
||||
|
||||
base = (u32)fsp_get_reserved_mem_from_guid(hob_list,
|
||||
&length, &guid);
|
||||
if (len && base)
|
||||
*len = (u32)length;
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
void *fsp_get_nvs_data(const void *hob_list, u32 *len)
|
||||
{
|
||||
const efi_guid_t guid = FSP_NON_VOLATILE_STORAGE_HOB_GUID;
|
||||
|
||||
return hob_get_guid_hob_data(hob_list, len, &guid);
|
||||
}
|
||||
|
||||
void *fsp_get_var_nvs_data(const void *hob_list, u32 *len)
|
||||
{
|
||||
const efi_guid_t guid = FSP_VARIABLE_NV_DATA_HOB_GUID;
|
||||
|
||||
return hob_get_guid_hob_data(hob_list, len, &guid);
|
||||
}
|
||||
|
||||
void *fsp_get_bootloader_tmp_mem(const void *hob_list, u32 *len)
|
||||
{
|
||||
const efi_guid_t guid = FSP_BOOTLOADER_TEMP_MEM_HOB_GUID;
|
||||
|
||||
return hob_get_guid_hob_data(hob_list, len, &guid);
|
||||
}
|
||||
|
||||
void *fsp_get_graphics_info(const void *hob_list, u32 *len)
|
||||
{
|
||||
const efi_guid_t guid = FSP_GRAPHICS_INFO_HOB_GUID;
|
||||
|
||||
return hob_get_guid_hob_data(hob_list, len, &guid);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (C) 2015 Google, Inc
|
||||
|
||||
obj-y += fsp_car.o
|
||||
obj-y += fsp_common.o
|
||||
obj-y += fsp_dram.o
|
||||
obj-$(CONFIG_VIDEO_FSP) += fsp_graphics.o
|
||||
obj-y += fsp_support.o
|
||||
@@ -0,0 +1,111 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <asm/post.h>
|
||||
|
||||
.globl car_init
|
||||
car_init:
|
||||
/*
|
||||
* Note: ebp holds the BIST value (built-in self test) so far, but ebp
|
||||
* will be destroyed through the FSP call, thus we have to test the
|
||||
* BIST value here before we call into FSP.
|
||||
*/
|
||||
test %ebp, %ebp
|
||||
jz car_init_start
|
||||
post_code(POST_BIST_FAILURE)
|
||||
jmp die
|
||||
|
||||
car_init_start:
|
||||
post_code(POST_CAR_START)
|
||||
lea fsp_find_header_romstack, %esp
|
||||
jmp fsp_find_header
|
||||
|
||||
fsp_find_header_ret:
|
||||
/* EAX points to FSP_INFO_HEADER */
|
||||
mov %eax, %ebp
|
||||
|
||||
/* sanity test */
|
||||
cmp $CONFIG_FSP_ADDR, %eax
|
||||
jb die
|
||||
|
||||
/* calculate TempRamInitEntry address */
|
||||
mov 0x30(%ebp), %eax
|
||||
add 0x1c(%ebp), %eax
|
||||
|
||||
/* call FSP TempRamInitEntry to setup temporary stack */
|
||||
lea temp_ram_init_romstack, %esp
|
||||
jmp *%eax
|
||||
|
||||
temp_ram_init_ret:
|
||||
addl $4, %esp
|
||||
cmp $0, %eax
|
||||
jnz car_init_fail
|
||||
|
||||
post_code(POST_CAR_CPU_CACHE)
|
||||
|
||||
/*
|
||||
* The FSP TempRamInit initializes the ecx and edx registers to
|
||||
* point to a temporary but writable memory range (Cache-As-RAM).
|
||||
* ecx: the start of this temporary memory range,
|
||||
* edx: the end of this range.
|
||||
*/
|
||||
|
||||
/* stack grows down from top of CAR */
|
||||
movl %edx, %esp
|
||||
subl $4, %esp
|
||||
|
||||
xor %esi, %esi
|
||||
jmp car_init_done
|
||||
|
||||
.global fsp_init_done
|
||||
fsp_init_done:
|
||||
/*
|
||||
* We come here from fsp_continue() with eax pointing to the HOB list.
|
||||
* Save eax to esi temporarily.
|
||||
*/
|
||||
movl %eax, %esi
|
||||
|
||||
car_init_done:
|
||||
/*
|
||||
* Re-initialize the ebp (BIST) to zero, as we already reach here
|
||||
* which means we passed BIST testing before.
|
||||
*/
|
||||
xorl %ebp, %ebp
|
||||
jmp car_init_ret
|
||||
|
||||
car_init_fail:
|
||||
post_code(POST_CAR_FAILURE)
|
||||
|
||||
die:
|
||||
hlt
|
||||
jmp die
|
||||
hlt
|
||||
|
||||
/*
|
||||
* The function call before CAR initialization is tricky. It cannot
|
||||
* be called using the 'call' instruction but only the 'jmp' with
|
||||
* the help of a handcrafted stack in the ROM. The stack needs to
|
||||
* contain the function return address as well as the parameters.
|
||||
*/
|
||||
.balign 4
|
||||
fsp_find_header_romstack:
|
||||
.long fsp_find_header_ret
|
||||
|
||||
.balign 4
|
||||
temp_ram_init_romstack:
|
||||
.long temp_ram_init_ret
|
||||
.long temp_ram_init_params
|
||||
temp_ram_init_params:
|
||||
_dt_ucode_base_size:
|
||||
/* These next two fields are filled in by binman */
|
||||
.globl ucode_base
|
||||
ucode_base: /* Declared in microcode.h */
|
||||
.long 0 /* microcode base */
|
||||
.globl ucode_size
|
||||
ucode_size: /* Declared in microcode.h */
|
||||
.long 0 /* microcode size */
|
||||
.long CONFIG_SYS_MONITOR_BASE /* code region base */
|
||||
.long CONFIG_SYS_MONITOR_LEN /* code region size */
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <acpi_s3.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <rtc.h>
|
||||
#include <asm/cmos_layout.h>
|
||||
#include <asm/early_cmos.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mrccache.h>
|
||||
#include <asm/post.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/fsp1/fsp_support.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int arch_fsp_init(void)
|
||||
{
|
||||
void *nvs;
|
||||
int stack = CONFIG_FSP_TEMP_RAM_ADDR;
|
||||
int boot_mode = BOOT_FULL_CONFIG;
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
int prev_sleep_state = chipset_prev_sleep_state();
|
||||
gd->arch.prev_sleep_state = prev_sleep_state;
|
||||
#endif
|
||||
|
||||
if (!gd->arch.hob_list) {
|
||||
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
|
||||
nvs = fsp_prepare_mrc_cache();
|
||||
else
|
||||
nvs = NULL;
|
||||
|
||||
#ifdef CONFIG_HAVE_ACPI_RESUME
|
||||
if (prev_sleep_state == ACPI_S3) {
|
||||
if (nvs == NULL) {
|
||||
/* If waking from S3 and no cache then */
|
||||
debug("No MRC cache found in S3 resume path\n");
|
||||
post_code(POST_RESUME_FAILURE);
|
||||
/* Clear Sleep Type */
|
||||
chipset_clear_sleep_state();
|
||||
/* Reboot */
|
||||
debug("Rebooting..\n");
|
||||
outb(SYS_RST | RST_CPU, IO_PORT_RESET);
|
||||
/* Should not reach here.. */
|
||||
panic("Reboot System");
|
||||
}
|
||||
|
||||
/*
|
||||
* DM is not available yet at this point, hence call
|
||||
* CMOS access library which does not depend on DM.
|
||||
*/
|
||||
stack = cmos_read32(CMOS_FSP_STACK_ADDR);
|
||||
boot_mode = BOOT_ON_S3_RESUME;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* The first time we enter here, call fsp_init().
|
||||
* Note the execution does not return to this function,
|
||||
* instead it jumps to fsp_continue().
|
||||
*/
|
||||
fsp_init(stack, boot_mode, nvs);
|
||||
} else {
|
||||
/*
|
||||
* The second time we enter here, adjust the size of malloc()
|
||||
* pool before relocation. Given gd->malloc_base was adjusted
|
||||
* after the call to board_init_f_init_reserve() in arch/x86/
|
||||
* cpu/start.S, we should fix up gd->malloc_limit here.
|
||||
*/
|
||||
gd->malloc_limit += CONFIG_FSP_SYS_MALLOC_F_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/fsp/fsp_support.h>
|
||||
|
||||
int dram_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* The FSP has already set up DRAM, so grab the info we need */
|
||||
ret = fsp_scan_for_ram_size();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE))
|
||||
gd->arch.mrc_output = fsp_get_nvs_data(gd->arch.hob_list,
|
||||
&gd->arch.mrc_output_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function looks for the highest region of memory lower than 4GB which
|
||||
* has enough space for U-Boot where U-Boot is aligned on a page boundary.
|
||||
* It overrides the default implementation found elsewhere which simply
|
||||
* picks the end of ram, wherever that may be. The location of the stack,
|
||||
* the relocation address, and how far U-Boot is moved by relocation are
|
||||
* set in the global data structure.
|
||||
*/
|
||||
ulong board_get_usable_ram_top(ulong total_size)
|
||||
{
|
||||
return fsp_get_usable_lowmem_top(gd->arch.hob_list);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <vbe.h>
|
||||
#include <video.h>
|
||||
#include <asm/fsp1/fsp_support.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct pixel {
|
||||
u8 pos;
|
||||
u8 size;
|
||||
};
|
||||
|
||||
static const struct fsp_framebuffer {
|
||||
struct pixel red;
|
||||
struct pixel green;
|
||||
struct pixel blue;
|
||||
struct pixel rsvd;
|
||||
} fsp_framebuffer_format_map[] = {
|
||||
[pixel_rgbx_8bpc] = { {0, 8}, {8, 8}, {16, 8}, {24, 8} },
|
||||
[pixel_bgrx_8bpc] = { {16, 8}, {8, 8}, {0, 8}, {24, 8} },
|
||||
};
|
||||
|
||||
static int save_vesa_mode(struct vesa_mode_info *vesa)
|
||||
{
|
||||
const struct hob_graphics_info *ginfo;
|
||||
const struct fsp_framebuffer *fbinfo;
|
||||
|
||||
ginfo = fsp_get_graphics_info(gd->arch.hob_list, NULL);
|
||||
|
||||
/*
|
||||
* If there is no graphics info structure, bail out and keep
|
||||
* running on the serial console.
|
||||
*
|
||||
* Note: on some platforms (eg: Braswell), the FSP will not produce
|
||||
* the graphics info HOB unless you plug some cables to the display
|
||||
* interface (eg: HDMI) on the board.
|
||||
*/
|
||||
if (!ginfo) {
|
||||
debug("FSP graphics hand-off block not found\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
vesa->x_resolution = ginfo->width;
|
||||
vesa->y_resolution = ginfo->height;
|
||||
vesa->bits_per_pixel = 32;
|
||||
vesa->bytes_per_scanline = ginfo->pixels_per_scanline * 4;
|
||||
vesa->phys_base_ptr = ginfo->fb_base;
|
||||
|
||||
if (ginfo->pixel_format >= pixel_bitmask) {
|
||||
debug("FSP set unknown framebuffer format: %d\n",
|
||||
ginfo->pixel_format);
|
||||
return -EINVAL;
|
||||
}
|
||||
fbinfo = &fsp_framebuffer_format_map[ginfo->pixel_format];
|
||||
vesa->red_mask_size = fbinfo->red.size;
|
||||
vesa->red_mask_pos = fbinfo->red.pos;
|
||||
vesa->green_mask_size = fbinfo->green.size;
|
||||
vesa->green_mask_pos = fbinfo->green.pos;
|
||||
vesa->blue_mask_size = fbinfo->blue.size;
|
||||
vesa->blue_mask_pos = fbinfo->blue.pos;
|
||||
vesa->reserved_mask_size = fbinfo->rsvd.size;
|
||||
vesa->reserved_mask_pos = fbinfo->rsvd.pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsp_video_probe(struct udevice *dev)
|
||||
{
|
||||
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
|
||||
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct vesa_mode_info *vesa = &mode_info.vesa;
|
||||
int ret;
|
||||
|
||||
printf("Video: ");
|
||||
|
||||
/* Initialize vesa_mode_info structure */
|
||||
ret = save_vesa_mode(vesa);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* The framebuffer base address in the FSP graphics info HOB reflects
|
||||
* the value assigned by the FSP. After PCI enumeration the framebuffer
|
||||
* base address may be relocated. Let's get the updated one from device.
|
||||
*
|
||||
* For IGD, it seems to be always on BAR2.
|
||||
*/
|
||||
vesa->phys_base_ptr = dm_pci_read_bar32(dev, 2);
|
||||
|
||||
ret = vbe_setup_video_priv(vesa, uc_priv, plat);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
printf("%dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
|
||||
vesa->bits_per_pixel);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
printf("No video mode configured in FSP!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct udevice_id fsp_video_ids[] = {
|
||||
{ .compatible = "fsp-fb" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(fsp_video) = {
|
||||
.name = "fsp_video",
|
||||
.id = UCLASS_VIDEO,
|
||||
.of_match = fsp_video_ids,
|
||||
.probe = fsp_video_probe,
|
||||
};
|
||||
|
||||
static struct pci_device_id fsp_video_supported[] = {
|
||||
{ PCI_DEVICE_CLASS(PCI_CLASS_DISPLAY_VGA << 8, 0xffff00) },
|
||||
{ },
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(fsp_video, fsp_video_supported);
|
||||
@@ -0,0 +1,199 @@
|
||||
// SPDX-License-Identifier: Intel
|
||||
/*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/fsp1/fsp_support.h>
|
||||
#include <asm/post.h>
|
||||
|
||||
struct fsp_header *__attribute__((optimize("O0"))) fsp_find_header(void)
|
||||
{
|
||||
/*
|
||||
* This function may be called before the a stack is established,
|
||||
* so special care must be taken. First, it cannot declare any local
|
||||
* variable using stack. Only register variable can be used here.
|
||||
* Secondly, some compiler version will add prolog or epilog code
|
||||
* for the C function. If so the function call may not work before
|
||||
* stack is ready.
|
||||
*
|
||||
* GCC 4.8.1 has been verified to be working for the following codes.
|
||||
*/
|
||||
volatile register u8 *fsp asm("eax");
|
||||
|
||||
/* Initalize the FSP base */
|
||||
fsp = (u8 *)CONFIG_FSP_ADDR;
|
||||
|
||||
/* Check the FV signature, _FVH */
|
||||
if (((struct fv_header *)fsp)->sign == EFI_FVH_SIGNATURE) {
|
||||
/* Go to the end of the FV header and align the address */
|
||||
fsp += ((struct fv_header *)fsp)->ext_hdr_off;
|
||||
fsp += ((struct fv_ext_header *)fsp)->ext_hdr_size;
|
||||
fsp = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8);
|
||||
} else {
|
||||
fsp = 0;
|
||||
}
|
||||
|
||||
/* Check the FFS GUID */
|
||||
if (fsp &&
|
||||
((struct ffs_file_header *)fsp)->name.b[0] == FSP_GUID_BYTE0 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[1] == FSP_GUID_BYTE1 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[2] == FSP_GUID_BYTE2 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[3] == FSP_GUID_BYTE3 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[4] == FSP_GUID_BYTE4 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[5] == FSP_GUID_BYTE5 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[6] == FSP_GUID_BYTE6 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[7] == FSP_GUID_BYTE7 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[8] == FSP_GUID_BYTE8 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[9] == FSP_GUID_BYTE9 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[10] == FSP_GUID_BYTE10 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[11] == FSP_GUID_BYTE11 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[12] == FSP_GUID_BYTE12 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[13] == FSP_GUID_BYTE13 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[14] == FSP_GUID_BYTE14 &&
|
||||
((struct ffs_file_header *)fsp)->name.b[15] == FSP_GUID_BYTE15) {
|
||||
/* Add the FFS header size to find the raw section header */
|
||||
fsp += sizeof(struct ffs_file_header);
|
||||
} else {
|
||||
fsp = 0;
|
||||
}
|
||||
|
||||
if (fsp &&
|
||||
((struct raw_section *)fsp)->type == EFI_SECTION_RAW) {
|
||||
/* Add the raw section header size to find the FSP header */
|
||||
fsp += sizeof(struct raw_section);
|
||||
} else {
|
||||
fsp = 0;
|
||||
}
|
||||
|
||||
return (struct fsp_header *)fsp;
|
||||
}
|
||||
|
||||
void fsp_continue(u32 status, void *hob_list)
|
||||
{
|
||||
post_code(POST_MRC);
|
||||
|
||||
assert(status == 0);
|
||||
|
||||
/* The boot loader main function entry */
|
||||
fsp_init_done(hob_list);
|
||||
}
|
||||
|
||||
void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf)
|
||||
{
|
||||
struct fsp_config_data config_data;
|
||||
fsp_init_f init;
|
||||
struct fsp_init_params params;
|
||||
struct fspinit_rtbuf rt_buf;
|
||||
struct fsp_header *fsp_hdr;
|
||||
struct fsp_init_params *params_ptr;
|
||||
#ifdef CONFIG_FSP_USE_UPD
|
||||
struct vpd_region *fsp_vpd;
|
||||
struct upd_region *fsp_upd;
|
||||
#endif
|
||||
|
||||
fsp_hdr = fsp_find_header();
|
||||
if (fsp_hdr == NULL) {
|
||||
/* No valid FSP info header was found */
|
||||
panic("Invalid FSP header");
|
||||
}
|
||||
|
||||
config_data.common.fsp_hdr = fsp_hdr;
|
||||
config_data.common.stack_top = stack_top;
|
||||
config_data.common.boot_mode = boot_mode;
|
||||
|
||||
#ifdef CONFIG_FSP_USE_UPD
|
||||
/* Get VPD region start */
|
||||
fsp_vpd = (struct vpd_region *)(fsp_hdr->img_base +
|
||||
fsp_hdr->cfg_region_off);
|
||||
|
||||
/* Verify the VPD data region is valid */
|
||||
assert(fsp_vpd->sign == VPD_IMAGE_ID);
|
||||
|
||||
fsp_upd = &config_data.fsp_upd;
|
||||
|
||||
/* Copy default data from Flash */
|
||||
memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset),
|
||||
sizeof(struct upd_region));
|
||||
|
||||
/* Verify the UPD data region is valid */
|
||||
assert(fsp_upd->terminator == UPD_TERMINATOR);
|
||||
#endif
|
||||
|
||||
memset(&rt_buf, 0, sizeof(struct fspinit_rtbuf));
|
||||
|
||||
/* Override any configuration if required */
|
||||
fsp_update_configs(&config_data, &rt_buf);
|
||||
|
||||
memset(¶ms, 0, sizeof(struct fsp_init_params));
|
||||
params.nvs_buf = nvs_buf;
|
||||
params.rt_buf = (struct fspinit_rtbuf *)&rt_buf;
|
||||
params.continuation = (fsp_continuation_f)fsp_asm_continuation;
|
||||
|
||||
init = (fsp_init_f)(fsp_hdr->img_base + fsp_hdr->fsp_init);
|
||||
params_ptr = ¶ms;
|
||||
|
||||
post_code(POST_PRE_MRC);
|
||||
|
||||
/* Load GDT for FSP */
|
||||
setup_fsp_gdt();
|
||||
|
||||
/*
|
||||
* Use ASM code to ensure the register value in EAX & EDX
|
||||
* will be passed into fsp_continue
|
||||
*/
|
||||
asm volatile (
|
||||
"pushl %0;"
|
||||
"call *%%eax;"
|
||||
".global fsp_asm_continuation;"
|
||||
"fsp_asm_continuation:;"
|
||||
"movl 4(%%esp), %%eax;" /* status */
|
||||
"movl 8(%%esp), %%edx;" /* hob_list */
|
||||
"jmp fsp_continue;"
|
||||
: : "m"(params_ptr), "a"(init)
|
||||
);
|
||||
|
||||
/*
|
||||
* Should never get here.
|
||||
* Control will continue from fsp_continue.
|
||||
* This line below is to prevent the compiler from optimizing
|
||||
* structure intialization.
|
||||
*
|
||||
* DO NOT REMOVE!
|
||||
*/
|
||||
init(¶ms);
|
||||
}
|
||||
|
||||
u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase)
|
||||
{
|
||||
fsp_notify_f notify;
|
||||
struct fsp_notify_params params;
|
||||
struct fsp_notify_params *params_ptr;
|
||||
u32 status;
|
||||
|
||||
if (!fsp_hdr)
|
||||
fsp_hdr = (struct fsp_header *)fsp_find_header();
|
||||
|
||||
if (fsp_hdr == NULL) {
|
||||
/* No valid FSP info header */
|
||||
panic("Invalid FSP header");
|
||||
}
|
||||
|
||||
notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify);
|
||||
params.phase = phase;
|
||||
params_ptr = ¶ms;
|
||||
|
||||
/*
|
||||
* Use ASM code to ensure correct parameter is on the stack for
|
||||
* FspNotify as U-Boot is using different ABI from FSP
|
||||
*/
|
||||
asm volatile (
|
||||
"pushl %1;" /* push notify phase */
|
||||
"call *%%eax;" /* call FspNotify */
|
||||
"addl $4, %%esp;" /* clean up the stack */
|
||||
: "=a"(status) : "m"(params_ptr), "a"(notify), "m"(*params_ptr)
|
||||
);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: Intel
|
||||
/*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/hob.h>
|
||||
|
||||
/**
|
||||
* Returns the next instance of a HOB type from the starting HOB.
|
||||
*
|
||||
* @type: HOB type to search
|
||||
* @hob_list: A pointer to the HOB list
|
||||
*
|
||||
* @return A HOB object with matching type; Otherwise NULL.
|
||||
*/
|
||||
const struct hob_header *hob_get_next_hob(uint type, const void *hob_list)
|
||||
{
|
||||
const struct hob_header *hdr;
|
||||
|
||||
hdr = hob_list;
|
||||
|
||||
/* Parse the HOB list until end of list or matching type is found */
|
||||
while (!end_of_hob(hdr)) {
|
||||
if (hdr->type == type)
|
||||
return hdr;
|
||||
|
||||
hdr = get_next_hob(hdr);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next instance of the matched GUID HOB from the starting HOB.
|
||||
*
|
||||
* @guid: GUID to search
|
||||
* @hob_list: A pointer to the HOB list
|
||||
*
|
||||
* @return A HOB object with matching GUID; Otherwise NULL.
|
||||
*/
|
||||
const struct hob_header *hob_get_next_guid_hob(const efi_guid_t *guid,
|
||||
const void *hob_list)
|
||||
{
|
||||
const struct hob_header *hdr;
|
||||
struct hob_guid *guid_hob;
|
||||
|
||||
hdr = hob_list;
|
||||
while ((hdr = hob_get_next_hob(HOB_TYPE_GUID_EXT, hdr))) {
|
||||
guid_hob = (struct hob_guid *)hdr;
|
||||
if (!guidcmp(guid, &guid_hob->name))
|
||||
break;
|
||||
hdr = get_next_hob(hdr);
|
||||
}
|
||||
|
||||
return hdr;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function retrieves a GUID HOB data buffer and size.
|
||||
*
|
||||
* @hob_list: A HOB list pointer.
|
||||
* @len: A pointer to the GUID HOB data buffer length.
|
||||
* If the GUID HOB is located, the length will be updated.
|
||||
* @guid A pointer to HOB GUID.
|
||||
*
|
||||
* @return NULL: Failed to find the GUID HOB.
|
||||
* @return others: GUID HOB data buffer pointer.
|
||||
*/
|
||||
void *hob_get_guid_hob_data(const void *hob_list, u32 *len,
|
||||
const efi_guid_t *guid)
|
||||
{
|
||||
const struct hob_header *guid_hob;
|
||||
|
||||
guid_hob = hob_get_next_guid_hob(guid, hob_list);
|
||||
if (!guid_hob)
|
||||
return NULL;
|
||||
|
||||
if (len)
|
||||
*len = get_guid_hob_data_size(guid_hob);
|
||||
|
||||
return get_guid_hob_data(guid_hob);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2002
|
||||
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/i8254.h>
|
||||
|
||||
#define TIMER1_VALUE 18 /* 15.6us */
|
||||
#define BEEP_FREQUENCY_HZ 440
|
||||
#define SYSCTL_PORTB 0x61
|
||||
#define PORTB_BEEP_ENABLE 0x3
|
||||
|
||||
static void i8254_set_beep_freq(uint frequency_hz)
|
||||
{
|
||||
uint countdown;
|
||||
|
||||
countdown = PIT_TICK_RATE / frequency_hz;
|
||||
|
||||
outb(countdown & 0xff, PIT_BASE + PIT_T2);
|
||||
outb((countdown >> 8) & 0xff, PIT_BASE + PIT_T2);
|
||||
}
|
||||
|
||||
int i8254_init(void)
|
||||
{
|
||||
/*
|
||||
* Initialize counter 1, used to refresh request signal.
|
||||
* This is required for legacy purpose as some codes like
|
||||
* vgabios utilizes counter 1 to provide delay functionality.
|
||||
*/
|
||||
outb(PIT_CMD_CTR1 | PIT_CMD_LOW | PIT_CMD_MODE2,
|
||||
PIT_BASE + PIT_COMMAND);
|
||||
outb(TIMER1_VALUE, PIT_BASE + PIT_T1);
|
||||
|
||||
/*
|
||||
* Initialize counter 2, used to drive the speaker.
|
||||
* To start a beep, set both bit0 and bit1 of port 0x61.
|
||||
* To stop it, clear both bit0 and bit1 of port 0x61.
|
||||
*/
|
||||
outb(PIT_CMD_CTR2 | PIT_CMD_BOTH | PIT_CMD_MODE3,
|
||||
PIT_BASE + PIT_COMMAND);
|
||||
i8254_set_beep_freq(BEEP_FREQUENCY_HZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i8254_enable_beep(uint frequency_hz)
|
||||
{
|
||||
if (!frequency_hz)
|
||||
return -EINVAL;
|
||||
|
||||
/* make sure i8254 is setup correctly before generating beeps */
|
||||
outb(PIT_CMD_CTR2 | PIT_CMD_BOTH | PIT_CMD_MODE3,
|
||||
PIT_BASE + PIT_COMMAND);
|
||||
|
||||
i8254_set_beep_freq(frequency_hz);
|
||||
setio_8(SYSCTL_PORTB, PORTB_BEEP_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i8254_disable_beep(void)
|
||||
{
|
||||
clrio_8(SYSCTL_PORTB, PORTB_BEEP_ENABLE);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Graeme Russ, <graeme.russ@gmail.com>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file provides the interrupt handling functionality for systems
|
||||
* based on the standard PC/AT architecture using two cascaded i8259
|
||||
* Programmable Interrupt Controllers.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/i8259.h>
|
||||
#include <asm/ibmpc.h>
|
||||
#include <asm/interrupt.h>
|
||||
|
||||
int i8259_init(void)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
/* Mask all interrupts */
|
||||
outb(0xff, MASTER_PIC + IMR);
|
||||
outb(0xff, SLAVE_PIC + IMR);
|
||||
|
||||
/*
|
||||
* Master PIC
|
||||
* Place master PIC interrupts at INT20
|
||||
*/
|
||||
outb(ICW1_SEL | ICW1_EICW4, MASTER_PIC + ICW1);
|
||||
outb(0x20, MASTER_PIC + ICW2);
|
||||
outb(IR2, MASTER_PIC + ICW3);
|
||||
outb(ICW4_PM, MASTER_PIC + ICW4);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
outb(OCW2_SEOI | i, MASTER_PIC + OCW2);
|
||||
|
||||
/*
|
||||
* Slave PIC
|
||||
* Place slave PIC interrupts at INT28
|
||||
*/
|
||||
outb(ICW1_SEL | ICW1_EICW4, SLAVE_PIC + ICW1);
|
||||
outb(0x28, SLAVE_PIC + ICW2);
|
||||
outb(0x02, SLAVE_PIC + ICW3);
|
||||
outb(ICW4_PM, SLAVE_PIC + ICW4);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
outb(OCW2_SEOI | i, SLAVE_PIC + OCW2);
|
||||
|
||||
/*
|
||||
* Enable cascaded interrupts by unmasking the cascade IRQ pin of
|
||||
* the master PIC
|
||||
*/
|
||||
unmask_irq(2);
|
||||
|
||||
/* Interrupt 9 should be level triggered (SCI). The OS might do this */
|
||||
configure_irq_trigger(9, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mask_irq(int irq)
|
||||
{
|
||||
int imr_port;
|
||||
|
||||
if (irq >= SYS_NUM_IRQS)
|
||||
return;
|
||||
|
||||
if (irq > 7)
|
||||
imr_port = SLAVE_PIC + IMR;
|
||||
else
|
||||
imr_port = MASTER_PIC + IMR;
|
||||
|
||||
outb(inb(imr_port) | (1 << (irq & 7)), imr_port);
|
||||
}
|
||||
|
||||
void unmask_irq(int irq)
|
||||
{
|
||||
int imr_port;
|
||||
|
||||
if (irq >= SYS_NUM_IRQS)
|
||||
return;
|
||||
|
||||
if (irq > 7)
|
||||
imr_port = SLAVE_PIC + IMR;
|
||||
else
|
||||
imr_port = MASTER_PIC + IMR;
|
||||
|
||||
outb(inb(imr_port) & ~(1 << (irq & 7)), imr_port);
|
||||
}
|
||||
|
||||
void specific_eoi(int irq)
|
||||
{
|
||||
if (irq >= SYS_NUM_IRQS)
|
||||
return;
|
||||
|
||||
if (irq > 7) {
|
||||
/*
|
||||
* IRQ is on the slave - Issue a corresponding EOI to the
|
||||
* slave PIC and an EOI for IRQ2 (the cascade interrupt)
|
||||
* on the master PIC
|
||||
*/
|
||||
outb(OCW2_SEOI | (irq & 7), SLAVE_PIC + OCW2);
|
||||
irq = SEOI_IR2;
|
||||
}
|
||||
|
||||
outb(OCW2_SEOI | irq, MASTER_PIC + OCW2);
|
||||
}
|
||||
|
||||
void configure_irq_trigger(int int_num, bool is_level_triggered)
|
||||
{
|
||||
u16 int_bits = inb(ELCR1) | (((u16)inb(ELCR2)) << 8);
|
||||
|
||||
debug("%s: current interrupts are 0x%x\n", __func__, int_bits);
|
||||
if (is_level_triggered)
|
||||
int_bits |= (1 << int_num);
|
||||
else
|
||||
int_bits &= ~(1 << int_num);
|
||||
|
||||
/* Write new values */
|
||||
debug("%s: try to set interrupts 0x%x\n", __func__, int_bits);
|
||||
outb((u8)(int_bits & 0xff), ELCR1);
|
||||
outb((u8)(int_bits >> 8), ELCR2);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2011
|
||||
* Graeme Russ, <graeme.russ@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/mtrr.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int init_cache_f_r(void)
|
||||
{
|
||||
bool do_mtrr = CONFIG_IS_ENABLED(X86_32BIT_INIT) ||
|
||||
IS_ENABLED(CONFIG_FSP_VERSION2);
|
||||
int ret;
|
||||
|
||||
do_mtrr &= !IS_ENABLED(CONFIG_FSP_VERSION1) &&
|
||||
!IS_ENABLED(CONFIG_SYS_SLIMBOOTLOADER);
|
||||
|
||||
if (do_mtrr) {
|
||||
ret = mtrr_commit(false);
|
||||
/*
|
||||
* If MTRR MSR is not implemented by the processor, just ignore
|
||||
* it
|
||||
*/
|
||||
if (ret && ret != -ENOSYS)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialise the CPU cache(s) */
|
||||
return init_cache();
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2009
|
||||
* Graeme Russ, <graeme.russ@gmail.com>
|
||||
*
|
||||
* (C) Copyright 2007
|
||||
* Daniel Hellstrom, Gaisler Research, <daniel@gaisler.com>
|
||||
*
|
||||
* (C) Copyright 2006
|
||||
* Detlev Zundel, DENX Software Engineering, <dzu@denx.de>
|
||||
*
|
||||
* (C) Copyright -2003
|
||||
* Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
|
||||
*
|
||||
* (C) Copyright 2001
|
||||
* Josh Huber, Mission Critical Linux, Inc, <huber@mclx.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains the high-level API for the interrupt sub-system
|
||||
* of the x86 port of U-Boot. Most of the functionality has been
|
||||
* shamelessly stolen from the leon2 / leon3 ports of U-Boot.
|
||||
* Daniel Hellstrom, Detlev Zundel, Wolfgang Denk and Josh Huber are
|
||||
* credited for the corresponding work on those ports. The original
|
||||
* interrupt handling routines for the x86 port were written by
|
||||
* Daniel Engström
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <irq_func.h>
|
||||
#include <asm/interrupt.h>
|
||||
|
||||
#if !CONFIG_IS_ENABLED(X86_64)
|
||||
|
||||
struct irq_action {
|
||||
interrupt_handler_t *handler;
|
||||
void *arg;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
static struct irq_action irq_handlers[SYS_NUM_IRQS] = { {0} };
|
||||
static int spurious_irq_cnt;
|
||||
static int spurious_irq;
|
||||
|
||||
void irq_install_handler(int irq, interrupt_handler_t *handler, void *arg)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (irq < 0 || irq >= SYS_NUM_IRQS) {
|
||||
printf("irq_install_handler: bad irq number %d\n", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (irq_handlers[irq].handler != NULL)
|
||||
printf("irq_install_handler: 0x%08lx replacing 0x%08lx\n",
|
||||
(ulong) handler,
|
||||
(ulong) irq_handlers[irq].handler);
|
||||
|
||||
status = disable_interrupts();
|
||||
|
||||
irq_handlers[irq].handler = handler;
|
||||
irq_handlers[irq].arg = arg;
|
||||
irq_handlers[irq].count = 0;
|
||||
|
||||
if (CONFIG_IS_ENABLED(I8259_PIC))
|
||||
unmask_irq(irq);
|
||||
|
||||
if (status)
|
||||
enable_interrupts();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void irq_free_handler(int irq)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (irq < 0 || irq >= SYS_NUM_IRQS) {
|
||||
printf("irq_free_handler: bad irq number %d\n", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
status = disable_interrupts();
|
||||
|
||||
if (CONFIG_IS_ENABLED(I8259_PIC))
|
||||
mask_irq(irq);
|
||||
|
||||
irq_handlers[irq].handler = NULL;
|
||||
irq_handlers[irq].arg = NULL;
|
||||
|
||||
if (status)
|
||||
enable_interrupts();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void do_irq(int hw_irq)
|
||||
{
|
||||
int irq = hw_irq - 0x20;
|
||||
|
||||
if (irq < 0 || irq >= SYS_NUM_IRQS) {
|
||||
printf("do_irq: bad irq number %d\n", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (irq_handlers[irq].handler) {
|
||||
if (CONFIG_IS_ENABLED(I8259_PIC))
|
||||
mask_irq(irq);
|
||||
|
||||
irq_handlers[irq].handler(irq_handlers[irq].arg);
|
||||
irq_handlers[irq].count++;
|
||||
|
||||
if (CONFIG_IS_ENABLED(I8259_PIC)) {
|
||||
unmask_irq(irq);
|
||||
specific_eoi(irq);
|
||||
}
|
||||
} else {
|
||||
if ((irq & 7) != 7) {
|
||||
spurious_irq_cnt++;
|
||||
spurious_irq = irq;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_CMD_IRQ)
|
||||
int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
#if !CONFIG_IS_ENABLED(X86_64)
|
||||
int irq;
|
||||
|
||||
printf("Spurious IRQ: %u, last unknown IRQ: %d\n",
|
||||
spurious_irq_cnt, spurious_irq);
|
||||
|
||||
printf("Interrupt-Information:\n");
|
||||
printf("Nr Routine Arg Count\n");
|
||||
|
||||
for (irq = 0; irq < SYS_NUM_IRQS; irq++) {
|
||||
if (irq_handlers[irq].handler != NULL) {
|
||||
printf("%02d %08lx %08lx %d\n",
|
||||
irq,
|
||||
(ulong)irq_handlers[irq].handler,
|
||||
(ulong)irq_handlers[irq].arg,
|
||||
irq_handlers[irq].count);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
|
||||
UCLASS_DRIVER(lpc) = {
|
||||
.id = UCLASS_LPC,
|
||||
.name = "lpc",
|
||||
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
||||
.post_bind = dm_scan_fdt_dev,
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,408 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
|
||||
*
|
||||
* Adapted from coreboot src/arch/x86/boot/mpspec.c
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/ioapic.h>
|
||||
#include <asm/lapic.h>
|
||||
#include <asm/mpspec.h>
|
||||
#include <asm/tables.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static bool isa_irq_occupied[16];
|
||||
|
||||
struct mp_config_table *mp_write_floating_table(struct mp_floating_table *mf)
|
||||
{
|
||||
ulong mc;
|
||||
|
||||
memcpy(mf->mpf_signature, MPF_SIGNATURE, 4);
|
||||
mf->mpf_physptr = (ulong)mf + sizeof(struct mp_floating_table);
|
||||
mf->mpf_length = 1;
|
||||
mf->mpf_spec = MPSPEC_V14;
|
||||
mf->mpf_checksum = 0;
|
||||
/* We don't use the default configuration table */
|
||||
mf->mpf_feature1 = 0;
|
||||
/* Indicate that virtual wire mode is always implemented */
|
||||
mf->mpf_feature2 = 0;
|
||||
mf->mpf_feature3 = 0;
|
||||
mf->mpf_feature4 = 0;
|
||||
mf->mpf_feature5 = 0;
|
||||
mf->mpf_checksum = table_compute_checksum(mf, mf->mpf_length * 16);
|
||||
|
||||
mc = (ulong)mf + sizeof(struct mp_floating_table);
|
||||
return (struct mp_config_table *)mc;
|
||||
}
|
||||
|
||||
void mp_config_table_init(struct mp_config_table *mc)
|
||||
{
|
||||
memcpy(mc->mpc_signature, MPC_SIGNATURE, 4);
|
||||
mc->mpc_length = sizeof(struct mp_config_table);
|
||||
mc->mpc_spec = MPSPEC_V14;
|
||||
mc->mpc_checksum = 0;
|
||||
mc->mpc_oemptr = 0;
|
||||
mc->mpc_oemsize = 0;
|
||||
mc->mpc_entry_count = 0;
|
||||
mc->mpc_lapic = LAPIC_DEFAULT_BASE;
|
||||
mc->mpe_length = 0;
|
||||
mc->mpe_checksum = 0;
|
||||
mc->reserved = 0;
|
||||
|
||||
/* The oem/product id fields are exactly 8/12 bytes long */
|
||||
table_fill_string(mc->mpc_oem, CONFIG_SYS_VENDOR, 8, ' ');
|
||||
table_fill_string(mc->mpc_product, CONFIG_SYS_BOARD, 12, ' ');
|
||||
}
|
||||
|
||||
void mp_write_processor(struct mp_config_table *mc)
|
||||
{
|
||||
struct mpc_config_processor *mpc;
|
||||
struct udevice *dev;
|
||||
u8 boot_apicid, apicver;
|
||||
u32 cpusignature, cpufeature;
|
||||
struct cpuid_result result;
|
||||
|
||||
boot_apicid = lapicid();
|
||||
apicver = lapic_read(LAPIC_LVR) & 0xff;
|
||||
result = cpuid(1);
|
||||
cpusignature = result.eax;
|
||||
cpufeature = result.edx;
|
||||
|
||||
for (uclass_find_first_device(UCLASS_CPU, &dev);
|
||||
dev;
|
||||
uclass_find_next_device(&dev)) {
|
||||
struct cpu_platdata *plat = dev_get_parent_platdata(dev);
|
||||
u8 cpuflag = MPC_CPU_EN;
|
||||
|
||||
if (!device_active(dev))
|
||||
continue;
|
||||
|
||||
mpc = (struct mpc_config_processor *)mp_next_mpc_entry(mc);
|
||||
mpc->mpc_type = MP_PROCESSOR;
|
||||
mpc->mpc_apicid = plat->cpu_id;
|
||||
mpc->mpc_apicver = apicver;
|
||||
if (boot_apicid == plat->cpu_id)
|
||||
cpuflag |= MPC_CPU_BP;
|
||||
mpc->mpc_cpuflag = cpuflag;
|
||||
mpc->mpc_cpusignature = cpusignature;
|
||||
mpc->mpc_cpufeature = cpufeature;
|
||||
mpc->mpc_reserved[0] = 0;
|
||||
mpc->mpc_reserved[1] = 0;
|
||||
mp_add_mpc_entry(mc, sizeof(*mpc));
|
||||
}
|
||||
}
|
||||
|
||||
void mp_write_bus(struct mp_config_table *mc, int id, const char *bustype)
|
||||
{
|
||||
struct mpc_config_bus *mpc;
|
||||
|
||||
mpc = (struct mpc_config_bus *)mp_next_mpc_entry(mc);
|
||||
mpc->mpc_type = MP_BUS;
|
||||
mpc->mpc_busid = id;
|
||||
memcpy(mpc->mpc_bustype, bustype, 6);
|
||||
mp_add_mpc_entry(mc, sizeof(*mpc));
|
||||
}
|
||||
|
||||
void mp_write_ioapic(struct mp_config_table *mc, int id, int ver, u32 apicaddr)
|
||||
{
|
||||
struct mpc_config_ioapic *mpc;
|
||||
|
||||
mpc = (struct mpc_config_ioapic *)mp_next_mpc_entry(mc);
|
||||
mpc->mpc_type = MP_IOAPIC;
|
||||
mpc->mpc_apicid = id;
|
||||
mpc->mpc_apicver = ver;
|
||||
mpc->mpc_flags = MPC_APIC_USABLE;
|
||||
mpc->mpc_apicaddr = apicaddr;
|
||||
mp_add_mpc_entry(mc, sizeof(*mpc));
|
||||
}
|
||||
|
||||
void mp_write_intsrc(struct mp_config_table *mc, int irqtype, int irqflag,
|
||||
int srcbus, int srcbusirq, int dstapic, int dstirq)
|
||||
{
|
||||
struct mpc_config_intsrc *mpc;
|
||||
|
||||
mpc = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
|
||||
mpc->mpc_type = MP_INTSRC;
|
||||
mpc->mpc_irqtype = irqtype;
|
||||
mpc->mpc_irqflag = irqflag;
|
||||
mpc->mpc_srcbus = srcbus;
|
||||
mpc->mpc_srcbusirq = srcbusirq;
|
||||
mpc->mpc_dstapic = dstapic;
|
||||
mpc->mpc_dstirq = dstirq;
|
||||
mp_add_mpc_entry(mc, sizeof(*mpc));
|
||||
}
|
||||
|
||||
void mp_write_pci_intsrc(struct mp_config_table *mc, int irqtype,
|
||||
int srcbus, int dev, int pin, int dstapic, int dstirq)
|
||||
{
|
||||
u8 srcbusirq = (dev << 2) | (pin - 1);
|
||||
|
||||
mp_write_intsrc(mc, irqtype, MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_LOW,
|
||||
srcbus, srcbusirq, dstapic, dstirq);
|
||||
}
|
||||
|
||||
void mp_write_lintsrc(struct mp_config_table *mc, int irqtype, int irqflag,
|
||||
int srcbus, int srcbusirq, int destapic, int destlint)
|
||||
{
|
||||
struct mpc_config_lintsrc *mpc;
|
||||
|
||||
mpc = (struct mpc_config_lintsrc *)mp_next_mpc_entry(mc);
|
||||
mpc->mpc_type = MP_LINTSRC;
|
||||
mpc->mpc_irqtype = irqtype;
|
||||
mpc->mpc_irqflag = irqflag;
|
||||
mpc->mpc_srcbusid = srcbus;
|
||||
mpc->mpc_srcbusirq = srcbusirq;
|
||||
mpc->mpc_destapic = destapic;
|
||||
mpc->mpc_destlint = destlint;
|
||||
mp_add_mpc_entry(mc, sizeof(*mpc));
|
||||
}
|
||||
|
||||
void mp_write_address_space(struct mp_config_table *mc,
|
||||
int busid, int addr_type,
|
||||
u32 addr_base_low, u32 addr_base_high,
|
||||
u32 addr_length_low, u32 addr_length_high)
|
||||
{
|
||||
struct mp_ext_system_address_space *mpe;
|
||||
|
||||
mpe = (struct mp_ext_system_address_space *)mp_next_mpe_entry(mc);
|
||||
mpe->mpe_type = MPE_SYSTEM_ADDRESS_SPACE;
|
||||
mpe->mpe_length = sizeof(*mpe);
|
||||
mpe->mpe_busid = busid;
|
||||
mpe->mpe_addr_type = addr_type;
|
||||
mpe->mpe_addr_base_low = addr_base_low;
|
||||
mpe->mpe_addr_base_high = addr_base_high;
|
||||
mpe->mpe_addr_length_low = addr_length_low;
|
||||
mpe->mpe_addr_length_high = addr_length_high;
|
||||
mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
|
||||
}
|
||||
|
||||
void mp_write_bus_hierarchy(struct mp_config_table *mc,
|
||||
int busid, int bus_info, int parent_busid)
|
||||
{
|
||||
struct mp_ext_bus_hierarchy *mpe;
|
||||
|
||||
mpe = (struct mp_ext_bus_hierarchy *)mp_next_mpe_entry(mc);
|
||||
mpe->mpe_type = MPE_BUS_HIERARCHY;
|
||||
mpe->mpe_length = sizeof(*mpe);
|
||||
mpe->mpe_busid = busid;
|
||||
mpe->mpe_bus_info = bus_info;
|
||||
mpe->mpe_parent_busid = parent_busid;
|
||||
mpe->reserved[0] = 0;
|
||||
mpe->reserved[1] = 0;
|
||||
mpe->reserved[2] = 0;
|
||||
mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
|
||||
}
|
||||
|
||||
void mp_write_compat_address_space(struct mp_config_table *mc, int busid,
|
||||
int addr_modifier, u32 range_list)
|
||||
{
|
||||
struct mp_ext_compat_address_space *mpe;
|
||||
|
||||
mpe = (struct mp_ext_compat_address_space *)mp_next_mpe_entry(mc);
|
||||
mpe->mpe_type = MPE_COMPAT_ADDRESS_SPACE;
|
||||
mpe->mpe_length = sizeof(*mpe);
|
||||
mpe->mpe_busid = busid;
|
||||
mpe->mpe_addr_modifier = addr_modifier;
|
||||
mpe->mpe_range_list = range_list;
|
||||
mp_add_mpe_entry(mc, (struct mp_ext_config *)mpe);
|
||||
}
|
||||
|
||||
u32 mptable_finalize(struct mp_config_table *mc)
|
||||
{
|
||||
ulong end;
|
||||
|
||||
mc->mpe_checksum = table_compute_checksum((void *)mp_next_mpc_entry(mc),
|
||||
mc->mpe_length);
|
||||
mc->mpc_checksum = table_compute_checksum(mc, mc->mpc_length);
|
||||
end = mp_next_mpe_entry(mc);
|
||||
|
||||
debug("Write the MP table at: %lx - %lx\n", (ulong)mc, end);
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
static void mptable_add_isa_interrupts(struct mp_config_table *mc, int bus_isa,
|
||||
int apicid, int external_int2)
|
||||
{
|
||||
int i;
|
||||
|
||||
mp_write_intsrc(mc, external_int2 ? MP_INT : MP_EXTINT,
|
||||
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
|
||||
bus_isa, 0, apicid, 0);
|
||||
mp_write_intsrc(mc, MP_INT, MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
|
||||
bus_isa, 1, apicid, 1);
|
||||
mp_write_intsrc(mc, external_int2 ? MP_EXTINT : MP_INT,
|
||||
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
|
||||
bus_isa, 0, apicid, 2);
|
||||
|
||||
for (i = 3; i < 16; i++) {
|
||||
/*
|
||||
* Do not write ISA interrupt entry if it is already occupied
|
||||
* by the platform devices.
|
||||
*/
|
||||
if (isa_irq_occupied[i])
|
||||
continue;
|
||||
|
||||
mp_write_intsrc(mc, MP_INT,
|
||||
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
|
||||
bus_isa, i, apicid, i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check duplicated I/O interrupt assignment table entry, to make sure
|
||||
* there is only one entry with the given bus, device and interrupt pin.
|
||||
*/
|
||||
static bool check_dup_entry(struct mpc_config_intsrc *intsrc_base,
|
||||
int entry_num, int bus, int device, int pin)
|
||||
{
|
||||
struct mpc_config_intsrc *intsrc = intsrc_base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < entry_num; i++) {
|
||||
if (intsrc->mpc_srcbus == bus &&
|
||||
intsrc->mpc_srcbusirq == ((device << 2) | (pin - 1)))
|
||||
break;
|
||||
intsrc++;
|
||||
}
|
||||
|
||||
return (i == entry_num) ? false : true;
|
||||
}
|
||||
|
||||
/* TODO: move this to driver model */
|
||||
__weak int mp_determine_pci_dstirq(int bus, int dev, int func, int pirq)
|
||||
{
|
||||
/* PIRQ[A-H] are connected to I/O APIC INTPIN#16-23 */
|
||||
return pirq + 16;
|
||||
}
|
||||
|
||||
static int mptable_add_intsrc(struct mp_config_table *mc,
|
||||
int bus_isa, int apicid)
|
||||
{
|
||||
struct mpc_config_intsrc *intsrc_base;
|
||||
int intsrc_entries = 0;
|
||||
const void *blob = gd->fdt_blob;
|
||||
struct udevice *dev;
|
||||
int len, count;
|
||||
const u32 *cell;
|
||||
int i, ret;
|
||||
|
||||
ret = uclass_first_device_err(UCLASS_IRQ, &dev);
|
||||
if (ret && ret != -ENODEV) {
|
||||
debug("%s: Cannot find irq router node\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get I/O interrupt information from device tree */
|
||||
cell = fdt_getprop(blob, dev_of_offset(dev), "intel,pirq-routing",
|
||||
&len);
|
||||
if (!cell)
|
||||
return -ENOENT;
|
||||
|
||||
if ((len % sizeof(struct pirq_routing)) == 0)
|
||||
count = len / sizeof(struct pirq_routing);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
intsrc_base = (struct mpc_config_intsrc *)mp_next_mpc_entry(mc);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct pirq_routing pr;
|
||||
int bus, dev, func;
|
||||
int dstirq;
|
||||
|
||||
pr.bdf = fdt_addr_to_cpu(cell[0]);
|
||||
pr.pin = fdt_addr_to_cpu(cell[1]);
|
||||
pr.pirq = fdt_addr_to_cpu(cell[2]);
|
||||
bus = PCI_BUS(pr.bdf);
|
||||
dev = PCI_DEV(pr.bdf);
|
||||
func = PCI_FUNC(pr.bdf);
|
||||
|
||||
if (check_dup_entry(intsrc_base, intsrc_entries,
|
||||
bus, dev, pr.pin)) {
|
||||
debug("found entry for bus %d device %d INT%c, skipping\n",
|
||||
bus, dev, 'A' + pr.pin - 1);
|
||||
cell += sizeof(struct pirq_routing) / sizeof(u32);
|
||||
continue;
|
||||
}
|
||||
|
||||
dstirq = mp_determine_pci_dstirq(bus, dev, func, pr.pirq);
|
||||
/*
|
||||
* For PIRQ which is connected to I/O APIC interrupt pin#0-15,
|
||||
* mark it as occupied so that we can skip it later.
|
||||
*/
|
||||
if (dstirq < 16)
|
||||
isa_irq_occupied[dstirq] = true;
|
||||
mp_write_pci_intsrc(mc, MP_INT, bus, dev, pr.pin,
|
||||
apicid, dstirq);
|
||||
intsrc_entries++;
|
||||
cell += sizeof(struct pirq_routing) / sizeof(u32);
|
||||
}
|
||||
|
||||
/* Legacy Interrupts */
|
||||
debug("Writing ISA IRQs\n");
|
||||
mptable_add_isa_interrupts(mc, bus_isa, apicid, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mptable_add_lintsrc(struct mp_config_table *mc, int bus_isa)
|
||||
{
|
||||
mp_write_lintsrc(mc, MP_EXTINT,
|
||||
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
|
||||
bus_isa, 0, MP_APIC_ALL, 0);
|
||||
mp_write_lintsrc(mc, MP_NMI,
|
||||
MP_IRQ_TRIGGER_EDGE | MP_IRQ_POLARITY_HIGH,
|
||||
bus_isa, 0, MP_APIC_ALL, 1);
|
||||
}
|
||||
|
||||
ulong write_mp_table(ulong addr)
|
||||
{
|
||||
struct mp_config_table *mc;
|
||||
int ioapic_id, ioapic_ver;
|
||||
int bus_isa = 0xff;
|
||||
int ret;
|
||||
ulong end;
|
||||
|
||||
/* 16 byte align the table address */
|
||||
addr = ALIGN(addr, 16);
|
||||
|
||||
/* Write floating table */
|
||||
mc = mp_write_floating_table((struct mp_floating_table *)addr);
|
||||
|
||||
/* Write configuration table header */
|
||||
mp_config_table_init(mc);
|
||||
|
||||
/* Write processor entry */
|
||||
mp_write_processor(mc);
|
||||
|
||||
/* Write bus entry */
|
||||
mp_write_bus(mc, bus_isa, BUSTYPE_ISA);
|
||||
|
||||
/* Write I/O APIC entry */
|
||||
ioapic_id = io_apic_read(IO_APIC_ID) >> 24;
|
||||
ioapic_ver = io_apic_read(IO_APIC_VER) & 0xff;
|
||||
mp_write_ioapic(mc, ioapic_id, ioapic_ver, IO_APIC_ADDR);
|
||||
|
||||
/* Write I/O interrupt assignment entry */
|
||||
ret = mptable_add_intsrc(mc, bus_isa, ioapic_id);
|
||||
if (ret)
|
||||
debug("Failed to write I/O interrupt assignment table\n");
|
||||
|
||||
/* Write local interrupt assignment entry */
|
||||
mptable_add_lintsrc(mc, bus_isa);
|
||||
|
||||
/* Finalize the MP table */
|
||||
end = mptable_finalize(mc);
|
||||
|
||||
return end;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* From coreboot src/southbridge/intel/bd82x6x/mrccache.c
|
||||
*
|
||||
* Copyright (C) 2014 Google Inc.
|
||||
* Copyright (C) 2015 Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <net.h>
|
||||
#include <spi.h>
|
||||
#include <spi_flash.h>
|
||||
#include <asm/mrccache.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static uint mrc_block_size(uint data_size)
|
||||
{
|
||||
uint mrc_size = sizeof(struct mrc_data_container) + data_size;
|
||||
|
||||
return ALIGN(mrc_size, MRC_DATA_ALIGN);
|
||||
}
|
||||
|
||||
static struct mrc_data_container *next_mrc_block(
|
||||
struct mrc_data_container *cache)
|
||||
{
|
||||
/* MRC data blocks are aligned within the region */
|
||||
u8 *region_ptr = (u8 *)cache;
|
||||
|
||||
region_ptr += mrc_block_size(cache->data_size);
|
||||
|
||||
return (struct mrc_data_container *)region_ptr;
|
||||
}
|
||||
|
||||
static int is_mrc_cache(struct mrc_data_container *cache)
|
||||
{
|
||||
return cache && (cache->signature == MRC_DATA_SIGNATURE);
|
||||
}
|
||||
|
||||
struct mrc_data_container *mrccache_find_current(struct mrc_region *entry)
|
||||
{
|
||||
struct mrc_data_container *cache, *next;
|
||||
ulong base_addr, end_addr;
|
||||
uint id;
|
||||
|
||||
base_addr = entry->base + entry->offset;
|
||||
end_addr = base_addr + entry->length;
|
||||
cache = NULL;
|
||||
|
||||
/* Search for the last filled entry in the region */
|
||||
for (id = 0, next = (struct mrc_data_container *)base_addr;
|
||||
is_mrc_cache(next);
|
||||
id++) {
|
||||
cache = next;
|
||||
next = next_mrc_block(next);
|
||||
if ((ulong)next >= end_addr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (id-- == 0) {
|
||||
debug("%s: No valid MRC cache found.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Verify checksum */
|
||||
if (cache->checksum != compute_ip_checksum(cache->data,
|
||||
cache->data_size)) {
|
||||
printf("%s: MRC cache checksum mismatch\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
debug("%s: picked entry %u from cache block\n", __func__, id);
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_next_mrc_cache() - get next cache entry
|
||||
*
|
||||
* @entry: MRC cache flash area
|
||||
* @cache: Entry to start from
|
||||
*
|
||||
* @return next cache entry if found, NULL if we got to the end
|
||||
*/
|
||||
static struct mrc_data_container *find_next_mrc_cache(struct mrc_region *entry,
|
||||
struct mrc_data_container *cache)
|
||||
{
|
||||
ulong base_addr, end_addr;
|
||||
|
||||
base_addr = entry->base + entry->offset;
|
||||
end_addr = base_addr + entry->length;
|
||||
|
||||
cache = next_mrc_block(cache);
|
||||
if ((ulong)cache >= end_addr) {
|
||||
/* Crossed the boundary */
|
||||
cache = NULL;
|
||||
debug("%s: no available entries found\n", __func__);
|
||||
} else {
|
||||
debug("%s: picked next entry from cache block at %p\n",
|
||||
__func__, cache);
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
int mrccache_update(struct udevice *sf, struct mrc_region *entry,
|
||||
struct mrc_data_container *cur)
|
||||
{
|
||||
struct mrc_data_container *cache;
|
||||
ulong offset;
|
||||
ulong base_addr;
|
||||
int ret;
|
||||
|
||||
if (!is_mrc_cache(cur)) {
|
||||
debug("%s: Cache data not valid\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Find the last used block */
|
||||
base_addr = entry->base + entry->offset;
|
||||
debug("Updating MRC cache data\n");
|
||||
cache = mrccache_find_current(entry);
|
||||
if (cache && (cache->data_size == cur->data_size) &&
|
||||
(!memcmp(cache, cur, cache->data_size + sizeof(*cur)))) {
|
||||
debug("MRC data in flash is up to date. No update\n");
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
/* Move to the next block, which will be the first unused block */
|
||||
if (cache)
|
||||
cache = find_next_mrc_cache(entry, cache);
|
||||
|
||||
/*
|
||||
* If we have got to the end, erase the entire mrc-cache area and start
|
||||
* again at block 0.
|
||||
*/
|
||||
if (!cache) {
|
||||
debug("Erasing the MRC cache region of %x bytes at %x\n",
|
||||
entry->length, entry->offset);
|
||||
|
||||
ret = spi_flash_erase_dm(sf, entry->offset, entry->length);
|
||||
if (ret) {
|
||||
debug("Failed to erase flash region\n");
|
||||
return ret;
|
||||
}
|
||||
cache = (struct mrc_data_container *)base_addr;
|
||||
}
|
||||
|
||||
/* Write the data out */
|
||||
offset = (ulong)cache - base_addr + entry->offset;
|
||||
debug("Write MRC cache update to flash at %lx\n", offset);
|
||||
ret = spi_flash_write_dm(sf, offset, cur->data_size + sizeof(*cur),
|
||||
cur);
|
||||
if (ret) {
|
||||
debug("Failed to write to SPI flash\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mrccache_setup(void *data)
|
||||
{
|
||||
struct mrc_data_container *cache = data;
|
||||
u16 checksum;
|
||||
|
||||
cache->signature = MRC_DATA_SIGNATURE;
|
||||
cache->data_size = gd->arch.mrc_output_len;
|
||||
checksum = compute_ip_checksum(gd->arch.mrc_output, cache->data_size);
|
||||
debug("Saving %d bytes for MRC output data, checksum %04x\n",
|
||||
cache->data_size, checksum);
|
||||
cache->checksum = checksum;
|
||||
cache->reserved = 0;
|
||||
memcpy(cache->data, gd->arch.mrc_output, cache->data_size);
|
||||
|
||||
/* gd->arch.mrc_output now points to the container */
|
||||
gd->arch.mrc_output = (char *)cache;
|
||||
}
|
||||
|
||||
int mrccache_reserve(void)
|
||||
{
|
||||
if (!gd->arch.mrc_output_len)
|
||||
return 0;
|
||||
|
||||
/* adjust stack pointer to store pure cache data plus the header */
|
||||
gd->start_addr_sp -= (gd->arch.mrc_output_len + MRC_DATA_HEADER_SIZE);
|
||||
mrccache_setup((void *)gd->start_addr_sp);
|
||||
|
||||
gd->start_addr_sp &= ~0xf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mrccache_get_region(struct udevice **devp, struct mrc_region *entry)
|
||||
{
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node, mrc_node;
|
||||
u32 reg[2];
|
||||
int ret;
|
||||
|
||||
/* Find the flash chip within the SPI controller node */
|
||||
node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
|
||||
if (node < 0) {
|
||||
debug("%s: Cannot find SPI flash\n", __func__);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (fdtdec_get_int_array(blob, node, "memory-map", reg, 2)) {
|
||||
debug("%s: Cannot find memory map\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
entry->base = reg[0];
|
||||
|
||||
/* Find the place where we put the MRC cache */
|
||||
mrc_node = fdt_subnode_offset(blob, node, "rw-mrc-cache");
|
||||
if (mrc_node < 0) {
|
||||
debug("%s: Cannot find node\n", __func__);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (fdtdec_get_int_array(blob, mrc_node, "reg", reg, 2)) {
|
||||
debug("%s: Cannot find address\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
entry->offset = reg[0];
|
||||
entry->length = reg[1];
|
||||
|
||||
if (devp) {
|
||||
ret = uclass_get_device_by_of_offset(UCLASS_SPI_FLASH, node,
|
||||
devp);
|
||||
debug("ret = %d\n", ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mrccache_save(void)
|
||||
{
|
||||
struct mrc_data_container *data;
|
||||
struct mrc_region entry;
|
||||
struct udevice *sf;
|
||||
int ret;
|
||||
|
||||
if (!gd->arch.mrc_output_len)
|
||||
return 0;
|
||||
debug("Saving %d bytes of MRC output data to SPI flash\n",
|
||||
gd->arch.mrc_output_len);
|
||||
|
||||
ret = mrccache_get_region(&sf, &entry);
|
||||
if (ret)
|
||||
goto err_entry;
|
||||
data = (struct mrc_data_container *)gd->arch.mrc_output;
|
||||
ret = mrccache_update(sf, &entry, data);
|
||||
if (!ret) {
|
||||
debug("Saved MRC data with checksum %04x\n", data->checksum);
|
||||
} else if (ret == -EEXIST) {
|
||||
debug("MRC data is the same as last time, skipping save\n");
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
err_entry:
|
||||
if (ret)
|
||||
debug("%s: Failed: %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mrccache_spl_save(void)
|
||||
{
|
||||
void *data;
|
||||
int size;
|
||||
|
||||
size = gd->arch.mrc_output_len + MRC_DATA_HEADER_SIZE;
|
||||
data = malloc(size);
|
||||
if (!data)
|
||||
return log_msg_ret("Allocate MRC cache block", -ENOMEM);
|
||||
mrccache_setup(data);
|
||||
gd->arch.mrc_output = data;
|
||||
|
||||
return mrccache_save();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/root.h>
|
||||
|
||||
UCLASS_DRIVER(northbridge) = {
|
||||
.id = UCLASS_NORTHBRIDGE,
|
||||
.name = "northbridge",
|
||||
};
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <physmem.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* Large pages are 2MB. */
|
||||
#define LARGE_PAGE_SIZE ((1 << 20) * 2)
|
||||
|
||||
/*
|
||||
* Paging data structures.
|
||||
*/
|
||||
|
||||
struct pdpe {
|
||||
uint64_t p:1;
|
||||
uint64_t mbz_0:2;
|
||||
uint64_t pwt:1;
|
||||
uint64_t pcd:1;
|
||||
uint64_t mbz_1:4;
|
||||
uint64_t avl:3;
|
||||
uint64_t base:40;
|
||||
uint64_t mbz_2:12;
|
||||
};
|
||||
|
||||
typedef struct pdpe pdpt_t[512];
|
||||
|
||||
struct pde {
|
||||
uint64_t p:1; /* present */
|
||||
uint64_t rw:1; /* read/write */
|
||||
uint64_t us:1; /* user/supervisor */
|
||||
uint64_t pwt:1; /* page-level writethrough */
|
||||
uint64_t pcd:1; /* page-level cache disable */
|
||||
uint64_t a:1; /* accessed */
|
||||
uint64_t d:1; /* dirty */
|
||||
uint64_t ps:1; /* page size */
|
||||
uint64_t g:1; /* global page */
|
||||
uint64_t avl:3; /* available to software */
|
||||
uint64_t pat:1; /* page-attribute table */
|
||||
uint64_t mbz_0:8; /* must be zero */
|
||||
uint64_t base:31; /* base address */
|
||||
};
|
||||
|
||||
typedef struct pde pdt_t[512];
|
||||
|
||||
static pdpt_t pdpt __aligned(4096);
|
||||
static pdt_t pdts[4] __aligned(4096);
|
||||
|
||||
/*
|
||||
* Map a virtual address to a physical address and optionally invalidate any
|
||||
* old mapping.
|
||||
*
|
||||
* @param virt The virtual address to use.
|
||||
* @param phys The physical address to use.
|
||||
* @param invlpg Whether to use invlpg to clear any old mappings.
|
||||
*/
|
||||
static void x86_phys_map_page(uintptr_t virt, phys_addr_t phys, int invlpg)
|
||||
{
|
||||
/* Extract the two bit PDPT index and the 9 bit PDT index. */
|
||||
uintptr_t pdpt_idx = (virt >> 30) & 0x3;
|
||||
uintptr_t pdt_idx = (virt >> 21) & 0x1ff;
|
||||
|
||||
/* Set up a handy pointer to the appropriate PDE. */
|
||||
struct pde *pde = &(pdts[pdpt_idx][pdt_idx]);
|
||||
|
||||
memset(pde, 0, sizeof(struct pde));
|
||||
pde->p = 1;
|
||||
pde->rw = 1;
|
||||
pde->us = 1;
|
||||
pde->ps = 1;
|
||||
pde->base = phys >> 21;
|
||||
|
||||
if (invlpg) {
|
||||
/* Flush any stale mapping out of the TLBs. */
|
||||
__asm__ __volatile__(
|
||||
"invlpg %0\n\t"
|
||||
:
|
||||
: "m" (*(uint8_t *)virt)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Identity map the lower 4GB and turn on paging with PAE. */
|
||||
static void x86_phys_enter_paging(void)
|
||||
{
|
||||
phys_addr_t page_addr;
|
||||
unsigned i;
|
||||
|
||||
/* Zero out the page tables. */
|
||||
memset(pdpt, 0, sizeof(pdpt));
|
||||
memset(pdts, 0, sizeof(pdts));
|
||||
|
||||
/* Set up the PDPT. */
|
||||
for (i = 0; i < ARRAY_SIZE(pdts); i++) {
|
||||
pdpt[i].p = 1;
|
||||
pdpt[i].base = ((uintptr_t)&pdts[i]) >> 12;
|
||||
}
|
||||
|
||||
/* Identity map everything up to 4GB. */
|
||||
for (page_addr = 0; page_addr < (1ULL << 32);
|
||||
page_addr += LARGE_PAGE_SIZE) {
|
||||
/* There's no reason to invalidate the TLB with paging off. */
|
||||
x86_phys_map_page(page_addr, page_addr, 0);
|
||||
}
|
||||
|
||||
cpu_enable_paging_pae((ulong)pdpt);
|
||||
}
|
||||
|
||||
/* Disable paging and PAE mode. */
|
||||
static void x86_phys_exit_paging(void)
|
||||
{
|
||||
cpu_disable_paging_pae();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set physical memory to a particular value when the whole region fits on one
|
||||
* page.
|
||||
*
|
||||
* @param map_addr The address that starts the physical page.
|
||||
* @param offset How far into that page to start setting a value.
|
||||
* @param c The value to set memory to.
|
||||
* @param size The size in bytes of the area to set.
|
||||
*/
|
||||
static void x86_phys_memset_page(phys_addr_t map_addr, uintptr_t offset, int c,
|
||||
unsigned size)
|
||||
{
|
||||
/*
|
||||
* U-Boot should be far away from the beginning of memory, so that's a
|
||||
* good place to map our window on top of.
|
||||
*/
|
||||
const uintptr_t window = LARGE_PAGE_SIZE;
|
||||
|
||||
/* Make sure the window is below U-Boot. */
|
||||
assert(window + LARGE_PAGE_SIZE <
|
||||
gd->relocaddr - CONFIG_SYS_MALLOC_LEN - CONFIG_SYS_STACK_SIZE);
|
||||
/* Map the page into the window and then memset the appropriate part. */
|
||||
x86_phys_map_page(window, map_addr, 1);
|
||||
memset((void *)(window + offset), c, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* A physical memory anologue to memset with matching parameters and return
|
||||
* value.
|
||||
*/
|
||||
phys_addr_t arch_phys_memset(phys_addr_t start, int c, phys_size_t size)
|
||||
{
|
||||
const phys_addr_t max_addr = (phys_addr_t)~(uintptr_t)0;
|
||||
const phys_addr_t orig_start = start;
|
||||
|
||||
if (!size)
|
||||
return orig_start;
|
||||
|
||||
/* Handle memory below 4GB. */
|
||||
if (start <= max_addr) {
|
||||
phys_size_t low_size = min(max_addr + 1 - start, size);
|
||||
void *start_ptr = (void *)(uintptr_t)start;
|
||||
|
||||
assert(((phys_addr_t)(uintptr_t)start) == start);
|
||||
memset(start_ptr, c, low_size);
|
||||
start += low_size;
|
||||
size -= low_size;
|
||||
}
|
||||
|
||||
/* Use paging and PAE to handle memory above 4GB up to 64GB. */
|
||||
if (size) {
|
||||
phys_addr_t map_addr = start & ~(LARGE_PAGE_SIZE - 1);
|
||||
phys_addr_t offset = start - map_addr;
|
||||
|
||||
x86_phys_enter_paging();
|
||||
|
||||
/* Handle the first partial page. */
|
||||
if (offset) {
|
||||
phys_addr_t end =
|
||||
min(map_addr + LARGE_PAGE_SIZE, start + size);
|
||||
phys_size_t cur_size = end - start;
|
||||
x86_phys_memset_page(map_addr, offset, c, cur_size);
|
||||
size -= cur_size;
|
||||
map_addr += LARGE_PAGE_SIZE;
|
||||
}
|
||||
/* Handle the complete pages. */
|
||||
while (size > LARGE_PAGE_SIZE) {
|
||||
x86_phys_memset_page(map_addr, 0, c, LARGE_PAGE_SIZE);
|
||||
size -= LARGE_PAGE_SIZE;
|
||||
map_addr += LARGE_PAGE_SIZE;
|
||||
}
|
||||
/* Handle the last partial page. */
|
||||
if (size)
|
||||
x86_phys_memset_page(map_addr, 0, c, size);
|
||||
|
||||
x86_phys_exit_paging();
|
||||
}
|
||||
return orig_start;
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2016 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <pch.h>
|
||||
#include <pci.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pci.h>
|
||||
#include <dm/pinctrl.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define GPIO_USESEL_OFFSET(x) (x)
|
||||
#define GPIO_IOSEL_OFFSET(x) (x + 4)
|
||||
#define GPIO_LVL_OFFSET(x) ((x) ? (x) + 8 : 0xc)
|
||||
#define GPI_INV 0x2c
|
||||
|
||||
#define IOPAD_MODE_MASK 0x7
|
||||
#define IOPAD_PULL_ASSIGN_SHIFT 7
|
||||
#define IOPAD_PULL_ASSIGN_MASK (0x3 << IOPAD_PULL_ASSIGN_SHIFT)
|
||||
#define IOPAD_PULL_STRENGTH_SHIFT 9
|
||||
#define IOPAD_PULL_STRENGTH_MASK (0x3 << IOPAD_PULL_STRENGTH_SHIFT)
|
||||
|
||||
static int ich6_pinctrl_set_value(uint16_t base, unsigned offset, int value)
|
||||
{
|
||||
if (value)
|
||||
setio_32(base, 1UL << offset);
|
||||
else
|
||||
clrio_32(base, 1UL << offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ich6_pinctrl_set_function(uint16_t base, unsigned offset, int func)
|
||||
{
|
||||
if (func)
|
||||
setio_32(base, 1UL << offset);
|
||||
else
|
||||
clrio_32(base, 1UL << offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ich6_pinctrl_set_direction(uint16_t base, unsigned offset, int dir)
|
||||
{
|
||||
if (!dir)
|
||||
setio_32(base, 1UL << offset);
|
||||
else
|
||||
clrio_32(base, 1UL << offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ich6_pinctrl_cfg_pin(s32 gpiobase, s32 iobase, int pin_node)
|
||||
{
|
||||
bool is_gpio, invert;
|
||||
u32 gpio_offset[2];
|
||||
int pad_offset;
|
||||
int dir, val;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* GPIO node is not mandatory, so we only do the pinmuxing if the
|
||||
* node exists.
|
||||
*/
|
||||
ret = fdtdec_get_int_array(gd->fdt_blob, pin_node, "gpio-offset",
|
||||
gpio_offset, 2);
|
||||
if (!ret) {
|
||||
/* Do we want to force the GPIO mode? */
|
||||
is_gpio = fdtdec_get_bool(gd->fdt_blob, pin_node, "mode-gpio");
|
||||
if (is_gpio)
|
||||
ich6_pinctrl_set_function(GPIO_USESEL_OFFSET(gpiobase) +
|
||||
gpio_offset[0], gpio_offset[1],
|
||||
1);
|
||||
|
||||
dir = fdtdec_get_int(gd->fdt_blob, pin_node, "direction", -1);
|
||||
if (dir != -1)
|
||||
ich6_pinctrl_set_direction(GPIO_IOSEL_OFFSET(gpiobase) +
|
||||
gpio_offset[0], gpio_offset[1],
|
||||
dir);
|
||||
|
||||
val = fdtdec_get_int(gd->fdt_blob, pin_node, "output-value",
|
||||
-1);
|
||||
if (val != -1)
|
||||
ich6_pinctrl_set_value(GPIO_LVL_OFFSET(gpiobase) +
|
||||
gpio_offset[0], gpio_offset[1],
|
||||
val);
|
||||
|
||||
invert = fdtdec_get_bool(gd->fdt_blob, pin_node, "invert");
|
||||
if (invert)
|
||||
setio_32(gpiobase + GPI_INV, 1 << gpio_offset[1]);
|
||||
debug("gpio %#x bit %d, is_gpio %d, dir %d, val %d, invert %d\n",
|
||||
gpio_offset[0], gpio_offset[1], is_gpio, dir, val,
|
||||
invert);
|
||||
}
|
||||
|
||||
/* if iobase is present, let's configure the pad */
|
||||
if (iobase != -1) {
|
||||
ulong iobase_addr;
|
||||
|
||||
/*
|
||||
* The offset for the same pin for the IOBASE and GPIOBASE are
|
||||
* different, so instead of maintaining a lookup table,
|
||||
* the device tree should provide directly the correct
|
||||
* value for both mapping.
|
||||
*/
|
||||
pad_offset = fdtdec_get_int(gd->fdt_blob, pin_node,
|
||||
"pad-offset", -1);
|
||||
if (pad_offset == -1)
|
||||
return 0;
|
||||
|
||||
/* compute the absolute pad address */
|
||||
iobase_addr = iobase + pad_offset;
|
||||
|
||||
/*
|
||||
* Do we need to set a specific function mode?
|
||||
* If someone put also 'mode-gpio', this option will
|
||||
* be just ignored by the controller
|
||||
*/
|
||||
val = fdtdec_get_int(gd->fdt_blob, pin_node, "mode-func", -1);
|
||||
if (val != -1)
|
||||
clrsetbits_le32(iobase_addr, IOPAD_MODE_MASK, val);
|
||||
|
||||
/* Configure the pull-up/down if needed */
|
||||
val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-assign", -1);
|
||||
if (val != -1)
|
||||
clrsetbits_le32(iobase_addr,
|
||||
IOPAD_PULL_ASSIGN_MASK,
|
||||
val << IOPAD_PULL_ASSIGN_SHIFT);
|
||||
|
||||
val = fdtdec_get_int(gd->fdt_blob, pin_node, "pull-strength",
|
||||
-1);
|
||||
if (val != -1)
|
||||
clrsetbits_le32(iobase_addr,
|
||||
IOPAD_PULL_STRENGTH_MASK,
|
||||
val << IOPAD_PULL_STRENGTH_SHIFT);
|
||||
|
||||
debug("%s: pad cfg [0x%x]: %08x\n", __func__, pad_offset,
|
||||
readl(iobase_addr));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ich6_pinctrl_probe(struct udevice *dev)
|
||||
{
|
||||
struct udevice *pch;
|
||||
int pin_node;
|
||||
int ret;
|
||||
u32 gpiobase;
|
||||
u32 iobase = -1;
|
||||
|
||||
debug("%s: start\n", __func__);
|
||||
ret = uclass_first_device(UCLASS_PCH, &pch);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!pch)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Get the memory/io base address to configure every pins.
|
||||
* IOBASE is used to configure the mode/pads
|
||||
* GPIOBASE is used to configure the direction and default value
|
||||
*/
|
||||
ret = pch_get_gpio_base(pch, &gpiobase);
|
||||
if (ret) {
|
||||
debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
|
||||
gpiobase);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the IOBASE, this is not mandatory as this is not
|
||||
* supported by all the CPU
|
||||
*/
|
||||
ret = pch_get_io_base(pch, &iobase);
|
||||
if (ret && ret != -ENOSYS) {
|
||||
debug("%s: invalid IOBASE address (%08x)\n", __func__, iobase);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (pin_node = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev));
|
||||
pin_node > 0;
|
||||
pin_node = fdt_next_subnode(gd->fdt_blob, pin_node)) {
|
||||
/* Configure the pin */
|
||||
ret = ich6_pinctrl_cfg_pin(gpiobase, iobase, pin_node);
|
||||
if (ret != 0) {
|
||||
debug("%s: invalid configuration for the pin %d\n",
|
||||
__func__, pin_node);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
debug("%s: done\n", __func__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id ich6_pinctrl_match[] = {
|
||||
{ .compatible = "intel,x86-pinctrl", .data = X86_SYSCON_PINCONF },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(ich6_pinctrl) = {
|
||||
.name = "ich6_pinctrl",
|
||||
.id = UCLASS_SYSCON,
|
||||
.of_match = ich6_pinctrl_match,
|
||||
.probe = ich6_pinctrl_probe,
|
||||
};
|
||||
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
|
||||
*
|
||||
* Part of this file is ported from coreboot src/arch/x86/boot/pirq_routing.c
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <asm/pci.h>
|
||||
#include <asm/pirq_routing.h>
|
||||
|
||||
static u8 pirq_get_next_free_irq(struct udevice *dev, u8 *pirq, u16 bitmap,
|
||||
bool irq_already_routed[])
|
||||
{
|
||||
int i, link;
|
||||
u8 irq = 0;
|
||||
|
||||
/* IRQ sharing starts from IRQ#3 */
|
||||
for (i = 3; i < 16; i++) {
|
||||
/* Can we assign this IRQ? */
|
||||
if (!((bitmap >> i) & 1))
|
||||
continue;
|
||||
|
||||
/* We can, now let's assume we can use this IRQ */
|
||||
irq = i;
|
||||
|
||||
/* Have we already routed it? */
|
||||
if (irq_already_routed[irq])
|
||||
continue;
|
||||
|
||||
for (link = 0; link < CONFIG_MAX_PIRQ_LINKS; link++) {
|
||||
if (pirq_check_irq_routed(dev, link, irq)) {
|
||||
irq_already_routed[irq] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If it's not yet routed, use it */
|
||||
if (!irq_already_routed[irq]) {
|
||||
irq_already_routed[irq] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* But if it was already routed, try the next one */
|
||||
}
|
||||
|
||||
/* Now we get our IRQ */
|
||||
return irq;
|
||||
}
|
||||
|
||||
void pirq_route_irqs(struct udevice *dev, struct irq_info *irq, int num)
|
||||
{
|
||||
unsigned char irq_slot[MAX_INTX_ENTRIES];
|
||||
unsigned char pirq[CONFIG_MAX_PIRQ_LINKS];
|
||||
bool irq_already_routed[16];
|
||||
int i, intx;
|
||||
|
||||
memset(pirq, 0, CONFIG_MAX_PIRQ_LINKS);
|
||||
memset(irq_already_routed, '\0', sizeof(irq_already_routed));
|
||||
|
||||
/* Set PCI IRQs */
|
||||
for (i = 0; i < num; i++) {
|
||||
debug("PIRQ Entry %d Dev: %d.%x.%d\n", i,
|
||||
irq->bus, irq->devfn >> 3, irq->devfn & 7);
|
||||
|
||||
for (intx = 0; intx < MAX_INTX_ENTRIES; intx++) {
|
||||
int link = irq->irq[intx].link;
|
||||
int bitmap = irq->irq[intx].bitmap;
|
||||
int irq = 0;
|
||||
|
||||
debug("INT%c link: %x bitmap: %x ",
|
||||
'A' + intx, link, bitmap);
|
||||
|
||||
if (!bitmap || !link) {
|
||||
debug("not routed\n");
|
||||
irq_slot[intx] = irq;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* translate link value to link number */
|
||||
link = pirq_translate_link(dev, link);
|
||||
|
||||
/* yet not routed */
|
||||
if (!pirq[link]) {
|
||||
irq = pirq_get_next_free_irq(dev, pirq, bitmap,
|
||||
irq_already_routed);
|
||||
pirq[link] = irq;
|
||||
} else {
|
||||
irq = pirq[link];
|
||||
}
|
||||
|
||||
debug("IRQ: %d\n", irq);
|
||||
irq_slot[intx] = irq;
|
||||
|
||||
/* Assign IRQ in the interrupt router */
|
||||
pirq_assign_irq(dev, link, irq);
|
||||
}
|
||||
|
||||
/* Bus, device, slots IRQs for {A,B,C,D} */
|
||||
pci_assign_irqs(irq->bus, irq->devfn >> 3, irq_slot);
|
||||
|
||||
irq++;
|
||||
}
|
||||
|
||||
for (i = 0; i < CONFIG_MAX_PIRQ_LINKS; i++)
|
||||
debug("PIRQ%c: %d\n", 'A' + i, pirq[i]);
|
||||
}
|
||||
|
||||
u32 copy_pirq_routing_table(u32 addr, struct irq_routing_table *rt)
|
||||
{
|
||||
struct irq_routing_table *rom_rt;
|
||||
|
||||
/* Align the table to be 16 byte aligned */
|
||||
addr = ALIGN(addr, 16);
|
||||
|
||||
debug("Copying Interrupt Routing Table to 0x%x\n", addr);
|
||||
memcpy((void *)(uintptr_t)addr, rt, rt->size);
|
||||
|
||||
/*
|
||||
* We do the sanity check here against the copied table after memcpy,
|
||||
* as something might go wrong after the memcpy, which is normally
|
||||
* due to the F segment decode is not turned on to systeam RAM.
|
||||
*/
|
||||
rom_rt = (struct irq_routing_table *)(uintptr_t)addr;
|
||||
if (rom_rt->signature != PIRQ_SIGNATURE ||
|
||||
rom_rt->version != PIRQ_VERSION || rom_rt->size % 16) {
|
||||
printf("Interrupt Routing Table not valid\n");
|
||||
return addr;
|
||||
}
|
||||
|
||||
return addr + rt->size;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/pmu.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
/* Registers */
|
||||
struct pmu_regs {
|
||||
u32 sts;
|
||||
u32 cmd;
|
||||
u32 ics;
|
||||
u32 reserved;
|
||||
u32 wkc[4];
|
||||
u32 wks[4];
|
||||
u32 ssc[4];
|
||||
u32 sss[4];
|
||||
};
|
||||
|
||||
/* Bits in PMU_REGS_STS */
|
||||
#define PMU_REGS_STS_BUSY (1 << 8)
|
||||
|
||||
struct pmu_mid {
|
||||
struct pmu_regs *regs;
|
||||
};
|
||||
|
||||
static int pmu_read_status(struct pmu_regs *regs)
|
||||
{
|
||||
int retry = 500000;
|
||||
u32 val;
|
||||
|
||||
do {
|
||||
val = readl(®s->sts);
|
||||
if (!(val & PMU_REGS_STS_BUSY))
|
||||
return 0;
|
||||
|
||||
udelay(1);
|
||||
} while (--retry);
|
||||
|
||||
printf("WARNING: PMU still busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int pmu_power_lss(struct pmu_regs *regs, unsigned int lss, bool on)
|
||||
{
|
||||
unsigned int offset = (lss * 2) / 32;
|
||||
unsigned int shift = (lss * 2) % 32;
|
||||
u32 ssc;
|
||||
int ret;
|
||||
|
||||
/* Check PMU status */
|
||||
ret = pmu_read_status(regs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read PMU values */
|
||||
ssc = readl(®s->sss[offset]);
|
||||
|
||||
/* Modify PMU values */
|
||||
if (on)
|
||||
ssc &= ~(0x3 << shift); /* D0 */
|
||||
else
|
||||
ssc |= 0x3 << shift; /* D3hot */
|
||||
|
||||
/* Write modified PMU values */
|
||||
writel(ssc, ®s->ssc[offset]);
|
||||
|
||||
/* Update modified PMU values */
|
||||
writel(0x00002201, ®s->cmd);
|
||||
|
||||
/* Check PMU status */
|
||||
return pmu_read_status(regs);
|
||||
}
|
||||
|
||||
int pmu_turn_power(unsigned int lss, bool on)
|
||||
{
|
||||
struct pmu_mid *pmu;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = syscon_get_by_driver_data(X86_SYSCON_PMU, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pmu = dev_get_priv(dev);
|
||||
|
||||
return pmu_power_lss(pmu->regs, lss, on);
|
||||
}
|
||||
|
||||
static int pmu_mid_probe(struct udevice *dev)
|
||||
{
|
||||
struct pmu_mid *pmu = dev_get_priv(dev);
|
||||
|
||||
pmu->regs = syscon_get_first_range(X86_SYSCON_PMU);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id pmu_mid_match[] = {
|
||||
{ .compatible = "intel,pmu-mid", .data = X86_SYSCON_PMU },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(intel_mid_pmu) = {
|
||||
.name = "pmu_mid",
|
||||
.id = UCLASS_SYSCON,
|
||||
.of_match = pmu_mid_match,
|
||||
.probe = pmu_mid_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct pmu_mid),
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* From Coreboot src/lib/ramtest.c
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/post.h>
|
||||
|
||||
static void write_phys(unsigned long addr, u32 value)
|
||||
{
|
||||
#if CONFIG_SSE2
|
||||
asm volatile(
|
||||
"movnti %1, (%0)"
|
||||
: /* outputs */
|
||||
: "r" (addr), "r" (value) /* inputs */
|
||||
: /* clobbers */
|
||||
);
|
||||
#else
|
||||
writel(value, addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static u32 read_phys(unsigned long addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static void phys_memory_barrier(void)
|
||||
{
|
||||
#if CONFIG_SSE2
|
||||
/* Needed for movnti */
|
||||
asm volatile(
|
||||
"sfence"
|
||||
:
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
#else
|
||||
asm volatile(""
|
||||
:
|
||||
:
|
||||
: "memory");
|
||||
#endif
|
||||
}
|
||||
|
||||
void quick_ram_check(void)
|
||||
{
|
||||
int fail = 0;
|
||||
u32 backup;
|
||||
|
||||
backup = read_phys(CONFIG_RAMBASE);
|
||||
write_phys(CONFIG_RAMBASE, 0x55555555);
|
||||
phys_memory_barrier();
|
||||
if (read_phys(CONFIG_RAMBASE) != 0x55555555)
|
||||
fail = 1;
|
||||
write_phys(CONFIG_RAMBASE, 0xaaaaaaaa);
|
||||
phys_memory_barrier();
|
||||
if (read_phys(CONFIG_RAMBASE) != 0xaaaaaaaa)
|
||||
fail = 1;
|
||||
write_phys(CONFIG_RAMBASE, 0x00000000);
|
||||
phys_memory_barrier();
|
||||
if (read_phys(CONFIG_RAMBASE) != 0x00000000)
|
||||
fail = 1;
|
||||
write_phys(CONFIG_RAMBASE, 0xffffffff);
|
||||
phys_memory_barrier();
|
||||
if (read_phys(CONFIG_RAMBASE) != 0xffffffff)
|
||||
fail = 1;
|
||||
|
||||
write_phys(CONFIG_RAMBASE, backup);
|
||||
if (fail) {
|
||||
post_code(POST_RAM_FAILURE);
|
||||
panic("RAM INIT FAILURE!\n");
|
||||
}
|
||||
phys_memory_barrier();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* reloc_ia32.c - position independent x86 ELF shared object relocator
|
||||
* Copyright (C) 1999 Hewlett-Packard Co.
|
||||
* Contributed by David Mosberger <davidm@hpl.hp.com>.
|
||||
*
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi.h>
|
||||
#include <elf.h>
|
||||
|
||||
efi_status_t EFIAPI _relocate(long ldbase, Elf32_Dyn *dyn)
|
||||
{
|
||||
long relsz = 0, relent = 0;
|
||||
Elf32_Rel *rel = 0;
|
||||
unsigned long *addr;
|
||||
int i;
|
||||
|
||||
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
|
||||
switch (dyn[i].d_tag) {
|
||||
case DT_REL:
|
||||
rel = (Elf32_Rel *)((unsigned long)dyn[i].d_un.d_ptr +
|
||||
ldbase);
|
||||
break;
|
||||
|
||||
case DT_RELSZ:
|
||||
relsz = dyn[i].d_un.d_val;
|
||||
break;
|
||||
|
||||
case DT_RELENT:
|
||||
relent = dyn[i].d_un.d_val;
|
||||
break;
|
||||
|
||||
case DT_RELA:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rel && relent == 0)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
if (!rel || relent == 0)
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
while (relsz > 0) {
|
||||
/* apply the relocs */
|
||||
switch (ELF32_R_TYPE(rel->r_info)) {
|
||||
case R_386_NONE:
|
||||
break;
|
||||
|
||||
case R_386_RELATIVE:
|
||||
addr = (unsigned long *)(ldbase + rel->r_offset);
|
||||
*addr += ldbase;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rel = (Elf32_Rel *)((char *)rel + relent);
|
||||
relsz -= relent;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
/*
|
||||
* reloc_x86_64.c - position independent x86_64 ELF shared object relocator
|
||||
* Copyright (C) 1999 Hewlett-Packard Co.
|
||||
* Contributed by David Mosberger <davidm@hpl.hp.com>.
|
||||
* Copyright (C) 2005 Intel Co.
|
||||
* Contributed by Fenghua Yu <fenghua.yu@intel.com>.
|
||||
*
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <efi.h>
|
||||
#include <elf.h>
|
||||
|
||||
efi_status_t EFIAPI _relocate(long ldbase, Elf64_Dyn *dyn)
|
||||
{
|
||||
long relsz = 0, relent = 0;
|
||||
Elf64_Rel *rel = 0;
|
||||
unsigned long *addr;
|
||||
int i;
|
||||
|
||||
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
|
||||
switch (dyn[i].d_tag) {
|
||||
case DT_RELA:
|
||||
rel = (Elf64_Rel *)
|
||||
((unsigned long)dyn[i].d_un.d_ptr + ldbase);
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
relsz = dyn[i].d_un.d_val;
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
relent = dyn[i].d_un.d_val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rel && relent == 0)
|
||||
return EFI_SUCCESS;
|
||||
|
||||
if (!rel || relent == 0)
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
while (relsz > 0) {
|
||||
/* apply the relocs */
|
||||
switch (ELF64_R_TYPE(rel->r_info)) {
|
||||
case R_X86_64_NONE:
|
||||
break;
|
||||
case R_X86_64_RELATIVE:
|
||||
addr = (unsigned long *)(ldbase + rel->r_offset);
|
||||
*addr += ldbase;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rel = (Elf64_Rel *)((char *)rel + relent);
|
||||
relsz -= relent;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2008-2011
|
||||
* Graeme Russ, <graeme.russ@gmail.com>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Wolfgang Denk, DENX Software Engineering, <wd@denx.de>
|
||||
*
|
||||
* (C) Copyright 2002
|
||||
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
* Marius Groeger <mgroeger@sysgo.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <relocate.h>
|
||||
#include <asm/u-boot-x86.h>
|
||||
#include <asm/sections.h>
|
||||
#include <elf.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int copy_uboot_to_ram(void)
|
||||
{
|
||||
size_t len = (uintptr_t)&__data_end - (uintptr_t)&__text_start;
|
||||
|
||||
if (gd->flags & GD_FLG_SKIP_RELOC)
|
||||
return 0;
|
||||
memcpy((void *)gd->relocaddr, (void *)&__text_start, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clear_bss(void)
|
||||
{
|
||||
ulong dst_addr = (ulong)&__bss_start + gd->reloc_off;
|
||||
size_t len = (uintptr_t)&__bss_end - (uintptr_t)&__bss_start;
|
||||
|
||||
if (gd->flags & GD_FLG_SKIP_RELOC)
|
||||
return 0;
|
||||
memset((void *)dst_addr, 0x00, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(X86_64)
|
||||
static void do_elf_reloc_fixups64(unsigned int text_base, uintptr_t size,
|
||||
Elf64_Rela *re_src, Elf64_Rela *re_end)
|
||||
{
|
||||
Elf64_Addr *offset_ptr_rom, *last_offset = NULL;
|
||||
Elf64_Addr *offset_ptr_ram;
|
||||
|
||||
do {
|
||||
unsigned long long type = ELF64_R_TYPE(re_src->r_info);
|
||||
|
||||
if (type != R_X86_64_RELATIVE) {
|
||||
printf("%s: unsupported relocation type 0x%llx "
|
||||
"at %p, ", __func__, type, re_src);
|
||||
printf("offset = 0x%llx\n", re_src->r_offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get the location from the relocation entry */
|
||||
offset_ptr_rom = (Elf64_Addr *)(uintptr_t)re_src->r_offset;
|
||||
|
||||
/* Check that the location of the relocation is in .text */
|
||||
if (offset_ptr_rom >= (Elf64_Addr *)(uintptr_t)text_base &&
|
||||
offset_ptr_rom > last_offset) {
|
||||
/* Switch to the in-RAM version */
|
||||
offset_ptr_ram = (Elf64_Addr *)((ulong)offset_ptr_rom +
|
||||
gd->reloc_off);
|
||||
|
||||
/* Check that the target points into .text */
|
||||
if (*offset_ptr_ram >= text_base &&
|
||||
*offset_ptr_ram <= text_base + size) {
|
||||
*offset_ptr_ram = gd->reloc_off +
|
||||
re_src->r_addend;
|
||||
} else {
|
||||
debug(" %p: %lx: rom reloc %lx, ram %p, value %lx, limit %lX\n",
|
||||
re_src, (ulong)re_src->r_info,
|
||||
(ulong)re_src->r_offset, offset_ptr_ram,
|
||||
(ulong)*offset_ptr_ram, text_base + size);
|
||||
}
|
||||
} else {
|
||||
debug(" %p: %lx: rom reloc %lx, last %p\n", re_src,
|
||||
(ulong)re_src->r_info, (ulong)re_src->r_offset,
|
||||
last_offset);
|
||||
}
|
||||
last_offset = offset_ptr_rom;
|
||||
|
||||
} while (++re_src < re_end);
|
||||
}
|
||||
#else
|
||||
static void do_elf_reloc_fixups32(unsigned int text_base, uintptr_t size,
|
||||
Elf32_Rel *re_src, Elf32_Rel *re_end)
|
||||
{
|
||||
Elf32_Addr *offset_ptr_rom, *last_offset = NULL;
|
||||
Elf32_Addr *offset_ptr_ram;
|
||||
|
||||
do {
|
||||
unsigned int type = ELF32_R_TYPE(re_src->r_info);
|
||||
|
||||
if (type != R_386_RELATIVE) {
|
||||
printf("%s: unsupported relocation type 0x%x "
|
||||
"at %p, ", __func__, type, re_src);
|
||||
printf("offset = 0x%x\n", re_src->r_offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get the location from the relocation entry */
|
||||
offset_ptr_rom = (Elf32_Addr *)(uintptr_t)re_src->r_offset;
|
||||
|
||||
/* Check that the location of the relocation is in .text */
|
||||
if (offset_ptr_rom >= (Elf32_Addr *)(uintptr_t)text_base &&
|
||||
offset_ptr_rom > last_offset) {
|
||||
|
||||
/* Switch to the in-RAM version */
|
||||
offset_ptr_ram = (Elf32_Addr *)((ulong)offset_ptr_rom +
|
||||
gd->reloc_off);
|
||||
|
||||
/* Check that the target points into .text */
|
||||
if (*offset_ptr_ram >= text_base &&
|
||||
*offset_ptr_ram <= text_base + size) {
|
||||
*offset_ptr_ram += gd->reloc_off;
|
||||
} else {
|
||||
debug(" %p: rom reloc %x, ram %p, value %x, limit %lX\n",
|
||||
re_src, re_src->r_offset, offset_ptr_ram,
|
||||
*offset_ptr_ram, text_base + size);
|
||||
}
|
||||
} else {
|
||||
debug(" %p: rom reloc %x, last %p\n", re_src,
|
||||
re_src->r_offset, last_offset);
|
||||
}
|
||||
last_offset = offset_ptr_rom;
|
||||
|
||||
} while (++re_src < re_end);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This function has more error checking than you might expect. Please see
|
||||
* this commit message for more information:
|
||||
* 62f7970a x86: Add error checking to x86 relocation code
|
||||
*/
|
||||
int do_elf_reloc_fixups(void)
|
||||
{
|
||||
void *re_src = (void *)(&__rel_dyn_start);
|
||||
void *re_end = (void *)(&__rel_dyn_end);
|
||||
uint text_base;
|
||||
|
||||
/* The size of the region of u-boot that runs out of RAM. */
|
||||
uintptr_t size = (uintptr_t)&__bss_end - (uintptr_t)&__text_start;
|
||||
|
||||
if (gd->flags & GD_FLG_SKIP_RELOC)
|
||||
return 0;
|
||||
if (re_src == re_end)
|
||||
panic("No relocation data");
|
||||
|
||||
#ifdef CONFIG_SYS_TEXT_BASE
|
||||
text_base = CONFIG_SYS_TEXT_BASE;
|
||||
#else
|
||||
panic("No CONFIG_SYS_TEXT_BASE");
|
||||
#endif
|
||||
#if CONFIG_IS_ENABLED(X86_64)
|
||||
do_elf_reloc_fixups64(text_base, size, re_src, re_end);
|
||||
#else
|
||||
do_elf_reloc_fixups32(text_base, size, re_src, re_end);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
*
|
||||
* Intel Mobile Internet Devices (MID) based on Intel Atom SoCs have few
|
||||
* microcontrollers inside to do some auxiliary tasks. One of such
|
||||
* microcontroller is System Controller Unit (SCU) which, in particular,
|
||||
* is servicing watchdog and controlling system reset function.
|
||||
*
|
||||
* This driver enables IPC channel to SCU.
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/scu.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
/* SCU register map */
|
||||
struct ipc_regs {
|
||||
u32 cmd;
|
||||
u32 status;
|
||||
u32 sptr;
|
||||
u32 dptr;
|
||||
u32 reserved[28];
|
||||
u32 wbuf[4];
|
||||
u32 rbuf[4];
|
||||
};
|
||||
|
||||
struct scu {
|
||||
struct ipc_regs *regs;
|
||||
};
|
||||
|
||||
/**
|
||||
* scu_ipc_send_command() - send command to SCU
|
||||
* @regs: register map of SCU
|
||||
* @cmd: command
|
||||
*
|
||||
* Command Register (Write Only):
|
||||
* A write to this register results in an interrupt to the SCU core processor
|
||||
* Format:
|
||||
* |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
|
||||
*/
|
||||
static void scu_ipc_send_command(struct ipc_regs *regs, u32 cmd)
|
||||
{
|
||||
writel(cmd, ®s->cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* scu_ipc_check_status() - check status of last command
|
||||
* @regs: register map of SCU
|
||||
*
|
||||
* Status Register (Read Only):
|
||||
* Driver will read this register to get the ready/busy status of the IPC
|
||||
* block and error status of the IPC command that was just processed by SCU
|
||||
* Format:
|
||||
* |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
|
||||
*/
|
||||
static int scu_ipc_check_status(struct ipc_regs *regs)
|
||||
{
|
||||
int loop_count = 100000;
|
||||
int status;
|
||||
|
||||
do {
|
||||
status = readl(®s->status);
|
||||
if (!(status & BIT(0)))
|
||||
break;
|
||||
|
||||
udelay(1);
|
||||
} while (--loop_count);
|
||||
if (!loop_count)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
if (status & BIT(1)) {
|
||||
printf("%s() status=0x%08x\n", __func__, status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scu_ipc_cmd(struct ipc_regs *regs, u32 cmd, u32 sub,
|
||||
u32 *in, int inlen, u32 *out, int outlen)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < inlen; i++)
|
||||
writel(*in++, ®s->wbuf[i]);
|
||||
|
||||
scu_ipc_send_command(regs, (inlen << 16) | (sub << 12) | cmd);
|
||||
err = scu_ipc_check_status(regs);
|
||||
|
||||
if (!err) {
|
||||
for (i = 0; i < outlen; i++)
|
||||
*out++ = readl(®s->rbuf[i]);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* scu_ipc_raw_command() - IPC command with data and pointers
|
||||
* @cmd: IPC command code
|
||||
* @sub: IPC command sub type
|
||||
* @in: input data of this IPC command
|
||||
* @inlen: input data length in dwords
|
||||
* @out: output data of this IPC command
|
||||
* @outlen: output data length in dwords
|
||||
* @dptr: data writing to SPTR register
|
||||
* @sptr: data writing to DPTR register
|
||||
*
|
||||
* Send an IPC command to SCU with input/output data and source/dest pointers.
|
||||
*
|
||||
* Return: an IPC error code or 0 on success.
|
||||
*/
|
||||
int scu_ipc_raw_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out,
|
||||
int outlen, u32 dptr, u32 sptr)
|
||||
{
|
||||
int inbuflen = DIV_ROUND_UP(inlen, 4);
|
||||
struct udevice *dev;
|
||||
struct scu *scu;
|
||||
int ret;
|
||||
|
||||
ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scu = dev_get_priv(dev);
|
||||
|
||||
/* Up to 16 bytes */
|
||||
if (inbuflen > 4)
|
||||
return -EINVAL;
|
||||
|
||||
writel(dptr, &scu->regs->dptr);
|
||||
writel(sptr, &scu->regs->sptr);
|
||||
|
||||
/*
|
||||
* SRAM controller doesn't support 8-bit writes, it only
|
||||
* supports 32-bit writes, so we have to copy input data into
|
||||
* the temporary buffer, and SCU FW will use the inlen to
|
||||
* determine the actual input data length in the temporary
|
||||
* buffer.
|
||||
*/
|
||||
|
||||
u32 inbuf[4] = {0};
|
||||
|
||||
memcpy(inbuf, in, inlen);
|
||||
|
||||
return scu_ipc_cmd(scu->regs, cmd, sub, inbuf, inlen, out, outlen);
|
||||
}
|
||||
|
||||
/**
|
||||
* scu_ipc_simple_command() - send a simple command
|
||||
* @cmd: command
|
||||
* @sub: sub type
|
||||
*
|
||||
* Issue a simple command to the SCU. Do not use this interface if
|
||||
* you must then access data as any data values may be overwritten
|
||||
* by another SCU access by the time this function returns.
|
||||
*
|
||||
* This function may sleep. Locking for SCU accesses is handled for
|
||||
* the caller.
|
||||
*/
|
||||
int scu_ipc_simple_command(u32 cmd, u32 sub)
|
||||
{
|
||||
struct scu *scu;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scu = dev_get_priv(dev);
|
||||
|
||||
scu_ipc_send_command(scu->regs, sub << 12 | cmd);
|
||||
return scu_ipc_check_status(scu->regs);
|
||||
}
|
||||
|
||||
/**
|
||||
* scu_ipc_command - command with data
|
||||
* @cmd: command
|
||||
* @sub: sub type
|
||||
* @in: input data
|
||||
* @inlen: input length in dwords
|
||||
* @out: output data
|
||||
* @outlen: output length in dwords
|
||||
*
|
||||
* Issue a command to the SCU which involves data transfers.
|
||||
*/
|
||||
int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen)
|
||||
{
|
||||
struct scu *scu;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scu = dev_get_priv(dev);
|
||||
|
||||
return scu_ipc_cmd(scu->regs, cmd, sub, in, inlen, out, outlen);
|
||||
}
|
||||
|
||||
static int scu_ipc_probe(struct udevice *dev)
|
||||
{
|
||||
struct scu *scu = dev_get_priv(dev);
|
||||
|
||||
scu->regs = syscon_get_first_range(X86_SYSCON_SCU);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id scu_ipc_match[] = {
|
||||
{ .compatible = "intel,scu-ipc", .data = X86_SYSCON_SCU },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(scu_ipc) = {
|
||||
.name = "scu_ipc",
|
||||
.id = UCLASS_SYSCON,
|
||||
.of_match = scu_ipc_match,
|
||||
.probe = scu_ipc_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct scu),
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2013 Albert ARIBAUD <albert.u.boot@aribaud.net>
|
||||
*/
|
||||
|
||||
char __efi_runtime_start[0] __attribute__((section(".__efi_runtime_start")));
|
||||
char __efi_runtime_stop[0] __attribute__((section(".__efi_runtime_stop")));
|
||||
char __efi_runtime_rel_start[0]
|
||||
__attribute__((section(".__efi_runtime_rel_start")));
|
||||
char __efi_runtime_rel_stop[0]
|
||||
__attribute__((section(".__efi_runtime_rel_stop")));
|
||||
@@ -0,0 +1,153 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Intel Simple Firmware Interface (SFI)
|
||||
*
|
||||
* Yet another way to pass information to the Linux kernel.
|
||||
*
|
||||
* See https://simplefirmware.org/ for details
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu.h>
|
||||
#include <dm.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/ioapic.h>
|
||||
#include <asm/sfi.h>
|
||||
#include <asm/tables.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
|
||||
struct table_info {
|
||||
u32 base;
|
||||
int ptr;
|
||||
u32 entry_start;
|
||||
u64 table[SFI_TABLE_MAX_ENTRIES];
|
||||
int count;
|
||||
};
|
||||
|
||||
static void *get_entry_start(struct table_info *tab)
|
||||
{
|
||||
if (tab->count == SFI_TABLE_MAX_ENTRIES)
|
||||
return NULL;
|
||||
tab->entry_start = tab->base + tab->ptr;
|
||||
tab->table[tab->count] = tab->entry_start;
|
||||
tab->entry_start += sizeof(struct sfi_table_header);
|
||||
|
||||
return (void *)(uintptr_t)tab->entry_start;
|
||||
}
|
||||
|
||||
static void finish_table(struct table_info *tab, const char *sig, void *entry)
|
||||
{
|
||||
struct sfi_table_header *hdr;
|
||||
|
||||
hdr = (struct sfi_table_header *)(uintptr_t)(tab->base + tab->ptr);
|
||||
strcpy(hdr->sig, sig);
|
||||
hdr->len = sizeof(*hdr) + ((ulong)entry - tab->entry_start);
|
||||
hdr->rev = 1;
|
||||
strncpy(hdr->oem_id, "U-Boot", SFI_OEM_ID_SIZE);
|
||||
strncpy(hdr->oem_table_id, "Table v1", SFI_OEM_TABLE_ID_SIZE);
|
||||
hdr->csum = 0;
|
||||
hdr->csum = table_compute_checksum(hdr, hdr->len);
|
||||
tab->ptr += hdr->len;
|
||||
tab->ptr = ALIGN(tab->ptr, 16);
|
||||
tab->count++;
|
||||
}
|
||||
|
||||
static int sfi_write_system_header(struct table_info *tab)
|
||||
{
|
||||
u64 *entry = get_entry_start(tab);
|
||||
int i;
|
||||
|
||||
if (!entry)
|
||||
return -ENOSPC;
|
||||
|
||||
for (i = 0; i < tab->count; i++)
|
||||
*entry++ = tab->table[i];
|
||||
finish_table(tab, SFI_SIG_SYST, entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sfi_write_cpus(struct table_info *tab)
|
||||
{
|
||||
struct sfi_cpu_table_entry *entry = get_entry_start(tab);
|
||||
struct udevice *dev;
|
||||
int count = 0;
|
||||
|
||||
if (!entry)
|
||||
return -ENOSPC;
|
||||
|
||||
for (uclass_find_first_device(UCLASS_CPU, &dev);
|
||||
dev;
|
||||
uclass_find_next_device(&dev)) {
|
||||
struct cpu_platdata *plat = dev_get_parent_platdata(dev);
|
||||
|
||||
if (!device_active(dev))
|
||||
continue;
|
||||
entry->apic_id = plat->cpu_id;
|
||||
entry++;
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Omit the table if there is only one CPU */
|
||||
if (count > 1)
|
||||
finish_table(tab, SFI_SIG_CPUS, entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sfi_write_apic(struct table_info *tab)
|
||||
{
|
||||
struct sfi_apic_table_entry *entry = get_entry_start(tab);
|
||||
|
||||
if (!entry)
|
||||
return -ENOSPC;
|
||||
|
||||
entry->phys_addr = IO_APIC_ADDR;
|
||||
entry++;
|
||||
finish_table(tab, SFI_SIG_APIC, entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sfi_write_xsdt(struct table_info *tab)
|
||||
{
|
||||
struct sfi_xsdt_header *entry = get_entry_start(tab);
|
||||
|
||||
if (!entry)
|
||||
return -ENOSPC;
|
||||
|
||||
entry->oem_revision = 1;
|
||||
entry->creator_id = 1;
|
||||
entry->creator_revision = 1;
|
||||
entry++;
|
||||
finish_table(tab, SFI_SIG_XSDT, entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong write_sfi_table(ulong base)
|
||||
{
|
||||
struct table_info table;
|
||||
|
||||
table.base = base;
|
||||
table.ptr = 0;
|
||||
table.count = 0;
|
||||
sfi_write_cpus(&table);
|
||||
sfi_write_apic(&table);
|
||||
|
||||
/*
|
||||
* The SFI specification marks the XSDT table as option, but Linux 4.0
|
||||
* crashes on start-up when it is not provided.
|
||||
*/
|
||||
sfi_write_xsdt(&table);
|
||||
|
||||
/* Finally, write out the system header which points to the others */
|
||||
sfi_write_system_header(&table);
|
||||
|
||||
return base + table.ptr;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <cpu_func.h>
|
||||
#include <debug_uart.h>
|
||||
#include <dm.h>
|
||||
#include <irq_func.h>
|
||||
#include <malloc.h>
|
||||
#include <spl.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/cpu_common.h>
|
||||
#include <asm/mrccache.h>
|
||||
#include <asm/mtrr.h>
|
||||
#include <asm/pci.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/spl.h>
|
||||
#include <asm-generic/sections.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
__weak int arch_cpu_init_dm(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TPL
|
||||
|
||||
static int set_max_freq(void)
|
||||
{
|
||||
if (cpu_get_burst_mode_state() == BURST_MODE_UNAVAILABLE) {
|
||||
/*
|
||||
* Burst Mode has been factory-configured as disabled and is not
|
||||
* available in this physical processor package
|
||||
*/
|
||||
debug("Burst Mode is factory-disabled\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Enable burst mode */
|
||||
cpu_set_burst_mode(true);
|
||||
|
||||
/* Enable speed step */
|
||||
cpu_set_eist(true);
|
||||
|
||||
/* Set P-State ratio */
|
||||
cpu_set_p_state_to_turbo_ratio();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int x86_spl_init(void)
|
||||
{
|
||||
#ifndef CONFIG_TPL
|
||||
/*
|
||||
* TODO(sjg@chromium.org): We use this area of RAM for the stack
|
||||
* and global_data in SPL. Once U-Boot starts up and releocates it
|
||||
* is not needed. We could make this a CONFIG option or perhaps
|
||||
* place it immediately below CONFIG_SYS_TEXT_BASE.
|
||||
*/
|
||||
char *ptr = (char *)0x110000;
|
||||
#else
|
||||
struct udevice *punit;
|
||||
#endif
|
||||
int ret;
|
||||
|
||||
debug("%s starting\n", __func__);
|
||||
if (IS_ENABLED(TPL))
|
||||
ret = x86_cpu_reinit_f();
|
||||
else
|
||||
ret = x86_cpu_init_f();
|
||||
ret = spl_init();
|
||||
if (ret) {
|
||||
debug("%s: spl_init() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
ret = arch_cpu_init();
|
||||
if (ret) {
|
||||
debug("%s: arch_cpu_init() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
#ifndef CONFIG_TPL
|
||||
ret = arch_cpu_init_dm();
|
||||
if (ret) {
|
||||
debug("%s: arch_cpu_init_dm() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
preloader_console_init();
|
||||
#ifndef CONFIG_TPL
|
||||
ret = print_cpuinfo();
|
||||
if (ret) {
|
||||
debug("%s: print_cpuinfo() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
ret = dram_init();
|
||||
if (ret) {
|
||||
debug("%s: dram_init() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE)) {
|
||||
ret = mrccache_spl_save();
|
||||
if (ret)
|
||||
debug("%s: Failed to write to mrccache (err=%d)\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_TPL
|
||||
memset(&__bss_start, 0, (ulong)&__bss_end - (ulong)&__bss_start);
|
||||
|
||||
/* TODO(sjg@chromium.org): Consider calling cpu_init_r() here */
|
||||
ret = interrupt_init();
|
||||
if (ret) {
|
||||
debug("%s: interrupt_init() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The stack grows down from ptr. Put the global data at ptr. This
|
||||
* will only be used for SPL. Once SPL loads U-Boot proper it will
|
||||
* set up its own stack.
|
||||
*/
|
||||
gd->new_gd = (struct global_data *)ptr;
|
||||
memcpy(gd->new_gd, gd, sizeof(*gd));
|
||||
arch_setup_gd(gd->new_gd);
|
||||
gd->start_addr_sp = (ulong)ptr;
|
||||
|
||||
/* Cache the SPI flash. Otherwise copying the code to RAM takes ages */
|
||||
ret = mtrr_add_request(MTRR_TYPE_WRBACK,
|
||||
(1ULL << 32) - CONFIG_XIP_ROM_SIZE,
|
||||
CONFIG_XIP_ROM_SIZE);
|
||||
if (ret) {
|
||||
debug("%s: SPI cache setup failed (err=%d)\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
mtrr_commit(true);
|
||||
#else
|
||||
ret = syscon_get_by_driver_data(X86_SYSCON_PUNIT, &punit);
|
||||
if (ret)
|
||||
debug("Could not find PUNIT (err=%d)\n", ret);
|
||||
|
||||
ret = set_max_freq();
|
||||
if (ret)
|
||||
debug("Failed to set CPU frequency (err=%d)\n", ret);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void board_init_f(ulong flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = x86_spl_init();
|
||||
if (ret) {
|
||||
debug("Error %d\n", ret);
|
||||
panic("x86_spl_init fail");
|
||||
}
|
||||
#ifdef CONFIG_TPL
|
||||
gd->bd = malloc(sizeof(*gd->bd));
|
||||
if (!gd->bd) {
|
||||
printf("Out of memory for bd_info size %x\n", sizeof(*gd->bd));
|
||||
hang();
|
||||
}
|
||||
board_init_r(gd, 0);
|
||||
#else
|
||||
/* Uninit CAR and jump to board_init_f_r() */
|
||||
board_init_f_r_trampoline(gd->start_addr_sp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void board_init_f_r(void)
|
||||
{
|
||||
init_cache_f_r();
|
||||
gd->flags &= ~GD_FLG_SERIAL_READY;
|
||||
debug("cache status %d\n", dcache_status());
|
||||
board_init_r(gd, 0);
|
||||
}
|
||||
|
||||
u32 spl_boot_device(void)
|
||||
{
|
||||
return BOOT_DEVICE_SPI_MMAP;
|
||||
}
|
||||
|
||||
int spl_start_uboot(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spl_board_announce_boot_device(void)
|
||||
{
|
||||
printf("SPI flash");
|
||||
}
|
||||
|
||||
static int spl_board_load_image(struct spl_image_info *spl_image,
|
||||
struct spl_boot_device *bootdev)
|
||||
{
|
||||
spl_image->size = CONFIG_SYS_MONITOR_LEN;
|
||||
spl_image->entry_point = CONFIG_SYS_TEXT_BASE;
|
||||
spl_image->load_addr = CONFIG_SYS_TEXT_BASE;
|
||||
spl_image->os = IH_OS_U_BOOT;
|
||||
spl_image->name = "U-Boot";
|
||||
|
||||
debug("Loading to %lx\n", spl_image->load_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
SPL_LOAD_IMAGE_METHOD("SPI", 5, BOOT_DEVICE_SPI_MMAP, spl_board_load_image);
|
||||
|
||||
int spl_spi_load_image(void)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_RUN_64BIT
|
||||
void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printf("Jumping to 64-bit U-Boot: Note many features are missing\n");
|
||||
ret = cpu_jump_to_64bit_uboot(spl_image->entry_point);
|
||||
debug("ret=%d\n", ret);
|
||||
hang();
|
||||
}
|
||||
#endif
|
||||
|
||||
void spl_board_init(void)
|
||||
{
|
||||
#ifndef CONFIG_TPL
|
||||
preloader_console_init();
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 1991,1992,1993,1997,1998,2003, 2005 Free Software Foundation, Inc.
|
||||
* This file is part of the GNU C Library.
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
*/
|
||||
|
||||
/* From glibc-2.14, sysdeps/i386/memset.c */
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
typedef uint32_t op_t;
|
||||
|
||||
void *memset(void *dstpp, int c, size_t len)
|
||||
{
|
||||
int d0;
|
||||
unsigned long int dstp = (unsigned long int) dstpp;
|
||||
|
||||
/* This explicit register allocation improves code very much indeed. */
|
||||
register op_t x asm("ax");
|
||||
|
||||
x = (unsigned char) c;
|
||||
|
||||
/* Clear the direction flag, so filling will move forward. */
|
||||
asm volatile("cld");
|
||||
|
||||
/* This threshold value is optimal. */
|
||||
if (len >= 12) {
|
||||
/* Fill X with four copies of the char we want to fill with. */
|
||||
x |= (x << 8);
|
||||
x |= (x << 16);
|
||||
|
||||
/* Adjust LEN for the bytes handled in the first loop. */
|
||||
len -= (-dstp) % sizeof(op_t);
|
||||
|
||||
/*
|
||||
* There are at least some bytes to set. No need to test for
|
||||
* LEN == 0 in this alignment loop.
|
||||
*/
|
||||
|
||||
/* Fill bytes until DSTP is aligned on a longword boundary. */
|
||||
asm volatile(
|
||||
"rep\n"
|
||||
"stosb" /* %0, %2, %3 */ :
|
||||
"=D" (dstp), "=c" (d0) :
|
||||
"0" (dstp), "1" ((-dstp) % sizeof(op_t)), "a" (x) :
|
||||
"memory");
|
||||
|
||||
/* Fill longwords. */
|
||||
asm volatile(
|
||||
"rep\n"
|
||||
"stosl" /* %0, %2, %3 */ :
|
||||
"=D" (dstp), "=c" (d0) :
|
||||
"0" (dstp), "1" (len / sizeof(op_t)), "a" (x) :
|
||||
"memory");
|
||||
len %= sizeof(op_t);
|
||||
}
|
||||
|
||||
/* Write the last few bytes. */
|
||||
asm volatile(
|
||||
"rep\n"
|
||||
"stosb" /* %0, %2, %3 */ :
|
||||
"=D" (dstp), "=c" (d0) :
|
||||
"0" (dstp), "1" (len), "a" (x) :
|
||||
"memory");
|
||||
|
||||
return dstpp;
|
||||
}
|
||||
|
||||
#define OP_T_THRES 8
|
||||
#define OPSIZ (sizeof(op_t))
|
||||
|
||||
#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes) \
|
||||
do { \
|
||||
int __d0; \
|
||||
asm volatile( \
|
||||
/* Clear the direction flag, so copying goes forward. */ \
|
||||
"cld\n" \
|
||||
/* Copy bytes. */ \
|
||||
"rep\n" \
|
||||
"movsb" : \
|
||||
"=D" (dst_bp), "=S" (src_bp), "=c" (__d0) : \
|
||||
"0" (dst_bp), "1" (src_bp), "2" (nbytes) : \
|
||||
"memory"); \
|
||||
} while (0)
|
||||
|
||||
#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes) \
|
||||
do { \
|
||||
int __d0; \
|
||||
asm volatile( \
|
||||
/* Clear the direction flag, so copying goes forward. */ \
|
||||
"cld\n" \
|
||||
/* Copy longwords. */ \
|
||||
"rep\n" \
|
||||
"movsl" : \
|
||||
"=D" (dst_bp), "=S" (src_bp), "=c" (__d0) : \
|
||||
"0" (dst_bp), "1" (src_bp), "2" ((nbytes) / 4) : \
|
||||
"memory"); \
|
||||
(nbytes_left) = (nbytes) % 4; \
|
||||
} while (0)
|
||||
|
||||
void *memcpy(void *dstpp, const void *srcpp, size_t len)
|
||||
{
|
||||
unsigned long int dstp = (long int)dstpp;
|
||||
unsigned long int srcp = (long int)srcpp;
|
||||
|
||||
/* Copy from the beginning to the end. */
|
||||
|
||||
/* If there not too few bytes to copy, use word copy. */
|
||||
if (len >= OP_T_THRES) {
|
||||
/* Copy just a few bytes to make DSTP aligned. */
|
||||
len -= (-dstp) % OPSIZ;
|
||||
BYTE_COPY_FWD(dstp, srcp, (-dstp) % OPSIZ);
|
||||
|
||||
/* Copy from SRCP to DSTP taking advantage of the known
|
||||
* alignment of DSTP. Number of bytes remaining is put
|
||||
* in the third argument, i.e. in LEN. This number may
|
||||
* vary from machine to machine.
|
||||
*/
|
||||
WORD_COPY_FWD(dstp, srcp, len, len);
|
||||
|
||||
/* Fall out and copy the tail. */
|
||||
}
|
||||
|
||||
/* There are just a few bytes to copy. Use byte memory operations. */
|
||||
BYTE_COPY_FWD(dstp, srcp, len);
|
||||
|
||||
return dstpp;
|
||||
}
|
||||
|
||||
void *memmove(void *dest, const void *src, size_t n)
|
||||
{
|
||||
int d0, d1, d2, d3, d4, d5;
|
||||
char *ret = dest;
|
||||
|
||||
__asm__ __volatile__(
|
||||
/* Handle more 16 bytes in loop */
|
||||
"cmp $0x10, %0\n\t"
|
||||
"jb 1f\n\t"
|
||||
|
||||
/* Decide forward/backward copy mode */
|
||||
"cmp %2, %1\n\t"
|
||||
"jb 2f\n\t"
|
||||
|
||||
/*
|
||||
* movs instruction have many startup latency
|
||||
* so we handle small size by general register.
|
||||
*/
|
||||
"cmp $680, %0\n\t"
|
||||
"jb 3f\n\t"
|
||||
/* movs instruction is only good for aligned case */
|
||||
"mov %1, %3\n\t"
|
||||
"xor %2, %3\n\t"
|
||||
"and $0xff, %3\n\t"
|
||||
"jz 4f\n\t"
|
||||
"3:\n\t"
|
||||
"sub $0x10, %0\n\t"
|
||||
|
||||
/* We gobble 16 bytes forward in each loop */
|
||||
"3:\n\t"
|
||||
"sub $0x10, %0\n\t"
|
||||
"mov 0*4(%1), %3\n\t"
|
||||
"mov 1*4(%1), %4\n\t"
|
||||
"mov %3, 0*4(%2)\n\t"
|
||||
"mov %4, 1*4(%2)\n\t"
|
||||
"mov 2*4(%1), %3\n\t"
|
||||
"mov 3*4(%1), %4\n\t"
|
||||
"mov %3, 2*4(%2)\n\t"
|
||||
"mov %4, 3*4(%2)\n\t"
|
||||
"lea 0x10(%1), %1\n\t"
|
||||
"lea 0x10(%2), %2\n\t"
|
||||
"jae 3b\n\t"
|
||||
"add $0x10, %0\n\t"
|
||||
"jmp 1f\n\t"
|
||||
|
||||
/* Handle data forward by movs */
|
||||
".p2align 4\n\t"
|
||||
"4:\n\t"
|
||||
"mov -4(%1, %0), %3\n\t"
|
||||
"lea -4(%2, %0), %4\n\t"
|
||||
"shr $2, %0\n\t"
|
||||
"rep movsl\n\t"
|
||||
"mov %3, (%4)\n\t"
|
||||
"jmp 11f\n\t"
|
||||
/* Handle data backward by movs */
|
||||
".p2align 4\n\t"
|
||||
"6:\n\t"
|
||||
"mov (%1), %3\n\t"
|
||||
"mov %2, %4\n\t"
|
||||
"lea -4(%1, %0), %1\n\t"
|
||||
"lea -4(%2, %0), %2\n\t"
|
||||
"shr $2, %0\n\t"
|
||||
"std\n\t"
|
||||
"rep movsl\n\t"
|
||||
"mov %3,(%4)\n\t"
|
||||
"cld\n\t"
|
||||
"jmp 11f\n\t"
|
||||
|
||||
/* Start to prepare for backward copy */
|
||||
".p2align 4\n\t"
|
||||
"2:\n\t"
|
||||
"cmp $680, %0\n\t"
|
||||
"jb 5f\n\t"
|
||||
"mov %1, %3\n\t"
|
||||
"xor %2, %3\n\t"
|
||||
"and $0xff, %3\n\t"
|
||||
"jz 6b\n\t"
|
||||
|
||||
/* Calculate copy position to tail */
|
||||
"5:\n\t"
|
||||
"add %0, %1\n\t"
|
||||
"add %0, %2\n\t"
|
||||
"sub $0x10, %0\n\t"
|
||||
|
||||
/* We gobble 16 bytes backward in each loop */
|
||||
"7:\n\t"
|
||||
"sub $0x10, %0\n\t"
|
||||
|
||||
"mov -1*4(%1), %3\n\t"
|
||||
"mov -2*4(%1), %4\n\t"
|
||||
"mov %3, -1*4(%2)\n\t"
|
||||
"mov %4, -2*4(%2)\n\t"
|
||||
"mov -3*4(%1), %3\n\t"
|
||||
"mov -4*4(%1), %4\n\t"
|
||||
"mov %3, -3*4(%2)\n\t"
|
||||
"mov %4, -4*4(%2)\n\t"
|
||||
"lea -0x10(%1), %1\n\t"
|
||||
"lea -0x10(%2), %2\n\t"
|
||||
"jae 7b\n\t"
|
||||
/* Calculate copy position to head */
|
||||
"add $0x10, %0\n\t"
|
||||
"sub %0, %1\n\t"
|
||||
"sub %0, %2\n\t"
|
||||
|
||||
/* Move data from 8 bytes to 15 bytes */
|
||||
".p2align 4\n\t"
|
||||
"1:\n\t"
|
||||
"cmp $8, %0\n\t"
|
||||
"jb 8f\n\t"
|
||||
"mov 0*4(%1), %3\n\t"
|
||||
"mov 1*4(%1), %4\n\t"
|
||||
"mov -2*4(%1, %0), %5\n\t"
|
||||
"mov -1*4(%1, %0), %1\n\t"
|
||||
|
||||
"mov %3, 0*4(%2)\n\t"
|
||||
"mov %4, 1*4(%2)\n\t"
|
||||
"mov %5, -2*4(%2, %0)\n\t"
|
||||
"mov %1, -1*4(%2, %0)\n\t"
|
||||
"jmp 11f\n\t"
|
||||
|
||||
/* Move data from 4 bytes to 7 bytes */
|
||||
".p2align 4\n\t"
|
||||
"8:\n\t"
|
||||
"cmp $4, %0\n\t"
|
||||
"jb 9f\n\t"
|
||||
"mov 0*4(%1), %3\n\t"
|
||||
"mov -1*4(%1, %0), %4\n\t"
|
||||
"mov %3, 0*4(%2)\n\t"
|
||||
"mov %4, -1*4(%2, %0)\n\t"
|
||||
"jmp 11f\n\t"
|
||||
|
||||
/* Move data from 2 bytes to 3 bytes */
|
||||
".p2align 4\n\t"
|
||||
"9:\n\t"
|
||||
"cmp $2, %0\n\t"
|
||||
"jb 10f\n\t"
|
||||
"movw 0*2(%1), %%dx\n\t"
|
||||
"movw -1*2(%1, %0), %%bx\n\t"
|
||||
"movw %%dx, 0*2(%2)\n\t"
|
||||
"movw %%bx, -1*2(%2, %0)\n\t"
|
||||
"jmp 11f\n\t"
|
||||
|
||||
/* Move data for 1 byte */
|
||||
".p2align 4\n\t"
|
||||
"10:\n\t"
|
||||
"cmp $1, %0\n\t"
|
||||
"jb 11f\n\t"
|
||||
"movb (%1), %%cl\n\t"
|
||||
"movb %%cl, (%2)\n\t"
|
||||
".p2align 4\n\t"
|
||||
"11:"
|
||||
: "=&c" (d0), "=&S" (d1), "=&D" (d2),
|
||||
"=r" (d3), "=r" (d4), "=r"(d5)
|
||||
: "0" (n),
|
||||
"1" (src),
|
||||
"2" (dest)
|
||||
: "memory");
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <smbios.h>
|
||||
#include <asm/sfi.h>
|
||||
#include <asm/mpspec.h>
|
||||
#include <asm/tables.h>
|
||||
#include <asm/acpi_table.h>
|
||||
#include <asm/coreboot_tables.h>
|
||||
|
||||
/**
|
||||
* Function prototype to write a specific configuration table
|
||||
*
|
||||
* @addr: start address to write the table
|
||||
* @return: end address of the table
|
||||
*/
|
||||
typedef ulong (*table_write)(ulong addr);
|
||||
|
||||
static table_write table_write_funcs[] = {
|
||||
#ifdef CONFIG_GENERATE_PIRQ_TABLE
|
||||
write_pirq_routing_table,
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_SFI_TABLE
|
||||
write_sfi_table,
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_MP_TABLE
|
||||
write_mp_table,
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_ACPI_TABLE
|
||||
write_acpi_tables,
|
||||
#endif
|
||||
#ifdef CONFIG_GENERATE_SMBIOS_TABLE
|
||||
write_smbios_table,
|
||||
#endif
|
||||
};
|
||||
|
||||
void table_fill_string(char *dest, const char *src, size_t n, char pad)
|
||||
{
|
||||
int start, len;
|
||||
int i;
|
||||
|
||||
strncpy(dest, src, n);
|
||||
|
||||
/* Fill the remaining bytes with pad */
|
||||
len = strlen(src);
|
||||
start = len < n ? len : n;
|
||||
for (i = start; i < n; i++)
|
||||
dest[i] = pad;
|
||||
}
|
||||
|
||||
void write_tables(void)
|
||||
{
|
||||
u32 rom_table_start = ROM_TABLE_ADDR;
|
||||
u32 rom_table_end;
|
||||
#ifdef CONFIG_SEABIOS
|
||||
u32 high_table, table_size;
|
||||
struct memory_area cfg_tables[ARRAY_SIZE(table_write_funcs) + 1];
|
||||
#endif
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(table_write_funcs); i++) {
|
||||
rom_table_end = table_write_funcs[i](rom_table_start);
|
||||
rom_table_end = ALIGN(rom_table_end, ROM_TABLE_ALIGN);
|
||||
|
||||
#ifdef CONFIG_SEABIOS
|
||||
table_size = rom_table_end - rom_table_start;
|
||||
high_table = (u32)high_table_malloc(table_size);
|
||||
if (high_table) {
|
||||
table_write_funcs[i](high_table);
|
||||
|
||||
cfg_tables[i].start = high_table;
|
||||
cfg_tables[i].size = table_size;
|
||||
} else {
|
||||
printf("%d: no memory for configuration tables\n", i);
|
||||
}
|
||||
#endif
|
||||
|
||||
rom_table_start = rom_table_end;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SEABIOS
|
||||
/* make sure the last item is zero */
|
||||
cfg_tables[i].size = 0;
|
||||
write_coreboot_table(CB_TABLE_ADDR, cfg_tables);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <debug_uart.h>
|
||||
#include <dm.h>
|
||||
#include <spl.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/mtrr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm-generic/sections.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
__weak int arch_cpu_init_dm(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int x86_tpl_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
debug("%s starting\n", __func__);
|
||||
ret = x86_cpu_init_tpl();
|
||||
if (ret) {
|
||||
debug("%s: x86_cpu_init_tpl() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
ret = spl_init();
|
||||
if (ret) {
|
||||
debug("%s: spl_init() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
ret = arch_cpu_init();
|
||||
if (ret) {
|
||||
debug("%s: arch_cpu_init() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
ret = arch_cpu_init_dm();
|
||||
if (ret) {
|
||||
debug("%s: arch_cpu_init_dm() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
preloader_console_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void board_init_f(ulong flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = x86_tpl_init();
|
||||
if (ret) {
|
||||
debug("Error %d\n", ret);
|
||||
panic("x86_tpl_init fail");
|
||||
}
|
||||
|
||||
/* Uninit CAR and jump to board_init_f_r() */
|
||||
board_init_r(gd, 0);
|
||||
}
|
||||
|
||||
void board_init_f_r(void)
|
||||
{
|
||||
/* Not used since we never call board_init_f_r_trampoline() */
|
||||
while (1);
|
||||
}
|
||||
|
||||
u32 spl_boot_device(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_CHROMEOS) ? BOOT_DEVICE_CROS_VBOOT :
|
||||
BOOT_DEVICE_SPI_MMAP;
|
||||
}
|
||||
|
||||
int spl_start_uboot(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spl_board_announce_boot_device(void)
|
||||
{
|
||||
printf("SPI flash");
|
||||
}
|
||||
|
||||
static int spl_board_load_image(struct spl_image_info *spl_image,
|
||||
struct spl_boot_device *bootdev)
|
||||
{
|
||||
spl_image->size = CONFIG_SYS_MONITOR_LEN; /* We don't know SPL size */
|
||||
spl_image->entry_point = CONFIG_SPL_TEXT_BASE;
|
||||
spl_image->load_addr = CONFIG_SPL_TEXT_BASE;
|
||||
spl_image->os = IH_OS_U_BOOT;
|
||||
spl_image->name = "U-Boot";
|
||||
|
||||
debug("Loading to %lx\n", spl_image->load_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
SPL_LOAD_IMAGE_METHOD("SPI", 5, BOOT_DEVICE_SPI_MMAP, spl_board_load_image);
|
||||
|
||||
int spl_spi_load_image(void)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
|
||||
{
|
||||
debug("Jumping to U-Boot SPL at %lx\n", (ulong)spl_image->entry_point);
|
||||
jump_to_spl(spl_image->entry_point);
|
||||
hang();
|
||||
}
|
||||
|
||||
void spl_board_init(void)
|
||||
{
|
||||
preloader_console_init();
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(PCI)
|
||||
/*
|
||||
* This is a fake PCI bus for TPL when it doesn't have proper PCI. It is enough
|
||||
* to bind the devices on the PCI bus, some of which have early-regs properties
|
||||
* providing fixed BARs. Individual drivers program these BARs themselves so
|
||||
* that they can access the devices. The BARs are allocated statically in the
|
||||
* device tree.
|
||||
*
|
||||
* Once SPL is running it enables PCI properly, but does not auto-assign BARs
|
||||
* for devices, so the TPL BARs continue to be used. Once U-Boot starts it does
|
||||
* the auto allocation (after relocation).
|
||||
*/
|
||||
static const struct udevice_id tpl_fake_pci_ids[] = {
|
||||
{ .compatible = "pci-x86" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_x86) = {
|
||||
.name = "pci_x86",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = tpl_fake_pci_ids,
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,376 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2011 The Chromium OS Authors.
|
||||
* (C) Copyright 2002
|
||||
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Linux x86 zImage and bzImage loading
|
||||
*
|
||||
* based on the procdure described in
|
||||
* linux/Documentation/i386/boot.txt
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <env.h>
|
||||
#include <irq_func.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/acpi_table.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/zimage.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/bootm.h>
|
||||
#include <asm/bootparam.h>
|
||||
#ifdef CONFIG_SYS_COREBOOT
|
||||
#include <asm/arch/timestamp.h>
|
||||
#endif
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/libfdt.h>
|
||||
|
||||
/*
|
||||
* Memory lay-out:
|
||||
*
|
||||
* relative to setup_base (which is 0x90000 currently)
|
||||
*
|
||||
* 0x0000-0x7FFF Real mode kernel
|
||||
* 0x8000-0x8FFF Stack and heap
|
||||
* 0x9000-0x90FF Kernel command line
|
||||
*/
|
||||
#define DEFAULT_SETUP_BASE 0x90000
|
||||
#define COMMAND_LINE_OFFSET 0x9000
|
||||
#define HEAP_END_OFFSET 0x8e00
|
||||
|
||||
#define COMMAND_LINE_SIZE 2048
|
||||
|
||||
static void build_command_line(char *command_line, int auto_boot)
|
||||
{
|
||||
char *env_command_line;
|
||||
|
||||
command_line[0] = '\0';
|
||||
|
||||
env_command_line = env_get("bootargs");
|
||||
|
||||
/* set console= argument if we use a serial console */
|
||||
if (!strstr(env_command_line, "console=")) {
|
||||
if (!strcmp(env_get("stdout"), "serial")) {
|
||||
|
||||
/* We seem to use serial console */
|
||||
sprintf(command_line, "console=ttyS0,%s ",
|
||||
env_get("baudrate"));
|
||||
}
|
||||
}
|
||||
|
||||
if (auto_boot)
|
||||
strcat(command_line, "auto ");
|
||||
|
||||
if (env_command_line)
|
||||
strcat(command_line, env_command_line);
|
||||
|
||||
printf("Kernel command line: \"%s\"\n", command_line);
|
||||
}
|
||||
|
||||
static int kernel_magic_ok(struct setup_header *hdr)
|
||||
{
|
||||
if (KERNEL_MAGIC != hdr->boot_flag) {
|
||||
printf("Error: Invalid Boot Flag "
|
||||
"(found 0x%04x, expected 0x%04x)\n",
|
||||
hdr->boot_flag, KERNEL_MAGIC);
|
||||
return 0;
|
||||
} else {
|
||||
printf("Valid Boot Flag\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_boot_protocol(struct setup_header *hdr)
|
||||
{
|
||||
if (hdr->header == KERNEL_V2_MAGIC) {
|
||||
printf("Magic signature found\n");
|
||||
return hdr->version;
|
||||
} else {
|
||||
/* Very old kernel */
|
||||
printf("Magic signature not found\n");
|
||||
return 0x0100;
|
||||
}
|
||||
}
|
||||
|
||||
static int setup_device_tree(struct setup_header *hdr, const void *fdt_blob)
|
||||
{
|
||||
int bootproto = get_boot_protocol(hdr);
|
||||
struct setup_data *sd;
|
||||
int size;
|
||||
|
||||
if (bootproto < 0x0209)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (!fdt_blob)
|
||||
return 0;
|
||||
|
||||
size = fdt_totalsize(fdt_blob);
|
||||
if (size < 0)
|
||||
return -EINVAL;
|
||||
|
||||
size += sizeof(struct setup_data);
|
||||
sd = (struct setup_data *)malloc(size);
|
||||
if (!sd) {
|
||||
printf("Not enough memory for DTB setup data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sd->next = hdr->setup_data;
|
||||
sd->type = SETUP_DTB;
|
||||
sd->len = fdt_totalsize(fdt_blob);
|
||||
memcpy(sd->data, fdt_blob, sd->len);
|
||||
hdr->setup_data = (unsigned long)sd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct boot_params *load_zimage(char *image, unsigned long kernel_size,
|
||||
ulong *load_addressp)
|
||||
{
|
||||
struct boot_params *setup_base;
|
||||
int setup_size;
|
||||
int bootproto;
|
||||
int big_image;
|
||||
|
||||
struct boot_params *params = (struct boot_params *)image;
|
||||
struct setup_header *hdr = ¶ms->hdr;
|
||||
|
||||
/* base address for real-mode segment */
|
||||
setup_base = (struct boot_params *)DEFAULT_SETUP_BASE;
|
||||
|
||||
if (!kernel_magic_ok(hdr))
|
||||
return 0;
|
||||
|
||||
/* determine size of setup */
|
||||
if (0 == hdr->setup_sects) {
|
||||
printf("Setup Sectors = 0 (defaulting to 4)\n");
|
||||
setup_size = 5 * 512;
|
||||
} else {
|
||||
setup_size = (hdr->setup_sects + 1) * 512;
|
||||
}
|
||||
|
||||
printf("Setup Size = 0x%8.8lx\n", (ulong)setup_size);
|
||||
|
||||
if (setup_size > SETUP_MAX_SIZE)
|
||||
printf("Error: Setup is too large (%d bytes)\n", setup_size);
|
||||
|
||||
/* determine boot protocol version */
|
||||
bootproto = get_boot_protocol(hdr);
|
||||
|
||||
printf("Using boot protocol version %x.%02x\n",
|
||||
(bootproto & 0xff00) >> 8, bootproto & 0xff);
|
||||
|
||||
if (bootproto >= 0x0200) {
|
||||
if (hdr->setup_sects >= 15) {
|
||||
printf("Linux kernel version %s\n",
|
||||
(char *)params +
|
||||
hdr->kernel_version + 0x200);
|
||||
} else {
|
||||
printf("Setup Sectors < 15 - "
|
||||
"Cannot print kernel version.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine image type */
|
||||
big_image = (bootproto >= 0x0200) &&
|
||||
(hdr->loadflags & BIG_KERNEL_FLAG);
|
||||
|
||||
/* Determine load address */
|
||||
if (big_image)
|
||||
*load_addressp = BZIMAGE_LOAD_ADDR;
|
||||
else
|
||||
*load_addressp = ZIMAGE_LOAD_ADDR;
|
||||
|
||||
printf("Building boot_params at 0x%8.8lx\n", (ulong)setup_base);
|
||||
memset(setup_base, 0, sizeof(*setup_base));
|
||||
setup_base->hdr = params->hdr;
|
||||
|
||||
if (bootproto >= 0x0204)
|
||||
kernel_size = hdr->syssize * 16;
|
||||
else
|
||||
kernel_size -= setup_size;
|
||||
|
||||
if (bootproto == 0x0100) {
|
||||
/*
|
||||
* A very old kernel MUST have its real-mode code
|
||||
* loaded at 0x90000
|
||||
*/
|
||||
if ((ulong)setup_base != 0x90000) {
|
||||
/* Copy the real-mode kernel */
|
||||
memmove((void *)0x90000, setup_base, setup_size);
|
||||
|
||||
/* Copy the command line */
|
||||
memmove((void *)0x99000,
|
||||
(u8 *)setup_base + COMMAND_LINE_OFFSET,
|
||||
COMMAND_LINE_SIZE);
|
||||
|
||||
/* Relocated */
|
||||
setup_base = (struct boot_params *)0x90000;
|
||||
}
|
||||
|
||||
/* It is recommended to clear memory up to the 32K mark */
|
||||
memset((u8 *)0x90000 + setup_size, 0,
|
||||
SETUP_MAX_SIZE - setup_size);
|
||||
}
|
||||
|
||||
if (big_image) {
|
||||
if (kernel_size > BZIMAGE_MAX_SIZE) {
|
||||
printf("Error: bzImage kernel too big! "
|
||||
"(size: %ld, max: %d)\n",
|
||||
kernel_size, BZIMAGE_MAX_SIZE);
|
||||
return 0;
|
||||
}
|
||||
} else if ((kernel_size) > ZIMAGE_MAX_SIZE) {
|
||||
printf("Error: zImage kernel too big! (size: %ld, max: %d)\n",
|
||||
kernel_size, ZIMAGE_MAX_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Loading %s at address %lx (%ld bytes)\n",
|
||||
big_image ? "bzImage" : "zImage", *load_addressp, kernel_size);
|
||||
|
||||
memmove((void *)*load_addressp, image + setup_size, kernel_size);
|
||||
|
||||
return setup_base;
|
||||
}
|
||||
|
||||
int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot,
|
||||
unsigned long initrd_addr, unsigned long initrd_size)
|
||||
{
|
||||
struct setup_header *hdr = &setup_base->hdr;
|
||||
int bootproto = get_boot_protocol(hdr);
|
||||
|
||||
setup_base->e820_entries = install_e820_map(
|
||||
ARRAY_SIZE(setup_base->e820_map), setup_base->e820_map);
|
||||
|
||||
if (bootproto == 0x0100) {
|
||||
setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
|
||||
setup_base->screen_info.cl_offset = COMMAND_LINE_OFFSET;
|
||||
}
|
||||
if (bootproto >= 0x0200) {
|
||||
hdr->type_of_loader = 8;
|
||||
|
||||
if (initrd_addr) {
|
||||
printf("Initial RAM disk at linear address "
|
||||
"0x%08lx, size %ld bytes\n",
|
||||
initrd_addr, initrd_size);
|
||||
|
||||
hdr->ramdisk_image = initrd_addr;
|
||||
hdr->ramdisk_size = initrd_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (bootproto >= 0x0201) {
|
||||
hdr->heap_end_ptr = HEAP_END_OFFSET;
|
||||
hdr->loadflags |= HEAP_FLAG;
|
||||
}
|
||||
|
||||
if (cmd_line) {
|
||||
if (bootproto >= 0x0202) {
|
||||
hdr->cmd_line_ptr = (uintptr_t)cmd_line;
|
||||
} else if (bootproto >= 0x0200) {
|
||||
setup_base->screen_info.cl_magic = COMMAND_LINE_MAGIC;
|
||||
setup_base->screen_info.cl_offset =
|
||||
(uintptr_t)cmd_line - (uintptr_t)setup_base;
|
||||
|
||||
hdr->setup_move_size = 0x9100;
|
||||
}
|
||||
|
||||
/* build command line at COMMAND_LINE_OFFSET */
|
||||
build_command_line(cmd_line, auto_boot);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INTEL_MID
|
||||
if (bootproto >= 0x0207)
|
||||
hdr->hardware_subarch = X86_SUBARCH_INTEL_MID;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GENERATE_ACPI_TABLE
|
||||
setup_base->acpi_rsdp_addr = acpi_get_rsdp_addr();
|
||||
#endif
|
||||
|
||||
setup_device_tree(hdr, (const void *)env_get_hex("fdtaddr", 0));
|
||||
setup_video(&setup_base->screen_info);
|
||||
|
||||
#ifdef CONFIG_EFI_STUB
|
||||
setup_efi_info(&setup_base->efi_info);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setup_pcat_compatibility(void)
|
||||
__attribute__((weak, alias("__setup_pcat_compatibility")));
|
||||
|
||||
void __setup_pcat_compatibility(void)
|
||||
{
|
||||
}
|
||||
|
||||
int do_zboot(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
||||
{
|
||||
struct boot_params *base_ptr;
|
||||
void *bzImage_addr = NULL;
|
||||
ulong load_address;
|
||||
char *s;
|
||||
ulong bzImage_size = 0;
|
||||
ulong initrd_addr = 0;
|
||||
ulong initrd_size = 0;
|
||||
|
||||
disable_interrupts();
|
||||
|
||||
/* Setup board for maximum PC/AT Compatibility */
|
||||
setup_pcat_compatibility();
|
||||
|
||||
if (argc >= 2) {
|
||||
/* argv[1] holds the address of the bzImage */
|
||||
s = argv[1];
|
||||
} else {
|
||||
s = env_get("fileaddr");
|
||||
}
|
||||
|
||||
if (s)
|
||||
bzImage_addr = (void *)simple_strtoul(s, NULL, 16);
|
||||
|
||||
if (argc >= 3) {
|
||||
/* argv[2] holds the size of the bzImage */
|
||||
bzImage_size = simple_strtoul(argv[2], NULL, 16);
|
||||
}
|
||||
|
||||
if (argc >= 4)
|
||||
initrd_addr = simple_strtoul(argv[3], NULL, 16);
|
||||
if (argc >= 5)
|
||||
initrd_size = simple_strtoul(argv[4], NULL, 16);
|
||||
|
||||
/* Lets look for */
|
||||
base_ptr = load_zimage(bzImage_addr, bzImage_size, &load_address);
|
||||
|
||||
if (!base_ptr) {
|
||||
puts("## Kernel loading failed ...\n");
|
||||
return -1;
|
||||
}
|
||||
if (setup_zimage(base_ptr, (char *)base_ptr + COMMAND_LINE_OFFSET,
|
||||
0, initrd_addr, initrd_size)) {
|
||||
puts("Setting up boot parameters failed ...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we assume that the kernel is in place */
|
||||
return boot_linux_kernel((ulong)base_ptr, load_address, false);
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
zboot, 5, 0, do_zboot,
|
||||
"Boot bzImage",
|
||||
"[addr] [size] [initrd addr] [initrd size]\n"
|
||||
" addr - The optional starting address of the bzimage.\n"
|
||||
" If not set it defaults to the environment\n"
|
||||
" variable \"fileaddr\".\n"
|
||||
" size - The optional size of the bzimage. Defaults to\n"
|
||||
" zero.\n"
|
||||
" initrd addr - The address of the initrd image to use, if any.\n"
|
||||
" initrd size - The size of the initrd image to use, if any.\n"
|
||||
);
|
||||
Reference in New Issue
Block a user