diff options
author | Kyösti Mälkki <kyosti.malkki@gmail.com> | 2022-11-08 04:43:41 +0000 |
---|---|---|
committer | Kyösti Mälkki <kyosti.malkki@gmail.com> | 2022-11-09 18:10:54 +0000 |
commit | 7b73e85283bea7f456ab2f86ed1d1099eb88bc2f (patch) | |
tree | a5aba9ebe735e4f904b5edad8f6b6ddf6122d607 | |
parent | c8a20b9d3b8939e4b7d259c5857631c9690657de (diff) |
Revert "mb/aopen/dxplplusu: Remove board"
This reverts commit eb76a455cd39ec59b7f2ba28baeec9538befd59e
and applies minor fixes to make it build again.
PARALLEL_MP was working prior to board removal and no
relevant SMI handlers were implemented. So NO_SMM choice
is now selected.
Change-Id: Ia1cd02278240d1b5d006fb2a7730d3d86390f85b
Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/69339
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
59 files changed, 4464 insertions, 2 deletions
diff --git a/src/arch/x86/smbios.c b/src/arch/x86/smbios.c index 17a37eadd3..24b18f2988 100644 --- a/src/arch/x86/smbios.c +++ b/src/arch/x86/smbios.c @@ -382,6 +382,8 @@ static int get_socket_type(void) { if (CONFIG(CPU_INTEL_SLOT_1)) return 0x08; + if (CONFIG(CPU_INTEL_SOCKET_MPGA604)) + return 0x13; if (CONFIG(CPU_INTEL_SOCKET_LGA775)) return 0x15; if (CONFIG(XEON_SP_COMMON_BASE)) diff --git a/src/cpu/intel/Kconfig b/src/cpu/intel/Kconfig index 62f88419bf..43b360bfc4 100644 --- a/src/cpu/intel/Kconfig +++ b/src/cpu/intel/Kconfig @@ -10,6 +10,7 @@ source "src/cpu/intel/model_1067x/Kconfig" source "src/cpu/intel/model_106cx/Kconfig" source "src/cpu/intel/model_206ax/Kconfig" source "src/cpu/intel/model_2065x/Kconfig" +source "src/cpu/intel/model_f2x/Kconfig" source "src/cpu/intel/model_f3x/Kconfig" source "src/cpu/intel/model_f4x/Kconfig" source "src/cpu/intel/haswell/Kconfig" @@ -19,6 +20,7 @@ source "src/cpu/intel/socket_BGA956/Kconfig" source "src/cpu/intel/socket_FCBGA559/Kconfig" source "src/cpu/intel/socket_m/Kconfig" source "src/cpu/intel/socket_p/Kconfig" +source "src/cpu/intel/socket_mPGA604/Kconfig" source "src/cpu/intel/socket_441/Kconfig" source "src/cpu/intel/socket_LGA775/Kconfig" # Architecture specific features diff --git a/src/cpu/intel/Makefile.inc b/src/cpu/intel/Makefile.inc index 92025224d9..01913de312 100644 --- a/src/cpu/intel/Makefile.inc +++ b/src/cpu/intel/Makefile.inc @@ -9,6 +9,7 @@ subdirs-$(CONFIG_CPU_INTEL_SOCKET_BGA956) += socket_BGA956 subdirs-$(CONFIG_CPU_INTEL_SOCKET_FCBGA559) += socket_FCBGA559 subdirs-$(CONFIG_CPU_INTEL_SOCKET_M) += socket_m subdirs-$(CONFIG_CPU_INTEL_SOCKET_P) += socket_p +subdirs-$(CONFIG_CPU_INTEL_SOCKET_MPGA604) += socket_mPGA604 subdirs-$(CONFIG_CPU_INTEL_MODEL_2065X) += model_2065x subdirs-$(CONFIG_CPU_INTEL_MODEL_206AX) += model_206ax subdirs-$(CONFIG_CPU_INTEL_HASWELL) += haswell diff --git a/src/cpu/intel/model_f2x/Kconfig b/src/cpu/intel/model_f2x/Kconfig new file mode 100644 index 0000000000..999ea4d386 --- /dev/null +++ b/src/cpu/intel/model_f2x/Kconfig @@ -0,0 +1,7 @@ +config CPU_INTEL_MODEL_F2X + bool + select ARCH_X86 + select SUPPORT_CPU_UCODE_IN_CBFS + select CPU_INTEL_COMMON + select SSE2 + select NO_SMM diff --git a/src/cpu/intel/model_f2x/Makefile.inc b/src/cpu/intel/model_f2x/Makefile.inc new file mode 100644 index 0000000000..5d60d21eda --- /dev/null +++ b/src/cpu/intel/model_f2x/Makefile.inc @@ -0,0 +1,5 @@ +subdirs-y += ../common + +ramstage-y += model_f2x_init.c + +cpu_microcode_bins += $(wildcard 3rdparty/intel-microcode/intel-ucode/0f-02-*) diff --git a/src/cpu/intel/model_f2x/model_f2x_init.c b/src/cpu/intel/model_f2x/model_f2x_init.c new file mode 100644 index 0000000000..a228229371 --- /dev/null +++ b/src/cpu/intel/model_f2x/model_f2x_init.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <device/device.h> +#include <cpu/cpu.h> +#include <cpu/x86/mp.h> +#include <cpu/x86/mtrr.h> +#include <cpu/intel/microcode.h> +#include <cpu/intel/common/common.h> +#include <cpu/x86/cache.h> + +static void model_f2x_init(struct device *cpu) +{ + /* Turn on caching if we haven't already */ + enable_cache(); +}; + +static struct device_operations cpu_dev_ops = { + .init = model_f2x_init, +}; + +static const struct cpu_device_id cpu_table[] = { + { X86_VENDOR_INTEL, 0x0f22 }, + { X86_VENDOR_INTEL, 0x0f24 }, + { X86_VENDOR_INTEL, 0x0f25 }, + { X86_VENDOR_INTEL, 0x0f26 }, + { X86_VENDOR_INTEL, 0x0f27 }, + { X86_VENDOR_INTEL, 0x0f29 }, + { 0, 0 }, +}; + +static const struct cpu_driver driver __cpu_driver = { + .ops = &cpu_dev_ops, + .id_table = cpu_table, +}; + +/* Parallel MP initialization support. */ +static void pre_mp_init(void) +{ + const void *patch = intel_microcode_find(); + intel_microcode_load_unlocked(patch); + + /* Setup MTRRs based on physical address size. */ + x86_setup_mtrrs_with_detect(); + x86_mtrr_check(); +} + +static int get_cpu_count(void) +{ + return CONFIG_MAX_CPUS; +} + +static void get_microcode_info(const void **microcode, int *parallel) +{ + *microcode = intel_microcode_find(); + *parallel = !intel_ht_supported(); +} + +static const struct mp_ops mp_ops = { + .pre_mp_init = pre_mp_init, + .get_cpu_count = get_cpu_count, + .get_microcode_info = get_microcode_info, +}; + +void mp_init_cpus(struct bus *cpu_bus) +{ + mp_init_with_smm(cpu_bus, &mp_ops); +} diff --git a/src/cpu/intel/socket_mPGA604/Kconfig b/src/cpu/intel/socket_mPGA604/Kconfig new file mode 100644 index 0000000000..7b086990f7 --- /dev/null +++ b/src/cpu/intel/socket_mPGA604/Kconfig @@ -0,0 +1,36 @@ +config CPU_INTEL_SOCKET_MPGA604 + bool + +if CPU_INTEL_SOCKET_MPGA604 + +config SOCKET_SPECIFIC_OPTIONS + def_bool y + select CPU_INTEL_MODEL_F2X + select MMX + select SSE + select UDELAY_TSC + select TSC_MONOTONIC_TIMER + select SIPI_VECTOR_IN_ROM + select CPU_INTEL_COMMON + select CPU_INTEL_COMMON_TIMEBASE + +# mPGA604 are usually Intel Netburst CPUs which should have SSE2 +# but the ramtest.c code on the Dell S1850 seems to choke on +# enabling it, so disable it for now. +config SSE2 + bool + default n + +config DCACHE_RAM_BASE + hex + default 0xfefc0000 + +config DCACHE_RAM_SIZE + hex + default 0x4000 + +config DCACHE_BSP_STACK_SIZE + hex + default 0x2000 + +endif # CPU_INTEL_SOCKET_MPGA604 diff --git a/src/cpu/intel/socket_mPGA604/Makefile.inc b/src/cpu/intel/socket_mPGA604/Makefile.inc new file mode 100644 index 0000000000..f9dfc67b0c --- /dev/null +++ b/src/cpu/intel/socket_mPGA604/Makefile.inc @@ -0,0 +1,9 @@ +subdirs-y += ../model_f2x +subdirs-y += ../../x86/lapic +subdirs-y += ../microcode + +bootblock-y += ../car/p4-netburst/cache_as_ram.S +bootblock-y += ../car/bootblock.c + +postcar-y += ../car/p4-netburst/exit_car.S +romstage-y += ../car/romstage.c diff --git a/src/cpu/x86/Kconfig b/src/cpu/x86/Kconfig index 1d6b489ef1..1852e78a0b 100644 --- a/src/cpu/x86/Kconfig +++ b/src/cpu/x86/Kconfig @@ -172,7 +172,7 @@ config SMM_LAPIC_REMAP_MITIGATION bool default y if NORTHBRIDGE_INTEL_I945 || NORTHBRIDGE_INTEL_GM45 \ || NORTHBRIDGE_INTEL_X4X || NORTHBRIDGE_INTEL_PINEVIEW \ - || NORTHBRIDGE_INTEL_IRONLAKE + || NORTHBRIDGE_INTEL_E7505 || NORTHBRIDGE_INTEL_IRONLAKE default n config X86_AMD_FIXED_MTRRS diff --git a/src/mainboard/aopen/Kconfig b/src/mainboard/aopen/Kconfig new file mode 100644 index 0000000000..754bab0a50 --- /dev/null +++ b/src/mainboard/aopen/Kconfig @@ -0,0 +1,15 @@ +if VENDOR_AOPEN + +choice + prompt "Mainboard model" + +source "src/mainboard/aopen/*/Kconfig.name" + +endchoice + +source "src/mainboard/aopen/*/Kconfig" + +config MAINBOARD_VENDOR + default "AOpen" + +endif # VENDOR_AOPEN diff --git a/src/mainboard/aopen/Kconfig.name b/src/mainboard/aopen/Kconfig.name new file mode 100644 index 0000000000..01ea2b6647 --- /dev/null +++ b/src/mainboard/aopen/Kconfig.name @@ -0,0 +1,2 @@ +config VENDOR_AOPEN + bool "AOpen" diff --git a/src/mainboard/aopen/dxplplusu/Kconfig b/src/mainboard/aopen/dxplplusu/Kconfig new file mode 100644 index 0000000000..5c0ca2b570 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/Kconfig @@ -0,0 +1,27 @@ +if BOARD_AOPEN_DXPLPLUSU + +config BOARD_SPECIFIC_OPTIONS + def_bool y + select CPU_INTEL_SOCKET_MPGA604 + select NORTHBRIDGE_INTEL_E7505 + select SOUTHBRIDGE_INTEL_I82870 + select SOUTHBRIDGE_INTEL_I82801DX + select SUPERIO_SMSC_LPC47M10X + select HAVE_ACPI_TABLES + select BOARD_ROMSIZE_KB_2048 + +config MAINBOARD_DIR + default "aopen/dxplplusu" + +config MAINBOARD_PART_NUMBER + default "DXPL Plus-U" + +config IRQ_SLOT_COUNT + int + default 12 + +config MAX_CPUS + int + default 4 + +endif # BOARD_AOPEN_DXPLPLUSU diff --git a/src/mainboard/aopen/dxplplusu/Kconfig.name b/src/mainboard/aopen/dxplplusu/Kconfig.name new file mode 100644 index 0000000000..131020335d --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/Kconfig.name @@ -0,0 +1,2 @@ +config BOARD_AOPEN_DXPLPLUSU + bool "DXPL Plus-U" diff --git a/src/mainboard/aopen/dxplplusu/Makefile.inc b/src/mainboard/aopen/dxplplusu/Makefile.inc new file mode 100644 index 0000000000..d604d021c5 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/Makefile.inc @@ -0,0 +1,3 @@ +## SPDX-License-Identifier: GPL-2.0-only + +bootblock-y += bootblock.c diff --git a/src/mainboard/aopen/dxplplusu/acpi/e7505_pri.asl b/src/mainboard/aopen/dxplplusu/acpi/e7505_pri.asl new file mode 100644 index 0000000000..aab327134a --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/acpi/e7505_pri.asl @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (MBRS) +{ + Name (_HID, EisaId ("PNP0C01")) + Name (_UID, 0x01) + Name (MSBF, ResourceTemplate () + { + /* System memory */ + QWordMemory (ResourceProducer, PosDecode, MinFixed, + MaxNotFixed, Prefetchable, ReadWrite, + 0x0, 0x100000000, 0x400000000, 0x0, 0x0, ,, _Y1C, + AddressRangeMemory, TypeStatic) + + /* Top Of Low Memory */ + Memory32 (ReadOnly, 0x0, 0x0, 0x1, 0x0, _Y1D) + + /* 640kB who wants more? */ + Memory32Fixed (ReadWrite, 0x0, 0xA0000, ) + + /* 64k BIOS bootblock */ + Memory32Fixed (ReadOnly, 0xF0000, 0x10000,) + + /* ISA memory hole 15-16 MB ? */ + /* Memory32Fixed (ReadOnly, 0x100000, 0xF00000,) */ + /* ISA memory hole 14-15 MB ? */ + /* Memory32Fixed (ReadOnly, 0x100000, 0xE00000,) */ + + /* Local APIC */ + Memory32Fixed (ReadWrite, 0xFEE00000, 0x00001000,) + }) + + Method (_CRS, 0, NotSerialized) + { + CreateQWordField (MSBF, \_SB.MBRS._Y1C._MIN, MEML) + CreateQWordField (MSBF, \_SB.MBRS._Y1C._MAX, MEMM) + CreateQWordField (MSBF, \_SB.MBRS._Y1C._LEN, LELM) + + And (\_SB.PCI0.RLAR, 0x03FF, Local1) + Local1++ + If (Local1 > 0x40) + { + ShiftLeft (Local1, 0x1A, LELM) + } + + + CreateDWordField (MSBF, \_SB.MBRS._Y1D._MIN, MS00) + CreateDWordField (MSBF, \_SB.MBRS._Y1D._MAX, MS01) + CreateDWordField (MSBF, \_SB.MBRS._Y1D._LEN, MEM2) + And (\_SB.PCI0.TOLM, 0xF800, Local1) + ShiftRight (Local1, 0x04, Local1) + Local1-- + If (Local1 > 0x10) + { + Local1 -= 0x0F + Store (ShiftLeft (Local1, 0x14), MEM2) + Store (0x01000000, MS00) + Store (MS00, MS01) + } + + Return (MSBF) + } + + Method (_STA, 0, NotSerialized) + { + Return (0x0F) + } +} diff --git a/src/mainboard/aopen/dxplplusu/acpi/e7505_sec.asl b/src/mainboard/aopen/dxplplusu/acpi/e7505_sec.asl new file mode 100644 index 0000000000..400a5d7092 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/acpi/e7505_sec.asl @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/ioapic.h> + +Name (PBRS, ResourceTemplate () +{ + WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, 0x0000, 0x00FF, 0x0000, 0x0100, ,, ) + + /* System IO */ + DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0, 0x0, 0xffff, 0x0000, 0x10000, ,,, TypeStatic) + IO (Decode16, 0x0CF8, 0x0CF8, 0x08, 0x08, ) + + /* Video RAM */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, 0x000A0000, 0x000BFFFF, + 0x00000000, 0x00020000, ,,, AddressRangeMemory, TypeStatic) + + /* Video ROM */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, 0x000C0000, 0x000C7FFF, + 0x00000000, 0x00008000, ,,, AddressRangeMemory, TypeStatic) + + /* Option ROMs ? */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, 0x000C8000, 0x000DFFFF, + 0x00000000, 0x00018000, ,,, AddressRangeMemory, TypeStatic) + + /* Top Of Lowmemory to IOAPIC */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, 0x00000000, 0xFEBFFFFF, + 0x00000000, IO_APIC_ADDR, ,, _Y08, AddressRangeMemory, TypeStatic) +}) + + +Method (_CRS, 0, NotSerialized) +{ + + /* Top Of Lowmemory to IOAPIC */ + CreateDWordField (PBRS, \_SB.PCI0._Y08._MIN, MEML) + CreateDWordField (PBRS, \_SB.PCI0._Y08._MAX, MEMH) + CreateDWordField (PBRS, \_SB.PCI0._Y08._LEN, LENM) + And (\_SB.PCI0.TOLM, 0xF800, Local1) + ShiftRight (Local1, 0x04, Local1) + ShiftLeft (Local1, 0x14, MEML) + MEMH = IO_APIC_ADDR - 1 + LENM = IO_APIC_ADDR - MEML + + Return (PBRS) +} + +Method (_STA, 0, NotSerialized) +{ + Return (0x0F) +} diff --git a/src/mainboard/aopen/dxplplusu/acpi/i82801db.asl b/src/mainboard/aopen/dxplplusu/acpi/i82801db.asl new file mode 100644 index 0000000000..9b16070167 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/acpi/i82801db.asl @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/ioapic.h> + +Device (USB0) +{ + Name (_ADR, 0x001D0000) + Name (_PRW, Package () { 0x03, 0x05 }) + + OperationRegion (USBS, PCI_Config, 0x00, 0x0100) + Field (USBS, ByteAcc, NoLock, Preserve) + { + Offset (0xC4), URES, 8 + } +} + +Device (USB1) +{ + Name (_ADR, 0x001D0001) + Name (_PRW, Package () { 0x04, 0x05 }) + OperationRegion (USBS, PCI_Config, 0x00, 0x0100) + Field (USBS, ByteAcc, NoLock, Preserve) + { + Offset (0xC4), URES, 8 + } +} + +Device (USB2) +{ + Name (_ADR, 0x001D0002) + Name (_PRW, Package () { 0x0C, 0x05 }) + OperationRegion (USBS, PCI_Config, 0x00, 0x0100) + Field (USBS, ByteAcc, NoLock, Preserve) + { + Offset (0xC4), URES, 8 + } +} + +Device (USB3) +{ + Name (_ADR, 0x001D0007) + Name (_PRW, Package () { 0x0D, 0x05 }) /* PME_B0_STS any 0:1d or 0:1f device */ + OperationRegion (USBS, PCI_Config, 0x00, 0x0100) + Field (USBS, ByteAcc, NoLock, Preserve) + { + Offset (0xC4), URES, 8 + } +} + +Device(PCI5) +{ + Name (_ADR, 0x001E0000) + Name (_PRW, Package () { 0x0B, 0x05 }) /* PME# _STS */ + Name (_PRT, Package() { + Package() { 0x0003ffff, 0, 0, 20 }, + Package() { 0x0003ffff, 1, 0, 21 }, + Package() { 0x0003ffff, 2, 0, 22 }, + Package() { 0x0003ffff, 3, 0, 23 }, + }) +} + +Device (ICH0) +{ + Name (_ADR, 0x001F0000) + OperationRegion (D310, PCI_Config, 0x00, 0xFF) + Field (D310, ByteAcc, NoLock, Preserve) + { + Offset (0x40), PBAR, 16, + Offset (0x58), GBAR, 16, + } + + OperationRegion (ACPI, SystemIO, 0x0400, 0xC0) + Field (ACPI, ByteAcc, NoLock, Preserve) + { + PS1L, 8, + PS1H, 8, + PE1L, 8, + PE1H, 8, + + Offset (0x28), + GS0L, 8, + GS0H, 8, + GSPL, 8, + GSPH, 8, + GE0L, 8, + GE0H, 8, + GEPL, 8, + GEPH, 8, + + Offset (0xB8), + GPLV, 8 + } + + Name (MSBF, ResourceTemplate () + { + /* IOAPIC 0 */ + Memory32Fixed (ReadWrite, IO_APIC_ADDR, 0x00001000,) + + IO (Decode16, 0x0, 0x0, 0x80, 0x0, PMIO) + IO (Decode16, 0x0, 0x0, 0x40, 0x0, GPIO) + + /* 8254 legacy irq */ + IO (Decode16, 0x04D0, 0x04D0, 0x02, 0x02,) + + /* reset generator */ + IO (Decode16, 0x0092, 0x0092, 0x01, 0x01, ) + }) + + Method (_CRS, 0, NotSerialized) + { + CreateWordField (MSBF, \_SB_.PCI0.ICH0.PMIO._MIN, IOA1) + CreateWordField (MSBF, \_SB_.PCI0.ICH0.PMIO._MAX, IOA2) + CreateByteField (MSBF, \_SB_.PCI0.ICH0.PMIO._LEN, IOAL) + + Store (PBAR, Local0) + If (Local0 && 1) + { + And (Local0, 0xFFFE, Local0) + Store (Local0, IOA1) + Store (Local0, IOA2) + Store (0x80, IOAL) + } Else { + Store (0x00, IOAL) + } + + CreateWordField (MSBF, \_SB_.PCI0.ICH0.GPIO._MIN, IOS1) + CreateWordField (MSBF, \_SB_.PCI0.ICH0.GPIO._MAX, IOS2) + CreateByteField (MSBF, \_SB_.PCI0.ICH0.GPIO._LEN, IOSL) + + Store (GBAR, Local0) + If (Local0 && 1) { + And (Local0, 0xFFFE, Local0) + Store (Local0, IOS1) + Store (Local0, IOS2) + Store (0x40, IOSL) + } Else { + Store (0x00, IOSL) + } + Return (MSBF) + } + + Device (FWH) + { + Name (_HID, EisaId ("PNP0C02")) + Name (_UID, 0x01) + + + Name (MSBG, ResourceTemplate () { + Memory32Fixed (ReadOnly, 0xFFF00000, 0x00080000,) + Memory32Fixed (ReadOnly, 0xFFF80000, 0x00080000,) + }) + + Method (_CRS, 0, NotSerialized) + { + Return (MSBG) + } + } + + Device (SMSC) + { + Name (_HID, EisaId ("PNP0C02")) + Name (_UID, 0x02) + #include "acpi/superio.asl" + } + +} diff --git a/src/mainboard/aopen/dxplplusu/acpi/p64h2.asl b/src/mainboard/aopen/dxplplusu/acpi/p64h2.asl new file mode 100644 index 0000000000..9a3e1b5aa7 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/acpi/p64h2.asl @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* Interrupt routing for PCI 03:xx.x */ + +/* I/O APIC id 0x3 */ +Device(PBIO) +{ + Name (_ADR, 0x001c0000) +} + +/* PCI-X bridge */ +Device(P64B) +{ + Name (_ADR, 0x001d0000) + Name (_PRT, Package() { + Package() { 0x0002ffff, 0, 0, 24 }, /* PCI-X slot 1 */ + Package() { 0x0002ffff, 1, 0, 25 }, + Package() { 0x0002ffff, 2, 0, 26 }, + Package() { 0x0002ffff, 3, 0, 27 }, + Package() { 0x0003ffff, 0, 0, 28 }, /* PCI-X slot 2 */ + Package() { 0x0003ffff, 1, 0, 29 }, + Package() { 0x0003ffff, 2, 0, 30 }, + Package() { 0x0003ffff, 3, 0, 31 }, + Package() { 0x0004ffff, 0, 0, 32 }, /* On-board GbE */ + }) + + Name (_PRW, Package () { 0x0B, 0x05 }) /* PME# _STS */ + OperationRegion (PBPC, PCI_Config, 0x00, 0xFF) + Field (PBPC, ByteAcc, NoLock, Preserve) + { + Offset (0x3E), BCRL, 8, BCRH, 8 + } + + + Device (ETH0) + { + Name (_ADR, 0x00040000) + Name (_PRW, Package () { 0x0B, 0x05 }) /* PME# _STS */ + } +} + + +/* Interrupt routing for PCI 04:xx.x */ + +/* I/O APIC id 0x4 */ +Device(PAIO) +{ + Name (_ADR, 0x001e0000) +} + +/* PCI-X bridge */ +Device(P64A) +{ + Name (_ADR, 0x001f0000) + Name (_PRT, Package() { + Package() { 0x0002ffff, 0, 0, 48 }, /* PCI-X slot 3 */ + Package() { 0x0002ffff, 1, 0, 49 }, + Package() { 0x0002ffff, 2, 0, 50 }, + Package() { 0x0002ffff, 3, 0, 51 }, + Package() { 0x0003ffff, 0, 0, 52 }, /* PCI-X slot 4 */ + Package() { 0x0003ffff, 1, 0, 53 }, + Package() { 0x0003ffff, 2, 0, 54 }, + Package() { 0x0003ffff, 3, 0, 55 }, + Package() { 0x0004ffff, 0, 0, 54 }, /* On-board SCSI, GSI not 56 ? */ + Package() { 0x0004ffff, 1, 0, 55 }, /* On-board SCSI, GSI not 57 */ + }) + + Name (_PRW, Package () { 0x0B, 0x05 }) /* PME# _STS */ + OperationRegion (PBPC, PCI_Config, 0x00, 0xFF) + Field (PBPC, ByteAcc, NoLock, Preserve) + { + Offset (0x3E), BCRL, 8, BCRH, 8 + } + + #include "acpi/scsi.asl" +} diff --git a/src/mainboard/aopen/dxplplusu/acpi/power.asl b/src/mainboard/aopen/dxplplusu/acpi/power.asl new file mode 100644 index 0000000000..903d844d42 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/acpi/power.asl @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + + +/* Board powers on with button or PME# from on-board GbE wake-on-lan. + * Board shuts down to S5/G2. Any other power management is untested. + */ + +Name (\_S0, Package () { 0x00, 0x00, 0x00, 0x00 }) +Name (\_S1, Package () { 0x01, 0x01, 0x00, 0x00 }) +Name (\_S3, Package () { 0x05, 0x05, 0x00, 0x00 }) +Name (\_S4, Package () { 0x06, 0x06, 0x00, 0x00 }) +Name (\_S5, Package () { 0x07, 0x07, 0x00, 0x00 }) + +Scope (\_GPE) +{ + Method (_L03, 0, NotSerialized) + { + Notify (\_SB.PCI0.USB0, 0x02) + } + Method (_L04, 0, NotSerialized) + { + Notify (\_SB.PCI0.USB1, 0x02) + } + + /* WOL header */ + Method (_L08, 0, NotSerialized) + { + Notify (\_SB.PCI0.PCI5, 0x02) + Notify (\_SB.SLBT, 0x02) + } + + /* PME# */ + Method (_L0B, 0, NotSerialized) + { + Notify (\_SB.LID0, 0x02) + } + + Method (_L0C, 0, NotSerialized) + { + Notify (\_SB.PCI0.USB2, 0x02) + } + + /* PME_B0_STS# */ + Method (_L0D, 0, NotSerialized) + { + Notify (\_SB.PCI0.USB3, 0x02) + } +} + +/* Clear power buttons */ +Method (\_INI, 0, NotSerialized) +{ + Or (\_SB.PCI0.ICH0.PS1H, 0x09, \_SB.PCI0.ICH0.PS1H) + Or (\_SB.PCI0.ICH0.PE1H, 0x01, \_SB.PCI0.ICH0.PE1H) +} + +/* Prepare To Sleep */ +Method (\_PTS, 1, NotSerialized) +{ + Or (\_SB.PCI0.ICH0.GS0H, 0x19, \_SB.PCI0.ICH0.GS0H) + Or (\_SB.PCI0.ICH0.GS0L, 0x11, \_SB.PCI0.ICH0.GS0L) +} + +/* System Wake */ +Method (\_WAK, 1, NotSerialized) +{ + Or (\_SB.PCI0.ICH0.GS0H, 0x19, \_SB.PCI0.ICH0.GS0H) + Or (\_SB.PCI0.ICH0.GS0L, 0x11, \_SB.PCI0.ICH0.GS0L) + + Return ( Package() { 0x0, 0x0 } ) +} diff --git a/src/mainboard/aopen/dxplplusu/acpi/scsi.asl b/src/mainboard/aopen/dxplplusu/acpi/scsi.asl new file mode 100644 index 0000000000..c34dd4ef0c --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/acpi/scsi.asl @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* PCI-X devices 04:04.0 and 04:04.1 : AIC-7902W + * U320 SCSI dual-channel controller + */ + +Device (SCS0) +{ + Name (_ADR, 0x00040000) + OperationRegion (SCSC, PCI_Config, 0x00, 0x0100) + Field (SCSC, ByteAcc, NoLock, Preserve) + { + Offset (0x2C), SID, 32, + Offset (0xE0), PMC, 8, + Offset (0xFF), IDW, 8 + } +} + +Device (SCS1) +{ + Name (_ADR, 0x00040001) + OperationRegion (SCSC, PCI_Config, 0x00, 0x0100) + Field (SCSC, ByteAcc, NoLock, Preserve) + { + Offset (0x2C), SID, 32, + Offset (0xE0), PMC, 8, + Offset (0xFF), IDW, 8 + } +} diff --git a/src/mainboard/aopen/dxplplusu/acpi/superio.asl b/src/mainboard/aopen/dxplplusu/acpi/superio.asl new file mode 100644 index 0000000000..b49ef444a1 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/acpi/superio.asl @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + + +/* SuperIO GPIO configuration via logical device 0x0A */ + +Name (MSBF, ResourceTemplate () +{ + IO (Decode16, 0x0000, 0x0000, 0x01, 0x80, _Y1B) +}) + +OperationRegion (LPC0, SystemIO, 0x0E00, 0x60) +Field (LPC0, ByteAcc, NoLock, Preserve) +{ + PME0, 8, + Offset (0x02), PME2,8, + Offset (0x04), PME4,8, + Offset (0x0A), PMEA,8, + Offset (0x23), + GC10,8, GC11,8, GC12,8, GC13,8, GC14,8, GC15,8, GC16,8, GC17,8, + GC20,8, GC21,8, GC22,8, GC23,8, GC24,8, GC25,8, GC26,8, GC27,8, + GC30,8, GC31,8, GC32,8, GC33,8, GC34,8, GC35,8, GC36,8, GC37,8, + GC40,8, GC41,8, GC42,8, GC43,8, + + Offset (0x3F), + GC50,8, GC51,8, GC52,8, GC53,8, GC54,8, GC55,8, GC56,8, GC57,8, + GC60,8, GC61,8, + + Offset (0x4B), + GP_1,8, GP_2,8, GP_3,8, GP_4,8, GP_5,8, GP_6,8, + Offset (0x56), FAN1,8, + Offset (0x5D), LED1,8, LED2,8, +} + +OperationRegion (SMC1, SystemIO, 0x2E, 0x02) +Field (SMC1, ByteAcc, NoLock, Preserve) +{ + INDX, 8, DATA, 8 +} + +IndexField (INDX, DATA, ByteAcc, NoLock, Preserve) +{ + Offset (0x07), LDN, 8, + Offset (0x22), PWRC, 8, + Offset (0x30), ACTR, 8, + Offset (0x60), + IOAH, 8, IOAL, 8, + IOBH, 8, IOBL, 8, + + Offset (0x70), INTR, 8, + Offset (0x72), INT1, 8, + Offset (0x74), DMCH, 8, + Offset (0xB2), SPS1, 8, SPS2, 8, + Offset (0xB8), D2TS, 8, + Offset (0xF0), OPT1, 8, OPT2, 8, OPT3, 8, + Offset (0xF4), WDTC, 8, + Offset (0xF6), GP01, 8, GP02, 8, GP04, 8 +} + +Method (ECFG, 0, NotSerialized) +{ + Store (0x55, INDX) +} +Method (XCFG, 0, NotSerialized) +{ + Store (0xAA, INDX) +} + +Method (_CRS, 0, NotSerialized) +{ + CreateWordField (MSBF, \_SB.PCI0.ICH0.SMSC._Y1B._MIN, IOM1) + CreateWordField (MSBF, \_SB.PCI0.ICH0.SMSC._Y1B._MAX, IOM2) + CreateByteField (MSBF, \_SB.PCI0.ICH0.SMSC._Y1B._LEN, IOML) + + ECFG () + Store (0x0A, \_SB.PCI0.ICH0.SMSC.LDN) + Store (0x00, IOM1) + Store (0x00, IOM2) + Or (\_SB.PCI0.ICH0.SMSC.IOAH, IOM1, IOM1) + ShiftLeft (IOM1, 0x08, IOM1) + Or (\_SB.PCI0.ICH0.SMSC.IOAL, IOM1, IOM1) + Store (IOM1, IOM2) + If (IOM1 != 0) + { + Store (0x80, IOML) + } + XCFG () + + Return (MSBF) +} + + +Method (_INI, 0, NotSerialized) +{ + /* GPIO configuration */ + Store (0x00, GC10) + Store (0x81, GC11) + Store (0x00, GC17) + Store (0x0c, GC21) + Store (0x00, GC22) + Store (0x04, GC27) + Store (0x04, GC30) + Store (0x01, GC31) + Store (0x01, GC32) + Store (0x01, GC33) + Store (0x01, GC34) /* GPI password jumper */ + Store (0x01, GC35) /* GPI scsi enable jumper */ + Store (0x01, GC42) /* GPI */ + Store (0x86, GC60) /* led 1 */ + Store (0x81, GC61) /* led 2 ?? */ + + /* GPIO initial output levels */ + Store (GP_1, Local0) + And( Local0, 0x7C, Local0) + Or ( Local0, 0x81, Local0) + Store (Local0, GP_1) + + Store (GP_2, Local0) + And( Local0, 0xFE, Local0) + Or ( Local0, 0x00, Local0) + Store (Local0, GP_2) + + Store (GP_3, Local0) + And( Local0, 0x7F, Local0) + Or ( Local0, 0x80, Local0) + Store (Local0, GP_3) + + Store (GP_4, Local0) + And( Local0, 0x7F, Local0) + Or ( Local0, 0x00, Local0) + Store (Local0, GP_4) + + /* Power Led */ + Store (LED1, Local0) + And( Local0, 0xfc, Local0) + Or ( Local0, 0x01, Local0) + Store (Local0, LED1) + +} + +Method (MLED, 1, NotSerialized) +{ + If (LEqual (Arg0, 0x00)) + { + Store (0x00, LED1) + } + + If (LEqual (Arg0, 0x01) || LEqual (Arg0, 0x02)) + { + Store (0x01, LED1) + } + + If (LEqual (Arg0, 0x03)) + { + Store (0x02, LED1) + } + + If (LEqual (Arg0, 0x04) || LEqual (Arg0, 0x05)) + { + Store (0x03, LED1) + } +} diff --git a/src/mainboard/aopen/dxplplusu/acpi_tables.c b/src/mainboard/aopen/dxplplusu/acpi_tables.c new file mode 100644 index 0000000000..7faa73d82d --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/acpi_tables.c @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Ported to Intel XE7501DEVKIT by Agami Aruma + * Ported to AOpen DXPL Plus-U by Kyösti Mälkki + */ + +#include <acpi/acpi.h> +#include <device/pci_def.h> + +#define IOAPIC_ICH4 2 +#define IOAPIC_P64H2_BUS_B 3 /* IOAPIC 3 at 02:1c.0 */ +#define IOAPIC_P64H2_BUS_A 4 /* IOAPIC 4 at 02:1e.0 */ + +#define INTEL_IOAPIC_NUM_INTERRUPTS 24 /* Both ICH-4 and P64-H2 */ + +unsigned long acpi_fill_madt(unsigned long current) +{ + unsigned int irq_start = 0; + struct device *bdev, *dev = NULL; + struct resource *res = NULL; + + /* SJM: Hard-code CPU LAPIC entries for now */ + current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, 0, 0); + current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, 1, 6); + current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, 2, 1); + current += acpi_create_madt_lapic((acpi_madt_lapic_t *)current, 3, 7); + + /* Southbridge IOAPIC */ + current += acpi_create_madt_ioapic((acpi_madt_ioapic_t *)current, IOAPIC_ICH4, + 0xfec00000, irq_start); + irq_start += INTEL_IOAPIC_NUM_INTERRUPTS; + + bdev = pcidev_on_root(2, 0); + /* P64H2 Bus B IOAPIC */ + if (bdev) + dev = pcidev_path_behind(bdev->link_list, PCI_DEVFN(28, 0)); + if (dev) { + res = find_resource(dev, PCI_BASE_ADDRESS_0); + current += acpi_create_madt_ioapic((acpi_madt_ioapic_t *)current, + IOAPIC_P64H2_BUS_B, res->base, irq_start); + irq_start += INTEL_IOAPIC_NUM_INTERRUPTS; + } + + /* P64H2 Bus A IOAPIC */ + if (bdev) + dev = pcidev_path_behind(bdev->link_list, PCI_DEVFN(30, 0)); + if (dev) { + res = find_resource(dev, PCI_BASE_ADDRESS_0); + current += acpi_create_madt_ioapic((acpi_madt_ioapic_t *)current, + IOAPIC_P64H2_BUS_A, res->base, irq_start); + irq_start += INTEL_IOAPIC_NUM_INTERRUPTS; + } + + /* Map ISA IRQ 0 to IRQ 2 */ + current += acpi_create_madt_irqoverride((acpi_madt_irqoverride_t *)current, 1, 0, 2, 0); + + /* IRQ9 differs from ISA standard - ours is active high, level-triggered */ + current += acpi_create_madt_irqoverride((acpi_madt_irqoverride_t *)current, 0, 9, 9, 0xD); + + return current; +} diff --git a/src/mainboard/aopen/dxplplusu/board_info.txt b/src/mainboard/aopen/dxplplusu/board_info.txt new file mode 100644 index 0000000000..4e50628327 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/board_info.txt @@ -0,0 +1,6 @@ +Category: server +Board URL: ftp://ftp.aopen.com/pub/server/motherboard/dxplpu/manual/dxplpu-ol-e.pdf +ROM package: PLCC +ROM protocol: FWH +ROM socketed: y +Flashrom support: y diff --git a/src/mainboard/aopen/dxplplusu/bootblock.c b/src/mainboard/aopen/dxplplusu/bootblock.c new file mode 100644 index 0000000000..5a969cfc13 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/bootblock.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <bootblock_common.h> +#include <superio/smsc/lpc47m10x/lpc47m10x.h> + +#define SERIAL_DEV PNP_DEV(0x2e, LPC47M10X2_SP1) + +void bootblock_mainboard_early_init(void) +{ + /* Get the serial port configured. */ + lpc47m10x_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE); +} diff --git a/src/mainboard/aopen/dxplplusu/devicetree.cb b/src/mainboard/aopen/dxplplusu/devicetree.cb new file mode 100644 index 0000000000..4f3102d328 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/devicetree.cb @@ -0,0 +1,67 @@ +## SPDX-License-Identifier: GPL-2.0-only + +chip northbridge/intel/e7505 + + device cpu_cluster 0 on + end + + device domain 0 on + device pci 0.0 on end # Chipset host controller + device pci 0.1 on end # Host RASUM controller + device pci 2.0 on # Hub interface B + chip southbridge/intel/i82870 # P64H2 + device pci 1c.0 on end # IOAPIC - bus B + device pci 1d.0 on end # Hub to PCI-B bridge + device pci 1e.0 on end # IOAPIC - bus A + device pci 1f.0 on end # Hub to PCI-A bridge + end + end + device pci 4.0 off end # (undocumented) + device pci 6.0 off end # (undocumented) + chip southbridge/intel/i82801dx + device pci 1d.0 on end # USB UHCI + device pci 1d.1 on end # USB UHCI + device pci 1d.2 on end # USB UHCI + device pci 1d.7 on end # USB EHCI + device pci 1e.0 on # Hub to PCI bridge + device pci 2.0 off end + end + device pci 1f.0 on # LPC bridge + chip superio/smsc/lpc47m10x + device pnp 2e.0 off # Floppy + io 0x60 = 0x3f0 + irq 0x70 = 6 + drq 0x74 = 2 + end + device pnp 2e.3 off # Parallel Port + io 0x60 = 0x378 + irq 0x70 = 7 + end + device pnp 2e.4 on # Com1 + io 0x60 = 0x3f8 + irq 0x70 = 4 + end + device pnp 2e.5 off # Com2 + io 0x60 = 0x2f8 + irq 0x70 = 3 + end + device pnp 2e.7 off # Keyboard + io 0x60 = 0x60 + io 0x62 = 0x64 + irq 0x70 = 1 # Keyboard interrupt + irq 0x72 = 12 # Mouse interrupt + end + device pnp 2e.a on # ACPI + io 0x60 = 0x0e00 + end + end + end + device pci 1f.1 on end # IDE + register "ide0_enable" = "1" + register "ide1_enable" = "1" + device pci 1f.3 on end # SMBus + device pci 1f.5 on end # AC97 Audio + device pci 1f.6 off end # AC97 Modem + end # SB + end # PCI domain +end diff --git a/src/mainboard/aopen/dxplplusu/dsdt.asl b/src/mainboard/aopen/dxplplusu/dsdt.asl new file mode 100644 index 0000000000..d00d1e5b41 --- /dev/null +++ b/src/mainboard/aopen/dxplplusu/dsdt.asl @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpi.h> + +DefinitionBlock( + "dsdt.aml", + "DSDT", + ACPI_DSDT_REV_2, + OEM_ID, + ACPI_TABLE_CREATOR, + 0x20111103 // OEM revision +) { + #include <acpi/dsdt_top.asl> + +Scope(\_SB) +{ + Device(PCI0) { + Name (_HID, EISAID("PNP0A03")) + Name (_PRT, Package() { + Package() { 0x001dffff, 0, 0, 16 }, + Package() { 0x001dffff, 1, 0, 19 }, + Package() { 0x001dffff, 2, 0, 18 }, + Package() { 0x001dffff, 3, 0, 23 }, + Package() { 0x001fffff, 0, 0, 18 }, + Package() { 0x001fffff, 1, 0, 17 }, + }) + + #include "acpi/e7505_sec.asl" + + OperationRegion (I750, PCI_Config, 0x00, 0x0100) + Field (I750, ByteAcc, NoLock, Preserve) + { + Offset (0xC4), + TOLM, 16, /* Top of Low Memory */ + RBAR, 16, /* REMAP_BASE */ + RLAR, 16 /* REMAP_LIMIT */ + } + } + + #include "acpi/e7505_pri.asl" + + + Device (PWBT) + { + Name (_HID, EisaId ("PNP0C0C")) + Name (_PRW, Package () { 0x08, 0x05 }) + } + + Device (SLBT) + { + Name (_HID, EisaId ("PNP0C0E")) + Name (_PRW, Package () { 0x0B, 0x05 }) + } + + Device (LID0) + { + Name (_HID, EisaId ("PNP0C0D")) + Name (_PRW, Package () { 0x0B, 0x05 }) + } + +} + +Scope(\_SB.PCI0) +{ + + Device(PCI1) + { + Name (_ADR, 0x00010000) + Name (_PRT, Package() { + Package() { 0x0000ffff, 0, 0, 16 }, + Package() { 0x0000ffff, 1, 0, 17 }, + }) + } + + Device(HLIB) + { + Name (_ADR, 0x00020000) + Name (_PRT, Package() { + Package() { 0x001dffff, 0, 0, 18 }, + Package() { 0x001dffff, 1, 0, 18 }, + Package() { 0x001dffff, 2, 0, 18 }, + Package() { 0x001dffff, 3, 0, 18 }, + Package() { 0x001fffff, 0, 0, 18 }, + Package() { 0x001fffff, 1, 0, 18 }, + Package() { 0x001fffff, 2, 0, 18 }, + Package() { 0x001fffff, 3, 0, 18 }, + }) + + #include "acpi/p64h2.asl" + } + + #include "acpi/i82801db.asl" +} + +#include "acpi/power.asl" + +} diff --git a/src/northbridge/intel/e7505/Kconfig b/src/northbridge/intel/e7505/Kconfig new file mode 100644 index 0000000000..4a54bc076d --- /dev/null +++ b/src/northbridge/intel/e7505/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config NORTHBRIDGE_INTEL_E7505 + bool + +if NORTHBRIDGE_INTEL_E7505 + +config NORTHBRIDGE_SPECIFIC_OPTIONS + def_bool y + select NO_ECAM_MMCONF_SUPPORT + select HAVE_DEBUG_RAM_SETUP + select NO_CBFS_MCACHE + +endif diff --git a/src/northbridge/intel/e7505/Makefile.inc b/src/northbridge/intel/e7505/Makefile.inc new file mode 100644 index 0000000000..29ac4379cf --- /dev/null +++ b/src/northbridge/intel/e7505/Makefile.inc @@ -0,0 +1,10 @@ +ifeq ($(CONFIG_NORTHBRIDGE_INTEL_E7505),y) + +ramstage-y += northbridge.c +ramstage-y += memmap.c + +romstage-y += romstage.c +romstage-y += raminit.c +romstage-y += memmap.c + +endif diff --git a/src/northbridge/intel/e7505/e7505.h b/src/northbridge/intel/e7505/e7505.h new file mode 100644 index 0000000000..b429c70ba4 --- /dev/null +++ b/src/northbridge/intel/e7505/e7505.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * e7505.h: PCI configuration space for the Intel E7501 memory controller + */ + +#ifndef NORTHBRIDGE_INTEL_E7505_E7505_H +#define NORTHBRIDGE_INTEL_E7505_E7505_H + +/************ D0:F0 ************/ +// Register offsets +#define SMRBASE 0x14 /* System Memory RCOMP Base Address Register, 32 bit? */ +#define MCHCFGNS 0x52 /* MCH (scrubber) configuration register, 16 bit */ + +#define PAM_0 0x59 + +#define DRB_ROW_0 0x60 /* DRAM Row Boundary register, 8 bit */ +#define DRB_ROW_1 0x61 +#define DRB_ROW_2 0x62 +#define DRB_ROW_3 0x63 +#define DRB_ROW_4 0x64 +#define DRB_ROW_5 0x65 +#define DRB_ROW_6 0x66 +#define DRB_ROW_7 0x67 + +#define DRA 0x70 /* DRAM Row Attributes registers, 4 x 8 bit */ +#define DRT 0x78 /* DRAM Timing register, 32 bit */ +#define DRC 0x7C /* DRAM Controller Mode register, 32 bit */ +#define DRDCTL 0x80 /* DRAM Read Timing Control register, 16 bit? (if similar to 855PM) */ +#define CKDIS 0x8C /* Clock disable register, 8 bit */ +#define SMRAMC 0x9D +#define ESMRAMC 0x9E +#define APSIZE 0xB4 +#define TOLM 0xC4 /* Top of Low Memory register, 16 bit */ +#define REMAPBASE 0xC6 /* Remap Base Address register, 16 bit */ +#define REMAPLIMIT 0xC8 /* Remap Limit Address register, 16 bit */ +#define SKPD 0xDE /* Scratchpad register, 16 bit */ +#define DVNP 0xE0 /* Device Not Present, 16 bit */ +#define MCHTST 0xF4 /* MCH Test Register, 32 bit? (if similar to 855PM) */ + +// CAS# Latency bits in the DRAM Timing (DRT) register +#define DRT_CAS_2_5 (0<<4) +#define DRT_CAS_2_0 (1<<4) +#define DRT_CAS_MASK (3<<4) + +// Mode Select (SMS) bits in the DRAM Controller Mode (DRC) register +#define RAM_COMMAND_NOP (1<<4) +#define RAM_COMMAND_PRECHARGE (2<<4) +#define RAM_COMMAND_MRS (3<<4) +#define RAM_COMMAND_EMRS (4<<4) +#define RAM_COMMAND_CBR (6<<4) +#define RAM_COMMAND_NORMAL (7<<4) + +#define DRC_DONE (1 << 29) + +// RCOMP Memory Map offsets +// Conjecture based on apparent similarity between E7501 and 855PM +// Intel doc. 252613-003 describes these for 855PM + +#define SMRCTL 0x20 /* System Memory RCOMP Control Register? */ +#define DQCMDSTR 0x30 /* Strength control for DQ and CMD signal groups? */ +#define CKESTR 0x31 /* Strength control for CKE signal group? */ +#define CSBSTR 0x32 /* Strength control for CS# signal group? */ +#define CKSTR 0x33 /* Strength control for CK signal group? */ +#define RCVENSTR 0x34 /* Strength control for RCVEnOut# signal group? */ + +/************ D0:F1 ************/ +// Register offsets +#define FERR_GLOBAL 0x40 /* First global error register, 32 bits */ +#define NERR_GLOBAL 0x44 /* Next global error register, 32 bits */ +#define DRAM_FERR 0x80 /* DRAM first error register, 8 bits */ +#define DRAM_NERR 0x82 /* DRAM next error register, 8 bits */ + +/************ D1:F0 ************/ + +#define APSIZE1 0x74 + +#endif /* NORTHBRIDGE_INTEL_E7505_E7505_H */ diff --git a/src/northbridge/intel/e7505/memmap.c b/src/northbridge/intel/e7505/memmap.c new file mode 100644 index 0000000000..b1ac3d1124 --- /dev/null +++ b/src/northbridge/intel/e7505/memmap.c @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +// Use simple device model for this file even in ramstage +#define __SIMPLE_DEVICE__ + +#include <device/pci_ops.h> +#include <arch/romstage.h> +#include <cbmem.h> +#include <cpu/x86/mtrr.h> +#include <program_loading.h> +#include "e7505.h" + +void *cbmem_top_chipset(void) +{ + const pci_devfn_t mch = PCI_DEV(0, 0, 0); + uintptr_t tolm; + + /* This is at 128 MiB boundary. */ + tolm = pci_read_config16(mch, TOLM) >> 11; + tolm <<= 27; + + return (void *)tolm; +} + +void northbridge_write_smram(u8 smram); + +void northbridge_write_smram(u8 smram) +{ + const pci_devfn_t mch = PCI_DEV(0, 0, 0); + pci_write_config8(mch, SMRAMC, smram); +} + +void fill_postcar_frame(struct postcar_frame *pcf) +{ + uintptr_t top_of_ram; + + /* + * Choose to NOT set ROM as WP cacheable here. + * Timestamps indicate the CPU this northbridge code is + * connected to, performs better for memcpy() and un-lzma + * operations when source is left as UC. + */ + + pcf->skip_common_mtrr = 1; + + /* Cache RAM as WB from 0 -> CACHE_TMP_RAMTOP. */ + postcar_frame_add_mtrr(pcf, 0, CACHE_TMP_RAMTOP, MTRR_TYPE_WRBACK); + + /* Cache CBMEM region as WB. */ + top_of_ram = (uintptr_t)cbmem_top(); + postcar_frame_add_mtrr(pcf, top_of_ram - 8*MiB, 8*MiB, + MTRR_TYPE_WRBACK); +} diff --git a/src/northbridge/intel/e7505/northbridge.c b/src/northbridge/intel/e7505/northbridge.c new file mode 100644 index 0000000000..08ac122243 --- /dev/null +++ b/src/northbridge/intel/e7505/northbridge.c @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <assert.h> +#include <console/console.h> +#include <device/pci_ops.h> +#include <device/device.h> +#include <device/pci.h> +#include <cpu/cpu.h> + +#include "e7505.h" + +static void mch_domain_read_resources(struct device *dev) +{ + int idx; + unsigned long tolmk; + uint64_t tom, remapbase, remaplimit; + struct device *mc_dev; + + pci_domain_read_resources(dev); + + mc_dev = pcidev_on_root(0, 0); + if (!mc_dev) + die("Could not find MCH device\n"); + + tolmk = pci_read_config16(mc_dev, TOLM) >> 11; + tolmk <<= 17; + + tom = pci_read_config8(mc_dev, DRB_ROW_7); + tom <<= 26; + + /* Remapped region with a 64 MiB granularity in register + definition. Limit is inclusive, so add one. */ + remapbase = pci_read_config16(mc_dev, REMAPBASE) & 0x3ff; + remapbase <<= 26; + + remaplimit = pci_read_config16(mc_dev, REMAPLIMIT) & 0x3ff; + remaplimit += 1; + remaplimit <<= 26; + + /* Report the memory regions */ + idx = 10; + ram_resource_kb(dev, idx++, 0, tolmk); + mmio_resource_kb(dev, idx++, 0xa0000 / KiB, (0xc0000 - 0xa0000) / KiB); + + + ASSERT(tom == remapbase); + upper_ram_end(dev, idx++, remaplimit); +} + +static void mch_domain_set_resources(struct device *dev) +{ + assign_resources(dev->link_list); +} + +static struct device_operations pci_domain_ops = { + .read_resources = mch_domain_read_resources, + .set_resources = mch_domain_set_resources, + .scan_bus = pci_domain_scan_bus, + .ops_pci = &pci_dev_ops_pci, +}; + + +static struct device_operations cpu_bus_ops = { + .read_resources = noop_read_resources, + .set_resources = noop_set_resources, + .init = mp_cpu_bus_init, +}; + +static void enable_dev(struct device *dev) +{ + /* Set the operations if it is a special bus type */ + if (dev->path.type == DEVICE_PATH_DOMAIN) { + dev->ops = &pci_domain_ops; + } else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) { + dev->ops = &cpu_bus_ops; + } +} + +struct chip_operations northbridge_intel_e7505_ops = { + CHIP_NAME("Intel E7505 Northbridge") + .enable_dev = enable_dev, +}; diff --git a/src/northbridge/intel/e7505/raminit.c b/src/northbridge/intel/e7505/raminit.c new file mode 100644 index 0000000000..5387d4df04 --- /dev/null +++ b/src/northbridge/intel/e7505/raminit.c @@ -0,0 +1,1718 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* This was originally for the e7500, modified for e7501 + * The primary differences are that 7501 apparently can + * support single channel RAM (i haven't tested), + * CAS1.5 is no longer supported, The ECC scrubber + * now supports a mode to zero RAM and init ECC in one step + * and the undocumented registers at 0x80 require new + * (undocumented) values determined by guesswork and + * comparison w/ OEM BIOS values. + * Steven James 02/06/2003 + */ + +#include <stdint.h> +#include <device/pci_def.h> +#include <arch/io.h> +#include <device/mmio.h> +#include <device/pci_ops.h> +#include <device/smbus_host.h> +#include <lib.h> +#include <commonlib/helpers.h> +#include <console/console.h> +#include <assert.h> +#include <spd.h> +#include <sdram_mode.h> +#include <timestamp.h> + +#include "raminit.h" +#include "e7505.h" + +/*----------------------------------------------------------------------------- +Definitions: +-----------------------------------------------------------------------------*/ + +#if CONFIG(DEBUG_RAM_SETUP) +#define RAM_DEBUG_MESSAGE(x) printk(BIOS_DEBUG, x) +#define RAM_DEBUG_HEX32(x) printk(BIOS_DEBUG, "%08x", x) +#define RAM_DEBUG_HEX8(x) printk(BIOS_DEBUG, "%02x", x) +#else +#define RAM_DEBUG_MESSAGE(x) +#define RAM_DEBUG_HEX32(x) +#define RAM_DEBUG_HEX8(x) +#endif + +#define E7501_SDRAM_MODE (SDRAM_BURST_INTERLEAVED | SDRAM_BURST_4) +#define SPD_ERROR "Error reading SPD info\n" + +#define MCHDEV PCI_DEV(0, 0, 0) +#define RASDEV PCI_DEV(0, 0, 1) +#define AGPDEV PCI_DEV(0, 1, 0) +#define D060DEV PCI_DEV(0, 6, 0) + +// NOTE: This used to be 0x100000. +// That doesn't work on systems where A20M# is asserted, because +// attempts to access 0x1000NN end up accessing 0x0000NN. +#define RCOMP_MMIO ((u8 *)0x200000) + +struct dimm_size { + unsigned long side1; + unsigned long side2; +}; + +static const uint32_t refresh_frequency[] = { + /* Relative frequency (array value) of each E7501 Refresh Mode Select + * (RMS) value (array index) + * 0 == least frequent refresh (longest interval between refreshes) + * [0] disabled -> 0 + * [1] 15.6 usec -> 2 + * [2] 7.8 usec -> 3 + * [3] 64 usec -> 1 + * [4] reserved -> 0 + * [5] reserved -> 0 + * [6] reserved -> 0 + * [7] 64 clocks -> 4 + */ + 0, 2, 3, 1, 0, 0, 0, 4 +}; + +static const uint32_t refresh_rate_map[] = { + /* Map the JEDEC spd refresh rates (array index) to E7501 Refresh Mode + * Select values (array value) + * These are all the rates defined by JESD21-C Appendix D, Rev. 1.0 + * The E7501 supports only 15.6 us (1), 7.8 us (2), 64 us (3), and + * 64 clock (481 ns) (7) refresh. + * [0] == 15.625 us -> 15.6 us + * [1] == 3.9 us -> 481 ns + * [2] == 7.8 us -> 7.8 us + * [3] == 31.3 us -> 15.6 us + * [4] == 62.5 us -> 15.6 us + * [5] == 125 us -> 64 us + */ + 1, 7, 2, 1, 1, 3 +}; + +#define MAX_SPD_REFRESH_RATE (ARRAY_SIZE(refresh_rate_map) - 1) + +// SPD parameters that must match for dual-channel operation +static const uint8_t dual_channel_parameters[] = { + SPD_MEMORY_TYPE, + SPD_MODULE_VOLTAGE, + SPD_NUM_COLUMNS, + SPD_NUM_ROWS, + SPD_NUM_DIMM_BANKS, + SPD_PRIMARY_SDRAM_WIDTH, + SPD_NUM_BANKS_PER_SDRAM +}; + + /* Comments here are remains of e7501 or even 855PM. + * They might be partially (in)correct for e7505. + */ + + /* (DRAM Read Timing Control, if similar to 855PM?) + * 0x80 - 0x81 documented differently for e7505 + * This register has something to do with CAS latencies, + * possibly this is the real chipset control. + * At 0x00 CAS latency 1.5 works. + * At 0x06 CAS latency 2.5 works. + * At 0x01 CAS latency 2.0 works. + * + * This is still undocumented in e7501, but with different values + * CAS 2.0 values taken from Intel BIOS settings, others are a guess + * and may be terribly wrong. Old values preserved as comments until I + * figure this out for sure. + * e7501 docs claim that CAS1.5 is unsupported, so it may or may not + * work at all. + * Steven James 02/06/2003 + * + * NOTE: values now configured in configure_e7501_cas_latency() based + * on SPD info and total number of DIMMs (per Intel) + */ + + /* FDHC - Fixed DRAM Hole Control ??? + * 0x58 undocumented for e7505, memory hole in southbridge configuration? + * [7:7] Hole_Enable + * 0 == No memory Hole + * 1 == Memory Hole from 15MB to 16MB + * [6:0] Reserved + */ + + /* Another Intel undocumented register + * 0x88 - 0x8B + * [31:31] Purpose unknown + * [26:26] Master DLL Reset? + * 0 == Normal operation? + * 1 == Reset? + * [07:07] Periodic memory recalibration? + * 0 == Disabled? + * 1 == Enabled? + * [04:04] Receive FIFO RE-Sync? + * 0 == Normal operation? + * 1 == Reset? + */ + +/* DDR RECOMP tables */ +// Slew table for 2x drive? +static const uint32_t slew_2x[] = { + 0x00000000, 0x76543210, 0xffffeca8, 0xffffffff, + 0x21000000, 0xa8765432, 0xffffffec, 0xffffffff, +}; + +// Pull Up / Pull Down offset table, if analogous to IXP2800? +static const uint32_t pull_updown_offset_table[] = { + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0x88888888, 0x88888888, 0x88888888, 0x88888888, +}; + +/*----------------------------------------------------------------------------- +Delay functions: +-----------------------------------------------------------------------------*/ + +/* Estimate that SLOW_DOWN_IO takes about 1 us */ +#define SLOW_DOWN_IO inb(0x80) +static void local_udelay(int i) +{ + while (i--) { + SLOW_DOWN_IO; + } +} + +/* delay for 200us */ +#define DO_DELAY local_udelay(200) +#define EXTRA_DELAY DO_DELAY + +/*----------------------------------------------------------------------------- +Handle (undocumented) control bits MCHTST and PCI_DEV(0,6,0) +-----------------------------------------------------------------------------*/ +typedef enum { + MCHTST_CMD_0, + D060_ENABLE, + D060_DISABLE, + RCOMP_BAR_ENABLE, + RCOMP_BAR_DISABLE, +} mchtst_cc; + +typedef enum { + D060_CMD_0, + D060_CMD_1, +} d060_cc; + +typedef enum { + RCOMP_HOLD, + RCOMP_RELEASE, + RCOMP_SMR_00, + RCOMP_SMR_01, +} rcomp_smr_cc; + +/** + * MCHTST - 0xF4 - 0xF7 -- Based on similarity to 855PM + * + * [31:31] Purpose unknown + * [30:30] Purpose unknown + * [29:23] Unknown - not used? + * [22:22] System Memory MMR Enable + * 0 == Disable: mem space and BAR at 0x14 are not accessible + * 1 == Enable: mem space and BAR at 0x14 are accessible + * [21:20] Purpose unknown + * [19:02] Unknown - not used? + * [01:01] D6EN (Device #6 enable) + * 0 == Disable + * 1 == Enable + * [00:00] Unknown - not used? + */ +static void mchtest_control(mchtst_cc cmd) +{ + uint32_t dword = pci_read_config32(MCHDEV, MCHTST); + switch (cmd) { + case MCHTST_CMD_0: + dword &= ~(3 << 30); + break; + case RCOMP_BAR_ENABLE: + dword |= (1 << 22); + break; + case RCOMP_BAR_DISABLE: + dword &= ~(1 << 22); + break; + case D060_ENABLE: + dword |= (1 << 1); + break; + case D060_DISABLE: + dword &= ~(1 << 1); + break; + }; + pci_write_config32(MCHDEV, MCHTST, dword); +} + +/** + * + */ +static void d060_control(d060_cc cmd) +{ + mchtest_control(D060_ENABLE); + uint32_t dword = pci_read_config32(D060DEV, 0xf0); + switch (cmd) { + case D060_CMD_0: + dword |= (1 << 2); + break; + case D060_CMD_1: + dword |= (3 << 27); + break; + } + pci_write_config32(D060DEV, 0xf0, dword); + mchtest_control(D060_DISABLE); +} + +/** + * + */ +static void rcomp_smr_control(rcomp_smr_cc cmd) +{ + uint32_t dword = read32(RCOMP_MMIO + SMRCTL); + switch (cmd) { + case RCOMP_HOLD: + dword |= (1 << 9); + break; + case RCOMP_RELEASE: + dword &= ~((1 << 9) | (3 << 0)); + dword |= (1 << 10) | (1 << 0); + break; + case RCOMP_SMR_00: + dword &= ~(1 << 8); + break; + case RCOMP_SMR_01: + dword |= (1 << 10) | (1 << 8); + break; + } + write32(RCOMP_MMIO + SMRCTL, dword); +} + +/*----------------------------------------------------------------------------- +Serial presence detect (SPD) functions: +-----------------------------------------------------------------------------*/ + +static void die_on_spd_error(int spd_return_value) +{ + if (spd_return_value < 0) + die("Error reading SPD info\n"); +} + +/** + * Calculate the page size for each physical bank of the DIMM: + * log2(page size) = (# columns) + log2(data width) + * + * NOTE: Page size is the total number of data bits in a row. + * + * @param dimm_socket_address SMBus address of DIMM socket to interrogate. + * @return log2(page size) for each side of the DIMM. + */ +static struct dimm_size sdram_spd_get_page_size(uint16_t dimm_socket_address) +{ + uint16_t module_data_width; + int value; + struct dimm_size pgsz; + + pgsz.side1 = 0; + pgsz.side2 = 0; + + // Side 1 + value = smbus_read_byte(dimm_socket_address, SPD_NUM_COLUMNS); + if (value < 0) + goto hw_err; + pgsz.side1 = value & 0xf; // # columns in bank 1 + + /* Get the module data width and convert it to a power of two */ + value = smbus_read_byte(dimm_socket_address, SPD_MODULE_DATA_WIDTH_MSB); + if (value < 0) + goto hw_err; + module_data_width = (value & 0xff) << 8; + + value = smbus_read_byte(dimm_socket_address, SPD_MODULE_DATA_WIDTH_LSB); + if (value < 0) + goto hw_err; + module_data_width |= (value & 0xff); + + pgsz.side1 += log2(module_data_width); + + /* side two */ + value = smbus_read_byte(dimm_socket_address, SPD_NUM_DIMM_BANKS); + if (value < 0) + goto hw_err; + if (value > 2) + die("Bad SPD value\n"); + if (value == 2) { + + pgsz.side2 = pgsz.side1; // Assume symmetric banks until we know differently + value = smbus_read_byte(dimm_socket_address, SPD_NUM_COLUMNS); + if (value < 0) + goto hw_err; + if ((value & 0xf0) != 0) { + // Asymmetric banks + pgsz.side2 -= value & 0xf; /* Subtract out columns on side 1 */ + pgsz.side2 += (value >> 4) & 0xf; /* Add in columns on side 2 */ + } + } + + return pgsz; + +hw_err: + die(SPD_ERROR); + return pgsz; // Never reached +} + +/** + * Read the width in bits of each DIMM side's DRAMs via SPD (i.e. 4, 8, 16). + * + * @param dimm_socket_address SMBus address of DIMM socket to interrogate. + * @return Width in bits of each DIMM side's DRAMs. + */ +static struct dimm_size sdram_spd_get_width(uint16_t dimm_socket_address) +{ + int value; + struct dimm_size width; + + width.side1 = 0; + width.side2 = 0; + + value = smbus_read_byte(dimm_socket_address, SPD_PRIMARY_SDRAM_WIDTH); + die_on_spd_error(value); + + width.side1 = value & 0x7f; // Mask off bank 2 flag + + if (value & 0x80) { + width.side2 = width.side1 << 1; // Bank 2 exists and is double-width + } else { + // If bank 2 exists, it's the same width as bank 1 + value = smbus_read_byte(dimm_socket_address, SPD_NUM_DIMM_BANKS); + die_on_spd_error(value); + + if (value == 2) + width.side2 = width.side1; + } + + return width; +} + +/** + * Calculate the log base 2 size in bits of both DIMM sides. + * + * log2(# bits) = (# columns) + log2(data width) + + * (# rows) + log2(banks per SDRAM) + * + * Note that it might be easier to use SPD byte 31 here, it has the DIMM size + * as a multiple of 4MB. The way we do it now we can size both sides of an + * asymmetric DIMM. + * + * @param dimm_socket_address SMBus address of DIMM socket to interrogate. + * @return log2(number of bits) for each side of the DIMM. + */ +static struct dimm_size spd_get_dimm_size(unsigned int dimm_socket_address) +{ + int value; + + // Start with log2(page size) + struct dimm_size sz = sdram_spd_get_page_size(dimm_socket_address); + + if (sz.side1 > 0) { + + value = smbus_read_byte(dimm_socket_address, SPD_NUM_ROWS); + die_on_spd_error(value); + + sz.side1 += value & 0xf; + + if (sz.side2 > 0) { + + // Double-sided DIMM + if (value & 0xF0) + sz.side2 += value >> 4; // Asymmetric + else + sz.side2 += value; // Symmetric + } + + value = smbus_read_byte(dimm_socket_address, + SPD_NUM_BANKS_PER_SDRAM); + die_on_spd_error(value); + + value = log2(value); + sz.side1 += value; + if (sz.side2 > 0) + sz.side2 += value; + } + + return sz; +} + +/** + * Determine whether two DIMMs have the same value for an SPD parameter. + * + * @param spd_byte_number The SPD byte number to compare in both DIMMs. + * @param dimm0_address SMBus address of the 1st DIMM socket to interrogate. + * @param dimm1_address SMBus address of the 2nd DIMM socket to interrogate. + * @return 1 if both DIMM sockets report the same value for the specified + * SPD parameter, 0 if the values differed or an error occurred. + */ +static uint8_t are_spd_values_equal(uint8_t spd_byte_number, + uint16_t dimm0_address, + uint16_t dimm1_address) +{ + uint8_t bEqual = 0; + int dimm0_value = smbus_read_byte(dimm0_address, spd_byte_number); + int dimm1_value = smbus_read_byte(dimm1_address, spd_byte_number); + + if ((dimm0_value >= 0) && (dimm1_value >= 0) + && (dimm0_value == dimm1_value)) + bEqual = 1; + + return bEqual; +} + +/** + * Scan for compatible DIMMs. + * + * The code in this module only supports dual-channel operation, so we test + * that compatible DIMMs are paired. + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + * @return A bitmask indicating which of the possible sockets for each channel + * was found to contain a compatible DIMM. + * Bit 0 corresponds to the closest socket for channel 0 + * Bit 1 to the next socket for channel 0 + * ... + * Bit MAX_DIMM_SOCKETS_PER_CHANNEL-1 to the last socket for channel 0 + * Bit MAX_DIMM_SOCKETS_PER_CHANNEL is the closest socket for channel 1 + * ... + * Bit 2*MAX_DIMM_SOCKETS_PER_CHANNEL-1 is the last socket for channel 1 + */ +static uint8_t spd_get_supported_dimms(const struct mem_controller *ctrl) +{ + int i; + uint8_t dimm_mask = 0; + + // Have to increase size of dimm_mask if this assertion is violated + ASSERT(MAX_DIMM_SOCKETS_PER_CHANNEL <= 4); + + // Find DIMMs we can support on channel 0. + // Then see if the corresponding channel 1 DIMM has the same parameters, + // since we only support dual-channel. + + for (i = 0; i < MAX_DIMM_SOCKETS_PER_CHANNEL; i++) { + + uint16_t channel0_dimm = ctrl->channel0[i]; + uint16_t channel1_dimm = ctrl->channel1[i]; + uint8_t bDualChannel = 1; + struct dimm_size page_size; + struct dimm_size sdram_width; + int spd_value; + + if (channel0_dimm == 0) + continue; // No such socket on this mainboard + + if (smbus_read_byte(channel0_dimm, SPD_MEMORY_TYPE) != + SPD_MEMORY_TYPE_SDRAM_DDR) + continue; + + if (smbus_read_byte(channel0_dimm, SPD_MODULE_VOLTAGE) != + SPD_VOLTAGE_SSTL2) + continue; // Unsupported voltage + + // E7501 does not support unregistered DIMMs + spd_value = smbus_read_byte(channel0_dimm, SPD_MODULE_ATTRIBUTES); + if (!(spd_value & MODULE_REGISTERED) || (spd_value < 0)) + continue; + + // Must support burst = 4 for dual-channel operation on E7501 + // NOTE: for single-channel, burst = 8 is required + spd_value = smbus_read_byte(channel0_dimm, + SPD_SUPPORTED_BURST_LENGTHS); + if (!(spd_value & SPD_BURST_LENGTH_4) || (spd_value < 0)) + continue; + + page_size = sdram_spd_get_page_size(channel0_dimm); + sdram_width = sdram_spd_get_width(channel0_dimm); + + // Validate DIMM page size + // The E7501 only supports page sizes of 4, 8, 16, or 32 KB per channel + // NOTE: 4 KB = 32 Kb = 2^15 + // 32 KB = 262 Kb = 2^18 + + if ((page_size.side1 < 15) || (page_size.side1 > 18)) + continue; + + // If DIMM is double-sided, verify side2 page size + if (page_size.side2 != 0) { + if ((page_size.side2 < 15) + || (page_size.side2 > 18)) + continue; + } + // Validate SDRAM width + // The E7501 only supports x4 and x8 devices + + if ((sdram_width.side1 != 4) && (sdram_width.side1 != 8)) + continue; + + // If DIMM is double-sided, verify side2 width + if (sdram_width.side2 != 0) { + if ((sdram_width.side2 != 4) + && (sdram_width.side2 != 8)) + continue; + } + + // Channel 0 DIMM looks compatible. + // Now see if it is paired with the proper DIMM on channel 1. + + ASSERT(channel1_dimm != 0); // No such socket on this mainboard?? + + // NOTE: unpopulated DIMMs cause read to fail + spd_value = smbus_read_byte(channel1_dimm, SPD_MODULE_ATTRIBUTES); + if (!(spd_value & MODULE_REGISTERED) || (spd_value < 0)) { + + printk(BIOS_DEBUG, "Skipping un-matched DIMMs - only dual-channel operation supported\n"); + continue; + } + + spd_value = smbus_read_byte(channel1_dimm, + SPD_SUPPORTED_BURST_LENGTHS); + if (!(spd_value & SPD_BURST_LENGTH_4) || (spd_value < 0)) + continue; + + int j; + for (j = 0; j < sizeof(dual_channel_parameters); ++j) { + if (!are_spd_values_equal + (dual_channel_parameters[j], channel0_dimm, + channel1_dimm)) { + + bDualChannel = 0; + break; + } + } + + if (bDualChannel) { + // This DIMM pair is usable + dimm_mask |= 1 << i; + dimm_mask |= 1 << (MAX_DIMM_SOCKETS_PER_CHANNEL + i); + } else + printk(BIOS_DEBUG, "Skipping un-matched DIMMs - only dual-channel operation supported\n"); + } + + return dimm_mask; +} + +/*----------------------------------------------------------------------------- +SDRAM configuration functions: +-----------------------------------------------------------------------------*/ + +/** + * Send the specified command to all DIMMs. + * + * @param command Specifies the command to be sent to the DIMMs. + * @param jedec_mode_bits For the MRS & EMRS commands, bits 0-12 contain the + * register value in JEDEC format. + */ +static void do_ram_command(uint8_t command, uint16_t jedec_mode_bits) +{ + uint8_t dimm_start_64M_multiple; + uintptr_t dimm_start_address; + uint32_t dram_controller_mode; + uint8_t i; + + // Configure the RAM command + dram_controller_mode = pci_read_config32(MCHDEV, DRC); + dram_controller_mode &= 0xFFFFFF8F; + dram_controller_mode |= command; + pci_write_config32(MCHDEV, DRC, dram_controller_mode); + + // RAM_COMMAND_NORMAL is an exception. + // It affects only the memory controller and does not need to be "sent" to the DIMMs. + if (command == RAM_COMMAND_NORMAL) { + EXTRA_DELAY; + return; + } + + // NOTE: for mode select commands, some of the location address bits are part of the command + // Map JEDEC mode bits to E7505 + if (command == RAM_COMMAND_MRS) { + // Host address lines [25:18] map to DIMM address lines [7:0] + // Host address lines [17:16] map to DIMM address lines [9:8] + // Host address lines [15:4] map to DIMM address lines [11:0] + dimm_start_address = (jedec_mode_bits & 0x00ff) << 18; + dimm_start_address |= (jedec_mode_bits & 0x0300) << 8; + dimm_start_address |= (jedec_mode_bits & 0x0fff) << 4; + } else if (command == RAM_COMMAND_EMRS) { + // Host address lines [15:4] map to DIMM address lines [11:0] + dimm_start_address = (jedec_mode_bits << 4); + } else { + ASSERT(jedec_mode_bits == 0); + dimm_start_address = 0; + } + + // Send the command to all DIMMs by accessing a memory location within each + + dimm_start_64M_multiple = 0; + + /* FIXME: Only address the number of rows present in the system? + * Seems like rows 4-7 overlap with 0-3. + */ + for (i = 0; i < (MAX_NUM_CHANNELS * MAX_DIMM_SOCKETS_PER_CHANNEL); ++i) { + + uint8_t dimm_end_64M_multiple = pci_read_config8(MCHDEV, DRB_ROW_0 + i); + + if (dimm_end_64M_multiple > dimm_start_64M_multiple) { + dimm_start_address &= 0x3ffffff; + dimm_start_address |= dimm_start_64M_multiple << 26; + read32((void *)dimm_start_address); + // Set the start of the next DIMM + dimm_start_64M_multiple = dimm_end_64M_multiple; + } + } + EXTRA_DELAY; +} + +/** + * Set the mode register of all DIMMs. + * + * The proper CAS# latency setting is added to the mode bits specified + * by the caller. + * + * @param jedec_mode_bits For the MRS & EMRS commands, bits 0-12 contain the + * register value in JEDEC format. + */ +static void set_ram_mode(uint16_t jedec_mode_bits) +{ + ASSERT(!(jedec_mode_bits & SDRAM_CAS_MASK)); + + uint32_t dram_cas_latency = + pci_read_config32(MCHDEV, DRT) & DRT_CAS_MASK; + + switch (dram_cas_latency) { + case DRT_CAS_2_5: + jedec_mode_bits |= SDRAM_CAS_2_5; + break; + + case DRT_CAS_2_0: + jedec_mode_bits |= SDRAM_CAS_2_0; + break; + + default: + BUG(); + break; + } + + do_ram_command(RAM_COMMAND_MRS, jedec_mode_bits); +} + +/*----------------------------------------------------------------------------- +DIMM-independent configuration functions: +-----------------------------------------------------------------------------*/ + +/** + * Configure the E7501's DRAM Row Boundary (DRB) registers for the memory + * present in the specified DIMM. + * + * @param dimm_log2_num_bits Specifies log2(number of bits) for each side of + * the DIMM. + * @param total_dram_64M_multiple Total DRAM in the system (as a multiple of + * 64 MB) for DIMMs < dimm_index. + * @param dimm_index Which DIMM pair is being processed + * (0..MAX_DIMM_SOCKETS_PER_CHANNEL). + * @return New multiple of 64 MB total DRAM in the system. + */ +static uint8_t configure_dimm_row_boundaries(struct dimm_size dimm_log2_num_bits, uint8_t total_dram_64M_multiple, unsigned int dimm_index) +{ + int i; + + ASSERT(dimm_index < MAX_DIMM_SOCKETS_PER_CHANNEL); + + // DIMM sides must be at least 32 MB + ASSERT(dimm_log2_num_bits.side1 >= 28); + ASSERT((dimm_log2_num_bits.side2 == 0) + || (dimm_log2_num_bits.side2 >= 28)); + + // In dual-channel mode, we are called only once for each pair of DIMMs. + // Each time we process twice the capacity of a single DIMM. + + // Convert single DIMM capacity to paired DIMM capacity + // (multiply by two ==> add 1 to log2) + dimm_log2_num_bits.side1++; + if (dimm_log2_num_bits.side2 > 0) + dimm_log2_num_bits.side2++; + + // Add the capacity of side 1 this DIMM pair (as a multiple of 64 MB) + // to the total capacity of the system + // NOTE: 64 MB == 512 Mb, and log2(512 Mb) == 29 + + total_dram_64M_multiple += (1 << (dimm_log2_num_bits.side1 - 29)); + + // Configure the boundary address for the row on side 1 + pci_write_config8(MCHDEV, DRB_ROW_0 + (dimm_index << 1), + total_dram_64M_multiple); + + // If the DIMMs are double-sided, add the capacity of side 2 this DIMM pair + // (as a multiple of 64 MB) to the total capacity of the system + if (dimm_log2_num_bits.side2 >= 29) + total_dram_64M_multiple += + (1 << (dimm_log2_num_bits.side2 - 29)); + + // Configure the boundary address for the row (if any) on side 2 + pci_write_config8(MCHDEV, DRB_ROW_1 + (dimm_index << 1), + total_dram_64M_multiple); + + // Update boundaries for rows subsequent to these. + // These settings will be overridden by a subsequent call if a populated physical slot exists + + for (i = dimm_index + 1; i < MAX_DIMM_SOCKETS_PER_CHANNEL; i++) { + pci_write_config8(MCHDEV, DRB_ROW_0 + (i << 1), + total_dram_64M_multiple); + pci_write_config8(MCHDEV, DRB_ROW_1 + (i << 1), + total_dram_64M_multiple); + } + + return total_dram_64M_multiple; +} + +/** + * Set the E7501's DRAM row boundary addresses & its Top Of Low Memory (TOLM). + * + * If necessary, set up a remap window so we don't waste DRAM that ordinarily + * would lie behind addresses reserved for memory-mapped I/O. + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + * @param dimm_mask Bitmask of populated DIMMs, see spd_get_supported_dimms(). + */ +static void configure_e7501_ram_addresses(const struct mem_controller + *ctrl, uint8_t dimm_mask) +{ + int i; + uint8_t total_dram_64M_multiple = 0; + uint64_t tolm, tom; + uint16_t reg; + + /* FIXME: Is there standard presence detect bit somewhere. */ + const int agp_slot_disabled = 1; + + /* Start with disabled remap range. */ + uint16_t remapbase_r = 0x3ff; + uint16_t remaplimit_r = 0; + + // Configure the E7501's DRAM row boundaries + // Start by zeroing out the temporary initial configuration + pci_write_config32(MCHDEV, DRB_ROW_0, 0); + pci_write_config32(MCHDEV, DRB_ROW_4, 0); + + for (i = 0; i < MAX_DIMM_SOCKETS_PER_CHANNEL; i++) { + + uint16_t dimm_socket_address = ctrl->channel0[i]; + struct dimm_size sz; + + if (!(dimm_mask & (1 << i))) + continue; // This DIMM not present + + sz = spd_get_dimm_size(dimm_socket_address); + + RAM_DEBUG_MESSAGE("dimm size ="); + RAM_DEBUG_HEX32((u32)sz.side1); + RAM_DEBUG_MESSAGE(" "); + RAM_DEBUG_HEX32((u32)sz.side2); + RAM_DEBUG_MESSAGE("\n"); + + if (sz.side1 == 0) + die("Bad SPD value\n"); + + total_dram_64M_multiple = + configure_dimm_row_boundaries(sz, total_dram_64M_multiple, i); + } + + tom = total_dram_64M_multiple * 64ULL * MiB; + + /* Reserve MMIO space. */ + tolm = 4ULL * GiB - 512 * MiB; + if (agp_slot_disabled) { + /* Reduce apertures to 2 x 4 MiB. */ + pci_write_config8(MCHDEV, APSIZE, 0x3F); + pci_write_config16(AGPDEV, APSIZE1, 0x3F); + } else { + /* Add MMIO reserve for 2 x 256 MiB apertures. */ + tolm -= 512 * MiB; + } + tolm = MIN(tolm, tom); + + /* The PCI memory hole overlaps memory setup the remap window. */ + if (tolm < tom) { + uint64_t remapbase = MAX(tom, 4ULL * GiB); + uint64_t remaplimit = remapbase + (4ULL * GiB - tolm); + + remapbase_r = remapbase / (64 * MiB); + remaplimit_r = remaplimit / (64 * MiB); + + /* Limit register is inclusive. */ + remaplimit_r -= 1; + } + + /* Write the RAM configuration registers, + preserving the reserved bits. */ + reg = pci_read_config16(MCHDEV, TOLM) & 0x7ff; + reg |= (tolm / (128 * MiB)) << 11; + pci_write_config16(MCHDEV, TOLM, reg); + + reg = pci_read_config16(MCHDEV, REMAPBASE) & 0xfc00; + reg |= remapbase_r; + pci_write_config16(MCHDEV, REMAPBASE, reg); + + reg = pci_read_config16(MCHDEV, REMAPLIMIT) & 0xfc00; + reg |= remaplimit_r; + pci_write_config16(MCHDEV, REMAPLIMIT, reg); +} + +/** + * Program the DRAM Timing register (DRT) of the E7501 (except for CAS# + * latency, which is assumed to have been programmed already), based on the + * parameters of the various installed DIMMs. + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + * @param dimm_mask Bitmask of populated DIMMs, see spd_get_supported_dimms(). + */ +static void configure_e7501_dram_timing(const struct mem_controller *ctrl, + uint8_t dimm_mask) +{ + int i; + uint32_t dram_timing; + int value; + uint8_t slowest_row_precharge = 0; + uint8_t slowest_ras_cas_delay = 0; + uint8_t slowest_active_to_precharge_delay = 0; + uint32_t current_cas_latency = + pci_read_config32(MCHDEV, DRT) & DRT_CAS_MASK; + + // CAS# latency must be programmed beforehand + ASSERT((current_cas_latency == DRT_CAS_2_0) + || (current_cas_latency == DRT_CAS_2_5)); + + // Each timing parameter is determined by the slowest DIMM + + for (i = 0; i < MAX_DIMM_SOCKETS; i++) { + uint16_t dimm_socket_address; + + if (!(dimm_mask & (1 << i))) + continue; // This DIMM not present + + if (i < MAX_DIMM_SOCKETS_PER_CHANNEL) + dimm_socket_address = ctrl->channel0[i]; + else + dimm_socket_address = + ctrl->channel1[i - MAX_DIMM_SOCKETS_PER_CHANNEL]; + + value = smbus_read_byte(dimm_socket_address, + SPD_MIN_ROW_PRECHARGE_TIME); + if (value < 0) + goto hw_err; + if (value > slowest_row_precharge) + slowest_row_precharge = value; + + value = smbus_read_byte(dimm_socket_address, + SPD_MIN_RAS_TO_CAS_DELAY); + if (value < 0) + goto hw_err; + if (value > slowest_ras_cas_delay) + slowest_ras_cas_delay = value; + + value = smbus_read_byte(dimm_socket_address, + SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY); + if (value < 0) + goto hw_err; + if (value > slowest_active_to_precharge_delay) + slowest_active_to_precharge_delay = value; + } + + // NOTE for timing parameters: + // At 133 MHz, 1 clock == 7.52 ns + + /* Read the initial state */ + dram_timing = pci_read_config32(MCHDEV, DRT); + + /* Trp */ + + // E7501 supports only 2 or 3 clocks for tRP + if (slowest_row_precharge > ((22 << 2) | (2 << 0))) + die("unsupported DIMM tRP"); // > 22.5 ns: 4 or more clocks + else if (slowest_row_precharge > (15 << 2)) + dram_timing &= ~(1 << 0); // > 15.0 ns: 3 clocks + else + dram_timing |= (1 << 0); // <= 15.0 ns: 2 clocks + + /* Trcd */ + + // E7501 supports only 2 or 3 clocks for tRCD + // Use the same value for both read & write + dram_timing &= ~((1 << 3) | (3 << 1)); + if (slowest_ras_cas_delay > ((22 << 2) | (2 << 0))) + die("unsupported DIMM tRCD"); // > 22.5 ns: 4 or more clocks + else if (slowest_ras_cas_delay > (15 << 2)) + dram_timing |= (2 << 1); // > 15.0 ns: 3 clocks + else + dram_timing |= ((1 << 3) | (3 << 1)); // <= 15.0 ns: 2 clocks + + /* Tras */ + + // E7501 supports only 5, 6, or 7 clocks for tRAS + // 5 clocks ~= 37.6 ns, 6 clocks ~= 45.1 ns, 7 clocks ~= 52.6 ns + dram_timing &= ~(3 << 9); + + if (slowest_active_to_precharge_delay > 52) + die("unsupported DIMM tRAS"); // > 52 ns: 8 or more clocks + else if (slowest_active_to_precharge_delay > 45) + dram_timing |= (0 << 9); // 46-52 ns: 7 clocks + else if (slowest_active_to_precharge_delay > 37) + dram_timing |= (1 << 9); // 38-45 ns: 6 clocks + else + dram_timing |= (2 << 9); // < 38 ns: 5 clocks + + /* Trd */ + + /* Set to a 7 clock read delay. This is for 133MHz + * with a CAS latency of 2.5 if 2.0 a 6 clock + * delay is good */ + + dram_timing &= ~(7 << 24); // 7 clocks + if (current_cas_latency == DRT_CAS_2_0) + dram_timing |= (1 << 24); // 6 clocks + + /* + * Back to Back Read-Write Turn Around + */ + /* Set to a 5 clock back to back read to write turn around. + * 4 is a good delay if the CAS latency is 2.0 */ + + dram_timing &= ~(1 << 28); // 5 clocks + if (current_cas_latency == DRT_CAS_2_0) + dram_timing |= (1 << 28); // 4 clocks + + pci_write_config32(MCHDEV, DRT, dram_timing); + + return; + +hw_err: + die(SPD_ERROR); +} + +/** + * Determine the shortest CAS# latency that the E7501 and all DIMMs have in + * common, and program the E7501 to use it. + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + * @param dimm_mask Bitmask of populated DIMMs, spd_get_supported_dimms(). + */ +static void configure_e7501_cas_latency(const struct mem_controller *ctrl, + uint8_t dimm_mask) +{ + int i; + int value; + uint32_t dram_timing; + uint16_t dram_read_timing; + uint32_t dword; + + // CAS# latency bitmasks in SPD_ACCEPTABLE_CAS_LATENCIES format + // NOTE: E7501 supports only 2.0 and 2.5 + uint32_t system_compatible_cas_latencies = + SPD_CAS_LATENCY_2_0 | SPD_CAS_LATENCY_2_5; + uint32_t current_cas_latency; + uint32_t dimm_compatible_cas_latencies; + + for (i = 0; i < MAX_DIMM_SOCKETS; i++) { + + uint16_t dimm_socket_address; + + if (!(dimm_mask & (1 << i))) + continue; // This DIMM not usable + + if (i < MAX_DIMM_SOCKETS_PER_CHANNEL) + dimm_socket_address = ctrl->channel0[i]; + else + dimm_socket_address = + ctrl->channel1[i - MAX_DIMM_SOCKETS_PER_CHANNEL]; + + value = smbus_read_byte(dimm_socket_address, + SPD_ACCEPTABLE_CAS_LATENCIES); + if (value < 0) + goto hw_err; + + dimm_compatible_cas_latencies = value & 0x7f; // Start with all supported by DIMM + current_cas_latency = 1 << log2(dimm_compatible_cas_latencies); // Max supported by DIMM + + // Can we support the highest CAS# latency? + + value = smbus_read_byte(dimm_socket_address, + SPD_MIN_CYCLE_TIME_AT_CAS_MAX); + if (value < 0) + goto hw_err; + + // NOTE: At 133 MHz, 1 clock == 7.52 ns + if (value > 0x75) { + // Our bus is too fast for this CAS# latency + // Remove it from the bitmask of those supported by the DIMM that are compatible + dimm_compatible_cas_latencies &= ~current_cas_latency; + } + // Can we support the next-highest CAS# latency (max - 0.5)? + + current_cas_latency >>= 1; + if (current_cas_latency != 0) { + value = smbus_read_byte(dimm_socket_address, + SPD_SDRAM_CYCLE_TIME_2ND); + if (value < 0) + goto hw_err; + if (value > 0x75) + dimm_compatible_cas_latencies &= + ~current_cas_latency; + } + // Can we support the next-highest CAS# latency (max - 1.0)? + current_cas_latency >>= 1; + if (current_cas_latency != 0) { + value = smbus_read_byte(dimm_socket_address, + SPD_SDRAM_CYCLE_TIME_3RD); + if (value < 0) + goto hw_err; + if (value > 0x75) + dimm_compatible_cas_latencies &= + ~current_cas_latency; + } + // Restrict the system to CAS# latencies compatible with this DIMM + system_compatible_cas_latencies &= + dimm_compatible_cas_latencies; + + /* go to the next DIMM */ + } + + /* After all of the arduous calculation setup with the fastest + * cas latency I can use. + */ + + dram_timing = pci_read_config32(MCHDEV, DRT); + dram_timing &= ~(DRT_CAS_MASK); + + dram_read_timing = + pci_read_config16(MCHDEV, DRDCTL); + dram_read_timing &= 0xF000; + + if (system_compatible_cas_latencies & SPD_CAS_LATENCY_2_0) { + dram_timing |= DRT_CAS_2_0; + dram_read_timing |= 0x0222; + } else if (system_compatible_cas_latencies & SPD_CAS_LATENCY_2_5) { + + uint32_t dram_row_attributes = + pci_read_config32(MCHDEV, DRA); + + dram_timing |= DRT_CAS_2_5; + + // At CAS# 2.5, DRAM Read Timing (if that's what it its) appears to need a slightly + // different value if all DIMM slots are populated + + if ((dram_row_attributes & 0xff) + && (dram_row_attributes & 0xff00) + && (dram_row_attributes & 0xff0000) + && (dram_row_attributes & 0xff000000)) { + + // All slots populated + dram_read_timing |= 0x0882; + } else { + // Some unpopulated slots + dram_read_timing |= 0x0662; + } + } else + die("No CAS# latencies compatible with all DIMMs!!\n"); + + pci_write_config32(MCHDEV, DRT, dram_timing); + + /* set master DLL reset */ + dword = pci_read_config32(MCHDEV, 0x88); + dword |= (1 << 26); + pci_write_config32(MCHDEV, 0x88, dword); + /* patch try register 88 is undocumented tnz */ + dword &= 0x0ca17fff; + dword |= 0xd14a5000; + pci_write_config32(MCHDEV, 0x88, dword); + + pci_write_config16(MCHDEV, DRDCTL, + dram_read_timing); + + /* clear master DLL reset */ + dword = pci_read_config32(MCHDEV, 0x88); + dword &= ~(1 << 26); + pci_write_config32(MCHDEV, 0x88, dword); + + return; + +hw_err: + die(SPD_ERROR); +} + +/** + * Configure the refresh interval so that we refresh no more often than + * required by the "most needy" DIMM. Also disable ECC if any of the DIMMs + * don't support it. + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + * @param dimm_mask Bitmask of populated DIMMs, spd_get_supported_dimms(). + */ +static void configure_e7501_dram_controller_mode(const struct mem_controller *ctrl, + uint8_t dimm_mask) +{ + int i; + + // Initial settings + uint32_t controller_mode = pci_read_config32(MCHDEV, DRC); + uint32_t system_refresh_mode = (controller_mode >> 8) & 7; + + // Code below assumes that most aggressive settings are in + // force when we are called, either via E7501 reset defaults + // or by sdram_set_registers(): + // - ECC enabled + // - No refresh + + ASSERT((controller_mode & (3 << 20)) == (2 << 20)); // ECC + ASSERT(!(controller_mode & (7 << 8))); // Refresh + + /* Walk through _all_ dimms and find the least-common denominator for: + * - ECC support + * - refresh rates + */ + + for (i = 0; i < MAX_DIMM_SOCKETS; i++) { + + uint32_t dimm_refresh_mode; + int value; + uint16_t dimm_socket_address; + + if (!(dimm_mask & (1 << i))) { + continue; // This DIMM not usable + } + + if (i < MAX_DIMM_SOCKETS_PER_CHANNEL) + dimm_socket_address = ctrl->channel0[i]; + else + dimm_socket_address = + ctrl->channel1[i - + MAX_DIMM_SOCKETS_PER_CHANNEL]; + + // Disable ECC mode if any one of the DIMMs does not support ECC + // SJM: Should we just die here? E7501 datasheet says non-ECC DIMMs aren't supported. + + value = smbus_read_byte(dimm_socket_address, + SPD_DIMM_CONFIG_TYPE); + die_on_spd_error(value); + if (value != ERROR_SCHEME_ECC) { + controller_mode &= ~(3 << 20); + } + + value = smbus_read_byte(dimm_socket_address, SPD_REFRESH); + die_on_spd_error(value); + value &= 0x7f; // Mask off self-refresh bit + if (value > MAX_SPD_REFRESH_RATE) { + printk(BIOS_ERR, "unsupported refresh rate\n"); + continue; + } + // Get the appropriate E7501 refresh mode for this DIMM + dimm_refresh_mode = refresh_rate_map[value]; + if (dimm_refresh_mode > 7) { + printk(BIOS_ERR, "unsupported refresh rate\n"); + continue; + } + // If this DIMM requires more frequent refresh than others, + // update the system setting + if (refresh_frequency[dimm_refresh_mode] > + refresh_frequency[system_refresh_mode]) + system_refresh_mode = dimm_refresh_mode; + + /* go to the next DIMM */ + } + + controller_mode |= (system_refresh_mode << 8); + + // Configure the E7501 + pci_write_config32(MCHDEV, DRC, controller_mode); +} + +/** + * Configure the E7501's DRAM Row Attributes (DRA) registers based on DIMM + * parameters read via SPD. This tells the controller the width of the SDRAM + * chips on each DIMM side (x4 or x8) and the page size of each DIMM side + * (4, 8, 16, or 32 KB). + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + * @param dimm_mask Bitmask of populated DIMMs, spd_get_supported_dimms(). + */ +static void configure_e7501_row_attributes(const struct mem_controller + *ctrl, uint8_t dimm_mask) +{ + int i; + uint32_t row_attributes = 0; + + for (i = 0; i < MAX_DIMM_SOCKETS_PER_CHANNEL; i++) { + + uint16_t dimm_socket_address = ctrl->channel0[i]; + struct dimm_size page_size; + struct dimm_size sdram_width; + + if (!(dimm_mask & (1 << i))) + continue; // This DIMM not usable + + // Get the relevant parameters via SPD + page_size = sdram_spd_get_page_size(dimm_socket_address); + sdram_width = sdram_spd_get_width(dimm_socket_address); + + // Update the DRAM Row Attributes. + // Page size is encoded as log2(page size in bits) - log2(8 Kb) + // NOTE: 8 Kb = 2^13 + row_attributes |= (page_size.side1 - 13) << (i << 3); // Side 1 of each DIMM is an EVEN row + + if (sdram_width.side2 > 0) + row_attributes |= (page_size.side2 - 13) << ((i << 3) + 4); // Side 2 is ODD + + // Set x4 flags if appropriate + if (sdram_width.side1 == 4) { + row_attributes |= 0x08 << (i << 3); + } + + if (sdram_width.side2 == 4) { + row_attributes |= 0x08 << ((i << 3) + 4); + } + + /* go to the next DIMM */ + } + + /* Write the new row attributes register */ + pci_write_config32(MCHDEV, DRA, row_attributes); +} + +/* + * Enable clock signals for populated DIMM sockets and disable them for + * unpopulated sockets (to reduce EMI). + * + * @param dimm_mask Bitmask of populated DIMMs, see spd_get_supported_dimms(). + */ +static void enable_e7501_clocks(uint8_t dimm_mask) +{ + int i; + uint8_t clock_disable = pci_read_config8(MCHDEV, CKDIS); + + pci_write_config8(MCHDEV, 0x8e, 0xb0); + + for (i = 0; i < MAX_DIMM_SOCKETS_PER_CHANNEL; i++) { + + uint8_t socket_mask = 1 << i; + + if (dimm_mask & socket_mask) + clock_disable &= ~socket_mask; // DIMM present, enable clock + else + clock_disable |= socket_mask; // DIMM absent, disable clock + } + + pci_write_config8(MCHDEV, CKDIS, clock_disable); +} + +/* DIMM-dependent configuration functions */ + +/** + * DDR Receive FIFO RE-Sync (?) + */ +static void RAM_RESET_DDR_PTR(void) +{ + uint8_t byte; + byte = pci_read_config8(MCHDEV, 0x88); + byte |= (1 << 4); + pci_write_config8(MCHDEV, 0x88, byte); + + byte = pci_read_config8(MCHDEV, 0x88); + byte &= ~(1 << 4); + pci_write_config8(MCHDEV, 0x88, byte); +} + +/** + * Copy 64 bytes from one location to another. + * + * @param src_addr TODO + * @param dst_addr TODO + */ +static void write_8dwords(const uint32_t *src_addr, u8 *dst_addr) +{ + int i; + for (i = 0; i < 8; i++) { + write32(dst_addr, *src_addr); + src_addr++; + dst_addr += sizeof(uint32_t); + } +} + +/** + * Set the E7501's (undocumented) RCOMP registers. + * + * Per the 855PM datasheet and IXP2800 HW Initialization Reference Manual, + * RCOMP registers appear to affect drive strength, pullup/pulldown offset, + * and slew rate of various signal groups. + * + * Comments below are conjecture based on apparent similarity between the + * E7501 and these two chips. + */ +static void rcomp_copy_registers(void) +{ + uint32_t dword; + uint8_t strength_control; + + RAM_DEBUG_MESSAGE("Setting RCOMP registers.\n"); + + /* Begin to write the RCOMP registers */ + write8(RCOMP_MMIO + 0x2c, 0x0); + + // Set CMD and DQ/DQS strength to 2x (?) + strength_control = read8(RCOMP_MMIO + DQCMDSTR) & 0x88; + strength_control |= 0x40; + write8(RCOMP_MMIO + DQCMDSTR, strength_control); + write_8dwords(slew_2x, RCOMP_MMIO + 0x80); + write16(RCOMP_MMIO + 0x42, 0); + + // Set CMD and DQ/DQS strength to 2x (?) + strength_control = read8(RCOMP_MMIO + DQCMDSTR) & 0xF8; + strength_control |= 0x04; + write8(RCOMP_MMIO + DQCMDSTR, strength_control); + write_8dwords(slew_2x, RCOMP_MMIO + 0x60); + write16(RCOMP_MMIO + 0x40, 0); + + // Set RCVEnOut# strength to 2x (?) + strength_control = read8(RCOMP_MMIO + RCVENSTR) & 0xF8; + strength_control |= 0x04; + write8(RCOMP_MMIO + RCVENSTR, strength_control); + write_8dwords(slew_2x, RCOMP_MMIO + 0x1c0); + write16(RCOMP_MMIO + 0x50, 0); + + // Set CS# strength for x4 SDRAM to 2x (?) + strength_control = read8(RCOMP_MMIO + CSBSTR) & 0x88; + strength_control |= 0x04; + write8(RCOMP_MMIO + CSBSTR, strength_control); + write_8dwords(slew_2x, RCOMP_MMIO + 0x140); + write16(RCOMP_MMIO + 0x48, 0); + + // Set CS# strength for x4 SDRAM to 2x (?) + strength_control = read8(RCOMP_MMIO + CSBSTR) & 0x8F; + strength_control |= 0x40; + write8(RCOMP_MMIO + CSBSTR, strength_control); + write_8dwords(slew_2x, RCOMP_MMIO + 0x160); + write16(RCOMP_MMIO + 0x4a, 0); + + // Set CKE strength for x4 SDRAM to 2x (?) + strength_control = read8(RCOMP_MMIO + CKESTR) & 0x88; + strength_control |= 0x04; + write8(RCOMP_MMIO + CKESTR, strength_control); + write_8dwords(slew_2x, RCOMP_MMIO + 0xa0); + write16(RCOMP_MMIO + 0x44, 0); + + // Set CKE strength for x4 SDRAM to 2x (?) + strength_control = read8(RCOMP_MMIO + CKESTR) & 0x8F; + strength_control |= 0x40; + write8(RCOMP_MMIO + CKESTR, strength_control); + write_8dwords(slew_2x, RCOMP_MMIO + 0xc0); + write16(RCOMP_MMIO + 0x46, 0); + + // Set CK strength for x4 SDRAM to 1x (?) + strength_control = read8(RCOMP_MMIO + CKSTR) & 0x88; + strength_control |= 0x01; + write8(RCOMP_MMIO + CKSTR, strength_control); + write_8dwords(pull_updown_offset_table, RCOMP_MMIO + 0x180); + write16(RCOMP_MMIO + 0x4c, 0); + + // Set CK strength for x4 SDRAM to 1x (?) + strength_control = read8(RCOMP_MMIO + CKSTR) & 0x8F; + strength_control |= 0x10; + write8(RCOMP_MMIO + CKSTR, strength_control); + write_8dwords(pull_updown_offset_table, RCOMP_MMIO + 0x1a0); + write16(RCOMP_MMIO + 0x4e, 0); + + dword = read32(RCOMP_MMIO + 0x400); + dword &= 0x7f7fffff; + write32(RCOMP_MMIO + 0x400, dword); + + dword = read32(RCOMP_MMIO + 0x408); + dword &= 0x7f7fffff; + write32(RCOMP_MMIO + 0x408, dword); +} + +static void ram_set_rcomp_regs(void) +{ + /* Set the RCOMP MMIO base address */ + mchtest_control(RCOMP_BAR_ENABLE); + pci_write_config32(MCHDEV, SMRBASE, (uintptr_t)RCOMP_MMIO); + + /* Block RCOMP updates while we configure the registers */ + rcomp_smr_control(RCOMP_HOLD); + rcomp_copy_registers(); + d060_control(D060_CMD_0); + mchtest_control(MCHTST_CMD_0); + + uint8_t revision = pci_read_config8(MCHDEV, 0x08); + if (revision >= 3) { + rcomp_smr_control(RCOMP_SMR_00); + rcomp_smr_control(RCOMP_SMR_01); + } + rcomp_smr_control(RCOMP_RELEASE); + + /* Wait 40 usec */ + SLOW_DOWN_IO; + + /* Clear the RCOMP MMIO base address */ + pci_write_config32(MCHDEV, SMRBASE, 0); + mchtest_control(RCOMP_BAR_DISABLE); +} + +/*----------------------------------------------------------------------------- +Public interface: +-----------------------------------------------------------------------------*/ + +/** + * Go through the JEDEC initialization sequence for all DIMMs, then enable + * refresh and initialize ECC and memory to zero. Upon exit, SDRAM is up + * and running. + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + */ +static void sdram_enable(const struct mem_controller *ctrl) +{ + uint8_t dimm_mask = pci_read_config16(MCHDEV, SKPD); + uint32_t dram_controller_mode; + + if (dimm_mask == 0) + return; + + /* 1 & 2 Power up and start clocks */ + RAM_DEBUG_MESSAGE("Ram Enable 1\n"); + RAM_DEBUG_MESSAGE("Ram Enable 2\n"); + + /* A 200us delay is needed */ + DO_DELAY; EXTRA_DELAY; + + /* 3. Apply NOP */ + RAM_DEBUG_MESSAGE("Ram Enable 3\n"); + do_ram_command(RAM_COMMAND_NOP, 0); + + /* 4 Precharge all */ + RAM_DEBUG_MESSAGE("Ram Enable 4\n"); + do_ram_command(RAM_COMMAND_PRECHARGE, 0); + /* wait until the all banks idle state... */ + + /* 5. Issue EMRS to enable DLL */ + RAM_DEBUG_MESSAGE("Ram Enable 5\n"); + do_ram_command(RAM_COMMAND_EMRS, + SDRAM_EXTMODE_DLL_ENABLE | + SDRAM_EXTMODE_DRIVE_NORMAL); + + /* 6. Reset DLL */ + RAM_DEBUG_MESSAGE("Ram Enable 6\n"); + set_ram_mode(E7501_SDRAM_MODE | SDRAM_MODE_DLL_RESET); + EXTRA_DELAY; + /* Ensure a 200us delay between the DLL reset in step 6 and the final + * mode register set in step 9. + * Infineon needs this before any other command is sent to the ram. + */ + DO_DELAY; EXTRA_DELAY; + + /* 7 Precharge all */ + RAM_DEBUG_MESSAGE("Ram Enable 7\n"); + do_ram_command(RAM_COMMAND_PRECHARGE, 0); + + /* 8 Now we need 2 AUTO REFRESH / CBR cycles to be performed */ + /* And for good luck 6 more CBRs */ + RAM_DEBUG_MESSAGE("Ram Enable 8\n"); + int i; + for (i = 0; i < 8; i++) + do_ram_command(RAM_COMMAND_CBR, 0); + + /* 9 mode register set */ + RAM_DEBUG_MESSAGE("Ram Enable 9\n"); + set_ram_mode(E7501_SDRAM_MODE | SDRAM_MODE_NORMAL); + + /* 10 DDR Receive FIFO RE-Sync */ + RAM_DEBUG_MESSAGE("Ram Enable 10\n"); + RAM_RESET_DDR_PTR(); + EXTRA_DELAY; + + /* 11 normal operation */ + RAM_DEBUG_MESSAGE("Ram Enable 11\n"); + do_ram_command(RAM_COMMAND_NORMAL, 0); + + // Reconfigure the row boundaries and Top of Low Memory + // to match the true size of the DIMMs + configure_e7501_ram_addresses(ctrl, dimm_mask); + + /* Finally enable refresh */ + dram_controller_mode = pci_read_config32(MCHDEV, DRC); + dram_controller_mode |= (1 << 29); + pci_write_config32(MCHDEV, DRC, dram_controller_mode); + EXTRA_DELAY; +} + +/** + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + */ +static void sdram_post_ecc(const struct mem_controller *ctrl) +{ + /* Fast CS# Enable. */ + uint32_t dram_controller_mode = pci_read_config32(MCHDEV, DRC); + dram_controller_mode = pci_read_config32(MCHDEV, DRC); + dram_controller_mode |= (1 << 17); + pci_write_config32(MCHDEV, DRC, dram_controller_mode); +} + +/** + * Configure SDRAM controller parameters that depend on characteristics of the + * DIMMs installed in the system. These characteristics are read from the + * DIMMs via the standard Serial Presence Detect (SPD) interface. + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + */ +static void sdram_set_spd_registers(const struct mem_controller *ctrl) +{ + uint8_t dimm_mask; + + RAM_DEBUG_MESSAGE("Reading SPD data...\n"); + + dimm_mask = spd_get_supported_dimms(ctrl); + + if (dimm_mask == 0) { + printk(BIOS_DEBUG, "No usable memory for this controller\n"); + } else { + enable_e7501_clocks(dimm_mask); + + RAM_DEBUG_MESSAGE("setting based on SPD data...\n"); + + configure_e7501_row_attributes(ctrl, dimm_mask); + configure_e7501_dram_controller_mode(ctrl, dimm_mask); + configure_e7501_cas_latency(ctrl, dimm_mask); + RAM_RESET_DDR_PTR(); + + configure_e7501_dram_timing(ctrl, dimm_mask); + DO_DELAY; + RAM_DEBUG_MESSAGE("done\n"); + } + + /* NOTE: configure_e7501_ram_addresses() is NOT called here. + * We want to keep the default 64 MB/row mapping until sdram_enable() is called, + * even though the default mapping is almost certainly incorrect. + * The default mapping makes it easy to initialize all of the DIMMs + * even if the total system memory is > 4 GB. + * + * Save the dimm_mask for when sdram_enable is called, so it can call + * configure_e7501_ram_addresses() without having to regenerate the bitmask + * of usable DIMMs. + */ + pci_write_config16(MCHDEV, SKPD, dimm_mask); +} + +/** + * Do basic RAM setup that does NOT depend on serial presence detect + * information (i.e. independent of DIMM specifics). + * + * @param ctrl PCI addresses of memory controller functions, and SMBus + * addresses of DIMM slots on the mainboard. + */ +static void sdram_set_registers(const struct mem_controller *ctrl) +{ + uint32_t dword; + uint16_t word; + uint8_t byte; + + ram_set_rcomp_regs(); + + /* Enable 0:0.1, 0:2.1 */ + word = pci_read_config16(MCHDEV, DVNP); + word &= ~0x05; + pci_write_config16(MCHDEV, DVNP, word); + + /* Disable high-memory remap (power-on defaults, really) */ + pci_write_config16(MCHDEV, REMAPBASE, 0x03ff); + pci_write_config16(MCHDEV, REMAPLIMIT, 0x0); + + /* Disable legacy MMIO (0xC0000-0xEFFFF is DRAM) */ + int i; + pci_write_config8(MCHDEV, PAM_0, 0x30); + for (i = 1; i <= 6; i++) + pci_write_config8(MCHDEV, PAM_0 + i, 0x33); + + /* Conservatively say each row has 64MB of ram, we will fix this up later + * Initial TOLM 8 rows 64MB each (1<<3 * 1<<26) >> 16 = 1<<13 + * + * FIXME: Hard-coded limit to first four rows to prevent overlap! + */ + pci_write_config32(MCHDEV, DRB_ROW_0, 0x04030201); + pci_write_config32(MCHDEV, DRB_ROW_4, 0x04040404); + //pci_write_config32(MCHDEV, DRB_ROW_4, 0x08070605); + pci_write_config16(MCHDEV, TOLM, (1<<13)); + + /* DIMM clocks off */ + pci_write_config8(MCHDEV, CKDIS, 0xff); + + /* reset row attributes */ + pci_write_config32(MCHDEV, DRA, 0x0); + + // The only things we need to set here are DRAM idle timer, Back-to-Back Read Turnaround, and + // Back-to-Back Write-Read Turnaround. All others are configured based on SPD. + dword = pci_read_config32(MCHDEV, DRT); + dword &= 0xC7F8FFFF; + dword |= (0x28<<24)|(0x03<<16); + pci_write_config32(MCHDEV, DRT, dword); + + dword = pci_read_config32(MCHDEV, DRC); + dword &= 0xffcef8f7; + dword |= 0x00210008; + pci_write_config32(MCHDEV, DRC, dword); + + /* Undocumented */ + pci_write_config8(MCHDEV, 0x88, 0x80); + + /* Undocumented. Set much later in vendor BIOS. */ + byte = pci_read_config8(MCHDEV, 0xd9); + byte &= ~0x60; + pci_write_config8(MCHDEV, 0xd9, byte); + + uint8_t revision = pci_read_config8(MCHDEV, 0x08); + if (revision >= 3) + d060_control(D060_CMD_1); +} + +static int e7505_mch_is_ready(void) +{ + uint32_t dword = pci_read_config32(MCHDEV, DRC); + return !!(dword & DRC_DONE); +} + +void sdram_initialize(void) +{ + static const struct mem_controller memctrl[] = { + { + .d0 = PCI_DEV(0, 0, 0), + .d0f1 = PCI_DEV(0, 0, 1), + .channel0 = { 0x50, 0x52, 0, 0 }, + .channel1 = { 0x51, 0x53, 0, 0 }, + }, + }; + + /* If this is a warm boot, some initialisation can be skipped */ + if (!e7505_mch_is_ready()) { + + /* The real MCH initialisation. */ + timestamp_add_now(TS_INITRAM_START); + + sdram_set_registers(memctrl); + sdram_set_spd_registers(memctrl); + sdram_enable(memctrl); + + /* Hook for post ECC scrub settings and debug. */ + sdram_post_ecc(memctrl); + + timestamp_add_now(TS_INITRAM_END); + } + + printk(BIOS_DEBUG, "SDRAM is up.\n"); +} diff --git a/src/northbridge/intel/e7505/raminit.h b/src/northbridge/intel/e7505/raminit.h new file mode 100644 index 0000000000..1e0128ce72 --- /dev/null +++ b/src/northbridge/intel/e7505/raminit.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef RAMINIT_H +#define RAMINIT_H + +#include <stdint.h> + +#define MAX_DIMM_SOCKETS_PER_CHANNEL 4 +#define MAX_NUM_CHANNELS 2 +#define MAX_DIMM_SOCKETS (MAX_NUM_CHANNELS * MAX_DIMM_SOCKETS_PER_CHANNEL) + +struct mem_controller { + pci_devfn_t d0, d0f1; // PCI bus/device/fcns of E7501 memory controller + + // SMBus addresses of DIMM slots for each channel, + // in order from closest to MCH to furthest away + // 0 == not present + uint16_t channel0[MAX_DIMM_SOCKETS_PER_CHANNEL]; + uint16_t channel1[MAX_DIMM_SOCKETS_PER_CHANNEL]; +}; + +void sdram_initialize(void); + +#endif /* RAMINIT_H */ diff --git a/src/northbridge/intel/e7505/romstage.c b/src/northbridge/intel/e7505/romstage.c new file mode 100644 index 0000000000..df10f9dbcf --- /dev/null +++ b/src/northbridge/intel/e7505/romstage.c @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <cbmem.h> +#include <arch/romstage.h> + +#include <southbridge/intel/i82801dx/i82801dx.h> +#include <northbridge/intel/e7505/raminit.h> + +void mainboard_romstage_entry(void) +{ + /* Perform some early chipset initialization required + * before RAM initialization can work + */ + i82801dx_early_init(); + + sdram_initialize(); + + cbmem_recovery(0); +} diff --git a/src/southbridge/intel/common/smbus.c b/src/southbridge/intel/common/smbus.c index 0e65cdaee4..8b19b7d900 100644 --- a/src/southbridge/intel/common/smbus.c +++ b/src/southbridge/intel/common/smbus.c @@ -457,7 +457,8 @@ int do_smbus_block_write(uintptr_t base, u8 device, u8 cmd, const size_t bytes, /* Only since ICH5 */ static int has_i2c_read_command(void) { - if (CONFIG(SOUTHBRIDGE_INTEL_I82371EB)) + if (CONFIG(SOUTHBRIDGE_INTEL_I82371EB) || + CONFIG(SOUTHBRIDGE_INTEL_I82801DX)) return 0; return 1; } diff --git a/src/southbridge/intel/i82801dx/Kconfig b/src/southbridge/intel/i82801dx/Kconfig new file mode 100644 index 0000000000..98c1a477ff --- /dev/null +++ b/src/southbridge/intel/i82801dx/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SOUTHBRIDGE_INTEL_I82801DX + bool + select ACPI_INTEL_HARDWARE_SLEEP_VALUES + select SOUTHBRIDGE_INTEL_COMMON_SMBUS + select SOUTHBRIDGE_INTEL_COMMON_EARLY_SMBUS + select SOUTHBRIDGE_INTEL_COMMON_RTC + select SOUTHBRIDGE_INTEL_COMMON_RESET + select SOUTHBRIDGE_INTEL_COMMON_USB_DEBUG + select HAVE_POWER_STATE_AFTER_FAILURE + select HAVE_POWER_STATE_PREVIOUS_AFTER_FAILURE + select BOOT_DEVICE_NOT_SPI_FLASH + +if SOUTHBRIDGE_INTEL_I82801DX + +config EHCI_BAR + hex + default 0xfef00000 + +endif diff --git a/src/southbridge/intel/i82801dx/Makefile.inc b/src/southbridge/intel/i82801dx/Makefile.inc new file mode 100644 index 0000000000..c352e3e9e8 --- /dev/null +++ b/src/southbridge/intel/i82801dx/Makefile.inc @@ -0,0 +1,17 @@ +## SPDX-License-Identifier: GPL-2.0-only + +ifeq ($(CONFIG_SOUTHBRIDGE_INTEL_I82801DX),y) + +ramstage-y += i82801dx.c +ramstage-y += ac97.c +ramstage-y += fadt.c +ramstage-y += ide.c +ramstage-y += lpc.c +ramstage-y += usb.c +ramstage-y += usb2.c + +romstage-y += early_smbus.c + +bootblock-y += bootblock.c + +endif diff --git a/src/southbridge/intel/i82801dx/ac97.c b/src/southbridge/intel/i82801dx/ac97.c new file mode 100644 index 0000000000..bca35eb7c9 --- /dev/null +++ b/src/southbridge/intel/i82801dx/ac97.c @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <arch/io.h> +#include <device/pci_ops.h> +#include <delay.h> +#include "i82801dx.h" + +#define NAMBAR 0x10 +#define MASTER_VOL 0x02 +#define PAGING 0x24 +#define EXT_AUDIO 0x28 +#define FUNC_SEL 0x66 +#define INFO_IO 0x68 +#define CONNECTOR 0x6a +#define VENDOR_ID1 0x7c +#define VENDOR_ID2 0x7e +#define SEC_VENDOR_ID1 0xfc +#define SEC_VENDOR_ID2 0xfe + +#define NABMBAR 0x14 +#define GLOB_CNT 0x2c +#define GLOB_STA 0x30 +#define CAS 0x34 + +#define MMBAR 0x10 +#define EXT_MODEM_ID1 0x3c +#define EXT_MODEM_ID2 0xbc + +#define MBAR 0x14 +#define SEC_CODEC 0x40 + +/* FIXME. This table is probably mainboard specific */ +static u16 ac97_function[16*2][4] = { + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) }, + { (1 << 5), (2 << 11), (1 << 10), (3 << 13) } +}; + +static u16 nabmbar; +static u16 nambar; + +static int ac97_semaphore(void) +{ + int timeout; + u8 reg8; + + timeout = 0xffff; + do { + reg8 = inb(nabmbar + CAS); + timeout--; + } while ((reg8 & 1) && timeout); + if (!timeout) + printk(BIOS_DEBUG, "Timeout!\n"); + + return (!timeout); +} + +static void init_cnr(void) +{ + // TODO +} + +static void program_sigid(struct device *dev, u32 id) +{ + pci_write_config32(dev, 0x2c, id); +} + +static void ac97_audio_init(struct device *dev) +{ + u16 reg16; + u32 reg32; + int i; + + printk(BIOS_DEBUG, "Initializing AC'97 Audio.\n"); + + /* top 16 bits are zero, so don't read them */ + nabmbar = pci_read_config16(dev, NABMBAR) & 0xfffe; + nambar = pci_read_config16(dev, NAMBAR) & 0xfffe; + + reg16 = inw(nabmbar + GLOB_CNT); + reg16 |= (1 << 1); /* Remove AC_RESET# */ + outw(reg16, nabmbar + GLOB_CNT); + + /* Wait 600ms. Ouch. */ + udelay(600 * 1000); + + init_cnr(); + + /* Detect Primary AC'97 Codec */ + reg32 = inl(nabmbar + GLOB_STA); + if ((reg32 & ((1 << 28) | (1 << 9) | (1 << 8))) == 0) { + /* Primary Codec not found */ + printk(BIOS_DEBUG, "No primary codec. Disabling AC'97 Audio.\n"); + return; + } + + ac97_semaphore(); + + /* Detect if codec is programmable */ + outw(0x8000, nambar + MASTER_VOL); + ac97_semaphore(); + if (inw(nambar + MASTER_VOL) != 0x8000) { + printk(BIOS_DEBUG, "Codec not programmable. Disabling AC'97 Audio.\n"); + return; + } + + /* Program Vendor IDs */ + reg32 = inw(nambar + VENDOR_ID1); + reg32 <<= 16; + reg32 |= (u16)inw(nambar + VENDOR_ID2); + + program_sigid(dev, reg32); + + /* Is Codec AC'97 2.3 compliant? */ + reg16 = inw(nambar + EXT_AUDIO); + /* [11:10] = 10b -> AC'97 2.3 */ + if ((reg16 & 0x0c00) != 0x0800) { + /* No 2.3 Codec. We're done */ + return; + } + + /* Select Page 1 */ + reg16 = inw(nambar + PAGING); + reg16 &= 0xfff0; + reg16 |= 0x0001; + outw(reg16, nambar + PAGING); + + for (i = 0x0a * 2; i > 0; i--) { + outw(i, nambar + FUNC_SEL); + + /* Function could not be selected. Next one */ + if (inw(nambar + FUNC_SEL) != i) + continue; + + reg16 = inw(nambar + INFO_IO); + + /* Function Information present? */ + if (!(reg16 & (1 << 0))) + continue; + + /* Function Information valid? */ + if (!(reg16 & (1 << 4))) + continue; + + /* Program Buffer Delay [9:5] */ + reg16 &= 0x03e0; + reg16 |= ac97_function[i][0]; + + /* Program Gain [15:11] */ + reg16 |= ac97_function[i][1]; + + /* Program Inversion [10] */ + reg16 |= ac97_function[i][2]; + + outw(reg16, nambar + INFO_IO); + + /* Program Connector / Jack Location */ + reg16 = inw(nambar + CONNECTOR); + reg16 &= 0x1fff; + reg16 |= ac97_function[i][3]; + outw(reg16, nambar + CONNECTOR); + } +} + +static void ac97_modem_init(struct device *dev) +{ + u16 reg16; + u32 reg32; + u16 mmbar, mbar; + + mmbar = pci_read_config16(dev, MMBAR) & 0xfffe; + mbar = pci_read_config16(dev, MBAR) & 0xfffe; + + reg16 = inw(mmbar + EXT_MODEM_ID1); + if ((reg16 & 0xc000) != 0xc000) { + if (reg16 & (1 << 0)) { + reg32 = inw(mmbar + VENDOR_ID2); + reg32 <<= 16; + reg32 |= (u16)inw(mmbar + VENDOR_ID1); + program_sigid(dev, reg32); + return; + } + } + + /* Secondary codec? */ + reg16 = inw(mbar + SEC_CODEC); + if ((reg16 & (1 << 9)) == 0) + return; + + reg16 = inw(mmbar + EXT_MODEM_ID2); + if ((reg16 & 0xc000) == 0x4000) { + if (reg16 & (1 << 0)) { + reg32 = inw(mmbar + SEC_VENDOR_ID2); + reg32 <<= 16; + reg32 |= (u16)inw(mmbar + SEC_VENDOR_ID1); + program_sigid(dev, reg32); + return; + } + } +} + +static struct device_operations ac97_audio_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .enable = i82801dx_enable, + .init = ac97_audio_init, +}; + +static struct device_operations ac97_modem_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .enable = i82801dx_enable, + .init = ac97_modem_init, +}; + +/* 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) */ +static const struct pci_driver i82801db_ac97_audio __pci_driver = { + .ops = &ac97_audio_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82801DB_AC97_AUDIO, +}; + +static const struct pci_driver i82801db_ac97_modem __pci_driver = { + .ops = &ac97_modem_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82801DB_AC97_MODEM, +}; diff --git a/src/southbridge/intel/i82801dx/bootblock.c b/src/southbridge/intel/i82801dx/bootblock.c new file mode 100644 index 0000000000..6cda0a87a4 --- /dev/null +++ b/src/southbridge/intel/i82801dx/bootblock.c @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/bootblock.h> +#include <device/pci_ops.h> + +void bootblock_early_southbridge_init(void) +{ + /* Set FWH IDs for 2 MB flash part. */ + if (CONFIG_ROM_SIZE == 0x200000) + pci_write_config32(PCI_DEV(0, 0x1f, 0), 0xe8, 0x00001111); +} diff --git a/src/southbridge/intel/i82801dx/chip.h b/src/southbridge/intel/i82801dx/chip.h new file mode 100644 index 0000000000..50f4f1c998 --- /dev/null +++ b/src/southbridge/intel/i82801dx/chip.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef I82801DX_CHIP_H +#define I82801DX_CHIP_H + +#include <stdint.h> + +struct southbridge_intel_i82801dx_config { + int enable_usb; + int enable_native_ide; + /** + * Interrupt Routing configuration + * If bit7 is 1, the interrupt is disabled. + */ + uint8_t pirqa_routing; + uint8_t pirqb_routing; + uint8_t pirqc_routing; + uint8_t pirqd_routing; + uint8_t pirqe_routing; + uint8_t pirqf_routing; + uint8_t pirqg_routing; + uint8_t pirqh_routing; + + uint8_t ide0_enable; + uint8_t ide1_enable; +}; + +#endif /* I82801DX_CHIP_H */ diff --git a/src/southbridge/intel/i82801dx/early_smbus.c b/src/southbridge/intel/i82801dx/early_smbus.c new file mode 100644 index 0000000000..76ef1b8a15 --- /dev/null +++ b/src/southbridge/intel/i82801dx/early_smbus.c @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <device/smbus_host.h> +#include "i82801dx.h" + +void i82801dx_early_init(void) +{ + enable_smbus(); +} diff --git a/src/southbridge/intel/i82801dx/fadt.c b/src/southbridge/intel/i82801dx/fadt.c new file mode 100644 index 0000000000..e12b2cae1f --- /dev/null +++ b/src/southbridge/intel/i82801dx/fadt.c @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <device/pci_ops.h> +#include <acpi/acpi.h> +#include <version.h> + +/* FIXME: This needs to go into a separate .h file + * to be included by the ich7 smi handler, ich7 smi init + * code and the mainboard fadt. + */ +#define APM_CNT 0x0 /* ACPI mode only */ +#define CST_CONTROL 0x85 +#define PST_CONTROL 0x0 +#define ACPI_DISABLE 0xAA +#define ACPI_ENABLE 0x55 +#define S4_BIOS 0x77 +#define GNVS_UPDATE 0xea + +void acpi_fill_fadt(acpi_fadt_t *fadt) +{ + u16 pmbase = pci_read_config16(pcidev_on_root(0x1f, 0), 0x40) & 0xfffe; + + fadt->sci_int = 0x9; + + if (permanent_smi_handler()) { + fadt->smi_cmd = APM_CNT; + fadt->acpi_enable = ACPI_ENABLE; + fadt->acpi_disable = ACPI_DISABLE; + fadt->pstate_cnt = PST_CONTROL; + } + + fadt->pm1a_evt_blk = pmbase; + fadt->pm1a_cnt_blk = pmbase + 0x4; + fadt->pm_tmr_blk = pmbase + 0x8; + fadt->gpe0_blk = pmbase + 0x28; + + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm_tmr_len = 4; + fadt->gpe0_blk_len = 8; + fadt->p_lvl2_lat = 1; + fadt->p_lvl3_lat = 85; + fadt->duty_offset = 1; + fadt->duty_width = 0; + fadt->day_alrm = 0xd; + fadt->mon_alrm = 0x00; + fadt->iapc_boot_arch = ACPI_FADT_LEGACY_DEVICES | ACPI_FADT_8042; + + fadt->flags |= ACPI_FADT_WBINVD | ACPI_FADT_C1_SUPPORTED | + ACPI_FADT_C2_MP_SUPPORTED | ACPI_FADT_SLEEP_BUTTON | + ACPI_FADT_S4_RTC_WAKE | ACPI_FADT_PLATFORM_CLOCK; + + fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; + fadt->x_pm1a_evt_blk.bit_offset = 0; + fadt->x_pm1a_evt_blk.access_size = ACPI_ACCESS_SIZE_UNDEFINED; + fadt->x_pm1a_evt_blk.addrl = fadt->pm1a_evt_blk; + fadt->x_pm1a_evt_blk.addrh = 0x0; + + fadt->x_pm1a_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_pm1a_cnt_blk.bit_width = fadt->pm1_cnt_len * 8; + fadt->x_pm1a_cnt_blk.bit_offset = 0; + fadt->x_pm1a_cnt_blk.access_size = ACPI_ACCESS_SIZE_UNDEFINED; + fadt->x_pm1a_cnt_blk.addrl = fadt->pm1a_cnt_blk; + fadt->x_pm1a_cnt_blk.addrh = 0x0; + + fadt->x_pm_tmr_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_pm_tmr_blk.bit_width = fadt->pm_tmr_len * 8; + fadt->x_pm_tmr_blk.bit_offset = 0; + fadt->x_pm_tmr_blk.access_size = ACPI_ACCESS_SIZE_UNDEFINED; + fadt->x_pm_tmr_blk.addrl = fadt->pm_tmr_blk; + fadt->x_pm_tmr_blk.addrh = 0x0; + + fadt->x_gpe0_blk.space_id = ACPI_ADDRESS_SPACE_IO; + fadt->x_gpe0_blk.bit_width = fadt->gpe0_blk_len * 8; + fadt->x_gpe0_blk.bit_offset = 0; + fadt->x_gpe0_blk.access_size = ACPI_ACCESS_SIZE_UNDEFINED; + fadt->x_gpe0_blk.addrl = fadt->gpe0_blk; + fadt->x_gpe0_blk.addrh = 0x0; +} diff --git a/src/southbridge/intel/i82801dx/i82801dx.c b/src/southbridge/intel/i82801dx/i82801dx.c new file mode 100644 index 0000000000..921cb1c8aa --- /dev/null +++ b/src/southbridge/intel/i82801dx/i82801dx.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ops.h> +#include "i82801dx.h" + +void i82801dx_enable(struct device *dev) +{ + unsigned int index = 0; + uint8_t bHasDisableBit = 0; + uint16_t cur_disable_mask, new_disable_mask; + +// all 82801dbm devices are in bus 0 + unsigned int devfn = PCI_DEVFN(0x1f, 0); // lpc + struct device *lpc_dev = pcidev_path_on_root(devfn); // 0 + if (!lpc_dev) + return; + + // Calculate disable bit position for specified device:function + // NOTE: For ICH-4, only the following devices can be disabled: + // D31: F0, F1, F3, F5, F6, + // D29: F0, F1, F2, F7 + + if (PCI_SLOT(dev->path.pci.devfn) == 31) { + index = PCI_FUNC(dev->path.pci.devfn); + + switch (index) { + case 0: + case 1: + case 3: + case 5: + case 6: + bHasDisableBit = 1; + break; + + default: + break; + }; + + if (index == 0) + index = 14; // D31:F0 bit is an exception + + } else if (PCI_SLOT(dev->path.pci.devfn) == 29) { + index = 8 + PCI_FUNC(dev->path.pci.devfn); + + if ((PCI_FUNC(dev->path.pci.devfn) < 3) + || (PCI_FUNC(dev->path.pci.devfn) == 7)) + bHasDisableBit = 1; + } + + if (bHasDisableBit) { + cur_disable_mask = pci_read_config16(lpc_dev, FUNC_DIS); + new_disable_mask = cur_disable_mask & ~(1 << index); // enable it + if (!dev->enabled) { + new_disable_mask |= (1 << index); // disable it + } + if (new_disable_mask != cur_disable_mask) { + pci_write_config16(lpc_dev, FUNC_DIS, new_disable_mask); + } + } +} + +struct chip_operations southbridge_intel_i82801dx_ops = { + CHIP_NAME("Intel ICH4/ICH4-M (82801Dx) Series Southbridge") + .enable_dev = i82801dx_enable, +}; diff --git a/src/southbridge/intel/i82801dx/i82801dx.h b/src/southbridge/intel/i82801dx/i82801dx.h new file mode 100644 index 0000000000..e63327c01e --- /dev/null +++ b/src/southbridge/intel/i82801dx/i82801dx.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* the problem: we have 82801dbm support in fb1, and 82801er in fb2. + * fb1 code is what we want, fb2 structure is needed however. + * so we need to get fb1 code for 82801dbm into fb2 structure. + */ +/* What I did: took the 80801er stuff from fb2, verify it against the + * db stuff in fb1, and made sure it was right. + */ + +#ifndef I82801DX_H +#define I82801DX_H + +#include <acpi/acpi.h> + +#if !defined(__ASSEMBLER__) + +#include <device/device.h> + +void i82801dx_enable(struct device *dev); +void i82801dx_early_init(void); + +#endif + +#define DEBUG_PERIODIC_SMIS 0 + +#define MAINBOARD_POWER_OFF 0 +#define MAINBOARD_POWER_ON 1 +#define MAINBOARD_POWER_KEEP 2 + +/* + * 000 = Non-combined. P0 is primary master. P1 is secondary master. + * 001 = Non-combined. P0 is secondary master. P1 is primary master. + * 100 = Combined. P0 is primary master. P1 is primary slave. IDE is secondary; + * Primary IDE channel disabled. + * 101 = Combined. P0 is primary slave. P1 is primary master. IDE is secondary. + * 110 = Combined. IDE is primary. P0 is secondary master. P1 is secondary + * slave; Secondary IDE channel disabled. + * 111 = Combined. IDE is primary. P0 is secondary slave. P1 is secondary master. + */ +/* PCI Configuration Space (D31:F1) */ +#define IDE_TIM_PRI 0x40 /* IDE timings, primary */ +#define IDE_TIM_SEC 0x42 /* IDE timings, secondary */ + +/* IDE_TIM bits */ +#define IDE_DECODE_ENABLE (1 << 15) + +#define PCI_DMA_CFG 0x90 +#define SERIRQ_CNTL 0x64 +#define GEN_CNTL 0xd0 +#define GEN_STS 0xd4 +#define RTC_CONF 0xd8 +#define GEN_PMCON_3 0xa4 + +#define PCICMD 0x04 +#define PMBASE 0x40 +#define PMBASE_ADDR 0x0400 +#define DEFAULT_PMBASE PMBASE_ADDR +#define ACPI_CNTL 0x44 +#define ACPI_EN (1 << 4) +#define BIOS_CNTL 0x4E +#define GPIO_BASE 0x58 +#define GPIO_CNTL 0x5C +#define GPIOBASE_ADDR 0x0500 +#define PIRQA_ROUT 0x60 +#define PIRQB_ROUT 0x61 +#define PIRQC_ROUT 0x62 +#define PIRQD_ROUT 0x63 +#define PIRQE_ROUT 0x68 +#define PIRQF_ROUT 0x69 +#define PIRQG_ROUT 0x6A +#define PIRQH_ROUT 0x6B +#define COM_DEC 0xE0 +#define LPC_EN 0xE6 +#define FUNC_DIS 0xF2 + +/* 1e f0 244e */ + +#define CMD 0x04 +#define SBUS_NUM 0x19 +#define SUB_BUS_NUM 0x1A +#define SMLT 0x1B +#define IOBASE 0x1C +#define IOLIM 0x1D +#define MEMBASE 0x20 +#define MEMLIM 0x22 +#define CNF 0x50 +#define MTT 0x70 +#define PCI_MAST_STS 0x82 + +#define RTC_FAILED (1 << 2) + +#define PM1_STS 0x00 +#define WAK_STS (1 << 15) +#define PCIEXPWAK_STS (1 << 14) +#define PRBTNOR_STS (1 << 11) +#define RTC_STS (1 << 10) +#define PWRBTN_STS (1 << 8) +#define GBL_STS (1 << 5) +#define BM_STS (1 << 4) +#define TMROF_STS (1 << 0) +#define PM1_EN 0x02 +#define PCIEXPWAK_DIS (1 << 14) +#define RTC_EN (1 << 10) +#define PWRBTN_EN (1 << 8) +#define GBL_EN (1 << 5) +#define TMROF_EN (1 << 0) +#define PM1_CNT 0x04 +#define GBL_RLS (1 << 2) +#define BM_RLD (1 << 1) +#define SCI_EN (1 << 0) +#define PM1_TMR 0x08 +#define PROC_CNT 0x10 +#define LV2 0x14 +#define LV3 0x15 +#define LV4 0x16 +#define PM2_CNT 0x20 // mobile only +#define GPE0_STS 0x28 +#define PME_B0_STS (1 << 13) +#define USB3_STS (1 << 12) +#define PME_STS (1 << 11) +#define BATLOW_STS (1 << 10) +#define GST_STS (1 << 9) +#define RI_STS (1 << 8) +#define SMB_WAK_STS (1 << 7) +#define TCOSCI_STS (1 << 6) +#define AC97_STS (1 << 5) +#define USB2_STS (1 << 4) +#define USB1_STS (1 << 3) +#define SWGPE_STS (1 << 2) +#define HOT_PLUG_STS (1 << 1) +#define THRM_STS (1 << 0) +#define GPE0_EN 0x2c +#define PME_B0_EN (1 << 13) +#define PME_EN (1 << 11) +#define SMI_EN 0x30 +#define EL_SMI_EN (1 << 25) // Intel Quick Resume Technology +#define INTEL_USB2_EN (1 << 18) // Intel-Specific USB2 SMI logic +#define LEGACY_USB2_EN (1 << 17) // Legacy USB2 SMI logic +#define PERIODIC_EN (1 << 14) // SMI on PERIODIC_STS in SMI_STS +#define TCO_EN (1 << 13) // Enable TCO Logic (BIOSWE et al) +#define MCSMI_EN (1 << 11) // Trap microcontroller range access +#define BIOS_RLS (1 << 7) // asserts SCI on bit set +#define SWSMI_TMR_EN (1 << 6) // start software smi timer on bit set +#define APMC_EN (1 << 5) // Writes to APM_CNT cause SMI# +#define SLP_SMI_EN (1 << 4) // Write to SLP_EN in PM1_CNT asserts SMI# +#define LEGACY_USB_EN (1 << 3) // Legacy USB circuit SMI logic +#define BIOS_EN (1 << 2) // Assert SMI# on setting GBL_RLS bit +#define EOS (1 << 1) // End of SMI (deassert SMI#) +#define GBL_SMI_EN (1 << 0) // SMI# generation at all? +#define SMI_STS 0x34 +#define ALT_GP_SMI_EN 0x38 +#define ALT_GP_SMI_STS 0x3a +#define GPE_CNTL 0x42 +#define DEVACT_STS 0x44 +#define SS_CNT 0x50 +#define C3_RES 0x54 + +#define TCOBASE 0x60 /* TCO Base Address Register */ +#define TCO1_CNT 0x08 /* TCO1 Control Register */ + +#define GEN_PMCON_1 0xa0 +#define GEN_PMCON_2 0xa2 +#define GEN_PMCON_3 0xa4 + +/* GEN_PMCON_3 bits */ +#define RTC_BATTERY_DEAD (1 << 2) +#define RTC_POWER_FAILED (1 << 1) +#define SLEEP_AFTER_POWER_FAIL (1 << 0) + +#endif /* I82801DX_H */ diff --git a/src/southbridge/intel/i82801dx/ide.c b/src/southbridge/intel/i82801dx/ide.c new file mode 100644 index 0000000000..a54e49781e --- /dev/null +++ b/src/southbridge/intel/i82801dx/ide.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include "chip.h" +#include "i82801dx.h" + +typedef struct southbridge_intel_i82801dx_config config_t; + +static void ide_init(struct device *dev) +{ + /* Get the chip configuration */ + config_t *config = dev->chip_info; + + /* Enable IDE devices so the Linux IDE driver will work. */ + uint16_t ideTimingConfig; + + ideTimingConfig = pci_read_config16(dev, IDE_TIM_PRI); + ideTimingConfig &= ~IDE_DECODE_ENABLE; + if (!config || config->ide0_enable) { + /* Enable primary IDE interface. */ + ideTimingConfig |= IDE_DECODE_ENABLE; + printk(BIOS_DEBUG, "IDE0: Primary IDE interface is enabled\n"); + } else { + printk(BIOS_INFO, "IDE0: Primary IDE interface is disabled\n"); + } + pci_write_config16(dev, IDE_TIM_PRI, ideTimingConfig); + + ideTimingConfig = pci_read_config16(dev, IDE_TIM_SEC); + ideTimingConfig &= ~IDE_DECODE_ENABLE; + if (!config || config->ide1_enable) { + /* Enable secondary IDE interface. */ + ideTimingConfig |= IDE_DECODE_ENABLE; + printk(BIOS_DEBUG, "IDE1: Secondary IDE interface is enabled\n"); + } else { + printk(BIOS_INFO, "IDE1: Secondary IDE interface is disabled\n"); + } + pci_write_config16(dev, IDE_TIM_SEC, ideTimingConfig); +} + +static struct device_operations ide_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = ide_init, + .enable = i82801dx_enable, +}; + +/* 82801DB */ +static const struct pci_driver i82801db_ide __pci_driver = { + .ops = &ide_ops, + .vendor = PCI_VID_INTEL, + .device = 0x24cb, +}; + +/* 82801DBM */ +static const struct pci_driver i82801dbm_ide __pci_driver = { + .ops = &ide_ops, + .vendor = PCI_VID_INTEL, + .device = 0x24ca, +}; diff --git a/src/southbridge/intel/i82801dx/lpc.c b/src/southbridge/intel/i82801dx/lpc.c new file mode 100644 index 0000000000..1de3766563 --- /dev/null +++ b/src/southbridge/intel/i82801dx/lpc.c @@ -0,0 +1,328 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/hpet.h> +#include <arch/io.h> +#include <arch/ioapic.h> +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <option.h> +#include <pc80/mc146818rtc.h> +#include <pc80/isa-dma.h> +#include <pc80/i8259.h> +#include "chip.h" +#include "i82801dx.h" + +#define NMI_OFF 0 + +typedef struct southbridge_intel_i82801dx_config config_t; + +/** + * Enable ACPI I/O range. + * + * @param dev PCI device with ACPI and PM BAR's + */ +static void i82801dx_enable_acpi(struct device *dev) +{ + /* Set ACPI base address (I/O space). */ + pci_write_config32(dev, PMBASE, (PMBASE_ADDR | 1)); + + /* Enable ACPI I/O range decode and ACPI power management. */ + pci_write_config8(dev, ACPI_CNTL, ACPI_EN); +} + +/** + * Set miscellaneous static southbridge features. + * + * @param dev PCI device with I/O APIC control registers + */ +static void i82801dx_enable_ioapic(struct device *dev) +{ + u32 reg32; + + reg32 = pci_read_config32(dev, GEN_CNTL); + reg32 |= (1 << 13); /* Coprocessor error enable (COPR_ERR_EN) */ + reg32 |= (3 << 7); /* IOAPIC enable (APIC_EN) */ + reg32 |= (1 << 2); /* DMA collection buffer enable (DCB_EN) */ + reg32 |= (1 << 1); /* Delayed transaction enable (DTE) */ + pci_write_config32(dev, GEN_CNTL, reg32); + printk(BIOS_DEBUG, "IOAPIC Southbridge enabled %x\n", reg32); + + setup_ioapic(VIO_APIC_VADDR, 0x02); + + ioapic_set_boot_config(VIO_APIC_VADDR, true); +} + +static void i82801dx_enable_serial_irqs(struct device *dev) +{ + /* Set packet length and toggle silent mode bit. */ + pci_write_config8(dev, SERIRQ_CNTL, + (1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0)); + pci_write_config8(dev, SERIRQ_CNTL, + (1 << 7) | (0 << 6) | ((21 - 17) << 2) | (0 << 0)); +} + +static void i82801dx_pirq_init(struct device *dev) +{ + /* Get the chip configuration */ + config_t *config = dev->chip_info; + + pci_write_config8(dev, PIRQA_ROUT, config->pirqa_routing); + pci_write_config8(dev, PIRQB_ROUT, config->pirqb_routing); + pci_write_config8(dev, PIRQC_ROUT, config->pirqc_routing); + pci_write_config8(dev, PIRQD_ROUT, config->pirqd_routing); + pci_write_config8(dev, PIRQE_ROUT, config->pirqe_routing); + pci_write_config8(dev, PIRQF_ROUT, config->pirqf_routing); + pci_write_config8(dev, PIRQG_ROUT, config->pirqg_routing); + pci_write_config8(dev, PIRQH_ROUT, config->pirqh_routing); +} + +static void i82801dx_power_options(struct device *dev) +{ + u8 reg8; + u16 reg16, pmbase; + u32 reg32; + const char *state; + + /* Which state do we want to goto after g3 (power restored)? + * 0 == S0 Full On + * 1 == S5 Soft Off + * + * If the option is not existent (Laptops), use MAINBOARD_POWER_ON. + */ + const unsigned int pwr_on = get_uint_option("power_on_after_fail", MAINBOARD_POWER_ON); + + reg8 = pci_read_config8(dev, GEN_PMCON_3); + reg8 &= 0xfe; + switch (pwr_on) { + case MAINBOARD_POWER_OFF: + reg8 |= 1; + state = "off"; + break; + case MAINBOARD_POWER_ON: + reg8 &= ~1; + state = "on"; + break; + case MAINBOARD_POWER_KEEP: + reg8 &= ~1; + state = "state keep"; + break; + default: + state = "undefined"; + } + + reg8 &= ~(1 << 3); /* minimum assertion is 1 to 2 RTCCLK */ + + pci_write_config8(dev, GEN_PMCON_3, reg8); + printk(BIOS_INFO, "Set power %s after power failure.\n", state); + + /* Set up NMI on errors. */ + reg8 = inb(0x61); + reg8 &= 0x0f; /* Higher Nibble must be 0 */ + reg8 &= ~(1 << 3); /* IOCHK# NMI Enable */ + // reg8 &= ~(1 << 2); /* PCI SERR# Enable */ + reg8 |= (1 << 2); /* PCI SERR# Disable for now */ + outb(reg8, 0x61); + + reg8 = inb(0x70); + const unsigned int nmi_option = get_uint_option("nmi", NMI_OFF); + if (nmi_option) { + printk(BIOS_INFO, "NMI sources enabled.\n"); + reg8 &= ~(1 << 7); /* Set NMI. */ + } else { + printk(BIOS_INFO, "NMI sources disabled.\n"); + reg8 |= (1 << 7); /* Disable NMI. */ + } + outb(reg8, 0x70); + + /* Set SMI# rate down and enable CPU_SLP# */ + reg16 = pci_read_config16(dev, GEN_PMCON_1); + reg16 &= ~(3 << 0); // SMI# rate 1 minute + reg16 |= (1 << 5); // CPUSLP_EN Desktop only + pci_write_config16(dev, GEN_PMCON_1, reg16); + + pmbase = pci_read_config16(dev, 0x40) & 0xfffe; + + /* Set up power management block and determine sleep mode */ + reg32 = inl(pmbase + 0x04); // PM1_CNT + + reg32 &= ~(7 << 10); // SLP_TYP + reg32 |= (1 << 0); // SCI_EN + outl(reg32, pmbase + 0x04); +} + +static void gpio_init(struct device *dev) +{ + /* This should be done in romstage.c already */ + pci_write_config32(dev, GPIO_BASE, (GPIOBASE_ADDR | 1)); + pci_write_config8(dev, GPIO_CNTL, 0x10); +} + +static void i82801dx_rtc_init(struct device *dev) +{ + u8 reg8; + u32 reg32; + int rtc_failed; + + reg8 = pci_read_config8(dev, GEN_PMCON_3); + rtc_failed = reg8 & RTC_BATTERY_DEAD; + if (rtc_failed) { + reg8 &= ~(1 << 1); /* Preserve the power fail state. */ + pci_write_config8(dev, GEN_PMCON_3, reg8); + } + reg32 = pci_read_config32(dev, GEN_STS); + rtc_failed |= reg32 & (1 << 2); + cmos_init(rtc_failed); + + /* Enable access to the upper 128 byte bank of CMOS RAM. */ + pci_write_config8(dev, RTC_CONF, 0x04); +} + +static void i82801dx_lpc_route_dma(struct device *dev, u8 mask) +{ + u16 reg16; + int i; + + reg16 = pci_read_config16(dev, PCI_DMA_CFG); + reg16 &= 0x300; + for (i = 0; i < 8; i++) { + if (i == 4) + continue; + reg16 |= ((mask & (1 << i)) ? 3 : 1) << (i * 2); + } + pci_write_config16(dev, PCI_DMA_CFG, reg16); +} + +static void i82801dx_lpc_decode_en(struct device *dev) +{ + /* Decode 0x3F8-0x3FF (COM1) for COMA port, 0x2F8-0x2FF (COM2) for COMB. + * LPT decode defaults to 0x378-0x37F and 0x778-0x77F. + * Floppy decode defaults to 0x3F0-0x3F5, 0x3F7. + * We also need to set the value for LPC I/F Enables Register. + */ + pci_write_config8(dev, COM_DEC, 0x10); + pci_write_config16(dev, LPC_EN, 0x300F); +} + +/* ICH4 does not mention HPET in the docs, but + * all ICH3 and ICH4 do have HPETs built in. + */ +static void enable_hpet(struct device *dev) +{ + u32 reg32, hpet, val; + + /* Set HPET base address and enable it */ + printk(BIOS_DEBUG, "Enabling HPET at 0x%x\n", HPET_BASE_ADDRESS); + reg32 = pci_read_config32(dev, GEN_CNTL); + /* + * Bit 17 is HPET enable bit. + * Bit 16:15 control the HPET base address. + */ + reg32 &= ~(3 << 15); /* Clear it */ + + hpet = HPET_BASE_ADDRESS >> 12; + hpet &= 0x3; + + reg32 |= (hpet << 15); + reg32 |= (1 << 17); /* Enable HPET. */ + pci_write_config32(dev, GEN_CNTL, reg32); + + /* Check to see whether it took */ + reg32 = pci_read_config32(dev, GEN_CNTL); + val = reg32 >> 15; + val &= 0x7; + + if ((val & 0x4) && (hpet == (val & 0x3))) { + printk(BIOS_INFO, "HPET enabled at 0x%x\n", HPET_BASE_ADDRESS); + } else { + printk(BIOS_WARNING, "HPET was not enabled correctly\n"); + reg32 &= ~(1 << 17); /* Clear Enable */ + pci_write_config32(dev, GEN_CNTL, reg32); + } +} + +static void lpc_init(struct device *dev) +{ + i82801dx_enable_acpi(dev); + /* IO APIC initialization. */ + i82801dx_enable_ioapic(dev); + + i82801dx_enable_serial_irqs(dev); + + /* Setup the PIRQ. */ + i82801dx_pirq_init(dev); + + /* Setup power options. */ + i82801dx_power_options(dev); + + /* Set the state of the GPIO lines. */ + gpio_init(dev); + + /* Initialize the real time clock. */ + i82801dx_rtc_init(dev); + + /* Route DMA. */ + i82801dx_lpc_route_dma(dev, 0xff); + + /* Initialize ISA DMA. */ + isa_dma_init(); + + /* Setup decode ports and LPC I/F enables. */ + i82801dx_lpc_decode_en(dev); + + /* Initialize the High Precision Event Timers */ + enable_hpet(dev); + + setup_i8259(); +} + +static void i82801dx_lpc_read_resources(struct device *dev) +{ + struct resource *res; + + /* Get the normal PCI resources of this device. */ + pci_dev_read_resources(dev); + + /* Add an extra subtractive resource for both memory and I/O. */ + res = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0)); + res->base = 0; + res->size = 0x1000; + res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | + IORESOURCE_ASSIGNED | IORESOURCE_FIXED; + + res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0)); + res->base = 0xff800000; + res->size = 0x00800000; /* 8 MB for flash */ + res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | + IORESOURCE_ASSIGNED | IORESOURCE_FIXED; + + res = new_resource(dev, 3); /* IOAPIC */ + res->base = IO_APIC_ADDR; + res->size = 0x00001000; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; +} + +static struct device_operations lpc_ops = { + .read_resources = i82801dx_lpc_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = lpc_init, + .scan_bus = scan_static_bus, + .enable = i82801dx_enable, +}; + +/* 82801DB/DBL */ +static const struct pci_driver lpc_driver_db __pci_driver = { + .ops = &lpc_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82801DB_LPC, +}; + +/* 82801DBM */ +static const struct pci_driver lpc_driver_dbm __pci_driver = { + .ops = &lpc_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82801DBM_LPC, +}; diff --git a/src/southbridge/intel/i82801dx/usb.c b/src/southbridge/intel/i82801dx/usb.c new file mode 100644 index 0000000000..16ec3077e9 --- /dev/null +++ b/src/southbridge/intel/i82801dx/usb.c @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include "i82801dx.h" + +static void usb_init(struct device *dev) +{ + printk(BIOS_DEBUG, "USB: Setting up controller.. "); + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE); + printk(BIOS_DEBUG, "done.\n"); +} + +static struct device_operations usb_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = usb_init, + .enable = i82801dx_enable, +}; + +/* 82801DB/DBL/DBM USB1 */ +static const struct pci_driver usb_driver_1 __pci_driver = { + .ops = &usb_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82801DB_USB1, +}; + +/* 82801DB/DBL/DBM USB2 */ +static const struct pci_driver usb_driver_2 __pci_driver = { + .ops = &usb_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82801DB_USB2, +}; + +/* 82801DB/DBL/DBM USB3 */ +static const struct pci_driver usb_driver_3 __pci_driver = { + .ops = &usb_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82801DB_USB3, +}; diff --git a/src/southbridge/intel/i82801dx/usb2.c b/src/southbridge/intel/i82801dx/usb2.c new file mode 100644 index 0000000000..0fe7c9399b --- /dev/null +++ b/src/southbridge/intel/i82801dx/usb2.c @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <device/pci_ehci.h> +#include "i82801dx.h" + +static void usb2_init(struct device *dev) +{ + printk(BIOS_DEBUG, "USB: Setting up controller.. "); + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE); + printk(BIOS_DEBUG, "done.\n"); +} + +static struct device_operations usb2_ops = { + .read_resources = pci_ehci_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = usb2_init, + .enable = i82801dx_enable, +}; + +/* 82801DB/DBM USB 2.0 */ +static const struct pci_driver usb2_driver __pci_driver = { + .ops = &usb2_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82801DB_EHCI, +}; diff --git a/src/southbridge/intel/i82870/82870.h b/src/southbridge/intel/i82870/82870.h new file mode 100644 index 0000000000..c8adb0eb95 --- /dev/null +++ b/src/southbridge/intel/i82870/82870.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* for io APIC 1461 */ +#define MBAR 0x10 +#define ABAR 0x40 + +/* for pci bridge 1460 */ +#define MTT 0x042 +#define HCCR 0x0f0 +#define ACNF 0x0e0 +#define STRP 0x44 // Strap status register + +#define STRP_EN133 0x0001 // 133 MHz-capable (Px_133EN) +#define STRP_HPCAP 0x0002 // Hot-plug capable (Hx_SLOT zero/nonzero) + +#define ACNF_SYNCPH 0x0010 // PCI(-X) input clock is synchronous to hub input clock diff --git a/src/southbridge/intel/i82870/Kconfig b/src/southbridge/intel/i82870/Kconfig new file mode 100644 index 0000000000..b56113b8e2 --- /dev/null +++ b/src/southbridge/intel/i82870/Kconfig @@ -0,0 +1,2 @@ +config SOUTHBRIDGE_INTEL_I82870 + bool diff --git a/src/southbridge/intel/i82870/Makefile.inc b/src/southbridge/intel/i82870/Makefile.inc new file mode 100644 index 0000000000..d6ae171926 --- /dev/null +++ b/src/southbridge/intel/i82870/Makefile.inc @@ -0,0 +1,6 @@ +ifeq ($(CONFIG_SOUTHBRIDGE_INTEL_I82870),y) + +ramstage-y += ioapic.c +ramstage-y += pcibridge.c + +endif diff --git a/src/southbridge/intel/i82870/ioapic.c b/src/southbridge/intel/i82870/ioapic.c new file mode 100644 index 0000000000..65fecd10f8 --- /dev/null +++ b/src/southbridge/intel/i82870/ioapic.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/ioapic.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <assert.h> +#include "82870.h" + +static int num_p64h2_ioapics = 0; + +static void p64h2_ioapic_enable(struct device *dev) +{ + /* We have to enable MEM and Bus Master for IOAPIC */ + uint16_t command = PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + + pci_write_config16(dev, PCI_COMMAND, command); +} + +/** + * Configure one of the IOAPICs in a P64H2. + * + * Note that a PCI bus scan will detect both IOAPICs, so this function + * will be called twice for each P64H2 in the system. + * + * @param dev PCI bus/device/function of P64H2 IOAPIC. + * NOTE: There are two IOAPICs per P64H2, at D28:F0 and D30:F0. + */ +static void p64h2_ioapic_init(struct device *dev) +{ + uint32_t memoryBase; + int apic_index, apic_id; + + apic_index = num_p64h2_ioapics; + num_p64h2_ioapics++; + + // A note on IOAPIC addresses: + // 0 and 1 are used for the local APICs of the dual virtual + // (hyper-threaded) CPUs of physical CPU 0 (devicetree.cb). + // 6 and 7 are used for the local APICs of the dual virtual + // (hyper-threaded) CPUs of physical CPU 1 (devicetree.cb). + // 2 is used for the IOAPIC in the 82801 southbridge (hard-coded in i82801xx_lpc.c) + + // Map APIC index into APIC ID + // IDs 3, 4, 5, and 8+ are available (see above note) + + if (apic_index < 3) + apic_id = apic_index + 3; + else + apic_id = apic_index + 5; + + ASSERT(apic_id < 16); // ID is only 4 bits + + // Read the MBAR address for setting up the IOAPIC in memory space + // NOTE: this address was assigned during enumeration of the bus + + memoryBase = pci_read_config32(dev, PCI_BASE_ADDRESS_0); + + set_ioapic_id((void *)memoryBase, apic_id); + + // Use Processor System Bus to deliver interrupts + ioapic_set_boot_config((void *)memoryBase, true); +} + +static struct device_operations ioapic_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = p64h2_ioapic_init, + .enable = p64h2_ioapic_enable, +}; + +static const struct pci_driver ioapic_driver __pci_driver = { + .ops = &ioapic_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82870_1E0, + +}; diff --git a/src/southbridge/intel/i82870/pcibridge.c b/src/southbridge/intel/i82870/pcibridge.c new file mode 100644 index 0000000000..00bf6bb1cf --- /dev/null +++ b/src/southbridge/intel/i82870/pcibridge.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include "82870.h" + +static void p64h2_pcix_init(struct device *dev) +{ + u32 dword; + u8 byte; + + /* The purpose of changes to HCCR, ACNF, and MTT is to speed + * up the PCI bus for cards having high speed transfers. + */ + dword = 0xc2040002; + pci_write_config32(dev, HCCR, dword); + dword = 0x0000c3bf; + pci_write_config32(dev, ACNF, dword); + byte = 0x08; + pci_write_config8(dev, MTT, byte); + +} +static struct device_operations pcix_ops = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = p64h2_pcix_init, + .scan_bus = pci_scan_bridge, + .reset_bus = pci_bus_reset, +}; + +static const struct pci_driver pcix_driver __pci_driver = { + .ops = &pcix_ops, + .vendor = PCI_VID_INTEL, + .device = PCI_DID_INTEL_82870_1F0, +}; + +struct chip_operations southbridge_intel_i82870_ops = { + CHIP_NAME("Intel 82870") +}; diff --git a/src/superio/smsc/Makefile.inc b/src/superio/smsc/Makefile.inc index e77ca497fd..86cf9c510f 100644 --- a/src/superio/smsc/Makefile.inc +++ b/src/superio/smsc/Makefile.inc @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only subdirs-y += fdc37n972 +subdirs-y += lpc47m10x subdirs-y += lpc47m15x subdirs-y += lpc47n207 subdirs-y += lpc47n217 diff --git a/src/superio/smsc/lpc47m10x/Kconfig b/src/superio/smsc/lpc47m10x/Kconfig new file mode 100644 index 0000000000..9d6b9481b1 --- /dev/null +++ b/src/superio/smsc/lpc47m10x/Kconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SUPERIO_SMSC_LPC47M10X + bool diff --git a/src/superio/smsc/lpc47m10x/Makefile.inc b/src/superio/smsc/lpc47m10x/Makefile.inc new file mode 100644 index 0000000000..d0d4cbc28c --- /dev/null +++ b/src/superio/smsc/lpc47m10x/Makefile.inc @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +bootblock-$(CONFIG_SUPERIO_SMSC_LPC47M10X) += early_serial.c +romstage-$(CONFIG_SUPERIO_SMSC_LPC47M10X) += early_serial.c +ramstage-$(CONFIG_SUPERIO_SMSC_LPC47M10X) += superio.c diff --git a/src/superio/smsc/lpc47m10x/early_serial.c b/src/superio/smsc/lpc47m10x/early_serial.c new file mode 100644 index 0000000000..38a13f56a7 --- /dev/null +++ b/src/superio/smsc/lpc47m10x/early_serial.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <arch/io.h> +#include <device/pnp_ops.h> +#include <device/pnp.h> +#include <stdint.h> +#include "lpc47m10x.h" + +void pnp_enter_conf_state(pnp_devfn_t dev) +{ + u16 port = dev >> 8; + outb(0x55, port); +} + +void pnp_exit_conf_state(pnp_devfn_t dev) +{ + u16 port = dev >> 8; + outb(0xaa, port); +} + +/** + * Configure the base I/O port of the specified serial device and enable the + * serial device. + * + * @param dev High 8 bits = Super I/O port, low 8 bits = logical device number. + * @param iobase Processor I/O port address to assign to this serial device. + */ +void lpc47m10x_enable_serial(pnp_devfn_t dev, u16 iobase) +{ + 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_enable(dev, 1); + pnp_exit_conf_state(dev); +} diff --git a/src/superio/smsc/lpc47m10x/lpc47m10x.h b/src/superio/smsc/lpc47m10x/lpc47m10x.h new file mode 100644 index 0000000000..b8bd3a0246 --- /dev/null +++ b/src/superio/smsc/lpc47m10x/lpc47m10x.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef SUPERIO_SMSC_LPC47M10X_H +#define SUPERIO_SMSC_LPC47M10X_H + +#include <device/pnp_type.h> +#include <stdint.h> + +#define LPC47M10X2_FDC 0 /* Floppy */ +#define LPC47M10X2_PP 3 /* Parallel Port */ +#define LPC47M10X2_SP1 4 /* Com1 */ +#define LPC47M10X2_SP2 5 /* Com2 */ +#define LPC47M10X2_KBC 7 /* Keyboard & Mouse */ +#define LPC47M10X2_GAME 9 /* GAME */ +#define LPC47M10X2_PME 10 /* PME reg*/ +#define LPC47M10X2_MPU 11 /* MPU-401 MIDI */ + +#define LPC47M10X2_MAX_CONFIG_REGISTER 0x5F + +void lpc47m10x_enable_serial(pnp_devfn_t dev, u16 iobase); + +void pnp_enter_conf_state(pnp_devfn_t dev); +void pnp_exit_conf_state(pnp_devfn_t dev); + +#endif /* SUPERIO_SMSC_LPC47M10X_H */ diff --git a/src/superio/smsc/lpc47m10x/superio.c b/src/superio/smsc/lpc47m10x/superio.c new file mode 100644 index 0000000000..cb297a5b14 --- /dev/null +++ b/src/superio/smsc/lpc47m10x/superio.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <device/device.h> +#include <device/pnp.h> +#include <superio/conf_mode.h> +#include <pc80/keyboard.h> + +#include "lpc47m10x.h" + +/** + * Initialize the specified Super I/O device. + * + * Devices other than COM ports and the keyboard controller are ignored. + * For COM ports, we configure the baud rate. + * + * @param dev Pointer to structure describing a Super I/O device. + */ +static void lpc47m10x_init(struct device *dev) +{ + if (!dev->enabled) + return; + + switch (dev->path.pnp.device) { + case LPC47M10X2_KBC: + pc_keyboard_init(NO_AUX_DEVICE); + break; + } +} + +static struct device_operations ops = { + .read_resources = pnp_read_resources, + .set_resources = pnp_set_resources, + .enable_resources = pnp_enable_resources, + .enable = pnp_alt_enable, + .init = lpc47m10x_init, + .ops_pnp_mode = &pnp_conf_mode_55_aa, +}; + +static struct pnp_info pnp_dev_info[] = { + { NULL, LPC47M10X2_FDC, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0, 0x07f8, }, + { NULL, LPC47M10X2_PP, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0, 0x07f8, }, + { NULL, LPC47M10X2_SP1, PNP_IO0 | PNP_IRQ0, 0x07f8, }, + { NULL, LPC47M10X2_SP2, PNP_IO0 | PNP_IRQ0, 0x07f8, }, + { NULL, LPC47M10X2_KBC, PNP_IO0 | PNP_IO1 | PNP_IRQ0 | PNP_IRQ1, + 0x07ff, 0x07ff, }, + { NULL, LPC47M10X2_PME, PNP_IO0, 0x0f80, }, +}; + +/** + * Create device structures and allocate resources to devices specified in the + * pnp_dev_info array (above). + * + * @param dev Pointer to structure describing a Super I/O device. + */ +static void enable_dev(struct device *dev) +{ + pnp_enable_devices(dev, &ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info); +} + +struct chip_operations superio_smsc_lpc47m10x_ops = { + CHIP_NAME("SMSC LPC47M10x Super I/O") + .enable_dev = enable_dev +}; |