blob: 9846794eddc131a33071654916eb7a67ea2b5039 [file] [log] [blame]
Stefan Reinauer7ce8c542005-12-02 21:52:30 +00001/*
Uwe Hermannb80dbf02007-04-22 19:08:13 +00002 * This file is part of the LinuxBIOS project.
3 *
4 * Copyright (C) 2003-2004 Linux Networx
5 * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx)
6 * Copyright (C) 2004 David Hendricks <sc@flagen.com>
7 * Copyright (C) 2004 Li-Ta Lo <ollie@lanl.gov>
8 * Copyright (C) 2005-2006 Tyan
9 * (Written by Yinghai Lu <yhlu@tyan.com> for Tyan)
10 * Copyright (C) 2005-2006 Stefan Reinauer <stepan@openbios.org>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; version 2 of the License.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
Stefan Reinauer7ce8c542005-12-02 21:52:30 +000025
Uwe Hermannb80dbf02007-04-22 19:08:13 +000026/*
27 2005.11 yhlu add let the real sb to use small uintid
Stefan Reinauer7ce8c542005-12-02 21:52:30 +000028*/
29
Eric Biederman0ac6b412003-09-02 17:16:48 +000030#include <bitops.h>
31#include <console/console.h>
32#include <device/device.h>
33#include <device/path.h>
34#include <device/pci.h>
Eric Biederman5cd81732004-03-11 15:01:31 +000035#include <device/pci_ids.h>
Eric Biedermanff0e8462003-09-04 03:00:54 +000036#include <device/hypertransport.h>
Eric Biederman0ac6b412003-09-02 17:16:48 +000037#include <part/hard_reset.h>
38#include <part/fallback_boot.h>
39
Yinghai Lu90b3e092005-01-26 22:00:20 +000040#define OPT_HT_LINK 0
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000041
arch import user (historical)ef03afa2005-07-06 17:15:30 +000042#if OPT_HT_LINK == 1
Stefan Reinauer7ce8c542005-12-02 21:52:30 +000043#include <cpu/amd/model_fxx_rev.h>
arch import user (historical)ef03afa2005-07-06 17:15:30 +000044#endif
45
Eric Biederman0ac6b412003-09-02 17:16:48 +000046static device_t ht_scan_get_devs(device_t *old_devices)
47{
48 device_t first, last;
49 first = *old_devices;
50 last = first;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000051 /* Extract the chain of devices to (first through last)
52 * for the next hypertransport device.
53 */
Eric Biederman03acab62004-10-14 21:25:53 +000054 while(last && last->sibling &&
55 (last->sibling->path.type == DEVICE_PATH_PCI) &&
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000056 (last->sibling->path.u.pci.devfn > last->path.u.pci.devfn))
57 {
Eric Biederman0ac6b412003-09-02 17:16:48 +000058 last = last->sibling;
59 }
60 if (first) {
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000061 device_t child;
62 /* Unlink the chain from the list of old devices */
Eric Biederman0ac6b412003-09-02 17:16:48 +000063 *old_devices = last->sibling;
64 last->sibling = 0;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000065
66 /* Now add the device to the list of devices on the bus.
67 */
68 /* Find the last child of our parent */
69 for(child = first->bus->children; child && child->sibling; ) {
70 child = child->sibling;
71 }
72 /* Place the chain on the list of children of their parent. */
73 if (child) {
74 child->sibling = first;
75 } else {
76 first->bus->children = first;
77 }
Eric Biederman0ac6b412003-09-02 17:16:48 +000078 }
79 return first;
80}
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000081
Yinghai Lu90b3e092005-01-26 22:00:20 +000082#if OPT_HT_LINK == 1
Eric Biederman5cd81732004-03-11 15:01:31 +000083static unsigned ht_read_freq_cap(device_t dev, unsigned pos)
84{
85 /* Handle bugs in valid hypertransport frequency reporting */
86 unsigned freq_cap;
87
88 freq_cap = pci_read_config16(dev, pos);
89 freq_cap &= ~(1 << HT_FREQ_VENDOR); /* Ignore Vendor HT frequencies */
90
91 /* AMD 8131 Errata 48 */
92 if ((dev->vendor == PCI_VENDOR_ID_AMD) &&
93 (dev->device == PCI_DEVICE_ID_AMD_8131_PCIX)) {
94 freq_cap &= ~(1 << HT_FREQ_800Mhz);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +000095 }
Eric Biederman5cd81732004-03-11 15:01:31 +000096 /* AMD 8151 Errata 23 */
97 if ((dev->vendor == PCI_VENDOR_ID_AMD) &&
98 (dev->device == PCI_DEVICE_ID_AMD_8151_SYSCTRL)) {
99 freq_cap &= ~(1 << HT_FREQ_800Mhz);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000100 }
Eric Biederman5cd81732004-03-11 15:01:31 +0000101 /* AMD K8 Unsupported 1Ghz? */
102 if ((dev->vendor == PCI_VENDOR_ID_AMD) && (dev->device == 0x1100)) {
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000103#if K8_HT_FREQ_1G_SUPPORT == 1
Yinghai Lud4b278c2006-10-04 20:46:15 +0000104 #if K8_REV_F_SUPPORT == 0
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000105 if (is_cpu_pre_e0()) { // only e0 later suupport 1GHz HT
Yinghai Lu90b3e092005-01-26 22:00:20 +0000106 freq_cap &= ~(1 << HT_FREQ_1000Mhz);
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000107 }
Yinghai Lud4b278c2006-10-04 20:46:15 +0000108 #endif
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000109#else
110 freq_cap &= ~(1 << HT_FREQ_1000Mhz);
111#endif
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000112
Eric Biederman5cd81732004-03-11 15:01:31 +0000113 }
114 return freq_cap;
115}
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000116#endif
Eric Biederman0ac6b412003-09-02 17:16:48 +0000117
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000118struct ht_link {
Eric Biederman0ac6b412003-09-02 17:16:48 +0000119 struct device *dev;
120 unsigned pos;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000121 unsigned char ctrl_off, config_off, freq_off, freq_cap_off;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000122};
123
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000124static int ht_setup_link(struct ht_link *prev, device_t dev, unsigned pos)
Eric Biederman0ac6b412003-09-02 17:16:48 +0000125{
126 static const uint8_t link_width_to_pow2[]= { 3, 4, 0, 5, 1, 2, 0, 0 };
127 static const uint8_t pow2_to_link_width[] = { 0x7, 4, 5, 0, 1, 3 };
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000128 struct ht_link cur[1];
Eric Biederman0ac6b412003-09-02 17:16:48 +0000129 unsigned present_width_cap, upstream_width_cap;
130 unsigned present_freq_cap, upstream_freq_cap;
131 unsigned ln_present_width_in, ln_upstream_width_in;
132 unsigned ln_present_width_out, ln_upstream_width_out;
133 unsigned freq, old_freq;
134 unsigned present_width, upstream_width, old_width;
135 int reset_needed;
Yinghai Lu1f1085b2005-01-17 21:37:12 +0000136 int linkb_to_host;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000137
138 /* Set the hypertransport link width and frequency */
139 reset_needed = 0;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000140 /* See which side of the device our previous write to
141 * set the unitid came from.
142 */
143 cur->dev = dev;
144 cur->pos = pos;
145 linkb_to_host = (pci_read_config16(cur->dev, cur->pos + PCI_CAP_FLAGS) >> 10) & 1;
146 if (!linkb_to_host) {
147 cur->ctrl_off = PCI_HT_CAP_SLAVE_CTRL0;
148 cur->config_off = PCI_HT_CAP_SLAVE_WIDTH0;
149 cur->freq_off = PCI_HT_CAP_SLAVE_FREQ0;
150 cur->freq_cap_off = PCI_HT_CAP_SLAVE_FREQ_CAP0;
151 }
152 else {
153 cur->ctrl_off = PCI_HT_CAP_SLAVE_CTRL1;
154 cur->config_off = PCI_HT_CAP_SLAVE_WIDTH1;
155 cur->freq_off = PCI_HT_CAP_SLAVE_FREQ1;
156 cur->freq_cap_off = PCI_HT_CAP_SLAVE_FREQ_CAP1;
157 }
158#if OPT_HT_LINK == 1
Eric Biederman0ac6b412003-09-02 17:16:48 +0000159 /* Read the capabilities */
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000160 present_freq_cap = ht_read_freq_cap(cur->dev, cur->pos + cur->freq_cap_off);
Eric Biederman5cd81732004-03-11 15:01:31 +0000161 upstream_freq_cap = ht_read_freq_cap(prev->dev, prev->pos + prev->freq_cap_off);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000162 present_width_cap = pci_read_config8(cur->dev, cur->pos + cur->config_off);
163 upstream_width_cap = pci_read_config8(prev->dev, prev->pos + prev->config_off);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000164
165 /* Calculate the highest useable frequency */
Eric Biederman0ac6b412003-09-02 17:16:48 +0000166 freq = log2(present_freq_cap & upstream_freq_cap);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000167
168 /* Calculate the highest width */
169 ln_upstream_width_in = link_width_to_pow2[upstream_width_cap & 7];
170 ln_present_width_out = link_width_to_pow2[(present_width_cap >> 4) & 7];
171 if (ln_upstream_width_in > ln_present_width_out) {
172 ln_upstream_width_in = ln_present_width_out;
173 }
174 upstream_width = pow2_to_link_width[ln_upstream_width_in];
175 present_width = pow2_to_link_width[ln_upstream_width_in] << 4;
176
177 ln_upstream_width_out = link_width_to_pow2[(upstream_width_cap >> 4) & 7];
178 ln_present_width_in = link_width_to_pow2[present_width_cap & 7];
179 if (ln_upstream_width_out > ln_present_width_in) {
180 ln_upstream_width_out = ln_present_width_in;
181 }
182 upstream_width |= pow2_to_link_width[ln_upstream_width_out] << 4;
183 present_width |= pow2_to_link_width[ln_upstream_width_out];
184
185 /* Set the current device */
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000186 old_freq = pci_read_config8(cur->dev, cur->pos + cur->freq_off);
187 old_freq &= 0x0f;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000188 if (freq != old_freq) {
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000189 unsigned new_freq;
190 pci_write_config8(cur->dev, cur->pos + cur->freq_off, freq);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000191 reset_needed = 1;
192 printk_spew("HyperT FreqP old %x new %x\n",old_freq,freq);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000193 new_freq = pci_read_config8(cur->dev, cur->pos + cur->freq_off);
194 new_freq &= 0x0f;
195 if (new_freq != freq) {
196 printk_err("%s Hypertransport frequency would not set wanted: %x got: %x\n",
197 dev_path(dev), freq, new_freq);
198 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000199 }
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000200 old_width = pci_read_config8(cur->dev, cur->pos + cur->config_off + 1);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000201 if (present_width != old_width) {
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000202 unsigned new_width;
203 pci_write_config8(cur->dev, cur->pos + cur->config_off + 1,
204 present_width);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000205 reset_needed = 1;
206 printk_spew("HyperT widthP old %x new %x\n",old_width, present_width);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000207 new_width = pci_read_config8(cur->dev, cur->pos + cur->config_off + 1);
208 if (new_width != present_width) {
209 printk_err("%s Hypertransport width would not set wanted: %x got: %x\n",
210 dev_path(dev), present_width, new_width);
211 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000212 }
213
214 /* Set the upstream device */
215 old_freq = pci_read_config8(prev->dev, prev->pos + prev->freq_off);
216 old_freq &= 0x0f;
217 if (freq != old_freq) {
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000218 unsigned new_freq;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000219 pci_write_config8(prev->dev, prev->pos + prev->freq_off, freq);
220 reset_needed = 1;
221 printk_spew("HyperT freqU old %x new %x\n", old_freq, freq);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000222 new_freq = pci_read_config8(prev->dev, prev->pos + prev->freq_off);
223 new_freq &= 0x0f;
224 if (new_freq != freq) {
225 printk_err("%s Hypertransport frequency would not set wanted: %x got: %x\n",
226 dev_path(prev->dev), freq, new_freq);
227 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000228 }
229 old_width = pci_read_config8(prev->dev, prev->pos + prev->config_off + 1);
230 if (upstream_width != old_width) {
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000231 unsigned new_width;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000232 pci_write_config8(prev->dev, prev->pos + prev->config_off + 1, upstream_width);
233 reset_needed = 1;
234 printk_spew("HyperT widthU old %x new %x\n", old_width, upstream_width);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000235 new_width = pci_read_config8(prev->dev, prev->pos + prev->config_off + 1);
236 if (new_width != upstream_width) {
237 printk_err("%s Hypertransport width would not set wanted: %x got: %x\n",
238 dev_path(prev->dev), upstream_width, new_width);
239 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000240 }
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000241#endif
Eric Biederman0ac6b412003-09-02 17:16:48 +0000242
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000243 /* Remember the current link as the previous link,
244 * But look at the other offsets.
245 */
246 prev->dev = cur->dev;
247 prev->pos = cur->pos;
248 if (cur->ctrl_off == PCI_HT_CAP_SLAVE_CTRL0) {
249 prev->ctrl_off = PCI_HT_CAP_SLAVE_CTRL1;
250 prev->config_off = PCI_HT_CAP_SLAVE_WIDTH1;
251 prev->freq_off = PCI_HT_CAP_SLAVE_FREQ1;
252 prev->freq_cap_off = PCI_HT_CAP_SLAVE_FREQ_CAP1;
253 } else {
254 prev->ctrl_off = PCI_HT_CAP_SLAVE_CTRL0;
255 prev->config_off = PCI_HT_CAP_SLAVE_WIDTH0;
256 prev->freq_off = PCI_HT_CAP_SLAVE_FREQ0;
257 prev->freq_cap_off = PCI_HT_CAP_SLAVE_FREQ_CAP0;
258 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000259
260 return reset_needed;
261
262}
263
264static unsigned ht_lookup_slave_capability(struct device *dev)
265{
266 unsigned pos;
267 pos = 0;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000268 do {
269 pos = pci_find_next_capability(dev, PCI_CAP_ID_HT, pos);
270 if (pos) {
Eric Biederman0ac6b412003-09-02 17:16:48 +0000271 unsigned flags;
272 flags = pci_read_config16(dev, pos + PCI_CAP_FLAGS);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000273 printk_spew("flags: 0x%04x\n", flags);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000274 if ((flags >> 13) == 0) {
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000275 /* Entry is a Slave secondary, success... */
Eric Biederman0ac6b412003-09-02 17:16:48 +0000276 break;
277 }
278 }
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000279 } while(pos);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000280 return pos;
281}
282
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000283static void ht_collapse_early_enumeration(struct bus *bus, unsigned offset_unitid)
Eric Biederman0ac6b412003-09-02 17:16:48 +0000284{
285 unsigned int devfn;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000286 struct ht_link prev;
287 unsigned ctrl;
288
289 /* Initialize the hypertransport enumeration state */
290 prev.dev = bus->dev;
291 prev.pos = bus->cap;
292 prev.ctrl_off = PCI_HT_CAP_HOST_CTRL;
293 prev.config_off = PCI_HT_CAP_HOST_WIDTH;
294 prev.freq_off = PCI_HT_CAP_HOST_FREQ;
295 prev.freq_cap_off = PCI_HT_CAP_HOST_FREQ_CAP;
296
297 /* Wait until the link initialization is complete */
298 do {
299 ctrl = pci_read_config16(prev.dev, prev.pos + prev.ctrl_off);
300 /* Is this the end of the hypertransport chain */
301 if (ctrl & (1 << 6)) {
302 return;
303 }
304 /* Has the link failed? */
305 if (ctrl & (1 << 4)) {
Stefan Reinauercf648c92006-04-11 19:23:57 +0000306 /*
307 * Either the link has failed, or we have
308 * a CRC error.
309 * Sometimes this can happen due to link
310 * retrain, so lets knock it down and see
311 * if its transient
312 */
313 ctrl |= ((1 << 4) | (1 <<8)); // Link fail + Crc
314 pci_write_config16(prev.dev, prev.pos + prev.ctrl_off, ctrl);
315 ctrl = pci_read_config16(prev.dev, prev.pos + prev.ctrl_off);
316 if (ctrl & ((1 << 4) | (1 << 8))) {
317 printk_alert("Detected error on Hypertransport Link\n");
318 return;
319 }
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000320 }
321 } while((ctrl & (1 << 5)) == 0);
322
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000323 //actually, only for one HT device HT chain, and unitid is 0
324#if HT_CHAIN_UNITID_BASE == 0
325 if(offset_unitid) {
326 return;
327 }
328#endif
329
330 /* Check if is already collapsed */
331 if((!offset_unitid)|| (offset_unitid && (!((HT_CHAIN_END_UNITID_BASE == 0) && (HT_CHAIN_END_UNITID_BASE <HT_CHAIN_UNITID_BASE))))) {
332 struct device dummy;
333 uint32_t id;
334 dummy.bus = bus;
335 dummy.path.type = DEVICE_PATH_PCI;
336 dummy.path.u.pci.devfn = PCI_DEVFN(0, 0);
337 id = pci_read_config32(&dummy, PCI_VENDOR_ID);
338 if ( ! ( (id == 0xffffffff) || (id == 0x00000000) ||
339 (id == 0x0000ffff) || (id == 0xffff0000) ) ) {
340 return;
341 }
342 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000343
344 /* Spin through the devices and collapse any early
345 * hypertransport enumeration.
346 */
Yinghai Lu1f1085b2005-01-17 21:37:12 +0000347 for(devfn = PCI_DEVFN(1, 0); devfn <= 0xff; devfn += 8) {
Eric Biederman0ac6b412003-09-02 17:16:48 +0000348 struct device dummy;
349 uint32_t id;
350 unsigned pos, flags;
351 dummy.bus = bus;
352 dummy.path.type = DEVICE_PATH_PCI;
353 dummy.path.u.pci.devfn = devfn;
354 id = pci_read_config32(&dummy, PCI_VENDOR_ID);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000355 if ( (id == 0xffffffff) || (id == 0x00000000) ||
356 (id == 0x0000ffff) || (id == 0xffff0000)) {
Eric Biederman0ac6b412003-09-02 17:16:48 +0000357 continue;
358 }
359 dummy.vendor = id & 0xffff;
360 dummy.device = (id >> 16) & 0xffff;
361 dummy.hdr_type = pci_read_config8(&dummy, PCI_HEADER_TYPE);
362 pos = ht_lookup_slave_capability(&dummy);
363 if (!pos){
364 continue;
365 }
366
367 /* Clear the unitid */
368 flags = pci_read_config16(&dummy, pos + PCI_CAP_FLAGS);
369 flags &= ~0x1f;
370 pci_write_config16(&dummy, pos + PCI_CAP_FLAGS, flags);
371 printk_spew("Collapsing %s [%04x/%04x]\n",
372 dev_path(&dummy), dummy.vendor, dummy.device);
373 }
374}
375
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000376unsigned int hypertransport_scan_chain(struct bus *bus,
Stefan Reinauerbbdd8f42005-12-04 21:52:58 +0000377 unsigned min_devfn, unsigned max_devfn, unsigned int max, unsigned *ht_unitid_base, unsigned offset_unitid)
Eric Biederman0ac6b412003-09-02 17:16:48 +0000378{
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000379 //even HT_CHAIN_UNITID_BASE == 0, we still can go through this function, because of end_of_chain check, also We need it to optimize link
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000380 unsigned next_unitid, last_unitid;
381 device_t old_devices, dev, func;
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000382 unsigned min_unitid = (offset_unitid) ? HT_CHAIN_UNITID_BASE:1;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000383 struct ht_link prev;
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000384 device_t last_func = 0;
Stefan Reinauerbbdd8f42005-12-04 21:52:58 +0000385 int ht_dev_num = 0;
Yinghai Lu18c70d72007-09-14 14:58:33 +0000386 unsigned max_unitid;
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000387
Yinghai Lu18c70d72007-09-14 14:58:33 +0000388#if HT_CHAIN_END_UNITID_BASE != 0x20
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000389 //let't record the device of last ht device, So we can set the Unitid to HT_CHAIN_END_UNITID_BASE
390 unsigned real_last_unitid;
391 uint8_t real_last_pos;
392 device_t real_last_dev;
Yinghai Lu18c70d72007-09-14 14:58:33 +0000393 unsigned end_used = 0;
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000394#endif
Eric Biederman0ac6b412003-09-02 17:16:48 +0000395
396 /* Restore the hypertransport chain to it's unitialized state */
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000397 ht_collapse_early_enumeration(bus, offset_unitid);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000398
399 /* See which static device nodes I have */
400 old_devices = bus->children;
401 bus->children = 0;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000402
403 /* Initialize the hypertransport enumeration state */
Eric Biederman0ac6b412003-09-02 17:16:48 +0000404 prev.dev = bus->dev;
405 prev.pos = bus->cap;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000406 prev.ctrl_off = PCI_HT_CAP_HOST_CTRL;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000407 prev.config_off = PCI_HT_CAP_HOST_WIDTH;
408 prev.freq_off = PCI_HT_CAP_HOST_FREQ;
409 prev.freq_cap_off = PCI_HT_CAP_HOST_FREQ_CAP;
410
411 /* If present assign unitid to a hypertransport chain */
412 last_unitid = min_unitid -1;
Yinghai Lu18c70d72007-09-14 14:58:33 +0000413 max_unitid = next_unitid = min_unitid;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000414 do {
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000415 uint8_t pos;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000416 uint16_t flags;
417 unsigned count, static_count;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000418 unsigned ctrl;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000419
Eric Biederman0ac6b412003-09-02 17:16:48 +0000420 last_unitid = next_unitid;
421
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000422 /* Wait until the link initialization is complete */
423 do {
424 ctrl = pci_read_config16(prev.dev, prev.pos + prev.ctrl_off);
Stefan Reinauercf648c92006-04-11 19:23:57 +0000425
426 if (ctrl & (1 << 6))
427 goto end_of_chain; // End of chain
428
429 if (ctrl & ((1 << 4) | (1 << 8))) {
430 /*
431 * Either the link has failed, or we have
432 * a CRC error.
433 * Sometimes this can happen due to link
434 * retrain, so lets knock it down and see
435 * if its transient
436 */
437 ctrl |= ((1 << 4) | (1 <<8)); // Link fail + Crc
438 pci_write_config16(prev.dev, prev.pos + prev.ctrl_off, ctrl);
439 ctrl = pci_read_config16(prev.dev, prev.pos + prev.ctrl_off);
440 if (ctrl & ((1 << 4) | (1 << 8))) {
441 printk_alert("Detected error on Hypertransport Link\n");
442 goto end_of_chain;
443 }
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000444 }
445 } while((ctrl & (1 << 5)) == 0);
446
447
448 /* Get and setup the device_structure */
Eric Biederman0ac6b412003-09-02 17:16:48 +0000449 dev = ht_scan_get_devs(&old_devices);
450
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000451 /* See if a device is present and setup the
452 * device structure.
453 */
454 dev = pci_probe_dev(dev, bus, 0);
455 if (!dev || !dev->enabled) {
456 break;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000457 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000458
459 /* Find the hypertransport link capability */
460 pos = ht_lookup_slave_capability(dev);
461 if (pos == 0) {
Eric Biederman83b991a2003-10-11 06:20:25 +0000462 printk_err("%s Hypertransport link capability not found",
463 dev_path(dev));
Eric Biederman0ac6b412003-09-02 17:16:48 +0000464 break;
465 }
466
467 /* Update the Unitid of the current device */
468 flags = pci_read_config16(dev, pos + PCI_CAP_FLAGS);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000469
470 /* If the devices has a unitid set and is at devfn 0 we are done.
471 * This can happen with shadow hypertransport devices,
472 * or if we have reached the bottom of a
473 * hypertransport device chain.
474 */
475 if (flags & 0x1f) {
476 break;
477 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000478 flags &= ~0x1f; /* mask out base Unit ID */
Yinghai Luecad5df2007-05-21 18:11:17 +0000479
480 count = (flags >> 5) & 0x1f; /* get unit count */
Yinghai Lu18c70d72007-09-14 14:58:33 +0000481#if HT_CHAIN_END_UNITID_BASE != 0x20
Yinghai Luecad5df2007-05-21 18:11:17 +0000482 if(offset_unitid) {
Yinghai Lu18c70d72007-09-14 14:58:33 +0000483 if(next_unitid > (max_devfn>>3)) { // max_devfn will be (0x17<<3)|7 or (0x1f<<3)|7
484 if(!end_used) {
485 next_unitid = HT_CHAIN_END_UNITID_BASE;
486 end_used = 1;
487 } else {
488 goto out;
489 }
Yinghai Luecad5df2007-05-21 18:11:17 +0000490 }
491
492 }
493#endif
494
Yinghai Lu18c70d72007-09-14 14:58:33 +0000495 flags |= next_unitid & 0x1f;
Yinghai Lud4b278c2006-10-04 20:46:15 +0000496 pci_write_config16(dev, pos + PCI_CAP_FLAGS, flags);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000497
498 /* Update the Unitd id in the device structure */
499 static_count = 1;
500 for(func = dev; func; func = func->sibling) {
Yinghai Lu18c70d72007-09-14 14:58:33 +0000501 func->path.u.pci.devfn += (next_unitid << 3);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000502 static_count = (func->path.u.pci.devfn >> 3)
503 - (dev->path.u.pci.devfn >> 3) + 1;
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000504 last_func = func;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000505 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000506 /* Compute the number of unitids consumed */
Eric Biederman0ac6b412003-09-02 17:16:48 +0000507 printk_spew("%s count: %04x static_count: %04x\n",
508 dev_path(dev), count, static_count);
509 if (count < static_count) {
510 count = static_count;
511 }
512
513 /* Update the Unitid of the next device */
Yinghai Lu18c70d72007-09-14 14:58:33 +0000514 ht_unitid_base[ht_dev_num] = next_unitid;
Stefan Reinauerbbdd8f42005-12-04 21:52:58 +0000515 ht_dev_num++;
Yinghai Luecad5df2007-05-21 18:11:17 +0000516
Yinghai Lu18c70d72007-09-14 14:58:33 +0000517#if HT_CHAIN_END_UNITID_BASE != 0x20
518 if (offset_unitid) {
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000519 real_last_pos = pos;
Yinghai Lu18c70d72007-09-14 14:58:33 +0000520 real_last_unitid = next_unitid;
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000521 real_last_dev = dev;
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000522 }
523#endif
Yinghai Lu18c70d72007-09-14 14:58:33 +0000524 next_unitid += count;
525 if (next_unitid > max_unitid) {
526 max_unitid = next_unitid;
527 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000528
529 /* Setup the hypetransport link */
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000530 bus->reset_needed |= ht_setup_link(&prev, dev, pos);
Eric Biederman0ac6b412003-09-02 17:16:48 +0000531
532 printk_debug("%s [%04x/%04x] %s next_unitid: %04x\n",
533 dev_path(dev),
534 dev->vendor, dev->device,
Li-Ta Lo69c5a902004-04-29 20:08:54 +0000535 (dev->enabled? "enabled": "disabled"), next_unitid);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000536
Yinghai Lu18c70d72007-09-14 14:58:33 +0000537 } while (last_unitid != next_unitid);
538 out:
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000539 end_of_chain:
Yinghai Lu90b3e092005-01-26 22:00:20 +0000540#if OPT_HT_LINK == 1
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000541 if(bus->reset_needed) {
Eric Biederman0ac6b412003-09-02 17:16:48 +0000542 printk_info("HyperT reset needed\n");
Eric Biederman5cd81732004-03-11 15:01:31 +0000543 }
544 else {
545 printk_debug("HyperT reset not needed\n");
546 }
Eric Biederman0ac6b412003-09-02 17:16:48 +0000547#endif
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000548
Yinghai Lu18c70d72007-09-14 14:58:33 +0000549#if HT_CHAIN_END_UNITID_BASE != 0x20
550 if(offset_unitid && (ht_dev_num>1) && (real_last_unitid != HT_CHAIN_END_UNITID_BASE) && !end_used) {
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000551 uint16_t flags;
552 int i;
553 device_t last_func = 0;
554 flags = pci_read_config16(real_last_dev, real_last_pos + PCI_CAP_FLAGS);
555 flags &= ~0x1f;
556 flags |= HT_CHAIN_END_UNITID_BASE & 0x1f;
557 pci_write_config16(real_last_dev, real_last_pos + PCI_CAP_FLAGS, flags);
558
559 for(func = real_last_dev; func; func = func->sibling) {
560 func->path.u.pci.devfn -= ((real_last_unitid - HT_CHAIN_END_UNITID_BASE) << 3);
561 last_func = func;
562 }
Yinghai Luecad5df2007-05-21 18:11:17 +0000563
Stefan Reinauerbbdd8f42005-12-04 21:52:58 +0000564 ht_unitid_base[ht_dev_num-1] = HT_CHAIN_END_UNITID_BASE; // update last one
565
Yinghai Lu18c70d72007-09-14 14:58:33 +0000566 printk_debug(" unitid: %04x --> %04x\n",
567 real_last_unitid, HT_CHAIN_END_UNITID_BASE);
568
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000569 }
570#endif
Yinghai Lu18c70d72007-09-14 14:58:33 +0000571 next_unitid = max_unitid;
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000572
Yinghai Lu18c70d72007-09-14 14:58:33 +0000573 if (next_unitid > 0x20) {
574 next_unitid = 0x20;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000575 }
Yinghai Lu18c70d72007-09-14 14:58:33 +0000576 if( (bus->secondary == 0) && (next_unitid > 0x18)) {
577 next_unitid = 0x18; /* avoid K8 on bus 0 */
Yinghai Luecad5df2007-05-21 18:11:17 +0000578 }
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000579
580 /* Die if any leftover Static devices are are found.
581 * There's probably a problem in the Config.lb.
582 */
583 if(old_devices) {
584 device_t left;
585 for(left = old_devices; left; left = left->sibling) {
586 printk_debug("%s\n", dev_path(left));
587 }
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000588 printk_err("HT: Left over static devices. Check your Config.lb\n");
589 if(last_func && !last_func->sibling) // put back the left over static device, and let pci_scan_bus disable it
590 last_func->sibling = old_devices;
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000591 }
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000592
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000593 /* Now that nothing is overlapping it is safe to scan the
594 * children.
595 */
Yinghai Luecad5df2007-05-21 18:11:17 +0000596 max = pci_scan_bus(bus, 0x00, ((next_unitid-1) << 3)|7, max);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000597 return max;
Eric Biederman0ac6b412003-09-02 17:16:48 +0000598}
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000599
600/**
601 * @brief Scan a PCI bridge and the buses behind the bridge.
602 *
603 * Determine the existence of buses behind the bridge. Set up the bridge
604 * according to the result of the scan.
605 *
606 * This function is the default scan_bus() method for PCI bridge devices.
607 *
608 * @param dev pointer to the bridge device
609 * @param max the highest bus number assgined up to now
610 *
611 * @return The maximum bus number found, after scanning all subordinate busses
612 */
Yinghai Lud4b278c2006-10-04 20:46:15 +0000613unsigned int hypertransport_scan_chain_x(struct bus *bus,
614 unsigned min_devfn, unsigned max_devfn, unsigned int max)
615{
616 unsigned ht_unitid_base[4];
617 unsigned offset_unitid = 1;
618 return hypertransport_scan_chain(bus, min_devfn, max_devfn, max, ht_unitid_base, offset_unitid);
619}
620
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000621unsigned int ht_scan_bridge(struct device *dev, unsigned int max)
622{
Yinghai Lud4b278c2006-10-04 20:46:15 +0000623 return do_pci_scan_bridge(dev, max, hypertransport_scan_chain_x);
Yinghai Lu13f1c2a2005-07-08 02:49:49 +0000624}
625
626
627/** Default device operations for hypertransport bridges */
628static struct pci_operations ht_bus_ops_pci = {
629 .set_subsystem = 0,
630};
631
632struct device_operations default_ht_ops_bus = {
633 .read_resources = pci_bus_read_resources,
634 .set_resources = pci_dev_set_resources,
635 .enable_resources = pci_bus_enable_resources,
636 .init = 0,
637 .scan_bus = ht_scan_bridge,
638 .enable = 0,
639 .reset_bus = pci_bus_reset,
640 .ops_pci = &ht_bus_ops_pci,
641};