From: Tom Rini Date: Mon, 4 May 2015 21:50:20 +0000 (-0400) Subject: Merge branch 'master' of git://git.denx.de/u-boot-sunxi X-Git-Tag: v2015.07-rc1~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=622da1c36aee9c39075f2109848228a5737925c0;hp=fd01ae1384a67c354eb53fc8a4c1e0d519014ae8;p=platform%2Fkernel%2Fu-boot.git Merge branch 'master' of git://git.denx.de/u-boot-sunxi --- diff --git a/Kconfig b/Kconfig index 41d4784..85faff7 100644 --- a/Kconfig +++ b/Kconfig @@ -178,7 +178,7 @@ config SYS_EXTRA_OPTIONS new boards should not use this option. config SYS_TEXT_BASE - depends on SPARC || ARC + depends on SPARC || ARC || X86 hex "Text Base" help TODO: Move CONFIG_SYS_TEXT_BASE for all the architecture diff --git a/README b/README index ee65fdb..7958921 100644 --- a/README +++ b/README @@ -1873,12 +1873,6 @@ CBFS (Coreboot Filesystem) support boot. See the documentation file README.video for a description of this variable. - CONFIG_VIDEO_VGA - - Enable the VGA video / BIOS for x86. The alternative if you - are using coreboot is to use the coreboot frame buffer - driver. - - Keyboard Support: CONFIG_KEYBOARD diff --git a/arch/Kconfig b/arch/Kconfig index 1102346..200588a 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -93,6 +93,8 @@ config X86 select DM select DM_SERIAL select DM_GPIO + select DM_SPI + select DM_SPI_FLASH endchoice diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 3f1401a..8e734fd 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -11,84 +11,33 @@ config SYS_VSNPRINTF default y choice - prompt "Target select" + prompt "Mainboard vendor" + default VENDOR_COREBOOT -config TARGET_COREBOOT - bool "Support coreboot" - help - This target is used for running U-Boot on top of Coreboot. In - this case Coreboot does the early inititalisation, and U-Boot - takes over once the RAM, video and CPU are fully running. - U-Boot is loaded as a fallback payload from Coreboot, in - Coreboot terminology. This method was used for the Chromebook - Pixel when launched. - -config TARGET_CHROMEBOOK_LINK - bool "Support Chromebook link" - help - This is the Chromebook Pixel released in 2013. It uses an Intel - i5 Ivybridge which is a die-shrink of Sandybridge, with 4GB of - SDRAM. It has a Panther Point platform controller hub, PCIe - WiFi and Bluetooth. It also includes a 720p webcam, USB SD - reader, microphone and speakers, display port and 32GB SATA - solid state drive. There is a Chrome OS EC connected on LPC, - and it provides a 2560x1700 high resolution touch-enabled LCD - display. - -config TARGET_CHROMEBOX_PANTHER - bool "Support Chromebox panther (not available)" - select n - help - Note: At present this must be used with Coreboot. See README.x86 - for instructions. - - This is the Asus Chromebox CN60 released in 2014. It uses an Intel - Haswell Celeron 2955U Dual Core CPU with 2GB of SDRAM. It has a - Lynx Point platform controller hub, PCIe WiFi and Bluetooth. It also - includes a USB SD reader, four USB3 ports, display port and HDMI - video output and a 16GB SATA solid state drive. There is no Chrome - OS EC on this model. - -config TARGET_CROWNBAY - bool "Support Intel Crown Bay CRB" - help - This is the Intel Crown Bay Customer Reference Board. It contains - the Intel Atom Processor E6xx populated on the COM Express module - with 1GB DDR2 soldered down memory and a carrier board with the - Intel Platform Controller Hub EG20T, other system components and - peripheral connectors for PCIe/SATA/USB/LAN/SD/UART/Audio/LVDS. - -config TARGET_MINNOWMAX - bool "Support Intel Minnowboard MAX" - help - This is the Intel Minnowboard MAX. It contains an Atom E3800 - processor in a small form factor with Ethernet, micro-SD, USB 2, - USB 3, SATA, serial console, some GPIOs and HDMI 1.3 video out. - It requires some binary blobs - see README.x86 for details. +config VENDOR_COREBOOT + bool "coreboot" - Note that PCIE_ECAM_BASE is set up by the FSP so the value used - by U-Boot matches that value. +config VENDOR_GOOGLE + bool "Google" -config TARGET_GALILEO - bool "Support Intel Galileo" - help - This is the Intel Galileo board, which is the first in a family of - Arduino-certified development and prototyping boards based on Intel - architecture. It includes an Intel Quark SoC X1000 processor, a 32-bit - single-core, single-thread, Intel Pentium processor instrunction set - architecture (ISA) compatible, operating at speeds up to 400Mhz, - along with 256MB DDR3 memory. It supports a wide range of industry - standard I/O interfaces, including a full-sized mini-PCIe slot, - one 100Mb Ethernet port, a microSD card slot, a USB host port and - a USB client port. +config VENDOR_INTEL + bool "Intel" endchoice -config DM_SPI - default y +# board-specific options below +source "board/coreboot/Kconfig" +source "board/google/Kconfig" +source "board/intel/Kconfig" -config DM_SPI_FLASH - default y +# platform-specific options below +source "arch/x86/cpu/baytrail/Kconfig" +source "arch/x86/cpu/coreboot/Kconfig" +source "arch/x86/cpu/ivybridge/Kconfig" +source "arch/x86/cpu/quark/Kconfig" +source "arch/x86/cpu/queensbay/Kconfig" + +# architecture-specific options below config SYS_MALLOC_F_LEN default 0x800 @@ -226,12 +175,12 @@ config X86_RAMTEST detecting obvious failures. config MARK_GRAPHICS_MEM_WRCOMB - bool "Mark graphics memory as write-combining." + bool "Mark graphics memory as write-combining" default n help - The graphics performance may increase if the graphics - memory is set as write-combining cache type. This option - enables marking the graphics memory as write-combining. + The graphics performance may increase if the graphics + memory is set as write-combining cache type. This option + enables marking the graphics memory as write-combining. menu "Display" @@ -412,15 +361,35 @@ config FSP_TEMP_RAM_ADDR Stack top address which is used in FspInit after DRAM is ready and CAR is disabled. -source "arch/x86/cpu/baytrail/Kconfig" - -source "arch/x86/cpu/coreboot/Kconfig" - -source "arch/x86/cpu/ivybridge/Kconfig" - -source "arch/x86/cpu/quark/Kconfig" +config MAX_CPUS + int "Maximum number of CPUs permitted" + default 4 + help + When using multi-CPU chips it is possible for U-Boot to start up + more than one CPU. The stack memory used by all of these CPUs is + pre-allocated so at present U-Boot wants to know the maximum + number of CPUs that may be present. Set this to at least as high + as the number of CPUs in your system (it uses about 4KB of RAM for + each CPU). + +config SMP + bool "Enable Symmetric Multiprocessing" + default n + help + Enable use of more than one CPU in U-Boot and the Operating System + when loaded. Each CPU will be started up and information can be + obtained using the 'cpu' command. If this option is disabled, then + only one CPU will be enabled regardless of the number of CPUs + available. -source "arch/x86/cpu/queensbay/Kconfig" +config AP_STACK_SIZE + hex + default 0x1000 + help + Each additional CPU started by U-Boot requires its own stack. This + option sets the stack size used by each CPU and directly affects + the memory used by this initialisation process. Typically 4KB is + enough space. config TSC_CALIBRATION_BYPASS bool "Bypass Time-Stamp Counter (TSC) calibration" @@ -442,17 +411,50 @@ config TSC_FREQ_IN_MHZ help The running frequency in MHz of Time-Stamp Counter (TSC). -source "board/coreboot/coreboot/Kconfig" +menu "System tables" -source "board/google/chromebook_link/Kconfig" +config GENERATE_PIRQ_TABLE + bool "Generate a PIRQ table" + default n + help + Generate a PIRQ routing table for this board. The PIRQ routing table + is generated by U-Boot in the system memory from 0xf0000 to 0xfffff + at every 16-byte boundary with a PCI IRQ routing signature ("$PIR"). + It specifies the interrupt router information as well how all the PCI + devices' interrupt pins are wired to PIRQs. + +config GENERATE_SFI_TABLE + bool "Generate a SFI (Simple Firmware Interface) table" + help + The Simple Firmware Interface (SFI) provides a lightweight method + for platform firmware to pass information to the operating system + via static tables in memory. Kernel SFI support is required to + boot on SFI-only platforms. If you have ACPI tables then these are + used instead. -source "board/google/chromebox_panther/Kconfig" + U-Boot writes this table in write_sfi_table() just before booting + the OS. -source "board/intel/crownbay/Kconfig" + For more information, see http://simplefirmware.org + +endmenu -source "board/intel/minnowmax/Kconfig" +config MAX_PIRQ_LINKS + int + default 8 + help + This variable specifies the number of PIRQ interrupt links which are + routable. On most older chipsets, this is 4, PIRQA through PIRQD. + Some newer chipsets offer more than four links, commonly up to PIRQH. -source "board/intel/galileo/Kconfig" +config IRQ_SLOT_COUNT + int + default 128 + help + U-Boot can support up to 254 IRQ slot info in the PIRQ routing table + which in turns forms a table of exact 4KiB. The default value 128 + should be enough for most boards. If this does not fit your board, + change it according to your needs. config PCIE_ECAM_BASE hex diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile index 6ded0a7..043bea2 100644 --- a/arch/x86/cpu/Makefile +++ b/arch/x86/cpu/Makefile @@ -19,6 +19,8 @@ obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/ obj-$(CONFIG_INTEL_QUARK) += quark/ obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/ obj-y += lapic.o +obj-$(CONFIG_SMP) += mp_init.o obj-y += mtrr.o obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_SMP) += sipi_vector.o obj-y += turbo.o diff --git a/arch/x86/cpu/baytrail/Makefile b/arch/x86/cpu/baytrail/Makefile index 8914e8b..c78b644 100644 --- a/arch/x86/cpu/baytrail/Makefile +++ b/arch/x86/cpu/baytrail/Makefile @@ -4,6 +4,7 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-y += cpu.o obj-y += early_uart.o obj-y += fsp_configs.o obj-y += pci.o diff --git a/arch/x86/cpu/baytrail/cpu.c b/arch/x86/cpu/baytrail/cpu.c new file mode 100644 index 0000000..1d48206 --- /dev/null +++ b/arch/x86/cpu/baytrail/cpu.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Based on code from coreboot + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SMP +static int enable_smis(struct udevice *cpu, void *unused) +{ + return 0; +} + +static struct mp_flight_record mp_steps[] = { + MP_FR_BLOCK_APS(mp_init_cpu, NULL, mp_init_cpu, NULL), + /* Wait for APs to finish initialization before proceeding. */ + MP_FR_BLOCK_APS(NULL, NULL, enable_smis, NULL), +}; + +static int detect_num_cpus(void) +{ + int ecx = 0; + + /* + * Use the algorithm described in Intel 64 and IA-32 Architectures + * Software Developer's Manual Volume 3 (3A, 3B & 3C): System + * Programming Guide, Jan-2015. Section 8.9.2: Hierarchical Mapping + * of CPUID Extended Topology Leaf. + */ + while (1) { + struct cpuid_result leaf_b; + + leaf_b = cpuid_ext(0xb, ecx); + + /* + * Bay Trail doesn't have hyperthreading so just determine the + * number of cores by from level type (ecx[15:8] == * 2) + */ + if ((leaf_b.ecx & 0xff00) == 0x0200) + return leaf_b.ebx & 0xffff; + ecx++; + } +} + +static int baytrail_init_cpus(void) +{ + struct mp_params mp_params; + + lapic_setup(); + + mp_params.num_cpus = detect_num_cpus(); + mp_params.parallel_microcode_load = 0, + mp_params.flight_plan = &mp_steps[0]; + mp_params.num_records = ARRAY_SIZE(mp_steps); + mp_params.microcode_pointer = 0; + + if (mp_init(&mp_params)) { + printf("Warning: MP init failure\n"); + return -EIO; + } + + return 0; +} +#endif + +int x86_init_cpus(void) +{ +#ifdef CONFIG_SMP + debug("Init additional CPUs\n"); + baytrail_init_cpus(); +#endif + + return 0; +} + +static void set_max_freq(void) +{ + msr_t perf_ctl; + msr_t msr; + + /* Enable speed step */ + msr = msr_read(MSR_IA32_MISC_ENABLES); + msr.lo |= (1 << 16); + msr_write(MSR_IA32_MISC_ENABLES, msr); + + /* + * Set guaranteed ratio [21:16] from IACORE_RATIOS to bits [15:8] of + * the PERF_CTL + */ + msr = msr_read(MSR_IACORE_RATIOS); + perf_ctl.lo = (msr.lo & 0x3f0000) >> 8; + + /* + * Set guaranteed vid [21:16] from IACORE_VIDS to bits [7:0] of + * the PERF_CTL + */ + msr = msr_read(MSR_IACORE_VIDS); + perf_ctl.lo |= (msr.lo & 0x7f0000) >> 16; + perf_ctl.hi = 0; + + msr_write(MSR_IA32_PERF_CTL, perf_ctl); +} + +static int cpu_x86_baytrail_probe(struct udevice *dev) +{ + debug("Init BayTrail core\n"); + + /* + * On BayTrail the turbo disable bit is actually scoped at the + * building-block level, not package. For non-BSP cores that are + * within a building block, enable turbo. The cores within the BSP's + * building block will just see it already enabled and move on. + */ + if (lapicid()) + turbo_enable(); + + /* Dynamic L2 shrink enable and threshold */ + msr_clrsetbits_64(MSR_PMG_CST_CONFIG_CONTROL, 0x3f000f, 0xe0008), + + /* Disable C1E */ + msr_clrsetbits_64(MSR_POWER_CTL, 2, 0); + msr_setbits_64(MSR_POWER_MISC, 0x44); + + /* Set this core to max frequency ratio */ + set_max_freq(); + + return 0; +} + +static unsigned bus_freq(void) +{ + msr_t clk_info = msr_read(MSR_BSEL_CR_OVERCLOCK_CONTROL); + switch (clk_info.lo & 0x3) { + case 0: + return 83333333; + case 1: + return 100000000; + case 2: + return 133333333; + case 3: + return 116666666; + default: + return 0; + } +} + +static unsigned long tsc_freq(void) +{ + msr_t platform_info; + ulong bclk = bus_freq(); + + if (!bclk) + return 0; + + platform_info = msr_read(MSR_PLATFORM_INFO); + + return bclk * ((platform_info.lo >> 8) & 0xff); +} + +static int baytrail_get_info(struct udevice *dev, struct cpu_info *info) +{ + info->cpu_freq = tsc_freq(); + info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU; + + return 0; +} + +static int cpu_x86_baytrail_bind(struct udevice *dev) +{ + struct cpu_platdata *plat = dev_get_parent_platdata(dev); + + plat->cpu_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "intel,apic-id", -1); + + return 0; +} + +static const struct cpu_ops cpu_x86_baytrail_ops = { + .get_desc = x86_cpu_get_desc, + .get_info = baytrail_get_info, +}; + +static const struct udevice_id cpu_x86_baytrail_ids[] = { + { .compatible = "intel,baytrail-cpu" }, + { } +}; + +U_BOOT_DRIVER(cpu_x86_baytrail_drv) = { + .name = "cpu_x86_baytrail", + .id = UCLASS_CPU, + .of_match = cpu_x86_baytrail_ids, + .bind = cpu_x86_baytrail_bind, + .probe = cpu_x86_baytrail_probe, + .ops = &cpu_x86_baytrail_ops, +}; diff --git a/arch/x86/cpu/baytrail/valleyview.c b/arch/x86/cpu/baytrail/valleyview.c index a3e837d..9915da5 100644 --- a/arch/x86/cpu/baytrail/valleyview.c +++ b/arch/x86/cpu/baytrail/valleyview.c @@ -16,7 +16,6 @@ static struct pci_device_id mmc_supported[] = { int cpu_mmc_init(bd_t *bis) { - printf("mmc init\n"); return pci_mmc_init("ValleyView SDHCI", mmc_supported, ARRAY_SIZE(mmc_supported)); } diff --git a/arch/x86/cpu/config.mk b/arch/x86/cpu/config.mk index 84aeaf3..4c4d0c7 100644 --- a/arch/x86/cpu/config.mk +++ b/arch/x86/cpu/config.mk @@ -7,7 +7,7 @@ CROSS_COMPILE ?= i386-linux- -PLATFORM_CPPFLAGS += -D__I386__ -Werror +PLATFORM_CPPFLAGS += -D__I386__ # DO NOT MODIFY THE FOLLOWING UNLESS YOU REALLY KNOW WHAT YOU ARE DOING! LDPPFLAGS += -DRESET_SEG_START=0xffff0000 diff --git a/arch/x86/cpu/coreboot/pci.c b/arch/x86/cpu/coreboot/pci.c index fa415dd..67eb14c 100644 --- a/arch/x86/cpu/coreboot/pci.c +++ b/arch/x86/cpu/coreboot/pci.c @@ -34,3 +34,14 @@ U_BOOT_DRIVER(pci_x86_drv) = { .of_match = pci_x86_ids, .ops = &pci_x86_ops, }; + +static const struct udevice_id generic_pch_ids[] = { + { .compatible = "intel,pch" }, + { } +}; + +U_BOOT_DRIVER(generic_pch_drv) = { + .name = "pch", + .id = UCLASS_PCH, + .of_match = generic_pch_ids, +}; diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c index a9ca50b..bb4a110 100644 --- a/arch/x86/cpu/cpu.c +++ b/arch/x86/cpu/cpu.c @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include #include @@ -29,6 +31,7 @@ #include #include #include +#include #include DECLARE_GLOBAL_DATA_PTR; @@ -132,6 +135,7 @@ static void load_gdt(const u64 *boot_gdt, u16 num_entries) void setup_gdt(gd_t *id, u64 *gdt_addr) { + id->arch.gdt = gdt_addr; /* CS: code, read/execute, 4 GB, base 0 */ gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff); @@ -379,21 +383,17 @@ void flush_cache(unsigned long dummy1, unsigned long dummy2) asm("wbinvd\n"); } -void __attribute__ ((regparm(0))) generate_gpf(void); - -/* segment 0x70 is an arbitrary segment which does not exist */ -asm(".globl generate_gpf\n" - ".hidden generate_gpf\n" - ".type generate_gpf, @function\n" - "generate_gpf:\n" - "ljmp $0x70, $0x47114711\n"); - __weak void reset_cpu(ulong addr) { - printf("Resetting using x86 Triple Fault\n"); - set_vector(13, generate_gpf); /* general protection fault handler */ - set_vector(8, generate_gpf); /* double fault handler */ - generate_gpf(); /* start the show */ + /* Do a hard reset through the chipset's reset control register */ + outb(SYS_RST | RST_CPU, PORT_RESET); + for (;;) + cpu_hlt(); +} + +void x86_full_reset(void) +{ + outb(FULL_RST | SYS_RST | RST_CPU, PORT_RESET); } int dcache_status(void) @@ -520,6 +520,16 @@ char *cpu_get_name(char *name) return ptr; } +int x86_cpu_get_desc(struct udevice *dev, char *buf, int size) +{ + if (size < CPU_MAX_NAME_LEN) + return -ENOSPC; + + cpu_get_name(buf); + + return 0; +} + int default_print_cpuinfo(void) { printf("CPU: %s, vendor %s, device %xh\n", @@ -593,3 +603,38 @@ void show_boot_progress(int val) #endif outb(val, POST_PORT); } + +#ifndef CONFIG_SYS_COREBOOT +int last_stage_init(void) +{ + write_tables(); + + return 0; +} +#endif + +__weak int x86_init_cpus(void) +{ + return 0; +} + +int cpu_init_r(void) +{ + return x86_init_cpus(); +} + +static const struct cpu_ops cpu_x86_ops = { + .get_desc = x86_cpu_get_desc, +}; + +static const struct udevice_id cpu_x86_ids[] = { + { .compatible = "cpu-x86" }, + { } +}; + +U_BOOT_DRIVER(cpu_x86_drv) = { + .name = "cpu_x86", + .id = UCLASS_CPU, + .of_match = cpu_x86_ids, + .ops = &cpu_x86_ops, +}; diff --git a/arch/x86/cpu/interrupts.c b/arch/x86/cpu/interrupts.c index a21d2a6..c777d36 100644 --- a/arch/x86/cpu/interrupts.c +++ b/arch/x86/cpu/interrupts.c @@ -147,6 +147,11 @@ int cpu_init_interrupts(void) return 0; } +void *x86_get_idt(void) +{ + return &idt_ptr; +} + void __do_irq(int irq) { printf("Unhandled IRQ : %d\n", irq); diff --git a/arch/x86/cpu/ivybridge/car.S b/arch/x86/cpu/ivybridge/car.S index 9441666..407e451 100644 --- a/arch/x86/cpu/ivybridge/car.S +++ b/arch/x86/cpu/ivybridge/car.S @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/cpu/ivybridge/cpu.c b/arch/x86/cpu/ivybridge/cpu.c index 37f3731..cce5923 100644 --- a/arch/x86/cpu/ivybridge/cpu.c +++ b/arch/x86/cpu/ivybridge/cpu.c @@ -92,7 +92,7 @@ static int set_flex_ratio_to_tdp_nominal(void) /* Issue warm reset, will be "CPU only" due to soft reset data */ outb(0x0, PORT_RESET); - outb(0x6, PORT_RESET); + outb(SYS_RST | RST_CPU, PORT_RESET); cpu_hlt(); /* Not reached */ @@ -286,8 +286,7 @@ int print_cpuinfo(void) /* System is not happy after keyboard reset... */ debug("Issuing CF9 warm reset\n"); - outb(0x6, 0xcf9); - cpu_hlt(); + reset_cpu(0); } /* Early chipset init required before RAM init can work */ diff --git a/arch/x86/cpu/ivybridge/early_me.c b/arch/x86/cpu/ivybridge/early_me.c index 356bbb4..711470f 100644 --- a/arch/x86/cpu/ivybridge/early_me.c +++ b/arch/x86/cpu/ivybridge/early_me.c @@ -117,7 +117,6 @@ static inline void set_global_reset(int enable) int intel_early_me_init_done(u8 status) { - u8 reset; int count; u32 mebase_l, mebase_h; struct me_hfs hfs; @@ -156,7 +155,6 @@ int intel_early_me_init_done(u8 status) /* Check status after acknowledgement */ intel_early_me_status(); - reset = 0; switch (hfs.ack_data) { case ME_HFS_ACK_CONTINUE: /* Continue to boot */ @@ -164,17 +162,17 @@ int intel_early_me_init_done(u8 status) case ME_HFS_ACK_RESET: /* Non-power cycle reset */ set_global_reset(0); - reset = 0x06; + reset_cpu(0); break; case ME_HFS_ACK_PWR_CYCLE: /* Power cycle reset */ set_global_reset(0); - reset = 0x0e; + x86_full_reset(); break; case ME_HFS_ACK_GBL_RESET: /* Global reset */ set_global_reset(1); - reset = 0x0e; + x86_full_reset(); break; case ME_HFS_ACK_S3: case ME_HFS_ACK_S4: @@ -182,10 +180,5 @@ int intel_early_me_init_done(u8 status) break; } - /* Perform the requested reset */ - if (reset) { - outb(reset, 0xcf9); - cpu_hlt(); - } return -1; } diff --git a/arch/x86/cpu/ivybridge/model_206ax.c b/arch/x86/cpu/ivybridge/model_206ax.c index 11dc625..8b08c40 100644 --- a/arch/x86/cpu/ivybridge/model_206ax.c +++ b/arch/x86/cpu/ivybridge/model_206ax.c @@ -435,8 +435,8 @@ static int intel_cores_init(struct x86_cpu_priv *cpu) debug("CPU: %u has core %u\n", cpu->apic_id, new_cpu->apic_id); -#if CONFIG_SMP && CONFIG_MAX_CPUS > 1 - /* Start the new cpu */ +#if 0 && CONFIG_SMP && CONFIG_MAX_CPUS > 1 + /* TODO(sjg@chromium.org): Start the new cpu */ if (!start_cpu(new_cpu)) { /* Record the error in cpu? */ printk(BIOS_ERR, "CPU %u would not start!\n", diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c index 9a6da37..af907c5 100644 --- a/arch/x86/cpu/ivybridge/sdram.c +++ b/arch/x86/cpu/ivybridge/sdram.c @@ -393,8 +393,7 @@ int sdram_initialise(struct pei_data *pei_data) /* If MRC data is not found we cannot continue S3 resume. */ if (pei_data->boot_mode == PEI_BOOT_RESUME && !pei_data->mrc_input) { debug("Giving up in sdram_initialize: No MRC data\n"); - outb(0x6, PORT_RESET); - cpu_hlt(); + reset_cpu(0); } /* Pass console handler in pei_data */ diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c new file mode 100644 index 0000000..ac5753a --- /dev/null +++ b/arch/x86/cpu/mp_init.c @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Based on code from the coreboot file of the same name + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This also needs to match the sipi.S assembly code for saved MSR encoding */ +struct saved_msr { + uint32_t index; + uint32_t lo; + uint32_t hi; +} __packed; + + +struct mp_flight_plan { + int num_records; + struct mp_flight_record *records; +}; + +static struct mp_flight_plan mp_info; + +struct cpu_map { + struct udevice *dev; + int apic_id; + int err_code; +}; + +static inline void barrier_wait(atomic_t *b) +{ + while (atomic_read(b) == 0) + asm("pause"); + mfence(); +} + +static inline void release_barrier(atomic_t *b) +{ + mfence(); + atomic_set(b, 1); +} + +/* Returns 1 if timeout waiting for APs. 0 if target APs found */ +static int wait_for_aps(atomic_t *val, int target, int total_delay, + int delay_step) +{ + int timeout = 0; + int delayed = 0; + + while (atomic_read(val) != target) { + udelay(delay_step); + delayed += delay_step; + if (delayed >= total_delay) { + timeout = 1; + break; + } + } + + return timeout; +} + +static void ap_do_flight_plan(struct udevice *cpu) +{ + int i; + + for (i = 0; i < mp_info.num_records; i++) { + struct mp_flight_record *rec = &mp_info.records[i]; + + atomic_inc(&rec->cpus_entered); + barrier_wait(&rec->barrier); + + if (rec->ap_call != NULL) + rec->ap_call(cpu, rec->ap_arg); + } +} + +static int find_cpu_by_apid_id(int apic_id, struct udevice **devp) +{ + struct udevice *dev; + + *devp = NULL; + for (uclass_find_first_device(UCLASS_CPU, &dev); + dev; + uclass_find_next_device(&dev)) { + struct cpu_platdata *plat = dev_get_parent_platdata(dev); + + if (plat->cpu_id == apic_id) { + *devp = dev; + return 0; + } + } + + return -ENOENT; +} + +/* + * By the time APs call ap_init() caching has been setup, and microcode has + * been loaded + */ +static void ap_init(unsigned int cpu_index) +{ + struct udevice *dev; + int apic_id; + int ret; + + /* Ensure the local apic is enabled */ + enable_lapic(); + + apic_id = lapicid(); + ret = find_cpu_by_apid_id(apic_id, &dev); + if (ret) { + debug("Unknown CPU apic_id %x\n", apic_id); + goto done; + } + + debug("AP: slot %d apic_id %x, dev %s\n", cpu_index, apic_id, + dev ? dev->name : "(apic_id not found)"); + + /* Walk the flight plan */ + ap_do_flight_plan(dev); + + /* Park the AP */ + debug("parking\n"); +done: + stop_this_cpu(); +} + +static const unsigned int fixed_mtrrs[NUM_FIXED_MTRRS] = { + MTRR_FIX_64K_00000_MSR, MTRR_FIX_16K_80000_MSR, MTRR_FIX_16K_A0000_MSR, + MTRR_FIX_4K_C0000_MSR, MTRR_FIX_4K_C8000_MSR, MTRR_FIX_4K_D0000_MSR, + MTRR_FIX_4K_D8000_MSR, MTRR_FIX_4K_E0000_MSR, MTRR_FIX_4K_E8000_MSR, + MTRR_FIX_4K_F0000_MSR, MTRR_FIX_4K_F8000_MSR, +}; + +static inline struct saved_msr *save_msr(int index, struct saved_msr *entry) +{ + msr_t msr; + + msr = msr_read(index); + entry->index = index; + entry->lo = msr.lo; + entry->hi = msr.hi; + + /* Return the next entry */ + entry++; + return entry; +} + +static int save_bsp_msrs(char *start, int size) +{ + int msr_count; + int num_var_mtrrs; + struct saved_msr *msr_entry; + int i; + msr_t msr; + + /* Determine number of MTRRs need to be saved */ + msr = msr_read(MTRR_CAP_MSR); + num_var_mtrrs = msr.lo & 0xff; + + /* 2 * num_var_mtrrs for base and mask. +1 for IA32_MTRR_DEF_TYPE */ + msr_count = 2 * num_var_mtrrs + NUM_FIXED_MTRRS + 1; + + if ((msr_count * sizeof(struct saved_msr)) > size) { + printf("Cannot mirror all %d msrs.\n", msr_count); + return -ENOSPC; + } + + msr_entry = (void *)start; + for (i = 0; i < NUM_FIXED_MTRRS; i++) + msr_entry = save_msr(fixed_mtrrs[i], msr_entry); + + for (i = 0; i < num_var_mtrrs; i++) { + msr_entry = save_msr(MTRR_PHYS_BASE_MSR(i), msr_entry); + msr_entry = save_msr(MTRR_PHYS_MASK_MSR(i), msr_entry); + } + + msr_entry = save_msr(MTRR_DEF_TYPE_MSR, msr_entry); + + return msr_count; +} + +static int load_sipi_vector(atomic_t **ap_countp) +{ + struct sipi_params_16bit *params16; + struct sipi_params *params; + static char msr_save[512]; + char *stack; + ulong addr; + int code_len; + int size; + int ret; + + /* Copy in the code */ + code_len = ap_start16_code_end - ap_start16; + debug("Copying SIPI code to %x: %d bytes\n", AP_DEFAULT_BASE, + code_len); + memcpy((void *)AP_DEFAULT_BASE, ap_start16, code_len); + + addr = AP_DEFAULT_BASE + (ulong)sipi_params_16bit - (ulong)ap_start16; + params16 = (struct sipi_params_16bit *)addr; + params16->ap_start = (uint32_t)ap_start; + params16->gdt = (uint32_t)gd->arch.gdt; + params16->gdt_limit = X86_GDT_SIZE - 1; + debug("gdt = %x, gdt_limit = %x\n", params16->gdt, params16->gdt_limit); + + params = (struct sipi_params *)sipi_params; + debug("SIPI 32-bit params at %p\n", params); + params->idt_ptr = (uint32_t)x86_get_idt(); + + params->stack_size = CONFIG_AP_STACK_SIZE; + size = params->stack_size * CONFIG_MAX_CPUS; + stack = memalign(size, 4096); + if (!stack) + return -ENOMEM; + params->stack_top = (u32)(stack + size); + + params->microcode_ptr = 0; + params->msr_table_ptr = (u32)msr_save; + ret = save_bsp_msrs(msr_save, sizeof(msr_save)); + if (ret < 0) + return ret; + params->msr_count = ret; + + params->c_handler = (uint32_t)&ap_init; + + *ap_countp = ¶ms->ap_count; + atomic_set(*ap_countp, 0); + debug("SIPI vector is ready\n"); + + return 0; +} + +static int check_cpu_devices(int expected_cpus) +{ + int i; + + for (i = 0; i < expected_cpus; i++) { + struct udevice *dev; + int ret; + + ret = uclass_find_device(UCLASS_CPU, i, &dev); + if (ret) { + debug("Cannot find CPU %d in device tree\n", i); + return ret; + } + } + + return 0; +} + +/* Returns 1 for timeout. 0 on success */ +static int apic_wait_timeout(int total_delay, int delay_step) +{ + int total = 0; + int timeout = 0; + + while (lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY) { + udelay(delay_step); + total += delay_step; + if (total >= total_delay) { + timeout = 1; + break; + } + } + + return timeout; +} + +static int start_aps(int ap_count, atomic_t *num_aps) +{ + int sipi_vector; + /* Max location is 4KiB below 1MiB */ + const int max_vector_loc = ((1 << 20) - (1 << 12)) >> 12; + + if (ap_count == 0) + return 0; + + /* The vector is sent as a 4k aligned address in one byte */ + sipi_vector = AP_DEFAULT_BASE >> 12; + + if (sipi_vector > max_vector_loc) { + printf("SIPI vector too large! 0x%08x\n", + sipi_vector); + return -1; + } + + debug("Attempting to start %d APs\n", ap_count); + + if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { + debug("Waiting for ICR not to be busy..."); + if (apic_wait_timeout(1000, 50)) { + debug("timed out. Aborting.\n"); + return -1; + } else { + debug("done.\n"); + } + } + + /* Send INIT IPI to all but self */ + lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); + lapic_write_around(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | + LAPIC_DM_INIT); + debug("Waiting for 10ms after sending INIT.\n"); + mdelay(10); + + /* Send 1st SIPI */ + if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { + debug("Waiting for ICR not to be busy..."); + if (apic_wait_timeout(1000, 50)) { + debug("timed out. Aborting.\n"); + return -1; + } else { + debug("done.\n"); + } + } + + lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); + lapic_write_around(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | + LAPIC_DM_STARTUP | sipi_vector); + debug("Waiting for 1st SIPI to complete..."); + if (apic_wait_timeout(10000, 50)) { + debug("timed out.\n"); + return -1; + } else { + debug("done.\n"); + } + + /* Wait for CPUs to check in up to 200 us */ + wait_for_aps(num_aps, ap_count, 200, 15); + + /* Send 2nd SIPI */ + if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { + debug("Waiting for ICR not to be busy..."); + if (apic_wait_timeout(1000, 50)) { + debug("timed out. Aborting.\n"); + return -1; + } else { + debug("done.\n"); + } + } + + lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0)); + lapic_write_around(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT | + LAPIC_DM_STARTUP | sipi_vector); + debug("Waiting for 2nd SIPI to complete..."); + if (apic_wait_timeout(10000, 50)) { + debug("timed out.\n"); + return -1; + } else { + debug("done.\n"); + } + + /* Wait for CPUs to check in */ + if (wait_for_aps(num_aps, ap_count, 10000, 50)) { + debug("Not all APs checked in: %d/%d.\n", + atomic_read(num_aps), ap_count); + return -1; + } + + return 0; +} + +static int bsp_do_flight_plan(struct udevice *cpu, struct mp_params *mp_params) +{ + int i; + int ret = 0; + const int timeout_us = 100000; + const int step_us = 100; + int num_aps = mp_params->num_cpus - 1; + + for (i = 0; i < mp_params->num_records; i++) { + struct mp_flight_record *rec = &mp_params->flight_plan[i]; + + /* Wait for APs if the record is not released */ + if (atomic_read(&rec->barrier) == 0) { + /* Wait for the APs to check in */ + if (wait_for_aps(&rec->cpus_entered, num_aps, + timeout_us, step_us)) { + debug("MP record %d timeout.\n", i); + ret = -1; + } + } + + if (rec->bsp_call != NULL) + rec->bsp_call(cpu, rec->bsp_arg); + + release_barrier(&rec->barrier); + } + return ret; +} + +static int init_bsp(struct udevice **devp) +{ + char processor_name[CPU_MAX_NAME_LEN]; + int apic_id; + int ret; + + cpu_get_name(processor_name); + debug("CPU: %s.\n", processor_name); + + enable_lapic(); + + apic_id = lapicid(); + ret = find_cpu_by_apid_id(apic_id, devp); + if (ret) { + printf("Cannot find boot CPU, APIC ID %d\n", apic_id); + return ret; + } + + return 0; +} + +int mp_init(struct mp_params *p) +{ + int num_aps; + atomic_t *ap_count; + struct udevice *cpu; + int ret; + + /* This will cause the CPUs devices to be bound */ + struct uclass *uc; + ret = uclass_get(UCLASS_CPU, &uc); + if (ret) + return ret; + + ret = init_bsp(&cpu); + if (ret) { + debug("Cannot init boot CPU: err=%d\n", ret); + return ret; + } + + if (p == NULL || p->flight_plan == NULL || p->num_records < 1) { + printf("Invalid MP parameters\n"); + return -1; + } + + ret = check_cpu_devices(p->num_cpus); + if (ret) + debug("Warning: Device tree does not describe all CPUs. Extra ones will not be started correctly\n"); + + /* Copy needed parameters so that APs have a reference to the plan */ + mp_info.num_records = p->num_records; + mp_info.records = p->flight_plan; + + /* Load the SIPI vector */ + ret = load_sipi_vector(&ap_count); + if (ap_count == NULL) + return -1; + + /* + * Make sure SIPI data hits RAM so the APs that come up will see + * the startup code even if the caches are disabled + */ + wbinvd(); + + /* Start the APs providing number of APs and the cpus_entered field */ + num_aps = p->num_cpus - 1; + ret = start_aps(num_aps, ap_count); + if (ret) { + mdelay(1000); + debug("%d/%d eventually checked in?\n", atomic_read(ap_count), + num_aps); + return ret; + } + + /* Walk the flight plan for the BSP */ + ret = bsp_do_flight_plan(cpu, p); + if (ret) { + debug("CPU init failed: err=%d\n", ret); + return ret; + } + + return 0; +} + +int mp_init_cpu(struct udevice *cpu, void *unused) +{ + return device_probe(cpu); +} diff --git a/arch/x86/cpu/pci.c b/arch/x86/cpu/pci.c index e23b233..c209f15 100644 --- a/arch/x86/cpu/pci.c +++ b/arch/x86/cpu/pci.c @@ -151,3 +151,24 @@ int pci_x86_write_config(struct udevice *bus, pci_dev_t bdf, uint offset, return 0; } + +void pci_assign_irqs(int bus, int device, int func, u8 irq[4]) +{ + pci_dev_t bdf; + u8 pin, line; + + bdf = PCI_BDF(bus, device, func); + + pin = x86_pci_read_config8(bdf, PCI_INTERRUPT_PIN); + + /* PCI spec says all values except 1..4 are reserved */ + if ((pin < 1) || (pin > 4)) + return; + + line = irq[pin - 1]; + + debug("Assigning IRQ %d to PCI device %d.%x.%d (INT%c)\n", + line, bus, device, func, 'A' + pin - 1); + + x86_pci_write_config8(bdf, PCI_INTERRUPT_LINE, line); +} diff --git a/arch/x86/cpu/quark/quark.c b/arch/x86/cpu/quark/quark.c index e4b19c2..e78a271 100644 --- a/arch/x86/cpu/quark/quark.c +++ b/arch/x86/cpu/quark/quark.c @@ -72,6 +72,15 @@ static void quark_setup_bars(void) CONFIG_PCIE_ECAM_BASE | MEM_BAR_EN); } +static void quark_enable_legacy_seg(void) +{ + u32 hmisc2; + + hmisc2 = msg_port_read(MSG_PORT_HOST_BRIDGE, HMISC2); + hmisc2 |= (HMISC2_SEGE | HMISC2_SEGF | HMISC2_SEGAB); + msg_port_write(MSG_PORT_HOST_BRIDGE, HMISC2, hmisc2); +} + int arch_cpu_init(void) { struct pci_controller *hose; @@ -96,6 +105,9 @@ int arch_cpu_init(void) */ quark_setup_bars(); + /* Turn on legacy segments (A/B/E/F) decode to system RAM */ + quark_enable_legacy_seg(); + unprotect_spi_flash(); return 0; @@ -110,7 +122,7 @@ int print_cpuinfo(void) void reset_cpu(ulong addr) { /* cold reset */ - outb(0x08, PORT_RESET); + x86_full_reset(); } int cpu_mmc_init(bd_t *bis) diff --git a/arch/x86/cpu/queensbay/Makefile b/arch/x86/cpu/queensbay/Makefile index d8761fd..4599a48 100644 --- a/arch/x86/cpu/queensbay/Makefile +++ b/arch/x86/cpu/queensbay/Makefile @@ -5,5 +5,5 @@ # obj-y += fsp_configs.o -obj-y += tnc.o topcliff.o +obj-y += irq.o tnc.o topcliff.o obj-$(CONFIG_PCI) += tnc_pci.o diff --git a/arch/x86/cpu/queensbay/irq.c b/arch/x86/cpu/queensbay/irq.c new file mode 100644 index 0000000..faf9515 --- /dev/null +++ b/arch/x86/cpu/queensbay/irq.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2015, Bin Meng + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct irq_routing_table *pirq_routing_table; + +bool pirq_check_irq_routed(int link, u8 irq) +{ + u8 pirq; + + pirq = x86_pci_read_config8(TNC_LPC, LINK_N2V(link)); + pirq &= 0xf; + + /* IRQ# 0/1/2/8/13 are reserved */ + if (pirq < 3 || pirq == 8 || pirq == 13) + return false; + + return pirq == irq ? true : false; +} + +int pirq_translate_link(int link) +{ + return LINK_V2N(link); +} + +void pirq_assign_irq(int link, u8 irq) +{ + /* IRQ# 0/1/2/8/13 are reserved */ + if (irq < 3 || irq == 8 || irq == 13) + return; + + x86_pci_write_config8(TNC_LPC, LINK_N2V(link), irq); +} + +static inline void fill_irq_info(struct irq_info **slotp, int *entries, u8 bus, + u8 device, u8 func, u8 pin, u8 pirq) +{ + struct irq_info *slot = *slotp; + + slot->bus = bus; + slot->devfn = (device << 3) | func; + slot->irq[pin - 1].link = LINK_N2V(pirq); + slot->irq[pin - 1].bitmap = PIRQ_BITMAP; + (*entries)++; + (*slotp)++; +} + +/* PCIe port downstream INTx swizzle */ +static inline u8 pin_swizzle(u8 pin, int port) +{ + return (pin + port) % 4; +} + +__weak int board_fill_irq_info(struct irq_info *slot) +{ + return 0; +} + +static int create_pirq_routing_table(void) +{ + struct irq_routing_table *rt; + struct irq_info *slot; + int irq_entries = 0; + pci_dev_t tcf_bdf; + u8 tcf_bus, bus; + int i; + + rt = malloc(sizeof(struct irq_routing_table)); + if (!rt) + return -ENOMEM; + memset((char *)rt, 0, sizeof(struct irq_routing_table)); + + /* Populate the PIRQ table fields */ + rt->signature = PIRQ_SIGNATURE; + rt->version = PIRQ_VERSION; + rt->rtr_bus = 0; + rt->rtr_devfn = (TNC_LPC_DEV << 3) | TNC_LPC_FUNC; + rt->rtr_vendor = PCI_VENDOR_ID_INTEL; + rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31; + + slot = rt->slots; + + /* + * Now fill in the irq_info entries in the PIRQ table + * + * We start from internal TunnelCreek PCI devices first, then + * followed by all the 4 PCIe ports downstream devices, including + * the Queensbay platform Topcliff chipset devices. + */ + fill_irq_info(&slot, &irq_entries, 0, TNC_IGD_DEV, + TNC_IGD_FUNC, INTA, PIRQE); + fill_irq_info(&slot, &irq_entries, 0, TNC_SDVO_DEV, + TNC_SDVO_FUNC, INTA, PIRQF); + fill_irq_info(&slot, &irq_entries, 0, TNC_HDA_DEV, + TNC_HDA_FUNC, INTA, PIRQG); + fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE0_DEV, + TNC_PCIE0_FUNC, INTA, PIRQE); + fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE1_DEV, + TNC_PCIE1_FUNC, INTA, PIRQF); + fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE2_DEV, + TNC_PCIE2_FUNC, INTA, PIRQG); + fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE3_DEV, + TNC_PCIE3_FUNC, INTA, PIRQH); + + /* Check which PCIe port the Topcliff chipset is connected to */ + tcf_bdf = pci_find_device(PCI_VENDOR_ID_INTEL, 0x8800, 0); + tcf_bus = PCI_BUS(tcf_bdf); + for (i = 0; i < 4; i++) { + bus = x86_pci_read_config8(PCI_BDF(0, TNC_PCIE0_DEV + i, 0), + PCI_SECONDARY_BUS); + if (bus == tcf_bus) + break; + } + + /* Fill in the Topcliff chipset devices' irq info */ + if (i < 4) { + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_PCIE_PORT_DEV, + TCF_PCIE_PORT_FUNC, INTA, pin_swizzle(PIRQA, i)); + + tcf_bus++; + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_0, + TCF_GBE_FUNC, INTA, pin_swizzle(PIRQA, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_0, + TCF_GPIO_FUNC, INTA, pin_swizzle(PIRQA, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2, + TCF_USB1_OHCI0_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2, + TCF_USB1_OHCI1_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2, + TCF_USB1_OHCI2_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2, + TCF_USB1_EHCI_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2, + TCF_USB_DEVICE_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_4, + TCF_SDIO0_FUNC, INTC, pin_swizzle(PIRQC, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_4, + TCF_SDIO1_FUNC, INTC, pin_swizzle(PIRQC, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_6, + TCF_SATA_FUNC, INTD, pin_swizzle(PIRQD, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8, + TCF_USB2_OHCI0_FUNC, INTA, pin_swizzle(PIRQA, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8, + TCF_USB2_OHCI1_FUNC, INTA, pin_swizzle(PIRQA, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8, + TCF_USB2_OHCI2_FUNC, INTA, pin_swizzle(PIRQA, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8, + TCF_USB2_EHCI_FUNC, INTA, pin_swizzle(PIRQA, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10, + TCF_DMA1_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10, + TCF_UART0_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10, + TCF_UART1_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10, + TCF_UART2_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10, + TCF_UART3_FUNC, INTB, pin_swizzle(PIRQB, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12, + TCF_DMA2_FUNC, INTC, pin_swizzle(PIRQC, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12, + TCF_SPI_FUNC, INTC, pin_swizzle(PIRQC, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12, + TCF_I2C_FUNC, INTC, pin_swizzle(PIRQC, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12, + TCF_CAN_FUNC, INTC, pin_swizzle(PIRQC, i)); + fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12, + TCF_1588_FUNC, INTC, pin_swizzle(PIRQC, i)); + } + + /* Call board-specific routine to fill in add-in card's irq info */ + irq_entries += board_fill_irq_info(slot); + + rt->size = irq_entries * sizeof(struct irq_info) + 32; + + pirq_routing_table = rt; + + return 0; +} + +void pirq_init(void) +{ + struct tnc_rcba *rcba; + u32 base; + + base = x86_pci_read_config32(TNC_LPC, LPC_RCBA); + base &= ~MEM_BAR_EN; + rcba = (struct tnc_rcba *)base; + + /* Make sure all internal PCI devices are using INTA */ + writel(INTA, &rcba->d02ip); + writel(INTA, &rcba->d03ip); + writel(INTA, &rcba->d27ip); + writel(INTA, &rcba->d31ip); + writel(INTA, &rcba->d23ip); + writel(INTA, &rcba->d24ip); + writel(INTA, &rcba->d25ip); + writel(INTA, &rcba->d26ip); + + /* + * Route TunnelCreek PCI device interrupt pin to PIRQ + * + * Since PCIe downstream ports received INTx are routed to PIRQ + * A/B/C/D directly and not configurable, we route internal PCI + * device's INTx to PIRQ E/F/G/H. + */ + writew(PIRQE, &rcba->d02ir); + writew(PIRQF, &rcba->d03ir); + writew(PIRQG, &rcba->d27ir); + writew(PIRQH, &rcba->d31ir); + writew(PIRQE, &rcba->d23ir); + writew(PIRQF, &rcba->d24ir); + writew(PIRQG, &rcba->d25ir); + writew(PIRQH, &rcba->d26ir); + + if (create_pirq_routing_table()) { + debug("Failed to create pirq routing table\n"); + } else { + /* Route PIRQ */ + pirq_route_irqs(pirq_routing_table->slots, + get_irq_slot_count(pirq_routing_table)); + } +} + +u32 write_pirq_routing_table(u32 addr) +{ + return copy_pirq_routing_table(addr, pirq_routing_table); +} diff --git a/arch/x86/cpu/queensbay/tnc.c b/arch/x86/cpu/queensbay/tnc.c index b7236e7..b46a7e9 100644 --- a/arch/x86/cpu/queensbay/tnc.c +++ b/arch/x86/cpu/queensbay/tnc.c @@ -8,7 +8,8 @@ #include #include #include -#include +#include +#include #include #include @@ -16,9 +17,9 @@ static void unprotect_spi_flash(void) { u32 bc; - bc = x86_pci_read_config32(PCH_LPC_DEV, 0xd8); + bc = x86_pci_read_config32(TNC_LPC, 0xd8); bc |= 0x1; /* unprotect the flash */ - x86_pci_write_config32(PCH_LPC_DEV, 0xd8, bc); + x86_pci_write_config32(TNC_LPC, 0xd8, bc); } int arch_cpu_init(void) @@ -43,3 +44,10 @@ int arch_cpu_init(void) return 0; } + +int arch_misc_init(void) +{ + pirq_init(); + + return 0; +} diff --git a/arch/x86/cpu/sipi_vector.S b/arch/x86/cpu/sipi_vector.S new file mode 100644 index 0000000..bcef12c --- /dev/null +++ b/arch/x86/cpu/sipi_vector.S @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + * + * Taken from coreboot file of the same name + */ + +/* + * The SIPI vector is responsible for initializing the APs in the sytem. It + * loads microcode, sets up MSRs, and enables caching before calling into + * C code + */ + +#include +#include +#include +#include +#include + +#define CODE_SEG (X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE) +#define DATA_SEG (X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE) + +/* + * First we have the 16-bit section. Every AP process starts here. + * The simple task is to load U-Boot's Global Descriptor Table (GDT) to allow + * U-Boot's 32-bit code to become visible, then jump to ap_start. + * + * Note that this code is copied to RAM below 1MB in mp_init.c, and runs from + * there, but the 32-bit code (ap_start and onwards) is part of U-Boot and + * is therefore relocated to the top of RAM with other U-Boot code. This + * means that for the 16-bit code we must write relocatable code, but for the + * rest, we can do what we like. + */ +.text +.code16 +.globl ap_start16 +ap_start16: + cli + xorl %eax, %eax + movl %eax, %cr3 /* Invalidate TLB */ + + /* setup the data segment */ + movw %cs, %ax + movw %ax, %ds + + /* Use an address relative to the data segment for the GDT */ + movl $gdtaddr, %ebx + subl $ap_start16, %ebx + + data32 lgdt (%ebx) + + movl %cr0, %eax + andl $(~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE | \ + X86_CR0_TS | X86_CR0_EM | X86_CR0_MP)), %eax + orl $(X86_CR0_NW | X86_CR0_CD | X86_CR0_PE), %eax + movl %eax, %cr0 + + movl $ap_start_jmp, %eax + subl $ap_start16, %eax + movw %ax, %bp + + /* Jump to ap_start within U-Boot */ +data32 cs ljmp *(%bp) + + .align 4 +.globl sipi_params_16bit +sipi_params_16bit: + /* 48-bit far pointer */ +ap_start_jmp: + .long 0 /* offset set to ap_start by U-Boot */ + .word CODE_SEG /* segment */ + + .word 0 /* padding */ +gdtaddr: + .word 0 /* limit */ + .long 0 /* table */ + .word 0 /* unused */ + +.globl ap_start16_code_end +ap_start16_code_end: + +/* + * Set up the special 'fs' segment for global_data. Then jump to ap_continue + * to set up the AP. + */ +.globl ap_start +ap_start: + .code32 + movw $DATA_SEG, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %gs + + movw $(X86_GDT_ENTRY_32BIT_FS * X86_GDT_ENTRY_SIZE), %ax + movw %ax, %fs + + /* Load the Interrupt descriptor table */ + mov idt_ptr, %ebx + lidt (%ebx) + + /* Obtain cpu number */ + movl ap_count, %eax +1: + movl %eax, %ecx + inc %ecx + lock cmpxchg %ecx, ap_count + jnz 1b + + /* Setup stacks for each CPU */ + movl stack_size, %eax + mul %ecx + movl stack_top, %edx + subl %eax, %edx + mov %edx, %esp + /* Save cpu number */ + mov %ecx, %esi + + /* Determine if one should check microcode versions */ + mov microcode_ptr, %edi + test %edi, %edi + jz microcode_done /* Bypass if no microde exists */ + + /* Get the Microcode version */ + mov $1, %eax + cpuid + mov $MSR_IA32_UCODE_REV, %ecx + rdmsr + /* If something already loaded skip loading again */ + test %edx, %edx + jnz microcode_done + + /* Determine if parallel microcode loading is allowed */ + cmp $0xffffffff, microcode_lock + je load_microcode + + /* Protect microcode loading */ +lock_microcode: + lock bts $0, microcode_lock + jc lock_microcode + +load_microcode: + /* Load new microcode */ + mov $MSR_IA32_UCODE_WRITE, %ecx + xor %edx, %edx + mov %edi, %eax + /* + * The microcode pointer is passed in pointing to the header. Adjust + * pointer to reflect the payload (header size is 48 bytes) + */ + add $UCODE_HEADER_LEN, %eax + pusha + wrmsr + popa + + /* Unconditionally unlock microcode loading */ + cmp $0xffffffff, microcode_lock + je microcode_done + + xor %eax, %eax + mov %eax, microcode_lock + +microcode_done: + /* + * Load MSRs. Each entry in the table consists of: + * 0: index, + * 4: value[31:0] + * 8: value[63:32] + * See struct saved_msr in mp_init.c. + */ + mov msr_table_ptr, %edi + mov msr_count, %ebx + test %ebx, %ebx + jz 1f +load_msr: + mov (%edi), %ecx + mov 4(%edi), %eax + mov 8(%edi), %edx + wrmsr + add $12, %edi + dec %ebx + jnz load_msr + +1: + /* Enable caching */ + mov %cr0, %eax + andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax + mov %eax, %cr0 + + /* c_handler(cpu_num) */ + movl %esi, %eax /* cpu_num */ + mov c_handler, %eax + call *%eax + + .align 4 +.globl sipi_params +sipi_params: +idt_ptr: + .long 0 +stack_top: + .long 0 +stack_size: + .long 0 +microcode_lock: + .long 0 +microcode_ptr: + .long 0 +msr_table_ptr: + .long 0 +msr_count: + .long 0 +c_handler: + .long 0 +ap_count: + .long 0 diff --git a/arch/x86/dts/chromebook_link.dts b/arch/x86/dts/chromebook_link.dts index b450c3c..7c7034c 100644 --- a/arch/x86/dts/chromebook_link.dts +++ b/arch/x86/dts/chromebook_link.dts @@ -181,7 +181,7 @@ pch { reg = <0x0000f800 0 0 0 0>; - compatible = "intel,bd82x6x"; + compatible = "intel,bd82x6x", "intel,pch"; u-boot,dm-pre-reloc; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/x86/dts/crownbay.dts b/arch/x86/dts/crownbay.dts index e81054e..fbdeade 100644 --- a/arch/x86/dts/crownbay.dts +++ b/arch/x86/dts/crownbay.dts @@ -13,6 +13,10 @@ model = "Intel Crown Bay"; compatible = "intel,crownbay", "intel,queensbay"; + aliases { + spi0 = "/spi"; + }; + config { silent_console = <0>; }; @@ -46,7 +50,7 @@ spi { #address-cells = <1>; #size-cells = <0>; - compatible = "intel,ich7"; + compatible = "intel,ich-spi"; spi-flash@0 { reg = <0>; compatible = "sst,25vf016b", "spi-flash"; diff --git a/arch/x86/dts/galileo.dts b/arch/x86/dts/galileo.dts index 66af64a..60dbc5f 100644 --- a/arch/x86/dts/galileo.dts +++ b/arch/x86/dts/galileo.dts @@ -14,6 +14,10 @@ model = "Intel Galileo"; compatible = "intel,galileo", "intel,quark"; + aliases { + spi0 = "/spi"; + }; + config { silent_console = <0>; }; diff --git a/arch/x86/dts/minnowmax.dts b/arch/x86/dts/minnowmax.dts index 8f34369..7103bc5 100644 --- a/arch/x86/dts/minnowmax.dts +++ b/arch/x86/dts/minnowmax.dts @@ -15,23 +15,86 @@ aliases { serial0 = &serial; + spi0 = "/spi"; }; config { silent_console = <0>; }; + gpioa { + compatible = "intel,ich6-gpio"; + u-boot,dm-pre-reloc; + reg = <0 0x20>; + bank-name = "A"; + }; + + gpiob { + compatible = "intel,ich6-gpio"; + u-boot,dm-pre-reloc; + reg = <0x20 0x20>; + bank-name = "B"; + }; + + gpioc { + compatible = "intel,ich6-gpio"; + u-boot,dm-pre-reloc; + reg = <0x40 0x20>; + bank-name = "C"; + }; + + gpiod { + compatible = "intel,ich6-gpio"; + u-boot,dm-pre-reloc; + reg = <0x60 0x20>; + bank-name = "D"; + }; + + gpioe { + compatible = "intel,ich6-gpio"; + u-boot,dm-pre-reloc; + reg = <0x80 0x20>; + bank-name = "E"; + }; + + gpiof { + compatible = "intel,ich6-gpio"; + u-boot,dm-pre-reloc; + reg = <0xA0 0x20>; + bank-name = "F"; + }; + chosen { stdout-path = "/serial"; }; + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "intel,baytrail-cpu"; + reg = <0>; + intel,apic-id = <0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "intel,baytrail-cpu"; + reg = <1>; + intel,apic-id = <4>; + }; + + }; + spi { #address-cells = <1>; #size-cells = <0>; - compatible = "intel,ich"; + compatible = "intel,ich-spi"; spi-flash@0 { reg = <0>; - compatible = "sst,25vf016b", "spi-flash"; + compatible = "stmicro,n25q064a", "spi-flash"; memory-map = <0xff800000 0x00800000>; }; }; diff --git a/arch/x86/include/asm/arch-baytrail/gpio.h b/arch/x86/include/asm/arch-baytrail/gpio.h index ab4e059..4e8987c 100644 --- a/arch/x86/include/asm/arch-baytrail/gpio.h +++ b/arch/x86/include/asm/arch-baytrail/gpio.h @@ -8,6 +8,6 @@ #define _X86_ARCH_GPIO_H_ /* Where in config space is the register that points to the GPIO registers? */ -#define PCI_CFG_GPIOBASE 0x44 +#define PCI_CFG_GPIOBASE 0x48 #endif /* _X86_ARCH_GPIO_H_ */ diff --git a/arch/x86/include/asm/arch-ivybridge/microcode.h b/arch/x86/include/asm/arch-ivybridge/microcode.h index b868283..67f32cc 100644 --- a/arch/x86/include/asm/arch-ivybridge/microcode.h +++ b/arch/x86/include/asm/arch-ivybridge/microcode.h @@ -7,9 +7,6 @@ #ifndef __ASM_ARCH_MICROCODE_H #define __ASM_ARCH_MICROCODE_H -/* Length of the public header on Intel microcode blobs */ -#define UCODE_HEADER_LEN 0x30 - #ifndef __ASSEMBLY__ /** diff --git a/arch/x86/include/asm/arch-ivybridge/model_206ax.h b/arch/x86/include/asm/arch-ivybridge/model_206ax.h index 7b4f2e7..d2f9006 100644 --- a/arch/x86/include/asm/arch-ivybridge/model_206ax.h +++ b/arch/x86/include/asm/arch-ivybridge/model_206ax.h @@ -37,7 +37,6 @@ #define MSR_MISC_PWR_MGMT 0x1aa #define MISC_PWR_MGMT_EIST_HW_DIS (1 << 0) #define MSR_TURBO_RATIO_LIMIT 0x1ad -#define MSR_POWER_CTL 0x1fc #define MSR_PKGC3_IRTL 0x60a #define MSR_PKGC6_IRTL 0x60b @@ -63,7 +62,6 @@ #define MSR_PP1_CURRENT_CONFIG 0x602 #define PP1_CURRENT_LIMIT_SNB (35 << 3) /* 35 A */ #define PP1_CURRENT_LIMIT_IVB (50 << 3) /* 50 A */ -#define MSR_PKG_POWER_SKU_UNIT 0x606 #define MSR_PKG_POWER_SKU 0x614 #define IVB_CONFIG_TDP_MIN_CPUID 0x306a2 diff --git a/arch/x86/include/asm/arch-quark/quark.h b/arch/x86/include/asm/arch-quark/quark.h index ceb583e..6dd02fd 100644 --- a/arch/x86/include/asm/arch-quark/quark.h +++ b/arch/x86/include/asm/arch-quark/quark.h @@ -21,6 +21,13 @@ /* Port 0x03: Host Bridge Message Port Registers */ +/* Host Miscellaneous Controls 2 */ +#define HMISC2 0x03 + +#define HMISC2_SEGE 0x00000002 +#define HMISC2_SEGF 0x00000004 +#define HMISC2_SEGAB 0x00000010 + /* Host Memory I/O Boundary */ #define HM_BOUND 0x08 diff --git a/arch/x86/include/asm/arch-queensbay/device.h b/arch/x86/include/asm/arch-queensbay/device.h new file mode 100644 index 0000000..953b48f --- /dev/null +++ b/arch/x86/include/asm/arch-queensbay/device.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2015, Bin Meng + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _QUEENSBAY_DEVICE_H_ +#define _QUEENSBAY_DEVICE_H_ + +#include + +/* TunnelCreek PCI Devices */ +#define TNC_HOST_BRIDGE_DEV 0 +#define TNC_HOST_BRIDGE_FUNC 0 +#define TNC_IGD_DEV 2 +#define TNC_IGD_FUNC 0 +#define TNC_SDVO_DEV 3 +#define TNC_SDVO_FUNC 0 +#define TNC_PCIE0_DEV 23 +#define TNC_PCIE0_FUNC 0 +#define TNC_PCIE1_DEV 24 +#define TNC_PCIE1_FUNC 0 +#define TNC_PCIE2_DEV 25 +#define TNC_PCIE2_FUNC 0 +#define TNC_PCIE3_DEV 26 +#define TNC_PCIE3_FUNC 0 +#define TNC_HDA_DEV 27 +#define TNC_HDA_FUNC 0 +#define TNC_LPC_DEV 31 +#define TNC_LPC_FUNC 0 + +#define TNC_HOST_BRIDGE \ + PCI_BDF(0, TNC_HOST_BRIDGE_DEV, TNC_HOST_BRIDGE_FUNC) +#define TNC_IGD \ + PCI_BDF(0, TNC_IGD_DEV, TNC_IGD_FUNC) +#define TNC_SDVO \ + PCI_BDF(0, TNC_SDVO_DEV, TNC_SDVO_FUNC) +#define TNC_PCIE0 \ + PCI_BDF(0, TNC_PCIE0_DEV, TNC_PCIE0_FUNC) +#define TNC_PCIE1 \ + PCI_BDF(0, TNC_PCIE1_DEV, TNC_PCIE1_FUNC) +#define TNC_PCIE2 \ + PCI_BDF(0, TNC_PCIE2_DEV, TNC_PCIE2_FUNC) +#define TNC_PCIE3 \ + PCI_BDF(0, TNC_PCIE3_DEV, TNC_PCIE3_FUNC) +#define TNC_HDA \ + PCI_BDF(0, TNC_HDA_DEV, TNC_HDA_FUNC) +#define TNC_LPC \ + PCI_BDF(0, TNC_LPC_DEV, TNC_LPC_FUNC) + +/* Topcliff IOH PCI Devices */ +#define TCF_PCIE_PORT_DEV 0 +#define TCF_PCIE_PORT_FUNC 0 + +#define TCF_DEV_0 0 +#define TCF_PKT_HUB_FUNC 0 +#define TCF_GBE_FUNC 1 +#define TCF_GPIO_FUNC 2 + +#define TCF_DEV_2 2 +#define TCF_USB1_OHCI0_FUNC 0 +#define TCF_USB1_OHCI1_FUNC 1 +#define TCF_USB1_OHCI2_FUNC 2 +#define TCF_USB1_EHCI_FUNC 3 +#define TCF_USB_DEVICE_FUNC 4 + +#define TCF_DEV_4 4 +#define TCF_SDIO0_FUNC 0 +#define TCF_SDIO1_FUNC 1 + +#define TCF_DEV_6 6 +#define TCF_SATA_FUNC 0 + +#define TCF_DEV_8 8 +#define TCF_USB2_OHCI0_FUNC 0 +#define TCF_USB2_OHCI1_FUNC 1 +#define TCF_USB2_OHCI2_FUNC 2 +#define TCF_USB2_EHCI_FUNC 3 + +#define TCF_DEV_10 10 +#define TCF_DMA1_FUNC 0 +#define TCF_UART0_FUNC 1 +#define TCF_UART1_FUNC 2 +#define TCF_UART2_FUNC 3 +#define TCF_UART3_FUNC 4 + +#define TCF_DEV_12 12 +#define TCF_DMA2_FUNC 0 +#define TCF_SPI_FUNC 1 +#define TCF_I2C_FUNC 2 +#define TCF_CAN_FUNC 3 +#define TCF_1588_FUNC 4 + +#endif /* _QUEENSBAY_DEVICE_H_ */ diff --git a/arch/x86/include/asm/arch-queensbay/irq.h b/arch/x86/include/asm/arch-queensbay/irq.h new file mode 100644 index 0000000..e7f8616 --- /dev/null +++ b/arch/x86/include/asm/arch-queensbay/irq.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015, Bin Meng + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ARCH_IRQ_H_ +#define _ARCH_IRQ_H_ + +enum pci_int_pin { + INTX, + INTA, + INTB, + INTC, + INTD +}; + +enum pirq_pin { + PIRQA, + PIRQB, + PIRQC, + PIRQD, + PIRQE, + PIRQF, + PIRQG, + PIRQH +}; + +/* PIRQ link number and value conversion */ +#define LINK_V2N(link) (link - 0x60) +#define LINK_N2V(link) (link + 0x60) + +#define PIRQ_BITMAP 0xdee0 + +struct irq_info; + +/** + * board_fill_irq_info() - Board-specific irq_info fill routine + * + * This fills the irq_info table for any board-specific add-in cards. + * + * @slot: pointer to the struct irq_info that is to be filled in + * @return: number of entries were written to the struct irq_info + */ +int board_fill_irq_info(struct irq_info *slot); + +/** + * pirq_init() - Initialize platform PIRQ routing + * + * This initializes the PIRQ routing on the platform and configures all PCI + * devices' interrupt line register to a working IRQ number on the 8259 PIC. + */ +void pirq_init(void); + +#endif /* _ARCH_IRQ_H_ */ diff --git a/arch/x86/include/asm/arch-queensbay/tnc.h b/arch/x86/include/asm/arch-queensbay/tnc.h index 67c5e05..ad9a6c4 100644 --- a/arch/x86/include/asm/arch-queensbay/tnc.h +++ b/arch/x86/include/asm/arch-queensbay/tnc.h @@ -7,9 +7,43 @@ #ifndef _X86_ARCH_TNC_H_ #define _X86_ARCH_TNC_H_ -#include +/* Memory BAR Enable */ +#define MEM_BAR_EN 0x00000001 -/* PCI Configuration Space (D31:F0): LPC */ -#define PCH_LPC_DEV PCI_BDF(0, 0x1f, 0) +/* LPC PCI Configuration Registers */ +#define LPC_RCBA 0xf0 + +/* Root Complex Register Block */ +struct tnc_rcba { + u32 rctl; + u32 esd; + u32 rsvd1[2]; + u32 hdd; + u32 rsvd2; + u32 hdba; + u32 rsvd3[3129]; + u32 d31ip; + u32 rsvd4[3]; + u32 d27ip; + u32 rsvd5; + u32 d02ip; + u32 rsvd6; + u32 d26ip; + u32 d25ip; + u32 d24ip; + u32 d23ip; + u32 d03ip; + u32 rsvd7[3]; + u16 d31ir; + u16 rsvd8[3]; + u16 d27ir; + u16 d26ir; + u16 d25ir; + u16 d24ir; + u16 d23ir; + u16 rsvd9[7]; + u16 d02ir; + u16 d03ir; +}; #endif /* _X86_ARCH_TNC_H_ */ diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h new file mode 100644 index 0000000..806f787 --- /dev/null +++ b/arch/x86/include/asm/atomic.h @@ -0,0 +1,115 @@ +#ifndef _ASM_X86_ATOMIC_H +#define _ASM_X86_ATOMIC_H + +#include +#include +#include + +typedef struct { volatile int counter; } atomic_t; + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +#define ATOMIC_INIT(i) { (i) } + +/** + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. + */ +static inline int atomic_read(const atomic_t *v) +{ + return ACCESS_ONCE((v)->counter); +} + +/** + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. + */ +static inline void atomic_set(atomic_t *v, int i) +{ + v->counter = i; +} + +/** + * atomic_add - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type atomic_t + * + * Atomically adds @i to @v. + */ +static inline void atomic_add(int i, atomic_t *v) +{ + asm volatile(LOCK_PREFIX "addl %1,%0" + : "+m" (v->counter) + : "ir" (i)); +} + +/** + * atomic_sub - subtract integer from atomic variable + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v. + */ +static inline void atomic_sub(int i, atomic_t *v) +{ + asm volatile(LOCK_PREFIX "subl %1,%0" + : "+m" (v->counter) + : "ir" (i)); +} + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. + */ +static inline void atomic_inc(atomic_t *v) +{ + asm volatile(LOCK_PREFIX "incl %0" + : "+m" (v->counter)); +} + +/** + * atomic_dec - decrement atomic variable + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. + */ +static inline void atomic_dec(atomic_t *v) +{ + asm volatile(LOCK_PREFIX "decl %0" + : "+m" (v->counter)); +} + +/** + * atomic_inc_short - increment of a short integer + * @v: pointer to type int + * + * Atomically adds 1 to @v + * Returns the new value of @u + */ +static inline short int atomic_inc_short(short int *v) +{ + asm(LOCK_PREFIX "addw $1, %0" : "+m" (*v)); + return *v; +} + +/* These are x86-specific, used by some header files */ +#define atomic_clear_mask(mask, addr) \ + asm volatile(LOCK_PREFIX "andl %0,%1" \ + : : "r" (~(mask)), "m" (*(addr)) : "memory") + +#define atomic_set_mask(mask, addr) \ + asm volatile(LOCK_PREFIX "orl %0,%1" \ + : : "r" ((unsigned)(mask)), "m" (*(addr)) \ + : "memory") + +#endif /* _ASM_X86_ATOMIC_H */ diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index c839291..ebc74ad 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -151,6 +151,11 @@ static inline int flag_is_changeable_p(uint32_t flag) return ((f1^f2) & flag) != 0; } +static inline void mfence(void) +{ + __asm__ __volatile__("mfence" : : : "memory"); +} + /** * cpu_enable_paging_pae() - Enable PAE-paging * @@ -192,6 +197,20 @@ const char *cpu_vendor_name(int vendor); char *cpu_get_name(char *name); /** + * +* x86_cpu_get_desc() - Get a description string for an x86 CPU +* +* This uses cpu_get_name() and is suitable to use as the get_desc() method for +* the CPU uclass. +* +* @dev: Device to check (UCLASS_CPU) +* @buf: Buffer to place string +* @size: Size of string space +* @return 0 if OK, -ENOSPC if buffer is too small, other -ve on error +*/ +int x86_cpu_get_desc(struct udevice *dev, char *buf, int size); + +/** * cpu_call64() - Jump to a 64-bit Linux kernel (internal function) * * The kernel is uncompressed and the 64-bit entry point is expected to be diff --git a/arch/x86/include/asm/e820.h b/arch/x86/include/asm/e820.h index d155ce9..21bc633 100644 --- a/arch/x86/include/asm/e820.h +++ b/arch/x86/include/asm/e820.h @@ -1,43 +1,7 @@ #ifndef _ASM_X86_E820_H #define _ASM_X86_E820_H -#define E820MAP 0x2d0 /* our map */ -#define E820MAX 128 /* number of entries in E820MAP */ -/* - * Legacy E820 BIOS limits us to 128 (E820MAX) nodes due to the - * constrained space in the zeropage. If we have more nodes than - * that, and if we've booted off EFI firmware, then the EFI tables - * passed us from the EFI firmware can list more nodes. Size our - * internal memory map tables to have room for these additional - * nodes, based on up to three entries per node for which the - * kernel was built: MAX_NUMNODES == (1 << CONFIG_NODES_SHIFT), - * plus E820MAX, allowing space for the possible duplicate E820 - * entries that might need room in the same arrays, prior to the - * call to sanitize_e820_map() to remove duplicates. The allowance - * of three memory map entries per node is "enough" entries for - * the initial hardware platform motivating this mechanism to make - * use of additional EFI map entries. Future platforms may want - * to allow more than three entries per node or otherwise refine - * this size. - */ - -/* - * Odd: 'make headers_check' complains about numa.h if I try - * to collapse the next two #ifdef lines to a single line: - * #if defined(__KERNEL__) && defined(CONFIG_EFI) - */ -#ifdef __KERNEL__ -#ifdef CONFIG_EFI -#include -#define E820_X_MAX (E820MAX + 3 * MAX_NUMNODES) -#else /* ! CONFIG_EFI */ -#define E820_X_MAX E820MAX -#endif -#else /* ! __KERNEL__ */ -#define E820_X_MAX E820MAX -#endif - -#define E820NR 0x1e8 /* # entries in E820MAP */ +#define E820MAX 128 /* number of entries in E820MAP */ #define E820_RAM 1 #define E820_RESERVED 2 @@ -45,111 +9,18 @@ #define E820_NVS 4 #define E820_UNUSABLE 5 -/* reserved RAM used by kernel itself */ -#define E820_RESERVED_KERN 128 - #ifndef __ASSEMBLY__ #include + struct e820entry { __u64 addr; /* start of memory segment */ __u64 size; /* size of memory segment */ __u32 type; /* type of memory segment */ } __attribute__((packed)); -struct e820map { - __u32 nr_map; - struct e820entry map[E820_X_MAX]; -}; - #define ISA_START_ADDRESS 0xa0000 #define ISA_END_ADDRESS 0x100000 -#define BIOS_BEGIN 0x000a0000 -#define BIOS_END 0x00100000 - -#ifdef __KERNEL__ -/* see comment in arch/x86/kernel/e820.c */ -extern struct e820map e820; -extern struct e820map e820_saved; - -extern unsigned long pci_mem_start; -extern int e820_any_mapped(u64 start, u64 end, unsigned type); -extern int e820_all_mapped(u64 start, u64 end, unsigned type); -extern void e820_add_region(u64 start, u64 size, int type); -extern void e820_print_map(char *who); -extern int -sanitize_e820_map(struct e820entry *biosmap, int max_nr_map, u32 *pnr_map); -extern u64 e820_update_range(u64 start, u64 size, unsigned old_type, - unsigned new_type); -extern u64 e820_remove_range(u64 start, u64 size, unsigned old_type, - int checktype); -extern void update_e820(void); -extern void e820_setup_gap(void); -extern int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize, - unsigned long start_addr, unsigned long long end_addr); -struct setup_data; -extern void parse_e820_ext(struct setup_data *data, unsigned long pa_data); - -#if defined(CONFIG_X86_64) || \ - (defined(CONFIG_X86_32) && defined(CONFIG_HIBERNATION)) -extern void e820_mark_nosave_regions(unsigned long limit_pfn); -#else -static inline void e820_mark_nosave_regions(unsigned long limit_pfn) -{ -} -#endif - -#ifdef CONFIG_MEMTEST -extern void early_memtest(unsigned long start, unsigned long end); -#else -static inline void early_memtest(unsigned long start, unsigned long end) -{ -} -#endif - -extern unsigned long end_user_pfn; - -extern u64 find_e820_area(u64 start, u64 end, u64 size, u64 align); -extern u64 find_e820_area_size(u64 start, u64 *sizep, u64 align); -extern void reserve_early(u64 start, u64 end, char *name); -extern void reserve_early_overlap_ok(u64 start, u64 end, char *name); -extern void free_early(u64 start, u64 end); -extern void early_res_to_bootmem(u64 start, u64 end); -extern u64 early_reserve_e820(u64 startt, u64 sizet, u64 align); - -extern unsigned long e820_end_of_ram_pfn(void); -extern unsigned long e820_end_of_low_ram_pfn(void); -extern int e820_find_active_region(const struct e820entry *ei, - unsigned long start_pfn, - unsigned long last_pfn, - unsigned long *ei_startpfn, - unsigned long *ei_endpfn); -extern void e820_register_active_regions(int nid, unsigned long start_pfn, - unsigned long end_pfn); -extern u64 e820_hole_size(u64 start, u64 end); -extern void finish_e820_parsing(void); -extern void e820_reserve_resources(void); -extern void e820_reserve_resources_late(void); -extern void setup_memory_map(void); -extern char *default_machine_specific_memory_setup(void); - -/* - * Returns true iff the specified range [s,e) is completely contained inside - * the ISA region. - */ -/* -static inline bool is_ISA_range(u64 s, u64 e) -{ - return s >= ISA_START_ADDRESS && e <= ISA_END_ADDRESS; -} -*/ -#endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ -#ifdef __KERNEL__ -/* #include */ - -#define HIGH_MEMORY (1024*1024) -#endif /* __KERNEL__ */ - #endif /* _ASM_X86_E820_H */ diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index 5ee06eb..4d9eac6 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -68,6 +68,7 @@ struct arch_global_data { /* MRC training data to save for the next boot */ char *mrc_output; unsigned int mrc_output_len; + void *gdt; /* Global descriptor table */ }; #endif diff --git a/arch/x86/include/asm/ibmpc.h b/arch/x86/include/asm/ibmpc.h index c3b5187..678cde4 100644 --- a/arch/x86/include/asm/ibmpc.h +++ b/arch/x86/include/asm/ibmpc.h @@ -21,4 +21,7 @@ #define UART0_BASE 0x3f8 #define UART1_BASE 0x2f8 +#define UART0_IRQ 4 +#define UART1_IRQ 3 + #endif diff --git a/arch/x86/include/asm/interrupt.h b/arch/x86/include/asm/interrupt.h index 25abde7..0a75f89 100644 --- a/arch/x86/include/asm/interrupt.h +++ b/arch/x86/include/asm/interrupt.h @@ -38,4 +38,6 @@ extern char exception_stack[]; */ void configure_irq_trigger(int int_num, bool is_level_triggered); +void *x86_get_idt(void); + #endif diff --git a/arch/x86/include/asm/mp.h b/arch/x86/include/asm/mp.h new file mode 100644 index 0000000..c0930fd --- /dev/null +++ b/arch/x86/include/asm/mp.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + * + * Taken from coreboot file of the same name + */ + +#ifndef _X86_MP_H_ +#define _X86_MP_H_ + +#include + +typedef int (*mp_callback_t)(struct udevice *cpu, void *arg); + +/* + * A mp_flight_record details a sequence of calls for the APs to perform + * along with the BSP to coordinate sequencing. Each flight record either + * provides a barrier for each AP before calling the callback or the APs + * are allowed to perform the callback without waiting. Regardless, each + * record has the cpus_entered field incremented for each record. When + * the BSP observes that the cpus_entered matches the number of APs + * the bsp_call is called with bsp_arg and upon returning releases the + * barrier allowing the APs to make further progress. + * + * Note that ap_call() and bsp_call() can be NULL. In the NULL case the + * callback will just not be called. + */ +struct mp_flight_record { + atomic_t barrier; + atomic_t cpus_entered; + mp_callback_t ap_call; + void *ap_arg; + mp_callback_t bsp_call; + void *bsp_arg; +} __attribute__((aligned(ARCH_DMA_MINALIGN))); + +#define MP_FLIGHT_RECORD(barrier_, ap_func_, ap_arg_, bsp_func_, bsp_arg_) \ + { \ + .barrier = ATOMIC_INIT(barrier_), \ + .cpus_entered = ATOMIC_INIT(0), \ + .ap_call = ap_func_, \ + .ap_arg = ap_arg_, \ + .bsp_call = bsp_func_, \ + .bsp_arg = bsp_arg_, \ + } + +#define MP_FR_BLOCK_APS(ap_func, ap_arg, bsp_func, bsp_arg) \ + MP_FLIGHT_RECORD(0, ap_func, ap_arg, bsp_func, bsp_arg) + +#define MP_FR_NOBLOCK_APS(ap_func, ap_arg, bsp_func, bsp_arg) \ + MP_FLIGHT_RECORD(1, ap_func, ap_arg, bsp_func, bsp_arg) + +/* + * The mp_params structure provides the arguments to the mp subsystem + * for bringing up APs. + * + * At present this is overkill for U-Boot, but it may make it easier to add + * SMM support. + */ +struct mp_params { + int num_cpus; /* Total cpus include BSP */ + int parallel_microcode_load; + const void *microcode_pointer; + /* Flight plan for APs and BSP */ + struct mp_flight_record *flight_plan; + int num_records; +}; + +/* + * mp_init() will set up the SIPI vector and bring up the APs according to + * mp_params. Each flight record will be executed according to the plan. Note + * that the MP infrastructure uses SMM default area without saving it. It's + * up to the chipset or mainboard to either e820 reserve this area or save this + * region prior to calling mp_init() and restoring it after mp_init returns. + * + * At the time mp_init() is called the MTRR MSRs are mirrored into APs then + * caching is enabled before running the flight plan. + * + * The MP init has the following properties: + * 1. APs are brought up in parallel. + * 2. The ordering of cpu number and APIC ids is not deterministic. + * Therefore, one cannot rely on this property or the order of devices in + * the device tree unless the chipset or mainboard know the APIC ids + * a priori. + * + * mp_init() returns < 0 on error, 0 on success. + */ +int mp_init(struct mp_params *params); + +/* Probes the CPU device */ +int mp_init_cpu(struct udevice *cpu, void *unused); + +#endif /* _X86_MP_H_ */ diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 2cbb270..38dbb31 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -53,10 +53,17 @@ #define SNB_C1_AUTO_UNDEMOTE (1UL << 27) #define SNB_C3_AUTO_UNDEMOTE (1UL << 28) +#define MSR_BSEL_CR_OVERCLOCK_CONTROL 0x000000cd #define MSR_PLATFORM_INFO 0x000000ce +#define MSR_PMG_CST_CONFIG_CONTROL 0x000000e2 +#define SINGLE_PCTL (1 << 11) + #define MSR_MTRRcap 0x000000fe #define MSR_IA32_BBL_CR_CTL 0x00000119 #define MSR_IA32_BBL_CR_CTL3 0x0000011e +#define MSR_POWER_MISC 0x00000120 +#define ENABLE_ULFM_AUTOCM_MASK (1 << 2) +#define ENABLE_INDP_AUTOCM_MASK (1 << 3) #define MSR_IA32_SYSENTER_CS 0x00000174 #define MSR_IA32_SYSENTER_ESP 0x00000175 @@ -66,6 +73,7 @@ #define MSR_IA32_MCG_STATUS 0x0000017a #define MSR_IA32_MCG_CTL 0x0000017b +#define MSR_IA32_MISC_ENABLES 0x000001a0 #define MSR_OFFCORE_RSP_0 0x000001a6 #define MSR_OFFCORE_RSP_1 0x000001a7 #define MSR_NHM_TURBO_RATIO_LIMIT 0x000001ad @@ -73,6 +81,7 @@ #define MSR_LBR_SELECT 0x000001c8 #define MSR_LBR_TOS 0x000001c9 +#define MSR_POWER_CTL 0x000001fc #define MSR_LBR_NHM_FROM 0x00000680 #define MSR_LBR_NHM_TO 0x000006c0 #define MSR_LBR_CORE_FROM 0x00000040 @@ -136,7 +145,7 @@ /* Run Time Average Power Limiting (RAPL) Interface */ -#define MSR_RAPL_POWER_UNIT 0x00000606 +#define MSR_PKG_POWER_SKU_UNIT 0x00000606 #define MSR_PKG_POWER_LIMIT 0x00000610 #define MSR_PKG_ENERGY_STATUS 0x00000611 @@ -158,6 +167,16 @@ #define MSR_PP1_POLICY 0x00000642 #define MSR_CORE_C1_RES 0x00000660 +#define MSR_IACORE_RATIOS 0x0000066a +#define MSR_IACORE_TURBO_RATIOS 0x0000066c +#define MSR_IACORE_VIDS 0x0000066b +#define MSR_IACORE_TURBO_VIDS 0x0000066d +#define MSR_PKG_TURBO_CFG1 0x00000670 +#define MSR_CPU_TURBO_WKLD_CFG1 0x00000671 +#define MSR_CPU_TURBO_WKLD_CFG2 0x00000672 +#define MSR_CPU_THERM_CFG1 0x00000673 +#define MSR_CPU_THERM_CFG2 0x00000674 +#define MSR_CPU_THERM_SENS_CFG 0x00000675 #define MSR_AMD64_MC0_MASK 0xc0010044 @@ -348,6 +367,7 @@ #define MSR_THERM2_CTL_TM_SELECT (1ULL << 16) #define MSR_IA32_MISC_ENABLE 0x000001a0 +#define H_MISC_DISABLE_TURBO (1 << 6) #define MSR_IA32_TEMPERATURE_TARGET 0x000001a2 diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h index 1955a75..c480920 100644 --- a/arch/x86/include/asm/msr.h +++ b/arch/x86/include/asm/msr.h @@ -128,6 +128,34 @@ static inline void wrmsr(unsigned msr, unsigned low, unsigned high) #define wrmsrl(msr, val) \ native_write_msr((msr), (u32)((u64)(val)), (u32)((u64)(val) >> 32)) +static inline void msr_clrsetbits_64(unsigned msr, u64 clear, u64 set) +{ + u64 val; + + val = native_read_msr(msr); + val &= ~clear; + val |= set; + wrmsrl(msr, val); +} + +static inline void msr_setbits_64(unsigned msr, u64 set) +{ + u64 val; + + val = native_read_msr(msr); + val |= set; + wrmsrl(msr, val); +} + +static inline void msr_clrbits_64(unsigned msr, u64 clear) +{ + u64 val; + + val = native_read_msr(msr); + val &= ~clear; + wrmsrl(msr, val); +} + /* rdmsr with exception handling */ #define rdmsr_safe(msr, p1, p2) \ ({ \ diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h index fda4eae..3ad617c 100644 --- a/arch/x86/include/asm/mtrr.h +++ b/arch/x86/include/asm/mtrr.h @@ -34,6 +34,22 @@ /* Number of MTRRs supported */ #define MTRR_COUNT 8 +#define NUM_FIXED_MTRRS 11 +#define RANGES_PER_FIXED_MTRR 8 +#define NUM_FIXED_RANGES (NUM_FIXED_MTRRS * RANGES_PER_FIXED_MTRR) + +#define MTRR_FIX_64K_00000_MSR 0x250 +#define MTRR_FIX_16K_80000_MSR 0x258 +#define MTRR_FIX_16K_A0000_MSR 0x259 +#define MTRR_FIX_4K_C0000_MSR 0x268 +#define MTRR_FIX_4K_C8000_MSR 0x269 +#define MTRR_FIX_4K_D0000_MSR 0x26a +#define MTRR_FIX_4K_D8000_MSR 0x26b +#define MTRR_FIX_4K_E0000_MSR 0x26c +#define MTRR_FIX_4K_E8000_MSR 0x26d +#define MTRR_FIX_4K_F0000_MSR 0x26e +#define MTRR_FIX_4K_F8000_MSR 0x26f + #if !defined(__ASSEMBLER__) /** diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index a1969ed..56eaa25 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -64,6 +64,20 @@ int pci_x86_read_config(struct udevice *bus, pci_dev_t bdf, uint offset, int pci_x86_write_config(struct udevice *bus, pci_dev_t bdf, uint offset, ulong value, enum pci_size_t size); +/** + * Assign IRQ number to a PCI device + * + * This function assigns IRQ for a PCI device. If the device does not exist + * or does not require interrupts then this function has no effect. + * + * @bus: PCI bus number + * @device: PCI device number + * @func: PCI function number + * @irq: An array of IRQ numbers that are assigned to INTA through + * INTD of this PCI device. + */ +void pci_assign_irqs(int bus, int device, int func, u8 irq[4]); + #endif /* __ASSEMBLY__ */ #endif /* _PCI_I386_H_ */ diff --git a/arch/x86/include/asm/pirq_routing.h b/arch/x86/include/asm/pirq_routing.h new file mode 100644 index 0000000..ddc08e1 --- /dev/null +++ b/arch/x86/include/asm/pirq_routing.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015, Bin Meng + * + * Ported from coreboot src/arch/x86/include/arch/pirq_routing.h + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _PIRQ_ROUTING_H_ +#define _PIRQ_ROUTING_H_ + +/* + * This is the maximum number on interrupt entries that a PCI device may have. + * This is NOT the number of slots or devices in the system + * This is NOT the number of entries in the PIRQ table + * + * This tells us that in the PIRQ table, we are going to have 4 link-bitmap + * entries per PCI device which is fixed at 4: INTA, INTB, INTC, and INTD. + * + * CAUTION: If you change this, PIRQ routing will not work correctly. + */ +#define MAX_INTX_ENTRIES 4 + +#define PIRQ_SIGNATURE \ + (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) +#define PIRQ_VERSION 0x0100 + +struct __packed irq_info { + u8 bus; /* Bus number */ + u8 devfn; /* Device and function number */ + struct __packed { + u8 link; /* IRQ line ID, 0=not routed */ + u16 bitmap; /* Available IRQs */ + } irq[MAX_INTX_ENTRIES]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +}; + +struct __packed irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus; /* busno of the interrupt router */ + u8 rtr_devfn; /* devfn of the interrupt router */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor; /* Vendor ID of the interrupt router */ + u16 rtr_device; /* Device ID of the interrupt router */ + u32 miniport_data; + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[CONFIG_IRQ_SLOT_COUNT]; +}; + +/** + * get_irq_slot_count() - Get the number of entries in the irq_info table + * + * This calculates the number of entries for the irq_info table. + * + * @rt: pointer to the base address of the struct irq_info + * @return: number of entries + */ +static inline int get_irq_slot_count(struct irq_routing_table *rt) +{ + return (rt->size - 32) / sizeof(struct irq_info); +} + +/** + * pirq_check_irq_routed() - Check whether an IRQ is routed to 8259 PIC + * + * This function checks whether an IRQ is routed to 8259 PIC for a given link. + * + * Note: this function should be provided by the platform codes, as the + * implementation of interrupt router may be different. + * + * @link: link number which represents a PIRQ + * @irq: the 8259 IRQ number + * @return: true if the irq is already routed to 8259 for a given link, + * false elsewise + */ +bool pirq_check_irq_routed(int link, u8 irq); + +/** + * pirq_translate_link() - Translate a link value + * + * This function translates a platform-specific link value to a link number. + * On Intel platforms, the link value is normally a offset into the PCI + * configuration space into the legacy bridge. + * + * Note: this function should be provided by the platform codes, as the + * implementation of interrupt router may be different. + * + * @link: platform-specific link value + * @return: link number which represents a PIRQ + */ +int pirq_translate_link(int link); + +/** + * pirq_assign_irq() - Assign an IRQ to a PIRQ link + * + * This function assigns the IRQ to a PIRQ link so that the PIRQ is routed to + * the 8259 PIC. + * + * Note: this function should be provided by the platform codes, as the + * implementation of interrupt router may be different. + * + * @link: link number which represents a PIRQ + * @irq: IRQ to which the PIRQ is routed + */ +void pirq_assign_irq(int link, u8 irq); + +/** + * pirq_route_irqs() - Route PIRQs to 8259 PIC + * + * This function configures all PCI devices' interrupt pins and maps them to + * PIRQs and finally 8259 PIC. The routed irq number is written to interrupt + * line register in the configuration space of the PCI device for OS to use. + * The configuration source is taken from a struct irq_info table, the format + * of which is defined in PIRQ routing table spec and PCI BIOS spec. + * + * @irq: pointer to the base address of the struct irq_info + * @num: number of entries in the struct irq_info + */ +void pirq_route_irqs(struct irq_info *irq, int num); + +/** + * copy_pirq_routing_table() - Copy a PIRQ routing table + * + * This helper function copies the given PIRQ routing table to a given address. + * Before copying, it does several sanity tests against the PIRQ routing table. + * It also fixes up the table checksum and align the given address to a 16 byte + * boundary to meet the PIRQ routing table spec requirements. + * + * @addr: address to store the copied PIRQ routing table + * @rt: pointer to the PIRQ routing table to copy from + * @return: end address of the copied PIRQ routing table + */ +u32 copy_pirq_routing_table(u32 addr, struct irq_routing_table *rt); + +#endif /* _PIRQ_ROUTING_H_ */ diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 3e26202..7c77b90 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -23,10 +23,32 @@ #define X86_GDT_SIZE (X86_GDT_NUM_ENTRIES * X86_GDT_ENTRY_SIZE) +/* Length of the public header on Intel microcode blobs */ +#define UCODE_HEADER_LEN 0x30 + #ifndef __ASSEMBLY__ +/* + * This register is documented in (for example) the Intel Atom Processor E3800 + * Product Family Datasheet in "PCU - Power Management Controller (PMC)". + * + * RST_CNT: Reset Control Register (RST_CNT) Offset cf9. + * + * The naming follows Intel's naming. + */ #define PORT_RESET 0xcf9 +enum { + SYS_RST = 1 << 1, /* 0 for soft reset, 1 for hard reset */ + RST_CPU = 1 << 2, /* initiate reset */ + FULL_RST = 1 << 3, /* full power cycle */ +}; + +/** + * x86_full_reset() - reset everything: perform a full power cycle + */ +void x86_full_reset(void); + static inline __attribute__((always_inline)) void cpu_hlt(void) { asm("hlt"); diff --git a/arch/x86/include/asm/sfi.h b/arch/x86/include/asm/sfi.h new file mode 100644 index 0000000..d1f0f0c --- /dev/null +++ b/arch/x86/include/asm/sfi.h @@ -0,0 +1,137 @@ +/* + * Copyright(c) 2009 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause + */ + +#ifndef _LINUX_SFI_H +#define _LINUX_SFI_H + +#include +#include + +/* Table signatures reserved by the SFI specification */ +#define SFI_SIG_SYST "SYST" +#define SFI_SIG_FREQ "FREQ" +#define SFI_SIG_CPUS "CPUS" +#define SFI_SIG_MTMR "MTMR" +#define SFI_SIG_MRTC "MRTC" +#define SFI_SIG_MMAP "MMAP" +#define SFI_SIG_APIC "APIC" +#define SFI_SIG_XSDT "XSDT" +#define SFI_SIG_WAKE "WAKE" +#define SFI_SIG_DEVS "DEVS" +#define SFI_SIG_GPIO "GPIO" + +#define SFI_SIGNATURE_SIZE 4 +#define SFI_OEM_ID_SIZE 6 +#define SFI_OEM_TABLE_ID_SIZE 8 + +#define SFI_NAME_LEN 16 +#define SFI_TABLE_MAX_ENTRIES 16 + +#define SFI_GET_NUM_ENTRIES(ptable, entry_type) \ + ((ptable->header.len - sizeof(struct sfi_table_header)) / \ + (sizeof(entry_type))) +/* + * Table structures must be byte-packed to match the SFI specification, + * as they are provided by the BIOS. + */ +struct __packed sfi_table_header { + char sig[SFI_SIGNATURE_SIZE]; + u32 len; + u8 rev; + u8 csum; + char oem_id[SFI_OEM_ID_SIZE]; + char oem_table_id[SFI_OEM_TABLE_ID_SIZE]; +}; + +struct __packed sfi_table_simple { + struct sfi_table_header header; + u64 pentry[1]; +}; + +/* Comply with UEFI spec 2.1 */ +struct __packed sfi_mem_entry { + u32 type; + u64 phys_start; + u64 virt_start; + u64 pages; + u64 attrib; +}; + +struct __packed sfi_cpu_table_entry { + u32 apic_id; +}; + +struct __packed sfi_cstate_table_entry { + u32 hint; /* MWAIT hint */ + u32 latency; /* latency in ms */ +}; + +struct __packed sfi_apic_table_entry { + u64 phys_addr; /* phy base addr for APIC reg */ +}; + +struct __packed sfi_freq_table_entry { + u32 freq_mhz; /* in MHZ */ + u32 latency; /* transition latency in ms */ + u32 ctrl_val; /* value to write to PERF_CTL */ +}; + +struct __packed sfi_wake_table_entry { + u64 phys_addr; /* pointer to where the wake vector locates */ +}; + +struct __packed sfi_timer_table_entry { + u64 phys_addr; /* phy base addr for the timer */ + u32 freq_hz; /* in HZ */ + u32 irq; +}; + +struct __packed sfi_rtc_table_entry { + u64 phys_addr; /* phy base addr for the RTC */ + u32 irq; +}; + +struct __packed sfi_device_table_entry { + u8 type; /* bus type, I2C, SPI or ...*/ + u8 host_num; /* attached to host 0, 1...*/ + u16 addr; + u8 irq; + u32 max_freq; + char name[SFI_NAME_LEN]; +}; + +enum { + SFI_DEV_TYPE_SPI = 0, + SFI_DEV_TYPE_I2C, + SFI_DEV_TYPE_UART, + SFI_DEV_TYPE_HSI, + SFI_DEV_TYPE_IPC, + SFI_DEV_TYPE_SD, +}; + +struct __packed sfi_gpio_table_entry { + char controller_name[SFI_NAME_LEN]; + u16 pin_no; + char pin_name[SFI_NAME_LEN]; +}; + +struct sfi_xsdt_header { + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; +}; + +typedef int (*sfi_table_handler) (struct sfi_table_header *table); + +/** + * write_sfi_table() - Write Simple Firmware Interface tables + * + * @base: Address to write table to + * @return address to use for the next table + */ +u32 write_sfi_table(u32 base); + +#endif /*_LINUX_SFI_H */ diff --git a/arch/x86/include/asm/sipi.h b/arch/x86/include/asm/sipi.h new file mode 100644 index 0000000..25d7d31 --- /dev/null +++ b/arch/x86/include/asm/sipi.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015 Gooogle, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _ASM_SIPI_H +#define _ASM_SIPI_H + +#define AP_DEFAULT_BASE 0x30000 +#define AP_DEFAULT_SIZE 0x10000 + +#ifndef __ASSEMBLER__ + +/** + * struct sipi_params_16bit - 16-bit SIPI entry-point parameters + * + * These are set up in the same space as the SIPI 16-bit code so that each AP + * can access the parameters when it boots. + * + * Each of these must be set up for the AP to boot, except @segment which is + * set in the assembly code. + * + * @ap_start: 32-bit SIPI entry point for U-Boot + * @segment: Code segment for U-Boot + * @pad: Padding (not used) + * @gdt_limit: U-Boot GDT limit (X86_GDT_SIZE - 1) + * @gdt: U-Boot GDT (gd->arch.gdt) + * @unused: Not used + */ +struct __packed sipi_params_16bit { + u32 ap_start; + u16 segment; + u16 pad; + u16 gdt_limit; + u32 gdt; + u16 unused; +}; + +/** + * struct sipi_params - 32-bit SIP entry-point parameters + * + * These are used by the AP init code and must be set up before the APs start. + * + * The stack area extends down from @stack_top, with @stack_size allocated + * for each AP. + * + * @idt_ptr: Interrupt descriptor table pointer + * @stack_top: Top of the AP stack area + * @stack_size: Size of each AP's stack + * @microcode_lock: Used to ensure only one AP loads microcode at once + * 0xffffffff enables parallel loading. + * @microcode_ptr: Pointer to microcode, or 0 if none + * @msr_table_ptr: Pointer to saved MSRs, a list of struct saved_msr + * @msr_count: Number of saved MSRs + * @c_handler: C function to call once early init is complete + * @ap_count: Shared atomic value to allocate CPU indexes + */ +struct sipi_params { + u32 idt_ptr; + u32 stack_top; + u32 stack_size; + u32 microcode_lock; + u32 microcode_ptr; + u32 msr_table_ptr; + u32 msr_count; + u32 c_handler; + atomic_t ap_count; +}; + +/* 16-bit AP entry point */ +void ap_start16(void); + +/* end of 16-bit code/data, marks the region to be copied to SIP vector */ +void ap_start16_code_end(void); + +/* 32-bit AP entry point */ +void ap_start(void); + +extern char sipi_params_16bit[]; +extern char sipi_params[]; + +#endif /* __ASSEMBLER__ */ + +#endif diff --git a/arch/x86/include/asm/tables.h b/arch/x86/include/asm/tables.h new file mode 100644 index 0000000..8146ba3 --- /dev/null +++ b/arch/x86/include/asm/tables.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015, Bin Meng + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _X86_TABLES_H_ +#define _X86_TABLES_H_ + +/* + * All x86 tables happen to like the address range from 0xf0000 to 0x100000. + * We use 0xf0000 as the starting address to store those tables, including + * PIRQ routing table, Multi-Processor table and ACPI table. + */ +#define ROM_TABLE_ADDR 0xf0000 + +/** + * table_compute_checksum() - Compute a table checksum + * + * This computes an 8-bit checksum for the configuration table. + * All bytes in the configuration table, including checksum itself and + * reserved bytes must add up to zero. + * + * @v: configuration table base address + * @len: configuration table size + * @return: the 8-bit checksum + */ +u8 table_compute_checksum(void *v, int len); + +/** + * write_tables() - Write x86 configuration tables + * + * This writes x86 configuration tables, including PIRQ routing table, + * Multi-Processor table and ACPI table. Whether a specific type of + * configuration table is written is controlled by a Kconfig option. + */ +void write_tables(void); + +/** + * write_pirq_routing_table() - Write PIRQ routing table + * + * This writes PIRQ routing table at a given address. + * + * @start: start address to write PIRQ routing table + * @return: end address of PIRQ routing table + */ +u32 write_pirq_routing_table(u32 start); + +#endif /* _X86_TABLES_H_ */ diff --git a/arch/x86/include/asm/turbo.h b/arch/x86/include/asm/turbo.h index bb0d4b4..21b910b 100644 --- a/arch/x86/include/asm/turbo.h +++ b/arch/x86/include/asm/turbo.h @@ -12,9 +12,6 @@ #define CPUID_LEAF_PM 6 #define PM_CAP_TURBO_MODE (1 << 1) -#define MSR_IA32_MISC_ENABLES 0x1a0 -#define H_MISC_DISABLE_TURBO (1 << 6) - enum { TURBO_UNKNOWN, TURBO_UNAVAILABLE, diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h index c743efd..be103c0 100644 --- a/arch/x86/include/asm/u-boot-x86.h +++ b/arch/x86/include/asm/u-boot-x86.h @@ -53,6 +53,8 @@ int video_bios_init(void); void board_init_f_r_trampoline(ulong) __attribute__ ((noreturn)); void board_init_f_r(void) __attribute__ ((noreturn)); +int arch_misc_init(void); + /* Read the time stamp counter */ static inline __attribute__((no_instrument_function)) uint64_t rdtsc(void) { @@ -67,6 +69,8 @@ uint64_t timer_get_tsc(void); void quick_ram_check(void); +int x86_init_cpus(void); + #define PCI_VGA_RAM_IMAGE_START 0xc0000 #endif /* _U_BOOT_I386_H_ */ diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index 6c571dd..70ad19b 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -22,12 +22,14 @@ ifndef CONFIG_DM_PCI obj-$(CONFIG_PCI) += pci_type1.o endif obj-y += pch-uclass.o +obj-y += pirq_routing.o obj-y += relocate.o obj-y += physmem.o obj-$(CONFIG_X86_RAMTEST) += ramtest.o +obj-y += sfi.o obj-y += string.o +obj-y += tables.o obj-$(CONFIG_SYS_X86_TSC_TIMER) += tsc_timer.o -obj-$(CONFIG_VIDEO_VGA) += video.o obj-$(CONFIG_CMD_ZBOOT) += zimage.o obj-$(CONFIG_HAVE_FSP) += fsp/ diff --git a/arch/x86/lib/fsp/fsp_common.c b/arch/x86/lib/fsp/fsp_common.c index f668259..001494d 100644 --- a/arch/x86/lib/fsp/fsp_common.c +++ b/arch/x86/lib/fsp/fsp_common.c @@ -17,13 +17,6 @@ int print_cpuinfo(void) return default_print_cpuinfo(); } -void reset_cpu(ulong addr) -{ - /* cold reset */ - outb(0x06, PORT_RESET); -} - - int board_pci_post_scan(struct pci_controller *hose) { u32 status; diff --git a/arch/x86/lib/pirq_routing.c b/arch/x86/lib/pirq_routing.c new file mode 100644 index 0000000..7a34dcf --- /dev/null +++ b/arch/x86/lib/pirq_routing.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015, Bin Meng + * + * Part of this file is ported from coreboot src/arch/x86/boot/pirq_routing.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static bool irq_already_routed[16]; + +static u8 pirq_get_next_free_irq(u8 *pirq, u16 bitmap) +{ + 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(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 irq_info *irq, int num) +{ + unsigned char irq_slot[MAX_INTX_ENTRIES]; + unsigned char pirq[CONFIG_MAX_PIRQ_LINKS]; + int i, intx; + + memset(pirq, 0, CONFIG_MAX_PIRQ_LINKS); + + /* 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(link); + + /* yet not routed */ + if (!pirq[link]) { + irq = pirq_get_next_free_irq(pirq, bitmap); + 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(link, irq); + } + + /* Bus, device, slots IRQs for {A,B,C,D} */ + pci_assign_irqs(irq->bus, irq->devfn >> 3, irq->devfn & 7, + 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; + + /* Fix up the table checksum */ + rt->checksum = table_compute_checksum(rt, rt->size); + + /* Align the table to be 16 byte aligned */ + addr = ALIGN(addr, 16); + + debug("Copying Interrupt Routing Table to 0x%x\n", addr); + memcpy((void *)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 *)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; +} diff --git a/arch/x86/lib/sfi.c b/arch/x86/lib/sfi.c new file mode 100644 index 0000000..3d36580 --- /dev/null +++ b/arch/x86/lib/sfi.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Intel Simple Firmware Interface (SFI) + * + * Yet another way to pass information to the Linux kernel. + * + * See https://simplefirmware.org/ for details + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 *)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 *)(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; +} + +u32 write_sfi_table(u32 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; +} diff --git a/arch/x86/lib/tables.c b/arch/x86/lib/tables.c new file mode 100644 index 0000000..8031201 --- /dev/null +++ b/arch/x86/lib/tables.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015, Bin Meng + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include + +u8 table_compute_checksum(void *v, int len) +{ + u8 *bytes = v; + u8 checksum = 0; + int i; + + for (i = 0; i < len; i++) + checksum -= bytes[i]; + + return checksum; +} + +void write_tables(void) +{ + u32 __maybe_unused rom_table_end = ROM_TABLE_ADDR; + +#ifdef CONFIG_GENERATE_PIRQ_TABLE + rom_table_end = write_pirq_routing_table(rom_table_end); + rom_table_end = ALIGN(rom_table_end, 1024); +#endif +#ifdef CONFIG_GENERATE_SFI_TABLE + rom_table_end = write_sfi_table(rom_table_end); + rom_table_end = ALIGN(rom_table_end, 1024); +#endif +} diff --git a/arch/x86/lib/video.c b/arch/x86/lib/video.c deleted file mode 100644 index 975949d..0000000 --- a/arch/x86/lib/video.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * (C) Copyright 2002 - * Daniel Engström, Omicron Ceti AB, - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include - -/* basic textmode I/O from linux kernel */ -static char *vidmem = (char *)0xb8000; -static int vidport; -static int lines, cols; -static int orig_x, orig_y; - -static void beep(int dur) -{ - int i; - - outb_p(3, 0x61); - for (i = 0; i < 10*dur; i++) - udelay(1000); - - outb_p(0, 0x61); -} - -static void scroll(void) -{ - int i; - - memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); - for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) - vidmem[i] = ' '; -} - -static void __video_putc(const char c, int *x, int *y) -{ - if (c == '\n') { - (*x) = 0; - if (++(*y) >= lines) { - scroll(); - (*y)--; - } - } else if (c == '\b') { - if ((*x) != 0) { - --(*x); - vidmem[((*x) + cols * (*y)) * 2] = ' '; - } - } else if (c == '\r') { - (*x) = 0; - - } else if (c == '\a') { - beep(3); - - } else if (c == '\t') { - __video_putc(' ', x, y); - __video_putc(' ', x, y); - __video_putc(' ', x, y); - __video_putc(' ', x, y); - __video_putc(' ', x, y); - __video_putc(' ', x, y); - __video_putc(' ', x, y); - __video_putc(' ', x, y); - } else if (c == '\v') { - switch ((*x) % 8) { - case 0: - __video_putc(' ', x, y); - case 7: - __video_putc(' ', x, y); - case 6: - __video_putc(' ', x, y); - case 5: - __video_putc(' ', x, y); - case 4: - __video_putc(' ', x, y); - case 3: - __video_putc(' ', x, y); - case 2: - __video_putc(' ', x, y); - case 1: - __video_putc(' ', x, y); - } - } else if (c == '\f') { - int i; - for (i = 0; i < lines * cols * 2; i += 2) - vidmem[i] = 0; - (*x) = 0; - (*y) = 0; - } else { - vidmem[((*x) + cols * (*y)) * 2] = c; - if (++(*x) >= cols) { - (*x) = 0; - if (++(*y) >= lines) { - scroll(); - (*y)--; - } - } - } -} - -static void video_putc(struct stdio_dev *dev, const char c) -{ - int x, y, pos; - - x = orig_x; - y = orig_y; - - __video_putc(c, &x, &y); - - orig_x = x; - orig_y = y; - - pos = (x + cols * y) * 2; /* Update cursor position */ - outb_p(14, vidport); - outb_p(0xff & (pos >> 9), vidport+1); - outb_p(15, vidport); - outb_p(0xff & (pos >> 1), vidport+1); -} - -static void video_puts(struct stdio_dev *dev, const char *s) -{ - int x, y, pos; - char c; - - x = orig_x; - y = orig_y; - - while ((c = *s++) != '\0') - __video_putc(c, &x, &y); - - orig_x = x; - orig_y = y; - - pos = (x + cols * y) * 2; /* Update cursor position */ - outb_p(14, vidport); - outb_p(0xff & (pos >> 9), vidport+1); - outb_p(15, vidport); - outb_p(0xff & (pos >> 1), vidport+1); -} - -int video_init(void) -{ - u16 pos; - - static struct stdio_dev vga_dev; - static struct stdio_dev kbd_dev; - - vidmem = (char *) 0xb8000; - vidport = 0x3d4; - - lines = 25; - cols = 80; - - outb_p(14, vidport); - pos = inb_p(vidport+1); - pos <<= 8; - outb_p(15, vidport); - pos |= inb_p(vidport+1); - - orig_x = pos%cols; - orig_y = pos/cols; - -#if 0 - printf("pos %x %d %d\n", pos, orig_x, orig_y); -#endif - if (orig_y > lines) - orig_x = orig_y = 0; - - memset(&vga_dev, 0, sizeof(vga_dev)); - strcpy(vga_dev.name, "vga"); - vga_dev.ext = 0; - vga_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_SYSTEM; - vga_dev.putc = video_putc; /* 'putc' function */ - vga_dev.puts = video_puts; /* 'puts' function */ - - if (stdio_register(&vga_dev) == 0) - return 1; - - if (i8042_kbd_init()) - return 1; - - memset(&kbd_dev, 0, sizeof(kbd_dev)); - strcpy(kbd_dev.name, "kbd"); - kbd_dev.ext = 0; - kbd_dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; - kbd_dev.tstc = i8042_tstc; /* 'tstc' function */ - kbd_dev.getc = i8042_getc; /* 'getc' function */ - - if (stdio_register(&kbd_dev) == 0) - return 1; - - return 0; -} - - -int drv_video_init(void) -{ - return video_init(); -} diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c index 566b048..c3f8a73 100644 --- a/arch/x86/lib/zimage.c +++ b/arch/x86/lib/zimage.c @@ -25,6 +25,8 @@ #endif #include +DECLARE_GLOBAL_DATA_PTR; + /* * Memory lay-out: * @@ -40,16 +42,29 @@ #define COMMAND_LINE_SIZE 2048 -unsigned generic_install_e820_map(unsigned max_entries, - struct e820entry *entries) +/* + * Install a default e820 table with 3 entries as follows: + * + * 0x000000-0x0a0000 Useable RAM + * 0x0a0000-0x100000 Reserved for ISA + * 0x100000-gd->ram_size Useable RAM + */ +__weak unsigned install_e820_map(unsigned max_entries, + struct e820entry *entries) { - return 0; + 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; + + return 3; } -unsigned install_e820_map(unsigned max_entries, - struct e820entry *entries) - __attribute__((weak, alias("generic_install_e820_map"))); - static void build_command_line(char *command_line, int auto_boot) { char *env_command_line; diff --git a/board/coreboot/Kconfig b/board/coreboot/Kconfig new file mode 100644 index 0000000..dc9b70f --- /dev/null +++ b/board/coreboot/Kconfig @@ -0,0 +1,26 @@ +# +# Copyright (C) 2015, Bin Meng +# +# SPDX-License-Identifier: GPL-2.0+ +# + +if VENDOR_COREBOOT + +choice + prompt "Mainboard model" + +config TARGET_COREBOOT + bool "coreboot" + help + This target is used for running U-Boot on top of coreboot. In + this case coreboot does the early inititalisation, and U-Boot + takes over once the RAM, video and CPU are fully running. + U-Boot is loaded as a fallback payload from coreboot, in + coreboot terminology. This method was used for the Chromebook + Pixel when launched. + +endchoice + +source "board/coreboot/coreboot/Kconfig" + +endif diff --git a/board/coreboot/coreboot/Kconfig b/board/coreboot/coreboot/Kconfig index 981de1f..6a04158 100644 --- a/board/coreboot/coreboot/Kconfig +++ b/board/coreboot/coreboot/Kconfig @@ -9,6 +9,9 @@ config SYS_VENDOR config SYS_SOC default "coreboot" +config SYS_TEXT_BASE + default 0x01110000 + comment "coreboot-specific options" config SYS_CONFIG_NAME diff --git a/board/google/Kconfig b/board/google/Kconfig new file mode 100644 index 0000000..302f68e --- /dev/null +++ b/board/google/Kconfig @@ -0,0 +1,43 @@ +# +# Copyright (C) 2015, Bin Meng +# +# SPDX-License-Identifier: GPL-2.0+ +# + +if VENDOR_GOOGLE + +choice + prompt "Mainboard model" + +config TARGET_CHROMEBOOK_LINK + bool "Chromebook link" + help + This is the Chromebook Pixel released in 2013. It uses an Intel + i5 Ivybridge which is a die-shrink of Sandybridge, with 4GB of + SDRAM. It has a Panther Point platform controller hub, PCIe + WiFi and Bluetooth. It also includes a 720p webcam, USB SD + reader, microphone and speakers, display port and 32GB SATA + solid state drive. There is a Chrome OS EC connected on LPC, + and it provides a 2560x1700 high resolution touch-enabled LCD + display. + +config TARGET_CHROMEBOX_PANTHER + bool "Chromebox panther (not available)" + select n + help + Note: At present this must be used with coreboot. See README.x86 + for instructions. + + This is the Asus Chromebox CN60 released in 2014. It uses an Intel + Haswell Celeron 2955U Dual Core CPU with 2GB of SDRAM. It has a + Lynx Point platform controller hub, PCIe WiFi and Bluetooth. It also + includes a USB SD reader, four USB3 ports, display port and HDMI + video output and a 16GB SATA solid state drive. There is no Chrome + OS EC on this model. + +endchoice + +source "board/google/chromebook_link/Kconfig" +source "board/google/chromebox_panther/Kconfig" + +endif diff --git a/board/google/chromebook_link/Kconfig b/board/google/chromebook_link/Kconfig index ea45472..9c8d020 100644 --- a/board/google/chromebook_link/Kconfig +++ b/board/google/chromebook_link/Kconfig @@ -12,6 +12,9 @@ config SYS_SOC config SYS_CONFIG_NAME default "chromebook_link" +config SYS_TEXT_BASE + default 0xfff00000 + config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select X86_RESET_VECTOR diff --git a/board/google/chromebox_panther/Kconfig b/board/google/chromebox_panther/Kconfig index 11df55a..e3604eb 100644 --- a/board/google/chromebox_panther/Kconfig +++ b/board/google/chromebox_panther/Kconfig @@ -12,6 +12,9 @@ config SYS_SOC config SYS_CONFIG_NAME default "chromebox_panther" +config SYS_TEXT_BASE + default 0xfff00000 + # Panther actually uses haswell, not ivybridge, so this is just a placeholder config BOARD_SPECIFIC_OPTIONS # dummy def_bool y diff --git a/board/intel/Kconfig b/board/intel/Kconfig new file mode 100644 index 0000000..7fe21b9 --- /dev/null +++ b/board/intel/Kconfig @@ -0,0 +1,51 @@ +# +# Copyright (C) 2015, Bin Meng +# +# SPDX-License-Identifier: GPL-2.0+ +# + +if VENDOR_INTEL + +choice + prompt "Mainboard model" + +config TARGET_CROWNBAY + bool "Crown Bay" + help + This is the Intel Crown Bay Customer Reference Board. It contains + the Intel Atom Processor E6xx populated on the COM Express module + with 1GB DDR2 soldered down memory and a carrier board with the + Intel Platform Controller Hub EG20T, other system components and + peripheral connectors for PCIe/SATA/USB/LAN/SD/UART/Audio/LVDS. + +config TARGET_GALILEO + bool "Galileo" + help + This is the Intel Galileo board, which is the first in a family of + Arduino-certified development and prototyping boards based on Intel + architecture. It includes an Intel Quark SoC X1000 processor, a 32-bit + single-core, single-thread, Intel Pentium processor instrunction set + architecture (ISA) compatible, operating at speeds up to 400Mhz, + along with 256MB DDR3 memory. It supports a wide range of industry + standard I/O interfaces, including a full-sized mini-PCIe slot, + one 100Mb Ethernet port, a microSD card slot, a USB host port and + a USB client port. + +config TARGET_MINNOWMAX + bool "Minnowboard MAX" + help + This is the Intel Minnowboard MAX. It contains an Atom E3800 + processor in a small form factor with Ethernet, micro-SD, USB 2, + USB 3, SATA, serial console, some GPIOs and HDMI 1.3 video out. + It requires some binary blobs - see README.x86 for details. + + Note that PCIE_ECAM_BASE is set up by the FSP so the value used + by U-Boot matches that value. + +endchoice + +source "board/intel/crownbay/Kconfig" +source "board/intel/galileo/Kconfig" +source "board/intel/minnowmax/Kconfig" + +endif diff --git a/board/intel/crownbay/Kconfig b/board/intel/crownbay/Kconfig index 762663a..b30701a 100644 --- a/board/intel/crownbay/Kconfig +++ b/board/intel/crownbay/Kconfig @@ -12,6 +12,9 @@ config SYS_SOC config SYS_CONFIG_NAME default "crownbay" +config SYS_TEXT_BASE + default 0xfff00000 + config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select X86_RESET_VECTOR diff --git a/board/intel/crownbay/crownbay.c b/board/intel/crownbay/crownbay.c index 2a254ef..31bb320 100644 --- a/board/intel/crownbay/crownbay.c +++ b/board/intel/crownbay/crownbay.c @@ -16,7 +16,7 @@ DECLARE_GLOBAL_DATA_PTR; int board_early_init_f(void) { - lpc47m_enable_serial(SERIAL_DEV, UART0_BASE); + lpc47m_enable_serial(SERIAL_DEV, UART0_BASE, UART0_IRQ); return 0; } diff --git a/board/intel/galileo/Kconfig b/board/intel/galileo/Kconfig index 85afbbc..6515bac 100644 --- a/board/intel/galileo/Kconfig +++ b/board/intel/galileo/Kconfig @@ -12,6 +12,9 @@ config SYS_SOC config SYS_CONFIG_NAME default "galileo" +config SYS_TEXT_BASE + default 0xfff10000 + config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select X86_RESET_VECTOR diff --git a/board/intel/minnowmax/Kconfig b/board/intel/minnowmax/Kconfig index 43c50a5..f2a0b71 100644 --- a/board/intel/minnowmax/Kconfig +++ b/board/intel/minnowmax/Kconfig @@ -12,6 +12,9 @@ config SYS_SOC config SYS_CONFIG_NAME default "minnowmax" +config SYS_TEXT_BASE + default 0xfff00000 + config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select X86_RESET_VECTOR diff --git a/board/intel/minnowmax/minnowmax.c b/board/intel/minnowmax/minnowmax.c index 6e82b16..fd2070a 100644 --- a/board/intel/minnowmax/minnowmax.c +++ b/board/intel/minnowmax/minnowmax.c @@ -16,7 +16,7 @@ DECLARE_GLOBAL_DATA_PTR; int board_early_init_f(void) { - lpc47m_enable_serial(SERIAL_DEV, UART0_BASE); + lpc47m_enable_serial(SERIAL_DEV, UART0_BASE, UART0_IRQ); return 0; } diff --git a/common/Kconfig b/common/Kconfig index 5d7e48a..15759f7 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -31,6 +31,14 @@ config CMD_CONSOLE help Print console devices and information. +config CMD_CPU + bool "cpu" + help + Print information about available CPUs. This normally shows the + number of CPUs, type (e.g. manufacturer, architecture, product or + internal name) and clock frequency. Other information may be + available depending on the CPU driver. + config CMD_LICENSE bool "license" help diff --git a/common/Makefile b/common/Makefile index fba3830..9084c73 100644 --- a/common/Makefile +++ b/common/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_CMD_CBFS) += cmd_cbfs.o obj-$(CONFIG_CMD_CLK) += cmd_clk.o obj-$(CONFIG_CMD_CONSOLE) += cmd_console.o obj-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o +obj-$(CONFIG_CMD_CPU) += cmd_cpu.o obj-$(CONFIG_DATAFLASH_MMC_SELECT) += cmd_dataflash_mmc_mux.o obj-$(CONFIG_CMD_DATE) += cmd_date.o obj-$(CONFIG_CMD_DEMO) += cmd_demo.o diff --git a/common/board_f.c b/common/board_f.c index 322e070..fbbad1b 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -73,7 +73,7 @@ DECLARE_GLOBAL_DATA_PTR; #endif /* - * sjg: IMO this code should be + * TODO(sjg@chromium.org): IMO this code should be * refactored to a single function, something like: * * void led_set_state(enum led_colour_t colour, int on); @@ -300,7 +300,7 @@ __weak ulong board_get_usable_ram_top(ulong total_size) { #ifdef CONFIG_SYS_SDRAM_BASE /* - * Detect whether we have so much RAM it goes past the end of our + * Detect whether we have so much RAM that it goes past the end of our * 32-bit address space. If so, clip the usable RAM so it doesn't. */ if (gd->ram_top < CONFIG_SYS_SDRAM_BASE) @@ -507,7 +507,7 @@ static int reserve_global_data(void) static int reserve_fdt(void) { /* - * If the device tree is sitting immediate above our image then we + * If the device tree is sitting immediately above our image then we * must relocate it. If it is embedded in the data section, then it * will be relocated with other data. */ @@ -535,7 +535,7 @@ static int reserve_stacks(void) gd->start_addr_sp &= ~0xf; /* - * let the architecture specific code tailor gd->start_addr_sp and + * let the architecture-specific code tailor gd->start_addr_sp and * gd->irq_sp */ return arch_reserve_stacks(); @@ -556,7 +556,6 @@ static int setup_board_part1(void) /* * Save local variables to board info struct */ - bd->bi_memstart = CONFIG_SYS_SDRAM_BASE; /* start of memory */ bd->bi_memsize = gd->ram_size; /* size in bytes */ diff --git a/common/board_r.c b/common/board_r.c index 307124e..1a46f62 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -779,7 +779,7 @@ init_fnc_t init_sequence_r[] = { initr_flash, #endif INIT_FUNC_WATCHDOG_RESET -#if defined(CONFIG_PPC) || defined(CONFIG_M68K) +#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86) /* initialize higher level parts of CPU like time base and timers */ cpu_init_r, #endif diff --git a/common/cmd_cpu.c b/common/cmd_cpu.c new file mode 100644 index 0000000..c3e229f --- /dev/null +++ b/common/cmd_cpu.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +static const char *cpu_feature_name[CPU_FEAT_COUNT] = { + "L1 cache", + "MMU", +}; + +static int print_cpu_list(bool detail) +{ + struct udevice *dev; + struct uclass *uc; + char buf[100]; + int ret; + + ret = uclass_get(UCLASS_CPU, &uc); + if (ret) { + printf("Cannot find CPU uclass\n"); + return ret; + } + uclass_foreach_dev(dev, uc) { + struct cpu_platdata *plat = dev_get_parent_platdata(dev); + struct cpu_info info; + bool first; + int i; + + ret = cpu_get_desc(dev, buf, sizeof(buf)); + printf("%3d: %-10s %s\n", dev->seq, dev->name, + ret ? "" : buf); + if (!detail) + continue; + ret = cpu_get_info(dev, &info); + if (ret) { + printf("\t(no detail available"); + if (ret != -ENOSYS) + printf(": err=%d\n", ret); + printf(")\n"); + continue; + } + printf("\tID = %d, freq = ", plat->cpu_id); + print_freq(info.cpu_freq, ""); + first = true; + for (i = 0; i < CPU_FEAT_COUNT; i++) { + if (info.features & (1 << i)) { + printf("%s%s", first ? ": " : ", ", + cpu_feature_name[i]); + first = false; + } + } + printf("\n"); + } + + return 0; +} + +static int do_cpu_list(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + if (print_cpu_list(false)) + return CMD_RET_FAILURE; + + return 0; +} + +static int do_cpu_detail(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (print_cpu_list(true)) + return CMD_RET_FAILURE; + + return 0; +} + +static cmd_tbl_t cmd_cpu_sub[] = { + U_BOOT_CMD_MKENT(list, 2, 1, do_cpu_list, "", ""), + U_BOOT_CMD_MKENT(detail, 4, 0, do_cpu_detail, "", ""), +}; + +/* + * Process a cpu sub-command + */ +static int do_cpu(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + cmd_tbl_t *c = NULL; + + /* Strip off leading 'cpu' command argument */ + argc--; + argv++; + + if (argc) + c = find_cmd_tbl(argv[0], cmd_cpu_sub, ARRAY_SIZE(cmd_cpu_sub)); + + if (c) + return c->cmd(cmdtp, flag, argc, argv); + else + return CMD_RET_USAGE; +} + +U_BOOT_CMD( + cpu, 2, 1, do_cpu, + "display information about CPUs", + "list - list available CPUs\n" + "cpu detail - show CPU detail" +); diff --git a/configs/chromebook_link_defconfig b/configs/chromebook_link_defconfig index fe2610a..81222d2 100644 --- a/configs/chromebook_link_defconfig +++ b/configs/chromebook_link_defconfig @@ -1,5 +1,5 @@ -CONFIG_SYS_EXTRA_OPTIONS="SYS_TEXT_BASE=0xfff00000" CONFIG_X86=y +CONFIG_VENDOR_GOOGLE=y CONFIG_TARGET_CHROMEBOOK_LINK=y CONFIG_OF_CONTROL=y CONFIG_OF_SEPARATE=y diff --git a/configs/chromebox_panther_defconfig b/configs/chromebox_panther_defconfig index 0613cd6..91189c9 100644 --- a/configs/chromebox_panther_defconfig +++ b/configs/chromebox_panther_defconfig @@ -1,5 +1,5 @@ -CONFIG_SYS_EXTRA_OPTIONS="SYS_TEXT_BASE=0xfff00000" CONFIG_X86=y +CONFIG_VENDOR_GOOGLE=y CONFIG_TARGET_CHROMEBOX_PANTHER=y CONFIG_OF_CONTROL=y CONFIG_OF_SEPARATE=y diff --git a/configs/coreboot-x86_defconfig b/configs/coreboot-x86_defconfig index 0249172..799853f 100644 --- a/configs/coreboot-x86_defconfig +++ b/configs/coreboot-x86_defconfig @@ -1,5 +1,5 @@ -CONFIG_SYS_EXTRA_OPTIONS="SYS_TEXT_BASE=0x01110000" CONFIG_X86=y +CONFIG_VENDOR_COREBOOT=y CONFIG_TARGET_COREBOOT=y CONFIG_OF_CONTROL=y CONFIG_DM_PCI=y diff --git a/configs/crownbay_defconfig b/configs/crownbay_defconfig index ce90553..61d1fcc 100644 --- a/configs/crownbay_defconfig +++ b/configs/crownbay_defconfig @@ -1,6 +1,7 @@ -CONFIG_SYS_EXTRA_OPTIONS="SYS_TEXT_BASE=0xfff00000" CONFIG_X86=y +CONFIG_VENDOR_INTEL=y CONFIG_TARGET_CROWNBAY=y CONFIG_OF_CONTROL=y CONFIG_OF_SEPARATE=y CONFIG_DEFAULT_DEVICE_TREE="crownbay" +CONFIG_GENERATE_PIRQ_TABLE=y diff --git a/configs/galileo_defconfig b/configs/galileo_defconfig index 9b0f969..1a54ba5 100644 --- a/configs/galileo_defconfig +++ b/configs/galileo_defconfig @@ -1,5 +1,5 @@ -CONFIG_SYS_EXTRA_OPTIONS="SYS_TEXT_BASE=0xfff10000" CONFIG_X86=y +CONFIG_VENDOR_INTEL=y CONFIG_TARGET_GALILEO=y CONFIG_OF_CONTROL=y CONFIG_OF_SEPARATE=y diff --git a/configs/minnowmax_defconfig b/configs/minnowmax_defconfig index d124289..426fb52 100644 --- a/configs/minnowmax_defconfig +++ b/configs/minnowmax_defconfig @@ -1,11 +1,14 @@ -CONFIG_SYS_EXTRA_OPTIONS="SYS_TEXT_BASE=0xfff00000" CONFIG_X86=y +CONFIG_VENDOR_INTEL=y CONFIG_TARGET_MINNOWMAX=y CONFIG_OF_CONTROL=y CONFIG_OF_SEPARATE=y CONFIG_DEFAULT_DEVICE_TREE="minnowmax" -CONFIG_VIDEO_X86=y CONFIG_FRAMEBUFFER_SET_VESA_MODE=y CONFIG_FRAMEBUFFER_VESA_MODE_11A=y CONFIG_MMCONF_BASE_ADDRESS=0xe0000000 CONFIG_HAVE_INTEL_ME=y +CONFIG_GENERATE_SFI_TABLE=y +CONFIG_CPU=y +CONFIG_CMD_CPU=y +CONFIG_SMP=y diff --git a/doc/README.x86 b/doc/README.x86 index 0355d1c..ef13fb4 100644 --- a/doc/README.x86 +++ b/doc/README.x86 @@ -75,7 +75,7 @@ Find the following files: * ./mainboard/google/link/descriptor.bin * ./mainboard/google/link/me.bin -* ./northbridge/intel/sandybridge/systemagent-ivybridge.bin +* ./northbridge/intel/sandybridge/systemagent-r6.bin The 3rd one should be renamed to mrc.bin. As for the video ROM, you can get it here [2]. @@ -127,15 +127,32 @@ board/intel/minnowmax/fsp.bin Obtain the VGA RAM (Vga.dat at the time of writing) and put it into the same directory: board/intel/minnowmax/vga.bin -You still need two more binary blobs. These come from the sample SPI image -provided in the FSP (SPI.bin at the time of writing). +You still need two more binary blobs. The first comes from the original +firmware image available from: + +http://firmware.intel.com/sites/default/files/2014-WW42.4-MinnowBoardMax.73-64-bit.bin_Release.zip + +Unzip it: + + $ unzip 2014-WW42.4-MinnowBoardMax.73-64-bit.bin_Release.zip Use ifdtool in the U-Boot tools directory to extract the images from that file, for example: + $ ./tools/ifdtool -x MNW2MAX1.X64.0073.R02.1409160934.bin + +This will provide the descriptor file - copy this into the correct place: + + $ cp flashregion_0_flashdescriptor.bin board/intel/minnowmax/descriptor.bin + +Then do the same with the sample SPI image provided in the FSP (SPI.bin at +the time of writing) to obtain the last image. Note that this will also +produce a flash descriptor file, but it does not seem to work, probably +because it is not designed for the Minnowmax. That is why you need to get +the flash descriptor from the original firmware as above. + $ ./tools/ifdtool -x BayleyBay/SPI.bin $ cp flashregion_2_intel_me.bin board/intel/minnowmax/me.bin - $ cp flashregion_0_flashdescriptor.bin board/intel/minnowmax/descriptor.bin Now you can build U-Boot and obtain u-boot.rom diff --git a/drivers/Kconfig b/drivers/Kconfig index 941aa0c..1f40887 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -2,6 +2,8 @@ menu "Device Drivers" source "drivers/core/Kconfig" +source "drivers/cpu/Kconfig" + source "drivers/demo/Kconfig" source "drivers/pci/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 5ef58c0..405b64b 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_DM_DEMO) += demo/ obj-$(CONFIG_BIOSEMU) += bios_emulator/ obj-y += block/ obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/ +obj-$(CONFIG_CPU) += cpu/ obj-y += crypto/ obj-$(CONFIG_FPGA) += fpga/ obj-y += hwmon/ diff --git a/drivers/bios_emulator/atibios.c b/drivers/bios_emulator/atibios.c index 7ea5fa6..dec6230 100644 --- a/drivers/bios_emulator/atibios.c +++ b/drivers/bios_emulator/atibios.c @@ -502,7 +502,8 @@ int biosemu_run(pci_dev_t pcidev, uchar *bios_rom, int bios_len, */ if (clean_up) { BE_exit(); - if (vga_info->BIOSImage) + if (vga_info->BIOSImage && + (u32)(vga_info->BIOSImage) != 0xc0000) free(vga_info->BIOSImage); free(vga_info); vga_info = NULL; diff --git a/drivers/core/lists.c b/drivers/core/lists.c index 647e390..0c49d99 100644 --- a/drivers/core/lists.c +++ b/drivers/core/lists.c @@ -74,6 +74,13 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) int device_bind_driver(struct udevice *parent, const char *drv_name, const char *dev_name, struct udevice **devp) { + return device_bind_driver_to_node(parent, drv_name, dev_name, -1, devp); +} + +int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, + const char *dev_name, int node, + struct udevice **devp) +{ struct driver *drv; int ret; @@ -82,7 +89,7 @@ int device_bind_driver(struct udevice *parent, const char *drv_name, printf("Cannot find driver '%s'\n", drv_name); return -ENOENT; } - ret = device_bind(parent, drv, dev_name, NULL, -1, devp); + ret = device_bind(parent, drv, dev_name, NULL, node, devp); if (ret) { printf("Cannot create device named '%s' (err=%d)\n", dev_name, ret); diff --git a/drivers/cpu/Kconfig b/drivers/cpu/Kconfig new file mode 100644 index 0000000..0d1424d --- /dev/null +++ b/drivers/cpu/Kconfig @@ -0,0 +1,8 @@ +config CPU + bool "Enable CPU drivers using Driver Model" + help + This allows drivers to be provided for CPUs and their type to be + specified in the board's device tree. For boards which support + multiple CPUs, then normally have to be set up in U-Boot so that + they can work correctly in the OS. This provides a framework for + finding out information about available CPUs and making changes. diff --git a/drivers/cpu/Makefile b/drivers/cpu/Makefile new file mode 100644 index 0000000..8710160 --- /dev/null +++ b/drivers/cpu/Makefile @@ -0,0 +1,7 @@ +# +# Copyright (c) 2015 Google, Inc +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# +obj-$(CONFIG_CPU) += cpu-uclass.o diff --git a/drivers/cpu/cpu-uclass.c b/drivers/cpu/cpu-uclass.c new file mode 100644 index 0000000..ab18ee2 --- /dev/null +++ b/drivers/cpu/cpu-uclass.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +int cpu_get_desc(struct udevice *dev, char *buf, int size) +{ + struct cpu_ops *ops = cpu_get_ops(dev); + + if (!ops->get_desc) + return -ENOSYS; + + return ops->get_desc(dev, buf, size); +} + +int cpu_get_info(struct udevice *dev, struct cpu_info *info) +{ + struct cpu_ops *ops = cpu_get_ops(dev); + + if (!ops->get_desc) + return -ENOSYS; + + return ops->get_info(dev, info); +} + +U_BOOT_DRIVER(cpu_bus) = { + .name = "cpu_bus", + .id = UCLASS_SIMPLE_BUS, + .per_child_platdata_auto_alloc_size = sizeof(struct cpu_platdata), +}; + +static int uclass_cpu_init(struct uclass *uc) +{ + struct udevice *dev; + int node; + int ret; + + node = fdt_path_offset(gd->fdt_blob, "/cpus"); + if (node < 0) + return 0; + + ret = device_bind_driver_to_node(dm_root(), "cpu_bus", "cpus", node, + &dev); + + return ret; +} + +UCLASS_DRIVER(cpu) = { + .id = UCLASS_CPU, + .name = "cpu", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .init = uclass_cpu_init, +}; diff --git a/drivers/misc/smsc_lpc47m.c b/drivers/misc/smsc_lpc47m.c index d51f8e3..1e50d5b 100644 --- a/drivers/misc/smsc_lpc47m.c +++ b/drivers/misc/smsc_lpc47m.c @@ -22,12 +22,13 @@ static void pnp_exit_conf_state(u16 dev) outb(0xaa, port); } -void lpc47m_enable_serial(u16 dev, u16 iobase) +void lpc47m_enable_serial(u16 dev, u16 iobase, u8 irq) { pnp_enter_conf_state(dev); pnp_set_logical_device(dev); pnp_set_enable(dev, 0); pnp_set_iobase(dev, PNP_IDX_IO0, iobase); + pnp_set_irq(dev, PNP_IDX_IRQ0, irq); pnp_set_enable(dev, 1); pnp_exit_conf_state(dev); } diff --git a/drivers/pci/pci_rom.c b/drivers/pci/pci_rom.c index 48c0a77..37450c8 100644 --- a/drivers/pci/pci_rom.c +++ b/drivers/pci/pci_rom.c @@ -67,6 +67,7 @@ static int pci_rom_probe(pci_dev_t dev, uint class, struct pci_rom_data *rom_data; u16 vendor, device; u16 rom_vendor, rom_device; + u32 rom_class; u32 vendev; u32 mapped_vendev; u32 rom_address; @@ -125,20 +126,20 @@ static int pci_rom_probe(pci_dev_t dev, uint class, /* Continue anyway */ } - debug("PCI ROM image, Class Code %04x%02x, Code Type %02x\n", - rom_data->class_hi, rom_data->class_lo, rom_data->type); + rom_class = (le16_to_cpu(rom_data->class_hi) << 8) | rom_data->class_lo; + debug("PCI ROM image, Class Code %06x, Code Type %02x\n", + rom_class, rom_data->type); - if (class != ((rom_data->class_hi << 8) | rom_data->class_lo)) { - debug("Class Code mismatch ROM %08x, dev %08x\n", - (rom_data->class_hi << 8) | rom_data->class_lo, - class); + if (class != rom_class) { + debug("Class Code mismatch ROM %06x, dev %06x\n", + rom_class, class); } *hdrp = rom_header; return 0; } -int pci_rom_load(uint16_t class, struct pci_rom_header *rom_header, +int pci_rom_load(struct pci_rom_header *rom_header, struct pci_rom_header **ram_headerp) { struct pci_rom_data *rom_data; @@ -232,17 +233,18 @@ int pci_run_vga_bios(pci_dev_t dev, int (*int15_handler)(void), int exec_method) { struct pci_rom_header *rom, *ram; int vesa_mode = -1; - uint16_t class; + uint class; bool emulate; int ret; /* Only execute VGA ROMs */ - pci_read_config_word(dev, PCI_CLASS_DEVICE, &class); - if ((class ^ PCI_CLASS_DISPLAY_VGA) & 0xff00) { + pci_read_config_dword(dev, PCI_REVISION_ID, &class); + if (((class >> 16) ^ PCI_CLASS_DISPLAY_VGA) & 0xff00) { debug("%s: Class %#x, should be %#x\n", __func__, class, PCI_CLASS_DISPLAY_VGA); return -ENODEV; } + class >>= 8; if (!should_load_oprom(dev)) return -ENXIO; @@ -251,7 +253,7 @@ int pci_run_vga_bios(pci_dev_t dev, int (*int15_handler)(void), int exec_method) if (ret) return ret; - ret = pci_rom_load(class, rom, &ram); + ret = pci_rom_load(rom, &ram); if (ret) return ret; diff --git a/include/common.h b/include/common.h index cde3474..d4d704a 100644 --- a/include/common.h +++ b/include/common.h @@ -192,22 +192,8 @@ int cpu_init(void); /* */ phys_size_t initdram (int); -int display_options (void); -/** - * print_size() - Print a size with a suffic - * - * print sizes as "xxx KiB", "xxx.y KiB", "xxx MiB", "xxx.y MiB", - * xxx GiB, xxx.y GiB, etc as needed; allow for optional trailing string - * (like "\n") - * - * @size: Size to print - * @suffix String to print after the size - */ -void print_size(uint64_t size, const char *suffix); - -int print_buffer(ulong addr, const void *data, uint width, uint count, - uint linelen); +#include /* common/main.c */ void main_loop (void); diff --git a/include/configs/chromebook_link.h b/include/configs/chromebook_link.h index 5265787..f2d798a 100644 --- a/include/configs/chromebook_link.h +++ b/include/configs/chromebook_link.h @@ -16,4 +16,7 @@ #include #include +#define CONFIG_ENV_SECT_SIZE 0x1000 +#define CONFIG_ENV_OFFSET 0x003f8000 + #endif /* __CONFIG_H */ diff --git a/include/configs/crownbay.h b/include/configs/crownbay.h index df32f2a..4fef433 100644 --- a/include/configs/crownbay.h +++ b/include/configs/crownbay.h @@ -15,6 +15,7 @@ #define CONFIG_SYS_MONITOR_LEN (1 << 20) #define CONFIG_BOARD_EARLY_INIT_F +#define CONFIG_ARCH_MISC_INIT #define CONFIG_NR_DRAM_BANKS 1 @@ -61,10 +62,6 @@ #undef CONFIG_CFB_CONSOLE /* Environment configuration */ -#undef CONFIG_ENV_IS_NOWHERE -#undef CONFIG_ENV_SIZE -#define CONFIG_ENV_IS_IN_SPI_FLASH -#define CONFIG_ENV_SIZE 0x1000 #define CONFIG_ENV_SECT_SIZE 0x1000 #define CONFIG_ENV_OFFSET 0 diff --git a/include/configs/galileo.h b/include/configs/galileo.h index 288acf3..f780b8f 100644 --- a/include/configs/galileo.h +++ b/include/configs/galileo.h @@ -63,10 +63,6 @@ #define CONFIG_PHYLIB /* Environment configuration */ -#undef CONFIG_ENV_IS_NOWHERE -#undef CONFIG_ENV_SIZE -#define CONFIG_ENV_IS_IN_SPI_FLASH -#define CONFIG_ENV_SIZE 0x1000 #define CONFIG_ENV_SECT_SIZE 0x1000 #define CONFIG_ENV_OFFSET 0 diff --git a/include/configs/minnowmax.h b/include/configs/minnowmax.h index 823e051..2a1915d 100644 --- a/include/configs/minnowmax.h +++ b/include/configs/minnowmax.h @@ -42,7 +42,7 @@ #define CONFIG_SCSI_DEV_LIST \ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VALLEYVIEW_SATA} -#define CONFIG_SPI_FLASH_SST +#define CONFIG_SPI_FLASH_STMICRO #define CONFIG_MMC #define CONFIG_SDHCI @@ -69,4 +69,8 @@ /* Avoid a warning in the Realtek Ethernet driver */ #define CONFIG_SYS_CACHELINE_SIZE 16 +/* Environment in SPI flash is unsupported for now */ +#undef CONFIG_ENV_IS_IN_SPI_FLASH +#define CONFIG_ENV_IS_NOWHERE + #endif /* __CONFIG_H */ diff --git a/include/configs/x86-common.h b/include/configs/x86-common.h index 9571c65..3e21e09 100644 --- a/include/configs/x86-common.h +++ b/include/configs/x86-common.h @@ -20,6 +20,7 @@ #define CONFIG_PHYSMEM #define CONFIG_DISPLAY_BOARDINFO_LATE #define CONFIG_DISPLAY_CPUINFO +#define CONFIG_LAST_STAGE_INIT #define CONFIG_LMB #define CONFIG_OF_LIBFDT @@ -207,7 +208,7 @@ /*----------------------------------------------------------------------- * Environment configuration */ -#define CONFIG_ENV_IS_NOWHERE +#define CONFIG_ENV_IS_IN_SPI_FLASH #define CONFIG_ENV_SIZE 0x01000 /*----------------------------------------------------------------------- diff --git a/include/cpu.h b/include/cpu.h new file mode 100644 index 0000000..34c60bc --- /dev/null +++ b/include/cpu.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CPU_H +#define __CPU_H + +/** + * struct cpu_platdata - platform data for a CPU + * + * This can be accessed with dev_get_parent_platdata() for any UCLASS_CPU + * device. + * + * @cpu_id: Platform-specific way of identifying the CPU. + */ +struct cpu_platdata { + int cpu_id; +}; + +/* CPU features - mostly just a placeholder for now */ +enum { + CPU_FEAT_L1_CACHE = 0, /* Supports level 1 cache */ + CPU_FEAT_MMU = 1, /* Supports virtual memory */ + + CPU_FEAT_COUNT, +}; + +/** + * struct cpu_info - Information about a CPU + * + * @cpu_freq: Current CPU frequency in Hz + * @features: Flags for supported CPU features + */ +struct cpu_info { + ulong cpu_freq; + ulong features; +}; + +struct cpu_ops { + /** + * get_desc() - Get a description string for a CPU + * + * @dev: Device to check (UCLASS_CPU) + * @buf: Buffer to place string + * @size: Size of string space + * @return 0 if OK, -ENOSPC if buffer is too small, other -ve on error + */ + int (*get_desc)(struct udevice *dev, char *buf, int size); + + /** + * get_info() - Get information about a CPU + * + * @dev: Device to check (UCLASS_CPU) + * @info: Returns CPU info + * @return 0 if OK, -ve on error + */ + int (*get_info)(struct udevice *dev, struct cpu_info *info); +}; + +#define cpu_get_ops(dev) ((struct cpu_ops *)(dev)->driver->ops) + +/** + * cpu_get_desc() - Get a description string for a CPU + * + * @dev: Device to check (UCLASS_CPU) + * @buf: Buffer to place string + * @size: Size of string space + * @return 0 if OK, -ENOSPC if buffer is too small, other -ve on error + */ +int cpu_get_desc(struct udevice *dev, char *buf, int size); + +/** + * cpu_get_info() - Get information about a CPU + * + * @dev: Device to check (UCLASS_CPU) + * @info: Returns CPU info + * @return 0 if OK, -ve on error + */ +int cpu_get_info(struct udevice *dev, struct cpu_info *info); + +#endif diff --git a/include/display_options.h b/include/display_options.h new file mode 100644 index 0000000..ac44c45 --- /dev/null +++ b/include/display_options.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * (C) Copyright 2000-2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DISPLAY_OPTIONS_H +#define __DISPLAY_OPTIONS_H + +/** + * print_size() - Print a size with a suffix + * + * Print sizes as "xxx KiB", "xxx.y KiB", "xxx MiB", "xxx.y MiB", + * xxx GiB, xxx.y GiB, etc as needed; allow for optional trailing string + * (like "\n") + * + * @size: Size to print + * @suffix String to print after the size + */ +void print_size(uint64_t size, const char *suffix); + +/** + * print_freq() - Print a frequency with a suffix + * + * Print frequencies as "x.xx GHz", "xxx KHz", etc as needed; allow for + * optional trailing string (like "\n") + * + * @freq: Frequency to print in Hz + * @suffix String to print after the frequency + */ +void print_freq(uint64_t freq, const char *suffix); + +/** + * print_buffer() - Print data buffer in hex and ascii form + * + * Data reads are buffered so that each memory address is only read once. + * This is useful when displaying the contents of volatile registers. + * + * @addr: Starting address to display at start of line + * @data: pointer to data buffer + * @width: data value width. May be 1, 2, or 4. + * @count: number of values to display + * @linelen: Number of values to print per line; specify 0 for default length + */ +int print_buffer(ulong addr, const void *data, uint width, uint count, + uint linelen); + +/** + * display_options() - display the version string / build tag + * + * This displays the U-Boot version string. If a build tag is available this + * is displayed also. + */ +int display_options(void); + +#endif diff --git a/include/dm/lists.h b/include/dm/lists.h index 1b50af9..61610e6 100644 --- a/include/dm/lists.h +++ b/include/dm/lists.h @@ -73,4 +73,20 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, int device_bind_driver(struct udevice *parent, const char *drv_name, const char *dev_name, struct udevice **devp); +/** + * device_bind_driver_to_node() - bind a device to a driver for a node + * + * This binds a new device to a driver for a given device tree node. This + * should only be needed if the node lacks a compatible strings. + * + * @parent: Parent device + * @drv_name: Name of driver to attach to this parent + * @dev_name: Name of the new device thus created + * @node: Device tree node + * @devp: Returns the newly bound device + */ +int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, + const char *dev_name, int node, + struct udevice **devp); + #endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index fddfd35..395e25a 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -45,6 +45,7 @@ enum uclass_id { UCLASS_USB_HUB, /* USB hub */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ UCLASS_MASS_STORAGE, /* Mass storage device */ + UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/smsc_lpc47m.h b/include/smsc_lpc47m.h index bffd622..32b069d 100644 --- a/include/smsc_lpc47m.h +++ b/include/smsc_lpc47m.h @@ -13,7 +13,8 @@ * * @dev: High 8 bits = Super I/O port, low 8 bits = logical device number. * @iobase: Processor I/O port address to assign to this serial device. + * @irq: Processor IRQ number to assign to this serial device. */ -void lpc47m_enable_serial(u16 dev, u16 iobase); +void lpc47m_enable_serial(u16 dev, u16 iobase, u8 irq); #endif /* _SMSC_LPC47M_H_ */ diff --git a/lib/display_options.c b/lib/display_options.c index d5d17b2..24d8f55 100644 --- a/lib/display_options.c +++ b/lib/display_options.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,47 @@ int display_options (void) return 0; } +void print_freq(uint64_t freq, const char *s) +{ + unsigned long m = 0, n; + uint32_t f; + static const char names[] = {'G', 'M', 'K'}; + unsigned long d = 1e9; + char c = 0; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(names); i++, d /= 1000) { + if (freq >= d) { + c = names[i]; + break; + } + } + + if (!c) { + printf("%" PRIu64 " Hz%s", freq, s); + return; + } + + f = do_div(freq, d); + n = freq; + + /* If there's a remainder, show the first few digits */ + if (f) { + m = f; + while (m > 1000) + m /= 10; + while (m && !(m % 10)) + m /= 10; + if (m >= 100) + m = (m / 10) + (m % 100 >= 50); + } + + printf("%lu", n); + if (m) + printf(".%ld", m); + printf(" %cHz%s", c, s); +} + void print_size(uint64_t size, const char *s) { unsigned long m = 0, n; @@ -63,19 +105,6 @@ void print_size(uint64_t size, const char *s) printf (" %ciB%s", c, s); } -/* - * Print data buffer in hex and ascii form to the terminal. - * - * data reads are buffered so that each memory address is only read once. - * Useful when displaying the contents of volatile registers. - * - * parameters: - * addr: Starting address to display at start of line - * data: pointer to data buffer - * width: data value width. May be 1, 2, or 4. - * count: number of values to display - * linelen: Number of values to print per line; specify 0 for default length - */ #define MAX_LINE_LENGTH_BYTES (64) #define DEFAULT_LINE_LENGTH_BYTES (16) int print_buffer(ulong addr, const void *data, uint width, uint count,