summaryrefslogtreecommitdiff
path: root/src/mainboard/dell/optiplex_9010
diff options
context:
space:
mode:
Diffstat (limited to 'src/mainboard/dell/optiplex_9010')
-rw-r--r--src/mainboard/dell/optiplex_9010/Kconfig69
-rw-r--r--src/mainboard/dell/optiplex_9010/Kconfig.name2
-rw-r--r--src/mainboard/dell/optiplex_9010/Makefile.inc22
-rw-r--r--src/mainboard/dell/optiplex_9010/acpi/ec.asl0
-rw-r--r--src/mainboard/dell/optiplex_9010/acpi/platform.asl54
-rw-r--r--src/mainboard/dell/optiplex_9010/acpi/superio.asl13
-rw-r--r--src/mainboard/dell/optiplex_9010/acpi_tables.c9
-rw-r--r--src/mainboard/dell/optiplex_9010/board_info.txt6
-rw-r--r--src/mainboard/dell/optiplex_9010/cmos.default7
-rw-r--r--src/mainboard/dell/optiplex_9010/cmos.layout95
-rw-r--r--src/mainboard/dell/optiplex_9010/data.vbtbin0 -> 4281 bytes
-rw-r--r--src/mainboard/dell/optiplex_9010/devicetree.cb99
-rw-r--r--src/mainboard/dell/optiplex_9010/dsdt.asl32
-rw-r--r--src/mainboard/dell/optiplex_9010/early_init.c47
-rw-r--r--src/mainboard/dell/optiplex_9010/gma-mainboard.ads19
-rw-r--r--src/mainboard/dell/optiplex_9010/gpio.c208
-rw-r--r--src/mainboard/dell/optiplex_9010/hda_verb.c36
-rw-r--r--src/mainboard/dell/optiplex_9010/mainboard.c191
-rw-r--r--src/mainboard/dell/optiplex_9010/romstage.c53
-rw-r--r--src/mainboard/dell/optiplex_9010/sch5545_ec.c726
-rw-r--r--src/mainboard/dell/optiplex_9010/sch5545_ec.h28
-rw-r--r--src/mainboard/dell/optiplex_9010/sch5545_ec_early.c116
-rw-r--r--src/mainboard/dell/optiplex_9010/smihandler.c47
23 files changed, 1879 insertions, 0 deletions
diff --git a/src/mainboard/dell/optiplex_9010/Kconfig b/src/mainboard/dell/optiplex_9010/Kconfig
new file mode 100644
index 0000000000..6a3feb3751
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/Kconfig
@@ -0,0 +1,69 @@
+if BOARD_DELL_OPTIPLEX_9010
+
+config BOARD_SPECIFIC_OPTIONS
+ def_bool y
+ select BOARD_ROMSIZE_KB_12288
+ select HAVE_ACPI_RESUME
+ select HAVE_ACPI_TABLES
+ select INTEL_INT15
+ select NORTHBRIDGE_INTEL_SANDYBRIDGE
+ select SERIRQ_CONTINUOUS_MODE
+ select SOUTHBRIDGE_INTEL_C216
+ select USE_NATIVE_RAMINIT
+ select MAINBOARD_HAS_LPC_TPM
+ select MAINBOARD_HAS_TPM1
+ select MAINBOARD_USES_IFD_GBE_REGION
+ select SUPERIO_SMSC_SCH5545
+ select MAINBOARD_HAS_LIBGFXINIT
+ select INTEL_GMA_HAVE_VBT
+ select HAVE_OPTION_TABLE
+ select HAVE_CMOS_DEFAULT
+ select PCIEXP_L1_SUB_STATE
+
+config MAINBOARD_DIR
+ string
+ default dell/optiplex_9010
+
+config MAINBOARD_PART_NUMBER
+ string
+ default "OptiPlex 9010"
+
+config VGA_BIOS_FILE
+ string
+ default "pci8086,0162.rom"
+
+config VGA_BIOS_ID
+ string
+ default "8086,0162"
+
+config DRAM_RESET_GATE_GPIO
+ int
+ default 60
+
+config MAX_CPUS
+ int
+ default 8
+
+config USBDEBUG_HCD_INDEX
+ int
+ default 2
+
+config CBFS_SIZE
+ hex
+ default 0x600000
+
+config INCLUDE_SMSC_SCH5545_EC_FW
+ bool "Include SMSC SCH5545 EC firmware binary"
+ default n
+ help
+ This option allows to add the SMSC SCH5545 Environmental Controller
+ firmware binary. The firmware must be loaded after each power failure
+ in order to properly initialize the fan control, because EC loses its
+ configuration when power is cut off. Otherwise the fans will keep
+ running at full speed after power failure.
+
+config SMSC_SCH5545_EC_FW_FILE
+ string "File path to the SMSC SCH5545 EC firmware binary"
+ depends on INCLUDE_SMSC_SCH5545_EC_FW
+
+endif
diff --git a/src/mainboard/dell/optiplex_9010/Kconfig.name b/src/mainboard/dell/optiplex_9010/Kconfig.name
new file mode 100644
index 0000000000..96707c2890
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/Kconfig.name
@@ -0,0 +1,2 @@
+config BOARD_DELL_OPTIPLEX_9010
+ bool "OptiPlex 9010 SFF"
diff --git a/src/mainboard/dell/optiplex_9010/Makefile.inc b/src/mainboard/dell/optiplex_9010/Makefile.inc
new file mode 100644
index 0000000000..7a8a68423a
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/Makefile.inc
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+smm-y += smihandler.c
+
+bootblock-y += gpio.c
+romstage-y += gpio.c
+
+bootblock-y += early_init.c
+romstage-y += early_init.c
+
+bootblock-y += sch5545_ec_early.c
+
+romstage-y += sch5545_ec.c
+
+ramstage-y += sch5545_ec.c
+ramstage-$(CONFIG_MAINBOARD_USE_LIBGFXINIT) += gma-mainboard.ads
+
+ifeq ($(CONFIG_INCLUDE_SMSC_SCH5545_EC_FW),y)
+cbfs-files-y += sch5545_ecfw.bin
+sch5545_ecfw.bin-file := $(call strip_quotes,$(CONFIG_SMSC_SCH5545_EC_FW_FILE))
+sch5545_ecfw.bin-type := raw
+endif
diff --git a/src/mainboard/dell/optiplex_9010/acpi/ec.asl b/src/mainboard/dell/optiplex_9010/acpi/ec.asl
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/acpi/ec.asl
diff --git a/src/mainboard/dell/optiplex_9010/acpi/platform.asl b/src/mainboard/dell/optiplex_9010/acpi/platform.asl
new file mode 100644
index 0000000000..f890b5af54
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/acpi/platform.asl
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+Method(_WAK,1)
+{
+ \_SB.PCI0.LPCB.SIO1.SIOW (Arg0)
+
+ Return(Package(){0,0})
+}
+
+Method(_PTS,1)
+{
+ \_SB.PCI0.LPCB.SIO1.SIOS (Arg0)
+}
+
+Scope (\_SB)
+{
+ Device (PWRB)
+ {
+ Name (_HID, EisaId ("PNP0C0C"))
+ Name (_UID, 0xAA)
+ Name (_STA, 0x0B)
+
+ Name (_PRW, Package() { 8, 3})
+ }
+}
+
+Scope (\_GPE)
+{
+ Method (_L08, 0, NotSerialized)
+ {
+ \_SB.PCI0.LPCB.SIO1.SIOH ()
+ Notify (\_SB.PWRB, 0x02)
+ }
+
+ Method (_L0D, 0, NotSerialized)
+ {
+ Notify (\_SB.PCI0.EHC1, 0x02)
+ Notify (\_SB.PCI0.EHC2, 0x02)
+ Notify (\_SB.PCI0.GLAN, 0x02)
+ }
+
+ Method (_L09, 0, NotSerialized)
+ {
+ Notify (\_SB.PCI0.RP01, 0x02)
+ Notify (\_SB.PCI0.RP02, 0x02)
+ Notify (\_SB.PCI0.RP03, 0x02)
+ Notify (\_SB.PCI0.RP04, 0x02)
+ Notify (\_SB.PCI0.RP05, 0x02)
+ Notify (\_SB.PCI0.RP06, 0x02)
+ Notify (\_SB.PCI0.RP07, 0x02)
+ Notify (\_SB.PCI0.RP08, 0x02)
+ Notify (\_SB.PCI0.PEGP, 0x02)
+ }
+}
diff --git a/src/mainboard/dell/optiplex_9010/acpi/superio.asl b/src/mainboard/dell/optiplex_9010/acpi/superio.asl
new file mode 100644
index 0000000000..fa1dfcf726
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/acpi/superio.asl
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#undef SUPERIO_DEV
+#undef SUPERIO_PNP_BASE
+#define SUPERIO_DEV SIO1
+#define SUPERIO_PNP_BASE 0x2e
+
+#define SCH5545_RUNTIME_BASE 0xa00
+#define SCH5545_EMI_BASE 0xa40
+#define SCH5545_SHOW_UARTA
+#define SCH5545_SHOW_KBC
+
+#include <superio/smsc/sch5545/acpi/superio.asl>
diff --git a/src/mainboard/dell/optiplex_9010/acpi_tables.c b/src/mainboard/dell/optiplex_9010/acpi_tables.c
new file mode 100644
index 0000000000..33feed2a98
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/acpi_tables.c
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <southbridge/intel/bd82x6x/nvs.h>
+
+void acpi_create_gnvs(global_nvs_t *gnvs)
+{
+ gnvs->tcrt = 100;
+ gnvs->tpsv = 90;
+}
diff --git a/src/mainboard/dell/optiplex_9010/board_info.txt b/src/mainboard/dell/optiplex_9010/board_info.txt
new file mode 100644
index 0000000000..f49f3ba416
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/board_info.txt
@@ -0,0 +1,6 @@
+Category: desktop
+ROM protocol: SPI
+ROM package: SOIC-8, SOIC-16
+ROM socketed: n
+Flashrom support: y
+Release year: 2012
diff --git a/src/mainboard/dell/optiplex_9010/cmos.default b/src/mainboard/dell/optiplex_9010/cmos.default
new file mode 100644
index 0000000000..ccc7e64625
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/cmos.default
@@ -0,0 +1,7 @@
+boot_option=Fallback
+debug_level=Debug
+power_on_after_fail=Disable
+nmi=Enable
+sata_mode=AHCI
+gfx_uma_size=128M
+fan_full_speed=Disable
diff --git a/src/mainboard/dell/optiplex_9010/cmos.layout b/src/mainboard/dell/optiplex_9010/cmos.layout
new file mode 100644
index 0000000000..dcf9639d3f
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/cmos.layout
@@ -0,0 +1,95 @@
+## SPDX-License-Identifier: GPL-2.0-only
+
+# -----------------------------------------------------------------
+entries
+
+# -----------------------------------------------------------------
+# Status Register A
+# -----------------------------------------------------------------
+# Status Register B
+# -----------------------------------------------------------------
+# Status Register C
+#96 4 r 0 status_c_rsvd
+#100 1 r 0 uf_flag
+#101 1 r 0 af_flag
+#102 1 r 0 pf_flag
+#103 1 r 0 irqf_flag
+# -----------------------------------------------------------------
+# Status Register D
+#104 7 r 0 status_d_rsvd
+#111 1 r 0 valid_cmos_ram
+# -----------------------------------------------------------------
+# Diagnostic Status Register
+#112 8 r 0 diag_rsvd1
+
+# -----------------------------------------------------------------
+0 120 r 0 reserved_memory
+
+# -----------------------------------------------------------------
+# RTC_BOOT_BYTE (coreboot hardcoded)
+384 1 e 4 boot_option
+388 4 h 0 reboot_counter
+
+# -----------------------------------------------------------------
+# coreboot config options: console
+395 4 e 6 debug_level
+
+#400 8 r 0 reserved for century byte
+
+# coreboot config options: southbridge
+408 1 e 1 nmi
+409 2 e 7 power_on_after_fail
+411 1 e 8 sata_mode
+
+# coreboot config options: EC
+412 1 e 1 fan_full_speed
+
+# coreboot config options: northbridge
+432 3 e 9 gfx_uma_size
+
+# SandyBridge MRC Scrambler Seed values
+896 32 r 0 mrc_scrambler_seed
+928 32 r 0 mrc_scrambler_seed_s3
+960 16 r 0 mrc_scrambler_seed_chk
+
+# coreboot config options: check sums
+984 16 h 0 check_sum
+
+# -----------------------------------------------------------------
+
+enumerations
+
+#ID value text
+1 0 Disable
+1 1 Enable
+2 0 Enable
+2 1 Disable
+4 0 Fallback
+4 1 Normal
+6 0 Emergency
+6 1 Alert
+6 2 Critical
+6 3 Error
+6 4 Warning
+6 5 Notice
+6 6 Info
+6 7 Debug
+6 8 Spew
+7 0 Disable
+7 1 Enable
+7 2 Keep
+8 0 AHCI
+8 1 Compatible
+9 0 32M
+9 1 64M
+9 2 96M
+9 3 128M
+9 4 160M
+9 5 192M
+9 6 224M
+
+
+# -----------------------------------------------------------------
+checksums
+
+checksum 392 447 984
diff --git a/src/mainboard/dell/optiplex_9010/data.vbt b/src/mainboard/dell/optiplex_9010/data.vbt
new file mode 100644
index 0000000000..d1a95e8632
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/data.vbt
Binary files differ
diff --git a/src/mainboard/dell/optiplex_9010/devicetree.cb b/src/mainboard/dell/optiplex_9010/devicetree.cb
new file mode 100644
index 0000000000..6a21d20b74
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/devicetree.cb
@@ -0,0 +1,99 @@
+chip northbridge/intel/sandybridge
+ device cpu_cluster 0 on
+ chip cpu/intel/model_206ax
+ register "c1_acpower" = "1"
+ register "c1_battery" = "1"
+ register "c2_acpower" = "3"
+ register "c2_battery" = "3"
+ register "c3_acpower" = "5"
+ register "c3_battery" = "5"
+ register "tcc_offset" = "5" # TCC of 95C
+ device lapic 0 on end
+ device lapic 0xacac off end
+ end
+ end
+ device domain 0x0 on
+ subsystemid 0x1028 0x052c inherit
+
+ device pci 00.0 on end # Host bridge Host bridge
+ device pci 01.0 on # PEG1 (blue slot1)
+ smbios_slot_desc "0xB6" "4" "SLOT1" "0x0D"
+ end
+ device pci 02.0 on end # Internal graphics VGA controller
+ device pci 06.0 off end # PEG2
+
+ chip southbridge/intel/bd82x6x # Intel Series 7 Panther Point PCH
+ register "gpe0_en" = "0x00000146"
+ register "alt_gp_smi_en" = "0x0004"
+ register "gpi2_routing" = "1"
+ register "gpi12_routing" = "2"
+ register "c2_latency" = "0x0065"
+ register "gen1_dec" = "0x007c0a01"
+ register "gen2_dec" = "0x007c0901"
+ register "gen3_dec" = "0x003c07e1"
+ register "gen4_dec" = "0x001c0901"
+ register "pcie_port_coalesce" = "1"
+ register "sata_interface_speed_support" = "0x3"
+ register "sata_port_map" = "0x7"
+ register "spi_lvscc" = "0x2005"
+ register "spi_uvscc" = "0x2005"
+ register "superspeed_capable_ports" = "0x0000000f"
+ register "xhci_overcurrent_mapping" = "0x08040201"
+ register "xhci_switchable_ports" = "0x0000000f"
+ device pci 14.0 on end # USB 3.0 Controller
+ device pci 16.0 off end # Management Engine Interface 1
+ device pci 16.1 off end # Management Engine Interface 2
+ device pci 16.2 off end # Management Engine IDE-R
+ device pci 16.3 off end # Management Engine KT
+ device pci 19.0 on end # Intel Gigabit Ethernet
+ device pci 1a.0 on end # USB2 EHCI #2
+ device pci 1b.0 on end # High Definition Audio controller
+ device pci 1c.0 off end # PCIe Port #1
+ device pci 1c.1 off end # PCIe Port #2
+ device pci 1c.2 off end # PCIe Port #3
+ device pci 1c.3 off end # PCIe Port #4
+ device pci 1c.4 on # PCIe Port #5
+ smbios_slot_desc "0xB6" "4" "SLOT2" "0x0A"
+ end
+ device pci 1c.5 on end # PCIe Port #6
+ device pci 1c.6 on end # PCIe Port #7
+ device pci 1c.7 on end # PCIe Port #8
+ device pci 1d.0 on end # USB2 EHCI #1
+ device pci 1e.0 off end # PCI bridge
+ device pci 1f.0 on # LPC bridge
+ chip drivers/pc80/tpm
+ device pnp 0c31.0 on end
+ end
+ chip superio/smsc/sch5545
+ device pnp 2e.c on # LPC
+ io 0x60 = 0x2e
+ end
+ device pnp 2e.0 on # EMI
+ io 0x60 = 0xa40
+ end
+ device pnp 2e.1 on # KBC/PS2M
+ io 0x60 = 0x60
+ irq 0x70 = 1
+ irq 0x72 = 12
+ end
+ device pnp 2e.7 on # UART1
+ io 0x60 = 0x3f8
+ irq 0x70 = 4
+ irq 0xf0 = 0x02
+ end
+ device pnp 2e.8 off end # UART2
+ device pnp 2e.a on # Runtime registers
+ io 0x60 = 0xa00
+ irq 0x70 = 9 # PME
+ end
+ device pnp 2e.b off end # Floppy
+ device pnp 2e.11 off end # PP
+ end
+ end
+ device pci 1f.2 on end # SATA Controller 1
+ device pci 1f.3 on end # SMBus
+ device pci 1f.5 off end # SATA Controller 2
+ device pci 1f.6 on end # Thermal
+ end
+ end
+end
diff --git a/src/mainboard/dell/optiplex_9010/dsdt.asl b/src/mainboard/dell/optiplex_9010/dsdt.asl
new file mode 100644
index 0000000000..6a6dcb4c36
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/dsdt.asl
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+DefinitionBlock(
+ "dsdt.aml",
+ "DSDT",
+ 0x02, /* DSDT revision: ACPI 2.0 and up */
+ OEM_ID,
+ ACPI_TABLE_CREATOR,
+ 0x20141018 /* OEM revision */
+)
+{
+ #include "acpi/platform.asl"
+ #include <cpu/intel/common/acpi/cpu.asl>
+ #include <southbridge/intel/common/acpi/platform.asl>
+ #include <southbridge/intel/bd82x6x/acpi/globalnvs.asl>
+ #include <southbridge/intel/common/acpi/sleepstates.asl>
+
+ Scope (\_SB)
+ {
+ Device (PCI0)
+ {
+ #include <northbridge/intel/sandybridge/acpi/sandybridge.asl>
+ #include <southbridge/intel/bd82x6x/acpi/pch.asl>
+ Device (GLAN)
+ {
+ Name (_ADR, 0x00190000)
+ Name (_PRW, Package() { 13, 4 })
+ }
+ }
+ }
+}
diff --git a/src/mainboard/dell/optiplex_9010/early_init.c b/src/mainboard/dell/optiplex_9010/early_init.c
new file mode 100644
index 0000000000..5469c94f15
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/early_init.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bootblock_common.h>
+#include <device/pci_ops.h>
+#include <northbridge/intel/sandybridge/sandybridge.h>
+#include <southbridge/intel/bd82x6x/pch.h>
+#include <superio/smsc/sch5545/sch5545.h>
+#include <superio/smsc/sch5545/sch5545_emi.h>
+
+#include "sch5545_ec.h"
+
+const struct southbridge_usb_port mainboard_usb_ports[] = {
+ { 1, 6, 0 },
+ { 1, 6, 0 },
+ { 1, 1, 1 },
+ { 1, 1, 1 },
+ { 1, 1, 2 },
+ { 1, 1, 2 },
+ { 1, 6, 3 },
+ { 1, 6, 3 },
+ { 1, 6, 4 },
+ { 1, 6, 4 },
+ { 1, 6, 5 },
+ { 1, 1, 5 },
+ { 1, 1, 6 },
+ { 1, 6, 6 },
+};
+
+void bootblock_mainboard_early_init(void)
+{
+ /*
+ * FIXME: the board gets stuck in reset loop in
+ * mainboard_romstage_entry. Avoid that by clearing SSKPD
+ */
+ pci_write_config32(HOST_BRIDGE, MCHBAR, (uintptr_t)DEFAULT_MCHBAR | 1);
+ pci_write_config32(HOST_BRIDGE, MCHBAR + 4, (0LL + (uintptr_t)DEFAULT_MCHBAR) >> 32);
+ MCHBAR16(SSKPD_HI) = 0;
+
+ sch5545_early_init(0x2e);
+ /* Bare EC and SIO GPIO initialization which allows to enable serial port */
+ sch5545_emi_init(0x2e);
+ sch5545_emi_disable_interrupts();
+ sch5545_ec_early_init();
+
+ if (CONFIG(CONSOLE_SERIAL))
+ sch5545_enable_uart(0x2e, 0);
+}
diff --git a/src/mainboard/dell/optiplex_9010/gma-mainboard.ads b/src/mainboard/dell/optiplex_9010/gma-mainboard.ads
new file mode 100644
index 0000000000..74b50645e6
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/gma-mainboard.ads
@@ -0,0 +1,19 @@
+-- SPDX-License-Identifier: GPL-2.0-or-later
+
+with HW.GFX.GMA;
+with HW.GFX.GMA.Display_Probing;
+
+use HW.GFX.GMA;
+use HW.GFX.GMA.Display_Probing;
+
+private package GMA.Mainboard is
+
+ ports : constant Port_List :=
+ (DP1,
+ DP2,
+ HDMI1,
+ HDMI2,
+ Analog,
+ others => Disabled);
+
+end GMA.Mainboard;
diff --git a/src/mainboard/dell/optiplex_9010/gpio.c b/src/mainboard/dell/optiplex_9010/gpio.c
new file mode 100644
index 0000000000..d01e6221a6
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/gpio.c
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <southbridge/intel/common/gpio.h>
+
+static const struct pch_gpio_set1 pch_gpio_set1_mode = {
+ .gpio0 = GPIO_MODE_NATIVE,
+ .gpio1 = GPIO_MODE_GPIO, /* CHASSIS_ID0 */
+ .gpio2 = GPIO_MODE_GPIO,
+ .gpio3 = GPIO_MODE_GPIO,
+ .gpio4 = GPIO_MODE_GPIO, /* VGA_CBL_DET# */
+ .gpio5 = GPIO_MODE_GPIO,
+ .gpio6 = GPIO_MODE_GPIO, /* PCH_HS_DET# (unused?) */
+ .gpio7 = GPIO_MODE_GPIO, /* SKU2 */
+ .gpio8 = GPIO_MODE_GPIO,
+ .gpio9 = GPIO_MODE_NATIVE,
+ .gpio10 = GPIO_MODE_NATIVE,
+ .gpio11 = GPIO_MODE_GPIO, /* PCIE_X4_WAKE*/
+ .gpio12 = GPIO_MODE_NATIVE,
+ .gpio13 = GPIO_MODE_GPIO, /* PCIE_X1_WAKE (MT/DT only)*/
+ .gpio14 = GPIO_MODE_GPIO,
+ .gpio15 = GPIO_MODE_GPIO,
+ .gpio16 = GPIO_MODE_GPIO,
+ .gpio17 = GPIO_MODE_GPIO, /* CHASSIS_ID1 */
+ .gpio18 = GPIO_MODE_NATIVE,
+ .gpio19 = GPIO_MODE_NATIVE,
+ .gpio20 = GPIO_MODE_GPIO, /* FLEXBAY_HDR_CBL_DET# */
+ .gpio21 = GPIO_MODE_GPIO, /* BOARD_REV0 */
+ .gpio22 = GPIO_MODE_GPIO,
+ .gpio23 = GPIO_MODE_GPIO,
+ .gpio24 = GPIO_MODE_GPIO,
+ .gpio25 = GPIO_MODE_NATIVE,
+ .gpio26 = GPIO_MODE_NATIVE,
+ .gpio27 = GPIO_MODE_GPIO,
+ .gpio28 = GPIO_MODE_GPIO,
+ .gpio29 = GPIO_MODE_GPIO,
+ .gpio30 = GPIO_MODE_NATIVE,
+ .gpio31 = GPIO_MODE_GPIO, /* Password Clear Jumper */
+};
+
+static const struct pch_gpio_set1 pch_gpio_set1_direction = {
+ .gpio1 = GPIO_DIR_INPUT,
+ .gpio2 = GPIO_DIR_INPUT,
+ .gpio3 = GPIO_DIR_INPUT,
+ .gpio4 = GPIO_DIR_INPUT,
+ .gpio5 = GPIO_DIR_INPUT,
+ .gpio6 = GPIO_DIR_INPUT,
+ .gpio7 = GPIO_DIR_INPUT,
+ .gpio8 = GPIO_DIR_OUTPUT,
+ .gpio11 = GPIO_DIR_INPUT,
+ .gpio13 = GPIO_DIR_INPUT,
+ .gpio14 = GPIO_DIR_OUTPUT,
+ .gpio15 = GPIO_DIR_OUTPUT,
+ .gpio16 = GPIO_DIR_INPUT,
+ .gpio17 = GPIO_DIR_INPUT,
+ .gpio20 = GPIO_DIR_INPUT,
+ .gpio21 = GPIO_DIR_INPUT,
+ .gpio22 = GPIO_DIR_INPUT,
+ .gpio23 = GPIO_DIR_INPUT,
+ .gpio24 = GPIO_DIR_INPUT,
+ .gpio27 = GPIO_DIR_OUTPUT,
+ .gpio28 = GPIO_DIR_OUTPUT,
+ .gpio29 = GPIO_DIR_OUTPUT,
+ .gpio31 = GPIO_DIR_INPUT,
+};
+
+static const struct pch_gpio_set1 pch_gpio_set1_level = {
+ .gpio8 = GPIO_LEVEL_HIGH,
+ .gpio14 = GPIO_LEVEL_HIGH,
+ .gpio15 = GPIO_LEVEL_LOW,
+ .gpio27 = GPIO_LEVEL_LOW,
+ .gpio28 = GPIO_LEVEL_LOW,
+ .gpio29 = GPIO_LEVEL_HIGH,
+};
+
+static const struct pch_gpio_set1 pch_gpio_set1_reset = {
+};
+
+static const struct pch_gpio_set1 pch_gpio_set1_invert = {
+ .gpio2 = GPIO_INVERT,
+ .gpio5 = GPIO_INVERT,
+ .gpio6 = GPIO_INVERT,
+ .gpio11 = GPIO_INVERT,
+ .gpio13 = GPIO_INVERT,
+};
+
+static const struct pch_gpio_set1 pch_gpio_set1_blink = {
+};
+
+static const struct pch_gpio_set2 pch_gpio_set2_mode = {
+ .gpio32 = GPIO_MODE_GPIO, /* SKU0 */
+ .gpio33 = GPIO_MODE_GPIO,
+ .gpio34 = GPIO_MODE_GPIO,
+ .gpio35 = GPIO_MODE_GPIO, /* SKU1 */
+ .gpio36 = GPIO_MODE_NATIVE,
+ .gpio37 = GPIO_MODE_NATIVE,
+ .gpio38 = GPIO_MODE_GPIO, /* CHASSIS_ID2 */
+ .gpio39 = GPIO_MODE_GPIO, /* FP_PRESENCE# */
+ .gpio40 = GPIO_MODE_NATIVE,
+ .gpio41 = GPIO_MODE_NATIVE,
+ .gpio42 = GPIO_MODE_NATIVE,
+ .gpio43 = GPIO_MODE_NATIVE,
+ .gpio44 = GPIO_MODE_GPIO, /* INTRUD_CBL_DET# */
+ .gpio45 = GPIO_MODE_GPIO, /* COM_SER2_DET# (unused?) */
+ .gpio46 = GPIO_MODE_GPIO, /* BOARD_REV1 */
+ .gpio47 = GPIO_MODE_NATIVE,
+ .gpio48 = GPIO_MODE_GPIO,
+ .gpio49 = GPIO_MODE_GPIO,
+ .gpio50 = GPIO_MODE_NATIVE,
+ .gpio51 = GPIO_MODE_NATIVE,
+ .gpio52 = GPIO_MODE_NATIVE,
+ .gpio53 = GPIO_MODE_NATIVE,
+ .gpio54 = GPIO_MODE_NATIVE,
+ .gpio55 = GPIO_MODE_NATIVE,
+ .gpio56 = GPIO_MODE_NATIVE,
+ .gpio57 = GPIO_MODE_GPIO,
+ .gpio58 = GPIO_MODE_NATIVE,
+ .gpio59 = GPIO_MODE_NATIVE,
+ .gpio60 = GPIO_MODE_GPIO,
+ .gpio61 = GPIO_MODE_NATIVE,
+ .gpio62 = GPIO_MODE_NATIVE,
+ .gpio63 = GPIO_MODE_NATIVE,
+};
+
+static const struct pch_gpio_set2 pch_gpio_set2_direction = {
+ .gpio32 = GPIO_DIR_INPUT,
+ .gpio33 = GPIO_DIR_OUTPUT,
+ .gpio34 = GPIO_DIR_OUTPUT,
+ .gpio35 = GPIO_DIR_INPUT,
+ .gpio38 = GPIO_DIR_INPUT,
+ .gpio39 = GPIO_DIR_INPUT,
+ .gpio44 = GPIO_DIR_INPUT,
+ .gpio45 = GPIO_DIR_INPUT,
+ .gpio46 = GPIO_DIR_INPUT,
+ .gpio48 = GPIO_DIR_INPUT,
+ .gpio49 = GPIO_DIR_OUTPUT,
+ .gpio57 = GPIO_DIR_OUTPUT,
+ .gpio60 = GPIO_DIR_OUTPUT,
+ .gpio63 = GPIO_DIR_OUTPUT,
+};
+
+static const struct pch_gpio_set2 pch_gpio_set2_level = {
+ .gpio33 = GPIO_LEVEL_HIGH,
+ .gpio34 = GPIO_LEVEL_HIGH,
+ .gpio49 = GPIO_LEVEL_HIGH,
+ .gpio57 = GPIO_LEVEL_LOW,
+ .gpio60 = GPIO_LEVEL_HIGH,
+};
+
+static const struct pch_gpio_set2 pch_gpio_set2_reset = {
+};
+
+static const struct pch_gpio_set3 pch_gpio_set3_mode = {
+ .gpio64 = GPIO_MODE_NATIVE,
+ .gpio65 = GPIO_MODE_NATIVE,
+ .gpio66 = GPIO_MODE_NATIVE,
+ .gpio67 = GPIO_MODE_NATIVE,
+ .gpio68 = GPIO_MODE_GPIO, /* BOARD_REV2 */
+ .gpio69 = GPIO_MODE_GPIO, /* USB_HDR_DET# */
+ .gpio70 = GPIO_MODE_GPIO, /* FP_CHAS_DET# */
+ .gpio71 = GPIO_MODE_GPIO,
+ .gpio72 = GPIO_MODE_GPIO,
+ .gpio73 = GPIO_MODE_GPIO,
+ .gpio74 = GPIO_MODE_GPIO, /* ME_MFG_MODE */
+ .gpio75 = GPIO_MODE_NATIVE,
+};
+
+static const struct pch_gpio_set3 pch_gpio_set3_direction = {
+ .gpio68 = GPIO_DIR_INPUT,
+ .gpio69 = GPIO_DIR_INPUT,
+ .gpio70 = GPIO_DIR_INPUT,
+ .gpio71 = GPIO_DIR_OUTPUT,
+ .gpio72 = GPIO_DIR_OUTPUT,
+ .gpio73 = GPIO_DIR_INPUT,
+ .gpio74 = GPIO_DIR_OUTPUT,
+};
+
+static const struct pch_gpio_set3 pch_gpio_set3_level = {
+ .gpio71 = GPIO_LEVEL_HIGH,
+ .gpio72 = GPIO_LEVEL_HIGH,
+ .gpio74 = GPIO_LEVEL_HIGH,
+};
+
+static const struct pch_gpio_set3 pch_gpio_set3_reset = {
+ .gpio74 = GPIO_RESET_RSMRST,
+};
+
+const struct pch_gpio_map mainboard_gpio_map = {
+ .set1 = {
+ .mode = &pch_gpio_set1_mode,
+ .direction = &pch_gpio_set1_direction,
+ .level = &pch_gpio_set1_level,
+ .blink = &pch_gpio_set1_blink,
+ .invert = &pch_gpio_set1_invert,
+ .reset = &pch_gpio_set1_reset,
+ },
+ .set2 = {
+ .mode = &pch_gpio_set2_mode,
+ .direction = &pch_gpio_set2_direction,
+ .level = &pch_gpio_set2_level,
+ .reset = &pch_gpio_set2_reset,
+ },
+ .set3 = {
+ .mode = &pch_gpio_set3_mode,
+ .direction = &pch_gpio_set3_direction,
+ .level = &pch_gpio_set3_level,
+ .reset = &pch_gpio_set3_reset,
+ },
+};
diff --git a/src/mainboard/dell/optiplex_9010/hda_verb.c b/src/mainboard/dell/optiplex_9010/hda_verb.c
new file mode 100644
index 0000000000..eab4ba9053
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/hda_verb.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <device/azalia_device.h>
+
+const u32 cim_verb_data[] = {
+ 0x10ec0269, /* Codec Vendor / Device ID: Realtek */
+ 0x1028052c, /* Subsystem ID */
+
+ 11, /* Number of 4 dword sets */
+
+ AZALIA_SUBVENDOR(0, 0x1028052c),
+ AZALIA_PIN_CFG(0, 0x12, 0x411111f0),
+ AZALIA_PIN_CFG(0, 0x14, 0x99130110),
+ AZALIA_PIN_CFG(0, 0x17, 0x411111f0),
+ AZALIA_PIN_CFG(0, 0x18, 0x02a19830),
+ AZALIA_PIN_CFG(0, 0x19, 0x01a19840),
+ AZALIA_PIN_CFG(0, 0x1a, 0x411111f0),
+ AZALIA_PIN_CFG(0, 0x1b, 0x01014020),
+ AZALIA_PIN_CFG(0, 0x1d, 0x411111f0),
+ AZALIA_PIN_CFG(0, 0x1e, 0x411111f0),
+ AZALIA_PIN_CFG(0, 0x21, 0x0221402f),
+
+ 0x80862806, /* Codec Vendor / Device ID: Intel */
+ 0x80860101, /* Subsystem ID */
+
+ 4, /* Number of 4 dword sets */
+
+ AZALIA_SUBVENDOR(3, 0x80860101),
+ AZALIA_PIN_CFG(3, 0x05, 0x18560010),
+ AZALIA_PIN_CFG(3, 0x06, 0x18560020),
+ AZALIA_PIN_CFG(3, 0x07, 0x58560030),
+};
+
+const u32 pc_beep_verbs[0] = {};
+
+AZALIA_ARRAY_SIZES;
diff --git a/src/mainboard/dell/optiplex_9010/mainboard.c b/src/mainboard/dell/optiplex_9010/mainboard.c
new file mode 100644
index 0000000000..4490c14a0d
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/mainboard.c
@@ -0,0 +1,191 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bootstate.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci_ops.h>
+#include <drivers/intel/gma/int15.h>
+#include <southbridge/intel/bd82x6x/pch.h>
+#include <southbridge/intel/common/gpio.h>
+#include <superio/smsc/sch5545/sch5545.h>
+
+#include "sch5545_ec.h"
+
+#define SIO_PORT 0x2e
+
+#define GPIO_CHASSIS_ID0 1
+#define GPIO_VGA_CABLE_DET_L 4
+#define GPIO_SKU2 7
+#define GPIO_CHASSIS_ID1 17
+/* Internal USB header on mainboard */
+#define FLEXBAY_HEADER_CABLE_DET_L 20
+#define GPIO_BOARD_REV0 21
+/* Password clear jumper */
+#define GPIO_PSWD_CLR 31
+#define GPIO_SKU0 32
+#define GPIO_SKU1 35
+#define GPIO_CHASSIS_ID2 37
+/* Front panel presence */
+#define GPIO_FRONT_PANEL_PRESENT_L 39
+#define GPIO_INTRUDER_CABLE_DET_L 44
+#define GPIO_BOARD_REV1 46
+#define GPIO_BOARD_REV2 68
+/* Front USB 3.0 ports */
+#define GPIO_USB_HEADER_DET_L 69
+/* Differentiate between MT/DT on the Medium Tower and Desktop variants */
+#define GPIO_FRONT_PANEL_CHASSIS_DET_L 70
+/*
+ * This GPIO is connected to the transistor gate. If high, it will pull the
+ * HDA_SDO high. When strapped at PCH_PWROK it will enable the Flash Descriptor
+ * Security Override and disable ME after chipset bringup. Alternative method
+ * is to use the service jumper on the mainboard.
+ */
+#define GPIO_ME_MFG_MODE 74
+
+/* These GPIOs are on SCH5545 */
+
+/* Detect if the power switch cable is connected */
+#define SIO_GPIO_FP_CBL_DET_L 25
+/* Detect internal speaker connected to front cover */
+#define SIO_GPIO_PCSPKR_DET_L 31
+
+static void mainboard_enable(struct device *dev)
+{
+ int pin_sts;
+ install_intel_vga_int15_handler(GMA_INT15_ACTIVE_LFP_NONE,
+ GMA_INT15_PANEL_FIT_DEFAULT,
+ GMA_INT15_BOOT_DISPLAY_DEFAULT, 0);
+
+ pin_sts = get_gpio(GPIO_CHASSIS_ID0);
+ pin_sts |= get_gpio(GPIO_CHASSIS_ID1) << 1;
+ pin_sts |= get_gpio(GPIO_CHASSIS_ID2) << 2;
+ pin_sts |= get_gpio(GPIO_FRONT_PANEL_CHASSIS_DET_L) << 3;
+
+ printk(BIOS_DEBUG, "Chassis type:");
+ switch (pin_sts) {
+ case 0:
+ printk(BIOS_DEBUG, "MT\n");
+ break;
+ case 3:
+ case 11:
+ printk(BIOS_DEBUG, "USFF\n");
+ break;
+ case 4:
+ /* As per table in schematics, but don't know what this is */
+ printk(BIOS_DEBUG, "Comoros\n");
+ break;
+ case 1:
+ case 9:
+ case 5:
+ case 13:
+ printk(BIOS_DEBUG, "SFF\n");
+ break;
+ case 8:
+ printk(BIOS_DEBUG, "DT\n");
+ break;
+ default:
+ printk(BIOS_DEBUG, "Unknown chassis type %u\n", pin_sts);
+ break;
+ }
+
+ pin_sts = get_gpio(GPIO_BOARD_REV0);
+ pin_sts |= get_gpio(GPIO_BOARD_REV1) << 1;
+ pin_sts |= get_gpio(GPIO_BOARD_REV2) << 2;
+
+ printk(BIOS_DEBUG, "Board revision: %d\n", pin_sts);
+
+ pin_sts = get_gpio(GPIO_SKU0);
+ pin_sts |= get_gpio(GPIO_SKU1) << 1;
+ pin_sts |= get_gpio(GPIO_SKU2) << 2;
+
+ printk(BIOS_DEBUG, "SKU ID is %d:", pin_sts);
+ switch (pin_sts) {
+ case 0:
+ printk(BIOS_DEBUG, "TPM\n");
+ break;
+ case 1:
+ printk(BIOS_DEBUG, "TCM\n");
+ break;
+ case 2:
+ printk(BIOS_DEBUG, "Non TPM/TCM\n");
+ break;
+ default:
+ printk(BIOS_DEBUG, "Unknown/reserved\n");
+ break;
+ }
+
+ printk(BIOS_DEBUG, "VGA cable %sconnected\n",
+ get_gpio(GPIO_VGA_CABLE_DET_L) ? "dis" : "");
+
+ printk(BIOS_DEBUG, "Flexbay %sattached to internal USB 2.0 header\n",
+ get_gpio(FLEXBAY_HEADER_CABLE_DET_L) ? "not " : "");
+
+ printk(BIOS_DEBUG, "Password clear jumper %sactive\n",
+ get_gpio(GPIO_PSWD_CLR) ? "in" : "");
+
+ if (!get_gpio(GPIO_FRONT_PANEL_PRESENT_L)) {
+ printk(BIOS_DEBUG, "Front panel cable connected\n");
+ } else {
+ printk(BIOS_WARNING, "Front panel cable not connected!\n");
+ printk(BIOS_WARNING, "Front USB 2.0 ports, SATA LED, microphone"
+ " and speaker jacks will not work!\n");
+ printk(BIOS_WARNING, "Check the front panel cable!\n");
+ }
+
+ if (!get_gpio(GPIO_INTRUDER_CABLE_DET_L)) {
+ printk(BIOS_DEBUG, "Intruder cable connected\n");
+ } else {
+ printk(BIOS_WARNING, "Intruder cable not connected!\n");
+ printk(BIOS_WARNING, "Intrusion detection will not work!\n");
+ printk(BIOS_WARNING, "Check the intruder cable!\n");
+ }
+
+ if (!get_gpio(GPIO_USB_HEADER_DET_L)) {
+ printk(BIOS_DEBUG, "Front USB 3.0 cable connected\n");
+ } else {
+ printk(BIOS_WARNING, "Front USB 3.0 cable not connected!\n");
+ printk(BIOS_WARNING, "Front USB 3.0 ports will not work!\n");
+ printk(BIOS_WARNING, "Check the front USB 3.0 cable!\n");
+ }
+}
+
+static void mainboard_final(void *chip_info)
+{
+ int pin_sts;
+ struct device *dev = pcidev_on_root(0x1f, 0);
+ const u8 pirq_routing = 11;
+
+ pci_write_config8(dev, PIRQA_ROUT, pirq_routing);
+ pci_write_config8(dev, PIRQB_ROUT, pirq_routing);
+ pci_write_config8(dev, PIRQC_ROUT, pirq_routing);
+ pci_write_config8(dev, PIRQD_ROUT, pirq_routing);
+
+ pci_write_config8(dev, PIRQE_ROUT, pirq_routing);
+ pci_write_config8(dev, PIRQF_ROUT, pirq_routing);
+ pci_write_config8(dev, PIRQG_ROUT, pirq_routing);
+ pci_write_config8(dev, PIRQH_ROUT, pirq_routing);
+
+ pin_sts = sch5545_get_gpio(SIO_PORT, SIO_GPIO_FP_CBL_DET_L);
+
+ if (pin_sts != -1) {
+ if (pin_sts) {
+ printk(BIOS_WARNING, "Power switch cable not connected!\n");
+ printk(BIOS_WARNING, "Check power switch cable!\n");
+ } else {
+ printk(BIOS_DEBUG, "Power switch cable connected\n");
+ }
+ }
+
+ pin_sts = sch5545_get_gpio(SIO_PORT, SIO_GPIO_PCSPKR_DET_L);
+
+ if (pin_sts != -1)
+ printk(BIOS_DEBUG, "Internal chassis PC speaker %sconnected\n",
+ pin_sts ? "not " : "");
+}
+
+struct chip_operations mainboard_ops = {
+ .enable_dev = mainboard_enable,
+ .final = mainboard_final,
+};
+
+BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, sch5545_ec_hwm_init, NULL);
diff --git a/src/mainboard/dell/optiplex_9010/romstage.c b/src/mainboard/dell/optiplex_9010/romstage.c
new file mode 100644
index 0000000000..36af6e49c0
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/romstage.c
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <stdint.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <northbridge/intel/sandybridge/sandybridge.h>
+#include <northbridge/intel/sandybridge/raminit_native.h>
+#include <southbridge/intel/bd82x6x/pch.h>
+#include <superio/smsc/sch5545/sch5545.h>
+#include <superio/smsc/sch5545/sch5545_emi.h>
+
+#include "sch5545_ec.h"
+
+void mainboard_early_init(int s3resume)
+{
+ uint16_t ec_fw_version;
+
+ /*
+ * We do EC initialization in romstage, because it makes no sense to
+ * bloat the bootblock any more. Secondly, the EC expects to receive
+ * correct initialization sequence from the host in the time window of
+ * about 3-5 seconds since system reset. If it doesn't receive the
+ * initialization sequence, it enters an error path which results in
+ * fans spinned up to high speed. In this state EC doesn't respond to
+ * further messages sent over EMI. The issue appears after power
+ * failure, where EC loses its configuration. For this particular
+ * reasons we do the initialization in romstage instead of ramstage.
+ */
+ sch5545_emi_init(0x2e);
+ if (sch5545_emi_get_int_mask_high())
+ printk(BIOS_SPEW, "EC interrupt mask MSB is not 0\n");
+
+ sch5545_ec_hwm_early_init();
+
+ if (!s3resume) {
+ ec_fw_version = sch5545_get_ec_fw_version();
+ printk(BIOS_DEBUG, "SCH5545 EC firmware version %04x\n", ec_fw_version);
+ sch5545_update_ec_firmware(ec_fw_version);
+ }
+ printk(BIOS_DEBUG, "EC early init complete.\n");
+
+ /* Disable SMIs and clear SMI status */
+ outb(0, SCH5545_RUNTIME_REG_BASE + SCH5545_RR_SMI_EN);
+ outb(SCH5545_SMI_GLOBAL_STS, SCH5545_RUNTIME_REG_BASE + SCH5545_RR_SMI_STS);
+}
+
+void mainboard_get_spd(spd_raw_data *spd, bool id_only)
+{
+ read_spd(&spd[0], 0x50, id_only);
+ read_spd(&spd[1], 0x51, id_only);
+ read_spd(&spd[2], 0x52, id_only);
+ read_spd(&spd[3], 0x53, id_only);
+}
diff --git a/src/mainboard/dell/optiplex_9010/sch5545_ec.c b/src/mainboard/dell/optiplex_9010/sch5545_ec.c
new file mode 100644
index 0000000000..401345254d
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/sch5545_ec.c
@@ -0,0 +1,726 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <cbfs.h>
+#include <cf9_reset.h>
+#include <string.h>
+#include <option.h>
+#include <arch/io.h>
+#include <cpu/x86/msr.h>
+#include <console/console.h>
+#include <cpu/intel/model_206ax/model_206ax.h>
+#include <southbridge/intel/common/gpio.h>
+#include <superio/smsc/sch5545/sch5545.h>
+#include <superio/smsc/sch5545/sch5545_emi.h>
+
+#include "sch5545_ec.h"
+
+#define GPIO_CHASSIS_ID0 1
+#define GPIO_CHASSIS_ID1 17
+#define GPIO_CHASSIS_ID2 37
+#define GPIO_FRONT_PANEL_CHASSIS_DET_L 70
+
+enum {
+ TDP_16 = 0x10,
+ TDP_32 = 0x20,
+ TDP_COMMON = 0xff,
+};
+
+typedef struct ec_val_reg_tdp {
+ uint8_t val;
+ uint16_t reg;
+ uint8_t tdp;
+} ec_chassis_tdp_t;
+
+static const struct ec_val_reg ec_hwm_init_seq[] = {
+ { 0xa0, 0x02fc },
+ { 0x32, 0x02fd },
+ { 0x77, 0x0005 },
+ { 0x0f, 0x0018 },
+ { 0x2f, 0x0019 },
+ { 0x2f, 0x001a },
+ { 0x33, 0x008a },
+ { 0x33, 0x008b },
+ { 0x33, 0x008c },
+ { 0x10, 0x00ba },
+ { 0xff, 0x00d1 },
+ { 0xff, 0x00d6 },
+ { 0xff, 0x00db },
+ { 0x00, 0x0048 },
+ { 0x00, 0x0049 },
+ { 0x00, 0x007a },
+ { 0x00, 0x007b },
+ { 0x00, 0x007c },
+ { 0x00, 0x0080 },
+ { 0x00, 0x0081 },
+ { 0x00, 0x0082 },
+ { 0xbb, 0x0083 },
+ { 0xb0, 0x0084 },
+ { 0x88, 0x01a1 },
+ { 0x80, 0x01a4 },
+ { 0x00, 0x0088 },
+ { 0x00, 0x0089 },
+ { 0x02, 0x00a0 },
+ { 0x02, 0x00a1 },
+ { 0x02, 0x00a2 },
+ { 0x04, 0x00a4 },
+ { 0x04, 0x00a5 },
+ { 0x04, 0x00a6 },
+ { 0x00, 0x00ab },
+ { 0x3f, 0x00ad },
+ { 0x07, 0x00b7 },
+ { 0x50, 0x0062 },
+ { 0x46, 0x0063 },
+ { 0x50, 0x0064 },
+ { 0x46, 0x0065 },
+ { 0x50, 0x0066 },
+ { 0x46, 0x0067 },
+ { 0x98, 0x0057 },
+ { 0x98, 0x0059 },
+ { 0x7c, 0x0061 },
+ { 0x00, 0x01bc },
+ { 0x00, 0x01bd },
+ { 0x00, 0x01bb },
+ { 0xdd, 0x0085 },
+ { 0xdd, 0x0086 },
+ { 0x07, 0x0087 },
+ { 0x5e, 0x0090 },
+ { 0x5e, 0x0091 },
+ { 0x5d, 0x0095 },
+ { 0x00, 0x0096 },
+ { 0x00, 0x0097 },
+ { 0x00, 0x009b },
+ { 0x86, 0x00ae },
+ { 0x86, 0x00af },
+ { 0x67, 0x00b3 },
+ { 0xff, 0x00c4 },
+ { 0xff, 0x00c5 },
+ { 0xff, 0x00c9 },
+ { 0x01, 0x0040 },
+ { 0x00, 0x02fc },
+ { 0x9a, 0x02b3 },
+ { 0x05, 0x02b4 },
+ { 0x01, 0x02cc },
+ { 0x4c, 0x02d0 },
+ { 0x01, 0x02d2 },
+ { 0x01, 0x006f },
+ { 0x02, 0x0070 },
+ { 0x03, 0x0071 },
+};
+
+
+static const ec_chassis_tdp_t ec_hwm_chassis3[] = {
+ { 0x33, 0x0005, TDP_COMMON },
+ { 0x2f, 0x0018, TDP_COMMON },
+ { 0x2f, 0x0019, TDP_COMMON },
+ { 0x2f, 0x001a, TDP_COMMON },
+ { 0x00, 0x0080, TDP_COMMON },
+ { 0x00, 0x0081, TDP_COMMON },
+ { 0xbb, 0x0083, TDP_COMMON },
+ { 0x8a, 0x0085, TDP_16 },
+ { 0x2c, 0x0086, TDP_16 },
+ { 0x66, 0x008a, TDP_16 },
+ { 0x5b, 0x008b, TDP_16 },
+ { 0x65, 0x0090, TDP_COMMON },
+ { 0x70, 0x0091, TDP_COMMON },
+ { 0x86, 0x0092, TDP_COMMON },
+ { 0xa4, 0x0096, TDP_COMMON },
+ { 0xa4, 0x0097, TDP_COMMON },
+ { 0xa4, 0x0098, TDP_COMMON },
+ { 0xa4, 0x009b, TDP_COMMON },
+ { 0x0e, 0x00a0, TDP_COMMON },
+ { 0x0e, 0x00a1, TDP_COMMON },
+ { 0x7c, 0x00ae, TDP_COMMON },
+ { 0x86, 0x00af, TDP_COMMON },
+ { 0x95, 0x00b0, TDP_COMMON },
+ { 0x9a, 0x00b3, TDP_COMMON },
+ { 0x08, 0x00b6, TDP_COMMON },
+ { 0x08, 0x00b7, TDP_COMMON },
+ { 0x64, 0x00ea, TDP_COMMON },
+ { 0xff, 0x00ef, TDP_COMMON },
+ { 0x15, 0x00f8, TDP_COMMON },
+ { 0x00, 0x00f9, TDP_COMMON },
+ { 0x30, 0x00f0, TDP_COMMON },
+ { 0x01, 0x00fd, TDP_COMMON },
+ { 0x88, 0x01a1, TDP_COMMON },
+ { 0x08, 0x01a2, TDP_COMMON },
+ { 0x08, 0x01b1, TDP_COMMON },
+ { 0x94, 0x01be, TDP_COMMON },
+ { 0x94, 0x0280, TDP_16 },
+ { 0x11, 0x0281, TDP_16 },
+ { 0x03, 0x0282, TDP_COMMON },
+ { 0x0a, 0x0283, TDP_COMMON },
+ { 0x80, 0x0284, TDP_COMMON },
+ { 0x03, 0x0285, TDP_COMMON },
+ { 0x68, 0x0288, TDP_16 },
+ { 0x10, 0x0289, TDP_16 },
+ { 0x03, 0x028a, TDP_COMMON },
+ { 0x0a, 0x028b, TDP_COMMON },
+ { 0x80, 0x028c, TDP_COMMON },
+ { 0x03, 0x028d, TDP_COMMON },
+};
+
+static const ec_chassis_tdp_t ec_hwm_chassis4[] = {
+ { 0x33, 0x0005, TDP_COMMON },
+ { 0x2f, 0x0018, TDP_COMMON },
+ { 0x2f, 0x0019, TDP_COMMON },
+ { 0x2f, 0x001a, TDP_COMMON },
+ { 0x00, 0x0080, TDP_COMMON },
+ { 0x00, 0x0081, TDP_COMMON },
+ { 0xbb, 0x0083, TDP_COMMON },
+ { 0x99, 0x0085, TDP_32 },
+ { 0x98, 0x0085, TDP_16 },
+ { 0xbc, 0x0086, TDP_32 },
+ { 0x1c, 0x0086, TDP_16 },
+ { 0x39, 0x008a, TDP_32 },
+ { 0x3d, 0x008a, TDP_16 },
+ { 0x40, 0x008b, TDP_32 },
+ { 0x43, 0x008b, TDP_16 },
+ { 0x68, 0x0090, TDP_COMMON },
+ { 0x5e, 0x0091, TDP_COMMON },
+ { 0x86, 0x0092, TDP_COMMON },
+ { 0xa4, 0x0096, TDP_COMMON },
+ { 0xa4, 0x0097, TDP_COMMON },
+ { 0xa4, 0x0098, TDP_COMMON },
+ { 0xa4, 0x009b, TDP_COMMON },
+ { 0x0c, 0x00a0, TDP_COMMON },
+ { 0x0c, 0x00a1, TDP_COMMON },
+ { 0x72, 0x00ae, TDP_COMMON },
+ { 0x7c, 0x00af, TDP_COMMON },
+ { 0x9a, 0x00b0, TDP_COMMON },
+ { 0x7c, 0x00b3, TDP_COMMON },
+ { 0x08, 0x00b6, TDP_COMMON },
+ { 0x08, 0x00b7, TDP_COMMON },
+ { 0x64, 0x00ea, TDP_COMMON },
+ { 0xff, 0x00ef, TDP_COMMON },
+ { 0x15, 0x00f8, TDP_COMMON },
+ { 0x00, 0x00f9, TDP_COMMON },
+ { 0x30, 0x00f0, TDP_COMMON },
+ { 0x01, 0x00fd, TDP_COMMON },
+ { 0x88, 0x01a1, TDP_COMMON },
+ { 0x08, 0x01a2, TDP_COMMON },
+ { 0x08, 0x01b1, TDP_COMMON },
+ { 0x90, 0x01be, TDP_COMMON },
+ { 0x94, 0x0280, TDP_32 },
+ { 0x11, 0x0281, TDP_32 },
+ { 0x68, 0x0280, TDP_16 },
+ { 0x10, 0x0281, TDP_16 },
+ { 0x03, 0x0282, TDP_COMMON },
+ { 0x0a, 0x0283, TDP_COMMON },
+ { 0x80, 0x0284, TDP_COMMON },
+ { 0x03, 0x0285, TDP_COMMON },
+ { 0xa0, 0x0288, TDP_32 },
+ { 0x0f, 0x0289, TDP_32 },
+ { 0xd8, 0x0288, TDP_16 },
+ { 0x0e, 0x0289, TDP_16 },
+ { 0x03, 0x028a, TDP_COMMON },
+ { 0x0a, 0x028b, TDP_COMMON },
+ { 0x80, 0x028c, TDP_COMMON },
+ { 0x03, 0x028d, TDP_COMMON },
+};
+
+static const ec_chassis_tdp_t ec_hwm_chassis5[] = {
+ { 0x33, 0x0005, TDP_COMMON },
+ { 0x2f, 0x0018, TDP_COMMON },
+ { 0x2f, 0x0019, TDP_COMMON },
+ { 0x2f, 0x001a, TDP_COMMON },
+ { 0x00, 0x0080, TDP_COMMON },
+ { 0x00, 0x0081, TDP_COMMON },
+ { 0xbb, 0x0083, TDP_COMMON },
+ { 0x89, 0x0085, TDP_32 },
+ { 0x99, 0x0085, TDP_16 },
+ { 0x9c, 0x0086, TDP_COMMON },
+ { 0x39, 0x008a, TDP_32 },
+ { 0x42, 0x008a, TDP_16 },
+ { 0x6b, 0x008b, TDP_32 },
+ { 0x74, 0x008b, TDP_16 },
+ { 0x5e, 0x0091, TDP_COMMON },
+ { 0x86, 0x0092, TDP_COMMON },
+ { 0xa4, 0x0096, TDP_COMMON },
+ { 0xa4, 0x0097, TDP_COMMON },
+ { 0xa4, 0x0098, TDP_COMMON },
+ { 0xa4, 0x009b, TDP_COMMON },
+ { 0x0c, 0x00a0, TDP_COMMON },
+ { 0x0c, 0x00a1, TDP_COMMON },
+ { 0x7c, 0x00ae, TDP_COMMON },
+ { 0x7c, 0x00af, TDP_COMMON },
+ { 0x9a, 0x00b0, TDP_COMMON },
+ { 0x7c, 0x00b3, TDP_COMMON },
+ { 0x08, 0x00b6, TDP_COMMON },
+ { 0x08, 0x00b7, TDP_COMMON },
+ { 0x64, 0x00ea, TDP_COMMON },
+ { 0xff, 0x00ef, TDP_COMMON },
+ { 0x15, 0x00f8, TDP_COMMON },
+ { 0x00, 0x00f9, TDP_COMMON },
+ { 0x30, 0x00f0, TDP_COMMON },
+ { 0x01, 0x00fd, TDP_COMMON },
+ { 0x88, 0x01a1, TDP_COMMON },
+ { 0x08, 0x01a2, TDP_COMMON },
+ { 0x08, 0x01b1, TDP_COMMON },
+ { 0x90, 0x01be, TDP_COMMON },
+ { 0x94, 0x0280, TDP_32 },
+ { 0x11, 0x0281, TDP_32 },
+ { 0x3c, 0x0280, TDP_16 },
+ { 0x0f, 0x0281, TDP_16 },
+ { 0x03, 0x0282, TDP_COMMON },
+ { 0x0a, 0x0283, TDP_COMMON },
+ { 0x80, 0x0284, TDP_COMMON },
+ { 0x03, 0x0285, TDP_COMMON },
+ { 0x60, 0x0288, TDP_32 },
+ { 0x09, 0x0289, TDP_32 },
+ { 0x98, 0x0288, TDP_16 },
+ { 0x08, 0x0289, TDP_16 },
+ { 0x03, 0x028a, TDP_COMMON },
+ { 0x0a, 0x028b, TDP_COMMON },
+ { 0x80, 0x028c, TDP_COMMON },
+ { 0x03, 0x028d, TDP_COMMON },
+};
+
+static const ec_chassis_tdp_t ec_hwm_chassis6[] = {
+ { 0x33, 0x0005, TDP_COMMON },
+ { 0x2f, 0x0018, TDP_COMMON },
+ { 0x2f, 0x0019, TDP_COMMON },
+ { 0x2f, 0x001a, TDP_COMMON },
+ { 0x00, 0x0080, TDP_COMMON },
+ { 0x00, 0x0081, TDP_COMMON },
+ { 0xbb, 0x0083, TDP_COMMON },
+ { 0x99, 0x0085, TDP_32 },
+ { 0x98, 0x0085, TDP_16 },
+ { 0xdc, 0x0086, TDP_32 },
+ { 0x9c, 0x0086, TDP_16 },
+ { 0x3d, 0x008a, TDP_32 },
+ { 0x43, 0x008a, TDP_16 },
+ { 0x4e, 0x008b, TDP_32 },
+ { 0x47, 0x008b, TDP_16 },
+ { 0x6d, 0x0090, TDP_COMMON },
+ { 0x5f, 0x0091, TDP_32 },
+ { 0x61, 0x0091, TDP_16 },
+ { 0x86, 0x0092, TDP_COMMON },
+ { 0xa4, 0x0096, TDP_COMMON },
+ { 0xa4, 0x0097, TDP_COMMON },
+ { 0xa4, 0x0098, TDP_COMMON },
+ { 0xa4, 0x009b, TDP_COMMON },
+ { 0x0e, 0x00a0, TDP_COMMON },
+ { 0x0e, 0x00a1, TDP_COMMON },
+ { 0x7c, 0x00ae, TDP_COMMON },
+ { 0x7c, 0x00af, TDP_COMMON },
+ { 0x98, 0x00b0, TDP_32 },
+ { 0x9a, 0x00b0, TDP_16 },
+ { 0x9a, 0x00b3, TDP_COMMON },
+ { 0x08, 0x00b6, TDP_COMMON },
+ { 0x08, 0x00b7, TDP_COMMON },
+ { 0x64, 0x00ea, TDP_COMMON },
+ { 0xff, 0x00ef, TDP_COMMON },
+ { 0x15, 0x00f8, TDP_COMMON },
+ { 0x00, 0x00f9, TDP_COMMON },
+ { 0x30, 0x00f0, TDP_COMMON },
+ { 0x01, 0x00fd, TDP_COMMON },
+ { 0x88, 0x01a1, TDP_COMMON },
+ { 0x08, 0x01a2, TDP_COMMON },
+ { 0x08, 0x01b1, TDP_COMMON },
+ { 0x97, 0x01be, TDP_32 },
+ { 0x95, 0x01be, TDP_16 },
+ { 0x68, 0x0280, TDP_32 },
+ { 0x10, 0x0281, TDP_32 },
+ { 0xd8, 0x0280, TDP_16 },
+ { 0x0e, 0x0281, TDP_16 },
+ { 0x03, 0x0282, TDP_COMMON },
+ { 0x0a, 0x0283, TDP_COMMON },
+ { 0x80, 0x0284, TDP_COMMON },
+ { 0x03, 0x0285, TDP_COMMON },
+ { 0xe4, 0x0288, TDP_32 },
+ { 0x0c, 0x0289, TDP_32 },
+ { 0x10, 0x0288, TDP_16 },
+ { 0x0e, 0x0289, TDP_16 },
+ { 0x03, 0x028a, TDP_COMMON },
+ { 0x0a, 0x028b, TDP_COMMON },
+ { 0x80, 0x028c, TDP_COMMON },
+ { 0x03, 0x028d, TDP_COMMON },
+};
+
+
+
+static uint8_t send_mbox_msg_with_int(uint8_t mbox_message)
+{
+ uint8_t int_sts, int_cond;
+
+ sch5545_emi_h2ec_mbox_write(mbox_message);
+
+ do {
+ int_sts = sch5545_emi_get_int_src_low();
+ int_cond = int_sts & 0x71;
+ } while (int_cond == 0);
+
+ sch5545_emi_set_int_src_low(int_cond);
+
+ if ((int_sts & 1) == 0)
+ return 0;
+
+ if (sch5545_emi_ec2h_mbox_read() == mbox_message)
+ return 1;
+
+ return 0;
+}
+
+static uint8_t send_mbox_msg_simple(uint8_t mbox_message)
+{
+ uint8_t int_sts;
+
+ sch5545_emi_h2ec_mbox_write(mbox_message);
+
+ do {
+ int_sts = sch5545_emi_get_int_src_low();
+ if ((int_sts & 70) != 0)
+ return 0;
+ } while ((int_sts & 1) == 0);
+
+ if (sch5545_emi_ec2h_mbox_read() == mbox_message)
+ return 1;
+
+ return 0;
+}
+
+static void ec_check_mbox_and_int_status(uint8_t int_src, uint8_t mbox_msg)
+{
+ uint8_t val;
+
+ val = sch5545_emi_ec2h_mbox_read();
+ if (val != mbox_msg)
+ printk(BIOS_SPEW, "EC2H mailbox should be %02x, is %02x\n", mbox_msg, val);
+
+ val = sch5545_emi_get_int_src_low();
+ if (val != int_src)
+ printk(BIOS_SPEW, "EC INT SRC should be %02x, is %02x\n", int_src, val);
+
+ sch5545_emi_set_int_src_low(val);
+}
+
+static uint8_t ec_read_write_reg(uint8_t ldn, uint16_t reg, uint8_t *value, uint8_t rw_bit)
+{
+ uint8_t int_mask_bckup, ret = 0;
+ rw_bit &= 1;
+
+ int_mask_bckup = sch5545_emi_get_int_mask_low();
+ sch5545_emi_set_int_mask_low(0);
+
+ sch5545_emi_ec_write16(0x8000, (ldn << 1) | 0x100 | rw_bit);
+ if (rw_bit)
+ sch5545_emi_ec_write32(0x8004, (reg << 16) | *value);
+ else
+ sch5545_emi_ec_write32(0x8004, reg << 16);
+
+ ret = send_mbox_msg_with_int(1);
+ if (ret && !rw_bit)
+ *value = sch5545_emi_ec_read8(0x8004);
+ else if (ret != 1 && rw_bit)
+ printk(BIOS_WARNING, "EC mailbox returned unexpected value "
+ "when writing %02x to %04x\n", *value, reg);
+ else if (ret != 1 && !rw_bit)
+ printk(BIOS_WARNING, "EC mailbox returned unexpected value "
+ "when reading %04x\n", reg);
+
+ sch5545_emi_set_int_mask_low(int_mask_bckup);
+
+ return ret;
+}
+
+uint16_t sch5545_get_ec_fw_version(void)
+{
+ uint8_t val;
+ uint16_t ec_fw_version;
+
+ /* Read the FW version currently loaded used by EC */
+ ec_read_write_reg(EC_HWM_LDN, 0x2ad, &val, READ_OP);
+ ec_fw_version = (val << 8);
+ ec_read_write_reg(EC_HWM_LDN, 0x2ae, &val, READ_OP);
+ ec_fw_version |= val;
+ ec_read_write_reg(EC_HWM_LDN, 0x2ac, &val, READ_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x2fd, &val, READ_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x2b0, &val, READ_OP);
+
+ return ec_fw_version;
+}
+
+void sch5545_update_ec_firmware(uint16_t ec_version)
+{
+ uint8_t status;
+ uint16_t ec_fw_version;
+ uint32_t *ec_fw_file;
+ size_t ec_fw_file_size;
+
+ ec_fw_file = cbfs_boot_map_with_leak("sch5545_ecfw.bin", CBFS_TYPE_RAW,
+ &ec_fw_file_size);
+
+ if (!ec_fw_file || ec_fw_file_size != 0x1750) {
+ printk(BIOS_ERR, "EC firmware file not found in CBFS!\n");
+ printk(BIOS_ERR, "The fans will keep running at maximum speed.\n");
+ return;
+ }
+
+ ec_fw_version = ec_fw_file[3] & 0xffff;
+
+ /*
+ * After power failure EC loses its configuration. The currently used firmware version
+ * by EC will be reported as 0x0000. In such case EC firmware needs to be uploaded.
+ */
+ if (ec_version != ec_fw_version) {
+ printk(BIOS_INFO, "SCH5545 EC is not functional, probably due to power "
+ "failure\n");
+ printk(BIOS_INFO, "Uploading EC firmware (version %04x) to SCH5545\n",
+ ec_fw_version);
+
+ if (!send_mbox_msg_simple(0x03)) {
+ printk(BIOS_WARNING, "EC didn't accept FW upload start signal\n");
+ printk(BIOS_WARNING, "EC firmware update failed!\n");
+ return;
+ }
+
+ sch5545_emi_ec_write32_bulk(0x8100, ec_fw_file, ec_fw_file_size);
+
+ status = send_mbox_msg_simple(0x04);
+ status += send_mbox_msg_simple(0x06);
+
+ if (status != 2)
+ printk(BIOS_WARNING, "EC firmware update failed!\n");
+
+ if (ec_fw_version != sch5545_get_ec_fw_version()) {
+ printk(BIOS_ERR, "EC firmware update failed!\n");
+ printk(BIOS_ERR, "The fans will keep running at maximum speed\n");
+ } else {
+ printk(BIOS_INFO, "EC firmware update success\n");
+ /*
+ * The vendor BIOS does a full reset after EC firmware update. Most
+ * likely because the fans are adapting very slowly after automatic fan
+ * control is enabled. This makes huge noise. To avoid it, also do the
+ * full reset. On next boot, it will not be necessary.
+ */
+ full_reset();
+ }
+ } else {
+ printk(BIOS_INFO, "SCH5545 EC firmware up to date (version %04x)\n",
+ ec_version);
+ }
+}
+
+void sch5545_ec_hwm_early_init(void)
+{
+ uint8_t val;
+ int i;
+
+ printk(BIOS_DEBUG, "%s\n", __func__);
+
+ ec_check_mbox_and_int_status(0x20, 0x01);
+
+ ec_read_write_reg(2, 0xcb, &val, READ_OP);
+ ec_read_write_reg(2, 0xb8, &val, READ_OP);
+
+ for (i = 0; i < ARRAY_SIZE(ec_hwm_init_seq); i++) {
+ val = ec_hwm_init_seq[i].val;
+ ec_read_write_reg(EC_HWM_LDN, ec_hwm_init_seq[i].reg, &val,
+ WRITE_OP);
+ }
+
+ ec_check_mbox_and_int_status(0x01, 0x01);
+}
+
+static uint8_t get_sku_tdp_config(void)
+{
+ msr_t msr;
+ uint32_t power_unit, tdp;
+ /* Get units */
+ msr = rdmsr(MSR_PKG_POWER_SKU_UNIT);
+ power_unit = msr.lo & 0xf;
+
+ /* Get power defaults for this SKU */
+ msr = rdmsr(MSR_PKG_POWER_SKU);
+ tdp = msr.lo & 0x7fff;
+
+ /* These numbers will determine which settings to use to init EC */
+ if ((tdp >> power_unit) < 66)
+ return 16;
+ else
+ return 32;
+}
+
+static uint8_t get_chassis_type(void)
+{
+ uint8_t chassis_id;
+
+ chassis_id = get_gpio(GPIO_CHASSIS_ID0);
+ chassis_id |= get_gpio(GPIO_CHASSIS_ID1) << 1;
+ chassis_id |= get_gpio(GPIO_CHASSIS_ID2) << 2;
+ chassis_id |= get_gpio(GPIO_FRONT_PANEL_CHASSIS_DET_L) << 3;
+
+ /* This mapping will determine which EC init sequence to use */
+ switch (chassis_id) {
+ case 0x0:
+ return 5;
+ case 0x8:
+ return 4;
+ case 0x3:
+ case 0xb:
+ return 3;
+ case 0x1:
+ case 0x9:
+ case 0x5:
+ case 0xd:
+ return 6;
+ default:
+ printk(BIOS_DEBUG, "Unknown chassis ID %x\n", chassis_id);
+ break;
+ }
+
+ return 0xff;
+}
+
+static void ec_hwm_init_late(const ec_chassis_tdp_t *ec_hwm_sequence, size_t size)
+{
+ unsigned int i;
+ uint8_t val;
+ uint8_t tdp_config = get_sku_tdp_config();
+
+ for (i = 0; i < size; i++) {
+ if (ec_hwm_sequence[i].tdp == tdp_config ||
+ ec_hwm_sequence[i].tdp == TDP_COMMON) {
+ val = ec_hwm_sequence[i].val;
+ ec_read_write_reg(EC_HWM_LDN, ec_hwm_sequence[i].reg, &val, WRITE_OP);
+ }
+ }
+}
+
+static void prepare_for_hwm_ec_sequence(uint8_t write_only, uint8_t *value)
+{
+ uint16_t reg;
+ uint8_t val;
+
+ if (write_only == 1) {
+ val = *value;
+ reg = 0x02fc;
+ } else {
+ if (value != NULL)
+ ec_read_write_reg(EC_HWM_LDN, 0x02fc, value, READ_OP);
+ val = 0xa0;
+ ec_read_write_reg(EC_HWM_LDN, 0x2fc, &val, WRITE_OP);
+ val = 0x32;
+ reg = 0x02fd;
+ }
+
+ ec_read_write_reg(1, reg, &val, WRITE_OP);
+}
+
+void sch5545_ec_hwm_init(void *unused)
+{
+ uint8_t val, val_2fc, chassis_type, fan_speed_full = 0;
+
+ printk(BIOS_DEBUG, "%s\n", __func__);
+ sch5545_emi_init(0x2e);
+
+ chassis_type = get_chassis_type();
+
+ ec_read_write_reg(EC_HWM_LDN, 0x0048, &val, READ_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0048, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0042, &val, READ_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0048, &val, READ_OP);
+ val |= 0x02;
+ ec_read_write_reg(EC_HWM_LDN, 0x0048, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0048, &val, READ_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0048, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0042, &val, READ_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0048, &val, READ_OP);
+ val |= 0x04;
+ ec_read_write_reg(EC_HWM_LDN, 0x0048, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0081, &val, READ_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0027, &val, READ_OP);
+
+ ec_check_mbox_and_int_status(0x00, 0x01);
+
+ prepare_for_hwm_ec_sequence(0, &val_2fc);
+
+ if (chassis_type != 0xff) {
+ printk(BIOS_DEBUG, "Performing HWM init for chassis %d\n", chassis_type);
+ switch (chassis_type) {
+ case 3:
+ ec_hwm_init_late(ec_hwm_chassis3, ARRAY_SIZE(ec_hwm_chassis3));
+ break;
+ case 4:
+ ec_hwm_init_late(ec_hwm_chassis4, ARRAY_SIZE(ec_hwm_chassis4));
+ break;
+ case 5:
+ ec_hwm_init_late(ec_hwm_chassis6, ARRAY_SIZE(ec_hwm_chassis5));
+ break;
+ case 6:
+ ec_hwm_init_late(ec_hwm_chassis6, ARRAY_SIZE(ec_hwm_chassis6));
+ break;
+ }
+ }
+
+ if (CONFIG_MAX_CPUS > 2) {
+ val = 0x30;
+ ec_read_write_reg(EC_HWM_LDN, 0x009e, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x00ea, &val, READ_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x00eb, &val, WRITE_OP);
+ }
+
+ ec_read_write_reg(EC_HWM_LDN, 0x02fc, &val_2fc, WRITE_OP);
+
+ if (get_option(&fan_speed_full, "fan_full_speed") != CB_SUCCESS)
+ printk(BIOS_INFO, "fan_full_speed CMOS option not found. "
+ "Fans will be set up for automatic control\n");
+
+ if (fan_speed_full) {
+ ec_read_write_reg(EC_HWM_LDN, 0x0080, &val, READ_OP);
+ val |= 0x60;
+ ec_read_write_reg(EC_HWM_LDN, 0x0080, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x0081, &val, READ_OP);
+ val |= 0x60;
+ ec_read_write_reg(EC_HWM_LDN, 0x0081, &val, WRITE_OP);
+ }
+
+ ec_read_write_reg(EC_HWM_LDN, 0x00b8, &val, READ_OP);
+
+ if (chassis_type == 4 || chassis_type == 5) {
+ ec_read_write_reg(EC_HWM_LDN, 0x00a0, &val, READ_OP);
+ val &= 0xfb;
+ ec_read_write_reg(EC_HWM_LDN, 0x00a0, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x00a1, &val, READ_OP);
+ val &= 0xfb;
+ ec_read_write_reg(EC_HWM_LDN, 0x00a1, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x00a2, &val, READ_OP);
+ val &= 0xfb;
+ ec_read_write_reg(EC_HWM_LDN, 0x00a2, &val, WRITE_OP);
+ val = 0x99;
+ ec_read_write_reg(EC_HWM_LDN, 0x008a, &val, WRITE_OP);
+ val = 0x47;
+ ec_read_write_reg(EC_HWM_LDN, 0x008b, &val, WRITE_OP);
+ val = 0x91;
+ ec_read_write_reg(EC_HWM_LDN, 0x008c, &val, WRITE_OP);
+ }
+
+ ec_read_write_reg(EC_HWM_LDN, 0x0049, &val, READ_OP);
+ val &= 0xf7;
+ ec_read_write_reg(EC_HWM_LDN, 0x0049, &val, WRITE_OP);
+
+ val = 0x6a;
+ if (chassis_type != 3)
+ ec_read_write_reg(EC_HWM_LDN, 0x0059, &val, WRITE_OP);
+ else
+ ec_read_write_reg(EC_HWM_LDN, 0x0057, &val, WRITE_OP);
+
+ ec_read_write_reg(EC_HWM_LDN, 0x0041, &val, READ_OP);
+ val |= 0x40;
+ ec_read_write_reg(EC_HWM_LDN, 0x0041, &val, WRITE_OP);
+
+ if (chassis_type == 3) {
+ ec_read_write_reg(EC_HWM_LDN, 0x0049, &val, READ_OP);
+ val |= 0x04;
+ } else {
+ ec_read_write_reg(EC_HWM_LDN, 0x0049, &val, READ_OP);
+ val |= 0x08;
+ }
+ ec_read_write_reg(EC_HWM_LDN, 0x0049, &val, WRITE_OP);
+
+ val = 0x0e;
+ ec_read_write_reg(EC_HWM_LDN, 0x007b, &val, WRITE_OP);
+ ec_read_write_reg(EC_HWM_LDN, 0x007c, &val, WRITE_OP);
+ val = 0x01;
+ ec_read_write_reg(EC_HWM_LDN, 0x007a, &val, WRITE_OP);
+}
diff --git a/src/mainboard/dell/optiplex_9010/sch5545_ec.h b/src/mainboard/dell/optiplex_9010/sch5545_ec.h
new file mode 100644
index 0000000000..0ca589f9eb
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/sch5545_ec.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <stdint.h>
+
+#define READ_OP 0
+#define WRITE_OP 1
+
+#define EC_HWM_LDN 1
+#define EC_GPIO_LDN 2
+
+/* EC GPIO configuration */
+#define EC_GPIO_PP (0 << 0)
+#define EC_GPIO_OD (1 << 0)
+#define EC_GPIO_FUNC0 (0 << 4)
+#define EC_GPIO_FUNC1 (1 << 4)
+#define EC_GPIO_FUNC2 (2 << 4)
+#define EC_GPIO_FUNC3 (3 << 4)
+
+struct ec_val_reg {
+ uint8_t val;
+ uint16_t reg;
+};
+
+uint16_t sch5545_get_ec_fw_version(void);
+void sch5545_update_ec_firmware(uint16_t ec_version);
+void sch5545_ec_early_init(void);
+void sch5545_ec_hwm_early_init(void);
+void sch5545_ec_hwm_init(void *unused);
diff --git a/src/mainboard/dell/optiplex_9010/sch5545_ec_early.c b/src/mainboard/dell/optiplex_9010/sch5545_ec_early.c
new file mode 100644
index 0000000000..70b7de6064
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/sch5545_ec_early.c
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <arch/io.h>
+#include <superio/smsc/sch5545/sch5545.h>
+#include <superio/smsc/sch5545/sch5545_emi.h>
+
+#include "sch5545_ec.h"
+
+static uint16_t emi_bar;
+
+static const struct ec_val_reg ec_gpio_init_table[] = {
+ /*
+ * Probably some early GPIO initialization, setting GPIO functions.
+ * The LSBs in third column match the GPIO config registers offsets for
+ * non-default GPIOs.
+ */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x08cc }, /* GP063 (def) / KBDRST# */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x08d0 }, /* GP064 (def) / A20M */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x089c }, /* GP047 / TXD1 (def) */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x0878 }, /* GP036 (def) / SMBCLK1 */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x0880 }, /* GP040 (def) / SMBDAT1 */
+ { EC_GPIO_OD | EC_GPIO_FUNC1, 0x0884 }, /* GP041 (def) / IO_PME# */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x08e4 }, /* GP071 (def) / IO_SMI# */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x08e0 }, /* GP070 (def) / SPEAKER */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x0848 }, /* GP022 (def) / PWM1 */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x084c }, /* GP023 (def) / PWM2 */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x0850 }, /* GP024 (def) / PWM3 */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x083c }, /* GP017 / TACH1 (def) */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x0840 }, /* GP020 / TACH2 (def) */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x0844 }, /* GP021 / TACH3 (def) */
+ { EC_GPIO_PP | EC_GPIO_FUNC1, 0x0814 }, /* GP005 (def) / PECI_REQ# */
+};
+
+static const struct ec_val_reg ec_hwm_early_init_table[] = {
+ /* Probably some early hardware monitor initialization */
+ { 0xff, 0x0005 },
+ { 0x30, 0x00f0 },
+ { 0x10, 0x00f8 },
+ { 0x00, 0x00f9 },
+ { 0x00, 0x00fa },
+ { 0x00, 0x00fb },
+ { 0x00, 0x00ea },
+ { 0x00, 0x00eb },
+ { 0x7c, 0x00ef },
+ { 0x03, 0x006e },
+ { 0x51, 0x02d0 },
+ { 0x01, 0x02d2 },
+ { 0x12, 0x059a },
+ { 0x11, 0x059e },
+ { 0x14, 0x05a2 },
+ { 0x55, 0x05a3 },
+ { 0x01, 0x02db },
+ { 0x01, 0x0040 },
+};
+
+static void ec_read_write_reg_timeout(uint16_t ldn, uint8_t *val, uint16_t reg,
+ uint8_t rw_bit)
+{
+ uint16_t timeout = 0;
+ rw_bit &= 1;
+ sch5545_emi_ec2h_mailbox_clear();
+ sch5545_emi_ec_write16(0x8000, (ldn << 1) | 0x100 | rw_bit);
+
+ sch5545_emi_set_ec_addr(0x8004);
+
+ if (rw_bit)
+ outb(*val, emi_bar + SCH5545_EMI_EC_DATA);
+
+ outb(reg & 0xff, emi_bar + SCH5545_EMI_EC_DATA + 2);
+ outb((reg >> 8) & 0xff, emi_bar + SCH5545_EMI_EC_DATA + 3);
+ sch5545_emi_h2ec_mbox_write(1);
+
+ do {
+ timeout++;
+ if ((sch5545_emi_ec2h_mbox_read() & 1) != 0)
+ break;
+ } while (timeout < 0xfff);
+
+ sch5545_emi_set_int_src(0x11);
+ sch5545_emi_h2ec_mbox_write(0xc0);
+
+ if (!rw_bit)
+ *val = inb(emi_bar + SCH5545_EMI_EC_DATA);
+}
+
+static void ec_init_gpios(void)
+{
+ unsigned int i;
+ uint8_t val;
+
+ for (i = 0; i < ARRAY_SIZE(ec_gpio_init_table); i++) {
+ val = ec_gpio_init_table[i].val;
+ ec_read_write_reg_timeout(EC_GPIO_LDN, &val, ec_gpio_init_table[i].reg,
+ WRITE_OP);
+ }
+}
+
+static void ec_early_hwm_init(void)
+{
+ unsigned int i;
+ uint8_t val;
+
+ for (i = 0; i < ARRAY_SIZE(ec_hwm_early_init_table); i++) {
+ val = ec_hwm_early_init_table[i].val;
+ ec_read_write_reg_timeout(EC_HWM_LDN, &val, ec_hwm_early_init_table[i].reg,
+ WRITE_OP);
+ }
+}
+
+void sch5545_ec_early_init(void)
+{
+ emi_bar = sch5545_read_emi_bar(0x2e);
+
+ ec_init_gpios();
+ ec_early_hwm_init();
+}
diff --git a/src/mainboard/dell/optiplex_9010/smihandler.c b/src/mainboard/dell/optiplex_9010/smihandler.c
new file mode 100644
index 0000000000..d3c83ef5e7
--- /dev/null
+++ b/src/mainboard/dell/optiplex_9010/smihandler.c
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <stdint.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <cpu/x86/smm.h>
+#include <superio/smsc/sch5545/sch5545.h>
+
+void mainboard_smi_gpi(u32 gpi_sts)
+{
+ printk(BIOS_SPEW, "%s: gpi_sts: %08x\n", __func__, gpi_sts);
+}
+
+int mainboard_smi_apmc(u8 data)
+{
+ u8 val;
+ switch (data) {
+ case APM_CNT_ACPI_ENABLE:
+ printk(BIOS_SPEW, "%s: APM CNT EN: %02x\n", __func__, data);
+ /* Enable wake on PS2 */
+ val = inb(SCH5545_RUNTIME_REG_BASE + SCH5545_RR_PME_EN1);
+ val |= (SCH5545_KBD_PME_EN | SCH5545_MOUSE_PME_EN);
+ outb(val, SCH5545_RUNTIME_REG_BASE + SCH5545_RR_PME_EN1);
+ /* Clear pending and enable PMEs */
+ outb(SCH5545_GLOBAL_PME_STS, SCH5545_RUNTIME_REG_BASE + SCH5545_RR_PME_STS);
+ outb(SCH5545_GLOBAL_PME_EN, SCH5545_RUNTIME_REG_BASE + SCH5545_RR_PME_EN);
+ break;
+ case APM_CNT_ACPI_DISABLE:
+ printk(BIOS_SPEW, "%s: APM CNT DIS: %02x\n", __func__, data);
+ /* Disable wake on PS2 */
+ val = inb(SCH5545_RUNTIME_REG_BASE + SCH5545_RR_PME_EN1);
+ val &= ~(SCH5545_KBD_PME_EN | SCH5545_MOUSE_PME_EN);
+ outb(val, SCH5545_RUNTIME_REG_BASE + SCH5545_RR_PME_EN1);
+ /* Clear pending and disable PMEs */
+ outb(SCH5545_GLOBAL_PME_STS, SCH5545_RUNTIME_REG_BASE + SCH5545_RR_PME_STS);
+ outb(0, SCH5545_RUNTIME_REG_BASE + SCH5545_RR_PME_EN);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+void mainboard_smi_sleep(u8 slp_typ)
+{
+ printk(BIOS_SPEW, "%s: SMI sleep: %02x\n", __func__, slp_typ);
+}