From 8cba237086dfbb312a5913bb75eef4f6046aeae5 Mon Sep 17 00:00:00 2001 From: Nicholas Chin Date: Mon, 18 Mar 2024 10:45:05 -0600 Subject: util: Import autoport with Haswell patches This is a copy of coreboot's autoport utility, with a patch applied to support Haswell/Lynx Point platforms. That patch is currently in review on coreboot's Gerrit. https://review.coreboot.org/c/coreboot/+/30890 Signed-off-by: Nicholas Chin --- util/autoport/azalia.go | 67 +++ util/autoport/bd82x6x.go | 337 ++++++++++++++ util/autoport/description.md | 1 + util/autoport/ec_fixme.go | 91 ++++ util/autoport/ec_lenovo.go | 245 ++++++++++ util/autoport/ec_none.go | 24 + util/autoport/go.mod | 1 + util/autoport/gpio_common.go | 113 +++++ util/autoport/haswell.go | 139 ++++++ util/autoport/log_maker.go | 190 ++++++++ util/autoport/log_reader.go | 417 +++++++++++++++++ util/autoport/lynxpoint.go | 490 ++++++++++++++++++++ util/autoport/lynxpoint_lp_gpio.go | 252 ++++++++++ util/autoport/main.go | 915 +++++++++++++++++++++++++++++++++++++ util/autoport/rce823.go | 39 ++ util/autoport/readme.md | 457 ++++++++++++++++++ util/autoport/root.go | 47 ++ util/autoport/sandybridge.go | 93 ++++ 18 files changed, 3918 insertions(+) create mode 100644 util/autoport/azalia.go create mode 100644 util/autoport/bd82x6x.go create mode 100644 util/autoport/description.md create mode 100644 util/autoport/ec_fixme.go create mode 100644 util/autoport/ec_lenovo.go create mode 100644 util/autoport/ec_none.go create mode 100644 util/autoport/go.mod create mode 100644 util/autoport/gpio_common.go create mode 100644 util/autoport/haswell.go create mode 100644 util/autoport/log_maker.go create mode 100644 util/autoport/log_reader.go create mode 100644 util/autoport/lynxpoint.go create mode 100644 util/autoport/lynxpoint_lp_gpio.go create mode 100644 util/autoport/main.go create mode 100644 util/autoport/rce823.go create mode 100644 util/autoport/readme.md create mode 100644 util/autoport/root.go create mode 100644 util/autoport/sandybridge.go diff --git a/util/autoport/azalia.go b/util/autoport/azalia.go new file mode 100644 index 00000000..6dd78b1e --- /dev/null +++ b/util/autoport/azalia.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "sort" +) + +type azalia struct { +} + +func (i azalia) Scan(ctx Context, addr PCIDevData) { + az := Create(ctx, "hda_verb.c") + defer az.Close() + + Add_gpl(az) + az.WriteString( + `#include + +const u32 cim_verb_data[] = { +`) + + for _, codec := range ctx.InfoSource.GetAzaliaCodecs() { + fmt.Fprintf(az, "\t0x%08x,\t/* Codec Vendor / Device ID: %s */\n", + codec.VendorID, codec.Name) + fmt.Fprintf(az, "\t0x%08x,\t/* Subsystem ID */\n", + codec.SubsystemID) + fmt.Fprintf(az, "\t%d,\t\t/* Number of 4 dword sets */\n", + len(codec.PinConfig)+1) + fmt.Fprintf(az, "\tAZALIA_SUBVENDOR(%d, 0x%08x),\n", + codec.CodecNo, codec.SubsystemID) + + keys := []int{} + for nid, _ := range codec.PinConfig { + keys = append(keys, nid) + } + + sort.Ints(keys) + + for _, nid := range keys { + fmt.Fprintf(az, "\tAZALIA_PIN_CFG(%d, 0x%02x, 0x%08x),\n", + codec.CodecNo, nid, codec.PinConfig[nid]) + } + az.WriteString("\n"); + } + + az.WriteString( + `}; + +const u32 pc_beep_verbs[0] = {}; + +AZALIA_ARRAY_SIZES; +`) + + PutPCIDev(addr, "") +} + +func init() { + /* I82801GX/I945 */ + RegisterPCI(0x8086, 0x27d8, azalia{}) + /* BD82X6X/sandybridge */ + RegisterPCI(0x8086, 0x1c20, azalia{}) + /* C216/ivybridge */ + RegisterPCI(0x8086, 0x1e20, azalia{}) + /* Lynx Point */ + RegisterPCI(0x8086, 0x8c20, azalia{}) + RegisterPCI(0x8086, 0x9c20, azalia{}) +} diff --git a/util/autoport/bd82x6x.go b/util/autoport/bd82x6x.go new file mode 100644 index 00000000..8c418e30 --- /dev/null +++ b/util/autoport/bd82x6x.go @@ -0,0 +1,337 @@ +package main + +import "fmt" + +type bd82x6x struct { + variant string + node *DevTreeNode +} + +func IsPCIeHotplug(ctx Context, port int) bool { + portDev, ok := PCIMap[PCIAddr{Bus: 0, Dev: 0x1c, Func: port}] + if !ok { + return false + } + return (portDev.ConfigDump[0xdb] & (1 << 6)) != 0 +} + +func ich9GetFlashSize(ctx Context) { + inteltool := ctx.InfoSource.GetInteltool() + switch (inteltool.RCBA[0x3410] >> 10) & 3 { + /* SPI. All boards I've seen with sandy/ivy use SPI. */ + case 3: + ROMProtocol = "SPI" + highflkb := uint32(0) + for reg := uint16(0); reg < 5; reg++ { + fl := (inteltool.RCBA[0x3854+4*reg] >> 16) & 0x1fff + flkb := (fl + 1) << 2 + if flkb > highflkb { + highflkb = flkb + } + } + ROMSizeKB = int(highflkb) + /* Shared with ME. Flashrom is unable to handle it. */ + FlashROMSupport = "n" + } +} + +func (b bd82x6x) GetGPIOHeader() string { + return "southbridge/intel/bd82x6x/pch.h" +} + +func (b bd82x6x) EnableGPE(in int) { + b.node.Registers[fmt.Sprintf("gpi%d_routing", in)] = "2" +} + +func (b bd82x6x) EncodeGPE(in int) int { + return in + 0x10 +} + +func (b bd82x6x) DecodeGPE(in int) int { + return in - 0x10 +} + +func (b bd82x6x) NeedRouteGPIOManually() { + b.node.Comment += ", FIXME: set gpiX_routing for EC support" +} + +func (b bd82x6x) Scan(ctx Context, addr PCIDevData) { + + SouthBridge = &b + + inteltool := ctx.InfoSource.GetInteltool() + GPIO(ctx, inteltool) + + KconfigBool["SOUTHBRIDGE_INTEL_"+b.variant] = true + KconfigBool["SERIRQ_CONTINUOUS_MODE"] = true + KconfigInt["USBDEBUG_HCD_INDEX"] = 2 + KconfigComment["USBDEBUG_HCD_INDEX"] = "FIXME: check this" + dmi := ctx.InfoSource.GetDMI() + if dmi.Vendor == "LENOVO" { + KconfigInt["DRAM_RESET_GATE_GPIO"] = 10 + } else { + KconfigInt["DRAM_RESET_GATE_GPIO"] = 60 + } + KconfigComment["DRAM_RESET_GATE_GPIO"] = "FIXME: check this" + + ich9GetFlashSize(ctx) + + DSDTDefines = append(DSDTDefines, + DSDTDefine{ + Key: "BRIGHTNESS_UP", + Value: "\\_SB.PCI0.GFX0.INCB", + }, + DSDTDefine{ + Key: "BRIGHTNESS_DOWN", + Value: "\\_SB.PCI0.GFX0.DECB", + }) + + /* SPI init */ + MainboardIncludes = append(MainboardIncludes, "southbridge/intel/bd82x6x/pch.h") + + FADT := ctx.InfoSource.GetACPI()["FACP"] + + pcieHotplugMap := "{ " + + for port := 0; port < 7; port++ { + if IsPCIeHotplug(ctx, port) { + pcieHotplugMap += "1, " + } else { + pcieHotplugMap += "0, " + } + } + + if IsPCIeHotplug(ctx, 7) { + pcieHotplugMap += "1 }" + } else { + pcieHotplugMap += "0 }" + } + + cur := DevTreeNode{ + Chip: "southbridge/intel/bd82x6x", + Comment: "Intel Series 6 Cougar Point PCH", + + Registers: map[string]string{ + "sata_interface_speed_support": "0x3", + "gen1_dec": FormatHexLE32(PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 0}].ConfigDump[0x84:0x88]), + "gen2_dec": FormatHexLE32(PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 0}].ConfigDump[0x88:0x8c]), + "gen3_dec": FormatHexLE32(PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 0}].ConfigDump[0x8c:0x90]), + "gen4_dec": FormatHexLE32(PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 0}].ConfigDump[0x90:0x94]), + "pcie_port_coalesce": "1", + "pcie_hotplug_map": pcieHotplugMap, + + "sata_port_map": fmt.Sprintf("0x%x", PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 2}].ConfigDump[0x92]&0x3f), + + "docking_supported": (FormatBool((FADT[113] & (1 << 1)) != 0)), + "spi_uvscc": fmt.Sprintf("0x%x", inteltool.RCBA[0x38c8]), + "spi_lvscc": fmt.Sprintf("0x%x", inteltool.RCBA[0x38c4]&^(1<<23)), + }, + PCISlots: []PCISlot{ + PCISlot{PCIAddr: PCIAddr{Dev: 0x14, Func: 0}, writeEmpty: false, alias: "xhci", additionalComment: "USB 3.0 Controller"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x16, Func: 0}, writeEmpty: true, alias: "mei1", additionalComment: "Management Engine Interface 1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x16, Func: 1}, writeEmpty: true, alias: "mei2", additionalComment: "Management Engine Interface 2"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x16, Func: 2}, writeEmpty: true, alias: "me_ide_r", additionalComment: "Management Engine IDE-R"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x16, Func: 3}, writeEmpty: true, alias: "me_kt", additionalComment: "Management Engine KT"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x19, Func: 0}, writeEmpty: true, alias: "gbe", additionalComment: "Intel Gigabit Ethernet"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1a, Func: 0}, writeEmpty: true, alias: "ehci2", additionalComment: "USB2 EHCI #2"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1b, Func: 0}, writeEmpty: true, alias: "hda", additionalComment: "High Definition Audio"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 0}, writeEmpty: true, alias: "pcie_rp1", additionalComment: "PCIe Port #1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 1}, writeEmpty: true, alias: "pcie_rp2", additionalComment: "PCIe Port #2"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 2}, writeEmpty: true, alias: "pcie_rp3", additionalComment: "PCIe Port #3"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 3}, writeEmpty: true, alias: "pcie_rp4", additionalComment: "PCIe Port #4"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 4}, writeEmpty: true, alias: "pcie_rp5", additionalComment: "PCIe Port #5"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 5}, writeEmpty: true, alias: "pcie_rp6", additionalComment: "PCIe Port #6"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 6}, writeEmpty: true, alias: "pcie_rp7", additionalComment: "PCIe Port #7"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 7}, writeEmpty: true, alias: "pcie_rp8", additionalComment: "PCIe Port #8"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1d, Func: 0}, writeEmpty: true, alias: "ehci1", additionalComment: "USB2 EHCI #1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1e, Func: 0}, writeEmpty: true, alias: "pci_bridge", additionalComment: "PCI bridge"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 0}, writeEmpty: true, alias: "lpc", additionalComment: "LPC bridge"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 2}, writeEmpty: true, alias: "sata1", additionalComment: "SATA Controller 1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 3}, writeEmpty: true, alias: "smbus", additionalComment: "SMBus"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 5}, writeEmpty: true, alias: "sata2", additionalComment: "SATA Controller 2"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 6}, writeEmpty: true, alias: "thermal", additionalComment: "Thermal"}, + }, + } + + b.node = &cur + + xhciDev, ok := PCIMap[PCIAddr{Bus: 0, Dev: 0x14, Func: 0}] + + if ok { + cur.Registers["xhci_switchable_ports"] = FormatHexLE32(xhciDev.ConfigDump[0xd4:0xd8]) + cur.Registers["superspeed_capable_ports"] = FormatHexLE32(xhciDev.ConfigDump[0xdc:0xe0]) + cur.Registers["xhci_overcurrent_mapping"] = FormatHexLE32(xhciDev.ConfigDump[0xc0:0xc4]) + } + + PutPCIChip(addr, cur) + PutPCIDevParent(addr, "", "lpc") + + DSDTIncludes = append(DSDTIncludes, DSDTInclude{ + File: "southbridge/intel/common/acpi/platform.asl", + }) + DSDTIncludes = append(DSDTIncludes, DSDTInclude{ + File: "southbridge/intel/bd82x6x/acpi/globalnvs.asl", + }) + DSDTIncludes = append(DSDTIncludes, DSDTInclude{ + File: "southbridge/intel/common/acpi/sleepstates.asl", + }) + DSDTPCI0Includes = append(DSDTPCI0Includes, DSDTInclude{ + File: "southbridge/intel/bd82x6x/acpi/pch.asl", + }) + + AddBootBlockFile("early_init.c", "") + AddROMStageFile("early_init.c", "") + + sb := Create(ctx, "early_init.c") + defer sb.Close() + Add_gpl(sb) + + sb.WriteString(` +#include +#include +#include +#include + +`) + sb.WriteString("const struct southbridge_usb_port mainboard_usb_ports[] = {\n") + + currentMap := map[uint32]int{ + 0x20000153: 0, + 0x20000f57: 1, + 0x2000055b: 2, + 0x20000f51: 3, + 0x2000094a: 4, + 0x2000035f: 5, + 0x20000f53: 6, + 0x20000357: 7, + 0x20000353: 8, + } + + for port := uint(0); port < 14; port++ { + var pinmask uint32 + OCPin := -1 + if port < 8 { + pinmask = inteltool.RCBA[0x35a0] + } else { + pinmask = inteltool.RCBA[0x35a4] + } + for pin := uint(0); pin < 4; pin++ { + if ((pinmask >> ((port % 8) + 8*pin)) & 1) != 0 { + OCPin = int(pin) + if port >= 8 { + OCPin += 4 + } + } + } + current, ok := currentMap[inteltool.RCBA[uint16(0x3500+4*port)]] + comment := "" + if !ok { + comment = fmt.Sprintf("// FIXME: Unknown current: RCBA(0x%x)=0x%x", 0x3500+4*port, uint16(0x3500+4*port)) + } + fmt.Fprintf(sb, "\t{ %d, %d, %d }, %s\n", + ((inteltool.RCBA[0x359c]>>port)&1)^1, + current, + OCPin, + comment) + } + sb.WriteString("};\n") + + guessedMap := GuessSPDMap(ctx) + + sb.WriteString(` +void bootblock_mainboard_early_init(void) +{ +`) + RestorePCI16Simple(sb, addr, 0x82) + + RestorePCI16Simple(sb, addr, 0x80) + + sb.WriteString(`} + +/* FIXME: Put proper SPD map here. */ +void mainboard_get_spd(spd_raw_data *spd, bool id_only) +{ +`) + for i, spd := range guessedMap { + fmt.Fprintf(sb, "\tread_spd(&spd[%d], 0x%02x, id_only);\n", i, spd) + } + sb.WriteString("}\n") + + gnvs := Create(ctx, "acpi_tables.c") + defer gnvs.Close() + + Add_gpl(gnvs) + gnvs.WriteString(`#include +#include + +/* FIXME: check this function. */ +void mainboard_fill_gnvs(struct global_nvs *gnvs) +{ + /* The lid is open by default. */ + gnvs->lids = 1; + + /* Temperature at which OS will shutdown */ + gnvs->tcrt = 100; + /* Temperature at which OS will throttle CPU */ + gnvs->tpsv = 90; +} +`) +} + +func init() { + /* BD82X6X LPC */ + for id := 0x1c40; id <= 0x1c5f; id++ { + RegisterPCI(0x8086, uint16(id), bd82x6x{variant: "BD82X6X"}) + } + + /* C216 LPC */ + for id := 0x1e41; id <= 0x1e5f; id++ { + RegisterPCI(0x8086, uint16(id), bd82x6x{variant: "C216"}) + } + + /* PCIe bridge */ + for _, id := range []uint16{ + 0x1c10, 0x1c12, 0x1c14, 0x1c16, + 0x1c18, 0x1c1a, 0x1c1c, 0x1c1e, + 0x1e10, 0x1e12, 0x1e14, 0x1e16, + 0x1e18, 0x1e1a, 0x1e1c, 0x1e1e, + 0x1e25, 0x244e, 0x2448, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } + + /* SMBus controller */ + RegisterPCI(0x8086, 0x1c22, GenericPCI{MissingParent: "smbus"}) + RegisterPCI(0x8086, 0x1e22, GenericPCI{MissingParent: "smbus"}) + + /* SATA */ + for _, id := range []uint16{ + 0x1c00, 0x1c01, 0x1c02, 0x1c03, + 0x1e00, 0x1e01, 0x1e02, 0x1e03, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } + + /* EHCI */ + for _, id := range []uint16{ + 0x1c26, 0x1c2d, 0x1e26, 0x1e2d, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } + + /* XHCI */ + RegisterPCI(0x8086, 0x1e31, GenericPCI{}) + + /* ME and children */ + for _, id := range []uint16{ + 0x1c3a, 0x1c3b, 0x1c3c, 0x1c3d, + 0x1e3a, 0x1e3b, 0x1e3c, 0x1e3d, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } + + /* Ethernet */ + RegisterPCI(0x8086, 0x1502, GenericPCI{}) + RegisterPCI(0x8086, 0x1503, GenericPCI{}) + +} diff --git a/util/autoport/description.md b/util/autoport/description.md new file mode 100644 index 00000000..9a0e8d43 --- /dev/null +++ b/util/autoport/description.md @@ -0,0 +1 @@ +Automated porting coreboot to Sandy Bridge/Ivy Bridge platforms `Go` diff --git a/util/autoport/ec_fixme.go b/util/autoport/ec_fixme.go new file mode 100644 index 00000000..54f78ab4 --- /dev/null +++ b/util/autoport/ec_fixme.go @@ -0,0 +1,91 @@ +package main + +import "fmt" + +func FIXMEEC(ctx Context) { + ap := Create(ctx, "acpi/platform.asl") + defer ap.Close() + + hasKeyboard := ctx.InfoSource.HasPS2() + + sbGPE := GuessECGPE(ctx) + var GPEUnsure bool + if sbGPE < 0 { + sbGPE = SouthBridge.EncodeGPE(1) + GPEUnsure = true + SouthBridge.NeedRouteGPIOManually() + } else { + GPEUnsure = false + SouthBridge.EnableGPE(SouthBridge.DecodeGPE(sbGPE)) + } + + Add_gpl(ap) + ap.WriteString( + `Method(_WAK, 1) +{ + /* FIXME: EC support */ + Return(Package() {0, 0}) +} + +Method(_PTS,1) +{ + /* FIXME: EC support */ +} +`) + + ecs := ctx.InfoSource.GetEC() + MainboardIncludes = append(MainboardIncludes, "ec/acpi/ec.h") + MainboardIncludes = append(MainboardIncludes, "console/console.h") + + MainboardInit += + ` /* FIXME: trim this down or remove if necessary */ + { + int i; + const u8 dmp[256] = {` + for i := 0; i < 0x100; i++ { + if (i & 0xf) == 0 { + MainboardInit += fmt.Sprintf("\n\t\t\t/* %02x */ ", i) + } + MainboardInit += fmt.Sprintf("0x%02x,", ecs[i]) + if (i & 0xf) != 0xf { + MainboardInit += " " + } + } + MainboardInit += "\n\t\t};\n" + MainboardInit += ` + printk(BIOS_DEBUG, "Replaying EC dump ..."); + for (i = 0; i < 256; i++) + ec_write (i, dmp[i]); + printk(BIOS_DEBUG, "done\n"); + } +` + + KconfigBool["EC_ACPI"] = true + si := Create(ctx, "acpi/superio.asl") + defer si.Close() + + if hasKeyboard { + Add_gpl(si) + si.WriteString("#include \n") + MainboardInit += fmt.Sprintf("\tpc_keyboard_init(NO_AUX_DEVICE);\n") + MainboardIncludes = append(MainboardIncludes, "pc80/keyboard.h") + } + + ec := Create(ctx, "acpi/ec.asl") + defer ec.Close() + + Add_gpl(ec) + ec.WriteString(`Device(EC) +{ + Name (_HID, EISAID("PNP0C09")) + Name (_UID, 0) +`) + if GPEUnsure { + ec.WriteString("\t/* FIXME: Set GPE */\n") + ec.WriteString("\t/* Name (_GPE, ?) */\n") + } else { + fmt.Fprintf(ec, "\tName (_GPE, %d)\n", sbGPE) + } + ec.WriteString("/* FIXME: EC support */\n") + ec.WriteString("}\n") +} diff --git a/util/autoport/ec_lenovo.go b/util/autoport/ec_lenovo.go new file mode 100644 index 00000000..a34960ff --- /dev/null +++ b/util/autoport/ec_lenovo.go @@ -0,0 +1,245 @@ +package main + +import "fmt" + +func LenovoEC(ctx Context) { + ap := Create(ctx, "acpi/platform.asl") + defer ap.Close() + + wakeGPE := 13 + + sbGPE := GuessECGPE(ctx) + var GPE int + var GPEUnsure bool + if sbGPE < 0 { + sbGPE = SouthBridge.EncodeGPE(1) + GPE = 1 + GPEUnsure = true + SouthBridge.NeedRouteGPIOManually() + } else { + GPE = SouthBridge.DecodeGPE(sbGPE) + GPEUnsure = false + } + + SouthBridge.EnableGPE(wakeGPE) + SouthBridge.EnableGPE(GPE) + + GPEDefine := DSDTDefine{ + Key: "THINKPAD_EC_GPE", + } + + GPEDefine.Value = fmt.Sprintf("%d", sbGPE) + if GPEUnsure { + GPEDefine.Comment = "FIXME: Check this" + } + + DSDTDefines = append(DSDTDefines, + DSDTDefine{ + Key: "EC_LENOVO_H8_ME_WORKAROUND", + Value: "1", + }, GPEDefine) + + Add_gpl(ap) + ap.WriteString( + `Method(_WAK, 1) +{ + /* ME may not be up yet. */ + Store(0, \_TZ.MEB1) + Store(0, \_TZ.MEB2) + Return(Package() {0, 0}) +} + +Method(_PTS,1) +{ + \_SB.PCI0.LPCB.EC.RADI(0) +} +`) + + si := Create(ctx, "acpi/superio.asl") + defer si.Close() + + Add_gpl(si) + si.WriteString("#include \n") + + /* FIXME:XX Move this to ec/lenovo. */ + smi := Create(ctx, "smihandler.c") + defer smi.Close() + + AddSMMFile("smihandler.c", "") + + Add_gpl(smi) + smi.WriteString( + `#include +#include +#include +#include +#include +#include +#include <` + SouthBridge.GetGPIOHeader() + ">\n\n") + + if GPEUnsure { + smi.WriteString("/* FIXME: check this */\n") + } + fmt.Fprintf(smi, "#define GPE_EC_SCI %d\n", GPE) + + smi.WriteString("/* FIXME: check this */\n") + fmt.Fprintf(smi, "#define GPE_EC_WAKE %d\n", wakeGPE) + + smi.WriteString(` +static void mainboard_smi_handle_ec_sci(void) +{ + u8 status = inb(EC_SC); + u8 event; + + if (!(status & EC_SCI_EVT)) + return; + + event = ec_query(); + printk(BIOS_DEBUG, "EC event %#02x\n", event); +} + +void mainboard_smi_gpi(u32 gpi_sts) +{ + if (gpi_sts & (1 << GPE_EC_SCI)) + mainboard_smi_handle_ec_sci(); +} + +int mainboard_smi_apmc(u8 data) +{ + switch (data) { + case APM_CNT_ACPI_ENABLE: + /* use 0x1600/0x1604 to prevent races with userspace */ + ec_set_ports(0x1604, 0x1600); + /* route EC_SCI to SCI */ + gpi_route_interrupt(GPE_EC_SCI, GPI_IS_SCI); + /* discard all events, and enable attention */ + ec_write(0x80, 0x01); + break; + case APM_CNT_ACPI_DISABLE: + /* we have to use port 0x62/0x66, as 0x1600/0x1604 doesn't + provide a EC query function */ + ec_set_ports(0x66, 0x62); + /* route EC_SCI to SMI */ + gpi_route_interrupt(GPE_EC_SCI, GPI_IS_SMI); + /* discard all events, and enable attention */ + ec_write(0x80, 0x01); + break; + default: + break; + } + return 0; +} + +void mainboard_smi_sleep(u8 slp_typ) +{ + if (slp_typ == 3) { + u8 ec_wake = ec_read(0x32); + /* If EC wake events are enabled, enable wake on EC WAKE GPE. */ + if (ec_wake & 0x14) { + /* Redirect EC WAKE GPE to SCI. */ + gpi_route_interrupt(GPE_EC_WAKE, GPI_IS_SCI); + } + } +} +`) + + ec := Create(ctx, "acpi/ec.asl") + defer ec.Close() + + Add_gpl(ec) + ec.WriteString("#include \n") + + KconfigBool["EC_LENOVO_PMH7"] = true + KconfigBool["EC_LENOVO_H8"] = true + + pmh := DevTreeNode{ + Chip: "ec/lenovo/pmh7", + Registers: map[string]string{ + "backlight_enable": "true", + "dock_event_enable": "true", + }, + Children: []DevTreeNode{ + DevTreeNode{ + Chip: "pnp", + Comment: "dummy", + Dev: 0xff, + Func: 1, + }, + }, + } + PutChip("lpc", pmh) + + ecs := ctx.InfoSource.GetEC() + h8 := DevTreeNode{ + Chip: "ec/lenovo/h8", + Children: []DevTreeNode{ + DevTreeNode{ + Chip: "pnp", + Comment: "dummy", + Dev: 0xff, + Func: 2, + IOs: map[uint16]uint16{ + 0x60: 0x62, + 0x62: 0x66, + 0x64: 0x1600, + 0x66: 0x1604, + }, + }, + }, + Comment: "FIXME: has_keyboard_backlight, has_power_management_beeps, has_uwb", + Registers: map[string]string{ + "config0": FormatHex8(ecs[0]), + "config1": FormatHex8(ecs[1]), + "config2": FormatHex8(ecs[2]), + "config3": FormatHex8(ecs[3]), + "beepmask0": FormatHex8(ecs[4]), + "beepmask1": FormatHex8(ecs[5]), + }, + } + for i := 0; i < 0x10; i++ { + if ecs[0x10+i] != 0 { + h8.Registers[fmt.Sprintf("event%x_enable", i)] = FormatHex8(ecs[0x10+i]) + } + } + PutChip("lpc", h8) + + eeprom := DevTreeNode{ + Chip: "drivers/i2c/at24rf08c", + Comment: "eeprom, 8 virtual devices, same chip", + Children: []DevTreeNode{ + DevTreeNode{ + Chip: "i2c", + Dev: 0x54, + }, + DevTreeNode{ + Chip: "i2c", + Dev: 0x55, + }, + DevTreeNode{ + Chip: "i2c", + Dev: 0x56, + }, + DevTreeNode{ + Chip: "i2c", + Dev: 0x57, + }, + DevTreeNode{ + Chip: "i2c", + Dev: 0x5c, + }, + DevTreeNode{ + Chip: "i2c", + Dev: 0x5d, + }, + DevTreeNode{ + Chip: "i2c", + Dev: 0x5e, + }, + DevTreeNode{ + Chip: "i2c", + Dev: 0x5f, + }, + }, + } + PutChip("smbus", eeprom) +} diff --git a/util/autoport/ec_none.go b/util/autoport/ec_none.go new file mode 100644 index 00000000..bcb61bf0 --- /dev/null +++ b/util/autoport/ec_none.go @@ -0,0 +1,24 @@ +package main + +func NoEC(ctx Context) { + ap := Create(ctx, "acpi/platform.asl") + defer ap.Close() + + Add_gpl(ap) + ap.WriteString( + `Method(_WAK, 1) +{ + Return(Package() {0, 0}) +} + +Method(_PTS, 1) +{ +} +`) + + si := Create(ctx, "acpi/superio.asl") + defer si.Close() + + ec := Create(ctx, "acpi/ec.asl") + defer ec.Close() +} diff --git a/util/autoport/go.mod b/util/autoport/go.mod new file mode 100644 index 00000000..55a89cc9 --- /dev/null +++ b/util/autoport/go.mod @@ -0,0 +1 @@ +module autoport diff --git a/util/autoport/gpio_common.go b/util/autoport/gpio_common.go new file mode 100644 index 00000000..a869dce4 --- /dev/null +++ b/util/autoport/gpio_common.go @@ -0,0 +1,113 @@ +/* code to generate common GPIO code for Intel 6/7/8 Series Chipset */ + +package main + +import ( + "fmt" + "os" +) + +func writeGPIOSet(ctx Context, sb *os.File, + val uint32, set uint, partno int, constraint uint32) { + + max := uint(32) + if set == 3 { + max = 12 + } + + bits := [6][2]string{ + {"GPIO_MODE_NATIVE", "GPIO_MODE_GPIO"}, + {"GPIO_DIR_OUTPUT", "GPIO_DIR_INPUT"}, + {"GPIO_LEVEL_LOW", "GPIO_LEVEL_HIGH"}, + {"GPIO_RESET_PWROK", "GPIO_RESET_RSMRST"}, + {"GPIO_NO_INVERT", "GPIO_INVERT"}, + {"GPIO_NO_BLINK", "GPIO_BLINK"}, + } + + for i := uint(0); i < max; i++ { + if (constraint>>i)&1 == 1 { + fmt.Fprintf(sb, " .gpio%d = %s,\n", + (set-1)*32+i, + bits[partno][(val>>i)&1]) + } + } +} + +func GPIO(ctx Context, inteltool InteltoolData) { + var constraint uint32 + gpio := Create(ctx, "gpio.c") + defer gpio.Close() + + AddBootBlockFile("gpio.c", "") + AddROMStageFile("gpio.c", "") + + Add_gpl(gpio) + gpio.WriteString("#include \n\n") + + addresses := [3][6]int{ + {0x00, 0x04, 0x0c, 0x60, 0x2c, 0x18}, + {0x30, 0x34, 0x38, 0x64, -1, -1}, + {0x40, 0x44, 0x48, 0x68, -1, -1}, + } + + for set := 1; set <= 3; set++ { + for partno, part := range []string{"mode", "direction", "level", "reset", "invert", "blink"} { + addr := addresses[set-1][partno] + if addr < 0 { + continue + } + fmt.Fprintf(gpio, "static const struct pch_gpio_set%d pch_gpio_set%d_%s = {\n", + set, set, part) + + constraint = 0xffffffff + switch part { + case "direction": + /* Ignored on native mode */ + constraint = inteltool.GPIO[uint16(addresses[set-1][0])] + case "level": + /* Level doesn't matter for input */ + constraint = inteltool.GPIO[uint16(addresses[set-1][0])] + constraint &^= inteltool.GPIO[uint16(addresses[set-1][1])] + case "reset": + /* Only show reset */ + constraint = inteltool.GPIO[uint16(addresses[set-1][3])] + case "invert": + /* Only on input and only show inverted GPIO */ + constraint = inteltool.GPIO[uint16(addresses[set-1][0])] + constraint &= inteltool.GPIO[uint16(addresses[set-1][1])] + constraint &= inteltool.GPIO[uint16(addresses[set-1][4])] + case "blink": + /* Only on output and only show blinking GPIO */ + constraint = inteltool.GPIO[uint16(addresses[set-1][0])] + constraint &^= inteltool.GPIO[uint16(addresses[set-1][1])] + constraint &= inteltool.GPIO[uint16(addresses[set-1][5])] + } + writeGPIOSet(ctx, gpio, inteltool.GPIO[uint16(addr)], uint(set), partno, constraint) + gpio.WriteString("};\n\n") + } + } + + gpio.WriteString(`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/util/autoport/haswell.go b/util/autoport/haswell.go new file mode 100644 index 00000000..645b197a --- /dev/null +++ b/util/autoport/haswell.go @@ -0,0 +1,139 @@ +package main + +import "fmt" + +type haswellmc struct { + variant string +} + +func divceil(a uint32, b uint32) uint32 { + return (a + b - 1) / b +} + +func getPanelCfg(inteltool InteltoolData, isULT bool) string { + var refclk uint32 + var pwm_hz uint32 + + if isULT { + refclk = 24000000 + } else { + refclk = 135000000 + } + if (inteltool.IGD[0xc8254] >> 16) != 0 { + pwm_hz = refclk / 128 / (inteltool.IGD[0xc8254] >> 16) + } else { + pwm_hz = 0 + } + + gpu_panel_power_up_delay := (inteltool.IGD[0xc7208] >> 16) & 0x1fff + gpu_panel_power_backlight_on_delay := inteltool.IGD[0xc7208] & 0x1fff + gpu_panel_power_down_delay := (inteltool.IGD[0xc720c] >> 16) & 0x1fff + gpu_panel_power_backlight_off_delay := inteltool.IGD[0xc720c] & 0x1fff + gpu_panel_power_cycle_delay := inteltool.IGD[0xc7210] & 0x1f + + return fmt.Sprintf(`{ + .up_delay_ms = %3d, + .down_delay_ms = %3d, + .cycle_delay_ms = %3d, + .backlight_on_delay_ms = %3d, + .backlight_off_delay_ms = %3d, + .backlight_pwm_hz = %3d, + }`, + divceil(gpu_panel_power_up_delay, 10), + divceil(gpu_panel_power_down_delay, 10), + (gpu_panel_power_cycle_delay-1)*100, + divceil(gpu_panel_power_backlight_on_delay, 10), + divceil(gpu_panel_power_backlight_off_delay, 10), + pwm_hz) +} + +func (i haswellmc) Scan(ctx Context, addr PCIDevData) { + inteltool := ctx.InfoSource.GetInteltool() + + isULT := (i.variant == "ULT") + DevTree = DevTreeNode{ + Chip: "northbridge/intel/haswell", + MissingParent: "northbridge", + Comment: "FIXME: check ec_present, usb_xhci_on_resume, gfx", + Registers: map[string]string{ + "gpu_dp_b_hotplug": FormatInt32((inteltool.IGD[0xc4030] >> 2) & 7), + "gpu_dp_c_hotplug": FormatInt32((inteltool.IGD[0xc4030] >> 10) & 7), + "gpu_dp_d_hotplug": FormatInt32((inteltool.IGD[0xc4030] >> 18) & 7), + "panel_cfg": getPanelCfg(inteltool, isULT), + "gpu_ddi_e_connected": FormatBool(((inteltool.IGD[0x64000] >> 4) & 1) == 0), + "ec_present": "false", + "usb_xhci_on_resume": "false", + /* FIXME:XX hardcoded. */ + "gfx": "GMA_STATIC_DISPLAYS(0)", + }, + Children: []DevTreeNode{ + { + Chip: "cpu/intel/haswell", + Children: []DevTreeNode{ + { + Chip: "cpu_cluster", + Dev: 0, + Ops: "haswell_cpu_bus_ops", + }, + }, + }, + + { + Chip: "domain", + Dev: 0, + Ops: "haswell_pci_domain_ops", + PCIController: true, + ChildPCIBus: 0, + PCISlots: []PCISlot{ + PCISlot{PCIAddr: PCIAddr{Dev: 0x0, Func: 0}, writeEmpty: true, additionalComment: i.variant}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1, Func: 0}, writeEmpty: !isULT, additionalComment: "PCIe Bridge for discrete graphics"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x2, Func: 0}, writeEmpty: true, additionalComment: "Internal graphics"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x3, Func: 0}, writeEmpty: true, additionalComment: "Mini-HD audio"}, + }, + }, + }, + } + + if isULT { + DevTree.Registers["dq_pins_interleaved"] = FormatBool(((inteltool.MCHBAR[0x2008] >> 10) & 1) == 0) + } + + PutPCIDev(addr, "Host bridge") + + KconfigBool["NORTHBRIDGE_INTEL_HASWELL"] = true + KconfigBool["HAVE_ACPI_TABLES"] = true + KconfigBool["HAVE_ACPI_RESUME"] = true + + DSDTIncludes = append(DSDTIncludes, DSDTInclude{ + File: "cpu/intel/common/acpi/cpu.asl", + }) + + DSDTPCI0Includes = append(DSDTPCI0Includes, DSDTInclude{ + File: "northbridge/intel/haswell/acpi/hostbridge.asl", + }, DSDTInclude{ + File: "drivers/intel/gma/acpi/default_brightness_levels.asl", + Comment: "FIXME: remove this if the board doesn't have backlight", + }) +} + +func init() { + RegisterPCI(0x8086, 0x0c00, haswellmc{variant: "Desktop"}) + RegisterPCI(0x8086, 0x0c04, haswellmc{variant: "Mobile"}) + RegisterPCI(0x8086, 0x0a04, haswellmc{variant: "ULT"}) + RegisterPCI(0x8086, 0x0c08, haswellmc{variant: "Server"}) + RegisterPCI(0x8086, 0x0d00, haswellmc{variant: "Crystal Well Desktop"}) + RegisterPCI(0x8086, 0x0d04, haswellmc{variant: "Crystal Well Mobile"}) + RegisterPCI(0x8086, 0x0d08, haswellmc{variant: "Crystal Well Server"}) + for _, id := range []uint16{ + 0x0402, 0x0412, 0x0422, /* Desktop */ + 0x0406, 0x0416, 0x0426, /* Mobile */ + 0x040a, 0x041a, 0x042a, /* Server */ + 0x0a06, 0x0a16, 0x0a26, /* ULT */ + 0x0d16, 0x0d22, 0x0d26, 0x0d36, /* Mobile 4+3, GT3e */ + } { + RegisterPCI(0x8086, id, GenericVGA{GenericPCI{Comment: "VGA controller"}}) + } + /* CPU HD Audio */ + RegisterPCI(0x8086, 0x0a0c, GenericPCI{}) + RegisterPCI(0x8086, 0x0c0c, GenericPCI{}) +} diff --git a/util/autoport/log_maker.go b/util/autoport/log_maker.go new file mode 100644 index 00000000..2a524d38 --- /dev/null +++ b/util/autoport/log_maker.go @@ -0,0 +1,190 @@ +package main + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "os/exec" + "strings" + "bytes" +) + +func TryRunAndSave(output string, name string, arg []string) error { + cmd := exec.Command(name, arg...) + + f, err := os.Create(output) + if err != nil { + log.Fatal(err) + } + + cmd.Stdout = f + cmd.Stderr = f + + err = cmd.Start() + if err != nil { + return err + } + cmd.Wait() + return nil +} + +func RunAndSave(output string, name string, arg ...string) { + err := TryRunAndSave(output, name, arg) + if err == nil { + return + } + idx := strings.LastIndex(name, "/") + relname := name + if idx >= 0 { + relname = name[idx+1:] + } + relname = "./" + relname + err = TryRunAndSave(output, relname, arg) + if err != nil { + log.Fatal(err) + } +} + +const MAXPROMPTRETRY = 3 + +func PromptUser(prompt string, opts []string) (match string, err error) { + for i := 1; i < MAXPROMPTRETRY; i++ { + fmt.Printf("%s. (%s) Default:%s\n", prompt, + strings.Join(opts, "/"), opts[0]) + var usrInput string + fmt.Scanln(&usrInput) + + // Check for default entry + if usrInput == "" { + match = opts[0] + return + } + + for _, opt := range opts { + if opt == usrInput { + match = opt + return + } + } + } + err = errors.New("max retries exceeded") + fmt.Fprintln(os.Stderr, "ERROR: max retries exceeded") + return +} + +func MakeHDALogs(outDir string, cardName string) { + SysDir := "/sys/class/sound/" + cardName + "/" + files, _ := ioutil.ReadDir(SysDir) + for _, f := range files { + if (strings.HasPrefix(f.Name(), "hw") || strings.HasPrefix(f.Name(), "hdaudio")) && f.IsDir() { + in, err := os.Open(SysDir + f.Name() + "/init_pin_configs") + defer in.Close() + if err != nil { + log.Fatal(err) + } + out, err := os.Create(outDir + "/pin_" + strings.Replace(f.Name(), "hdaudio", "hw", -1)) + if err != nil { + log.Fatal(err) + } + defer out.Close() + io.Copy(out, in) + } + } + + ProcDir := "/proc/asound/" + cardName + "/" + files, _ = ioutil.ReadDir(ProcDir) + for _, f := range files { + if strings.HasPrefix(f.Name(), "codec#") && !f.IsDir() { + in, err := os.Open(ProcDir + f.Name()) + defer in.Close() + if err != nil { + log.Fatal(err) + } + out, err := os.Create(outDir + "/" + f.Name()) + if err != nil { + log.Fatal(err) + } + defer out.Close() + io.Copy(out, in) + } + } +} + +func MakeLogs(outDir string) { + os.MkdirAll(outDir, 0700) + RunAndSave(outDir+"/lspci.log", "lspci", "-nnvvvxxxx") + RunAndSave(outDir+"/dmidecode.log", "dmidecode") + RunAndSave(outDir+"/acpidump.log", "acpidump") + + inteltoolArgs := "-a" + opt, err := PromptUser("WARNING: The following tool MAY cause your system to hang when it attempts "+ + "to probe for graphics registers. Having the graphics registers will help create a better port. "+ + "Should autoport probe these registers?", + []string{"y", "yes", "n", "no"}) + + // Continue even if there is an error + + switch opt { + case "y", "yes": + inteltoolArgs += "f" + } + + RunAndSave(outDir+"/inteltool.log", "../inteltool/inteltool", inteltoolArgs) + RunAndSave(outDir+"/ectool.log", "../ectool/ectool", "-pd") + RunAndSave(outDir+"/superiotool.log", "../superiotool/superiotool", "-ade") + + SysSound := "/sys/class/sound/" + card := "" + cards, _ := ioutil.ReadDir(SysSound) + for _, f := range cards { + if strings.HasPrefix(f.Name(), "card") { + cid, err := ioutil.ReadFile(SysSound + f.Name() + "/id") + if err == nil && bytes.Equal(cid, []byte("PCH\n")) { + fmt.Fprintln(os.Stderr, "PCH sound card is", f.Name()) + card = f.Name() + } + } + } + + if card != "" { + MakeHDALogs(outDir, card) + } else { + fmt.Fprintln(os.Stderr, "HDAudio not found on PCH.") + } + + for _, fname := range []string{"cpuinfo", "ioports"} { + in, err := os.Open("/proc/" + fname) + defer in.Close() + if err != nil { + log.Fatal(err) + } + out, err := os.Create(outDir + "/" + fname + ".log") + if err != nil { + log.Fatal(err) + } + defer out.Close() + io.Copy(out, in) + } + + out, err := os.Create(outDir + "/input_bustypes.log") + if err != nil { + log.Fatal(err) + } + defer out.Close() + + ClassInputDir := "/sys/class/input/" + files, _ := ioutil.ReadDir(ClassInputDir) + for _, f := range files { + if strings.HasPrefix(f.Name(), "input") && !f.Mode().IsRegular() { /* Allow both dirs and symlinks. */ + in, err := os.Open(ClassInputDir + f.Name() + "/id/bustype") + defer in.Close() + if err != nil { + log.Fatal(err) + } + io.Copy(out, in) + } + } +} diff --git a/util/autoport/log_reader.go b/util/autoport/log_reader.go new file mode 100644 index 00000000..b0518d25 --- /dev/null +++ b/util/autoport/log_reader.go @@ -0,0 +1,417 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "log" + "os" + "regexp" + "strconv" + "strings" +) + +type LogDevReader struct { + InputDirectory string + ACPITables map[string][]byte + EC []byte +} + +func isXDigit(x uint8) bool { + if x >= '0' && x <= '9' { + return true + } + if x >= 'a' && x <= 'f' { + return true + } + if x >= 'A' && x <= 'F' { + return true + } + return false +} + +type HexLine struct { + length uint + values [16]byte + start uint +} + +func (l *LogDevReader) ReadHexLine(line string) (hex HexLine) { + hex.start = 0 + line = strings.Trim(line, " ") + fmt.Sscanf(line, "%x:", &hex.start) + ll, _ := fmt.Sscanf(line, "%x: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x", &hex.start, + &hex.values[0], &hex.values[1], &hex.values[2], + &hex.values[3], &hex.values[4], &hex.values[5], + &hex.values[6], &hex.values[7], &hex.values[8], + &hex.values[9], &hex.values[10], &hex.values[11], + &hex.values[12], &hex.values[13], &hex.values[14], + &hex.values[15]) + hex.length = uint(ll - 1) + return +} + +func (l *LogDevReader) AssignHexLine(inp string, target []byte) []byte { + hex := l.ReadHexLine(inp) + if hex.start+hex.length > uint(len(target)) { + target = target[0 : hex.start+hex.length] + } + copy(target[hex.start:hex.start+hex.length], hex.values[0:hex.length]) + return target +} + +func (l *LogDevReader) GetEC() []byte { + if l.EC != nil { + return l.EC + } + l.EC = make([]byte, 0x100, 0x100) + + file, err := os.Open(l.InputDirectory + "/ectool.log") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + + for scanner.Scan() { + line := scanner.Text() + if len(line) > 7 && isXDigit(line[0]) && isXDigit(line[1]) && line[2] == ':' { + l.EC = l.AssignHexLine(line, l.EC) + } + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + return l.EC +} + +func (l *LogDevReader) GetACPI() (Tables map[string][]byte) { + if l.ACPITables != nil { + return l.ACPITables + } + l.ACPITables = Tables + + file, err := os.Open(l.InputDirectory + "/acpidump.log") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + + Tables = map[string][]byte{} + + curTable := "" + for scanner.Scan() { + line := scanner.Text() + /* Only supports ACPI tables up to 0x100000 in size, FIXME if needed */ + is_hexline, _ := regexp.MatchString(" *[0-9A-Fa-f]{4,5}: ", line) + switch { + case len(line) >= 6 && line[5] == '@': + curTable = line[0:4] + Tables[curTable] = make([]byte, 0, 0x100000) + case is_hexline: + Tables[curTable] = l.AssignHexLine(line, Tables[curTable]) + } + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + return +} + +func (l *LogDevReader) GetPCIList() (PCIList []PCIDevData) { + file, err := os.Open(l.InputDirectory + "/lspci.log") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + + PCIList = []PCIDevData{} + + for scanner.Scan() { + line := scanner.Text() + switch { + case !(len(line) < 7 || !isXDigit(line[0]) || !isXDigit(line[1]) || line[2] != ':' || !isXDigit(line[3]) || !isXDigit(line[4]) || line[5] != '.' || !isXDigit(line[6])): + cur := PCIDevData{} + fmt.Sscanf(line, "%x:%x.%x", &cur.Bus, &cur.Dev, &cur.Func) + lc := strings.LastIndex(line, ":") + li := strings.LastIndex(line[0:lc], "[") + if li < 0 { + continue + } + ven := 0 + dev := 0 + fmt.Sscanf(line[li+1:], "%x:%x", &ven, &dev) + cur.PCIDevID = uint16(dev) + cur.PCIVenID = uint16(ven) + cur.ConfigDump = make([]byte, 0x100, 0x1000) + PCIList = append(PCIList, cur) + case len(line) > 7 && isXDigit(line[0]) && line[1] == '0' && line[2] == ':': + start := 0 + fmt.Sscanf(line, "%x:", &start) + cur := &PCIList[len(PCIList)-1] + cur.ConfigDump = l.AssignHexLine(line, cur.ConfigDump) + } + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + return +} + +func (l *LogDevReader) GetInteltool() (ret InteltoolData) { + file, err := os.Open(l.InputDirectory + "/inteltool.log") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + paragraph := "" + ret.GPIO = map[uint16]uint32{} + ret.RCBA = map[uint16]uint32{} + ret.IOBP = map[uint32]uint32{} + ret.IGD = map[uint32]uint32{} + ret.MCHBAR = map[uint16]uint32{} + ret.PMBASE = map[uint16]uint32{} + for scanner.Scan() { + line := scanner.Text() + switch { + case len(line) > 7 && line[0] == '0' && line[1] == 'x' && line[6] == ':' && paragraph == "RCBA": + addr, value := 0, 0 + fmt.Sscanf(line, "0x%x: 0x%x", &addr, &value) + ret.RCBA[uint16(addr)] = uint32(value) + case len(line) > 11 && line[0] == '0' && line[1] == 'x' && line[10] == ':' && paragraph == "IOBP": + addr, value := 0, 0 + fmt.Sscanf(line, "0x%x: 0x%x", &addr, &value) + ret.IOBP[uint32(addr)] = uint32(value) + case len(line) > 9 && line[0] == '0' && line[1] == 'x' && line[8] == ':' && paragraph == "IGD": + addr, value := 0, 0 + fmt.Sscanf(line, "0x%x: 0x%x", &addr, &value) + ret.IGD[uint32(addr)] = uint32(value) + case len(line) > 7 && line[0] == '0' && line[1] == 'x' && line[6] == ':' && paragraph == "MCHBAR": + addr, value := 0, 0 + fmt.Sscanf(line, "0x%x: 0x%x", &addr, &value) + ret.MCHBAR[uint16(addr)] = uint32(value) + case strings.Contains(line, "DEFAULT"): + continue + case strings.Contains(line, "DIFF"): + continue + case strings.HasPrefix(line, "gpiobase"): + addr, value := 0, 0 + fmt.Sscanf(line, "gpiobase+0x%x: 0x%x", &addr, &value) + ret.GPIO[uint16(addr)] = uint32(value) + case strings.HasPrefix(line, "pmbase"): + addr, value := 0, 0 + fmt.Sscanf(line, "pmbase+0x%x: 0x%x", &addr, &value) + ret.PMBASE[uint16(addr)] = uint32(value) + case strings.HasPrefix(line, "============="): + paragraph = strings.Trim(line, "= ") + } + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + return +} + +func (l *LogDevReader) GetDMI() (ret DMIData) { + file, err := os.Open(l.InputDirectory + "/dmidecode.log") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + paragraph := "" + for scanner.Scan() { + line := scanner.Text() + if !strings.HasPrefix(line, "\t") { + paragraph = strings.TrimSpace(line) + continue + } + idx := strings.Index(line, ":") + if idx < 0 { + continue + } + name := strings.TrimSpace(line[0:idx]) + value := strings.TrimSpace(line[idx+1:]) + switch paragraph + ":" + name { + case "System Information:Manufacturer": + ret.Vendor = value + case "System Information:Product Name": + ret.Model = value + case "System Information:Version": + ret.Version = value + case "Chassis Information:Type": + ret.IsLaptop = (value == "Notebook" || value == "Laptop") + } + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + return +} + +func (l *LogDevReader) GetAzaliaCodecs() (ret []AzaliaCodec) { + cardno := -1 + for i := 0; i < 10; i++ { + pin, err := os.Open(l.InputDirectory + "/pin_hwC" + strconv.Itoa(i) + "D0") + if err == nil { + pin.Close() + cardno = i + break + } + } + if cardno == -1 { + return + } + for codecno := 0; codecno < 10; codecno++ { + cur := AzaliaCodec{CodecNo: codecno, PinConfig: map[int]uint32{}} + codec, err := os.Open(l.InputDirectory + "/codec#" + strconv.Itoa(codecno)) + if err != nil { + continue + } + defer codec.Close() + pin, err := os.Open(l.InputDirectory + "/pin_hwC" + strconv.Itoa(cardno) + + "D" + strconv.Itoa(codecno)) + if err != nil { + continue + } + defer pin.Close() + + scanner := bufio.NewScanner(codec) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "Codec:") { + fmt.Sscanf(line, "Codec: %s", &cur.Name) + continue + } + if strings.HasPrefix(line, "Vendor Id:") { + fmt.Sscanf(line, "Vendor Id: 0x%x", &cur.VendorID) + continue + } + if strings.HasPrefix(line, "Subsystem Id:") { + fmt.Sscanf(line, "Subsystem Id: 0x%x", &cur.SubsystemID) + continue + } + } + + scanner = bufio.NewScanner(pin) + for scanner.Scan() { + line := scanner.Text() + addr := 0 + val := uint32(0) + fmt.Sscanf(line, "0x%x 0x%x", &addr, &val) + cur.PinConfig[addr] = val + } + ret = append(ret, cur) + } + return +} + +func (l *LogDevReader) GetIOPorts() []IOPorts { + file, err := os.Open(l.InputDirectory + "/ioports.log") + if err != nil { + log.Fatal(err) + } + defer file.Close() + scanner := bufio.NewScanner(file) + ret := make([]IOPorts, 0, 100) + for scanner.Scan() { + line := scanner.Text() + el := IOPorts{} + fmt.Sscanf(line, " %x-%x : %s", &el.Start, &el.End, &el.Usage) + ret = append(ret, el) + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + return ret + +} + +func (l *LogDevReader) GetCPUModel() (ret []uint32) { + file, err := os.Open(l.InputDirectory + "/cpuinfo.log") + if err != nil { + log.Fatal(err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + ret = make([]uint32, 0, 100) + proc := 0 + for scanner.Scan() { + line := scanner.Text() + sep := strings.Index(line, ":") + if sep < 0 { + continue + } + key := strings.TrimSpace(line[0:sep]) + val := strings.TrimSpace(line[sep+1:]) + + if key == "processor" { + proc, _ := strconv.Atoi(val) + if len(ret) <= proc { + ret = ret[0 : proc+1] + } + continue + } + if key == "cpu family" { + family, _ := strconv.Atoi(val) + ret[proc] |= uint32(((family & 0xf) << 8) | ((family & 0xff0) << 16)) + } + if key == "model" { + model, _ := strconv.Atoi(val) + ret[proc] |= uint32(((model & 0xf) << 4) | ((model & 0xf0) << 12)) + } + if key == "stepping" { + stepping, _ := strconv.Atoi(val) + ret[proc] |= uint32(stepping & 0xf) + } + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + return +} + +func (l *LogDevReader) HasPS2() bool { + file, err := os.Open(l.InputDirectory + "/input_bustypes.log") + if err != nil { + log.Fatal(err) + } + defer file.Close() + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.Index(line, "0011") >= 0 { + return true + } + } + return false +} + +var FlagLogInput = flag.String("input_log", ".", "Input log directory") +var FlagLogMkLogs = flag.Bool("make_logs", false, "Dump logs") + +func MakeLogReader() *LogDevReader { + if *FlagLogMkLogs { + MakeLogs(*FlagLogInput) + } + return &LogDevReader{InputDirectory: *FlagLogInput} +} diff --git a/util/autoport/lynxpoint.go b/util/autoport/lynxpoint.go new file mode 100644 index 00000000..98a1ca82 --- /dev/null +++ b/util/autoport/lynxpoint.go @@ -0,0 +1,490 @@ +package main + +import "fmt" + +type LPVariant int + +const ( + LYNX_POINT_MOBILE LPVariant = iota + LYNX_POINT_DESKTOP + LYNX_POINT_SERVER + LYNX_POINT_ULT +) + +type lynxpoint struct { + variant LPVariant + node *DevTreeNode +} + +func lpPchGetFlashSize(ctx Context) { + inteltool := ctx.InfoSource.GetInteltool() + /* In LP PCH, Boot BIOS Straps field in GCS has only one bit. */ + switch (inteltool.RCBA[0x3410] >> 10) & 1 { + case 0: + ROMProtocol = "SPI" + highflkb := uint32(0) + for reg := uint16(0); reg < 5; reg++ { + fl := (inteltool.RCBA[0x3854+4*reg] >> 16) & 0x1fff + flkb := (fl + 1) << 2 + if flkb > highflkb { + highflkb = flkb + } + } + ROMSizeKB = int(highflkb) + FlashROMSupport = "y" + } +} + +func (b lynxpoint) GetGPIOHeader() string { + return "southbridge/intel/lynxpoint/pch.h" +} + +func (b lynxpoint) EnableGPE(in int) { + if b.variant != LYNX_POINT_ULT { + b.node.Registers[fmt.Sprintf("gpi%d_routing", in)] = "2" + } +} + +func (b lynxpoint) EncodeGPE(in int) int { + return in + 0x10 +} + +func (b lynxpoint) DecodeGPE(in int) int { + return in - 0x10 +} + +func (b lynxpoint) NeedRouteGPIOManually() { + b.node.Comment += ", FIXME: set gpiX_routing for EC support" +} + +func GetLptDesktopEHCISetting(loc_param uint32, txamp uint32) (string, int) { + var port_pos string + var port_length int + + if loc_param == 4 { + port_pos = "USB_PORT_BACK_PANEL" + if txamp <= 2 { + port_length = 0x40 + } else if txamp >= 4 { + port_length = 0x140 + } else { + port_length = 0x110 + } + } else { + port_pos = "USB_PORT_FLEX" + port_length = 0x40 + } + return port_pos, port_length +} + +func GetLptMobileEHCISetting(loc_param uint32, txamp uint32) (string, int) { + var port_pos string + var port_length int + + if loc_param == 4 { + port_pos = "USB_PORT_DOCK" + if txamp <= 1 { + port_length = 0x40 + } else { + port_length = 0x80 + } + } else if loc_param == 6 { + /* not internal, not dock, port_length >= 0x70 */ + port_pos = "USB_PORT_BACK_PANEL" + if txamp <= 2 { + port_length = 0x80 + } else { + port_length = 0x110 + } + } else { + port_pos = "USB_PORT_BACK_PANEL" + port_length = 0x40 + } + return port_pos, port_length +} + +func GetLptLPEHCISetting(loc_param uint32, txamp uint32) (string, int) { + var port_pos string + var port_length int + + if loc_param == 6 { + /* back panel or mini pcie, length >= 0x70 */ + port_pos = "USB_PORT_MINI_PCIE" + if txamp <= 2 { + port_length = 0x80 + } else { + port_length = 0x110 + } + } else if loc_param == 4 { + port_pos = "USB_PORT_DOCK" + if txamp <= 1 { + port_length = 0x40 + } else { + port_length = 0x80 + } + } else { + port_pos = "USB_PORT_BACK_PANEL" + port_length = 0x40 + } + return port_pos, port_length +} + +func (b lynxpoint) Scan(ctx Context, addr PCIDevData) { + + SouthBridge = &b + + inteltool := ctx.InfoSource.GetInteltool() + + isULT := (b.variant == LYNX_POINT_ULT) + + if isULT { + Lynxpoint_LP_GPIO(ctx, inteltool) + } else { + GPIO(ctx, inteltool) + } + + KconfigBool["SOUTHBRIDGE_INTEL_LYNXPOINT"] = true + if isULT { + KconfigBool["INTEL_LYNXPOINT_LP"] = true + } + KconfigBool["SERIRQ_CONTINUOUS_MODE"] = true + if isULT { + KconfigInt["USBDEBUG_HCD_INDEX"] = 1 + } else { + KconfigInt["USBDEBUG_HCD_INDEX"] = 2 + KconfigComment["USBDEBUG_HCD_INDEX"] = "FIXME: check this" + } + + if isULT { + lpPchGetFlashSize(ctx) + } else { + ich9GetFlashSize(ctx) + } + + FADT := ctx.InfoSource.GetACPI()["FACP"] + + sp0dtle_data := (inteltool.IOBP[0xea002750] >> 24) & 0xf + sp0dtle_edge := (inteltool.IOBP[0xea002754] >> 16) & 0xf + sp1dtle_data := (inteltool.IOBP[0xea002550] >> 24) & 0xf + sp1dtle_edge := (inteltool.IOBP[0xea002554] >> 16) & 0xf + + if sp0dtle_data != sp0dtle_edge { + fmt.Printf("Different SATA Gen3 port0 DTLE data and edge values are used.\n") + } + + if sp1dtle_data != sp1dtle_edge { + fmt.Printf("Different SATA Gen3 port1 DTLE data and edge values are used.\n") + } + + cur := DevTreeNode{ + Chip: "southbridge/intel/lynxpoint", + Comment: "Intel Series 8 Lynx Point PCH", + + /* alt_gp_smi_en is not generated because coreboot doesn't use SMI like OEM firmware */ + Registers: map[string]string{ + "gen1_dec": FormatHexLE32(PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 0}].ConfigDump[0x84:0x88]), + "gen2_dec": FormatHexLE32(PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 0}].ConfigDump[0x88:0x8c]), + "gen3_dec": FormatHexLE32(PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 0}].ConfigDump[0x8c:0x90]), + "gen4_dec": FormatHexLE32(PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 0}].ConfigDump[0x90:0x94]), + "sata_port_map": fmt.Sprintf("0x%x", PCIMap[PCIAddr{Bus: 0, Dev: 0x1f, Func: 2}].ConfigDump[0x92]&0x3f), + "docking_supported": (FormatBool((FADT[113] & (1 << 1)) != 0)), + "sata_port0_gen3_dtle": fmt.Sprintf("0x%x", sp0dtle_data), + "sata_port1_gen3_dtle": fmt.Sprintf("0x%x", sp1dtle_data), + }, + PCISlots: []PCISlot{ + PCISlot{PCIAddr: PCIAddr{Dev: 0x13, Func: 0}, writeEmpty: isULT, additionalComment: "Smart Sound Audio DSP"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x14, Func: 0}, writeEmpty: true, additionalComment: "xHCI Controller"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x15, Func: 0}, writeEmpty: isULT, additionalComment: "Serial I/O DMA"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x15, Func: 1}, writeEmpty: isULT, additionalComment: "I2C0"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x15, Func: 2}, writeEmpty: isULT, additionalComment: "I2C1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x15, Func: 3}, writeEmpty: isULT, additionalComment: "GSPI0"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x15, Func: 4}, writeEmpty: isULT, additionalComment: "GSPI1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x15, Func: 5}, writeEmpty: isULT, additionalComment: "UART0"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x15, Func: 6}, writeEmpty: isULT, additionalComment: "UART1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x16, Func: 0}, writeEmpty: true, additionalComment: "Management Engine Interface 1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x16, Func: 1}, writeEmpty: true, additionalComment: "Management Engine Interface 2"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x16, Func: 2}, writeEmpty: true, additionalComment: "Management Engine IDE-R"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x16, Func: 3}, writeEmpty: true, additionalComment: "Management Engine KT"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x17, Func: 0}, writeEmpty: isULT, additionalComment: "SDIO"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x19, Func: 0}, writeEmpty: true, additionalComment: "Intel Gigabit Ethernet"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1a, Func: 0}, writeEmpty: !isULT, additionalComment: "USB2 EHCI #2"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1b, Func: 0}, writeEmpty: true, additionalComment: "High Definition Audio"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 0}, writeEmpty: true, additionalComment: "PCIe Port #1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 1}, writeEmpty: true, additionalComment: "PCIe Port #2"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 2}, writeEmpty: true, additionalComment: "PCIe Port #3"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 3}, writeEmpty: true, additionalComment: "PCIe Port #4"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 4}, writeEmpty: true, additionalComment: "PCIe Port #5"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 5}, writeEmpty: true, additionalComment: "PCIe Port #6"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 6}, writeEmpty: !isULT, additionalComment: "PCIe Port #7"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 7}, writeEmpty: !isULT, additionalComment: "PCIe Port #8"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1d, Func: 0}, writeEmpty: true, additionalComment: "USB2 EHCI #1"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 0}, writeEmpty: true, additionalComment: "LPC bridge"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 2}, writeEmpty: true, additionalComment: "SATA Controller (AHCI)"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 3}, writeEmpty: true, additionalComment: "SMBus"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 5}, writeEmpty: !isULT, additionalComment: "SATA Controller (Legacy)"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 6}, writeEmpty: true, additionalComment: "Thermal"}, + }, + } + + if isULT { + cur.Registers["gpe0_en_1"] = fmt.Sprintf("0x%x", inteltool.PMBASE[0x90]) + cur.Registers["gpe0_en_2"] = fmt.Sprintf("0x%x", inteltool.PMBASE[0x94]) + cur.Registers["gpe0_en_3"] = fmt.Sprintf("0x%x", inteltool.PMBASE[0x98]) + cur.Registers["gpe0_en_4"] = fmt.Sprintf("0x%x", inteltool.PMBASE[0x9c]) + } else { + cur.Registers["gpe0_en_1"] = fmt.Sprintf("0x%x", inteltool.PMBASE[0x28]) + cur.Registers["gpe0_en_2"] = fmt.Sprintf("0x%x", inteltool.PMBASE[0x2c]) + } + + b.node = &cur + + PutPCIChip(addr, cur) + PutPCIDevParent(addr, "", "lpc") + + DSDTIncludes = append(DSDTIncludes, DSDTInclude{ + File: "southbridge/intel/common/acpi/platform.asl", + }) + DSDTIncludes = append(DSDTIncludes, DSDTInclude{ + File: "southbridge/intel/lynxpoint/acpi/globalnvs.asl", + Comment: "global NVS and variables", + }) + DSDTIncludes = append(DSDTIncludes, DSDTInclude{ + File: "southbridge/intel/common/acpi/sleepstates.asl", + }) + DSDTPCI0Includes = append(DSDTPCI0Includes, DSDTInclude{ + File: "southbridge/intel/lynxpoint/acpi/pch.asl", + }) + + AddBootBlockFile("bootblock.c", "") + bb := Create(ctx, "bootblock.c") + defer bb.Close() + Add_gpl(bb) + bb.WriteString(`#include + +/* FIXME: remove this if not needed */ +void mainboard_config_superio(void) +{ +} +`) + + sb := Create(ctx, "romstage.c") + defer sb.Close() + Add_gpl(sb) + sb.WriteString(`#include +#include +#include +#include + +void mainboard_config_rcba(void) +{ +} + +/* FIXME: called after romstage_common, remove it if not used */ +void mb_late_romstage_setup(void) +{ +} + +void mb_get_spd_map(struct spd_info *spdi) +{ + /* FIXME: check this */ + spdi->addresses[0] = 0x50; + spdi->addresses[1] = 0x51; + spdi->addresses[2] = 0x52; + spdi->addresses[3] = 0x53; +} + +const struct usb2_port_config mainboard_usb2_ports[MAX_USB2_PORTS] = { + /* FIXME: Length and Location are computed from IOBP values, may be inaccurate */ + /* Length, Enable, OCn#, Location */ +`) + + pdo1 := PCIMap[PCIAddr{Bus: 0, Dev: 0x1d, Func: 0}].ConfigDump[0x64] + ocmap1 := PCIMap[PCIAddr{Bus: 0, Dev: 0x1d, Func: 0}].ConfigDump[0x74:0x78] + + var pdo2 uint8 + var ocmap2 []uint8 + var nPorts uint + if isULT { + nPorts = 8 + } else { + pdo2 = PCIMap[PCIAddr{Bus: 0, Dev: 0x1a, Func: 0}].ConfigDump[0x64] + ocmap2 = PCIMap[PCIAddr{Bus: 0, Dev: 0x1a, Func: 0}].ConfigDump[0x74:0x78] + nPorts = 14 + } + + xusb2pr := GetLE16(PCIMap[PCIAddr{Bus: 0, Dev: 0x14, Func: 0}].ConfigDump[0xd0:0xd4]) + + for port := uint(0); port < nPorts; port++ { + var port_oc int = -1 + var port_pos string + var port_disable uint8 + + if port < 8 { + port_disable = ((pdo1 >> port) & (uint8(xusb2pr>>port) ^ 1)) & 1 + for oc := 0; oc < 4; oc++ { + if (ocmap1[oc] & (1 << port)) != 0 { + port_oc = oc + break + } + } + } else { + port_disable = ((pdo2 >> (port - 8)) & (uint8(xusb2pr>>port) ^ 1)) & 1 + for oc := 0; oc < 4; oc++ { + if (ocmap2[oc] & (1 << (port - 8))) != 0 { + port_oc = oc + 4 + break + } + } + } + + /* get USB2 port length and location from IOBP */ + port_iobp := inteltool.IOBP[0xe5004100+uint32(port)*0x100] + loc_param := (port_iobp >> 8) & 7 + txamp := (port_iobp >> 11) & 7 + var port_length int + + if isULT { + port_pos, port_length = GetLptLPEHCISetting(loc_param, txamp) + } else if b.variant == LYNX_POINT_MOBILE { + port_pos, port_length = GetLptMobileEHCISetting(loc_param, txamp) + } else { /* desktop or server */ + port_pos, port_length = GetLptDesktopEHCISetting(loc_param, txamp) + } + + if port_disable == 1 { + port_pos = "USB_PORT_SKIP" + } + + if port_oc == -1 { + fmt.Fprintf(sb, "\t{ 0x%04x, %d, USB_OC_PIN_SKIP, %s },\n", + port_length, (port_disable ^ 1), port_pos) + } else { + fmt.Fprintf(sb, "\t{ 0x%04x, %d, %d, %s },\n", + port_length, (port_disable ^ 1), port_oc, port_pos) + } + } + + sb.WriteString(`}; + +const struct usb3_port_config mainboard_usb3_ports[MAX_USB3_PORTS] = { +`) + + xpdo := PCIMap[PCIAddr{Bus: 0, Dev: 0x14, Func: 0}].ConfigDump[0xe8] + u3ocm := PCIMap[PCIAddr{Bus: 0, Dev: 0x14, Func: 0}].ConfigDump[0xc8:0xd0] + + if !isULT { + nPorts = 6 + } else { + nPorts = 4 + } + + for port := uint(0); port < nPorts; port++ { + var port_oc int = -1 + port_disable := (xpdo >> port) & 1 + for oc := 0; oc < 8; oc++ { + if (u3ocm[oc] & (1 << port)) != 0 { + port_oc = oc + break + } + } + if port_oc == -1 { + fmt.Fprintf(sb, "\t{ %d, USB_OC_PIN_SKIP },\n", + (port_disable ^ 1)) + } else { + fmt.Fprintf(sb, "\t{ %d, %d },\n", + (port_disable ^ 1), port_oc) + } + } + + sb.WriteString(`}; +`) + +} + +func init() { + for _, id := range []uint16{ + 0x8c41, 0x8c49, 0x8c4b, 0x8c4f, + } { + RegisterPCI(0x8086, uint16(id), lynxpoint{variant: LYNX_POINT_MOBILE}) + } + + for _, id := range []uint16{ + 0x8c42, 0x8c44, 0x8c46, 0x8c4a, + 0x8c4c, 0x8c4e, 0x8c50, 0x8c5c, + } { + RegisterPCI(0x8086, uint16(id), lynxpoint{variant: LYNX_POINT_DESKTOP}) + } + + for _, id := range []uint16{ + 0x8c52, 0x8c54, 0x8c56, + } { + RegisterPCI(0x8086, uint16(id), lynxpoint{variant: LYNX_POINT_SERVER}) + } + + for _, id := range []uint16{ + 0x9c41, 0x9c43, 0x9c45, + } { + RegisterPCI(0x8086, uint16(id), lynxpoint{variant: LYNX_POINT_ULT}) + } + + /* PCIe bridge */ + for _, id := range []uint16{ + 0x8c10, 0x8c12, 0x8c14, 0x8c16, 0x8c18, 0x8c1a, 0x8c1c, 0x8c1e, + 0x9c10, 0x9c12, 0x9c14, 0x9c16, 0x9c18, 0x9c1a, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } + + /* SMBus controller */ + RegisterPCI(0x8086, 0x8c22, GenericPCI{MissingParent: "smbus"}) + RegisterPCI(0x8086, 0x9c22, GenericPCI{MissingParent: "smbus"}) + + /* SATA */ + for _, id := range []uint16{ + 0x8c00, 0x8c02, 0x8c04, 0x8c06, 0x8c08, 0x8c0e, + 0x8c01, 0x8c03, 0x8c05, 0x8c07, 0x8c09, 0x8c0f, + 0x9c03, 0x9c05, 0x9c07, 0x9c0f, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } + + /* EHCI */ + for _, id := range []uint16{ + 0x9c26, 0x8c26, 0x8c2d, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } + + /* XHCI */ + RegisterPCI(0x8086, 0x8c31, GenericPCI{}) + RegisterPCI(0x8086, 0x9c31, GenericPCI{}) + + /* ME and children */ + for _, id := range []uint16{ + 0x8c3a, 0x8c3b, 0x8c3c, 0x8c3d, + 0x9c3a, 0x9c3b, 0x9c3c, 0x9c3d, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } + + /* Ethernet */ + RegisterPCI(0x8086, 0x8c33, GenericPCI{}) + + /* Thermal */ + RegisterPCI(0x8086, 0x8c24, GenericPCI{}) + RegisterPCI(0x8086, 0x9c24, GenericPCI{}) + + /* LAN Controller on LP PCH (if EEPROM has 0x0000/0xffff in DID) */ + RegisterPCI(0x8086, 0x155a, GenericPCI{}) + + /* SDIO */ + RegisterPCI(0x8086, 0x9c35, GenericPCI{}) + + /* Smart Sound Technology Controller */ + RegisterPCI(0x8086, 0x9c36, GenericPCI{}) + + /* Serial I/O */ + for id := uint16(0x9c60); id <= 0x9c66; id++ { + RegisterPCI(0x8086, id, GenericPCI{}) + } +} diff --git a/util/autoport/lynxpoint_lp_gpio.go b/util/autoport/lynxpoint_lp_gpio.go new file mode 100644 index 00000000..fbc1686a --- /dev/null +++ b/util/autoport/lynxpoint_lp_gpio.go @@ -0,0 +1,252 @@ +package main + +import ( + "fmt" + "os" + "strings" +) + +const ( + PIRQI = 0 + PIRQJ = 1 + PIRQK = 2 + PIRQL = 3 + PIRQM = 4 + PIRQN = 5 + PIRQO = 6 + PIRQP = 7 + PIRQQ = 8 + PIRQR = 9 + PIRQS = 10 + PIRQT = 11 + PIRQU = 12 + PIRQV = 13 + PIRQW = 14 + PIRQX = 15 +) + +/* from sb/intel/lynxpoint/lp_gpio.c */ +func lp_gpio_to_pirq(gpioNum uint16) int { + switch gpioNum { + case 8: + return PIRQI + case 9: + return PIRQJ + case 10: + return PIRQK + case 13: + return PIRQL + case 14: + return PIRQM + case 45: + return PIRQN + case 46: + return PIRQO + case 47: + return PIRQP + case 48: + return PIRQQ + case 49: + return PIRQR + case 50: + return PIRQS + case 51: + return PIRQT + case 52: + return PIRQU + case 53: + return PIRQV + case 54: + return PIRQW + case 55: + return PIRQX + default: + return -1 + } +} + +func conf0str(conf0 uint32) string { + if (conf0 & 1) == 0 { + return "GPIO_MODE_NATIVE" + } else { + s := []string{"GPIO_MODE_GPIO"} + var gpio_output bool + if ((conf0 >> 2) & 1) == 1 { + s = append(s, "GPIO_DIR_INPUT") + gpio_output = false + } else { + s = append(s, "GPIO_DIR_OUTPUT") + gpio_output = true + } + if ((conf0 >> 3) & 1) == 1 { + s = append(s, "GPIO_INVERT") + } + if ((conf0 >> 4) & 1) == 1 { + s = append(s, "GPIO_IRQ_LEVEL") + } + if gpio_output { + if ((conf0 >> 31) & 1) == 1 { + s = append(s, "GPO_LEVEL_HIGH") + } else { + s = append(s, "GPO_LEVEL_LOW") + } + } + return strings.Join(s, " | ") + } +} + +func lpgpio_preset(conf0 uint32, owner uint32, route uint32, irqen uint32, pirq uint32) string { + if conf0 == 0xd { /* 0b1101: MODE_GPIO | INPUT | INVERT */ + if owner == 0 { /* OWNER_ACPI */ + if irqen == 0 && pirq == 0 { + if route == 0 { /* SCI */ + return "GPIO_ACPI_SCI" + } else { + return "GPIO_ACPI_SMI" + } + } + return "" + } else { /* OWNER_GPIO */ + if route == 0 && irqen == 0 && pirq != 0 { + return "GPIO_INPUT_INVERT" + } + return "" + } + } + + if conf0 == 0x5 && owner == 1 { /* 0b101: MODE_GPIO | INPUT, OWNER_GPIO */ + if route == 0 && irqen == 0 { + if pirq == 1 { + return "GPIO_PIRQ" + } else { + return "GPIO_INPUT" + } + } + return "" + } + + if owner == 1 && irqen == 1 { + if route == 0 && pirq == 0 { + if conf0 == 0x5 { /* 0b00101 */ + return "GPIO_IRQ_EDGE" + } + if conf0 == 0x15 { /* 0b10101 */ + return "GPIO_IRQ_LEVEL" + } + } + return "" + } + return "" +} + +func gpio_str(conf0 uint32, conf1 uint32, owner uint32, route uint32, irqen uint32, reset uint32, blink uint32, pirq uint32) string { + s := []string{} + s = append(s, fmt.Sprintf(".conf0 = %s", conf0str(conf0))) + if conf1 != 0 { + s = append(s, fmt.Sprintf(".conf1 = 0x%x", conf1)) + } + if owner != 0 { + s = append(s, ".owner = GPIO_OWNER_GPIO") + } + if route != 0 { + s = append(s, ".route = GPIO_ROUTE_SMI") + } + if irqen != 0 { + s = append(s, ".irqen = GPIO_IRQ_ENABLE") + } + if reset != 0 { + s = append(s, ".reset = GPIO_RESET_RSMRST") + } + if blink != 0 { + s = append(s, ".blink = GPO_BLINK") + } + if pirq != 0 { + s = append(s, ".pirq = GPIO_PIRQ_APIC_ROUTE") + } + return strings.Join(s, ", ") +} + +/* start addresses of GPIO registers */ +const ( + GPIO_OWN = 0x0 + GPIPIRQ2IOXAPIC = 0x10 + GPO_BLINK = 0x18 + GPI_ROUT = 0x30 + GP_RST_SEL = 0x60 + GPI_IE = 0x90 + GPnCONFIGA = 0x100 + GPnCONFIGB = 0x104 +) + +func PrintLPGPIO(gpio *os.File, inteltool InteltoolData) { + for gpioNum := uint16(0); gpioNum <= 94; gpioNum++ { + if gpioNum < 10 { + fmt.Fprintf(gpio, "\t[%d] = ", gpioNum) + } else { + fmt.Fprintf(gpio, "\t[%d] = ", gpioNum) + } + conf0 := inteltool.GPIO[GPnCONFIGA+gpioNum*8] + conf1 := inteltool.GPIO[GPnCONFIGB+gpioNum*8] + set := gpioNum / 32 + bit := gpioNum % 32 + /* owner only effective in GPIO mode */ + owner := (inteltool.GPIO[GPIO_OWN+set*4] >> bit) & 1 + route := (inteltool.GPIO[GPI_ROUT+set*4] >> bit) & 1 + irqen := (inteltool.GPIO[GPI_IE+set*4] >> bit) & 1 + reset := (inteltool.GPIO[GP_RST_SEL+set*4] >> bit) & 1 + var blink, pirq uint32 + /* blink only effective in GPIO output mode */ + if set == 0 { + blink = (inteltool.GPIO[GPO_BLINK] >> bit) & 1 + } else { + blink = 0 + } + irqset := lp_gpio_to_pirq(gpioNum) + if irqset >= 0 { + pirq = (inteltool.GPIO[GPIPIRQ2IOXAPIC] >> uint(irqset)) & 1 + } else { + pirq = 0 + } + + if (conf0 & 1) == 0 { + fmt.Fprintf(gpio, "LP_GPIO_NATIVE,\n") + } else if (conf0 & 4) == 0 { + /* configured as output */ + if ((conf0 >> 31) & 1) == 0 { + fmt.Fprintf(gpio, "LP_GPIO_OUT_LOW,\n") + } else { + fmt.Fprintf(gpio, "LP_GPIO_OUT_HIGH,\n") + } + } else if (conf1 & 4) != 0 { + /* configured as input and sensing disabled */ + fmt.Fprintf(gpio, "LP_GPIO_UNUSED,\n") + } else { + is_preset := false + if conf1 == 0 && reset == 0 && blink == 0 { + preset := lpgpio_preset(conf0, owner, route, irqen, pirq) + if preset != "" { + fmt.Fprintf(gpio, "LP_%s,\n", preset) + is_preset = true + } + } + if !is_preset { + fmt.Fprintf(gpio, "{ %s },\n", gpio_str(conf0, conf1, owner, route, irqen, reset, blink, pirq)) + } + } + } +} + +func Lynxpoint_LP_GPIO(ctx Context, inteltool InteltoolData) { + gpio := Create(ctx, "gpio.c") + defer gpio.Close() + + AddROMStageFile("gpio.c", "") + + Add_gpl(gpio) + gpio.WriteString(`#include + +const struct pch_lp_gpio_map mainboard_lp_gpio_map[] = { +`) + PrintLPGPIO(gpio, inteltool) + gpio.WriteString("\tLP_GPIO_END\n};\n") +} diff --git a/util/autoport/main.go b/util/autoport/main.go new file mode 100644 index 00000000..3a2f0617 --- /dev/null +++ b/util/autoport/main.go @@ -0,0 +1,915 @@ +/* This is just an experiment. Full automatic porting + is probably not possible but a lot can be automated. */ +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "os" + "sort" + "strings" +) + +type PCIAddr struct { + Bus int + Dev int + Func int +} + +type PCIDevData struct { + PCIAddr + PCIVenID uint16 + PCIDevID uint16 + ConfigDump []uint8 +} + +type PCIDevice interface { + Scan(ctx Context, addr PCIDevData) +} + +type InteltoolData struct { + GPIO map[uint16]uint32 + RCBA map[uint16]uint32 + IOBP map[uint32]uint32 + IGD map[uint32]uint32 + MCHBAR map[uint16]uint32 + PMBASE map[uint16]uint32 +} + +type DMIData struct { + Vendor string + Model string + Version string + IsLaptop bool +} + +type AzaliaCodec struct { + Name string + VendorID uint32 + SubsystemID uint32 + CodecNo int + PinConfig map[int]uint32 +} + +type DevReader interface { + GetPCIList() []PCIDevData + GetDMI() DMIData + GetInteltool() InteltoolData + GetAzaliaCodecs() []AzaliaCodec + GetACPI() map[string][]byte + GetCPUModel() []uint32 + GetEC() []byte + GetIOPorts() []IOPorts + HasPS2() bool +} + +type IOPorts struct { + Start uint16 + End uint16 + Usage string +} + +type SouthBridger interface { + GetGPIOHeader() string + EncodeGPE(int) int + DecodeGPE(int) int + EnableGPE(int) + NeedRouteGPIOManually() +} + +var SouthBridge SouthBridger +var BootBlockFiles map[string]string = map[string]string{} +var ROMStageFiles map[string]string = map[string]string{} +var RAMStageFiles map[string]string = map[string]string{} +var SMMFiles map[string]string = map[string]string{} +var MainboardInit string +var MainboardEnable string +var MainboardIncludes []string + +type Context struct { + MoboID string + KconfigName string + Vendor string + Model string + BaseDirectory string + InfoSource DevReader + SaneVendor string +} + +type IOAPICIRQ struct { + APICID int + IRQNO [4]int +} + +var IOAPICIRQs map[PCIAddr]IOAPICIRQ = map[PCIAddr]IOAPICIRQ{} +var KconfigBool map[string]bool = map[string]bool{} +var KconfigComment map[string]string = map[string]string{} +var KconfigString map[string]string = map[string]string{} +var KconfigHex map[string]uint32 = map[string]uint32{} +var KconfigInt map[string]int = map[string]int{} +var ROMSizeKB = 0 +var ROMProtocol = "" +var FlashROMSupport = "" + +func GetLE16(inp []byte) uint16 { + return uint16(inp[0]) | (uint16(inp[1]) << 8) +} + +func FormatHexLE16(inp []byte) string { + return fmt.Sprintf("0x%04x", GetLE16(inp)) +} + +func FormatHex32(u uint32) string { + return fmt.Sprintf("0x%08x", u) +} + +func FormatHex8(u uint8) string { + return fmt.Sprintf("0x%02x", u) +} + +func FormatInt32(u uint32) string { + return fmt.Sprintf("%d", u) +} + +func FormatHexLE32(d []uint8) string { + u := uint32(d[0]) | (uint32(d[1]) << 8) | (uint32(d[2]) << 16) | (uint32(d[3]) << 24) + return FormatHex32(u) +} + +func FormatBool(inp bool) string { + if inp { + return "1" + } else { + return "0" + } +} + +func sanitize(inp string) string { + result := strings.ToLower(inp) + result = strings.Replace(result, " ", "_", -1) + result = strings.Replace(result, ",", "_", -1) + result = strings.Replace(result, "-", "_", -1) + for strings.HasSuffix(result, ".") { + result = result[0 : len(result)-1] + } + return result +} + +func AddBootBlockFile(Name string, Condition string) { + BootBlockFiles[Name] = Condition +} + +func AddROMStageFile(Name string, Condition string) { + ROMStageFiles[Name] = Condition +} + +func AddRAMStageFile(Name string, Condition string) { + RAMStageFiles[Name] = Condition +} + +func AddSMMFile(Name string, Condition string) { + SMMFiles[Name] = Condition +} + +func IsIOPortUsedBy(ctx Context, port uint16, name string) bool { + for _, io := range ctx.InfoSource.GetIOPorts() { + if io.Start <= port && port <= io.End && io.Usage == name { + return true + } + } + return false +} + +var FlagOutDir = flag.String("coreboot_dir", ".", "Resulting coreboot directory") + +func writeMF(mf *os.File, files map[string]string, category string) { + keys := []string{} + for file, _ := range files { + keys = append(keys, file) + } + + sort.Strings(keys) + + for _, file := range keys { + condition := files[file] + if condition == "" { + fmt.Fprintf(mf, "%s-y += %s\n", category, file) + } else { + fmt.Fprintf(mf, "%s-$(%s) += %s\n", category, condition, file) + } + } +} + +func Create(ctx Context, name string) *os.File { + li := strings.LastIndex(name, "/") + if li > 0 { + os.MkdirAll(ctx.BaseDirectory+"/"+name[0:li], 0700) + } + mf, err := os.Create(ctx.BaseDirectory + "/" + name) + if err != nil { + log.Fatal(err) + } + return mf +} + +func Add_gpl(f *os.File) { + fmt.Fprintln(f, "/* SPDX-License-Identifier: GPL-2.0-only */") + fmt.Fprintln(f) +} + +func RestorePCI16Simple(f *os.File, pcidev PCIDevData, addr uint16) { + fmt.Fprintf(f, " pci_write_config16(PCI_DEV(%d, 0x%02x, %d), 0x%02x, 0x%02x%02x);\n", + pcidev.Bus, pcidev.Dev, pcidev.Func, addr, + pcidev.ConfigDump[addr+1], + pcidev.ConfigDump[addr]) +} + +func RestorePCI32Simple(f *os.File, pcidev PCIDevData, addr uint16) { + fmt.Fprintf(f, " pci_write_config32(PCI_DEV(%d, 0x%02x, %d), 0x%02x, 0x%02x%02x%02x%02x);\n", + pcidev.Bus, pcidev.Dev, pcidev.Func, addr, + pcidev.ConfigDump[addr+3], + pcidev.ConfigDump[addr+2], + pcidev.ConfigDump[addr+1], + pcidev.ConfigDump[addr]) +} + +func RestoreRCBA32(f *os.File, inteltool InteltoolData, addr uint16) { + fmt.Fprintf(f, "\tRCBA32(0x%04x) = 0x%08x;\n", addr, inteltool.RCBA[addr]) +} + +type PCISlot struct { + PCIAddr + alias string + additionalComment string + writeEmpty bool +} + +type DevTreeNode struct { + Bus int + Dev int + Func int + Disabled bool + Registers map[string]string + IOs map[uint16]uint16 + Children []DevTreeNode + PCISlots []PCISlot + PCIController bool + ChildPCIBus int + MissingParent string + SubVendor uint16 + SubSystem uint16 + Chip string + Ops string + Comment string +} + +var DevTree DevTreeNode +var MissingChildren map[string][]DevTreeNode = map[string][]DevTreeNode{} +var unmatchedPCIChips map[PCIAddr]DevTreeNode = map[PCIAddr]DevTreeNode{} +var unmatchedPCIDevices map[PCIAddr]DevTreeNode = map[PCIAddr]DevTreeNode{} + +func Offset(dt *os.File, offset int) { + for i := 0; i < offset; i++ { + fmt.Fprintf(dt, "\t") + } +} + +func MatchDev(dev *DevTreeNode) { + for idx := range dev.Children { + MatchDev(&dev.Children[idx]) + } + + for _, slot := range dev.PCISlots { + slotChip, ok := unmatchedPCIChips[slot.PCIAddr] + + if !ok { + continue + } + + if slot.additionalComment != "" && slotChip.Comment != "" { + slotChip.Comment = slot.additionalComment + " " + slotChip.Comment + } else { + slotChip.Comment = slot.additionalComment + slotChip.Comment + } + + delete(unmatchedPCIChips, slot.PCIAddr) + MatchDev(&slotChip) + dev.Children = append(dev.Children, slotChip) + } + + if dev.PCIController { + for slot, slotDev := range unmatchedPCIChips { + if slot.Bus == dev.ChildPCIBus { + delete(unmatchedPCIChips, slot) + MatchDev(&slotDev) + dev.Children = append(dev.Children, slotDev) + } + } + } + + for _, slot := range dev.PCISlots { + slotDev, ok := unmatchedPCIDevices[slot.PCIAddr] + if !ok { + if slot.writeEmpty { + dev.Children = append(dev.Children, + DevTreeNode{ + Registers: map[string]string{}, + Chip: "pci", + Bus: slot.Bus, + Dev: slot.Dev, + Func: slot.Func, + Comment: slot.additionalComment, + Disabled: true, + }, + ) + } + continue + } + + if slot.additionalComment != "" && slotDev.Comment != "" { + slotDev.Comment = slot.additionalComment + " " + slotDev.Comment + } else { + slotDev.Comment = slot.additionalComment + slotDev.Comment + } + + MatchDev(&slotDev) + dev.Children = append(dev.Children, slotDev) + delete(unmatchedPCIDevices, slot.PCIAddr) + } + + if dev.MissingParent != "" { + for _, child := range MissingChildren[dev.MissingParent] { + MatchDev(&child) + dev.Children = append(dev.Children, child) + } + delete(MissingChildren, dev.MissingParent) + } + + if dev.PCIController { + for slot, slotDev := range unmatchedPCIDevices { + if slot.Bus == dev.ChildPCIBus { + MatchDev(&slotDev) + dev.Children = append(dev.Children, slotDev) + delete(unmatchedPCIDevices, slot) + } + } + } +} + +func writeOn(dt *os.File, dev DevTreeNode) { + if dev.Disabled { + fmt.Fprintf(dt, "off") + } else { + fmt.Fprintf(dt, "on") + } +} + +func WriteDev(dt *os.File, offset int, alias string, dev DevTreeNode) { + Offset(dt, offset) + switch dev.Chip { + case "cpu_cluster", "lapic", "domain", "ioapic": + fmt.Fprintf(dt, "device %s 0x%x ", dev.Chip, dev.Dev) + writeOn(dt, dev) + case "pci", "pnp": + if alias != "" { + fmt.Fprintf(dt, "device ref %s ", alias) + } else { + fmt.Fprintf(dt, "device %s %02x.%x ", dev.Chip, dev.Dev, dev.Func) + } + writeOn(dt, dev) + case "i2c": + fmt.Fprintf(dt, "device %s %02x ", dev.Chip, dev.Dev) + writeOn(dt, dev) + default: + fmt.Fprintf(dt, "chip %s", dev.Chip) + } + if dev.Comment != "" { + fmt.Fprintf(dt, " # %s", dev.Comment) + } + fmt.Fprintf(dt, "\n") + if dev.Ops != "" { + Offset(dt, offset+1) + fmt.Fprintf(dt, "ops %s\n", dev.Ops) + } + if dev.Chip == "pci" && dev.SubSystem != 0 && dev.SubVendor != 0 { + Offset(dt, offset+1) + fmt.Fprintf(dt, "subsystemid 0x%04x 0x%04x\n", dev.SubVendor, dev.SubSystem) + } + + ioapic, ok := IOAPICIRQs[PCIAddr{Bus: dev.Bus, Dev: dev.Dev, Func: dev.Func}] + if dev.Chip == "pci" && ok { + for pin, irq := range ioapic.IRQNO { + if irq != 0 { + Offset(dt, offset+1) + fmt.Fprintf(dt, "ioapic_irq %d INT%c 0x%x\n", ioapic.APICID, 'A'+pin, irq) + } + } + } + + keys := []string{} + for reg, _ := range dev.Registers { + keys = append(keys, reg) + } + + sort.Strings(keys) + + for _, reg := range keys { + val := dev.Registers[reg] + Offset(dt, offset+1) + fmt.Fprintf(dt, "register \"%s\" = \"%s\"\n", reg, val) + } + + ios := []int{} + for reg, _ := range dev.IOs { + ios = append(ios, int(reg)) + } + + sort.Ints(ios) + + for _, reg := range ios { + val := dev.IOs[uint16(reg)] + Offset(dt, offset+1) + fmt.Fprintf(dt, "io 0x%x = 0x%x\n", reg, val) + } + + for _, child := range dev.Children { + alias = "" + for _, slot := range dev.PCISlots { + if slot.PCIAddr.Bus == child.Bus && + slot.PCIAddr.Dev == child.Dev && slot.PCIAddr.Func == child.Func { + alias = slot.alias + } + } + WriteDev(dt, offset+1, alias, child) + } + + Offset(dt, offset) + fmt.Fprintf(dt, "end\n") +} + +func PutChip(domain string, cur DevTreeNode) { + MissingChildren[domain] = append(MissingChildren[domain], cur) +} + +func PutPCIChip(addr PCIDevData, cur DevTreeNode) { + unmatchedPCIChips[addr.PCIAddr] = cur +} + +func PutPCIDevParent(addr PCIDevData, comment string, parent string) { + cur := DevTreeNode{ + Registers: map[string]string{}, + Chip: "pci", + Bus: addr.Bus, + Dev: addr.Dev, + Func: addr.Func, + MissingParent: parent, + Comment: comment, + } + if addr.ConfigDump[0xa] == 0x04 && addr.ConfigDump[0xb] == 0x06 { + cur.PCIController = true + cur.ChildPCIBus = int(addr.ConfigDump[0x19]) + + loopCtr := 0 + for capPtr := addr.ConfigDump[0x34]; capPtr != 0; capPtr = addr.ConfigDump[capPtr+1] { + /* Avoid hangs. There are only 0x100 different possible values for capPtr. + If we iterate longer than that, we're in endless loop. */ + loopCtr++ + if loopCtr > 0x100 { + break + } + if addr.ConfigDump[capPtr] == 0x0d { + cur.SubVendor = GetLE16(addr.ConfigDump[capPtr+4 : capPtr+6]) + cur.SubSystem = GetLE16(addr.ConfigDump[capPtr+6 : capPtr+8]) + } + } + } else { + cur.SubVendor = GetLE16(addr.ConfigDump[0x2c:0x2e]) + cur.SubSystem = GetLE16(addr.ConfigDump[0x2e:0x30]) + } + unmatchedPCIDevices[addr.PCIAddr] = cur +} + +func PutPCIDev(addr PCIDevData, comment string) { + PutPCIDevParent(addr, comment, "") +} + +type GenericPCI struct { + Comment string + Bus0Subdiv string + MissingParent string +} + +type GenericVGA struct { + GenericPCI +} + +type DSDTInclude struct { + Comment string + File string +} + +type DSDTDefine struct { + Key string + Comment string + Value string +} + +var DSDTIncludes []DSDTInclude +var DSDTPCI0Includes []DSDTInclude +var DSDTDefines []DSDTDefine + +func (g GenericPCI) Scan(ctx Context, addr PCIDevData) { + PutPCIDevParent(addr, g.Comment, g.MissingParent) +} + +var IGDEnabled bool = false + +func (g GenericVGA) Scan(ctx Context, addr PCIDevData) { + KconfigString["VGA_BIOS_ID"] = fmt.Sprintf("%04x,%04x", + addr.PCIVenID, + addr.PCIDevID) + PutPCIDevParent(addr, g.Comment, g.MissingParent) + IGDEnabled = true +} + +func makeKconfigName(ctx Context) { + kn := Create(ctx, "Kconfig.name") + defer kn.Close() + + fmt.Fprintf(kn, "config %s\n\tbool \"%s\"\n", ctx.KconfigName, ctx.Model) +} + +func makeComment(name string) string { + cmt, ok := KconfigComment[name] + if !ok { + return "" + } + return " # " + cmt +} + +func makeKconfig(ctx Context) { + kc := Create(ctx, "Kconfig") + defer kc.Close() + + fmt.Fprintf(kc, "if %s\n\n", ctx.KconfigName) + + fmt.Fprintf(kc, "config BOARD_SPECIFIC_OPTIONS\n\tdef_bool y\n") + keys := []string{} + for name, val := range KconfigBool { + if val { + keys = append(keys, name) + } + } + + sort.Strings(keys) + + for _, name := range keys { + fmt.Fprintf(kc, "\tselect %s%s\n", name, makeComment(name)) + } + + keys = nil + for name, val := range KconfigBool { + if !val { + keys = append(keys, name) + } + } + + sort.Strings(keys) + + for _, name := range keys { + fmt.Fprintf(kc, ` +config %s%s + bool + default n +`, name, makeComment(name)) + } + + keys = nil + for name, _ := range KconfigString { + keys = append(keys, name) + } + + sort.Strings(keys) + + for _, name := range keys { + fmt.Fprintf(kc, ` +config %s%s + string + default "%s" +`, name, makeComment(name), KconfigString[name]) + } + + keys = nil + for name, _ := range KconfigHex { + keys = append(keys, name) + } + + sort.Strings(keys) + + for _, name := range keys { + fmt.Fprintf(kc, ` +config %s%s + hex + default 0x%x +`, name, makeComment(name), KconfigHex[name]) + } + + keys = nil + for name, _ := range KconfigInt { + keys = append(keys, name) + } + + sort.Strings(keys) + + for _, name := range keys { + fmt.Fprintf(kc, ` +config %s%s + int + default %d +`, name, makeComment(name), KconfigInt[name]) + } + + fmt.Fprintf(kc, "endif\n") +} + +const MoboDir = "/src/mainboard/" + +func makeVendor(ctx Context) { + vendor := ctx.Vendor + vendorSane := ctx.SaneVendor + vendorDir := *FlagOutDir + MoboDir + vendorSane + vendorUpper := strings.ToUpper(vendorSane) + kconfig := vendorDir + "/Kconfig" + if _, err := os.Stat(kconfig); os.IsNotExist(err) { + f, err := os.Create(kconfig) + if err != nil { + log.Fatal(err) + } + defer f.Close() + f.WriteString(`if VENDOR_` + vendorUpper + ` + +choice + prompt "Mainboard model" + +source "src/mainboard/` + vendorSane + `/*/Kconfig.name" + +endchoice + +source "src/mainboard/` + vendorSane + `/*/Kconfig" + +config MAINBOARD_VENDOR + string + default "` + vendor + `" + +endif # VENDOR_` + vendorUpper + "\n") + } + kconfigName := vendorDir + "/Kconfig.name" + if _, err := os.Stat(kconfigName); os.IsNotExist(err) { + f, err := os.Create(kconfigName) + if err != nil { + log.Fatal(err) + } + defer f.Close() + f.WriteString(`config VENDOR_` + vendorUpper + ` + bool "` + vendor + `" +`) + } + +} + +func GuessECGPE(ctx Context) int { + /* FIXME:XX Use iasl -d and/or better parsing */ + dsdt := ctx.InfoSource.GetACPI()["DSDT"] + idx := bytes.Index(dsdt, []byte{0x08, '_', 'G', 'P', 'E', 0x0a}) /* Name (_GPE, byte). */ + if idx > 0 { + return int(dsdt[idx+6]) + } + return -1 +} + +func GuessSPDMap(ctx Context) []uint8 { + dmi := ctx.InfoSource.GetDMI() + + if dmi.Vendor == "LENOVO" { + return []uint8{0x50, 0x52, 0x51, 0x53} + } + return []uint8{0x50, 0x51, 0x52, 0x53} +} + +func main() { + flag.Parse() + + ctx := Context{} + + ctx.InfoSource = MakeLogReader() + + dmi := ctx.InfoSource.GetDMI() + + ctx.Vendor = dmi.Vendor + + if dmi.Vendor == "LENOVO" { + ctx.Model = dmi.Version + } else { + ctx.Model = dmi.Model + } + + if dmi.IsLaptop { + KconfigBool["SYSTEM_TYPE_LAPTOP"] = true + } + ctx.SaneVendor = sanitize(ctx.Vendor) + for { + last := ctx.SaneVendor + for _, suf := range []string{"_inc", "_co", "_corp"} { + ctx.SaneVendor = strings.TrimSuffix(ctx.SaneVendor, suf) + } + if last == ctx.SaneVendor { + break + } + } + ctx.MoboID = ctx.SaneVendor + "/" + sanitize(ctx.Model) + ctx.KconfigName = "BOARD_" + strings.ToUpper(ctx.SaneVendor+"_"+sanitize(ctx.Model)) + ctx.BaseDirectory = *FlagOutDir + MoboDir + ctx.MoboID + KconfigString["MAINBOARD_DIR"] = ctx.MoboID + KconfigString["MAINBOARD_PART_NUMBER"] = ctx.Model + + os.MkdirAll(ctx.BaseDirectory, 0700) + + makeVendor(ctx) + + ScanRoot(ctx) + + if IGDEnabled { + KconfigBool["MAINBOARD_HAS_LIBGFXINIT"] = true + KconfigComment["MAINBOARD_HAS_LIBGFXINIT"] = "FIXME: check this" + AddRAMStageFile("gma-mainboard.ads", "CONFIG_MAINBOARD_USE_LIBGFXINIT") + } + + if len(BootBlockFiles) > 0 || len(ROMStageFiles) > 0 || len(RAMStageFiles) > 0 || len(SMMFiles) > 0 { + mf := Create(ctx, "Makefile.mk") + defer mf.Close() + writeMF(mf, BootBlockFiles, "bootblock") + writeMF(mf, ROMStageFiles, "romstage") + writeMF(mf, RAMStageFiles, "ramstage") + writeMF(mf, SMMFiles, "smm") + } + + devtree := Create(ctx, "devicetree.cb") + defer devtree.Close() + + MatchDev(&DevTree) + WriteDev(devtree, 0, "", DevTree) + + if MainboardInit != "" || MainboardEnable != "" || MainboardIncludes != nil { + mainboard := Create(ctx, "mainboard.c") + defer mainboard.Close() + Add_gpl(mainboard) + mainboard.WriteString("#include \n") + for _, include := range MainboardIncludes { + mainboard.WriteString("#include <" + include + ">\n") + } + mainboard.WriteString("\n") + if MainboardInit != "" { + mainboard.WriteString(`static void mainboard_init(struct device *dev) +{ +` + MainboardInit + "}\n\n") + } + if MainboardInit != "" || MainboardEnable != "" { + mainboard.WriteString("static void mainboard_enable(struct device *dev)\n{\n") + if MainboardInit != "" { + mainboard.WriteString("\tdev->ops->init = mainboard_init;\n\n") + } + mainboard.WriteString(MainboardEnable) + mainboard.WriteString("}\n\n") + mainboard.WriteString(`struct chip_operations mainboard_ops = { + .enable_dev = mainboard_enable, +}; +`) + } + } + + bi := Create(ctx, "board_info.txt") + defer bi.Close() + + fixme := "" + + if dmi.IsLaptop { + bi.WriteString("Category: laptop\n") + } else { + bi.WriteString("Category: desktop\n") + fixme += "check category, " + } + + missing := "ROM package, ROM socketed" + + if ROMProtocol != "" { + fmt.Fprintf(bi, "ROM protocol: %s\n", ROMProtocol) + } else { + missing += ", ROM protocol" + } + + if FlashROMSupport != "" { + fmt.Fprintf(bi, "Flashrom support: %s\n", FlashROMSupport) + } else { + missing += ", Flashrom support" + } + + missing += ", Release year" + + if fixme != "" { + fmt.Fprintf(bi, "FIXME: %s, put %s\n", fixme, missing) + } else { + fmt.Fprintf(bi, "FIXME: put %s\n", missing) + } + + if ROMSizeKB == 0 { + KconfigBool["BOARD_ROMSIZE_KB_2048"] = true + KconfigComment["BOARD_ROMSIZE_KB_2048"] = "FIXME: correct this" + } else { + KconfigBool[fmt.Sprintf("BOARD_ROMSIZE_KB_%d", ROMSizeKB)] = true + } + + makeKconfig(ctx) + makeKconfigName(ctx) + + dsdt := Create(ctx, "dsdt.asl") + defer dsdt.Close() + + for _, define := range DSDTDefines { + if define.Comment != "" { + fmt.Fprintf(dsdt, "\t/* %s. */\n", define.Comment) + } + dsdt.WriteString("#define " + define.Key + " " + define.Value + "\n") + } + + Add_gpl(dsdt) + dsdt.WriteString( + ` +#include + +DefinitionBlock( + "dsdt.aml", + "DSDT", + ACPI_DSDT_REV_2, + OEM_ID, + ACPI_TABLE_CREATOR, + 0x20141018 /* OEM revision */ +) +{ + #include + #include "acpi/platform.asl" +`) + + for _, x := range DSDTIncludes { + if x.Comment != "" { + fmt.Fprintf(dsdt, "\t/* %s. */\n", x.Comment) + } + fmt.Fprintf(dsdt, "\t#include <%s>\n", x.File) + } + + dsdt.WriteString(` + Device (\_SB.PCI0) + { +`) + for _, x := range DSDTPCI0Includes { + if x.Comment != "" { + fmt.Fprintf(dsdt, "\t/* %s. */\n", x.Comment) + } + fmt.Fprintf(dsdt, "\t\t#include <%s>\n", x.File) + } + dsdt.WriteString( + ` } +} +`) + + if IGDEnabled { + gma := Create(ctx, "gma-mainboard.ads") + defer gma.Close() + + gma.WriteString(`-- 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 + + -- FIXME: check this + ports : constant Port_List := + (DP1, + DP2, + DP3, + HDMI1, + HDMI2, + HDMI3, + Analog, + LVDS, + eDP); + +end GMA.Mainboard; +`) + } +} diff --git a/util/autoport/rce823.go b/util/autoport/rce823.go new file mode 100644 index 00000000..7c921093 --- /dev/null +++ b/util/autoport/rce823.go @@ -0,0 +1,39 @@ +package main + +import "fmt" + +type rce823 struct { + variant string +} + +func (r rce823) Scan(ctx Context, addr PCIDevData) { + if addr.Dev == 0 && addr.Func == 0 { + cur := DevTreeNode{ + Chip: "drivers/ricoh/rce822", + Comment: "Ricoh cardreader", + Registers: map[string]string{ + + "sdwppol": fmt.Sprintf("%d", (addr.ConfigDump[0xfb]&2)>>1), + "disable_mask": fmt.Sprintf("0x%x", addr.ConfigDump[0xcb]), + }, + PCISlots: []PCISlot{ + PCISlot{PCIAddr: PCIAddr{Bus: addr.Bus, Dev: 0x0, Func: 0}, writeEmpty: false}, + PCISlot{PCIAddr: PCIAddr{Bus: addr.Bus, Dev: 0x0, Func: 1}, writeEmpty: false}, + PCISlot{PCIAddr: PCIAddr{Bus: addr.Bus, Dev: 0x0, Func: 2}, writeEmpty: false}, + PCISlot{PCIAddr: PCIAddr{Bus: addr.Bus, Dev: 0x0, Func: 3}, writeEmpty: false}, + PCISlot{PCIAddr: PCIAddr{Bus: addr.Bus, Dev: 0x0, Func: 4}, writeEmpty: false}, + PCISlot{PCIAddr: PCIAddr{Bus: addr.Bus, Dev: 0x0, Func: 5}, writeEmpty: false}, + PCISlot{PCIAddr: PCIAddr{Bus: addr.Bus, Dev: 0x0, Func: 6}, writeEmpty: false}, + PCISlot{PCIAddr: PCIAddr{Bus: addr.Bus, Dev: 0x0, Func: 7}, writeEmpty: false}, + }, + } + PutPCIChip(addr, cur) + } + PutPCIDev(addr, "Ricoh SD card reader") + KconfigBool["DRIVERS_RICOH_RCE822"] = true +} + +func init() { + RegisterPCI(0x1180, 0xe822, rce823{}) + RegisterPCI(0x1180, 0xe823, rce823{}) +} diff --git a/util/autoport/readme.md b/util/autoport/readme.md new file mode 100644 index 00000000..b546120f --- /dev/null +++ b/util/autoport/readme.md @@ -0,0 +1,457 @@ +# Porting coreboot using autoport + +## Supported platforms + +### Chipset +For any Sandy Bridge or Ivy Bridge platform the generated result should +be bootable, possibly with minor fixes. + +### EC / SuperIO +EC support is likely to work on Intel-based thinkpads. Other laptops are +likely to miss EC support. SuperIO support on desktops is more likely to +work out of the box than any EC. + +## How to use autoport + +Enable as many devices as possible in the firmware setup of your system. +This is useful to detect as many devices as possible and make the port +more complete, as disabled devices cannot be detected. + +Boot into target machine under any Linux-based distribution and install +the following tools on it: +* `gcc` +* `golang` +* `lspci` +* `dmidecode` +* `acpidump` (part of `acpica` on some distros) + +Clone the coreboot tree and `cd` into it. For more detailed steps, refer +to Rookie Guide, Lesson 1. Afterwards, run these commands: + + cd util/ectool + make + cd ../inteltool + make + cd ../superiotool + make + cd ../autoport + go build + sudo ./autoport --input_log=logs --make_logs --coreboot_dir=../.. + + Note: in case you have problems getting gcc and golang on the target + machine, you can compile the utilities on another computer and copy + the binaries to the target machine. You will still need the other + listed programs on the target machine, but you may place them in the + same directory as autoport. + +Check for unknown detected PCI devices, e.g.: + + Unknown PCI device 8086:0085, assuming removable + +If autoport says `assuming removable`, you are fine. If it doesn't, +you may want to add the relevant PCI IDs to autoport. Run `lspci -nn` +and check which device this is using the PCI ID. Devices which are not +part of the chipset, such as GPUs or network cards, can be considered +removable, whereas devices inside the CPU or the PCH such as integrated +GPUs and bus controllers (SATA, USB, LPC, SMBus...) are non-removable. + +Your board has now been added to the tree. However, do not flash it +in its current state. It can brick your machine. Instead, keep this +new port and the logs from `util/autoport/logs` somewhere safe. The +following steps will back up your current firmware, which is always +recommended, since coreboot may not boot on the first try. + +Disassemble your computer and find the flash chip(s). Since there could be +more than one, this guide will refer to "flash chips" as one or more chips. +Refer to as a reference. The flash chip is +usually in a `SOIC-8` (2x4 pins, 200mil) or `SOIC-16` (2x8 pins) package. As +it can be seen on flashrom's wiki, the former package is like any other 8-pin +chip on the mainboard, but it is slightly larger. The latter package is much +easier to locate. Always make sure it is a flash chip by looking up what its +model, printed on it, refers to. + +There may be a smaller flash chip for the EC on some laptops, and other chips +such as network cards may use similar flash chips. These should be left as-is. +If in doubt, ask! + +Once located, use an external flasher to read the flash chips with `flashrom -r`. +Verify with `flashrom -v` several times that reading is consistent. If it is not, +troubleshoot your flashing setup. Save the results somewhere safe, preferably on +media that cannot be easily overwritten and on several devices. You may need this +later. The write process erases the flash chips first, and erased data on a flash +chip is lost for a very long time, usually forever! + +Compile coreboot for your ported mainboard with some console enabled. The most +common ones are EHCI debug, serial port and SPI flash console as a last resort. +If your system is a laptop and has a dedicated video card, you may need to add +a video BIOS (VBIOS) to coreboot to be able to see any video output. Desktop +video cards, as well as some MXM video cards, have this VBIOS on a flash chip +on the card's PCB, so this step is not necessary for them. + +Flash coreboot on the machine. On recent Intel chipsets, the flash space is split +in several regions. Only the one known as "BIOS region" should be flashed. If +there is only one flash chip present, this is best done by adding the `--ifd` +and `-i bios` parameters flashrom has (from v1.0 onwards) to specify what flash +descriptor region it should operate on. If the ME (Management Engine) region is +not readable, which is the case on most systems, use the `--noverify-all` +parameter as well. + +For systems with two flash chips, this is not so easy. It is probably better to +ask in coreboot or flashrom communication channels, such as via IRC or on the +mailing lists. + +Once flashed, try to boot. Anything is possible. If a log is generated, save it +and use it to address any issues. See the next section for useful information. +Find all the sections marked with `FIXME` and correct them. + +Send your work to review.coreboot.org. I mean it, your effort is very appreciated. +Refer to Rookie Guide, Lesson 2 for instructions on how to submit a patch. + +## Manual fixes +### SPD +In order to initialize the RAM memory, coreboot needs to know its timings, which vary between +modules. Socketed RAM has a small EEPROM chip, which is accessible via SMBus and contains the +timing data. This data is usually known as SPD. Unfortunately, the SMBus addresses may not +correlate with the RAM slots and cannot always be detected automatically. The address map is +encoded in function `mainboard_get_spd` in `romstage.c`. By default, autoport uses the most +common map `0x50, 0x51, 0x52, 0x53` on everything except for Lenovo systems, which are known +to use `0x50, 0x52, 0x51, 0x53`. To detect the correct memory map, the easiest way is to boot +on the vendor firmware with just one module in channel 0, slot 0, and check the SMBus address +the EEPROM has. Under Linux, you can use these commands to see what is on SMBus: + + $ sudo modprobe i2c-dev + $ sudo modprobe i2c-i801 + $ sudo i2cdetect -l + i2c-0 i2c i915 gmbus ssc I2C adapter + i2c-1 i2c i915 gmbus vga I2C adapter + i2c-2 i2c i915 gmbus panel I2C adapter + i2c-3 i2c i915 gmbus dpc I2C adapter + i2c-4 i2c i915 gmbus dpb I2C adapter + i2c-5 i2c i915 gmbus dpd I2C adapter + i2c-6 i2c DPDDC-B I2C adapter + i2c-7 i2c DPDDC-C I2C adapter + i2c-8 i2c DPDDC-D I2C adapter + i2c-9 smbus SMBus I801 adapter at 0400 SMBus adapter + + $ sudo i2cdetect 9 + WARNING! This program can confuse your I2C bus, cause data loss and worse! + I will probe file /dev/i2c-9. + I will probe address range 0x03-0x77. + Continue? [Y/n] y + 0 1 2 3 4 5 6 7 8 9 a b c d e f + 00: -- -- -- -- -- 08 -- -- -- -- -- -- -- + 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + 20: -- -- -- -- 24 -- -- -- -- -- -- -- -- -- -- -- + 30: 30 31 -- -- -- -- -- -- -- -- -- -- -- -- -- -- + 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + 50: 50 -- -- -- 54 55 56 57 -- -- -- -- 5c 5d 5e 5f + 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- + 70: -- -- -- -- -- -- -- -- + +Make sure to replace the `9` on the last command with the bus number for SMBus on +your system. Here, there is a module at address `0x50`. Since only one module was +installed on the first slot of the first channel, we know the first position of +the SPD array must be `0x50`. After testing all the slots, your `mainboard_get_spd` +should look similar to this: + + void mainboard_get_spd(spd_raw_data *spd) { + read_spd(&spd[0], 0x50); + read_spd(&spd[1], 0x51); + read_spd(&spd[2], 0x52); + read_spd(&spd[3], 0x53); + } + +Note that there should be one line per memory slot on the mainboard. + +Note: slot labelling may be missing or unreliable. Use `inteltool` to see +which slots have modules in them. + +This procedure is ideal, if your RAM is socketed. If you have soldered RAM, +remove any socketed memory modules and check if any EEPROM appears on SMBus. +If this is the case, you can proceed as if the RAM was socketed. However, +you may have to guess some entries if there multiple EEPROMs appear. + +Most of the time, soldered RAM does not have an EEPROM. Instead, the SPD data is +inside the main flash chip where the firmware is. If this is the case, you need +to generate the SPD data to use with coreboot. Look at `inteltool.log`. There +should be something like this: + + /* SPD matching current mode: */ + /* CH0S0 */ + 00: 92 11 0b 03 04 00 00 09 03 52 01 08 0a 00 80 00 + 10: 6e 78 6e 32 6e 11 18 81 20 08 3c 3c 00 f0 00 00 + 20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 00 + 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6d 17 + 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + /* CH1S0 */ + 00: 92 11 0b 03 04 00 00 09 03 52 01 08 0a 00 80 00 + 10: 6e 78 6e 32 6e 11 18 81 20 08 3c 3c 00 f0 00 00 + 20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 00 + 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6d 17 + 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +This is not a full-fledged SPD dump, as it only lists +the currently-used speed configuration, and lacks info +such as a serial number, vendor and model. Use `xxd` +to create a binary file with this SPD data: + + $ cat | xxd -r > spd.bin <= 128) + memcpy(&spd[0], spd_file, 128); + + /* C1S0 is a physical slot. */ + read_spd(&spd[2], 0x52); + } + +If several slots are soldered there are two ways to handle them: + +* If all use the same SPD data, use the same file for all the slots. Do + not forget to copy the data on all the array elements that need it. +* If they use different data, use several files. + +If memory initialization is not working, in particular write training (timB) +on DIMM's second rank fails, try enabling rank 1 mirroring, which can't be +detected by inteltool. It is described by SPD field "Address Mapping from Edge +Connector to DRAM", byte `63` (`0x3f`). Bit 0 describes Rank 1 Mapping, +0 = standard, 1 = mirrored; set it to 1. Bits 1-7 are reserved. + +### `board_info.txt` + +`board_info.txt` is a text file used in the board status page to list all +the supported boards and their specifications. Most of the information +cannot be detected by autoport. Common entries are: + +* `ROM package`, `ROM protocol` and `ROM socketed`: + These refer to the flash chips you found earlier. You can visit + for more information. + +* `Release year`: Use the power of Internet to find that information. +* `Category`: This describes the type of mainboard you have. + Valid categories are: + * `desktop`. Desktops and workstations. + * `server`. Servers. + * `laptop`. Laptops, notebooks and netbooks. + * `half`. Embedded / PC/104 / Half-size boards. + * `mini`. Mini-ITX / Micro-ITX / Nano-ITX + * `settop`. Set-top-boxes / Thin clients. + * `eval`. Development / Evaluation Boards. + * `sbc`. Single-Board computer. + * `emulation`: Virtual machines and emulators. May require especial care + as they often behave differently from real counterparts. + * `misc`. Anything not fitting the categories above. Not recommended. + +* `Flashrom support`: This means whether the internal programmer is usable. + If flashing coreboot internally works, this should be set to `y`. Else, + feel free to investigate why it is not working. + +### `USBDEBUG_HCD_INDEX` + +Which controller the most easily accessible USB debug port is. On Intel, +1 is for `00:1d.0` and 2 is for `00:1a.0` (yes, it's reversed). Refer to + for more info. + +If you are able to use EHCI debug without setting the HCD index manually, +this is correct. + +### `BOARD_ROMSIZE_KB_2048` + +This parameter refers to the total size of the flash chips coreboot will be in. +This value must be correct for S3 resume to work properly. This parameter also +defines the size of the generated coreboot image, but that is not a major issue +since tools like `dd` can be used to cut fragments of a coreboot image to flash +on smaller chips. + +This should be detected automatically, but it may not be detected properly in +some cases. If it was not detected, put the correct total size here to serve +as a sane default when configuring coreboot. + +### `DRAM_RESET_GATE_GPIO` + +When the computer is suspended to RAM (ACPI S3), the RAM reset signal must not +reach the RAM modules. Otherwise, the computer will not resume and any opened +programs will be lost. This is done by powering down a MOSFET, which disconnects +the reset signal from the RAM modules. Most manufacturers put this gate on GPIO +60 but Lenovo is known to put it on GPIO 10. If suspending and resuming works, +this value is correct. This can also be determined from the board's schematics. + +## GNVS + +`mainboard_fill_gnvs` sets values in GNVS, which then ACPI makes use of for +various power-related functions. Normally, there is no need to modify it +on laptops (desktops have no "lid"!) but it makes sense to proofread it. + +## `gfx.ndid` and `gfx.did` + +Those describe which video outputs are declared in ACPI tables. +Normally, there is no need to have these values, but if you miss some +non-standard video output, you can declare it there. Bit 31 is set to +indicate the presence of the output. Byte 1 is the type and byte 0 is +used for disambigution so that ID composed of byte 1 and 0 is unique. + +Types are: +* 1 = VGA +* 2 = TV +* 3 = DVI +* 4 = LCD + +## `c*_acpower` and `c*_battery` + +Which mwait states to match to which ACPI levels. Normally, there is no +need to modify anything unless your device has very special power saving +requirements. + +## `install_intel_vga_int15_handler` + +This is used with the Intel VGA BIOS, which is not the default option. +It is more error-prone than open-source graphics initialization, so do +not bother with this until your mainboard boots. This is a function +which takes four parameters: +1. Which type of LCD panel is connected. +2. Panel fit. +3. Boot display. +4. Display type. + +Refer to `src/drivers/intel/gma/int15.h` to see which values can be used. +For desktops, there is no LCD panel directly connected to the Intel GPU, +so the first parameter should be `GMA_INT15_ACTIVE_LFP_NONE`. On other +mainboards, it depends. + +## CMOS options + +Due to the poor state of CMOS support in coreboot, autoport does not +support it and this probably won't change until the format in the tree +improves. If you really care about CMOS options: + +* Create files `cmos.layout` and `cmos.default` +* Enable `HAVE_OPTION_TABLE` and `HAVE_CMOS_DEFAULT` in `Kconfig` + +## EC (lenovo) + +You need to set `has_keyboard_backlight` (backlit keyboard like X230), +`has_power_management_beeps` (optional beeps when e.g. plugging the cord +in) and `has_uwb` (third MiniPCIe slot) in accordance to functions available +on your machine + +In rare cases autoport is unable to detect GPE. You can detect it from +dmesg or ACPI tables. Look for line in dmesg like + + ACPI: EC: GPE = 0x11, I/O: command/status = 0x66, data = 0x62 + +This means that GPE is `0x11` in ACPI notation. This is the correct +value for `THINKPAD_EC_GPE`. To get the correct value for `GPE_EC_SCI` +you need to substract `0x10`, so value for it is `1`. + +The pin used to wake the machine from EC is guessed. If your machine doesn't +wake on lid open and pressing of Fn, change `GPE_EC_WAKE`. + +Keep `GPE_EC_WAKE` and `GPE_EC_SCI` in sync with `gpi*_routing`. +`gpi*_routing` matching `GPE_EC_WAKE` or `GPE_EC_SCI` is set to `2` +and all others are absent. + +If your dock has LPC wires or needs some special treatement you may +need to add codes to initialize the dock and support code to +DSDT. See the `init_dock()` for `x60`, `x200` or `x201`. + +## EC (generic laptop) + +Almost any laptop has an embedded controller. In a nutshell, it's a +small, low-powered computer designed to be used on laptops. Exact +functionality differs between machines. Its main functions include: + +* Control of power and rfkill to different component +* Keyboard (PS/2) interface implementation +* Battery, AC, LID and thermal information exporting +* Hotkey support + +autoport automatically attempts to restore the dumped config but it +may or may not work and may even lead to a hang or powerdown. If your +machine stops at `Replaying EC dump ...` try commenting EC replay out + +autoport tries to detect if machine has PS/2 interface and if so calls +`pc_keyboard_init` and exports relevant ACPI objects. If detection fails +you may have to add them yourself + +ACPI methods `_PTS` (prepare to sleep) and `_WAK` (wake) are executed +when transitioning to sleep or wake state respectively. You may need to +add power-related calls there to either shutdown some components or to +add a workaround to stop giving OS thermal info until next refresh. + +For exporting the battery/AC/LID/hotkey/thermal info you need to write +`acpi/ec.asl`. For an easy example look into `apple/macbook21` or +`packardbell/ms2290`. For information about needed methods consult +relevant ACPI specs. Tracing which EC events can be done using +[dynamic debug](https://wiki.ubuntu.com/Kernel/Reference/ACPITricksAndTips) + +EC GPE needs to be routed to SCI in order for OS in order to receive +EC events like "hotkey X pressed" or "AC plugged". autoport attempts +to detect GPE but in rare cases may fail. You can detect it from +dmesg or ACPI tables. Look for line in dmesg like + + ACPI: EC: GPE = 0x11, I/O: command/status = 0x66, data = 0x62 + +This means that GPE is `0x11` in ACPI notation. This is the correct +value for `_GPE`. + +Keep GPE in sync with `gpi*_routing`. +`gpi*_routing` matching `GPE - 0x10` is set to `2` +and all others are absent. If EC has separate wake pin +then this GPE needs to be routed as well diff --git a/util/autoport/root.go b/util/autoport/root.go new file mode 100644 index 00000000..7e9e8145 --- /dev/null +++ b/util/autoport/root.go @@ -0,0 +1,47 @@ +package main + +import "fmt" +import "os" + +var supportedPCIDevices map[uint32]PCIDevice = map[uint32]PCIDevice{} +var PCIMap map[PCIAddr]PCIDevData = map[PCIAddr]PCIDevData{} + +func ScanRoot(ctx Context) { + for _, pciDev := range ctx.InfoSource.GetPCIList() { + PCIMap[pciDev.PCIAddr] = pciDev + } + for _, pciDev := range ctx.InfoSource.GetPCIList() { + vendevid := (uint32(pciDev.PCIDevID) << 16) | uint32(pciDev.PCIVenID) + + dev, ok := supportedPCIDevices[vendevid] + if !ok { + if pciDev.PCIAddr.Bus != 0 { + fmt.Printf("Unknown PCI device %04x:%04x, assuming removable\n", + pciDev.PCIVenID, pciDev.PCIDevID) + continue + } + fmt.Printf("Unsupported PCI device %04x:%04x\n", + pciDev.PCIVenID, pciDev.PCIDevID) + dev = GenericPCI{Comment: fmt.Sprintf("Unsupported PCI device %04x:%04x", + pciDev.PCIVenID, pciDev.PCIDevID)} + } + dev.Scan(ctx, pciDev) + } + if SouthBridge == nil { + fmt.Println("Could not detect southbridge. Aborting!") + os.Exit(1) + } + dmi := ctx.InfoSource.GetDMI() + if !dmi.IsLaptop { + NoEC(ctx) + } else if dmi.Vendor == "LENOVO" { + LenovoEC(ctx) + } else { + FIXMEEC(ctx) + } +} + +func RegisterPCI(VenID uint16, DevID uint16, dev PCIDevice) { + vendevid := (uint32(DevID) << 16) | uint32(VenID) + supportedPCIDevices[vendevid] = dev +} diff --git a/util/autoport/sandybridge.go b/util/autoport/sandybridge.go new file mode 100644 index 00000000..bd7f0f0c --- /dev/null +++ b/util/autoport/sandybridge.go @@ -0,0 +1,93 @@ +package main + +import "fmt" + +type sandybridgemc struct { +} + +func (i sandybridgemc) Scan(ctx Context, addr PCIDevData) { + inteltool := ctx.InfoSource.GetInteltool() + + /* FIXME:XX Move this somewhere else. */ + MainboardIncludes = append(MainboardIncludes, "drivers/intel/gma/int15.h") + MainboardEnable += (` /* FIXME: fix these values. */ + install_intel_vga_int15_handler(GMA_INT15_ACTIVE_LFP_INT_LVDS, + GMA_INT15_PANEL_FIT_DEFAULT, + GMA_INT15_BOOT_DISPLAY_DEFAULT, 0); +`) + + DevTree = DevTreeNode{ + Chip: "northbridge/intel/sandybridge", + MissingParent: "northbridge", + Comment: "FIXME: GPU registers may not always apply.", + Registers: map[string]string{ + "gpu_dp_b_hotplug": FormatInt32((inteltool.IGD[0xc4030] >> 2) & 7), + "gpu_dp_c_hotplug": FormatInt32((inteltool.IGD[0xc4030] >> 10) & 7), + "gpu_dp_d_hotplug": FormatInt32((inteltool.IGD[0xc4030] >> 18) & 7), + "gpu_panel_port_select": FormatInt32((inteltool.IGD[0xc7208] >> 30) & 3), + "gpu_panel_power_up_delay": FormatInt32((inteltool.IGD[0xc7208] >> 16) & 0x1fff), + "gpu_panel_power_backlight_on_delay": FormatInt32(inteltool.IGD[0xc7208] & 0x1fff), + "gpu_panel_power_down_delay": FormatInt32((inteltool.IGD[0xc720c] >> 16) & 0x1fff), + "gpu_panel_power_backlight_off_delay": FormatInt32(inteltool.IGD[0xc720c] & 0x1fff), + "gpu_panel_power_cycle_delay": FormatInt32(inteltool.IGD[0xc7210] & 0xff), + "gpu_cpu_backlight": FormatHex32(inteltool.IGD[0x48254]), + "gpu_pch_backlight": FormatHex32((inteltool.IGD[0xc8254] >> 16) * 0x10001), + "gfx": fmt.Sprintf("GMA_STATIC_DISPLAYS(%d)", (inteltool.IGD[0xc6200] >> 12) & 1), + }, + Children: []DevTreeNode{ + { + Chip: "domain", + Dev: 0, + PCIController: true, + ChildPCIBus: 0, + PCISlots: []PCISlot{ + PCISlot{PCIAddr: PCIAddr{Dev: 0x0, Func: 0}, writeEmpty: true, alias: "host_bridge", additionalComment: "Host bridge"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x1, Func: 0}, writeEmpty: true, alias: "peg10", additionalComment: "PEG"}, + PCISlot{PCIAddr: PCIAddr{Dev: 0x2, Func: 0}, writeEmpty: true, alias: "igd", additionalComment: "iGPU"}, + }, + }, + }, + } + + PutPCIDev(addr, "Host bridge") + + /* FIXME:XX some configs are unsupported. */ + KconfigBool["NORTHBRIDGE_INTEL_SANDYBRIDGE"] = true + KconfigBool["USE_NATIVE_RAMINIT"] = true + KconfigBool["INTEL_INT15"] = true + KconfigBool["HAVE_ACPI_TABLES"] = true + KconfigBool["HAVE_ACPI_RESUME"] = true + + DSDTIncludes = append(DSDTIncludes, DSDTInclude{ + File: "cpu/intel/common/acpi/cpu.asl", + }) + + DSDTPCI0Includes = append(DSDTPCI0Includes, DSDTInclude{ + File: "northbridge/intel/sandybridge/acpi/sandybridge.asl", + }, DSDTInclude{ + File: "drivers/intel/gma/acpi/default_brightness_levels.asl", + }) +} + +func init() { + RegisterPCI(0x8086, 0x0100, sandybridgemc{}) + RegisterPCI(0x8086, 0x0104, sandybridgemc{}) + RegisterPCI(0x8086, 0x0150, sandybridgemc{}) + RegisterPCI(0x8086, 0x0154, sandybridgemc{}) + RegisterPCI(0x8086, 0x0158, sandybridgemc{}) + for _, id := range []uint16{ + 0x0102, 0x0106, 0x010a, + 0x0112, 0x0116, 0x0122, 0x0126, + 0x0152, 0x0156, 0x0162, 0x0166, + } { + RegisterPCI(0x8086, id, GenericVGA{GenericPCI{}}) + } + + /* PCIe bridge */ + for _, id := range []uint16{ + 0x0101, 0x0105, 0x0109, 0x010d, + 0x0151, 0x0155, 0x0159, 0x015d, + } { + RegisterPCI(0x8086, id, GenericPCI{}) + } +} -- cgit v1.2.1