summaryrefslogtreecommitdiff
path: root/src/mainboard/lenovo/t400
diff options
context:
space:
mode:
authorKyösti Mälkki <kyosti.malkki@gmail.com>2017-01-08 09:07:14 +0200
committerKyösti Mälkki <kyosti.malkki@gmail.com>2017-03-11 13:31:41 +0100
commit9ab5adbde4c3eb820e5f88db765fac33eb30bd97 (patch)
tree7bf5512734226490ccfbb0ac728b0461b9ddc961 /src/mainboard/lenovo/t400
parentf3d07f274e5084bf6958b0fbcbd3cb2bfd2c998e (diff)
downloadcoreboot-9ab5adbde4c3eb820e5f88db765fac33eb30bd97.tar.xz
lenovo/t400: Rewrite dock from t60
Old dock.c copied from x201 was incorrect. Do a rewrite of t60 dock code as pnp devices. Fixes USB and serial on the dock, if it is already connected when computer is powered on. DVI and ethernet worked without this patch. Hot-plug is yet to be fixed. Change-Id: Ib20a0eff10d0cde92dd089baf4fca28b117dc999 Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com> Reviewed-on: https://review.coreboot.org/18054 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Rudolph <siro@das-labor.org>
Diffstat (limited to 'src/mainboard/lenovo/t400')
-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
5 files changed, 244 insertions, 25 deletions
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);