blob: 60223c1310cc09f3acf14a3556a13030f0c202bf [file] [log] [blame]
Duncan Lauriec88c54c2014-04-30 16:36:13 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2014 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <console/console.h>
21#include <delay.h>
22#include <device/device.h>
23#include <device/pci.h>
24#include <device/pci_ids.h>
25#include <arch/io.h>
26#include <broadwell/ramstage.h>
27#include <broadwell/xhci.h>
28
29#ifdef __SMM__
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080030static u8 *usb_xhci_mem_base(device_t dev)
Duncan Lauriec88c54c2014-04-30 16:36:13 -070031{
32 u32 mem_base = pci_read_config32(dev, PCI_BASE_ADDRESS_0);
33
34 /* Check if the controller is disabled or not present */
35 if (mem_base == 0 || mem_base == 0xffffffff)
36 return 0;
37
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080038 return (u8 *)(mem_base & ~0xf);
Duncan Lauriec88c54c2014-04-30 16:36:13 -070039}
40
41static int usb_xhci_port_count_usb3(device_t dev)
42{
43 /* PCH-LP has 4 SS ports */
44 return 4;
45}
46
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080047static void usb_xhci_reset_status_usb3(u8 *mem_base, int port)
Duncan Lauriec88c54c2014-04-30 16:36:13 -070048{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080049 u8 *portsc = mem_base + XHCI_USB3_PORTSC(port);
Duncan Lauriec88c54c2014-04-30 16:36:13 -070050 u32 status = read32(portsc);
51 /* Do not set Port Enabled/Disabled field */
52 status &= ~XHCI_USB3_PORTSC_PED;
53 /* Clear all change status bits */
54 status |= XHCI_USB3_PORTSC_CHST;
55 write32(portsc, status);
56}
57
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080058static void usb_xhci_reset_port_usb3(u8 *mem_base, int port)
Duncan Lauriec88c54c2014-04-30 16:36:13 -070059{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080060 u8 *portsc = mem_base + XHCI_USB3_PORTSC(port);
Duncan Lauriec88c54c2014-04-30 16:36:13 -070061 write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR);
62}
63
64#define XHCI_RESET_DELAY_US 1000 /* 1ms */
65#define XHCI_RESET_TIMEOUT 100 /* 100ms */
66
67/*
68 * 1) Wait until port is done polling
69 * 2) If port is disconnected
70 * a) Issue warm port reset
71 * b) Poll for warm reset complete
72 * c) Write 1 to port change status bits
73 */
74static void usb_xhci_reset_usb3(device_t dev, int all)
75{
76 u32 status, port_disabled;
77 int timeout, port;
78 int port_count = usb_xhci_port_count_usb3(dev);
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080079 u8 *mem_base = usb_xhci_mem_base(dev);
Duncan Lauriec88c54c2014-04-30 16:36:13 -070080
81 if (!mem_base || !port_count)
82 return;
83
84 /* Get mask of disabled ports */
85 port_disabled = pci_read_config32(dev, XHCI_USB3PDO);
86
87 /* Wait until all enabled ports are done polling */
88 for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) {
89 int complete = 1;
90 for (port = 0; port < port_count; port++) {
91 /* Skip disabled ports */
92 if (port_disabled & (1 << port))
93 continue;
94 /* Read port link status field */
95 status = read32(mem_base + XHCI_USB3_PORTSC(port));
96 status &= XHCI_USB3_PORTSC_PLS;
97 if (status == XHCI_PLSR_POLLING)
98 complete = 0;
99 }
100 /* Exit if all ports not polling */
101 if (complete)
102 break;
103 udelay(XHCI_RESET_DELAY_US);
104 }
105
106 /* Reset all requested ports */
107 for (port = 0; port < port_count; port++) {
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -0800108 u8 *portsc = mem_base + XHCI_USB3_PORTSC(port);
Duncan Lauriec88c54c2014-04-30 16:36:13 -0700109 /* Skip disabled ports */
110 if (port_disabled & (1 << port))
111 continue;
112 status = read32(portsc) & XHCI_USB3_PORTSC_PLS;
113 /* Reset all or only disconnected ports */
114 if (all || (status == XHCI_PLSR_RXDETECT ||
115 status == XHCI_PLSR_POLLING))
116 usb_xhci_reset_port_usb3(mem_base, port);
117 else
118 port_disabled |= 1 << port;
119 }
120
121 /* Wait for warm reset complete on all reset ports */
122 for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) {
123 int complete = 1;
124 for (port = 0; port < port_count; port++) {
125 /* Only check ports that were reset */
126 if (port_disabled & (1 << port))
127 continue;
128 /* Check if warm reset is complete */
129 status = read32(mem_base + XHCI_USB3_PORTSC(port));
130 if (!(status & XHCI_USB3_PORTSC_WRC))
131 complete = 0;
132 }
133 /* Check for warm reset complete in any port */
134 if (complete)
135 break;
136 udelay(XHCI_RESET_DELAY_US);
137 }
138
139 /* Clear port change status bits */
140 for (port = 0; port < port_count; port++)
141 usb_xhci_reset_status_usb3(mem_base, port);
142}
143
144/* Handler for XHCI controller on entry to S3/S4/S5 */
145void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ)
146{
147 u16 reg16;
148 u32 reg32;
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -0800149 u8 *mem_base = usb_xhci_mem_base(dev);
Duncan Lauriec88c54c2014-04-30 16:36:13 -0700150
151 if (!mem_base || slp_typ < 3)
152 return;
153
154 /* Set D0 state */
155 reg16 = pci_read_config16(dev, XHCI_PWR_CTL_STS);
156 reg16 &= ~XHCI_PWR_CTL_SET_MASK;
157 reg16 |= XHCI_PWR_CTL_SET_D0;
158 pci_write_config16(dev, XHCI_PWR_CTL_STS, reg16);
159
160 /* Clear PCI 0xB0[14:13] */
161 reg32 = pci_read_config32(dev, 0xb0);
162 reg32 &= ~((1 << 14) | (1 << 13));
163 pci_write_config32(dev, 0xb0, reg32);
164
165 /* Clear MMIO 0x816c[14,2] */
166 reg32 = read32(mem_base + 0x816c);
167 reg32 &= ~((1 << 14) | (1 << 2));
168 write32(mem_base + 0x816c, reg32);
169
170 /* Reset disconnected USB3 ports */
171 usb_xhci_reset_usb3(dev, 0);
172
173 /* Set MMIO 0x80e0[15] */
174 reg32 = read32(mem_base + 0x80e0);
175 reg32 |= (1 << 15);
176 write32(mem_base + 0x80e0, reg32);
177
178 /* Set D3Hot state and enable PME */
179 pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_SET_D3);
180 pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_STATUS_PME);
181 pci_or_config16(dev, XHCI_PWR_CTL_STS, XHCI_PWR_CTL_ENABLE_PME);
182}
183#else /* !__SMM__ */
184
185static struct device_operations usb_xhci_ops = {
186 .read_resources = &pci_dev_read_resources,
187 .set_resources = &pci_dev_set_resources,
188 .enable_resources = &pci_dev_enable_resources,
189 .ops_pci = &broadwell_pci_ops,
190};
191
192static const unsigned short pci_device_ids[] = {
193 0x9c31, /* LynxPoint-LP */
194 0x9cb1, /* WildcatPoint */
195 0
196};
197
198static const struct pci_driver pch_usb_xhci __pci_driver = {
199 .ops = &usb_xhci_ops,
200 .vendor = PCI_VENDOR_ID_INTEL,
201 .devices = pci_device_ids,
202};
203#endif /* !__SMM__ */