Sorry it's rough. I'm really ready to clean up the #if statements in this
code. This is for hypertransport.c, next is northbridge/amd/k8/*.c
Thanks,
Myles
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
--
coreboot mailing list: [email protected]
http://www.coreboot.org/mailman/listinfo/coreboot