autoport: Write autoport together with porting guide for sandy/ivybridge.

This should be able to generate bootable ports for sandy/ivy, possible with
minor fixes. Howto is in readme.md

Change-Id: Ia126cf0939ef2dc2cdbb7ea100d2b63ea6b02f28
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-on: http://review.coreboot.org/7131
Tested-by: build bot (Jenkins)
Reviewed-by: Edward O'Callaghan <edward.ocallaghan@koparo.com>
This commit is contained in:
Vladimir Serbinenko 2014-10-15 21:51:47 +02:00
parent b06a249c3b
commit 3129f792f7
12 changed files with 2862 additions and 0 deletions

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

@ -0,0 +1,64 @@
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()
az.WriteString(
`#include <device/azalia_device.h>
const u32 cim_verb_data[] = {
`)
for _, codec := range ctx.InfoSource.GetAzaliaCodecs() {
fmt.Fprintf(az, "\t0x%08x, /* Codec Vendor / Device ID: %s */\n",
codec.VendorID, codec.Name)
fmt.Fprintf(az, "\t0x%08x, /* Subsystem ID */\n",
codec.SubsystemID)
fmt.Fprintf(az, "\n\t0x%08x, /* Number of 4 dword sets */\n",
len(codec.PinConfig)+1)
fmt.Fprintf(az, "\t/* NID 0x01: Subsystem ID. */\n")
fmt.Fprintf(az, "\tAZALIA_SUBVENDOR(0x%x, 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, "\n\t/* NID 0x%02x. */\n", nid)
fmt.Fprintf(az, "\tAZALIA_PIN_CFG(0x%x, 0x%02x, 0x%08x),\n",
codec.CodecNo, nid, codec.PinConfig[nid])
}
}
az.WriteString(
`};
const u32 pc_beep_verbs[0] = {};
AZALIA_ARRAY_SIZES;
`)
PutPCIDev(addr, "Audio controller")
}
func init() {
/* I82801GX/I945 */
RegisterPCI(0x8086, 0x27d8, azalia{})
/* BD82X6X/sandybridge */
RegisterPCI(0x8086, 0x1c20, azalia{})
/* C216/ivybridge */
RegisterPCI(0x8086, 0x1e20, azalia{})
}

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

@ -0,0 +1,456 @@
package main
import (
"fmt"
"os"
)
type bd82x6x struct {
variant string
node *DevTreeNode
}
func (b bd82x6x) writeGPIOSet(ctx Context, sb *os.File,
val uint32, set uint, partno int) {
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++ {
fmt.Fprintf(sb, " .gpio%d = %s,\n",
(set-1)*32+i,
bits[partno][(val>>i)&1])
}
}
func (b bd82x6x) GPIO(ctx Context, inteltool InteltoolData) {
gpio := Create(ctx, "gpio.c")
defer gpio.Close()
AddROMStageFile("gpio.c", "")
gpio.WriteString(`#include "southbridge/intel/bd82x6x/gpio.h"
`)
adresses := [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 := adresses[set-1][partno]
if addr < 0 {
continue
}
fmt.Fprintf(gpio, "const struct pch_gpio_set%d pch_gpio_set%d_%s = {\n",
set, set, part)
b.writeGPIOSet(ctx, gpio, inteltool.GPIO[uint16(addr)], uint(set), partno)
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,
},
};
`)
}
func (b bd82x6x) 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 << 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()
b.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"
/* Not strictly speaking correct. These subsys/subvendor referer to PCI devices.
But most systems don't have any of those. But the config needs to be set
nevertheless. So set it to southbridge subsys/subvendor. */
KconfigHex["MAINBOARD_PCI_SUBSYSTEM_VENDOR_ID"] = uint32(GetLE16(addr.ConfigDump[0x2c:0x2e]))
KconfigHex["MAINBOARD_PCI_SUBSYSTEM_DEVICE_ID"] = uint32(GetLE16(addr.ConfigDump[0x2e:0x30]))
ich9GetFlashSize(ctx)
DSDTDefines = append(DSDTDefines,
DSDTDefine{
Key: "BRIGHTNESS_UP",
Value: "\\_SB.PCI0.GFX0.INCB",
},
DSDTDefine{
Key: "BRIGHTNESS_DOWN",
Value: "\\_SB.PCI0.GFX0.DECB",
},
DSDTDefine{
Key: "ACPI_VIDEO_DEVICE",
Value: "\\_SB.PCI0.GFX0",
})
/* SPI init */
MainboardIncludes = append(MainboardIncludes, "southbridge/intel/bd82x6x/pch.h")
/* FIXME:XX Move this to runtime. */
for _, addr := range []uint16{0x38c8, 0x38c4, 0x38c0} {
MainboardInit += fmt.Sprintf("\tRCBA32(0x%04x) = 0x%08x;\n", addr, inteltool.RCBA[addr])
}
FADT := ctx.InfoSource.GetACPI()["FACP"]
pcieHotplugMap := "{ "
for port := 0; port < 7; port++ {
if b.IsPCIeHotplug(ctx, port) {
pcieHotplugMap += "1, "
} else {
pcieHotplugMap += "0, "
}
}
if b.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),
"p_cnt_throttling_supported": (FormatBool(FADT[104] == 1 && FADT[105] == 3)),
"c2_latency": FormatHexLE16(FADT[96:98]),
"docking_supported": (FormatBool((FADT[113] & (1 << 1)) != 0)),
},
PCISlots: []PCISlot{
PCISlot{PCIAddr: PCIAddr{Dev: 0x14, Func: 0}, writeEmpty: false, additionalComment: "USB 3.0 Controller"},
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: 0x19, Func: 0}, writeEmpty: true, additionalComment: "Intel Gigabit Ethernet"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1a, Func: 0}, writeEmpty: true, 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: true, additionalComment: "PCIe Port #7"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1c, Func: 7}, writeEmpty: true, additionalComment: "PCIe Port #8"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1d, Func: 0}, writeEmpty: true, additionalComment: "USB2 EHCI #1"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1e, Func: 0}, writeEmpty: true, additionalComment: "PCI bridge"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 0}, writeEmpty: true, additionalComment: "LPC bridge"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 2}, writeEmpty: true, additionalComment: "SATA Controller 1"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 3}, writeEmpty: true, additionalComment: "SMBus"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 5}, writeEmpty: true, additionalComment: "SATA Controller 2"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1f, Func: 6}, writeEmpty: true, 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, "PCI-LPC bridge", "lpc")
DSDTIncludes = append(DSDTIncludes, DSDTInclude{
File: "southbridge/intel/bd82x6x/acpi/platform.asl",
})
DSDTIncludes = append(DSDTIncludes, DSDTInclude{
File: "southbridge/intel/bd82x6x/acpi/globalnvs.asl",
Comment: "global NVS and variables",
})
DSDTIncludes = append(DSDTIncludes, DSDTInclude{
File: "southbridge/intel/bd82x6x/acpi/sleepstates.asl",
})
DSDTPCI0Includes = append(DSDTPCI0Includes, DSDTInclude{
File: "southbridge/intel/bd82x6x/acpi/pch.asl",
})
sb := Create(ctx, "early_southbridge.c")
defer sb.Close()
AddROMStageFile("early_southbridge.c", "")
sb.WriteString(`#include <stdint.h>
#include <string.h>
#include <lib.h>
#include <timestamp.h>
#include <arch/byteorder.h>
#include <arch/io.h>
#include <device/pci_def.h>
#include <device/pnp_def.h>
#include <cpu/x86/lapic.h>
#include <arch/acpi.h>
#include <console/console.h>
#include "northbridge/intel/sandybridge/sandybridge.h"
#include "northbridge/intel/sandybridge/raminit_native.h"
#include "southbridge/intel/bd82x6x/pch.h"
#include "southbridge/intel/bd82x6x/gpio.h"
#include <arch/cpu.h>
#include <cpu/x86/msr.h>
void pch_enable_lpc(void)
{
`)
RestorePCI16Simple(sb, addr, 0x82)
RestorePCI32Simple(sb, addr, 0x84)
RestorePCI32Simple(sb, addr, 0x88)
RestorePCI32Simple(sb, addr, 0x8c)
RestorePCI32Simple(sb, addr, 0x90)
RestorePCI16Simple(sb, addr, 0x80)
RestorePCI32Simple(sb, addr, 0xac)
sb.WriteString(`}
void rcba_config(void)
{
/* Disable devices. */
`)
RestoreRCBA32(sb, inteltool, 0x3414)
RestoreRCBA32(sb, inteltool, 0x3418)
sb.WriteString("\n}\n")
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,
}
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
}
}
}
fmt.Fprintf(sb, "\t{ %d, %d, %d },\n",
((inteltool.RCBA[0x359c]>>port)&1)^1,
currentMap[inteltool.RCBA[uint16(0x3500+4*port)]],
OCPin)
}
sb.WriteString("};\n")
guessedMap := GuessSPDMap(ctx)
sb.WriteString(`
/* FIXME: Put proper SPD map here. */
void mainboard_get_spd(spd_raw_data *spd)
{
`)
for i, spd := range guessedMap {
fmt.Fprintf(sb, "\tread_spd(&spd[%d], 0x%02x);\n", i, spd)
}
sb.WriteString("}\n")
gnvs := Create(ctx, "gnvs.c")
defer gnvs.Close()
gnvs.WriteString(`#include <southbridge/intel/bd82x6x/nvs.h>
/* FIXME: check this function. */
void acpi_create_gnvs(global_nvs_t *gnvs)
{
/* Disable USB ports in S3 by default */
gnvs->s3u0 = 0;
gnvs->s3u1 = 0;
/* Disable USB ports in S5 by default */
gnvs->s5u0 = 0;
gnvs->s5u1 = 0;
// the lid is open by default.
gnvs->lids = 1;
gnvs->tcrt = 100;
gnvs->tpsv = 90;
}
`)
AddRAMStageFile("gnvs.c", "")
}
func init() {
/* BD82X6X LPC */
for _, id := range []uint16{
0x1c46, 0x1c47, 0x1c49, 0x1c4a,
0x1c4b, 0x1c4c, 0x1c4d, 0x1c4e,
0x1c4f, 0x1c50, 0x1c52, 0x1c54,
0x1c56, 0x1c5c,
} {
RegisterPCI(0x8086, id, bd82x6x{variant: "BD82X6X"})
}
/* C216 LPC */
for _, id := range []uint16{
0x1e55, 0x1e57, 0x1e5d, 0x1e5e, 0x1e5f, 0x1e49,
} {
RegisterPCI(0x8086, 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,
} {
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{})
}

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

@ -0,0 +1,85 @@
package main
import "fmt"
func FIXMEEC(ctx Context) {
ap := Create(ctx, "acpi/platform.asl")
defer ap.Close()
hasKeyboard := IsIOPortUsedBy(ctx, 0x60, "keyboard")
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))
}
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")
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");
}
`
si := Create(ctx, "acpi/superio.asl")
defer si.Close()
if hasKeyboard {
si.WriteString("#include <drivers/pc80/ps2_controller.asl>\n")
MainboardInit += fmt.Sprintf("\tpc_keyboard_init();\n")
MainboardIncludes = append(MainboardIncludes, "pc80/keyboard.h")
}
ec := Create(ctx, "acpi/ec.asl")
defer ec.Close()
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")
}

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

@ -0,0 +1,295 @@
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)
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()
si.WriteString("#include <drivers/pc80/ps2_controller.asl>\n")
dock := Create(ctx, "dock.c")
defer dock.Close()
AddRAMStageFile("dock.c", "")
dock.WriteString(
`#include <ec/lenovo/h8/h8.h>
void h8_mainboard_init_dock (void)
{
/* FIXME: fill this if needed. */
}
`)
/* FIXME:XX Move this to ec/lenovo. */
smi := Create(ctx, "smihandler.c")
defer smi.Close()
AddSMMFile("smihandler.c", "")
smi.WriteString(
`/*
* This file is part of the coreboot project.
*
* Copyright (C) 2008-2009 coresystems GmbH
* Copyright (C) 2014 Vladimir Serbinenko
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; version 2 of
* the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc.
*/
#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_smm_init(void)
{
printk(BIOS_DEBUG, "initializing SMI\n");
/* Enable 0x1600/0x1600 register pair */
ec_set_bit(0x00, 0x05);
}
int mainboard_io_trap_handler(int smif)
{
static int smm_initialized;
if (!smm_initialized) {
mainboard_smm_init();
smm_initialized = 1;
}
return 0;
}
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()
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": "0x01",
"dock_event_enable": "0x01",
},
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)
}

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

@ -0,0 +1,23 @@
package main
func NoEC(ctx Context) {
ap := Create(ctx, "acpi/platform.asl")
defer ap.Close()
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()
}

View File

@ -0,0 +1,88 @@
package main
import (
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
)
func RunAndSave(output string, name string, arg ...string) {
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 {
log.Fatal(err)
}
cmd.Wait()
}
func MakeLogs(outDir string) {
os.MkdirAll(outDir, 0700)
RunAndSave(outDir+"/lspci.log", "lspci", "-nnvvvxxxx")
RunAndSave(outDir+"/dmidecode.log", "dmidecode")
RunAndSave(outDir+"/acpidump.log", "acpidump")
/* FIXME:XX */
RunAndSave(outDir+"/inteltool.log", "../inteltool/inteltool", "-a")
RunAndSave(outDir+"/ectool.log", "../ectool/ectool", "-a")
SysDir := "/sys/class/sound/card0/"
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/card0/"
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)
}
}
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)
}
}

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

@ -0,0 +1,370 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"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()
switch {
case len(line) >= 6 && line[5] == '@':
curTable = line[0:4]
Tables[curTable] = make([]byte, 0, 100000)
case len(line) > 7 && line[0:2] == " " && isXDigit(line[2]) && isXDigit(line[3]) && isXDigit(line[4]) && isXDigit(line[5]) && line[6] == ':':
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.IGD = map[uint32]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) > 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 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, "============="):
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) {
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_hwC0D" + 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
}
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}
}

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

@ -0,0 +1,858 @@
/* 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
IGD map[uint32]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
}
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 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
}
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 KconfigStringUnquoted 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)
for strings.HasSuffix(result, ".") {
result = result[0 : len(result)-1]
}
return result
}
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 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
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
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, 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":
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.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 {
WriteDev(dt, offset+1, 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)
}
func (g GenericVGA) Scan(ctx Context, addr PCIDevData) {
KconfigString["VGA_BIOS_ID"] = fmt.Sprintf("%04x,%04x",
addr.PCIVenID,
addr.PCIDevID)
KconfigString["VGA_BIOS_FILE"] = fmt.Sprintf("pci%04x,%04x.rom",
addr.PCIVenID,
addr.PCIDevID)
PutPCIDevParent(addr, g.Comment, g.MissingParent)
}
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 # dummy\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 KconfigStringUnquoted {
keys = append(keys, name)
}
sort.Strings(keys)
for _, name := range keys {
fmt.Fprintf(kc, `
config %s%s
string
default %s
`, name, makeComment(name), KconfigStringUnquoted[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 := sanitize(ctx.Vendor)
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, 0x51, 0x52, 0x53}
}
return []uint8{0x50, 0x52, 0x51, 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.MoboID = sanitize(ctx.Vendor) + "/" + sanitize(ctx.Model)
ctx.KconfigName = "BOARD_" + strings.ToUpper(sanitize(ctx.Vendor)+"_"+sanitize(ctx.Model))
ctx.BaseDirectory = *FlagOutDir + MoboDir + ctx.MoboID
KconfigStringUnquoted["MAINBOARD_DIR"] = ctx.MoboID
KconfigString["MAINBOARD_PART_NUMBER"] = ctx.Model
os.MkdirAll(ctx.BaseDirectory, 0700)
makeVendor(ctx)
ScanRoot(ctx)
if len(ROMStageFiles) > 0 || len(RAMStageFiles) > 0 || len(SMMFiles) > 0 {
mf := Create(ctx, "Makefile.inc")
defer mf.Close()
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()
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(device_t dev)
{
` + MainboardInit + "}\n\n")
}
if MainboardInit != "" || MainboardEnable != "" {
mainboard.WriteString("static void mainboard_enable(device_t 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,
};
`)
}
}
at := Create(ctx, "acpi_tables.c")
defer at.Close()
at.WriteString("/* dummy */\n")
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)
}
rs := Create(ctx, "romstage.c")
defer rs.Close()
rs.WriteString("/* dummy file */\n")
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")
}
dsdt.WriteString(
`DefinitionBlock(
"dsdt.aml",
"DSDT",
0x03, // DSDT revision: ACPI v3.0
"COREv4", // OEM id
"COREBOOT", // OEM table id
0x20141018 // OEM revision
)
{
// Some generic macros
#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(`
Scope (\_SB) {
Device (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(
` }
}
}
`)
}

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

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

@ -0,0 +1,383 @@
# Porting coreboot using autoport
## Supported platforms
### Chipset.
For any sandybridge or ivybridge platform generated result should
be bootable, possibly with minor fixes.
### EC
EC support is likely to work on intel-based thinkpads. Other laptops are likely to miss EC support
## How to use
* Go into BIOS setup on target machine and enable all the device.
This will allow autoport to detect as much as possible
* Boot into target machine under GNU/Linux
* Make sure you have GCC and golang installed
* Grab coreboot tree
* Execute following commands starting from coreboot tree
cd util/ectool
make
cd ../inteltool
make
cd ../autoport
go build
./autoport --input_log=logs --make_logs --coreboot_dir=../..
* Look for output unknown PCI devices. E.g.
Unknown PCI device 8086:0085, assuming removable
If autoport says `assuming removable` then you're fine. If it doesn't
then you may want to add relevant PCIIDs to autoport. When rerunning
you can skip argument `--make_logs` to reuse the same logs
* At this point the new board is added to the tree but don't flash it
yet as it will brick you machine. Instead keep this new port and logs
from `util/autoport/logs` somewhere safe
* Disassemble your laptop and locate flash chip <http://flashrom.org/Technology>
is a great resource. Flash chip is usually in `SOIC-8` (2x4 pins) or `SOIC-16`
(2x8 chips). You'll probably have several candidates. Look up what's written on
them and look up what's this chip on the web.
* Once you know what's the chip is, get external flasher and read it. Twice. Compare
the results and retry if they differ. Save the result somewhere safe, in preference
copy to read-only storage as backup.
* Compile coreboot with console enabled (EHCI debug or serial if present are recommended)
* For recent intel chipsets you need to avoid overwriting ME firmware. Recommended procedure is
(replace 8 with your flash size in MiB):
cp backup.rom flash.rom
dd if=coreboot/build/coreboot.rom skip=$[8-1] seek=$[8-1] bs=1M of=flash.rom
* Flash the result
* Boot and grab the log and fix the issues. See next section for useful info.
* grep your board for FIXME. autoport adds comments when it's unsure. Sometimes it's just
a minor check and sometimes it needs more involvment. See next section.
* Send new board to review.coreboot.org. I mean it, your effort is very appreciated.
## Manual fixes
### SPD
If you're able to use full memory with any combination of inserted modules than this is
most likely correct. In order to initialize the memory coreboot needs to know RAM timings.
For socketed RAM it's stored in a small EEPROM chip which can be accessed through SPD. Unfortunately
mapping between SPD addresses and RAM slots differs and cannot always be detected automatically.
Resulting SPD map is encoded in function `mainboard_get_spd` in `early_southbridge.c`.
autoport uses the most common map `0x50, 0x51, 0x52, 0x53` except for lenovos which are
known to use `0x50, 0x52, 0x51, 0x53`. To detect the correct memory map the easiest way is with
vendor BIOS to boot with just one module in channel 0 slot 0 and then see where does it show
up in SPD. Under Linux you can see present SPD addresses with following commands:
phcoder@sid:~/coreboot/util/autoport$ sudo modprobe i2c-dev
phcoder@sid:~/coreboot/util/autoport$ 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
phcoder@sid:~/coreboot/util/autoport$ 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 `9` with whatever bus is marked as smbus. Here in an example
you see SPD at address `0x50`. Since we've booted with just the module in C0S0, so
the first entry in SPD map has to be `0x50`. Once you have SPD map your
`mainboard_get_spd` should look something like:
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);
}
You can and should omit lines which correspond to
slots not present on your machine.
This way works well if your RAM is socketed. For soldered RAM if you see
its SPD, you're in a luck and can proceed the same way although you may have to
guess some entries due to RAM not being removable.
Most cases of soldered RAM don't have EEPROM chip. In this case you'd have to create
fake SPD. Look in `inteltool.log`. You'll see something like:
/* 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 completely exact represantation of RAM
capablities as it lists only the mode currently used
and lacks minor info like serial number. Using `xxd`
you can create binary represantation of this SPD:
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
Then you can place this file into mainboard directory
and hook it up into build system by adding following
lines to `Makefile.inc` (creating a new file if needed)
cbfs-files-y += spd.bin
spd.bin-file := spd.bin
spd.bin-type := raw
And then make coreboot actually use this SPD. Following
example shows a hybrid situation with one module
soldered and another 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_get_file_content(CBFS_DEFAULT_MEDIA, "spd.bin", CBFS_TYPE_RAW,
&spd_file_len);
if (spd_file && spd_file_len >= 128)
memcpy(&spd[0], spd_file, 128);
/* C0S0 is a DIMM slot. */
read_spd(&spd[1], 0x51);
}
If several slots are soldered there are 3 ways of doing things:
* If all of them are the same use the same file. Don't forget to copy
it to all array elements.
* Use several files (recommended). Name them e.g. spd1, spd2,...
* Concatenate it into a single file and split into several
array elements on runtime. Not recommended
### `board_info.txt`
`board_info.txt` is a simple text file used to generate wiki page
listing supported boards. Some of the info can't be detected automatically.
As this is used only to present information to users then when it matches
your board and definitions it is correct.
* Which package is used for ROM and whether it's socketed, as well
as release year. For ROM package refer to <http://flashrom.org/Technology>
and compare it with ROM you found at the beginning of the port. Set
`ROM package`, `ROM socketed` and other variables mentioned in FIXME.
* Release year, just go to web and find that information.
* Category. It's difficult to make difference between desktop and similar
categories from inside the computer. Valid categories are:
* `desktop`. Desktops and workstations.
* `server`. Servers
* `laptop`. Laptops.
* `half`. Embedded / PC/104 / Half-size boards.
* `mini`. Mini-ITX / Micro-ITX / Nano-ITX
* `settop`. Set-top-boxes / Thin clients.
* `eval`. Devel/Eval 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. You probably shouldn't use
this.
### `USBDEBUG_HCD_INDEX`
Which controller the most easily accessible USB debug port is. On intel
1 is for `00:1d.0` and 2 is `00:1a.0` (yes, it's reversed). See
<http://www.coreboot.org/EHCI_Debug_Port> for more info.
If you're able to use EHCI debug port without setting HCD index manually
in config this is correct.
### `BOARD_ROMSIZE_KB_2048`
Default rom size should be detected automatically but sometimes isn't.
If yours weren't detected put correct rom size here to serve as sane
default when configuring coreboot.
If default ROM size when slecting the board is right one than this value
is correct.
### `DRAM_RESET_GATE_GPIO`
When machine is going to S3 in order not to reset the RAM modules, the
reset signal must be filtered out from reachin RAM. This is done by
powering down a special gate. Most manufacturers put this gate on
GPIO 60 but Lenovo is knowon to put it on GPIO 10. If you're able to
go to S3 and back than this value is correct.
## GNVS
`acpi_create_gnvs` sets values in GNVS which in turn is used by ACPI for
various power-related functions. Normally there is no need to modify it
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 adjust but if you miss some nonstandard output
you can declare it there. Bit 31 is set to indicate 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 no need to modify unless
your device has very special power saving requirements.
## `install_intel_vga_int15_handler`
This is used to configure int15 hook used by vga bios. Parameters 2 and 3 usually
shouldn't be modified as vgabios is quite ok to figure panel fit and active output
by itself. Last number is the numerical ID of display type. If you don't get any output
with VGABIOS you should try different values for 4th parameter. If it doesn't help
try different values for first parameter as well
## CMOS options
Due to horrible state of CMOS support in coreboot tree, autoport doesn't support it and
this probably won't change until 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
need to fill `h8_mainboard_init_dock` and add support code to
DSDT. See the code for `x60`, `x200` or `x201`
## EC (generic laptop)
Almost any laptop has an embedded controller. In nutshell it's a small
low-powered computer specialised on managing power for laptop. Exact
functionality differs between macines but of interest to us is mainly:
* 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

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

@ -0,0 +1,42 @@
package main
import "fmt"
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)
}
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,159 @@
package main
type sandybridgemc struct {
variant string
}
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 those values*/
install_intel_vga_int15_handler(GMA_INT15_ACTIVE_LFP_INT_LVDS, GMA_INT15_PANEL_FIT_DEFAULT, GMA_INT15_BOOT_DISPLAY_DEFAULT, 0);
`)
pchLVDS := inteltool.IGD[0xe1180]
dualChannel := pchLVDS&(3<<2) == (3 << 2)
pipe := (pchLVDS >> 30) & 1
data_m1 := inteltool.IGD[0x60030+0x1000*pipe] & 0xffffff
data_n1 := inteltool.IGD[0x60034+0x1000*pipe]
link_m1 := inteltool.IGD[0x60040+0x1000*pipe]
link_n1 := inteltool.IGD[0x60044+0x1000*pipe]
data_factor := float32(data_m1) / float32(data_n1)
link_factor := float32(link_m1) / float32(link_n1)
num_lanes := uint32((link_factor/data_factor)*18.0/8.0 + 0.5)
fp0 := inteltool.IGD[0xc6040+8*pipe]
dpll := inteltool.IGD[0xc6014+4*pipe]
pixel_m2 := fp0 & 0xff
pixel_m1 := (fp0>>8)&0xff + 2
pixel_p1 := uint32(1)
for i := dpll & 0x1ffff; i != 0 && i&1 == 0; i >>= 1 {
pixel_p1++
}
pixel_n := ((fp0 >> 16) & 0xff) + 2
pixel_frequency := float32(120000*(5*pixel_m1+pixel_m2)) / float32(pixel_n*pixel_p1*7.0)
if !dualChannel {
pixel_frequency /= 2
}
link_frequency := pixel_frequency / link_factor
DevTree = DevTreeNode{
Chip: "northbridge/intel/sandybridge",
MissingParent: "northbridge",
Comment: "FIXME: check gfx.ndid and gfx.did",
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.use_spread_spectrum_clock": FormatBool((inteltool.IGD[0xc6200]>>12)&1 != 0),
"gfx.lvds_dual_channel": FormatBool(dualChannel),
"gfx.lvds_num_lanes": FormatInt32(num_lanes),
"gfx.link_frequency_270_mhz": FormatBool(link_frequency > 200000),
/* FIXME:XX hardcoded. */
"gfx.ndid": "3",
"gfx.did": "{ 0x80000100, 0x80000240, 0x80000410, 0x80000410, 0x00000005 }",
},
Children: []DevTreeNode{
{
Chip: "cpu_cluster",
Dev: 0,
Children: []DevTreeNode{
{
Chip: "cpu/intel/socket_rPGA989",
Children: []DevTreeNode{
{
Chip: "lapic",
Dev: 0,
},
},
},
{
Chip: "cpu/intel/model_206ax",
Comment: "FIXME: check all registers",
Registers: map[string]string{
/* FIXME:XX hardcoded. */
"c1_acpower": "1",
"c2_acpower": "3",
"c3_acpower": "5",
"c1_battery": "1",
"c2_battery": "3",
"c3_battery": "5",
},
Children: []DevTreeNode{
{
Chip: "lapic",
Dev: 0xacac,
Disabled: true,
},
},
},
},
},
{
Chip: "domain",
Dev: 0,
PCIController: true,
ChildPCIBus: 0,
PCISlots: []PCISlot{
PCISlot{PCIAddr: PCIAddr{Dev: 0x0, Func: 0}, writeEmpty: true, additionalComment: "Host bridge"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x1, Func: 0}, writeEmpty: true, additionalComment: "PCIe Bridge for discrete graphics"},
PCISlot{PCIAddr: PCIAddr{Dev: 0x2, Func: 0}, writeEmpty: true, additionalComment: "Internal graphics"},
},
},
},
}
PutPCIDev(addr, "Host bridge")
/* FIXME:XX Move part to northbridge? */
/* FIXME:XX some configs are unsupported. */
KconfigBool["MAINBOARD_HAS_NATIVE_VGA_INIT"] = true
KconfigBool["MAINBOARD_HAS_NATIVE_VGA_INIT_TEXTMODECFG"] = true
KconfigBool[i.variant+"BRIDGE_LVDS"] = true
KconfigBool["VGA"] = true
KconfigBool["INTEL_EDID"] = true
KconfigBool["CPU_INTEL_SOCKET_RPGA989"] = true
KconfigBool["NORTHBRIDGE_INTEL_"+i.variant+"BRIDGE_NATIVE"] = true
KconfigBool["INTEL_INT15"] = true
KconfigBool["HAVE_ACPI_TABLES"] = true
KconfigBool["HAVE_ACPI_RESUME"] = true
KconfigBool["HAVE_IFD_BIN"] = false
KconfigBool["HAVE_ME_BIN"] = false
KconfigHex["MMCONF_BASE_ADDRESS"] = 0xf0000000
KconfigInt["MAX_CPUS"] = 8
DSDTIncludes = append(DSDTIncludes, DSDTInclude{
File: "cpu/intel/model_206ax/acpi/cpu.asl",
})
DSDTPCI0Includes = append(DSDTPCI0Includes, DSDTInclude{
File: "northbridge/intel/sandybridge/acpi/sandybridge.asl",
})
}
func init() {
RegisterPCI(0x8086, 0x0100, sandybridgemc{variant: "SANDY"})
RegisterPCI(0x8086, 0x0104, sandybridgemc{variant: "SANDY"})
RegisterPCI(0x8086, 0x0150, sandybridgemc{variant: "IVY"})
RegisterPCI(0x8086, 0x0154, sandybridgemc{variant: "IVY"})
for _, id := range []uint16{
0x0102, 0x0106, 0x010a, 0x0112,
0x0116, 0x0122, 0x0126, 0x0156,
0x0166,
} {
RegisterPCI(0x8086, id, GenericVGA{GenericPCI{Comment: "VGA controller"}})
}
}