1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #define __SIMPLE_DEVICE__
4 #include <console/console.h>
5 #include <arch/io.h>
6 #include <device/pnp_ops.h>
7 #include <device/device.h>
8 #include <device/pnp.h>
9 #include <delay.h>
10 #include "dock.h"
11 #include <superio/nsc/pc87382/pc87382.h>
12
13 #include <southbridge/intel/i82801ix/i82801ix.h>
14 #include <ec/lenovo/h8/h8.h>
15 #include <ec/acpi/ec.h>
16
17 struct pin_config {
18 u8 port;
19 u8 mode;
20 };
21
poll_clk_stable(pnp_devfn_t dev,int timeout)22 static int poll_clk_stable(pnp_devfn_t dev, int timeout)
23 {
24 /* Enable 14.318MHz CLK on CLKIN */
25 pnp_write_config(dev, 0x29, 0xa0);
26 while (!(pnp_read_config(dev, 0x29) & 0x10) && timeout--)
27 udelay(1000);
28 if (!timeout)
29 return 1;
30
31 return 0;
32 }
33
gpio_init(pnp_devfn_t gpio,u16 gpio_base,const struct pin_config pincfg[],int num_cfgs)34 static 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
44 for (i = 0; i < num_cfgs; i++) {
45 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
52 static const pnp_devfn_t l_dlpc = PNP_DEV(0x164e, PC87382_DOCK);
53 static const pnp_devfn_t l_gpio = PNP_DEV(0x164e, PC87382_GPIO);
54
pc87382_init(pnp_devfn_t dlpc,u16 dlpc_base)55 static int pc87382_init(pnp_devfn_t dlpc, u16 dlpc_base)
56 {
57 /* Maximum 3300 LCLKs at 14.318MHz */
58 int timeout = 230;
59
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);
68 while (!(inb(dlpc_base) & 8) && timeout--)
69 udelay(1);
70 if (!timeout)
71 return 1;
72
73 return 0;
74 }
75
pc87382_close(pnp_devfn_t dlpc)76 static 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);
82 if (dlpc_base) {
83 outb(0x00, dlpc_base);
84 pnp_set_enable(dlpc, 0);
85 }
86 }
87
88 static const struct pin_config local_gpio[] = {
89 {0x00, 3}, {0x01, 3}, {0x02, 0}, {0x03, 3},
90 {0x04, 4}, {0x20, 4}, {0x21, 4}, {0x23, 4},
91 };
92
93 /* Enable internal clock and configure GPIO LDN */
pc87382_early(void)94 int pc87382_early(void)
95 {
96 /* Wake-up time is 33 msec (maximum). */
97 if (poll_clk_stable(l_gpio, 33) != 0)
98 return 1;
99
100 /* Set up GPIOs */
101 if (gpio_init(l_gpio, DLPC_GPIO_BASE,
102 local_gpio, ARRAY_SIZE(local_gpio)) != 0) {
103 return 1;
104 }
105
106 return 0;
107 }
108
pc87382_connect(void)109 static int pc87382_connect(void)
110 {
111 u8 reg;
112
113 reg = inb(DLPC_GPDO0);
114 reg |= D_PLTRST | D_LPCPD;
115 /* Deassert D_PLTRST# and D_LPCPD# */
116 outb(reg, DLPC_GPDO0);
117
118 if (pc87382_init(l_dlpc, DLPC_CONTROL) != 0)
119 return 1;
120
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);
129 mdelay(10);
130
131 return 0;
132 }
133
pc87382_disconnect(void)134 static 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
144 /* Returns 3bit dock id */
dock_identify(void)145 static u8 dock_identify(void)
146 {
147 u8 id;
148
149 /* Make sure GPIO LDN is configured first ! */
150 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
160 static const pnp_devfn_t r_gpio = PNP_DEV(SUPERIO_DEV, PC87384_GPIO);
161 static const pnp_devfn_t r_serial = PNP_DEV(SUPERIO_DEV, PC87384_SP1);
162
163 static 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
pc87384_init(void)174 static int pc87384_init(void)
175 {
176 if (poll_clk_stable(r_gpio, 1000) != 0)
177 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 */
185
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 }
191
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
dock_connect(void)215 void dock_connect(void)
216 {
217 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)
221 return;
222
223 if (pc87382_connect() != 0 || pc87384_init() != 0) {
224 pc87382_disconnect();
225 return;
226 }
227
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);
232 }
233
dock_disconnect(void)234 void dock_disconnect(void)
235 {
236 pc87382_disconnect();
237
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);
242 }
243
dock_info(void)244 void dock_info(void)
245 {
246 const u8 id = dock_identify();
247
248 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");
252 }
253