/* SPDX-License-Identifier: GPL-2.0-only */ #include <device/device.h> #include <arch/io.h> #include <delay.h> #include "dock.h" #include <superio/nsc/pc87384/pc87384.h> #include "ec/acpi/ec.h" #include "ec/lenovo/pmh7/pmh7.h" #include <southbridge/intel/i82801gx/i82801gx.h> #define DLPC_CONTROL 0x164c static void dlpc_write_register(int reg, int value) { outb(reg, 0x164e); outb(value, 0x164f); } static u8 dlpc_read_register(int reg) { outb(reg, 0x164e); return inb(0x164f); } static void dock_write_register(int reg, int value) { outb(reg, 0x2e); outb(value, 0x2f); } static u8 dock_read_register(int reg) { outb(reg, 0x2e); return inb(0x2f); } static void dlpc_gpio_set_mode(int port, int mode) { dlpc_write_register(0xf0, port); dlpc_write_register(0xf1, mode); } static void dock_gpio_set_mode(int port, int mode, int irq) { dock_write_register(0xf0, port); dock_write_register(0xf1, mode); dock_write_register(0xf2, irq); } static void dlpc_gpio_init(void) { /* Select GPIO module */ dlpc_write_register(0x07, 0x07); /* GPIO Base Address 0x1680 */ dlpc_write_register(0x60, 0x16); dlpc_write_register(0x61, 0x80); /* Activate GPIO */ dlpc_write_register(0x30, 0x01); dlpc_gpio_set_mode(0x00, 3); dlpc_gpio_set_mode(0x01, 3); dlpc_gpio_set_mode(0x02, 0); dlpc_gpio_set_mode(0x03, 3); dlpc_gpio_set_mode(0x04, 4); dlpc_gpio_set_mode(0x20, 4); dlpc_gpio_set_mode(0x21, 4); dlpc_gpio_set_mode(0x23, 4); } int dlpc_init(void) { int timeout = 1000; /* Enable 14.318MHz CLK on CLKIN */ dlpc_write_register(0x29, 0xa0); while (!(dlpc_read_register(0x29) & 0x10) && timeout--) udelay(1000); if (!timeout) return 1; /* Select DLPC module */ dlpc_write_register(0x07, 0x19); /* DLPC Base Address */ dlpc_write_register(0x60, (DLPC_CONTROL >> 8) & 0xff); dlpc_write_register(0x61, DLPC_CONTROL & 0xff); /* Activate DLPC */ dlpc_write_register(0x30, 0x01); /* Reset docking state */ outb(0x00, DLPC_CONTROL); dlpc_gpio_init(); return 0; } static int dock_superio_init(void) { int timeout = 1000; /* startup 14.318MHz Clock */ dock_write_register(0x29, 0xa0); /* wait until clock is settled */ while (!(dock_read_register(0x29) & 0x10) && timeout--) udelay(1000); if (!timeout) return 1; /* set GPIO pins to Serial/Parallel Port * functions */ dock_write_register(0x22, 0xa9); /* enable serial port */ dock_write_register(0x07, PC87384_SP1); dock_write_register(0x30, 0x01); dock_write_register(0x07, PC87384_GPIO); dock_write_register(0x60, 0x16); dock_write_register(0x61, 0x20); /* enable GPIO */ dock_write_register(0x30, 0x01); dock_gpio_set_mode(0x00, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP, 0x00); dock_gpio_set_mode(0x01, PC87384_GPIO_PIN_TYPE_PUSH_PULL | PC87384_GPIO_PIN_OE, 0x00); dock_gpio_set_mode(0x02, PC87384_GPIO_PIN_TYPE_PUSH_PULL | PC87384_GPIO_PIN_OE, 0x00); dock_gpio_set_mode(0x03, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP, 0x00); dock_gpio_set_mode(0x04, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP, 0x00); dock_gpio_set_mode(0x05, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP, 0x00); dock_gpio_set_mode(0x06, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP, 0x00); dock_gpio_set_mode(0x07, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP, 0x00); /* no GPIO events enabled for PORT0 */ outb(0x00, 0x1622); /* clear GPIO events on PORT0 */ outb(0xff, 0x1623); outb(0xff, 0x1624); /* no GPIO events enabled for PORT1 */ outb(0x00, 0x1626); /* clear GPIO events on PORT1*/ outb(0xff, 0x1627); outb(0x1F, 0x1628); outb(0xfd, 0x1620); return 0; } int dock_connect(void) { int timeout = 1000; outb(0x07, DLPC_CONTROL); timeout = 1000; while (!(inb(DLPC_CONTROL) & 8) && timeout--) udelay(1000); if (!timeout) { /* docking failed, disable DLPC switch */ outb(0x00, DLPC_CONTROL); dlpc_write_register(0x30, 0x00); return 1; } /* Assert D_PLTRST# */ outb(0xfe, 0x1680); udelay(1000); /* Deassert D_PLTRST# */ outb(0xff, 0x1680); udelay(10000); return dock_superio_init(); } void dock_disconnect(void) { /* disconnect LPC bus */ outb(0x00, DLPC_CONTROL); /* Assert PLTRST and DLPCPD */ outb(0xfc, 0x1680); } int dock_present(void) { return pmh7_register_read(0x61) & 1; } int legacy_io_present(void) { return !(inb(DEFAULT_GPIOBASE + 0x0c) & 0x40); } void legacy_io_init(void) { /* Enable Power for Ultrabay slot */ pmh7_ultrabay_power_enable(1); udelay(100000); dock_superio_init(); }