HyperTransport Scanning HT scanning starts with hypertransport_scan_chain. It is much like pci_scan_bus, but it takes two extra parameters: a pointer to an array of UnitIDs, and an offset_unitid parameter. offset_unitid sets the min_unitid to HT_CHAIN_UNITID_BASE instead of 1. This code has lots of #if statements. It makes the code harder to read. A major problem with the defines in this code is that they all seem to be geared toward single HT chains. Notice that there is no HT_CHAIN_2_UNITID_BASE, even though there could be multiple chains. All HyperTransport devices start out with a UnitID (similar to a PCI device number) of zero. This is OK because HyperTransport is point-to-point. This creates the need for early enumeration to be able to access the southbridge and the boot code at reset. The first thing hypertransport_scan_chain does is call ht_collapse_early_enumeration. This function is also complicated by #if statements, but is logically just going through the chain setting each HT device's UnitID to 0. Begin 1.1 Collapse It first checks to see if the link is up, which I think should be moved to another function. The check for "already collapsed" is pretty complex looking. It returns if no device responds at 0, and this if statement is true. if ((!offset_unitid) || (offset_unitid && (!((HT_CHAIN_END_UNITID_BASE == 0) && (HT_CHAIN_END_UNITID_BASE < HT_CHAIN_UNITID_BASE))))) { It's equivalent to: if ((!offset_unitid) || (!((HT_CHAIN_END_UNITID_BASE == 0) && (HT_CHAIN_END_UNITID_BASE < HT_CHAIN_UNITID_BASE)))) { So return if no one responds and there's no need to offset UnitIDs, or the end UnitID isn't zero or the end UnitID is less than the chain UnitID. Then start from PCI_DEVFN(1,0) and go through all devices, clearing the UnitID if the device has a slave capability. End 1.1 Collapse Next the children are removed and put on an old_devices list just like pci_scan_bus does. Then the link is checked to see if initialization is complete. This probably needs to be refactored with the code in collapse. Now ht_scan_get_devs is called, which should just be called ht_get_devs. It is a little different from pci_get_devs, because it is supposed to get the next device and all of it's sub devices. It also updates the pointer to old_devices. Begin 1.2 Get Devs It starts at the beginning of the old_devices and breaks the list at the next HyperTransport device. This means that it leaves each device connected to its siblings that are not HyperTransport devices. As an example, the AMD 8111 southbridge has multiple PCI devices that respond once the UnitID of the southbridge is set. All these devices remain linked as siblings when they are removed from this list. The HT child and his siblings are added to the end of the bus' children list, and old_devices is updated to be the next HT device. End 1.2 Get Devs At this point the device that just got found is probed with pci_probe_dev. Because the HT enumeration has been collapsed, this will only work if the device has devfn==0. If it is successful, the HT slave capability is looked up and the UnitID is updated. Then it spins through and updates the UnitIDs of the siblings of this device, who reside on the same device. Unfortunately this seems happens before the numbers get set to the correct device number and has to be undone in the case where that changes later. Now it reads the number of UnitIDs that the device consumed from the device, and checks to see if there are more devices from the tree or from the device. It takes the larger number and sets count. Now it fills in the UnitID in ht_unitid_base and increments ht_dev_num. At this point, if the chains end UnitID is less than the base UnitID (HT_CHAIN_END_UNITID_BASE < HT_CHAIN_UNITID_BASE) the "real" UnitID is saved with the device and capability number. Now ht_setup_link is called. This doesn't do much for us right now because OPT_HT_LINK is defined as 0, but that'll have to change, since it means the difference between 8-bit 200MHz links and 16-bit 1.0GHz links. Begin 1.3 Setup Link The first thing this function does is determine whether the last write to the UnitID came from upstream or downstream. This is to support double-ended HT chains. Then if we're optimizing the link we read the capabilities (frequency and width) of the links and set them so that they match at the highest possible settings. If they change we set the reset_needed flag and update the registers. Then we set prev (the last link in the chain) to the other link on the current device, and return. End 1.3 Setup Link Now we go back to the link check and continue until we reach the end of the chain. This can be reached at the end of the do while when the next UnitID is the last one or is greater than the max devfn to find. It can also be reached if it encounters a link that is not set up. At this point there's a check to see if the end UnitID is supposed to be less than the base UnitID. If it is, the UnitID is changed along with the devfns of all the siblings. Now that the chain has had all HyperTransport devices set to have non-zero UnitIDs, pci_scan_bus is called from 0 to the largest found UnitID translated to a devfn (or 0x1f if it was larger to protect the processors.) Begin example An example here is probably necessary. In AMD Serengeti Cheetah, the processor is connected to an amd 8132 PCI-X tunnel, which is connected to the 8111 southbridge. When this chain is collapsed, both devices have UnitID 0, so the 8132 claims all config reads and writes to 0. The list of devices looks like this: bus->children-> old_devices-> 8132(A.0)->8132(A.1)->8132(B.0)->8132(B.1)->8111(A.0)->8111(A.1)->8111(B.0)...->8111(last) There are two UnitIDs on an 8132, each with two functions. When ht_get_devs splits this list, it needs to come back with two lists bus->children-> 8132(A.0)->8132(A.1)->8132(B.0)->8132(B.1)-> old_devices-> 8111(A.0)->8111(A.1)->8111(B.0)...->8111(last) After the UnitIDs are assigned (8132 -> 0xa0 and 8111 -> 0x6) the lists should be (I don't think this works correctly because the children get put on the end always.) bus->children-> 8111(0x6.0)->8111(0x6.1)->8111(0x7.0)...->8111(last)->8132(0xa.0)->8132(0xa.1)->8132(0xb.0)->8132(0xb.1) old_devices-> End example