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 <nic.c3.14@gmail.com>
This commit is contained in:
Nicholas Chin 2024-03-18 10:45:05 -06:00
parent c578fe56c3
commit 8cba237086
No known key found for this signature in database
GPG Key ID: 2D0EB28C84678DAA
18 changed files with 3918 additions and 0 deletions

67
util/autoport/azalia.go Normal file
View File

@ -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 <device/azalia_device.h>
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{})
}

337
util/autoport/bd82x6x.go Normal file
View File

@ -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 <bootblock_common.h>
#include <device/pci_ops.h>
#include <northbridge/intel/sandybridge/raminit_native.h>
#include <southbridge/intel/bd82x6x/pch.h>
`)
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 <acpi/acpi_gnvs.h>
#include <soc/nvs.h>
/* 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{})
}

View File

@ -0,0 +1 @@
Automated porting coreboot to Sandy Bridge/Ivy Bridge platforms `Go`

91
util/autoport/ec_fixme.go Normal file
View File

@ -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 <drivers/pc80/pc/ps2_controller.asl>\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")
}

245
util/autoport/ec_lenovo.go Normal file
View File

@ -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 <drivers/pc80/pc/ps2_controller.asl>\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 <arch/io.h>
#include <console/console.h>
#include <cpu/x86/smm.h>
#include <ec/acpi/ec.h>
#include <ec/lenovo/h8/h8.h>
#include <delay.h>
#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 <ec/lenovo/h8/acpi/ec.asl>\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)
}

24
util/autoport/ec_none.go Normal file
View File

@ -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()
}

1
util/autoport/go.mod Normal file
View File

@ -0,0 +1 @@
module autoport

View File

@ -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 <southbridge/intel/common/gpio.h>\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,
},
};
`)
}

139
util/autoport/haswell.go Normal file
View File

@ -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{})
}

190
util/autoport/log_maker.go Normal file
View File

@ -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)
}
}
}

417
util/autoport/log_reader.go Normal file
View File

@ -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}
}

490
util/autoport/lynxpoint.go Normal file
View File

@ -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 <southbridge/intel/lynxpoint/pch.h>
/* 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 <stdint.h>
#include <northbridge/intel/haswell/haswell.h>
#include <northbridge/intel/haswell/raminit.h>
#include <southbridge/intel/lynxpoint/pch.h>
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{})
}
}

View File

@ -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 <southbridge/intel/lynxpoint/lp_gpio.h>
const struct pch_lp_gpio_map mainboard_lp_gpio_map[] = {
`)
PrintLPGPIO(gpio, inteltool)
gpio.WriteString("\tLP_GPIO_END\n};\n")
}

915
util/autoport/main.go Normal file
View File

@ -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 <device/device.h>\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 <acpi/acpi.h>
DefinitionBlock(
"dsdt.aml",
"DSDT",
ACPI_DSDT_REV_2,
OEM_ID,
ACPI_TABLE_CREATOR,
0x20141018 /* OEM revision */
)
{
#include <acpi/dsdt_top.asl>
#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;
`)
}
}

39
util/autoport/rce823.go Normal file
View File

@ -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{})
}

457
util/autoport/readme.md Normal file
View File

@ -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 <https://flashrom.org/Technology> 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 <<EOF
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
EOF (press Ctrl + D)
Then, move the generated file into your mainboard's directory
and hook it up to the build system by adding the following
lines to `Makefile.mk`:
cbfs-files-y += spd.bin
spd.bin-file := spd.bin
spd.bin-type := raw
Now we need coreboot to use this SPD file. The following example
shows a hybrid configuration, in which one module is soldered and
the other one is socketed:
void mainboard_get_spd(spd_raw_data *spd)
{
void *spd_file;
size_t spd_file_len = 0;
/* C0S0 is a soldered RAM with no real SPD. Use stored SPD. */
spd_file = cbfs_boot_map_with_leak("spd.bin", CBFS_TYPE_RAW,
&spd_file_len);
if (spd_file && spd_file_len >= 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
<https://flashrom.org/Technology> 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
<https://www.coreboot.org/EHCI_Debug_Port> 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

47
util/autoport/root.go Normal file
View File

@ -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
}

View File

@ -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{})
}
}