blob: b8163e710be5005856a9be96433b0e963a1226f0 [file] [log] [blame]
Angel Pons89ab2502020-04-03 01:22:28 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Timothy Pearson4b373c92015-04-05 17:54:08 -05002
3#define __SIMPLE_DEVICE__
4#include <console/console.h>
5#include <arch/io.h>
Kyösti Mälkki3855c012019-03-03 08:45:19 +02006#include <device/pnp_ops.h>
Timothy Pearson4b373c92015-04-05 17:54:08 -05007#include <device/device.h>
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +02008#include <device/pnp.h>
Timothy Pearson4b373c92015-04-05 17:54:08 -05009#include <delay.h>
10#include "dock.h"
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020011#include <superio/nsc/pc87382/pc87382.h>
12
Elyes HAOUAS4ad14462018-06-16 18:29:33 +020013#include <southbridge/intel/i82801ix/i82801ix.h>
Peter Lemenkovc8ed32e2018-11-05 19:54:57 +010014#include <ec/lenovo/h8/h8.h>
Timothy Pearson4b373c92015-04-05 17:54:08 -050015#include <ec/acpi/ec.h>
16
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020017struct pin_config {
18 u8 port;
19 u8 mode;
20};
Timothy Pearson4b373c92015-04-05 17:54:08 -050021
Patrick Rudolphc0a16252018-10-30 17:38:13 +010022static int poll_clk_stable(pnp_devfn_t dev, int timeout)
Timothy Pearson4b373c92015-04-05 17:54:08 -050023{
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020024 /* Enable 14.318MHz CLK on CLKIN */
25 pnp_write_config(dev, 0x29, 0xa0);
Elyes HAOUAS6aa9d662020-08-04 13:20:13 +020026 while (!(pnp_read_config(dev, 0x29) & 0x10) && timeout--)
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020027 udelay(1000);
28 if (!timeout)
29 return 1;
30
31 return 0;
Timothy Pearson4b373c92015-04-05 17:54:08 -050032}
33
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020034static int gpio_init(pnp_devfn_t gpio, u16 gpio_base,
35 const struct pin_config pincfg[], int num_cfgs)
36{
37 int i;
38
39 /* Enable GPIO LDN. */
40 pnp_set_logical_device(gpio);
41 pnp_set_iobase(gpio, PNP_IDX_IO0, gpio_base);
42 pnp_set_enable(gpio, 1);
43
Paul Menzel4eb155c2017-03-11 15:11:36 +010044 for (i = 0; i < num_cfgs; i++) {
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020045 pnp_write_config(gpio, 0xf0, pincfg[i].port);
46 pnp_write_config(gpio, 0xf1, pincfg[i].mode);
47 pnp_write_config(gpio, 0xf2, 0x0);
48 }
49 return 0;
50}
51
52static const pnp_devfn_t l_dlpc = PNP_DEV(0x164e, PC87382_DOCK);
53static const pnp_devfn_t l_gpio = PNP_DEV(0x164e, PC87382_GPIO);
54
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020055static int pc87382_init(pnp_devfn_t dlpc, u16 dlpc_base)
56{
Patrick Rudolphc0a16252018-10-30 17:38:13 +010057 /* Maximum 3300 LCLKs at 14.318MHz */
58 int timeout = 230;
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020059
60 /* Enable LPC bridge LDN. */
61 pnp_set_logical_device(dlpc);
62 pnp_set_iobase(dlpc, PNP_IDX_IO0, dlpc_base);
63 pnp_set_enable(dlpc, 1);
64
65 /* Reset docking state */
66 outb(0x00, dlpc_base);
67 outb(0x07, dlpc_base);
Paul Menzel4eb155c2017-03-11 15:11:36 +010068 while (!(inb(dlpc_base) & 8) && timeout--)
Patrick Rudolphc0a16252018-10-30 17:38:13 +010069 udelay(1);
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020070 if (!timeout)
71 return 1;
72
73 return 0;
74}
75
76static void pc87382_close(pnp_devfn_t dlpc)
77{
78 pnp_set_logical_device(dlpc);
79
80 /* Disconnect LPC bus */
81 u16 dlpc_base = pnp_read_iobase(dlpc, PNP_IDX_IO0);
Patrick Rudolphc0a16252018-10-30 17:38:13 +010082 if (dlpc_base) {
83 outb(0x00, dlpc_base);
84 pnp_set_enable(dlpc, 0);
85 }
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020086}
87
88static const struct pin_config local_gpio[] = {
Paul Menzel4eb155c2017-03-11 15:11:36 +010089 {0x00, 3}, {0x01, 3}, {0x02, 0}, {0x03, 3},
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020090 {0x04, 4}, {0x20, 4}, {0x21, 4}, {0x23, 4},
91};
92
Patrick Rudolphc0a16252018-10-30 17:38:13 +010093/* Enable internal clock and configure GPIO LDN */
94int pc87382_early(void)
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020095{
Patrick Rudolphc0a16252018-10-30 17:38:13 +010096 /* Wake-up time is 33 msec (maximum). */
97 if (poll_clk_stable(l_gpio, 33) != 0)
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +020098 return 1;
99
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100100 /* Set up GPIOs */
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200101 if (gpio_init(l_gpio, DLPC_GPIO_BASE,
102 local_gpio, ARRAY_SIZE(local_gpio)) != 0) {
103 return 1;
104 }
105
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100106 return 0;
107}
108
109static int pc87382_connect(void)
110{
111 u8 reg;
112
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200113 reg = inb(DLPC_GPDO0);
114 reg |= D_PLTRST | D_LPCPD;
115 /* Deassert D_PLTRST# and D_LPCPD# */
116 outb(reg, DLPC_GPDO0);
117
Paul Menzel4eb155c2017-03-11 15:11:36 +0100118 if (pc87382_init(l_dlpc, DLPC_CONTROL) != 0)
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200119 return 1;
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200120
121 /* Assert D_PLTRST# */
122 reg &= ~D_PLTRST;
123 outb(reg, DLPC_GPDO0);
124 udelay(1000);
125
126 /* Deassert D_PLTRST# */
127 reg |= D_PLTRST;
128 outb(reg, DLPC_GPDO0);
Paul Menzel4eb155c2017-03-11 15:11:36 +0100129 mdelay(10);
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200130
131 return 0;
132}
133
134static void pc87382_disconnect(void)
135{
136 pc87382_close(l_dlpc);
137
138 /* Assert D_PLTRST# and D_LPCPD# */
139 u8 reg = inb(DLPC_GPDO0);
140 reg &= ~(D_PLTRST | D_LPCPD);
141 outb(reg, DLPC_GPDO0);
142}
143
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100144/* Returns 3bit dock id */
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200145static u8 dock_identify(void)
146{
147 u8 id;
148
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100149 /* Make sure GPIO LDN is configured first ! */
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200150 id = (inb(DLPC_GPDI0) >> 4) & 1;
151 id |= (inb(DLPC_GPDI2) & 3) << 1;
152
153 return id;
154}
155
156/* Docking station side. */
157
158#include <superio/nsc/pc87384/pc87384.h>
159
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100160static const pnp_devfn_t r_gpio = PNP_DEV(SUPERIO_DEV, PC87384_GPIO);
161static const pnp_devfn_t r_serial = PNP_DEV(SUPERIO_DEV, PC87384_SP1);
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200162
163static const struct pin_config remote_gpio[] = {
164 {0x00, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP},
165 {0x01, PC87384_GPIO_PIN_TYPE_PUSH_PULL | PC87384_GPIO_PIN_OE},
166 {0x02, PC87384_GPIO_PIN_TYPE_PUSH_PULL | PC87384_GPIO_PIN_OE},
167 {0x03, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP},
168 {0x04, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP},
169 {0x05, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP},
170 {0x06, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP},
171 {0x07, PC87384_GPIO_PIN_DEBOUNCE | PC87384_GPIO_PIN_PULLUP},
172};
173
174static int pc87384_init(void)
175{
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100176 if (poll_clk_stable(r_gpio, 1000) != 0)
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200177 return 1;
178
179 /* set GPIO pins to Serial/Parallel Port
180 * functions
181 */
182 pnp_write_config(r_gpio, 0x22, 0xa9);
183
184 /* enable serial port */
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100185
186 if (CONFIG_TTYS0_BASE > 0) {
187 pnp_set_logical_device(r_serial);
188 pnp_set_iobase(r_serial, PNP_IDX_IO0, CONFIG_TTYS0_BASE);
189 pnp_set_enable(r_serial, 1);
190 }
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200191
192 if (gpio_init(r_gpio, DOCK_GPIO_BASE,
193 remote_gpio, ARRAY_SIZE(remote_gpio)) != 0)
194 return 1;
195
196 /* no GPIO events enabled for PORT0 */
197 outb(0x00, DOCK_GPIO_BASE + 0x02);
198 /* clear GPIO events on PORT0 */
199 outb(0xff, DOCK_GPIO_BASE + 0x03);
200 outb(0xff, DOCK_GPIO_BASE + 0x04);
201
202 /* no GPIO events enabled for PORT1 */
203 outb(0x00, DOCK_GPIO_BASE + 0x06);
204 /* clear GPIO events on PORT1*/
205 outb(0xff, DOCK_GPIO_BASE + 0x07);
206 outb(0x1f, DOCK_GPIO_BASE + 0x08);
207
208 outb(0xfd, DOCK_GPIO_BASE + 0x00);
209
210 return 0;
211}
212
213/* Mainboard */
214
Timothy Pearson4b373c92015-04-05 17:54:08 -0500215void dock_connect(void)
216{
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100217 const u8 id = dock_identify();
218
219 /* Dock type 2505 doesn't have serial, LPT port or LEDs */
220 if (id == DOCK_TYPE_NONE || id == DOCK_TYPE_2505)
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200221 return;
222
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100223 if (pc87382_connect() != 0 || pc87384_init() != 0) {
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200224 pc87382_disconnect();
225 return;
226 }
Patrick Rudolphcfc73952017-06-25 09:03:37 +0200227
228 ec_write(H8_LED_CONTROL,
229 H8_LED_CONTROL_OFF | H8_LED_CONTROL_DOCK_LED1);
230 ec_write(H8_LED_CONTROL,
231 H8_LED_CONTROL_ON | H8_LED_CONTROL_DOCK_LED2);
Timothy Pearson4b373c92015-04-05 17:54:08 -0500232}
233
234void dock_disconnect(void)
235{
Kyösti Mälkki9ab5adb2017-01-08 09:07:14 +0200236 pc87382_disconnect();
Patrick Rudolphcfc73952017-06-25 09:03:37 +0200237
238 ec_write(H8_LED_CONTROL,
239 H8_LED_CONTROL_OFF | H8_LED_CONTROL_DOCK_LED1);
240 ec_write(H8_LED_CONTROL,
241 H8_LED_CONTROL_OFF | H8_LED_CONTROL_DOCK_LED2);
Timothy Pearson4b373c92015-04-05 17:54:08 -0500242}
243
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100244void dock_info(void)
Timothy Pearson4b373c92015-04-05 17:54:08 -0500245{
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100246 const u8 id = dock_identify();
Timothy Pearson4b373c92015-04-05 17:54:08 -0500247
Patrick Rudolphc0a16252018-10-30 17:38:13 +0100248 if (id != DOCK_TYPE_NONE)
249 printk(BIOS_DEBUG, "DOCK: is present: id=%d\n", id);
250 else
251 printk(BIOS_DEBUG, "DOCK: not connected\n");
Timothy Pearson4b373c92015-04-05 17:54:08 -0500252}