blob: df264e31f4812d0462dba9727e0814fe44d2a603 [file] [log] [blame]
Duncan Laurie2d9d39a2013-05-29 15:27:55 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2013 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; version 2 of
9 * the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
Duncan Laurie1f529082013-07-30 15:53:45 -070021#include <console/console.h>
22#include <delay.h>
Duncan Laurie2d9d39a2013-05-29 15:27:55 -070023#include <device/device.h>
24#include <device/pci.h>
25#include <device/pci_ids.h>
26#include <arch/io.h>
27#include "pch.h"
28
Duncan Laurie1f529082013-07-30 15:53:45 -070029#ifdef __SMM__
30
31static u32 usb_xhci_mem_base(device_t dev)
32{
33 u32 mem_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0);
34
35 /* Check if the controller is disabled or not present */
36 if (mem_base == 0 || mem_base == 0xffffffff)
37 return 0;
38
39 return mem_base & ~0xf;
40}
41
42#endif
43
44#if 0
45
46static int usb_xhci_port_count_usb3(device_t dev)
47{
48 if (pch_is_lp()) {
49 /* LynxPoint-LP has 4 SS ports */
50 return 4;
51 } else {
52 /* LynxPoint-H can have 0, 2, 4, or 6 SS ports */
53 u32 mem_base = usb_xhci_mem_base(dev);
54 u32 fus = read32(mem_base + XHCI_USB3FUS);
55 fus >>= XHCI_USB3FUS_SS_SHIFT;
56 fus &= XHCI_USB3FUS_SS_MASK;
57 switch (fus) {
58 case 3: return 0;
59 case 2: return 2;
60 case 1: return 4;
61 case 0: default: return 6;
62 }
63 }
64 return 0;
65}
66
67static void usb_xhci_reset_status_usb3(u32 mem_base, int port)
68{
69 u32 portsc = mem_base + XHCI_USB3_PORTSC(port);
70 write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_CHST);
71}
72
73static void usb_xhci_reset_port_usb3(u32 mem_base, int port)
74{
75 u32 portsc = mem_base + XHCI_USB3_PORTSC(port);
76 write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR);
77}
78
79#define XHCI_RESET_DELAY_US 1000 /* 1ms */
80#define XHCI_RESET_TIMEOUT 100 /* 100ms */
81
82/*
83 * 1) Wait until port is done polling
84 * 2) If port is disconnected
85 * a) Issue warm port reset
86 * b) Poll for warm reset complete
87 * c) Write 1 to port change status bits
88 */
89static void usb_xhci_reset_usb3(device_t dev, int all)
90{
91 u32 status, port_disabled;
92 int timeout, port;
93 int port_count = usb_xhci_port_count_usb3(dev);
94 u32 mem_base = usb_xhci_mem_base(dev);
95
96 if (!mem_base || !port_count)
97 return;
98
99 /* Get mask of disabled ports */
100 port_disabled = pci_read_config32(dev, XHCI_USB3PDO);
101
102 /* Wait until all enabled ports are done polling */
103 for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) {
104 int complete = 1;
105 for (port = 0; port < port_count; port++) {
106 /* Skip disabled ports */
107 if (port_disabled & (1 << port))
108 continue;
109 /* Read port link status field */
110 status = read32(mem_base + XHCI_USB3_PORTSC(port));
111 status &= XHCI_USB3_PORTSC_PLS;
112 if (status == XHCI_PLSR_POLLING)
113 complete = 0;
114 }
115 /* Exit if all ports not polling */
116 if (complete)
117 break;
118 udelay(XHCI_RESET_DELAY_US);
119 }
120
121 /* Reset all requested ports */
122 for (port = 0; port < port_count; port++) {
123 u32 portsc = mem_base + XHCI_USB3_PORTSC(port);
124 /* Skip disabled ports */
125 if (port_disabled & (1 << port))
126 continue;
127 status = read32(portsc) & XHCI_USB3_PORTSC_PLS;
128 /* Reset all or only disconnected ports */
129 if (all || status == XHCI_PLSR_RXDETECT)
130 usb_xhci_reset_port_usb3(mem_base, port);
131 else
132 port_disabled |= 1 << port; /* No reset */
133 }
134
135 /* Wait for warm reset complete on all reset ports */
136 for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) {
137 int complete = 1;
138 for (port = 0; port < port_count; port++) {
139 /* Only check ports that were reset */
140 if (port_disabled & (1 << port))
141 continue;
142 /* Check if warm reset is complete */
143 status = read32(mem_base + XHCI_USB3_PORTSC(port));
144 if (!(status & XHCI_USB3_PORTSC_WRC))
145 complete = 0;
146 }
147 /* Check for warm reset complete in any port */
148 if (complete)
149 break;
150 udelay(XHCI_RESET_DELAY_US);
151 }
152
153 /* Clear port change status bits */
154 for (port = 0; port < port_count; port++)
155 usb_xhci_reset_status_usb3(mem_base, port);
156}
157
158#endif
159
160#ifdef __SMM__
161
162/* Handler for XHCI controller on entry to S3/S4/S5 */
163void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ)
164{
165 u16 reg16;
166 u32 reg32;
167 u32 mem_base = usb_xhci_mem_base(dev);
168
169 if (!mem_base || slp_typ < 3)
170 return;
171
172 if (pch_is_lp()) {
173 /* Set D0 state */
174 reg16 = pci_read_config16(dev, XHCI_PWR_CTL_STS);
175 reg16 &= ~PWR_CTL_SET_MASK;
176 reg16 |= PWR_CTL_SET_D0;
177 pci_write_config16(dev, XHCI_PWR_CTL_STS, reg16);
178
179 /* Clear PCI 0xB0[14:13] */
180 reg32 = pci_read_config32(dev, 0xb0);
181 reg32 &= ~((1 << 14) | (1 << 13));
182 pci_write_config32(dev, 0xb0, reg32);
183
184 /* Clear MMIO 0x816c[14,2] */
185 reg32 = read32(mem_base + 0x816c);
186 reg32 &= ~((1 << 14) | (1 << 2));
187 write32(mem_base + 0x816c, reg32);
188
189 /* Set MMIO 0x80e0[15] */
190 reg32 = read32(mem_base + 0x80e0);
191 reg32 |= (1 << 15);
192 write32(mem_base + 0x80e0, reg32);
193 }
194
195 /* Set D3Hot state and enable PME */
196 pci_or_config16(dev, XHCI_PWR_CTL_STS, PWR_CTL_SET_D3);
197 pci_or_config16(dev, XHCI_PWR_CTL_STS, PWR_CTL_ENABLE_PME);
198}
199
200#else /* !__SMM__ */
201
Duncan Laurie2d9d39a2013-05-29 15:27:55 -0700202static void usb_xhci_clock_gating(device_t dev)
203{
204 u32 reg32;
205
206 /* IOBP 0xE5004001[7:6] = 11b */
207 pch_iobp_update(0xe5004001, ~0, (1 << 7)|(1 << 6));
208
209 reg32 = pci_read_config32(dev, 0x40);
210 reg32 &= ~(1 << 23); /* unsupported request */
211
212 if (pch_is_lp()) {
213 /* D20:F0:40h[18,17,8] = 111b */
214 reg32 |= (1 << 18) | (1 << 17) | (1 << 8);
215 /* D20:F0:40h[21,20,19] = 110b to enable XHCI Idle L1 */
216 reg32 &= ~(1 << 19);
217 reg32 |= (1 << 21) | (1 << 20);
218 } else {
219 /* D20:F0:40h[21,20,18,17,8] = 11111b */
220 reg32 |= (1 << 21)|(1 << 20)|(1 << 18)|(1 << 17)|(1 << 8);
221 }
222
223 /* Avoid writing upper byte as it is write-once */
224 pci_write_config16(dev, 0x40, (u16)(reg32 & 0xffff));
225 pci_write_config8(dev, 0x40 + 2, (u8)((reg32 >> 16) & 0xff));
226
227 /* D20:F0:44h[9,7,3] = 111b */
228 reg32 = pci_read_config32(dev, 0x44);
229 reg32 |= (1 << 9) | (1 << 7) | (1 << 3);
230 pci_write_config32(dev, 0x44, reg32);
231
232 reg32 = pci_read_config32(dev, 0xa0);
233 if (pch_is_lp()) {
234 /* D20:F0:A0h[18] = 1 */
235 reg32 |= (1 << 18);
236 } else {
237 /* D20:F0:A0h[6] = 1 */
238 reg32 |= (1 << 6);
239 }
240 pci_write_config32(dev, 0xa0, reg32);
241
242 /* D20:F0:A4h[13] = 0 */
243 reg32 = pci_read_config32(dev, 0xa4);
244 reg32 &= ~(1 << 13);
245 pci_write_config32(dev, 0xa4, reg32);
246}
247
248static void usb_xhci_init(device_t dev)
249{
250 struct resource *bar0 = find_resource(dev, PCI_BASE_ADDRESS_0);
251 u32 reg32;
252
253 if (!bar0 || bar0->base == 0 || bar0->base == 0xffffffff)
254 return;
255
256 /* Enable clock gating first */
257 usb_xhci_clock_gating(dev);
258
259 /* D20:F0:74h[1:0] = 11b (set D3Hot state) */
260 reg32 = pci_read_config16(dev, 0x74);
261 reg32 |= (1 << 1) | (1 << 0);
262 pci_write_config16(dev, 0x74, reg32);
263
264 reg32 = read32(bar0->base + 0x8144);
265 if (pch_is_lp()) {
266 /* XHCIBAR + 8144h[8,7,6] = 111b */
267 reg32 |= (1 << 8) | (1 << 7) | (1 << 6);
268 } else {
269 /* XHCIBAR + 8144h[8,7,6] = 100b */
270 reg32 &= ~((1 << 7) | (1 << 6));
271 reg32 |= (1 << 8);
272 }
273 write32(bar0->base + 0x8144, reg32);
274
275 if (pch_is_lp()) {
276 /* XHCIBAR + 816Ch[19:0] = 000f0038h */
277 reg32 = read32(bar0->base + 0x816c);
278 reg32 &= ~0x000fffff;
279 reg32 |= 0x000f0038;
280 write32(bar0->base + 0x816c, reg32);
281
282 /* D20:F0:B0h[17,14,13] = 100b */
283 reg32 = pci_read_config32(dev, 0xb0);
284 reg32 &= ~((1 << 14) | (1 << 13));
285 reg32 |= (1 << 17);
286 pci_write_config32(dev, 0xb0, reg32);
287
288 /* XHCIBAR + 818Ch[7:0] = FFh */
289 reg32 = read32(bar0->base + 0x818c);
290 reg32 |= 0xff;
291 write32(bar0->base + 0x818c, reg32);
292 }
293
294 reg32 = pci_read_config32(dev, 0x50);
295 if (pch_is_lp()) {
296 /* D20:F0:50h[28:0] = 0FCE2E5Fh */
297 reg32 &= ~0x1fffffff;
298 reg32 |= 0x0fce2e5f;
299 } else {
300 /* D20:F0:50h[26:0] = 07886E9Fh */
301 reg32 &= ~0x07ffffff;
302 reg32 |= 0x07886e9f;
303 }
304 pci_write_config32(dev, 0x50, reg32);
305
306 /* D20:F0:44h[31] = 1 (Access Control Bit) */
307 reg32 = pci_read_config32(dev, 0x40);
308 reg32 |= (1 << 31);
309 pci_write_config32(dev, 0x40, reg32);
310
311 /* D20:F0:40h[31,23] = 10b (OC Configuration Done) */
312 reg32 = pci_read_config32(dev, 0x40);
313 reg32 &= ~(1 << 23); /* unsupported request */
314 reg32 |= (1 << 31);
315 pci_write_config32(dev, 0x40, reg32);
316}
317
318static void usb_xhci_set_subsystem(device_t dev, unsigned vendor,
319 unsigned device)
320{
321 if (!vendor || !device) {
322 pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
323 pci_read_config32(dev, PCI_VENDOR_ID));
324 } else {
325 pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
326 ((device & 0xffff) << 16) | (vendor & 0xffff));
327 }
328}
329
330static struct pci_operations lops_pci = {
331 .set_subsystem = &usb_xhci_set_subsystem,
332};
333
334static struct device_operations usb_xhci_ops = {
335 .read_resources = pci_dev_read_resources,
336 .set_resources = pci_dev_set_resources,
337 .enable_resources = pci_dev_enable_resources,
338 .init = usb_xhci_init,
339 .ops_pci = &lops_pci,
340};
341
342static const unsigned short pci_device_ids[] = { 0x8c31, /* LynxPoint-H */
343 0x9c31, /* LynxPoint-LP */
344 0 };
345
346static const struct pci_driver pch_usb_xhci __pci_driver = {
347 .ops = &usb_xhci_ops,
348 .vendor = PCI_VENDOR_ID_INTEL,
349 .devices = pci_device_ids,
350};
Duncan Laurie1f529082013-07-30 15:53:45 -0700351#endif /* !__SMM__ */