summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ec/lenovo/h8/h8.h2
-rw-r--r--src/mainboard/lenovo/t400/Kconfig3
-rw-r--r--src/mainboard/lenovo/t400/Makefile.inc2
-rw-r--r--src/mainboard/lenovo/t400/devicetree.cb30
-rw-r--r--src/mainboard/lenovo/t400/dock.c230
-rw-r--r--src/mainboard/lenovo/t400/romstage.c4
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);