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>
This commit is contained in:
Kyösti Mälkki 2017-01-08 09:07:14 +02:00
parent f3d07f274e
commit 9ab5adbde4
6 changed files with 244 additions and 27 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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