mirror of
https://github.com/fail0verflow/switch-coreboot.git
synced 2025-05-04 01:39:18 -04:00
Rename 'bus' to 'busdevice' in a few places, to make the code somewhat
more comprehensible. tested on bochs. Some documentation on what is going on in stage2 Signed-off-by: Ronald G. Minnich <rminnich@gmail.com> Acked-by: Ronald G. Minnich <rminnich@gmail.com> Acked-by: Stefan Reinauer <stepan@coresystems.de> git-svn-id: svn://coreboot.org/repository/LinuxBIOSv3@136 f3766cd6-281f-0410-b1cd-43a5c92072e9
This commit is contained in:
parent
0e68335b17
commit
890cbc8210
3 changed files with 434 additions and 24 deletions
|
@ -637,32 +637,34 @@ void dev_phase2(void)
|
|||
*
|
||||
* @return The maximum bus number found, after scanning all subordinate busses
|
||||
*/
|
||||
unsigned int dev_phase3(struct device * bus, unsigned int max)
|
||||
unsigned int dev_phase3(struct device * busdevice, unsigned int max)
|
||||
{
|
||||
unsigned int new_max;
|
||||
int do_phase3;
|
||||
if ( !bus ||
|
||||
!bus->enabled ||
|
||||
!bus->ops ||
|
||||
!bus->ops->phase3)
|
||||
post_code(0x42);
|
||||
if ( !busdevice ||
|
||||
!busdevice->enabled ||
|
||||
!busdevice->ops ||
|
||||
!busdevice->ops->phase3)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
do_phase3 = 1;
|
||||
while(do_phase3) {
|
||||
int link;
|
||||
new_max = bus->ops->phase3(bus, max);
|
||||
new_max = busdevice->ops->phase3(busdevice, max);
|
||||
do_phase3 = 0;
|
||||
for(link = 0; link < bus->links; link++) {
|
||||
if (bus->link[link].reset_needed) {
|
||||
if (reset_bus(&bus->link[link])) {
|
||||
for(link = 0; link < busdevice->links; link++) {
|
||||
if (busdevice->link[link].reset_needed) {
|
||||
if (reset_bus(&busdevice->link[link])) {
|
||||
do_phase3 = 1;
|
||||
} else {
|
||||
bus->bus->reset_needed = 1;
|
||||
busdevice->bus->reset_needed = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
post_code(0x4e);
|
||||
return new_max;
|
||||
}
|
||||
|
||||
|
@ -698,6 +700,7 @@ void dev_root_phase3(void)
|
|||
if (root->chip_ops && root->chip_ops->enable_dev) {
|
||||
root->chip_ops->enable_dev(root);
|
||||
}
|
||||
post_code(0x41);
|
||||
if (!root->ops) {
|
||||
printk(BIOS_ERR, "dev_root_phase3 missing 'ops' initialization\nPhase 3: Failed\n");
|
||||
return;
|
||||
|
@ -740,6 +743,7 @@ void dev_phase4(void)
|
|||
printk(BIOS_ERR, "dev_root ops missing read_resources\nPhase 4: Failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!root->ops->phase4_set_resources) {
|
||||
printk(BIOS_ERR, "dev_root ops missing set_resources\nPhase 4: Failed\n");
|
||||
return;
|
||||
|
|
|
@ -93,20 +93,20 @@ void root_dev_set_resources(struct device * root)
|
|||
* @return Largest bus number used.
|
||||
*/
|
||||
static int smbus_max = 0;
|
||||
unsigned int scan_static_bus(struct device * bus, unsigned int max)
|
||||
unsigned int scan_static_bus(struct device * busdevice, unsigned int max)
|
||||
{
|
||||
struct device * child;
|
||||
unsigned link;
|
||||
|
||||
printk(BIOS_SPEW, "%s for %s\n", __func__, dev_path(bus));
|
||||
printk(BIOS_SPEW, "%s for %s\n", __func__, dev_path(busdevice));
|
||||
|
||||
for(link = 0; link < bus->links; link++) {
|
||||
for(link = 0; link < busdevice->links; link++) {
|
||||
/* for smbus bus enumerate */
|
||||
child = bus->link[link].children;
|
||||
child = busdevice->link[link].children;
|
||||
if(child && child->path.type == DEVICE_PATH_I2C) {
|
||||
bus->link[link].secondary = ++smbus_max;
|
||||
busdevice->link[link].secondary = ++smbus_max;
|
||||
}
|
||||
for(child = bus->link[link].children; child; child = child->sibling) {
|
||||
for(child = busdevice->link[link].children; child; child = child->sibling) {
|
||||
if (child->chip_ops && child->chip_ops->enable_dev) {
|
||||
child->chip_ops->enable_dev(child);
|
||||
}
|
||||
|
@ -123,8 +123,8 @@ unsigned int scan_static_bus(struct device * bus, unsigned int max)
|
|||
child->enabled?"enabled": "disabled");
|
||||
}
|
||||
}
|
||||
for(link = 0; link < bus->links; link++) {
|
||||
for(child = bus->link[link].children; child; child = child->sibling) {
|
||||
for(link = 0; link < busdevice->links; link++) {
|
||||
for(child = busdevice->link[link].children; child; child = child->sibling) {
|
||||
if (!child->ops || !child->ops->phase3)
|
||||
continue;
|
||||
printk(BIOS_SPEW, "%s scanning...\n", dev_path(child));
|
||||
|
@ -132,7 +132,7 @@ unsigned int scan_static_bus(struct device * bus, unsigned int max)
|
|||
}
|
||||
}
|
||||
|
||||
printk(BIOS_SPEW, "%s for %s done\n", __func__, dev_path(bus));
|
||||
printk(BIOS_SPEW, "%s for %s done\n", __func__, dev_path(busdevice));
|
||||
|
||||
return max;
|
||||
}
|
||||
|
|
|
@ -450,11 +450,33 @@ Stage 2: Device tree
|
|||
\begin_layout Standard
|
||||
Run the standard device tree code.
|
||||
This code runs in 6 phases.
|
||||
The device tree, as set up by dts, has two ways it can be traversed.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The first is the hierarchy formed by busses and devices.
|
||||
Devices have up to MAX_LINKS links, which are initialized as part of the
|
||||
process of creating the static tree.
|
||||
These links point to busses.
|
||||
A bus has a child device, a device associated with it (e.g.
|
||||
a PCI bridge device), and other attributes described elsewhere.
|
||||
Some operations, such as enumeration, require that the tree be traversed
|
||||
in the hierarchy represented by the bus/device relationship.
|
||||
This traversal starts at the root device, and for each link, follows those
|
||||
busses to the other devices.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The second is a simple traversal, via linked list, of all the devices.
|
||||
This faster, less complex traversal, is performed when there is no need
|
||||
to be concerned with the bus/device relationship.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subparagraph*
|
||||
Phase 1
|
||||
Phase 1 -- making printk work
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
|
@ -463,6 +485,11 @@ These are any functions that are required to make printk operational.
|
|||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The simple traversal (forall devices) is used for this phase.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Post codes:
|
||||
\end_layout
|
||||
|
@ -476,12 +503,18 @@ Exit: 0x2f
|
|||
\end_layout
|
||||
|
||||
\begin_layout Subparagraph*
|
||||
Phase 2
|
||||
Phase 2 -- preparation for bus scan
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
These are functions that are required before any PCI operations of any kind
|
||||
are run.
|
||||
These functions may call printk.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The simple traversal (forall devices) is used for this phase.
|
||||
|
||||
\end_layout
|
||||
|
||||
|
@ -498,7 +531,301 @@ Exit: 0x3f
|
|||
\end_layout
|
||||
|
||||
\begin_layout Subparagraph*
|
||||
Phase 3
|
||||
Phase 3 -- bus scan
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
This phase is typical of all the phases that do a hierarchical traversal.
|
||||
It starts at the root device (i.e.
|
||||
the mainboard), which uses the distinguished function
|
||||
\emph on
|
||||
dev_root_phase3
|
||||
\emph default
|
||||
.
|
||||
Some root devices have special setup requirements, and there is a way to
|
||||
call this special setup code.
|
||||
If the dts has specified a configuration for the root device, it is possible
|
||||
to set up an enable_dev function.
|
||||
In other words, for any device, it is possible to call some 'preparation'
|
||||
code for that device.
|
||||
We show an example of such a function below, from the qemu mainboard.
|
||||
First, we show the dts, to show how the chip operations can be enabled.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
\begin_inset Float figure
|
||||
wide false
|
||||
sideways false
|
||||
status open
|
||||
|
||||
\begin_layout Caption
|
||||
The dts for the emulation/qemu target
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
/{ config="mainboard,emulation,qemu-i386";
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
\InsetSpace ~
|
||||
\InsetSpace ~
|
||||
\InsetSpace ~
|
||||
\InsetSpace ~
|
||||
cpus { ...};
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
%%
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
struct mainboard_emulation_qemu_i386_config root = { .nothing = 1, };
|
||||
\end_layout
|
||||
|
||||
\end_inset
|
||||
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The dts has been shortened for readability.
|
||||
Note the use of the 'config=' in the root.
|
||||
It specifies that there is an initialized structure after the %% in the
|
||||
dts file.
|
||||
The structure is at the bottom.
|
||||
The dts generates the code shown below.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
\begin_inset Float figure
|
||||
wide false
|
||||
sideways false
|
||||
status open
|
||||
|
||||
\begin_layout Caption
|
||||
Code generated for the dts
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
struct mainboard_emulation_qemu_i386_config root = { .nothing = 1, };
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
struct device dev_root = {
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
.path = { .type = DEVICE_PATH_ROOT },
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
.chip_ops = &mainboard_emulation_qemu_i386_ops,
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
.
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
.
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
.
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
};
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
|
||||
\end_layout
|
||||
|
||||
\end_inset
|
||||
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The code after the %% is reproduced exactly.
|
||||
The dts generates a generic device struct, and initializes the .chip_ops
|
||||
struct member to point to the mainboard_emulation_qemu_i386_ops structure.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
When phase 3 is run, the code checks the chip_ops structure member and,
|
||||
if it is non-zero, checks the chip_ops->enable_dev pointer and, if it is
|
||||
non-zero, calls it.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The mainboard code is shown below.
|
||||
The enable_dev function will be called in phase 3,
|
||||
\emph on
|
||||
before
|
||||
\emph default
|
||||
any other enumeration is done for that device.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
\begin_inset Float figure
|
||||
wide false
|
||||
sideways false
|
||||
status open
|
||||
|
||||
\begin_layout Caption
|
||||
What the mainboard file looks like with enable_dev
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
static void enable_dev(struct device *dev){
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
printk(BIOS_INFO, "qemu-i386 enable_dev done
|
||||
\backslash
|
||||
n");
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
}
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
struct chip_operations mainboard_emulation_qemu_i386_ops = {
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
.name = "QEMU Mainboard",
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
.enable_dev = enable_dev
|
||||
\end_layout
|
||||
|
||||
\begin_layout LyX-Code
|
||||
};
|
||||
\end_layout
|
||||
|
||||
\end_inset
|
||||
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Root_dev_phase3, which is called with the root
|
||||
\emph on
|
||||
device
|
||||
\emph default
|
||||
, calls dev_phase3, for each device attached to the root device.
|
||||
The devices are, in fact, bridge devices, i.e.
|
||||
the device attached to a bus.
|
||||
Dev_phase3, in turn, checks the bus device to see if it is a non-NULL pointer,
|
||||
if is enabled, if it has ops and a phase 3 ops; if so, the functions calls
|
||||
the bus device's phase 3 op to kick off scanning of busses.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The phase 3 op is different for each type of bus.
|
||||
For the root bus, which is statically configured, the phase 3 operation
|
||||
walks the set of statically initialized pointers for the root device; for
|
||||
the (e.g.) PCI device, which is much more dynamic, the code does actual probing.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Some busses require a reset operation after scanning.
|
||||
The dev_phase3 code will scan its subordinate busses, and then test all
|
||||
the busses to see if a reset is needed.
|
||||
If so, for each bus that needs a reset, a reset is performed, and
|
||||
\emph on
|
||||
the bus scanning operation is repeated
|
||||
\emph default
|
||||
until a reset is no longer needed.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
To sum up, the operation for phase 3, bus scanning, is as follows
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
The root device is the starting point for bus scanning
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
After some initial setup, including an optional call to the chip_ops->enable_dev
|
||||
method for the root device, the dev_phase3 function is called with the
|
||||
root device as the parameter.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
The dev_phase3 function, after checking that the bus has the ability to
|
||||
be scanned (i.e.
|
||||
the device has an ops->phase3 pointer), scans the bus by calling the phase3
|
||||
function for the bus.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
If scanning results in a need for a reset, the reset(s) are performed on
|
||||
the links that need it, and
|
||||
\emph on
|
||||
the scan operation is repeated
|
||||
\emph default
|
||||
.
|
||||
This cycle continues until no resets are needed.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The per-device phase 3 operation for a bus has a mutually recursive relationship
|
||||
with dev_phase3.
|
||||
The per-device function is called with the pointer to the device that was
|
||||
passed into dev_phase3.
|
||||
The per-device phase 3 iterates over the set of child links (i.e.
|
||||
busses) that are attached to the device and, for each link, checks the
|
||||
chip_ops of the child link device for each link, and determines whether
|
||||
to call the enable_dev for each child link device.
|
||||
The one quite non-intuitive action that some of these functions take is
|
||||
to enable the child link device, whether the child link device is enabled
|
||||
or not in the configuration.
|
||||
This enable is done in order to ensure that child busses are properly enumerate
|
||||
d, whether they are enabled or not.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Once the child link devices have been properly examined and (for some busses)
|
||||
set up for enumeration, the per-device phase 3 operation iterates over
|
||||
the child link devices one more time and calls dev_phase 3 for each child
|
||||
link device.
|
||||
This final loop completes the enumeration for this level of the hierarchy.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
At the end of the per-device phase 3 operation, the structure of the physical
|
||||
device tree has been completely determined, including both the static devices
|
||||
and any dynamic devices, such as cards plugged into PCI slots.
|
||||
For each level of the tree, the structure which define the devices, and
|
||||
busses, have been filled in, and the presence or absence of devices has
|
||||
been determined.
|
||||
At the end of this pass, it is possible to determine what resources each
|
||||
device will need, and to allocate those resources as needed.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subparagraph*
|
||||
|
@ -506,17 +833,96 @@ Post codes:
|
|||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Entry: 0x40
|
||||
root_dev_phase3 entry: 0x40
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
Exit: 0x4f
|
||||
After enable_dev is tested and potentially called: 0x41
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
dev_phase3 entry: 0x42
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
dev_phase3 entry: 0x4e (note: since this is a recursive function, the post
|
||||
codes can cycle from 4e to 43 and back again)
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
root_dev_phase3 exit: 0x4f
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subparagraph*
|
||||
Phase 4
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
The point of phase 4 is to determine what resources are needed for each
|
||||
device; to allocate those resources; and to configure the devices with
|
||||
those resource values.
|
||||
Resources are determined in one of two ways.
|
||||
Some devices, if present, have static resource requirements (e.g.
|
||||
superio parts, which have a fixed requirement for two I/O addresses).
|
||||
Other devices have resource requirements that can be determined by reading
|
||||
registers (such as Base Address Registers in PCI) and are hence dynamic.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
A similarl mutual recursion is employed, starting again at the root.
|
||||
The root devices phase 4 ops are called with the root device as a parameter.
|
||||
For each link on the device, and for each type of resource that is needed
|
||||
to be determined, the compute_allocate_resource function is called.
|
||||
This function takes a bus, resource, mask, and type as a parameter.
|
||||
As busses as scanned, and resources are read, the mask is applied ot the
|
||||
resource and compared to the type, so as to select the type of resource
|
||||
desired.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Once the reading of resources is done, the root device has IO resources
|
||||
as resource 0, and mem resources as resource 1.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
After the generic resource reading has been done, there is one special case,
|
||||
for VGA, which overrides the standard hierarchical traversal.
|
||||
If VGA console is enabled, the bridges must be configured in such a way
|
||||
as to pick a
|
||||
\begin_inset Quotes eld
|
||||
\end_inset
|
||||
|
||||
primary
|
||||
\begin_inset Quotes erd
|
||||
\end_inset
|
||||
|
||||
VGA device.
|
||||
Once the resources have been enumerated, a function called allocate_vga_resourc
|
||||
e is called.
|
||||
This function traverses the devices in non-hierarchical order, and selects
|
||||
one of them as the VGA device for the so-called
|
||||
\begin_inset Quotes eld
|
||||
\end_inset
|
||||
|
||||
compatibilty chain
|
||||
\begin_inset Quotes erd
|
||||
\end_inset
|
||||
|
||||
.
|
||||
Once this device is selected, the function walkss the tree from the device
|
||||
to the root, enabling the VGA CTL bit in each bridge.
|
||||
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Once this phase has been done, all the memory and IO resources have been
|
||||
enumerated and allocated to each device, and to each bridge, in the system.
|
||||
This phase is easily the most complex of all the phases in stage 2.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subparagraph*
|
||||
Post codes:
|
||||
\end_layout
|
||||
|
|
Loading…
Add table
Reference in a new issue