mb/dell/optiplex_9010: Add Dell OptiPlex 9010 SFF support
Based on the autoport. The OptiPlex 9010 comes in four different sizes:
MT, DT, SFF and USFF. Tested on SFF only. The other PCBs are slightly
different, but they are designed with intercompatibility in mind. With
small devicetree overrides it should work on OptiPlex 7010 and other
OptiPlex 9010 variants as well.
Signed-off-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Change-Id: I88d65cae30d08ca727d86d930707c2be25a527cf
Reviewed-on: https://review.coreboot.org/c/coreboot/+/40351
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/Documentation/mainboard/dell/optiplex_9010.jpg b/Documentation/mainboard/dell/optiplex_9010.jpg
new file mode 100644
index 0000000..a5978ac
--- /dev/null
+++ b/Documentation/mainboard/dell/optiplex_9010.jpg
Binary files differ
diff --git a/Documentation/mainboard/dell/optiplex_9010.md b/Documentation/mainboard/dell/optiplex_9010.md
new file mode 100644
index 0000000..f22623d
--- /dev/null
+++ b/Documentation/mainboard/dell/optiplex_9010.md
@@ -0,0 +1,147 @@
+# Dell OptiPlex 9010
+
+This page describes how to run coreboot on Dell OptiPlex 9010 SFF.
+
+![](optiplex_9010.jpg)
+
+## Technology
+
+```eval_rst
++------------+---------------------------------------------------------------+
+| CPU | Intel Core 2nd Gen (Sandybridge) or 3rd Gen (Ivybridge) |
++------------+---------------------------------------------------------------+
+| DRAM | Up to 4 DIMM slots, up to 32GB 1600MHz non-ECC DDR3 SDRAM |
++------------+---------------------------------------------------------------+
+| Chipset | Intel Q77 Express |
++------------+---------------------------------------------------------------+
+| Super I/O | SMSC SCH5545 (or SCH5544) with Environmental Controller |
++------------+---------------------------------------------------------------+
+| TPM | ST Microelectronics ST33ZP24 |
++------------+---------------------------------------------------------------+
+| Boot | From USB, SATA, NVMe (using PCIe x4 expansion card) |
++------------+---------------------------------------------------------------+
+| Power | 200W-275W PSU |
++------------+---------------------------------------------------------------+
+```
+
+More specifications on [Dell OptiPlex 9010 specifications].
+
+## Required proprietary blobs
+
+```eval_rst
++------------------+---------------------------------+---------------------+
+| Binary file | Apply | Required / Optional |
++==================+=================================+=====================+
+| smsc_sch5545.bin | SMSC SCH5545 EC | Optional |
++------------------+---------------------------------+---------------------+
+| microcode | CPU microcode | Required |
++------------------+---------------------------------+---------------------+
+```
+
+Microcode updates are automatically included into the coreboot image by build
+system from the `3rdparty/intel-microcode` submodule.
+
+SMSC SC5545 EC firmware is optional, however lack of the binary will result in
+EC malfunction after power failure and fans running at full speed. The blob can
+be extracted from original firmware. It should be located under a file with
+GUID D386BEB8-4B54-4E69-94F5-06091F67E0D3, raw section. The file begins with a
+signature `SMSCUBIM`. The easiest way to do this is to use [UEFITool] and
+`Extract body` option on the raw section of the file.
+
+## Flashing coreboot
+
+```eval_rst
++---------------------+--------------------------+
+| Type | Value |
++=====================+==========================+
+| Socketed flash | no |
++---------------------+--------------------------+
+| Model | MX25L6406E/MX25L3206E |
++---------------------+--------------------------+
+| Size | 8 + 4 MiB |
++---------------------+--------------------------+
+| Package | SOIC-16 + SOIC-8 |
++---------------------+--------------------------+
+| Write protection | chipset PRR |
++---------------------+--------------------------+
+| Dual BIOS feature | no |
++---------------------+--------------------------+
+| Internal flashing | yes |
++---------------------+--------------------------+
+```
+
+### Internal programming
+
+The SPI flash can be accessed using [flashrom].
+
+ flashrom -p internal -w coreboot.rom --ifd -i bios
+
+Internal programming will not work when migrating from original UEFI firmware.
+One will have to short the SERVICE_MODE jumper to enable HMRFPO and then boot
+the machine to flash it.
+
+### External programming
+
+The external access to flash chip is available through standard SOP-8 clip
+and/or SOP-16 clip on the right side of the CPU fan (marked on the board
+image). The voltage of SPI flash is 3.3V.
+
+There are no restrictions as to the programmer device. It is only recommended
+to flash firmware without supplying power. There are no diodes connected to the
+flash chips. External programming can be performed, for example using OrangePi
+and Armbian. You can use linux_spi driver which provides communication with SPI
+devices. Example command to program SPI flash with OrangePi using linux_spi:
+
+ flashrom -w coreboot.rom -p linux_spi:dev=/dev/spidev1.0,spispeed=16000
+
+## Schematics
+
+There are no schematics for SFF, but if one looks for MT/DT schematics, they
+can be found publicly. Most of the schematics should match the SFF (although
+MT/DT has additional PCIe and PCI slot).
+
+## Known issues
+
+- There seems to be a problem with DRAM clearing on reboot. The SSKPD register
+ still contains 0xCAFE which leads to reset loop.
+
+## Untested
+
+Not all mainboard's peripherals and functions were tested because of lack of
+the cables or not being populated on the board case.
+
+- Internal USB 2.0 header
+- Wake from S3 using serial port
+- Wake-on-Lan from ACPI S4/S5
+
+## Working
+
+- USB 3.0 and 2.0 rear and front ports (SeaBIOS and Linux 4.19)
+- Gigabit Ethernet
+- VGA and 2x DP port using libgfxinit
+- flashrom
+- PCIe x1 WiFi in PCIe x4 slot
+- NVMe PCIe x4 using PCIe x4 expansion card
+- PCIe x16 PEG port using Dell Radeon HD 7570
+- SATA ports (SATA disks and DVD)
+- Super I/O serial port 0 (RS232 DB9 connector on the rear side)
+- SMBus (reading SPD from DIMMs)
+- CPU initialization using Intel i7-3770
+- Sandy Bridge/Ivy Bridge native RAM initialization
+- SeaBIOS payload (version rel-1.13.0)
+- PS/2 keyboard and mouse (including wake support)
+- LPC debug header (requires soldering of the pin header and shorting RF24 for
+ LPC clock)
+- USB debug dongle (the most bottom USB 2.0 port under RJ45 on the read side)
+- SMSC SCH5545 Super I/O initialization
+- SMSC SCH5545 EC initialization and firmware update
+- SMSC SCH5545 EC automatic fan control
+- TPM 1.2
+- Booting Debian 10, Ubuntu 18.04, QubesOS R4.01
+- Boot with cleaned ME
+- Intruder detection
+- Wake-on-Lan from ACPI S3
+
+[flashrom]: https://flashrom.org/Flashrom
+[Dell OptiPlex 9010 specifications]: https://www.dell.com/downloads/global/products/optix/en/dell_optiplex_9010_spec_sheet.pdf
+[UEFITool]: https://github.com/LongSoft/UEFITool
diff --git a/Documentation/mainboard/index.md b/Documentation/mainboard/index.md
index 33c60a4..b9fc68d 100644
--- a/Documentation/mainboard/index.md
+++ b/Documentation/mainboard/index.md
@@ -26,6 +26,10 @@
- [CN81XX EVB SFF](cavium/cn8100_sff_evb.md)
+## Dell
+
+- [OptiPlex 9010 SFF](dell/optiplex_9010.md)
+
## Emulation
The boards in this section are not real mainboards, but emulators.
diff --git a/configs/config.dell_optiplex_9010_sff b/configs/config.dell_optiplex_9010_sff
new file mode 100644
index 0000000..e25653e
--- /dev/null
+++ b/configs/config.dell_optiplex_9010_sff
@@ -0,0 +1,9 @@
+CONFIG_USE_OPTION_TABLE=y
+CONFIG_USE_BLOBS=y
+CONFIG_VENDOR_DELL=y
+CONFIG_ONBOARD_VGA_IS_PRIMARY=y
+# CONFIG_DRIVERS_UART_8250IO is not set
+CONFIG_PCIEXP_CLK_PM=y
+CONFIG_SEABIOS_PS2_TIMEOUT=3000
+CONFIG_POST_DEVICE_LPC=y
+CONFIG_HAVE_EM100_SUPPORT=y
diff --git a/src/mainboard/dell/Kconfig b/src/mainboard/dell/Kconfig
new file mode 100644
index 0000000..298c62b
--- /dev/null
+++ b/src/mainboard/dell/Kconfig
@@ -0,0 +1,16 @@
+if VENDOR_DELL
+
+choice
+ prompt "Mainboard model"
+
+source "src/mainboard/dell/*/Kconfig.name"
+
+endchoice
+
+source "src/mainboard/dell/*/Kconfig"
+
+config MAINBOARD_VENDOR
+ string
+ default "Dell Inc."
+
+endif # VENDOR_DELL
diff --git a/src/mainboard/dell/Kconfig.name b/src/mainboard/dell/Kconfig.name
new file mode 100644
index 0000000..3d2fefd
--- /dev/null
+++ b/src/mainboard/dell/Kconfig.name
@@ -0,0 +1,2 @@
+config VENDOR_DELL
+ bool "Dell Inc."
diff --git a/src/mainboard/dell/optiplex_9010/Kconfig b/src/mainboard/dell/optiplex_9010/Kconfig
new file mode 100644
index 0000000..6a3feb3
--- /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 0000000..96707c2
--- /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 0000000..7a8a684
--- /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 0000000..e69de29
--- /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 0000000..f890b5a
--- /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 0000000..fa1dfcf
--- /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 0000000..33feed2
--- /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 0000000..f49f3ba
--- /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 0000000..ccc7e64
--- /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 0000000..dcf9639
--- /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 0000000..d1a95e8
--- /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 0000000..6a21d20
--- /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 0000000..6a6dcb4
--- /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 0000000..5469c94
--- /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 0000000..74b5064
--- /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 0000000..d01e622
--- /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 0000000..eab4ba9
--- /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 0000000..4490c14
--- /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 0000000..36af6e4
--- /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 0000000..4013452
--- /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 0000000..0ca589f
--- /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 0000000..70b7de6
--- /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 0000000..d3c83ef
--- /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);
+}