diff options
-rw-r--r-- | src/ec/lenovo/h8/h8.h | 2 | ||||
-rw-r--r-- | src/mainboard/lenovo/t400/Kconfig | 3 | ||||
-rw-r--r-- | src/mainboard/lenovo/t400/Makefile.inc | 2 | ||||
-rw-r--r-- | src/mainboard/lenovo/t400/devicetree.cb | 30 | ||||
-rw-r--r-- | src/mainboard/lenovo/t400/dock.c | 230 | ||||
-rw-r--r-- | src/mainboard/lenovo/t400/romstage.c | 4 |
6 files changed, 244 insertions, 27 deletions
diff --git a/src/ec/lenovo/h8/h8.h b/src/ec/lenovo/h8/h8.h index c721d38234..42d279fc6f 100644 --- a/src/ec/lenovo/h8/h8.h +++ b/src/ec/lenovo/h8/h8.h @@ -28,9 +28,7 @@ int h8_ultrabay_device_present(void); u8 h8_build_id_and_function_spec_version(char *buf, u8 buf_len); void h8_usb_always_on(void); -#if !IS_ENABLED (CONFIG_H8_DOCK_EARLY_INIT) void h8_mainboard_init_dock (void); -#endif /* EC registers */ #define H8_CONFIG0 0x00 diff --git a/src/mainboard/lenovo/t400/Kconfig b/src/mainboard/lenovo/t400/Kconfig index 8eb1a57178..031e9e8e1c 100644 --- a/src/mainboard/lenovo/t400/Kconfig +++ b/src/mainboard/lenovo/t400/Kconfig @@ -8,7 +8,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy select SOUTHBRIDGE_INTEL_I82801IX select EC_LENOVO_PMH7 select EC_LENOVO_H8 - select NO_UART_ON_SUPERIO + select H8_DOCK_EARLY_INIT select BOARD_ROMSIZE_KB_8192 select DRIVERS_GENERIC_IOAPIC select HAVE_MP_TABLE @@ -21,6 +21,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy select MAINBOARD_HAS_NATIVE_VGA_INIT_TEXTMODECFG select INTEL_INT15 select SUPERIO_NSC_PC87382 + select SUPERIO_NSC_PC87384 config MAINBOARD_DIR string diff --git a/src/mainboard/lenovo/t400/Makefile.inc b/src/mainboard/lenovo/t400/Makefile.inc index c02a70b86b..3aa94b59d2 100644 --- a/src/mainboard/lenovo/t400/Makefile.inc +++ b/src/mainboard/lenovo/t400/Makefile.inc @@ -13,6 +13,8 @@ ## GNU General Public License for more details. ## +romstage-y += dock.c + ramstage-y += dock.c ramstage-y += cstates.c romstage-y += hybrid_graphics.c diff --git a/src/mainboard/lenovo/t400/devicetree.cb b/src/mainboard/lenovo/t400/devicetree.cb index 46cca1e23f..1adba8b17d 100644 --- a/src/mainboard/lenovo/t400/devicetree.cb +++ b/src/mainboard/lenovo/t400/devicetree.cb @@ -181,13 +181,29 @@ chip northbridge/intel/gm45 end chip superio/nsc/pc87382 - device pnp 164e.3 off end - # IR, not connected - device pnp 164e.2 off end - # GPIO, not connected - device pnp 164e.7 off end - # DLPC, not connected - device pnp 164e.19 off end + device pnp 164e.2 off end # IR + device pnp 164e.3 off end # Serial Port + device pnp 164e.7 on # GPIO + io 0x60 = 0x1680 + end + device pnp 164e.19 on # DLPC + io 0x60 = 0x164c + end + end + + chip superio/nsc/pc87384 + device pnp 2e.1 on # Parallel Port + io 0x60 = 0x3bc + irq 0x70 = 7 + end + device pnp 2e.2 off end # Serial Port / IR + device pnp 2e.3 on # Serial Port + io 0x60 = 0x3f8 + irq 0x70 = 4 + end + device pnp 2e.7 on # GPIO + io 0x60 = 0x1620 + end end end diff --git a/src/mainboard/lenovo/t400/dock.c b/src/mainboard/lenovo/t400/dock.c index 836ff35c07..a3412bf4d0 100644 --- a/src/mainboard/lenovo/t400/dock.c +++ b/src/mainboard/lenovo/t400/dock.c @@ -20,41 +20,237 @@ #include <arch/io.h> #include <device/device.h> #include <device/pci.h> +#include <device/pnp.h> #include <delay.h> #include "dock.h" +#include <superio/nsc/pc87382/pc87382.h> + #include "southbridge/intel/i82801ix/i82801ix.h" #include "ec/lenovo/h8/h8.h" #include <ec/acpi/ec.h> -#define LPC_DEV PCI_DEV(0, 0x1f, 0) +struct pin_config { + u8 port; + u8 mode; +}; -void h8_mainboard_init_dock (void) +static int poll_clk_stable(pnp_devfn_t dev) { - if (dock_present()) { - printk(BIOS_DEBUG, "dock is connected\n"); - dock_connect(); - } else - printk(BIOS_DEBUG, "dock is not connected\n"); + int timeout = 1000; + + /* Enable 14.318MHz CLK on CLKIN */ + pnp_write_config(dev, 0x29, 0xa0); + while(!(pnp_read_config(dev, 0x29) & 0x10) && timeout--) + udelay(1000); + if (!timeout) + return 1; + + return 0; } +static int gpio_init(pnp_devfn_t gpio, u16 gpio_base, + const struct pin_config pincfg[], int num_cfgs) +{ + int i; + + /* Enable GPIO LDN. */ + pnp_set_logical_device(gpio); + pnp_set_iobase(gpio, PNP_IDX_IO0, gpio_base); + pnp_set_enable(gpio, 1); + + for (i=0; i < num_cfgs; i++) { + pnp_write_config(gpio, 0xf0, pincfg[i].port); + pnp_write_config(gpio, 0xf1, pincfg[i].mode); + pnp_write_config(gpio, 0xf2, 0x0); + } + return 0; +} + +static const pnp_devfn_t l_dlpc = PNP_DEV(0x164e, PC87382_DOCK); +static const pnp_devfn_t l_gpio = PNP_DEV(0x164e, PC87382_GPIO); + +#define DLPC_CONTROL 0x164c +#define DLPC_GPIO_BASE 0x1680 + +#define DLPC_GPDO0 (DLPC_GPIO_BASE + 0x0) +#define DLPC_GPDI0 (DLPC_GPIO_BASE + 0x1) +#define D_PLTRST 0x01 +#define D_LPCPD 0x02 + +#define DLPC_GPDO2 (DLPC_GPIO_BASE + 0x8) +#define DLPC_GPDI2 (DLPC_GPIO_BASE + 0x9) + +static int pc87382_init(pnp_devfn_t dlpc, u16 dlpc_base) +{ + int timeout = 1000; + + /* Enable LPC bridge LDN. */ + pnp_set_logical_device(dlpc); + pnp_set_iobase(dlpc, PNP_IDX_IO0, dlpc_base); + pnp_set_enable(dlpc, 1); + + /* Reset docking state */ + outb(0x00, dlpc_base); + outb(0x07, dlpc_base); + while(!(inb(dlpc_base) & 8) && timeout--) + udelay(1000); + if (!timeout) + return 1; + + return 0; +} + +static void pc87382_close(pnp_devfn_t dlpc) +{ + pnp_set_logical_device(dlpc); + + /* Disconnect LPC bus */ + u16 dlpc_base = pnp_read_iobase(dlpc, PNP_IDX_IO0); + outb(0x00, dlpc_base); + pnp_set_enable(dlpc, 0); +} + +static const struct pin_config local_gpio[] = { + {0x00, 3}, {0x01, 3}, {0x02, 0}, {0x03, 3}, + {0x04, 4}, {0x20, 4}, {0x21, 4}, {0x23, 4}, +}; + +static int pc87382_connect(void) +{ + u8 reg; + + if (poll_clk_stable(l_gpio) != 0) + return 1; + + if (gpio_init(l_gpio, DLPC_GPIO_BASE, + local_gpio, ARRAY_SIZE(local_gpio)) != 0) { + return 1; + } + + reg = inb(DLPC_GPDO0); + reg |= D_PLTRST | D_LPCPD; + /* Deassert D_PLTRST# and D_LPCPD# */ + outb(reg, DLPC_GPDO0); + + if (pc87382_init(l_dlpc, DLPC_CONTROL) != 0) { + return 1; + } + + /* Assert D_PLTRST# */ + reg &= ~D_PLTRST; + outb(reg, DLPC_GPDO0); + udelay(1000); + + /* Deassert D_PLTRST# */ + reg |= D_PLTRST; + outb(reg, DLPC_GPDO0); + udelay(10000); + + return 0; +} + +static void pc87382_disconnect(void) +{ + pc87382_close(l_dlpc); + + /* Assert D_PLTRST# and D_LPCPD# */ + u8 reg = inb(DLPC_GPDO0); + reg &= ~(D_PLTRST | D_LPCPD); + outb(reg, DLPC_GPDO0); +} + +static u8 dock_identify(void) +{ + u8 id; + + id = (inb(DLPC_GPDI0) >> 4) & 1; + id |= (inb(DLPC_GPDI2) & 3) << 1; + + return id; +} + +/* Docking station side. */ + +#include <superio/nsc/pc87384/pc87384.h> + +static const pnp_devfn_t r_gpio = PNP_DEV(0x2e, PC87384_GPIO); +static const pnp_devfn_t r_serial = PNP_DEV(0x2e, PC87384_SP1); + +#define DOCK_GPIO_BASE 0x1620 + +static const struct pin_config remote_gpio[] = { + {0x00, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP}, + {0x01, PC87384_GPIO_PIN_TYPE_PUSH_PULL | PC87384_GPIO_PIN_OE}, + {0x02, PC87384_GPIO_PIN_TYPE_PUSH_PULL | PC87384_GPIO_PIN_OE}, + {0x03, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP}, + {0x04, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP}, + {0x05, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP}, + {0x06, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP}, + {0x07, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP}, +}; + +static int pc87384_init(void) +{ + if (poll_clk_stable(r_gpio) != 0) + return 1; + + /* set GPIO pins to Serial/Parallel Port + * functions + */ + pnp_write_config(r_gpio, 0x22, 0xa9); + + /* enable serial port */ + pnp_set_logical_device(r_serial); + pnp_set_iobase(r_serial, PNP_IDX_IO0, 0x3f8); + pnp_set_enable(r_serial, 1); + + if (gpio_init(r_gpio, DOCK_GPIO_BASE, + remote_gpio, ARRAY_SIZE(remote_gpio)) != 0) + return 1; + + /* no GPIO events enabled for PORT0 */ + outb(0x00, DOCK_GPIO_BASE + 0x02); + /* clear GPIO events on PORT0 */ + outb(0xff, DOCK_GPIO_BASE + 0x03); + outb(0xff, DOCK_GPIO_BASE + 0x04); + + /* no GPIO events enabled for PORT1 */ + outb(0x00, DOCK_GPIO_BASE + 0x06); + /* clear GPIO events on PORT1*/ + outb(0xff, DOCK_GPIO_BASE + 0x07); + outb(0x1f, DOCK_GPIO_BASE + 0x08); + + outb(0xfd, DOCK_GPIO_BASE + 0x00); + + return 0; +} + +/* Mainboard */ + void dock_connect(void) { - u16 gpiobase = pci_read_config16(LPC_DEV, D31F0_GPIO_BASE) & 0xfffc; - ec_set_bit(0x02, 0); - outl(inl(gpiobase + 0x0c) | (1 << 28), gpiobase + 0x0c); + if (dock_identify() == 0) + return; + + if (pc87382_connect() != 0) { + pc87382_disconnect(); + return; + } + pc87384_init(); } void dock_disconnect(void) { - u16 gpiobase = pci_read_config16(LPC_DEV, D31F0_GPIO_BASE) & 0xfffc; - ec_clr_bit(0x02, 0); - outl(inl(gpiobase + 0x0c) & ~(1 << 28), gpiobase + 0x0c); + pc87382_disconnect(); } -int dock_present(void) +void h8_mainboard_init_dock(void) { - u16 gpiobase = pci_read_config16(LPC_DEV, D31F0_GPIO_BASE) & 0xfffc; - u8 st = inb(gpiobase + 0x0c); + u8 id = dock_identify(); - return ((st >> 2) & 7) != 7; + if (id != 0) { + printk(BIOS_DEBUG, "dock (id=%d) is present\n", id); + dock_connect(); + } else + printk(BIOS_DEBUG, "dock is not connected\n"); } diff --git a/src/mainboard/lenovo/t400/romstage.c b/src/mainboard/lenovo/t400/romstage.c index 2a3a770bee..31a80a4a19 100644 --- a/src/mainboard/lenovo/t400/romstage.c +++ b/src/mainboard/lenovo/t400/romstage.c @@ -31,6 +31,7 @@ #include <console/console.h> #include <southbridge/intel/i82801ix/i82801ix.h> #include <northbridge/intel/gm45/gm45.h> +#include "dock.h" #include "gpio.h" #define LPC_DEV PCI_DEV(0, 0x1f, 0) @@ -68,6 +69,9 @@ void mainboard_romstage_entry(unsigned long bist) /* First, run everything needed for console output. */ i82801ix_early_init(); early_lpc_setup(); + + dock_connect(); + console_init(); printk(BIOS_DEBUG, "running main(bist = %lu)\n", bist); |