Merge remote-tracking branch 'origin/master' into system76
Change-Id: I4593b91276d447f8ac00daca7388fdfb22bca7f2
This commit is contained in:
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -42,3 +42,7 @@
|
||||
url = https://review.coreboot.org/amd_blobs.git
|
||||
update = none
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/cmocka"]
|
||||
path = 3rdparty/cmocka
|
||||
url = ../cmocka.git
|
||||
update = none
|
||||
|
2
3rdparty/amd_blobs
vendored
2
3rdparty/amd_blobs
vendored
Submodule 3rdparty/amd_blobs updated: 17d0288698...0ac1af437d
1
3rdparty/cmocka
vendored
Submodule
1
3rdparty/cmocka
vendored
Submodule
Submodule 3rdparty/cmocka added at 672c5cee79
2
3rdparty/libgfxinit
vendored
2
3rdparty/libgfxinit
vendored
Submodule 3rdparty/libgfxinit updated: 2e87c0d40a...8fc8e49a93
2
3rdparty/vboot
vendored
2
3rdparty/vboot
vendored
Submodule 3rdparty/vboot updated: 3aab301473...c531000f85
@@ -23,7 +23,7 @@ write the ASL (ACPI Source Language) code yourself.
|
||||
## Device drivers
|
||||
|
||||
Let's take a look at an example entry from
|
||||
``src/mainboard/google/hatch/variant/hatch/overridetree.cb``:
|
||||
``src/mainboard/google/hatch/variants/hatch/overridetree.cb``:
|
||||
|
||||
```
|
||||
device pci 15.0 on
|
||||
|
@@ -6,3 +6,4 @@ they allow to easily reuse existing code accross platforms.
|
||||
|
||||
* [IPMI KCS](ipmi_kcs.md)
|
||||
* [SMMSTORE](smmstore.md)
|
||||
* [SoundWire](soundwire.md)
|
||||
|
496
Documentation/drivers/soundwire.md
Normal file
496
Documentation/drivers/soundwire.md
Normal file
@@ -0,0 +1,496 @@
|
||||
# SoundWire Implementation in coreboot
|
||||
|
||||
## Introduction
|
||||
|
||||
SoundWire is an audio interface specification from the MIPI Alliance.
|
||||
|
||||
- Low complexity
|
||||
- Low power
|
||||
- Low latency
|
||||
- Two pins (clock and data)
|
||||
- Multi-drop capable
|
||||
- Multiple audio streams
|
||||
- Embedded control/command channel
|
||||
|
||||
The main *SoundWire Specification* is at version 1.2 and can be downloaded from
|
||||
<https://mipi.org> but it is unfortunately only available to MIPI Alliance members.
|
||||
|
||||
There is a separate *SoundWire Discovery and Configuration (DisCo) Specification* which
|
||||
is at version 1.0 and is available for non-members after providing name and email at
|
||||
<https://resources.mipi.org/disco_soundwire>.
|
||||
|
||||
The coreboot implementation is based on the SoundWire DisCo Specification which defines
|
||||
object hierarchy and properties for providing topology and configuration information to
|
||||
OS kernel drivers via ACPI or DeviceTree.
|
||||
|
||||
SoundWire itself is architecture independent and the coreboot basic definition is also not
|
||||
specific to any to any SoC. The examples in this document use ACPI to generate properties,
|
||||
but the same structures and properties would be needed in a DeviceTree implementation.
|
||||
|
||||
## Bus
|
||||
|
||||
The SoundWire bus commonly consists of two pins:
|
||||
|
||||
* Clock: A common clock signal distributed from the master to all of the slaves.
|
||||
* Data: A shared data signal that can be driven by any of the devices, and has a defined
|
||||
value when no device is driving it.
|
||||
|
||||
While most designs have one data lane it is possible for a multi-lane device to have up
|
||||
to 8 data lanes and thus would have more than two pins.
|
||||
|
||||
A SoundWire bus consists of one master device, up to 11 slave devices, and an optional
|
||||
monitor interface for debug.
|
||||
|
||||
SoundWire is an enumerable bus, but not a discoverable one. That means it is required
|
||||
for firmware to provide details about the connected devices to the OS.
|
||||
|
||||
### Controller
|
||||
|
||||
A SoundWire controller contains one or more master devices. The handling of multiple
|
||||
masters is left up to the implementation, they may share a clock or be operated
|
||||
independently or entirely in tandem. The master devices connected to a controller are
|
||||
also referred to as links.
|
||||
|
||||
In coreboot the controller device is provided by the SoC or an add-in PCI card.
|
||||
|
||||
### Master
|
||||
|
||||
A SoundWire master (or link) device is responsible for clock and data handling, bus
|
||||
management, and bit slot allocation.
|
||||
|
||||
In coreboot the definition of the master device is left up to the controller and the
|
||||
mainboard should only need to know the controller's SoundWire topology (number of masters)
|
||||
to configure `devicetree.cb`.
|
||||
|
||||
It may however be expected to provide some additional SoC-specific configuration data to
|
||||
the controller, such as an input clock rate or a list of available masters that cannot
|
||||
be determined at run time.
|
||||
|
||||
### Slave
|
||||
|
||||
SoundWire slave devices are connected to a master and respond to the two-wire control
|
||||
information on the SoundWire bus. There can be up to 11 slave devices on a bus and they
|
||||
are capable of interrupting and waking the host.
|
||||
|
||||
Slave devices may also have master links which can be connected to other slave devices.
|
||||
It is also possible for a multi-lane slave device to have multiple data lanes connected
|
||||
to different combinations of master and slave devices.
|
||||
|
||||
In coreboot the slave device is defined by a codec driver which should be found in the
|
||||
source tree at `src/drivers/soundwire`.
|
||||
|
||||
The mainboard provides:
|
||||
|
||||
* Master link that this slave device is connected to.
|
||||
* Unique ID that this codec responds to on the SoundWire bus.
|
||||
* Multi-lane mapping. (optional)
|
||||
|
||||
The codec driver provides:
|
||||
|
||||
* Slave device properties.
|
||||
* Audio Mode properties including bus frequencies and sampling rates.
|
||||
* Data Port 1-14 properties such as word lengths, interrupt support, channels.
|
||||
* Data Port 0 and Bulk Register Access properties. (optional)
|
||||
|
||||
### Monitor
|
||||
|
||||
A SoundWire monitor device is defined that allows for test equipment to snoop the bus and
|
||||
take over and issue commands. The monitor interface is not defined for coreboot.
|
||||
|
||||
### Example SoundWire Bus
|
||||
|
||||
```
|
||||
+---------------+ +---------------+
|
||||
| | Clock Signal | |
|
||||
| Master |-------+-------------------------------| Slave |
|
||||
| Interface | | Data Signal | Interface 1 |
|
||||
| |-------|-------+-----------------------| |
|
||||
+---------------+ | | +---------------+
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+--+-------+--+
|
||||
| |
|
||||
| Slave |
|
||||
| Interface 2 |
|
||||
| |
|
||||
+-------------+
|
||||
```
|
||||
|
||||
## coreboot
|
||||
|
||||
The coreboot implementation of SoundWire integrates with the device model and takes
|
||||
advantage of the hierarchical nature of `devicetree.cb` to populate the topology.
|
||||
|
||||
The architecture-independent SoundWire tables are defined at
|
||||
|
||||
src/include/device/soundwire.h
|
||||
|
||||
Support for new devices comes in three forms:
|
||||
|
||||
1. New controller and master drivers. The first implementation in coreboot is for an Intel
|
||||
SoC but the SoundWire specification is in wide use on various ARM SoCs.
|
||||
|
||||
Controller drivers can be implemented in `src/soc` or `src/drivers` and should
|
||||
strive to re-use code as much as possible between different SoC generations from the
|
||||
same vendor.
|
||||
|
||||
2. New codec drivers. These should be implemented for each codec that is added which
|
||||
supports SoundWire. The properties vary between codecs and careful study of the data sheet
|
||||
is necessary to ensure proper operation.
|
||||
|
||||
Codec drivers should be implemented in `src/drivers/soundwire` as separate chip drivers.
|
||||
As every codec is different there may not be opportunities of code re-use except between
|
||||
similar codecs from the same vendor.
|
||||
|
||||
3. New mainboards with SoundWire support. The mainboard will combine controllers and codecs
|
||||
to form a topology that is described in `devicetree.cb`. Some devices may need to provide
|
||||
board-specific configuration information, and multi-lane devices will need to provide the
|
||||
master/slave lane map.
|
||||
|
||||
## ACPI Implementation
|
||||
|
||||
The implementation for x86 devices relies on ACPI for providing device properties to the OS
|
||||
kernel drivers.
|
||||
|
||||
The ACPI implementation can be found at
|
||||
|
||||
src/acpi/soundwire.c
|
||||
|
||||
And used by including
|
||||
|
||||
#include <acpi/acpi_soundwire.h>
|
||||
|
||||
### Controller
|
||||
|
||||
The controller driver should populate a `struct soundwire_controller`:
|
||||
|
||||
```c
|
||||
/**
|
||||
* struct soundwire_controller - SoundWire controller properties.
|
||||
* @master_count: Number of masters present on this device.
|
||||
* @master_list: One entry for each master device.
|
||||
*/
|
||||
struct soundwire_controller {
|
||||
unsigned int master_list_count;
|
||||
struct soundwire_link master_list[SOUNDWIRE_MAX_DEV];
|
||||
};
|
||||
```
|
||||
|
||||
Once the detail of the master links are specified in the `master_list` variable, the controller
|
||||
properties for the ACPI object can be generated:
|
||||
|
||||
```c
|
||||
struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
|
||||
soundwire_gen_controller(dsd, &soc_controller, NULL);
|
||||
acpi_dp_write(dsd);
|
||||
```
|
||||
|
||||
If the controller needs to generate custom properties for links it can provide a callback
|
||||
function to `soundwire_gen_controller()` instead of passing NULL:
|
||||
|
||||
```c
|
||||
static void controller_link_prop_cb(struct acpi_dp *dsd, unsigned int id,
|
||||
struct soundwire_controller *controller)
|
||||
{
|
||||
acpi_dp_add_integer(dsd, "custom-link-property", 1);
|
||||
}
|
||||
```
|
||||
|
||||
### Codec
|
||||
|
||||
The codec driver should populate a *struct soundwire_codec* with necessary properties:
|
||||
|
||||
```c
|
||||
/**
|
||||
* struct soundwire_codec - Contains all configuration for a SoundWire codec slave device.
|
||||
* @slave: Properties for slave device.
|
||||
* @audio_mode: Properties for Audio Mode for Data Ports 1-14.
|
||||
* @dpn: Properties for Data Ports 1-14.
|
||||
* @multilane: Properties for slave multilane device. (optional)
|
||||
* @dp0_bra_mode: Properties for Bulk Register Access mode for Data Port 0. (optional)
|
||||
* @dp0: Properties for Data Port 0 for Bulk Register Access. (optional)
|
||||
*/
|
||||
struct soundwire_codec {
|
||||
struct soundwire_slave *slave;
|
||||
struct soundwire_audio_mode *audio_mode[SOUNDWIRE_MAX_DEV];
|
||||
struct soundwire_dpn_entry dpn[SOUNDWIRE_MAX_DPN - SOUNDWIRE_MIN_DPN];
|
||||
struct soundwire_multilane *multilane;
|
||||
struct soundwire_bra_mode *dp0_bra_mode[SOUNDWIRE_MAX_DEV];
|
||||
struct soundwire_dp0 *dp0;
|
||||
};
|
||||
```
|
||||
|
||||
Many of these properties are optional, and depending on the codec will not be supported.
|
||||
|
||||
#### Slave Device Properties
|
||||
|
||||
These properties provide information about the codec device and what features it supports:
|
||||
|
||||
* Wake capability
|
||||
* Clock stop behavior
|
||||
* Clock and channel state machine behavior
|
||||
* Features like register pages, broadcast read, bank delay, and high performance PHY
|
||||
|
||||
#### Multi-lane Slave Device Properties
|
||||
|
||||
Most slave devices have a single data pin and a single lane, but it is possible for up to
|
||||
7 other lanes to be supported on a device. These lanes can be connected to other master
|
||||
links or to other slave devices.
|
||||
|
||||
If a codec supports this feature it must indicate that by providing an entry for
|
||||
`struct soundwire_multilane` in the chip configuration.
|
||||
|
||||
```c
|
||||
/**
|
||||
* struct drivers_soundwire_example_config - Example codec configuration.
|
||||
* @multilane: Multi-lane slave configuration.
|
||||
*/
|
||||
struct drivers_soundwire_example_config {
|
||||
struct soundwire_multilane multilane;
|
||||
};
|
||||
```
|
||||
|
||||
The mainboard is required to provide the lane map in `devicetree.cb` for any codec that has
|
||||
multiple lanes connected. This includes the definition up to 7 entries that indicate which
|
||||
lane number on the slave devices (array index starting at 1) maps to which other device:
|
||||
|
||||
```
|
||||
chip drivers/soundwire/multilane_codec
|
||||
register "multilane.lane_mapping" = "{
|
||||
{
|
||||
# Slave Lane 1 maps to Master Lane 2
|
||||
.lane = 1,
|
||||
.direction = MASTER_LANE,
|
||||
.connection.master_lane = 2
|
||||
},
|
||||
{
|
||||
# Slave Lane 3 maps to Slave Link B
|
||||
.lane = 3,
|
||||
.direction = SLAVE_LINK,
|
||||
.connection.slave_link = 1
|
||||
}
|
||||
}"
|
||||
device generic 0.0 on end
|
||||
end
|
||||
```
|
||||
|
||||
#### Data Port 0 Properties
|
||||
|
||||
SoundWire Data Port 0 (DP0) is a special port used for control and status operation relating
|
||||
to the whole device interface, and as a special data port for bulk read/write operations.
|
||||
|
||||
The properties for data port 0 are different from that of data ports 1-14 and are about the
|
||||
control channel behavior and the overall bulk register mode.
|
||||
|
||||
Data port 0 is not required to be supported by the slave device.
|
||||
|
||||
#### Bulk Register Access Mode Properties
|
||||
|
||||
Bulk Register Access (BRA) is an optional mechanism for transporting higher bandwidth of
|
||||
register operations than the typical command mechanism. The BRA protocol is a particular
|
||||
format of the data on the (optional) data port 0 connection between the master and slave.
|
||||
|
||||
The BRA protocol may have alignment or timing requirements that are directly related to the
|
||||
bus frequencies. As a result there may be several configurations listed, for symmetry with
|
||||
the audio modes paired with data ports 1-14.
|
||||
|
||||
#### Data Port 1-14 Properties
|
||||
|
||||
Data ports 1-14 are typically dedicated to streaming audio payloads, and each data port can
|
||||
have from 1 to 8 channels. There are different levels of data ports, with some registers
|
||||
being required and supported on all data ports and some optional registers only being used
|
||||
on some data ports.
|
||||
|
||||
Data ports can have both a sink and a source component, and the codec may support one or
|
||||
both of these on each port.
|
||||
|
||||
Similar to data port 0 the properties defined here describe the capabilities and supported
|
||||
features of each data port, and they may be configured separately. For example the Maxim
|
||||
MAX98373 codec supports a 32bit source data port for speaker output, and a 16bit sink data
|
||||
port for speaker sense data.
|
||||
|
||||
#### Audio Mode Properties
|
||||
|
||||
Each data port may be tied to one or more audio modes. The audio mode describes the actual
|
||||
audio capabilities of the codec, including supported frequencies and sample rates. These
|
||||
modes can be shared by multiple data ports and do not need to be duplicated.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
static struct soundwire_audio_mode audio_mode = {
|
||||
.bus_frequency_max = 24 * MHz,
|
||||
.bus_frequency_min = 24 * KHz,
|
||||
.max_sampling_frequency = 192 * KHz,
|
||||
.min_sampling_frequency = 8 * KHz,
|
||||
};
|
||||
static struct soundwire_dpn codec_dp1 = {
|
||||
[...]
|
||||
.port_audio_mode_count = 1,
|
||||
.port_audio_mode_list = {0}
|
||||
};
|
||||
static struct soundwire_dpn codec_dp3 = {
|
||||
[...]
|
||||
.port_audio_mode_count = 1,
|
||||
.port_audio_mode_list = {0}
|
||||
};
|
||||
```
|
||||
|
||||
### Generating Codec Properties
|
||||
|
||||
Once the properties are known it can generate the ACPI code with:
|
||||
|
||||
```c
|
||||
struct acpi_dp *dsd = acpi_dp_new_table("_DSD");
|
||||
soundwire_gen_codec(dsd, &soundwire_codec, NULL);
|
||||
acpi_dp_write(dsd);
|
||||
```
|
||||
|
||||
If the codec needs to generate custom properties for links it can provide a callback
|
||||
function to `soundwire_gen_codec()` instead of passing NULL:
|
||||
|
||||
```c
|
||||
static void codec_dp_prop_cb(struct acpi_dp *dsd, unsigned int id,
|
||||
struct soundwire_codec *codec)
|
||||
{
|
||||
acpi_dp_add_integer(dsd, "custom-dp-property", 1);
|
||||
}
|
||||
```
|
||||
|
||||
#### Codec Address
|
||||
|
||||
SoundWire slave devices use a SoundWire defined ACPI _ADR that requires a 64-bit integer
|
||||
and uses the master link ID and slave device unique ID to form a unique address for the
|
||||
device on this controller.
|
||||
|
||||
SoundWire addresses must be distinguishable from all other slave devices on the same master
|
||||
link, so multiple instances of the same manufacturer and part on the same master link will
|
||||
need different unique IDs. The value is typically determined by strapping pins on the codec
|
||||
chip and can be decoded for this table with the codec datasheet and board schematics.
|
||||
|
||||
```c
|
||||
/**
|
||||
* struct soundwire_address - SoundWire ACPI Device Address Encoding.
|
||||
* @version: SoundWire specification version from &enum soundwire_version.
|
||||
* @link_id: Zero-based SoundWire Link Number.
|
||||
* @unique_id: Unique ID for multiple devices.
|
||||
* @manufacturer_id: Manufacturer ID from include/device/mipi_ids.h.
|
||||
* @part_id: Vendor defined part ID.
|
||||
* @class: MIPI class encoding in &enum mipi_class.
|
||||
*/
|
||||
struct soundwire_address {
|
||||
enum soundwire_version version;
|
||||
uint8_t link_id;
|
||||
uint8_t unique_id;
|
||||
uint16_t manufacturer_id;
|
||||
uint16_t part_id;
|
||||
enum mipi_class class;
|
||||
};
|
||||
```
|
||||
|
||||
This ACPI address can be generated by calling the provided acpigen function:
|
||||
|
||||
acpigen_write_ADR_soundwire_device(const struct soundwire_address *sdw);
|
||||
|
||||
### Mainboard
|
||||
|
||||
The mainboard needs to select appropriate drivers in `Kconfig` and define the topology in
|
||||
`devicetree.cb` with the controllers and codecs that exist on the board.
|
||||
|
||||
The topology uses the **generic** device to describe SoundWire:
|
||||
|
||||
```c
|
||||
struct generic_path {
|
||||
unsigned int id; /* SoundWire Master Link ID */
|
||||
unsigned int subid; /* SoundWire Slave Unique ID */
|
||||
};
|
||||
```
|
||||
|
||||
This allows devices to be specified in `devicetree.cb` with the necessary information to
|
||||
generate ACPI address and device properties.
|
||||
|
||||
```
|
||||
chip drivers/intel/soundwire
|
||||
# SoundWire Controller 0
|
||||
device generic 0 on
|
||||
chip drivers/soundwire/codec1
|
||||
# SoundWire Link 0 ID 0
|
||||
device generic 0.0 on end
|
||||
end
|
||||
chip drivers/soundwire/codec2
|
||||
# SoundWire Link 1 ID 2
|
||||
device generic 1.2 on end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Volteer Example
|
||||
|
||||
This is an example of an Intel Tiger Lake reference board using SoundWire Link 0 for the
|
||||
headphone codec connection, and Link 1 for connecting two speaker amps for stereo speakers.
|
||||
|
||||
The mainboard can be found at
|
||||
|
||||
src/mainboard/google/volteer
|
||||
|
||||
```
|
||||
+------------------+ +-------------------+
|
||||
| | | Headphone Codec |
|
||||
| Intel Tiger Lake | +--->| Realtek ALC5682 |
|
||||
| SoundWire | | | ID 1 |
|
||||
| Controller | | +-------------------+
|
||||
| | |
|
||||
| Link 0 +----+ +-------------------+
|
||||
| | | Left Speaker Amp |
|
||||
| Link 1 +----+--->| Maxim MAX98373 |
|
||||
| | | | ID 3 |
|
||||
| Link 2 | | +-------------------+
|
||||
| | |
|
||||
| Link 3 | | +-------------------+
|
||||
| | | | Right Speaker Amp |
|
||||
+------------------+ +--->| Maxim MAX98373 |
|
||||
| ID 7 |
|
||||
+-------------------+
|
||||
```
|
||||
|
||||
This implementation requires a controller driver for the Intel Tigerlake SoC and a codec
|
||||
driver for the Realtek and Maxim chips. If those drivers did not already exist they would
|
||||
need to be added and reviewed separately before adding the support to the mainboard.
|
||||
|
||||
The volteer example requires some `Kconfig` options to be selected:
|
||||
|
||||
```
|
||||
config BOARD_GOOGLE_BASEBOARD_VOLTEER
|
||||
select DRIVERS_INTEL_SOUNDWIRE
|
||||
select DRIVERS_SOUNDWIRE_ALC5682
|
||||
select DRIVERS_SOUNDWIRE_MAX98373
|
||||
```
|
||||
|
||||
And the following `devicetree.cb` entries to define this topology:
|
||||
|
||||
```
|
||||
device pci 1f.3 on
|
||||
chip drivers/intel/soundwire
|
||||
# SoundWire Controller 0
|
||||
device generic 0 on
|
||||
chip drivers/soundwire/alc5682
|
||||
# SoundWire Link 0 ID 1
|
||||
register "desc" = ""Headphone Jack""
|
||||
device generic 0.1 on end
|
||||
end
|
||||
chip drivers/soundwire/max98373
|
||||
# SoundWire Link 0 ID 1
|
||||
register "desc" = ""Left Speaker Amp""
|
||||
device generic 1.3 on end
|
||||
end
|
||||
chip drivers/soundwire/max98373
|
||||
# SoundWire Link 1 ID 7
|
||||
register "desc" = ""Right Speaker Amp""
|
||||
device generic 1.7 on end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
@@ -10,7 +10,7 @@ coreboot consists of multiple stages that are compiled as separate binaries and
|
||||
are inserted into the CBFS with custom compression. The bootblock usually doesn't
|
||||
have compression while the ramstage and payload are compressed with LZMA.
|
||||
|
||||
Each stage loads the next stage a given address (possibly decompressing it).
|
||||
Each stage loads the next stage at given address (possibly decompressing it).
|
||||
|
||||
Some stages are relocatable and can be placed anywhere in DRAM. Those stages are
|
||||
usually cached in CBMEM for faster loading times on ACPI S3 resume.
|
||||
|
@@ -254,6 +254,23 @@ commit message itself:
|
||||
The script 'util/gitconfig/rebase.sh' can be used to help automate this.
|
||||
Other tags such as 'Commit-Queue' can simply be removed.
|
||||
|
||||
* Check if there's documentation that needs to be updated to remain current
|
||||
after your change. If there's no documentation for the part of coreboot
|
||||
you're working on, consider adding some.
|
||||
|
||||
* When contributing a significant change to core parts of the code base (such
|
||||
as the boot state machine or the resource allocator), or when introducing
|
||||
a new way of doing something that you think is worthwhile to apply across
|
||||
the tree (e.g. board variants), please bring up your design on the [mailing
|
||||
list](../community/forums.md). When changing behavior substantially, an
|
||||
explanation of what changes and why may be useful to have, either in the
|
||||
commit message or, if the discussion of the subject matter needs way more
|
||||
space, in the documentation. Since "what we did in the past and why it isn't
|
||||
appropriate anymore" isn't the most useful reading several years down the road,
|
||||
such a description could be put into the release notes for the next version
|
||||
(that you can find in Documentation/releases/) where it will inform people
|
||||
now without cluttering up the regular documentation, and also gives a nice
|
||||
shout-out to your contribution by the next release.
|
||||
|
||||
Expectations contributors should have
|
||||
-------------------------------------
|
||||
|
@@ -88,11 +88,28 @@ know through which interface the EDID can be queried:
|
||||
select GFX_GMA_ANALOG_I2C_HDMI_C # or
|
||||
select GFX_GMA_ANALOG_I2C_HDMI_D
|
||||
|
||||
Beside Kconfig options, *libgfxinit* needs to know which ports are
|
||||
implemented on a board and should be probed for displays. The mapping
|
||||
between the physical ports and these entries depends on the hardware
|
||||
implementation and can be recovered by testing or studying the output
|
||||
of `intelvbttool` or `intel_vbt_decode`.
|
||||
*libgfxinit* needs to know which ports are implemented on a board
|
||||
and should be probed for displays. There are two mechanisms to
|
||||
constrain the list of ports to probe, 1. port presence straps on
|
||||
the mainboard, and 2. a list of ports provided by *coreboot* (see
|
||||
below).
|
||||
|
||||
Presence straps are configured via the state of certains pins of
|
||||
the chipset at reset time. They are documented in the chipset's
|
||||
datasheets. By default, *libgfxinit* honors these straps for
|
||||
safety. However, some boards don't implement the straps correctly.
|
||||
If ports are not strapped as implemented by error, one can select
|
||||
an option to ignore the straps:
|
||||
|
||||
select GFX_GMA_IGNORE_PRESENCE_STRAPS
|
||||
|
||||
In the opposite case, that ports are strapped as implemented,
|
||||
but are actually unconnected, one has to make sure that the
|
||||
list of ports in *coreboot* omits them.
|
||||
|
||||
The mapping between the physical ports and these entries depends on
|
||||
the hardware implementation and can be recovered by testing or
|
||||
studying the output of `intelvbttool` or `intel_vbt_decode`.
|
||||
Each board has to implement the package `GMA.Mainboard` with a list:
|
||||
|
||||
ports : HW.GFX.GMA.Display_Probing.Port_List;
|
||||
|
BIN
Documentation/mainboard/dell/optiplex_9010.jpg
Normal file
BIN
Documentation/mainboard/dell/optiplex_9010.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
147
Documentation/mainboard/dell/optiplex_9010.md
Normal file
147
Documentation/mainboard/dell/optiplex_9010.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Dell OptiPlex 9010
|
||||
|
||||
This page describes how to run coreboot on Dell OptiPlex 9010 SFF.
|
||||
|
||||

|
||||
|
||||
## Technology
|
||||
|
||||
```eval_rst
|
||||
+------------+---------------------------------------------------------------+
|
||||
| CPU | Intel Core 2nd Gen (Sandybridge) or 3rd Gen (Ivybridge) |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| DRAM | Up to 4 DIMM slots, up to 32GB 1600MHz non-ECC DDR3 SDRAM |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| Chipset | Intel Q77 Express |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| Super I/O | SMSC SCH5545 (or SCH5544) with Environmental Controller |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| TPM | ST Microelectronics ST33ZP24 |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| Boot | From USB, SATA, NVMe (using PCIe x4 expansion card) |
|
||||
+------------+---------------------------------------------------------------+
|
||||
| Power | 200W-275W PSU |
|
||||
+------------+---------------------------------------------------------------+
|
||||
```
|
||||
|
||||
More specifications on [Dell OptiPlex 9010 specifications].
|
||||
|
||||
## Required proprietary blobs
|
||||
|
||||
```eval_rst
|
||||
+------------------+---------------------------------+---------------------+
|
||||
| Binary file | Apply | Required / Optional |
|
||||
+==================+=================================+=====================+
|
||||
| smsc_sch5545.bin | SMSC SCH5545 EC | Optional |
|
||||
+------------------+---------------------------------+---------------------+
|
||||
| microcode | CPU microcode | Required |
|
||||
+------------------+---------------------------------+---------------------+
|
||||
```
|
||||
|
||||
Microcode updates are automatically included into the coreboot image by build
|
||||
system from the `3rdparty/intel-microcode` submodule.
|
||||
|
||||
SMSC SC5545 EC firmware is optional, however lack of the binary will result in
|
||||
EC malfunction after power failure and fans running at full speed. The blob can
|
||||
be extracted from original firmware. It should be located under a file with
|
||||
GUID D386BEB8-4B54-4E69-94F5-06091F67E0D3, raw section. The file begins with a
|
||||
signature `SMSCUBIM`. The easiest way to do this is to use [UEFITool] and
|
||||
`Extract body` option on the raw section of the file.
|
||||
|
||||
## Flashing coreboot
|
||||
|
||||
```eval_rst
|
||||
+---------------------+--------------------------+
|
||||
| Type | Value |
|
||||
+=====================+==========================+
|
||||
| Socketed flash | no |
|
||||
+---------------------+--------------------------+
|
||||
| Model | MX25L6406E/MX25L3206E |
|
||||
+---------------------+--------------------------+
|
||||
| Size | 8 + 4 MiB |
|
||||
+---------------------+--------------------------+
|
||||
| Package | SOIC-16 + SOIC-8 |
|
||||
+---------------------+--------------------------+
|
||||
| Write protection | chipset PRR |
|
||||
+---------------------+--------------------------+
|
||||
| Dual BIOS feature | no |
|
||||
+---------------------+--------------------------+
|
||||
| Internal flashing | yes |
|
||||
+---------------------+--------------------------+
|
||||
```
|
||||
|
||||
### Internal programming
|
||||
|
||||
The SPI flash can be accessed using [flashrom].
|
||||
|
||||
flashrom -p internal -w coreboot.rom --ifd -i bios
|
||||
|
||||
Internal programming will not work when migrating from original UEFI firmware.
|
||||
One will have to short the SERVICE_MODE jumper to enable HMRFPO and then boot
|
||||
the machine to flash it.
|
||||
|
||||
### External programming
|
||||
|
||||
The external access to flash chip is available through standard SOP-8 clip
|
||||
and/or SOP-16 clip on the right side of the CPU fan (marked on the board
|
||||
image). The voltage of SPI flash is 3.3V.
|
||||
|
||||
There are no restrictions as to the programmer device. It is only recommended
|
||||
to flash firmware without supplying power. There are no diodes connected to the
|
||||
flash chips. External programming can be performed, for example using OrangePi
|
||||
and Armbian. You can use linux_spi driver which provides communication with SPI
|
||||
devices. Example command to program SPI flash with OrangePi using linux_spi:
|
||||
|
||||
flashrom -w coreboot.rom -p linux_spi:dev=/dev/spidev1.0,spispeed=16000
|
||||
|
||||
## Schematics
|
||||
|
||||
There are no schematics for SFF, but if one looks for MT/DT schematics, they
|
||||
can be found publicly. Most of the schematics should match the SFF (although
|
||||
MT/DT has additional PCIe and PCI slot).
|
||||
|
||||
## Known issues
|
||||
|
||||
- There seems to be a problem with DRAM clearing on reboot. The SSKPD register
|
||||
still contains 0xCAFE which leads to reset loop.
|
||||
|
||||
## Untested
|
||||
|
||||
Not all mainboard's peripherals and functions were tested because of lack of
|
||||
the cables or not being populated on the board case.
|
||||
|
||||
- Internal USB 2.0 header
|
||||
- Wake from S3 using serial port
|
||||
- Wake-on-Lan from ACPI S4/S5
|
||||
|
||||
## Working
|
||||
|
||||
- USB 3.0 and 2.0 rear and front ports (SeaBIOS and Linux 4.19)
|
||||
- Gigabit Ethernet
|
||||
- VGA and 2x DP port using libgfxinit
|
||||
- flashrom
|
||||
- PCIe x1 WiFi in PCIe x4 slot
|
||||
- NVMe PCIe x4 using PCIe x4 expansion card
|
||||
- PCIe x16 PEG port using Dell Radeon HD 7570
|
||||
- SATA ports (SATA disks and DVD)
|
||||
- Super I/O serial port 0 (RS232 DB9 connector on the rear side)
|
||||
- SMBus (reading SPD from DIMMs)
|
||||
- CPU initialization using Intel i7-3770
|
||||
- Sandy Bridge/Ivy Bridge native RAM initialization
|
||||
- SeaBIOS payload (version rel-1.13.0)
|
||||
- PS/2 keyboard and mouse (including wake support)
|
||||
- LPC debug header (requires soldering of the pin header and shorting RF24 for
|
||||
LPC clock)
|
||||
- USB debug dongle (the most bottom USB 2.0 port under RJ45 on the read side)
|
||||
- SMSC SCH5545 Super I/O initialization
|
||||
- SMSC SCH5545 EC initialization and firmware update
|
||||
- SMSC SCH5545 EC automatic fan control
|
||||
- TPM 1.2
|
||||
- Booting Debian 10, Ubuntu 18.04, QubesOS R4.01
|
||||
- Boot with cleaned ME
|
||||
- Intruder detection
|
||||
- Wake-on-Lan from ACPI S3
|
||||
|
||||
[flashrom]: https://flashrom.org/Flashrom
|
||||
[Dell OptiPlex 9010 specifications]: https://www.dell.com/downloads/global/products/optix/en/dell_optiplex_9010_spec_sheet.pdf
|
||||
[UEFITool]: https://github.com/LongSoft/UEFITool
|
@@ -26,6 +26,10 @@ This section contains documentation about coreboot on specific mainboards.
|
||||
|
||||
- [CN81XX EVB SFF](cavium/cn8100_sff_evb.md)
|
||||
|
||||
## Dell
|
||||
|
||||
- [OptiPlex 9010 SFF](dell/optiplex_9010.md)
|
||||
|
||||
## Emulation
|
||||
|
||||
The boards in this section are not real mainboards, but emulators.
|
||||
|
@@ -67,3 +67,4 @@ the remaining space for the `bios` partition.
|
||||
|
||||
[me_cleaner]: ../../northbridge/intel/sandybridge/me_cleaner.md
|
||||
[external programmer]: ../../flash_tutorial/index.md
|
||||
[flashing tutorial]: ../../flash_tutorial/index.md
|
||||
|
@@ -67,6 +67,9 @@ be more frequent than was needed, so we scaled it back to twice a year.
|
||||
ask for testing.
|
||||
- [ ] Test the commit selected for release.
|
||||
- [ ] Update release notes with actual commit id, push to repo.
|
||||
- [ ] Create new release notes doc template for the next version.
|
||||
- [ ] Fill in the release date, remove "Upcoming release" and other filler
|
||||
from the current release notes.
|
||||
- [ ] Run release script.
|
||||
- [ ] Run vboot_list script.
|
||||
- [ ] Test the release from the actual release tarballs.
|
||||
|
@@ -1,5 +1,5 @@
|
||||
Upcoming release - coreboot 4.12
|
||||
================================
|
||||
coreboot 4.12
|
||||
=============
|
||||
|
||||
coreboot 4.12 was released on May 12th, 2020.
|
||||
|
||||
|
@@ -237,6 +237,51 @@ Picasso's FSP is compatible with rev. 2.0 of the External Architecture
|
||||
Specification. Deviations, e.g., no FSP-T support, shall be published
|
||||
in an Integration Guide.
|
||||
|
||||
## APCB setup
|
||||
|
||||
APCBs are used to provide the PSP with SPD information and optionally a set of
|
||||
GPIOs to use for selecting which SPD to load.
|
||||
|
||||
### Prebuilt
|
||||
The picasso `Makefile` expects APCBs to be located in
|
||||
`3rdparty/blobs/mainboard/$(MAINBOARDDIR)`. If you have a pre-built binary just
|
||||
add the following to your mainboard's Makefile.
|
||||
|
||||
```
|
||||
# i.e., 3rdparty/blobs/mainboard/amd/mandolin/APCB_mandolin.bin
|
||||
APCB_SOURCES = mandolin
|
||||
```
|
||||
|
||||
### Generating APCBs
|
||||
If you have a template APCB file, the `apcb_edit` tool can be used to inject the
|
||||
SPD and GPIOs used to select the correct slot. Entries should match this
|
||||
pattern `{NAME}_x{1,2}`. There should be a matching SPD hex file in
|
||||
`SPD_SOURCES_DIR` matching the pattern `{NAME}.spd.hex`.
|
||||
The `_x{1,2}` suffix denotes single or dual channel. Up to 16 slots can be used.
|
||||
If a slot is empty, the special empty keyword can be used. This will generate
|
||||
an APCB with an empty SPD.
|
||||
|
||||
```
|
||||
APCB_SOURCES = hynix-HMA851S6CJR6N-VK_x1 # 0b0000
|
||||
APCB_SOURCES += hynix-HMAA1GS6CMR6N-VK_x2 # 0b0001
|
||||
APCB_SOURCES += empty # 0b0010
|
||||
APCB_SOURCES += samsung-K4A8G165WC-BCWE_x1 # 0b0011
|
||||
```
|
||||
|
||||
#### APCB Board ID GPIO configuration.
|
||||
The GPIOs determine which memory SPD will be used during boot.
|
||||
```
|
||||
# APCB_BOARD_ID_GPIO[0-3] = GPIO_NUMBER GPIO_IO_MUX GPIO_BANK_CTL
|
||||
# GPIO_NUMBER: FCH GPIO number
|
||||
# GPIO_IO_MUX: Value write to IOMUX to configure this GPIO
|
||||
# GPIO_BANK_CTL: Value write to GPIOBankCtl[23:16] to configure this GPIO
|
||||
|
||||
APCB_BOARD_ID_GPIO0 = 121 1 0
|
||||
APCB_BOARD_ID_GPIO1 = 120 1 0
|
||||
APCB_BOARD_ID_GPIO2 = 131 3 0
|
||||
APCB_BOARD_ID_GPIO3 = 116 1 0
|
||||
```
|
||||
|
||||
## Footnotes
|
||||
|
||||
1. *AMD Platform Security Processor BIOS Architecture Design Guide
|
||||
|
@@ -279,41 +279,6 @@ tests/lib/string-test and tests/device/i2c-test:
|
||||
├── i2c.o
|
||||
```
|
||||
|
||||
### Adding new tests
|
||||
For purpose of this description, let's assume that we want to add a new unit test
|
||||
for src/device/i2c.c module. Since this module is rather simple, it will be enough
|
||||
to have only one test module.
|
||||
|
||||
Firstly (assuming there is no tests/device/Makefile.inc file) we need to create
|
||||
Makefile.inc in main unit test module directory. Inside this Makefile.inc, one
|
||||
need to register new test and can specify multiple different attributes for it.
|
||||
|
||||
```bash
|
||||
# Register new test, by adding its name to tests variable
|
||||
tests-y += i2c-test
|
||||
|
||||
# All attributes are defined by <test_name>-<attribute> variables
|
||||
# <test_name>-srcs is used to register all input files (test harness, unit under
|
||||
# test and others) for this particular test. Remember to add relative paths.
|
||||
i2c-test-srcs += tests/device/i2c-test.c
|
||||
i2c-test-srcs += src/device/i2c.c
|
||||
|
||||
# We can define extra cflags for this particular test
|
||||
i2c-test-cflags += -DSOME_DEFINE=1
|
||||
|
||||
# For mocking out external dependencies (functions which cannot be resolved by
|
||||
# linker), it is possible to register a mock function. To register new mock, it
|
||||
# is enough to add function-to-be-mocked name to <test_name>-mocks variable.
|
||||
i2c-test-mocks += platform_i2c_transfer
|
||||
|
||||
# Similar to coreboot concept, unit tests also runs in the context of stages.
|
||||
# By default all unit tests are compiled to be ramstage executables. If one want
|
||||
# to overwrite this setting, there is <test_name>-stage variable available.
|
||||
i2c-test-stage:= bootblock
|
||||
```
|
||||
|
||||
### Writing new tests
|
||||
Full description of how to write unit tests and Cmocka API description is out of
|
||||
the scope of this document. There are other documents related to this
|
||||
[Cmocka API](https://api.cmocka.org/) and
|
||||
[Mocks](https://lwn.net/Articles/558106/).
|
||||
Our tutorial series has [detailed guidelines](../tutorial/part3.md) for writing
|
||||
unit tests.
|
||||
|
@@ -2,3 +2,4 @@
|
||||
|
||||
* [Part 1: Starting from scratch](part1.md)
|
||||
* [Part 2: Submitting a patch to coreboot.org](part2.md)
|
||||
* [Part 3: Writing unit tests](part3.md)
|
||||
|
384
Documentation/tutorial/part3.md
Normal file
384
Documentation/tutorial/part3.md
Normal file
@@ -0,0 +1,384 @@
|
||||
# Writing unit tests for coreboot
|
||||
|
||||
## Introduction
|
||||
General thoughts about unit testing coreboot can be found in
|
||||
[Unit testing coreboot](../technotes/2020-03-unit-testing-coreboot.md).
|
||||
|
||||
This document aims to guide developers through the process of adding and writing
|
||||
unit tests for coreboot modules.
|
||||
|
||||
As an example of unit under test, `src/device/i2c.c` (referred hereafter as UUT
|
||||
"Unit Under Test") will be used. This is simple module, thus it should be easy
|
||||
for the reader to focus solely on the testing logic, without the need to spend
|
||||
too much time on digging deeply into the source code details and flow of
|
||||
operations. That being said, a good understanding of what the unit under test is
|
||||
doing is crucial for writing unit tests.
|
||||
|
||||
This tutorial should also be helpful for developers who want to follow
|
||||
[TDD](https://en.wikipedia.org/wiki/Test-driven_development). Even though TDD
|
||||
has a different work flow of building tests first, followed by the code that
|
||||
satisfies them, the process of writing tests and adding them to the tree is the
|
||||
same.
|
||||
|
||||
## Analysis of unit under test
|
||||
First of all, it is necessary to precisely establish what we want to test in a
|
||||
particular module. Usually this will be an externally exposed API, which can be
|
||||
used by other modules.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
In case of our UUT, API consist of two methods:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t *data,
|
||||
uint8_t mask, uint8_t shift)
|
||||
int i2c_write_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t data,
|
||||
uint8_t mask, uint8_t shift)
|
||||
|
||||
For sake of simplicity, let's focus on `i2c_read_field` in this document.
|
||||
```
|
||||
|
||||
Once the API is defined, the next question is __what__ this API is doing (or
|
||||
what it will be doing in case of TDD). In other words, what outputs we are
|
||||
expecting from particular functions, when providing particular input parameters.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int i2c_read_field(unsigned int bus, uint8_t chip, uint8_t reg, uint8_t *data,
|
||||
uint8_t mask, uint8_t shift)
|
||||
|
||||
This is a method which means to read content of register `reg` from i2c device
|
||||
on i2c `bus` and slave address `chip`, applying bit `mask` and offset `shift`
|
||||
to it. Returned data should be placed in `data`.
|
||||
```
|
||||
|
||||
The next step is to determine all external dependencies of UUT in order to mock
|
||||
them out. Usually we want to isolate the UUT as much as possible, so that the
|
||||
test result depends __only__ on the behavior of UUT and not on the other
|
||||
modules. While some software dependencies may be hard to be mock (for example
|
||||
due to complicated dependencies) and thus should be simply linked into the test
|
||||
binaries, all hardware dependencies need to be mocked out, since in the
|
||||
user-space host environment, targets hardware is not available.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
`i2c_read_field` is calling `i2c_readb`, which eventually invokes
|
||||
`i2c_transfer`. This method simply calls `platform_i2c_transfer`. The last
|
||||
function in the chain is a hardware-touching one, and defined separately for
|
||||
different SOCs. It is responsible for issuing transactions on the i2c bus.
|
||||
For the purpose of writing unit test, we should mock this function.
|
||||
```
|
||||
|
||||
## Adding new tests
|
||||
In order to keep the tree clean, the `tests/` directory should mimic the `src/`
|
||||
directory, so that test harness code is placed in a location corresponding to
|
||||
UUT. Furthermore, the naming convention is to add the suffix `-test` to the UUT
|
||||
name when creating a new test harness file.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
Considering that UUT is `src/device/i2c.c`, test file should be named
|
||||
`tests/device/i2c-test.c`. When adding a new test file, it needs to be
|
||||
registered with the coreboot unit testing infrastructure.
|
||||
```
|
||||
|
||||
Every directory under `tests/` should contain a Makefile.inc, similar to what
|
||||
can be seen under the `src/`. Register a new test in Makefile.inc, by
|
||||
__appending__ test name to the `tests-y` variable.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
tests-y += i2c-test
|
||||
```
|
||||
|
||||
Next step is to list all source files, which should be linked together in order
|
||||
to create test binary. Usually a tests requires only two files - UUT and test
|
||||
harness code, but sometimes more is needed to provide the test environment.
|
||||
Source files are registered in `<test_name>-srcs` variable.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
i2c-test-srcs += tests/device/i2c-test.c
|
||||
i2c-test-srcs += src/device/i2c.c
|
||||
```
|
||||
|
||||
Above minimal configuration is a basis for further work. One can try to build
|
||||
and run test binary either by invoking `make tests/<test_dir>/<test_name>` or by
|
||||
running all unit tests (whole suite) for coreboot `make unit-tests`.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
make tests/device/i2c-test
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
make unit-tests
|
||||
```
|
||||
|
||||
When trying to build test binary, one can often see linker complains about
|
||||
`undefined reference` to couple of symbols. This is one of solutions to
|
||||
determine all external dependencies of UUT - iteratively build test and resolve
|
||||
errors one by one. At this step, developer should decide either it's better to
|
||||
add an extra module to provide necessary definitions or rather mock such
|
||||
dependency. Quick guide through adding mocks is provided later in this doc.
|
||||
|
||||
## Writing new tests
|
||||
In coreboot, [Cmocka](https://cmocka.org/) is used as unit test framework. The
|
||||
project has exhaustive [API documentation](https://api.cmocka.org/). Let's see
|
||||
how we may incorporate it when writing tests.
|
||||
|
||||
### Assertions
|
||||
Testing the UUT consists of calling the functions in the UUT and comparing the
|
||||
returned values to the expected values. Cmocka implements
|
||||
[a set of assert macros](https://api.cmocka.org/group__cmocka__asserts.html) to
|
||||
compare a value with an expected value. If the two values do not match, the test
|
||||
fails with an error message.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
In our example, the simplest test is to call UUT for reading our fake devices
|
||||
registers and do all calculation in the test harness itself. At the end, let's
|
||||
compare integers with `assert_int_equal`.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MASK 0x3
|
||||
#define SHIFT 0x1
|
||||
|
||||
static void i2c_read_field_test(void **state)
|
||||
{
|
||||
int bus, slave, reg;
|
||||
int i, j;
|
||||
uint8_t buf;
|
||||
|
||||
mock_expect_params_platform_i2c_transfer();
|
||||
|
||||
/* Read particular bits in all registers in all devices, then compare
|
||||
with expected value. */
|
||||
for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++)
|
||||
for (j = 0; j < ARRAY_SIZE(i2c_ex_devs[0].regs); j++) {
|
||||
i2c_read_field(i2c_ex_devs[i].bus,
|
||||
i2c_ex_devs[i].slave,
|
||||
i2c_ex_devs[i].regs[j].reg,
|
||||
&buf, MASK, SHIFT);
|
||||
assert_int_equal((i2c_ex_devs[i].regs[j].data &
|
||||
(MASK << SHIFT)) >> SHIFT, buf);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Mocks
|
||||
|
||||
#### Overview
|
||||
Many coreboot modules are low level software that touch hardware directly.
|
||||
Because of this, one of the most important and challenging part of
|
||||
writing tests is to design and implement mocks. A mock is a software component
|
||||
which implements the API of another component so that the test can verify that
|
||||
certain functions are called (or not called), verify the parameters passed to
|
||||
those functions, and specify the return values from those functions. Mocks are
|
||||
especially useful when the API to be implemented is one that accesses hardware
|
||||
components.
|
||||
|
||||
When writing a mock, the developer implements the same API as the module being
|
||||
mocked. Such a mock may, for example, register a set of driver methods. Behind
|
||||
this API, there is usually a simulation of real hardware.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
For purpose of our i2c test, we may introduce two i2c devices with set of
|
||||
registers, which simply are structs in memory.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Simulate two i2c devices, both on bus 0, each with three uint8_t regs
|
||||
implemented. */
|
||||
typedef struct {
|
||||
uint8_t reg;
|
||||
uint8_t data;
|
||||
} i2c_ex_regs_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int bus;
|
||||
uint8_t slave;
|
||||
i2c_ex_regs_t regs[3];
|
||||
} i2c_ex_devs_t;
|
||||
|
||||
i2c_ex_devs_t i2c_ex_devs[] = {
|
||||
{.bus = 0, .slave = 0xA, .regs = {
|
||||
{.reg = 0x0, .data = 0xB},
|
||||
{.reg = 0x1, .data = 0x6},
|
||||
{.reg = 0x2, .data = 0xF},
|
||||
} },
|
||||
{.bus = 0, .slave = 0x3, .regs = {
|
||||
{.reg = 0x0, .data = 0xDE},
|
||||
{.reg = 0x1, .data = 0xAD},
|
||||
{.reg = 0x2, .data = 0xBE},
|
||||
} },
|
||||
};
|
||||
|
||||
These fake devices will be accessed instead of hardware ones:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
reg = tmp->buf[0];
|
||||
|
||||
/* Find object for requested device */
|
||||
for (i = 0; i < ARRAY_SIZE(i2c_ex_devs); i++, i2c_dev++)
|
||||
if (i2c_ex_devs[i].slave == tmp->slave) {
|
||||
i2c_dev = &i2c_ex_devs[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (i2c_dev == NULL)
|
||||
return -1;
|
||||
|
||||
/* Write commands */
|
||||
if (tmp->len > 1) {
|
||||
i2c_dev->regs[reg].data = tmp->buf[1];
|
||||
};
|
||||
|
||||
/* Read commands */
|
||||
for (i = 0; i < count; i++, tmp++)
|
||||
if (tmp->flags & I2C_M_RD) {
|
||||
*(tmp->buf) = i2c_dev->regs[reg].data;
|
||||
};
|
||||
```
|
||||
|
||||
Cmocka uses a feature that gcc provides for breaking dependencies at the link
|
||||
time. It is possible to override implementation of some function, with the
|
||||
method from test harness. This allows test harness to take control of execution
|
||||
from binary (during the execution of test), and stimulate UUT as required
|
||||
without changing the source code.
|
||||
|
||||
coreboot unit test infrastructure supports overriding of functions at link time.
|
||||
This is as simple as adding a `name_of_function` to be mocked into
|
||||
<test_name>-mocks variable in Makefile.inc. The result is that every time the
|
||||
function is called, `wrap_name_of_function` will be called instead.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
i2c-test-mocks += platform_i2c_transfer
|
||||
|
||||
Now, dev can write own implementation of `platform_i2c_transfer` and define it
|
||||
as `wrap_platform_i2c_transfer`. This implementation instead of accessing real
|
||||
i2c bus, will write/read from fake structs.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int __wrap_platform_i2c_transfer(unsigned int bus, struct i2c_msg *segments,
|
||||
int count)
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
#### Checking mock's arguments
|
||||
A test can verify the parameters provided by the UUT to the mock function. The
|
||||
developer may also verify that number of calls to mock is correct and the order
|
||||
of calls to particular mocks is as expected (See
|
||||
[this](https://api.cmocka.org/group__cmocka__call__order.html)). The Cmocka
|
||||
macros for checking parameters are described
|
||||
[here](https://api.cmocka.org/group__cmocka__param.html). In general, in mock
|
||||
function, one makes a call to `check_expected(<param_name>)` and in the
|
||||
corresponding test function, `expect*()` macro, with description which parameter
|
||||
in which mock should have particular value, or be inside a described range.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
In our example, we may want to check that `platform_i2c_transfer` is fed with
|
||||
number of segments bigger than 0, each segment has flags which are in
|
||||
supported range and each segment has buf which is non-NULL. We are expecting
|
||||
such values for _every_ call, thus the last parameter in `expect*` macros is
|
||||
-1.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static void mock_expect_params_platform_i2c_transfer(void)
|
||||
{
|
||||
unsigned long int expected_flags[] = {0, I2C_M_RD, I2C_M_TEN,
|
||||
I2C_M_RECV_LEN, I2C_M_NOSTART};
|
||||
|
||||
/* Flags should always be only within supported range */
|
||||
expect_in_set_count(__wrap_platform_i2c_transfer, segments->flags,
|
||||
expected_flags, -1);
|
||||
|
||||
expect_not_value_count(__wrap_platform_i2c_transfer, segments->buf,
|
||||
NULL, -1);
|
||||
|
||||
expect_in_range_count(__wrap_platform_i2c_transfer, count, 1, INT_MAX,
|
||||
-1);
|
||||
}
|
||||
|
||||
And the checks below should be added to our mock
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
check_expected(count);
|
||||
|
||||
for (i = 0; i < count; i++, segments++) {
|
||||
check_expected_ptr(segments->buf);
|
||||
check_expected(segments->flags);
|
||||
}
|
||||
```
|
||||
|
||||
#### Instrument mocks
|
||||
It is possible for the test function to instrument what the mock will return to
|
||||
the UUT. This can be done by using the `will_return*()` and `mock()` macros.
|
||||
These are described in
|
||||
[the Mock Object section](https://api.cmocka.org/group__cmocka__mock.html) of
|
||||
the Cmocka API documentation.
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: Example
|
||||
|
||||
There is an non-coreboot example for using Cmocka available
|
||||
`here <https://lwn.net/Articles/558106/>`_.
|
||||
```
|
||||
|
||||
### Test runner
|
||||
Finally, the developer needs to implement the test `main()` function. All tests
|
||||
should be registered there and cmocka test runner invoked. All methods for
|
||||
invoking Cmocka test are described
|
||||
[here](https://api.cmocka.org/group__cmocka__exec.html).
|
||||
|
||||
```eval_rst
|
||||
.. admonition:: i2c-test example
|
||||
|
||||
We don't need any extra setup and teardown functions for i2c-test, so let's
|
||||
simply register test for `i2c_read_field` and return from main value which is
|
||||
output of Cmocka's runner (it returns number of tests that failed).
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test(i2c_read_field_test),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
```
|
@@ -7,14 +7,17 @@ Copyright © 2012 Intel Corporation
|
||||
Copyright 2012 Red Hat Inc.
|
||||
Copyright 2013 Google Inc.
|
||||
Copyright 2014 Google Inc.
|
||||
Copyright 2014 The Chromium OS Authors. All rights reserved.
|
||||
Copyright 2015 Google Inc.
|
||||
Copyright 2015, Google Inc.
|
||||
Copyright 2016 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
|
||||
Copyright 2016 The Chromium OS Authors. All rights reserved.
|
||||
Copyright 2017-2019 Eltan B.V.
|
||||
Copyright 2017 Google Inc.
|
||||
Copyright 2018 Generated Code
|
||||
Copyright 2018-present Facebook, Inc.
|
||||
Copyright 2019 9Elements Agency GmbH <patrick.rudolph@9elements.com>
|
||||
Copyright 2019 The Chromium OS Authors. All rights reserved.
|
||||
Copyright (C) 2002 David S. Peterson. All rights reserved.
|
||||
Copyright (c) 2003-2016 Cavium Inc. (support@cavium.com). All rights
|
||||
Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights
|
||||
@@ -25,17 +28,21 @@ Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
|
||||
Copyright (C) 2008 Advanced Micro Devices, Inc.
|
||||
Copyright (c) 2008, Google Inc.
|
||||
Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
|
||||
Copyright (C) 2008 Uwe Hermann <uwe@hermann-uwe.de>
|
||||
Copyright (C) 2009-2010 coresystems GmbH
|
||||
Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
|
||||
Copyright (c) 2010-2017, The Regents of the University of California
|
||||
Copyright (c) 2010, Code Aurora Forum. All rights reserved.
|
||||
Copyright (C) 2010 coresystems GmbH
|
||||
Copyright (c) 2010 Per Odlund <per.odlund@armagedon.se>
|
||||
Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
|
||||
Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
|
||||
Copyright (c) 2011-2012 The Linux Foundation. All rights reserved.
|
||||
Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
|
||||
Copyright (c) 2011 - 2014 The Linux Foundation. All rights reserved.
|
||||
Copyright (c) 2011-2019 The Linux Foundation. All rights reserved.
|
||||
Copyright (c) 2011, Google Inc.
|
||||
Copyright (C) 2011 secunet Security Networks AG
|
||||
Copyright (c) 2012 - 2013, 2015, 2019 The Linux Foundation.
|
||||
Copyright (c) 2012 - 2013, 2015 The Linux Foundation. All rights reserved.
|
||||
Copyright (c) 2012 - 2013 The Linux Foundation. All rights reserved.
|
||||
@@ -76,7 +83,9 @@ Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor E
|
||||
Copyright (c) 2016, 2018, The Linux Foundation. All rights reserved.
|
||||
Copyright (C) 2016 Google Inc.
|
||||
Copyright (c) 2016, The Regents of the University of California (Regents).
|
||||
Copyright (C) 2018-2019 Eltan B.V.
|
||||
Copyright (C) 2018 - 2019 The Linux Foundation. All rights reserved.
|
||||
Copyright (c) 2018 Eltan B.V.
|
||||
Copyright (c) 2018, HardenedLinux.
|
||||
Copyright (C) 2018, The Linux Foundation. All rights reserved.
|
||||
Copyright Dave Airlie <airlied@redhat.com>
|
||||
|
31
Makefile
31
Makefile
@@ -1,33 +1,4 @@
|
||||
##
|
||||
##
|
||||
## Copyright (C) 2008 Advanced Micro Devices, Inc.
|
||||
## Copyright (C) 2008 Uwe Hermann <uwe@hermann-uwe.de>
|
||||
## Copyright (C) 2009-2010 coresystems GmbH
|
||||
## Copyright (C) 2011 secunet Security Networks AG
|
||||
##
|
||||
## Redistribution and use in source and binary forms, with or without
|
||||
## modification, are permitted provided that the following conditions
|
||||
## are met:
|
||||
## 1. Redistributions of source code must retain the above copyright
|
||||
## notice, this list of conditions and the following disclaimer.
|
||||
## 2. Redistributions in binary form must reproduce the above copyright
|
||||
## notice, this list of conditions and the following disclaimer in the
|
||||
## documentation and/or other materials provided with the distribution.
|
||||
## 3. The name of the author may not be used to endorse or promote products
|
||||
## derived from this software without specific prior written permission.
|
||||
##
|
||||
## THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
## ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
## DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
## OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
## OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
## SUCH DAMAGE.
|
||||
##
|
||||
## SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
ifneq ($(words $(CURDIR)),1)
|
||||
$(error Error: Path to the main directory cannot contain spaces)
|
||||
|
13
Makefile.inc
13
Makefile.inc
@@ -562,6 +562,8 @@ $(AMDFWTOOL): $(top)/util/amdfwtool/amdfwtool.c
|
||||
@printf " HOSTCC $(subst $(obj)/,,$(@))\n"
|
||||
$(HOSTCC) $(HOSTCFLAGS) -DCONFIG_ROM_SIZE=$(CONFIG_ROM_SIZE) -o $@ $<
|
||||
|
||||
APCB_EDIT_TOOL:=$(top)/util/apcb/apcb_edit.py
|
||||
|
||||
CBOOTIMAGE:=$(objutil)/cbootimage/cbootimage
|
||||
|
||||
FUTILITY?=$(objutil)/futility/futility
|
||||
@@ -596,6 +598,17 @@ bootblock-y+=$(DEVICETREE_STATIC_C)
|
||||
postcar-y+=$(DEVICETREE_STATIC_C)
|
||||
smm-y+=$(DEVICETREE_STATIC_C)
|
||||
|
||||
# Ensure static.c and static.h are created before any objects are compiled
|
||||
ramstage-c-deps+=$(DEVICETREE_STATIC_C)
|
||||
romstage-c-deps+=$(DEVICETREE_STATIC_C)
|
||||
verstage-c-deps+=$(DEVICETREE_STATIC_C)
|
||||
bootblock-c-deps+=$(DEVICETREE_STATIC_C)
|
||||
postcar-c-deps+=$(DEVICETREE_STATIC_C)
|
||||
smm-c-deps+=$(DEVICETREE_STATIC_C)
|
||||
|
||||
.PHONY: devicetree
|
||||
devicetree: $(DEVICETREE_STATIC_C)
|
||||
|
||||
#######################################################################
|
||||
# Clean up rules
|
||||
clean-abuild:
|
||||
|
9
configs/config.dell_optiplex_9010_sff
Normal file
9
configs/config.dell_optiplex_9010_sff
Normal file
@@ -0,0 +1,9 @@
|
||||
CONFIG_USE_OPTION_TABLE=y
|
||||
CONFIG_USE_BLOBS=y
|
||||
CONFIG_VENDOR_DELL=y
|
||||
CONFIG_ONBOARD_VGA_IS_PRIMARY=y
|
||||
# CONFIG_DRIVERS_UART_8250IO is not set
|
||||
CONFIG_PCIEXP_CLK_PM=y
|
||||
CONFIG_SEABIOS_PS2_TIMEOUT=3000
|
||||
CONFIG_POST_DEVICE_LPC=y
|
||||
CONFIG_HAVE_EM100_SUPPORT=y
|
@@ -72,6 +72,35 @@ static int is_valid_fraction(const struct fraction *f)
|
||||
return f->d != 0;
|
||||
}
|
||||
|
||||
static int is_valid_scale(const struct scale *s)
|
||||
{
|
||||
return is_valid_fraction(&s->x) && is_valid_fraction(&s->y);
|
||||
}
|
||||
|
||||
static void add_fractions(struct fraction *out,
|
||||
const struct fraction *f1, const struct fraction *f2)
|
||||
{
|
||||
int64_t n, d;
|
||||
int shift;
|
||||
n = (int64_t)f1->n * f2->d + (int64_t)f2->n * f1->d;
|
||||
d = (int64_t)f1->d * f2->d;
|
||||
/* Simplest way to reduce the fraction until fitting in int32_t */
|
||||
shift = log2(MAX(ABS(n), ABS(d)) >> 31);
|
||||
if (shift > 0) {
|
||||
n >>= shift;
|
||||
d >>= shift;
|
||||
}
|
||||
out->n = n;
|
||||
out->d = d;
|
||||
}
|
||||
|
||||
static void add_scales(struct scale *out,
|
||||
const struct scale *s1, const struct scale *s2)
|
||||
{
|
||||
add_fractions(&out->x, &s1->x, &s2->x);
|
||||
add_fractions(&out->y, &s1->y, &s2->y);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform a vector:
|
||||
* x' = x * a_x + offset_x
|
||||
@@ -82,7 +111,7 @@ static int transform_vector(struct vector *out,
|
||||
const struct scale *a,
|
||||
const struct vector *offset)
|
||||
{
|
||||
if (!is_valid_fraction(&a->x) || !is_valid_fraction(&a->y))
|
||||
if (!is_valid_scale(a))
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
out->x = a->x.n * in->x / a->x.d + offset->x;
|
||||
out->y = a->y.n * in->y / a->y.d + offset->y;
|
||||
@@ -211,7 +240,6 @@ static int cbgfx_init(void)
|
||||
int draw_box(const struct rect *box, const struct rgb_color *rgb)
|
||||
{
|
||||
struct vector top_left;
|
||||
struct vector size;
|
||||
struct vector p, t;
|
||||
|
||||
if (cbgfx_init())
|
||||
@@ -222,14 +250,13 @@ int draw_box(const struct rect *box, const struct rgb_color *rgb)
|
||||
.x = { .n = box->offset.x, .d = CANVAS_SCALE, },
|
||||
.y = { .n = box->offset.y, .d = CANVAS_SCALE, }
|
||||
};
|
||||
const struct scale size_s = {
|
||||
.x = { .n = box->size.x, .d = CANVAS_SCALE, },
|
||||
.y = { .n = box->size.y, .d = CANVAS_SCALE, }
|
||||
const struct scale bottom_right_s = {
|
||||
.x = { .n = box->offset.x + box->size.x, .d = CANVAS_SCALE, },
|
||||
.y = { .n = box->offset.y + box->size.y, .d = CANVAS_SCALE, }
|
||||
};
|
||||
|
||||
transform_vector(&top_left, &canvas.size, &top_left_s, &canvas.offset);
|
||||
transform_vector(&size, &canvas.size, &size_s, &vzero);
|
||||
add_vectors(&t, &top_left, &size);
|
||||
transform_vector(&t, &canvas.size, &bottom_right_s, &canvas.offset);
|
||||
if (within_box(&t, &canvas) < 0) {
|
||||
LOG("Box exceeds canvas boundary\n");
|
||||
return CBGFX_ERROR_BOUNDARY;
|
||||
@@ -247,8 +274,8 @@ int draw_rounded_box(const struct scale *pos_rel, const struct scale *dim_rel,
|
||||
const struct fraction *thickness,
|
||||
const struct fraction *radius)
|
||||
{
|
||||
struct scale pos_end_rel;
|
||||
struct vector top_left;
|
||||
struct vector size;
|
||||
struct vector p, t;
|
||||
|
||||
if (cbgfx_init())
|
||||
@@ -256,9 +283,12 @@ int draw_rounded_box(const struct scale *pos_rel, const struct scale *dim_rel,
|
||||
|
||||
const uint32_t color = calculate_color(rgb, 0);
|
||||
|
||||
if (!is_valid_scale(pos_rel) || !is_valid_scale(dim_rel))
|
||||
return CBGFX_ERROR_INVALID_PARAMETER;
|
||||
|
||||
add_scales(&pos_end_rel, pos_rel, dim_rel);
|
||||
transform_vector(&top_left, &canvas.size, pos_rel, &canvas.offset);
|
||||
transform_vector(&size, &canvas.size, dim_rel, &vzero);
|
||||
add_vectors(&t, &top_left, &size);
|
||||
transform_vector(&t, &canvas.size, &pos_end_rel, &canvas.offset);
|
||||
if (within_box(&t, &canvas) < 0) {
|
||||
LOG("Box exceeds canvas boundary\n");
|
||||
return CBGFX_ERROR_BOUNDARY;
|
||||
|
@@ -45,9 +45,9 @@ typedef long ptrdiff_t;
|
||||
#define INT64_MAX (9223372036854775807LL)
|
||||
|
||||
#define INT8_MIN (-INT8_MAX - 1)
|
||||
#define INT16_MIN (-INT16_MIN - 1)
|
||||
#define INT32_MIN (-INT32_MIN - 1)
|
||||
#define INT64_MIN (-INT64_MIN - 1)
|
||||
#define INT16_MIN (-INT16_MAX - 1)
|
||||
#define INT32_MIN (-INT32_MAX - 1)
|
||||
#define INT64_MIN (-INT64_MAX - 1)
|
||||
|
||||
#define UINT8_MAX (255)
|
||||
#define UINT16_MAX (65535)
|
||||
|
@@ -361,7 +361,7 @@ char *strsep(char **stringp, const char *delim)
|
||||
token = walk = *stringp;
|
||||
|
||||
/* Walk, search for delimiters */
|
||||
while(*walk && !strchr(delim, *walk))
|
||||
while (*walk && !strchr(delim, *walk))
|
||||
walk++;
|
||||
|
||||
if (*walk) {
|
||||
@@ -424,7 +424,7 @@ long long int strtoll(const char *orig_ptr, char **endptr, int base)
|
||||
|
||||
/* Purge whitespace */
|
||||
|
||||
for( ; *ptr && isspace(*ptr); ptr++);
|
||||
for ( ; *ptr && isspace(*ptr); ptr++);
|
||||
|
||||
if (ptr[0] == '-') {
|
||||
is_negative = 1;
|
||||
@@ -478,7 +478,7 @@ unsigned long long int strtoull(const char *ptr, char **endptr, int base)
|
||||
|
||||
/* Purge whitespace */
|
||||
|
||||
for( ; *ptr && isspace(*ptr); ptr++);
|
||||
for ( ; *ptr && isspace(*ptr); ptr++);
|
||||
|
||||
if (!*ptr)
|
||||
return 0;
|
||||
@@ -491,10 +491,10 @@ unsigned long long int strtoull(const char *ptr, char **endptr, int base)
|
||||
else if (ptr[0] == '0') {
|
||||
base = 8;
|
||||
ptr++;
|
||||
}
|
||||
else
|
||||
} else {
|
||||
base = 10;
|
||||
}
|
||||
}
|
||||
|
||||
/* Base 16 allows the 0x on front - so skip over it */
|
||||
|
||||
@@ -504,7 +504,7 @@ unsigned long long int strtoull(const char *ptr, char **endptr, int base)
|
||||
ptr += 2;
|
||||
}
|
||||
|
||||
for( ; *ptr && _valid(*ptr, base); ptr++)
|
||||
for ( ; *ptr && _valid(*ptr, base); ptr++)
|
||||
ret = (ret * base) + _offset(*ptr, base);
|
||||
|
||||
if (endptr != NULL)
|
||||
@@ -516,7 +516,8 @@ unsigned long long int strtoull(const char *ptr, char **endptr, int base)
|
||||
unsigned long int strtoul(const char *ptr, char **endptr, int base)
|
||||
{
|
||||
unsigned long long val = strtoull(ptr, endptr, base);
|
||||
if (val > ULONG_MAX) return ULONG_MAX;
|
||||
if (val > ULONG_MAX)
|
||||
return ULONG_MAX;
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -577,7 +578,7 @@ size_t strcspn(const char *s, const char *a)
|
||||
* @param ptr A pointer to a string pointer to keep state of the tokenizer
|
||||
* @return Pointer to token
|
||||
*/
|
||||
char* strtok_r(char *str, const char *delim, char **ptr)
|
||||
char *strtok_r(char *str, const char *delim, char **ptr)
|
||||
{
|
||||
/* start new tokenizing job or continue existing one? */
|
||||
if (str == NULL)
|
||||
@@ -598,8 +599,6 @@ char* strtok_r(char *str, const char *delim, char **ptr)
|
||||
return start;
|
||||
}
|
||||
|
||||
static char **strtok_global;
|
||||
|
||||
/**
|
||||
* Extract first token in string str that is delimited by a character in tokens.
|
||||
* Destroys str, eliminates the token delimiter and uses global state.
|
||||
@@ -607,9 +606,11 @@ static char **strtok_global;
|
||||
* @param delim A pointer to an array of characters that delimit the token
|
||||
* @return Pointer to token
|
||||
*/
|
||||
char* strtok(char *str, const char *delim)
|
||||
char *strtok(char *str, const char *delim)
|
||||
{
|
||||
return strtok_r(str, delim, strtok_global);
|
||||
static char *strtok_ptr;
|
||||
|
||||
return strtok_r(str, delim, &strtok_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,6 +1,3 @@
|
||||
##
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
mainmenu "coreboot configuration"
|
||||
|
@@ -6,9 +6,11 @@ ramstage-y += acpi.c
|
||||
ramstage-y += acpigen.c
|
||||
ramstage-y += acpigen_dsm.c
|
||||
ramstage-y += acpigen_ps2_keybd.c
|
||||
ramstage-y += acpigen_usb.c
|
||||
ramstage-y += device.c
|
||||
ramstage-y += pld.c
|
||||
ramstage-y += sata.c
|
||||
ramstage-y += soundwire.c
|
||||
|
||||
ifneq ($(wildcard src/mainboard/$(MAINBOARDDIR)/acpi_tables.c),)
|
||||
ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/acpi_tables.c
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include <device/device.h>
|
||||
#include <device/pci_def.h>
|
||||
#include <device/pci_type.h>
|
||||
#include <device/soundwire.h>
|
||||
|
||||
static char *gencurrent;
|
||||
|
||||
@@ -1885,3 +1886,30 @@ void acpigen_write_ADR_pci_device(const struct device *dev)
|
||||
assert(dev->path.type == DEVICE_PATH_PCI);
|
||||
acpigen_write_ADR_pci_devfn(dev->path.pci.devfn);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpigen_write_ADR_soundwire_device() - SoundWire ACPI Device Address Encoding.
|
||||
* @address: SoundWire device address properties.
|
||||
*
|
||||
* From SoundWire Discovery and Configuration Specification Version 1.0 Table 3.
|
||||
*
|
||||
* 63..52 - Reserved (0)
|
||||
* 51..48 - Zero-based SoundWire Link ID, relative to the immediate parent.
|
||||
* Used when a Controller has multiple master devices, each producing a
|
||||
* separate SoundWire Link. Set to 0 for single-link controllers.
|
||||
* 47..0 - SoundWire Device ID Encoding from specification version 1.2 table 88
|
||||
* 47..44 - SoundWire specification version that this device supports
|
||||
* 43..40 - Unique ID for multiple devices
|
||||
* 39..24 - MIPI standard manufacturer code
|
||||
* 23..08 - Vendor defined part ID
|
||||
* 07..00 - MIPI class encoding
|
||||
*/
|
||||
void acpigen_write_ADR_soundwire_device(const struct soundwire_address *address)
|
||||
{
|
||||
acpigen_write_ADR((((uint64_t)address->link_id & 0xf) << 48) |
|
||||
(((uint64_t)address->version & 0xf) << 44) |
|
||||
(((uint64_t)address->unique_id & 0xf) << 40) |
|
||||
(((uint64_t)address->manufacturer_id & 0xffff) << 24) |
|
||||
(((uint64_t)address->part_id & 0xffff) << 8) |
|
||||
(((uint64_t)address->class & 0xff)));
|
||||
}
|
||||
|
@@ -168,6 +168,8 @@ static uint32_t rest_of_keymaps[] = {
|
||||
KEYMAP(0xd0, KEY_DOWN),
|
||||
KEYMAP(0xcd, KEY_RIGHT),
|
||||
KEYMAP(0xc8, KEY_UP),
|
||||
/* Power Key */
|
||||
KEYMAP(0xde, KEY_POWER),
|
||||
};
|
||||
|
||||
static void ssdt_generate_physmap(struct acpi_dp *dp, uint8_t num_top_row_keys,
|
||||
|
136
src/acpi/acpigen_usb.c
Normal file
136
src/acpi/acpigen_usb.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <acpi/acpi.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpigen_usb.h>
|
||||
|
||||
static const char *power_role_to_str(enum usb_typec_power_role power_role)
|
||||
{
|
||||
switch (power_role) {
|
||||
case TYPEC_POWER_ROLE_SOURCE:
|
||||
return "source";
|
||||
case TYPEC_POWER_ROLE_SINK:
|
||||
return "sink";
|
||||
case TYPEC_POWER_ROLE_DUAL:
|
||||
return "dual";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *try_power_role_to_str(enum usb_typec_try_power_role try_power_role)
|
||||
{
|
||||
switch (try_power_role) {
|
||||
case TYPEC_TRY_POWER_ROLE_NONE:
|
||||
/*
|
||||
* This should never get returned; if there is no try-power role for a device,
|
||||
* then the try-power-role field is not added to the DSD. Thus, this is just
|
||||
* for completeness.
|
||||
*/
|
||||
return "none";
|
||||
case TYPEC_TRY_POWER_ROLE_SINK:
|
||||
return "sink";
|
||||
case TYPEC_TRY_POWER_ROLE_SOURCE:
|
||||
return "source";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *data_role_to_str(enum usb_typec_data_role data_role)
|
||||
{
|
||||
switch (data_role) {
|
||||
case TYPEC_DATA_ROLE_DFP:
|
||||
return "host";
|
||||
case TYPEC_DATA_ROLE_UFP:
|
||||
return "device";
|
||||
case TYPEC_DATA_ROLE_DUAL:
|
||||
return "dual";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/* Add port capabilities as DP properties */
|
||||
static void add_port_caps(struct acpi_dp *dsd,
|
||||
const struct typec_connector_class_config *config)
|
||||
{
|
||||
acpi_dp_add_string(dsd, "power-role", power_role_to_str(config->power_role));
|
||||
acpi_dp_add_string(dsd, "data-role", data_role_to_str(config->data_role));
|
||||
|
||||
if (config->try_power_role != TYPEC_TRY_POWER_ROLE_NONE)
|
||||
acpi_dp_add_string(dsd, "try-power-role",
|
||||
try_power_role_to_str(config->try_power_role));
|
||||
}
|
||||
|
||||
static void add_device_ref(struct acpi_dp *dsd,
|
||||
const char *prop_name,
|
||||
const struct device *dev)
|
||||
{
|
||||
const char *path;
|
||||
char *fresh;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Unfortunately, the acpi_dp_* API doesn't write out the data immediately, thus we need
|
||||
* different storage areas for all of the strings, so strdup() is used for that. It is
|
||||
* safe to use strdup() here, because the strings are generated at build-time and are
|
||||
* guaranteed to be NUL-terminated (they come from the devicetree).
|
||||
*/
|
||||
path = acpi_device_path(dev);
|
||||
if (path) {
|
||||
fresh = strdup(path);
|
||||
if (fresh)
|
||||
acpi_dp_add_reference(dsd, prop_name, fresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_device_references(struct acpi_dp *dsd,
|
||||
const struct typec_connector_class_config *config)
|
||||
{
|
||||
/*
|
||||
* Add references to the USB port objects so that the consumer of this information can
|
||||
* know whether the port supports USB2, USB3, and/or USB4.
|
||||
*/
|
||||
add_device_ref(dsd, "usb2-port", config->usb2_port);
|
||||
add_device_ref(dsd, "usb3-port", config->usb3_port);
|
||||
add_device_ref(dsd, "usb4-port", config->usb4_port);
|
||||
|
||||
/*
|
||||
* Add references to the ACPI device(s) which control the orientation, USB data role and
|
||||
* data muxing.
|
||||
*/
|
||||
add_device_ref(dsd, "orientation-switch", config->orientation_switch);
|
||||
add_device_ref(dsd, "usb-role-switch", config->usb_role_switch);
|
||||
add_device_ref(dsd, "mode-switch", config->mode_switch);
|
||||
}
|
||||
|
||||
void acpigen_write_typec_connector(const struct typec_connector_class_config *config,
|
||||
int port_number,
|
||||
add_custom_dsd_property_cb add_custom_dsd_property)
|
||||
{
|
||||
struct acpi_dp *dsd;
|
||||
char name[5];
|
||||
|
||||
/* Create a CONx device */
|
||||
snprintf(name, sizeof(name), "CON%1X", port_number);
|
||||
acpigen_write_device(name);
|
||||
acpigen_write_name_integer("_ADR", port_number);
|
||||
|
||||
dsd = acpi_dp_new_table("_DSD");
|
||||
|
||||
/* Write out the _DSD table */
|
||||
acpi_dp_add_integer(dsd, "port-number", port_number);
|
||||
add_port_caps(dsd, config);
|
||||
add_device_references(dsd, config);
|
||||
|
||||
/* Allow client to add custom properties if desired */
|
||||
if (add_custom_dsd_property)
|
||||
add_custom_dsd_property(dsd, port_number);
|
||||
acpi_dp_write(dsd);
|
||||
|
||||
acpigen_pop_len(); /* Device */
|
||||
}
|
429
src/acpi/soundwire.c
Normal file
429
src/acpi/soundwire.c
Normal file
@@ -0,0 +1,429 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <acpi/acpigen.h>
|
||||
#include <acpi/acpi_device.h>
|
||||
#include <acpi/acpi_soundwire.h>
|
||||
#include <commonlib/helpers.h>
|
||||
#include <device/soundwire.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Specification-defined prefix for SoundWire properties. */
|
||||
#define SDW_PFX "mipi-sdw-"
|
||||
|
||||
/* Generate SoundWire property for integer. */
|
||||
#define SDW_INT(__key, __val) \
|
||||
acpi_dp_add_integer(dsd, SDW_PFX __key, __val)
|
||||
|
||||
/* Generate SoundWire property for integer array. */
|
||||
#define SDW_INT_ARRAY(__key, __val) \
|
||||
acpi_dp_add_integer_array(dsd, SDW_PFX __key, __val, __val##_count)
|
||||
|
||||
/**
|
||||
* struct soundwire_name_map - Map ACPI name to SoundWire property name.
|
||||
* @acpi_name: ACPI compatible name string.
|
||||
* @sdw_name: MIPI SoundWire property name string.
|
||||
*/
|
||||
struct soundwire_name_map {
|
||||
const char *acpi_name;
|
||||
const char *sdw_name;
|
||||
};
|
||||
|
||||
static const struct soundwire_name_map bra_mode_names[] = {
|
||||
{ "BRA0", SDW_PFX "port-bra-mode-0" },
|
||||
{ "BRA1", SDW_PFX "port-bra-mode-1" },
|
||||
{ "BRA2", SDW_PFX "port-bra-mode-2" },
|
||||
{ "BRA3", SDW_PFX "port-bra-mode-3" },
|
||||
};
|
||||
|
||||
static const struct soundwire_name_map audio_mode_names[] = {
|
||||
{ "MOD0", SDW_PFX "port-audio-mode-0" },
|
||||
{ "MOD1", SDW_PFX "port-audio-mode-1" },
|
||||
{ "MOD2", SDW_PFX "port-audio-mode-2" },
|
||||
{ "MOD3", SDW_PFX "port-audio-mode-3" },
|
||||
};
|
||||
|
||||
static const struct soundwire_name_map dpn_source_names[] = {
|
||||
{ "DP0", SDW_PFX "dp-0-subproperties" },
|
||||
{ "SRC1", SDW_PFX "dp-1-source-subproperties" },
|
||||
{ "SRC2", SDW_PFX "dp-2-source-subproperties" },
|
||||
{ "SRC3", SDW_PFX "dp-3-source-subproperties" },
|
||||
{ "SRC4", SDW_PFX "dp-4-source-subproperties" },
|
||||
{ "SRC5", SDW_PFX "dp-5-source-subproperties" },
|
||||
{ "SRC6", SDW_PFX "dp-6-source-subproperties" },
|
||||
{ "SRC7", SDW_PFX "dp-7-source-subproperties" },
|
||||
{ "SRC8", SDW_PFX "dp-8-source-subproperties" },
|
||||
{ "SRC9", SDW_PFX "dp-9-source-subproperties" },
|
||||
{ "SRCA", SDW_PFX "dp-10-source-subproperties" },
|
||||
{ "SRCB", SDW_PFX "dp-11-source-subproperties" },
|
||||
{ "SRCC", SDW_PFX "dp-12-source-subproperties" },
|
||||
{ "SRCD", SDW_PFX "dp-13-source-subproperties" }
|
||||
};
|
||||
|
||||
static const struct soundwire_name_map dpn_sink_names[] = {
|
||||
{ "DP0", SDW_PFX "dp-0-subproperties" },
|
||||
{ "SNK1", SDW_PFX "dp-1-sink-subproperties" },
|
||||
{ "SNK2", SDW_PFX "dp-2-sink-subproperties" },
|
||||
{ "SNK3", SDW_PFX "dp-3-sink-subproperties" },
|
||||
{ "SNK4", SDW_PFX "dp-4-sink-subproperties" },
|
||||
{ "SNK5", SDW_PFX "dp-5-sink-subproperties" },
|
||||
{ "SNK6", SDW_PFX "dp-6-sink-subproperties" },
|
||||
{ "SNK7", SDW_PFX "dp-7-sink-subproperties" },
|
||||
{ "SNK8", SDW_PFX "dp-8-sink-subproperties" },
|
||||
{ "SNK9", SDW_PFX "dp-9-sink-subproperties" },
|
||||
{ "SNKA", SDW_PFX "dp-10-sink-subproperties" },
|
||||
{ "SNKB", SDW_PFX "dp-11-sink-subproperties" },
|
||||
{ "SNKC", SDW_PFX "dp-12-sink-subproperties" },
|
||||
{ "SNKD", SDW_PFX "dp-13-sink-subproperties" }
|
||||
};
|
||||
|
||||
static const struct soundwire_name_map link_names[] = {
|
||||
{ "LNK0", SDW_PFX "link-0-subproperties" },
|
||||
{ "LNK1", SDW_PFX "link-1-subproperties" },
|
||||
{ "LNK2", SDW_PFX "link-2-subproperties" },
|
||||
{ "LNK3", SDW_PFX "link-3-subproperties" },
|
||||
{ "LNK4", SDW_PFX "link-4-subproperties" },
|
||||
{ "LNK5", SDW_PFX "link-5-subproperties" },
|
||||
{ "LNK6", SDW_PFX "link-6-subproperties" },
|
||||
{ "LNK7", SDW_PFX "link-7-subproperties" }
|
||||
};
|
||||
|
||||
static const char * const multilane_names[] = {
|
||||
SDW_PFX "lane-1-mapping",
|
||||
SDW_PFX "lane-2-mapping",
|
||||
SDW_PFX "lane-3-mapping",
|
||||
SDW_PFX "lane-4-mapping",
|
||||
SDW_PFX "lane-5-mapping",
|
||||
SDW_PFX "lane-6-mapping",
|
||||
SDW_PFX "lane-7-mapping",
|
||||
SDW_PFX "lane-8-mapping"
|
||||
};
|
||||
|
||||
static const char * const multilane_master_lane_names[] = {
|
||||
SDW_PFX "master-lane-1",
|
||||
SDW_PFX "master-lane-2",
|
||||
SDW_PFX "master-lane-3",
|
||||
SDW_PFX "master-lane-4",
|
||||
SDW_PFX "master-lane-5",
|
||||
SDW_PFX "master-lane-6",
|
||||
SDW_PFX "master-lane-7",
|
||||
SDW_PFX "master-lane-8"
|
||||
};
|
||||
|
||||
static const char * const multilane_slave_link_names[] = {
|
||||
SDW_PFX "slave-link-A",
|
||||
SDW_PFX "slave-link-B",
|
||||
SDW_PFX "slave-link-C",
|
||||
SDW_PFX "slave-link-D",
|
||||
SDW_PFX "slave-link-E",
|
||||
SDW_PFX "slave-link-F",
|
||||
SDW_PFX "slave-link-G",
|
||||
SDW_PFX "slave-link-I"
|
||||
};
|
||||
|
||||
static const char * const multilane_bus_holder_names[] = {
|
||||
SDW_PFX "lane-1-bus-holder",
|
||||
SDW_PFX "lane-2-bus-holder",
|
||||
SDW_PFX "lane-3-bus-holder",
|
||||
SDW_PFX "lane-4-bus-holder",
|
||||
SDW_PFX "lane-5-bus-holder",
|
||||
SDW_PFX "lane-6-bus-holder",
|
||||
SDW_PFX "lane-7-bus-holder",
|
||||
SDW_PFX "lane-8-bus-holder"
|
||||
};
|
||||
|
||||
static void soundwire_gen_interface_revision(struct acpi_dp *dsd)
|
||||
{
|
||||
acpi_dp_add_integer(dsd, SDW_PFX "sw-interface-revision", SOUNDWIRE_SW_VERSION_1_0);
|
||||
}
|
||||
|
||||
static void soundwire_gen_slave(struct acpi_dp *dsd, const struct soundwire_slave *prop)
|
||||
{
|
||||
soundwire_gen_interface_revision(dsd);
|
||||
SDW_INT("wake-up-unavailable", prop->wake_up_unavailable);
|
||||
SDW_INT("test-mode-supported", prop->test_mode_supported);
|
||||
SDW_INT("clock-stop-mode1-supported", prop->clock_stop_mode1_supported);
|
||||
|
||||
/* Clock Stop Prepare Timeout only used without simplified Clock Stop Prepare. */
|
||||
SDW_INT("simplified-clockstopprepare-sm-supported",
|
||||
prop->simplified_clockstopprepare_sm_supported);
|
||||
if (!prop->simplified_clockstopprepare_sm_supported)
|
||||
SDW_INT("clockstopprepare-timeout", prop->clockstopprepare_timeout);
|
||||
|
||||
SDW_INT("clockstopprepare-hard-reset-behavior",
|
||||
prop->clockstopprepare_hard_reset_behavior);
|
||||
SDW_INT("slave-channelprepare-timeout", prop->slave_channelprepare_timeout);
|
||||
SDW_INT("highPHY-capable", prop->highPHY_capable);
|
||||
SDW_INT("paging-supported", prop->paging_supported);
|
||||
SDW_INT("bank-delay-supported", prop->bank_delay_supported);
|
||||
SDW_INT("port15-read-behavior", prop->port15_read_behavior);
|
||||
SDW_INT("master-count", prop->master_count);
|
||||
SDW_INT("source-port-list", prop->source_port_list);
|
||||
SDW_INT("sink-port-list", prop->sink_port_list);
|
||||
}
|
||||
|
||||
static void soundwire_gen_multilane(struct acpi_dp *dsd, const struct soundwire_multilane *prop)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
soundwire_gen_interface_revision(dsd);
|
||||
|
||||
/* Fill out multilane map based on master/slave links. */
|
||||
for (i = 0; i < prop->lane_mapping_count && i < SOUNDWIRE_MAX_LANE; i++) {
|
||||
const struct soundwire_multilane_map *map = &prop->lane_mapping[i];
|
||||
const char *name;
|
||||
|
||||
/* Get the name of this connection */
|
||||
if (map->direction == MASTER_LANE)
|
||||
name = multilane_master_lane_names[map->connection.master_lane];
|
||||
else
|
||||
name = multilane_slave_link_names[map->connection.slave_link];
|
||||
|
||||
acpi_dp_add_string(dsd, multilane_names[map->lane], name);
|
||||
}
|
||||
|
||||
/* Add bus holder properties. */
|
||||
for (i = 0; i < prop->lane_bus_holder_count; i++)
|
||||
acpi_dp_add_integer(dsd, multilane_bus_holder_names[i],
|
||||
prop->lane_bus_holder[i]);
|
||||
}
|
||||
|
||||
static void soundwire_gen_link(struct acpi_dp *dsd, const struct soundwire_link *prop)
|
||||
{
|
||||
SDW_INT("clock-stop-mode0-supported", prop->clock_stop_mode0_supported);
|
||||
SDW_INT("clock-stop-mode1-supported", prop->clock_stop_mode1_supported);
|
||||
if (prop->clock_frequencies_supported_count > 0 &&
|
||||
prop->clock_frequencies_supported_count < SOUNDWIRE_MAX) {
|
||||
SDW_INT_ARRAY("clock-frequencies-supported",
|
||||
prop->clock_frequencies_supported);
|
||||
}
|
||||
SDW_INT("default-frame-rate", prop->default_frame_rate);
|
||||
SDW_INT("default-frame-row-size", prop->default_frame_row_size);
|
||||
SDW_INT("default-frame-col-size", prop->default_frame_col_size);
|
||||
SDW_INT("dynamic-frame-shape", prop->dynamic_frame_shape);
|
||||
SDW_INT("command-error-threshold", prop->command_error_threshold);
|
||||
}
|
||||
|
||||
static void soundwire_gen_bra_mode(struct acpi_dp *dsd, const struct soundwire_bra_mode *prop)
|
||||
{
|
||||
/* Bus frequency configs used if min/max not supported. */
|
||||
if (prop->bus_frequency_configs_count > 0 &&
|
||||
prop->bus_frequency_configs_count < SOUNDWIRE_MAX) {
|
||||
SDW_INT_ARRAY("bra-mode-bus-frequency-configs", prop->bus_frequency_configs);
|
||||
} else {
|
||||
SDW_INT("bra-mode-min-bus-frequency", prop->min_bus_frequency);
|
||||
SDW_INT("bra-mode-max-bus-frequency", prop->max_bus_frequency);
|
||||
}
|
||||
SDW_INT("bra-mode-max-data-per-frame", prop->max_data_per_frame);
|
||||
SDW_INT("bra-mode-min-us-between-transactions", prop->min_us_between_transactions);
|
||||
}
|
||||
|
||||
static void soundwire_gen_audio_mode(struct acpi_dp *dsd,
|
||||
const struct soundwire_audio_mode *prop)
|
||||
{
|
||||
/* Bus frequency configs used if min/max not supported. */
|
||||
if (prop->bus_frequency_configs_count > 0 &&
|
||||
prop->bus_frequency_configs_count < SOUNDWIRE_MAX) {
|
||||
SDW_INT_ARRAY("audio-mode-bus-frequency-configs", prop->bus_frequency_configs);
|
||||
} else {
|
||||
SDW_INT("audio-mode-min-bus-frequency", prop->min_bus_frequency);
|
||||
SDW_INT("audio-mode-max-bus-frequency", prop->max_bus_frequency);
|
||||
}
|
||||
|
||||
/* Sampling frequency configs used if min/max not supported. */
|
||||
if (prop->sampling_frequency_configs_count > 0 &&
|
||||
prop->sampling_frequency_configs_count < SOUNDWIRE_MAX) {
|
||||
SDW_INT_ARRAY("audio-mode-sampling-frequency-configs",
|
||||
prop->sampling_frequency_configs);
|
||||
} else {
|
||||
SDW_INT("audio-mode-max-sampling-frequency", prop->max_sampling_frequency);
|
||||
SDW_INT("audio-mode-min-sampling-frequency", prop->min_sampling_frequency);
|
||||
}
|
||||
|
||||
SDW_INT("audio-mode-prepare-channel-behavior", prop->prepare_channel_behavior);
|
||||
SDW_INT("audio-mode-glitchless-transitions", prop->glitchless_transitions);
|
||||
}
|
||||
|
||||
static void soundwire_gen_dp0(struct acpi_dp *dsd, const struct soundwire_dp0 *prop)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* Max wordlength configs used if min/max not supported. */
|
||||
if (prop->port_wordlength_configs_count > 0 &&
|
||||
prop->port_wordlength_configs_count < SOUNDWIRE_MAX) {
|
||||
SDW_INT_ARRAY("port-wordlength-configs", prop->port_wordlength_configs);
|
||||
} else {
|
||||
SDW_INT("port-max-wordlength", prop->port_max_wordlength);
|
||||
SDW_INT("port-min-wordlength", prop->port_min_wordlength);
|
||||
}
|
||||
SDW_INT("bra-flow-controlled", prop->bra_flow_controlled);
|
||||
SDW_INT("bra-imp-def-response-supported", prop->bra_imp_def_response_supported);
|
||||
SDW_INT("bra-role-supported", prop->bra_role_supported);
|
||||
SDW_INT("simplified-channel-prepare-sm", prop->simplified_channel_prepare_sm);
|
||||
SDW_INT("imp-def-dp0-interrupts-supported", prop->imp_def_dp0_interrupts_supported);
|
||||
SDW_INT("imp-def-bpt-supported", prop->imp_def_bpt_supported);
|
||||
|
||||
/* Add bulk register access mode property pointers. */
|
||||
for (i = 0; i < prop->bra_mode_count && i < SOUNDWIRE_MAX_MODE; i++) {
|
||||
struct acpi_dp *bra = acpi_dp_new_table(bra_mode_names[i].acpi_name);
|
||||
acpi_dp_add_child(dsd, bra_mode_names[i].sdw_name, bra);
|
||||
}
|
||||
}
|
||||
|
||||
static void soundwire_gen_dpn(struct acpi_dp *dsd, const struct soundwire_dpn *prop)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
SDW_INT("data-port-type", prop->data_port_type);
|
||||
SDW_INT("max-grouping-supported", prop->max_grouping_supported);
|
||||
SDW_INT("imp-def-dpn-interrupts-supported", prop->imp_def_dpn_interrupts_supported);
|
||||
SDW_INT("modes-supported", prop->modes_supported);
|
||||
SDW_INT("max-async-buffer", prop->max_async_buffer);
|
||||
SDW_INT("block-packing-mode", prop->block_packing_mode);
|
||||
SDW_INT("port-encoding-type", prop->port_encoding_type);
|
||||
|
||||
/* Max wordlength configs used if min/max not supported. */
|
||||
if (prop->port_wordlength_configs_count > 0 &&
|
||||
prop->port_wordlength_configs_count < SOUNDWIRE_MAX) {
|
||||
SDW_INT_ARRAY("port-wordlength-configs", prop->port_wordlength_configs);
|
||||
} else {
|
||||
SDW_INT("port-max-wordlength", prop->port_max_wordlength);
|
||||
SDW_INT("port-min-wordlength", prop->port_min_wordlength);
|
||||
}
|
||||
|
||||
/* Channel Prepare Timeout only used without simplified Channel Prepare. */
|
||||
SDW_INT("simplified-channelprepare-sm", prop->simplified_channelprepare_sm);
|
||||
if (!prop->simplified_channelprepare_sm)
|
||||
SDW_INT("port-channelprepare-timeout", prop->port_channelprepare_timeout);
|
||||
|
||||
/* Channel number list used if min/max not supported. */
|
||||
if (prop->channel_number_list_count > 0 &&
|
||||
prop->channel_number_list_count < SOUNDWIRE_MAX) {
|
||||
SDW_INT_ARRAY("channel-number-list", prop->channel_number_list);
|
||||
} else {
|
||||
SDW_INT("min-channel-number", prop->min_channel_number);
|
||||
SDW_INT("max-channel-number", prop->max_channel_number);
|
||||
}
|
||||
if (prop->channel_combination_list_count > 0 &&
|
||||
prop->channel_combination_list_count < SOUNDWIRE_MAX) {
|
||||
SDW_INT_ARRAY("channel-combination-list", prop->channel_combination_list);
|
||||
}
|
||||
|
||||
/* Add reference to Audio Mode properties. */
|
||||
for (i = 0; i < prop->port_audio_mode_count && i < SOUNDWIRE_MAX_MODE; i++) {
|
||||
struct acpi_dp *am = acpi_dp_new_table(audio_mode_names[i].acpi_name);
|
||||
acpi_dp_add_child(dsd, audio_mode_names[i].sdw_name, am);
|
||||
}
|
||||
}
|
||||
|
||||
void soundwire_gen_controller(struct acpi_dp *dsd, const struct soundwire_controller *prop,
|
||||
soundwire_link_prop_cb link_prop_cb)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
soundwire_gen_interface_revision(dsd);
|
||||
SDW_INT("master-count", prop->master_list_count);
|
||||
|
||||
/* Generate properties for each master link on the controller. */
|
||||
for (i = 0; i < prop->master_list_count && i < SOUNDWIRE_MAX_LINK; i++) {
|
||||
struct acpi_dp *link = acpi_dp_new_table(link_names[i].acpi_name);
|
||||
soundwire_gen_link(link, &prop->master_list[i]);
|
||||
|
||||
/* Callback for custom link properties from the controller. */
|
||||
if (link_prop_cb)
|
||||
link_prop_cb(link, i, prop);
|
||||
acpi_dp_add_child(dsd, link_names[i].sdw_name, link);
|
||||
}
|
||||
}
|
||||
|
||||
void soundwire_gen_codec(struct acpi_dp *dsd, const struct soundwire_codec *codec,
|
||||
soundwire_dp_prop_cb dp_prop_cb)
|
||||
{
|
||||
const struct soundwire_dpn_entry *entry;
|
||||
const struct soundwire_name_map *name;
|
||||
size_t i;
|
||||
|
||||
/* Generate slave properties for this codec. */
|
||||
soundwire_gen_slave(dsd, codec->slave);
|
||||
|
||||
/* Generate properties for multilane config, if provided. */
|
||||
if (codec->multilane)
|
||||
soundwire_gen_multilane(dsd, codec->multilane);
|
||||
|
||||
/* Generate properties for data port 0, if provided. */
|
||||
if (codec->dp0) {
|
||||
struct acpi_dp *dp0;
|
||||
|
||||
/* First generate any Bulk Register Access mode properties. */
|
||||
for (i = 0; i < SOUNDWIRE_MAX_MODE; i++) {
|
||||
const struct soundwire_bra_mode *prop = codec->dp0_bra_mode[i];
|
||||
struct acpi_dp *bra;
|
||||
|
||||
/* Stop processing at the first undefined BRA mode. */
|
||||
if (!prop)
|
||||
break;
|
||||
name = &bra_mode_names[i];
|
||||
bra = acpi_dp_new_table(name->acpi_name);
|
||||
soundwire_gen_bra_mode(bra, prop);
|
||||
acpi_dp_add_child(dsd, name->sdw_name, bra);
|
||||
}
|
||||
|
||||
name = &dpn_source_names[0];
|
||||
dp0 = acpi_dp_new_table(name->acpi_name);
|
||||
soundwire_gen_dp0(dp0, codec->dp0);
|
||||
|
||||
/* Callback for custom properties from the codec. */
|
||||
if (dp_prop_cb)
|
||||
dp_prop_cb(dp0, 0, codec);
|
||||
acpi_dp_add_child(dsd, name->sdw_name, dp0);
|
||||
}
|
||||
|
||||
/*
|
||||
* First generate audio modes for the data ports. This results in unnecessary
|
||||
* (but harmless) references to the audio modes at the codec level, but it allows
|
||||
* the data ports to use these objects without duplication.
|
||||
*/
|
||||
for (i = 0; i < SOUNDWIRE_MAX_MODE; i++) {
|
||||
const struct soundwire_audio_mode *prop = codec->audio_mode[i];
|
||||
struct acpi_dp *am;
|
||||
|
||||
/* Stop processing at the first undefined audio mode. */
|
||||
if (!prop)
|
||||
break;
|
||||
name = &audio_mode_names[i];
|
||||
am = acpi_dp_new_table(name->acpi_name);
|
||||
soundwire_gen_audio_mode(am, prop);
|
||||
acpi_dp_add_child(dsd, name->sdw_name, am);
|
||||
}
|
||||
|
||||
/* Now generate properties for source/slave on each defined data port. */
|
||||
for (entry = codec->dpn; entry; entry++) {
|
||||
struct acpi_dp *dpn;
|
||||
|
||||
/* Stop processing at the first invalid data port. */
|
||||
if (entry->port < SOUNDWIRE_MIN_DPN || entry->port > SOUNDWIRE_MAX_DPN)
|
||||
break;
|
||||
|
||||
if (entry->source) {
|
||||
name = &dpn_source_names[entry->port];
|
||||
dpn = acpi_dp_new_table(name->acpi_name);
|
||||
soundwire_gen_dpn(dpn, entry->source);
|
||||
|
||||
/* Callback for custom properties from the codec. */
|
||||
if (dp_prop_cb)
|
||||
dp_prop_cb(dpn, entry->port, codec);
|
||||
acpi_dp_add_child(dsd, name->sdw_name, dpn);
|
||||
}
|
||||
if (entry->sink) {
|
||||
name = &dpn_sink_names[entry->port];
|
||||
dpn = acpi_dp_new_table(name->acpi_name);
|
||||
soundwire_gen_dpn(dpn, entry->sink);
|
||||
|
||||
/* Callback for custom properties from the codec. */
|
||||
if (dp_prop_cb)
|
||||
dp_prop_cb(dpn, entry->port, codec);
|
||||
acpi_dp_add_child(dsd, name->sdw_name, dpn);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,4 @@
|
||||
################################################################################
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
|
||||
###############################################################################
|
||||
# ARM specific options
|
||||
|
@@ -1,8 +1,4 @@
|
||||
################################################################################
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
###############################################################################
|
||||
|
||||
armv4_flags = -marm -march=armv4t -I$(src)/arch/arm/include/armv4/ \
|
||||
-D__COREBOOT_ARM_ARCH__=4
|
||||
|
@@ -5,7 +5,6 @@
|
||||
* Reference: ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <arch/cache.h>
|
||||
|
||||
|
@@ -1,8 +1,4 @@
|
||||
################################################################################
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
###############################################################################
|
||||
|
||||
armv7_flags = -mthumb -I$(src)/arch/arm/include/armv7/ -D__COREBOOT_ARM_ARCH__=7
|
||||
armv7-a_flags = -march=armv7-a $(armv7_flags) -D__COREBOOT_ARM_V7_A__
|
||||
|
@@ -3,7 +3,6 @@
|
||||
* cache.c: Cache maintenance routines for ARMv7-M
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <arch/cache.h>
|
||||
|
||||
|
@@ -1,8 +1,4 @@
|
||||
################################################################################
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
################################################################################
|
||||
|
||||
libgcc_files = ashldi3.S lib1funcs.S lshrdi3.S muldi3.S ucmpdi2.S uldivmod.S
|
||||
libgcc_files += udivmoddi4.c umoddi3.c
|
||||
|
@@ -1,8 +1,4 @@
|
||||
################################################################################
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
# Take care of subdirectories
|
||||
|
@@ -1,8 +1,4 @@
|
||||
################################################################################
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
################################################################################
|
||||
|
||||
ifeq ($(CONFIG_ARCH_ARMV8_EXTENSION),0)
|
||||
march = armv8-a
|
||||
|
@@ -1,8 +1,4 @@
|
||||
################################################################################
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
################################################################################
|
||||
|
||||
ppc64_flags = -I$(src)/arch/ppc64/ -mbig-endian -mcpu=power8 -mtune=power8
|
||||
|
||||
|
@@ -1,8 +1,4 @@
|
||||
################################################################################
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
## RISC-V specific options
|
||||
|
@@ -5,7 +5,6 @@
|
||||
#include <console/console.h>
|
||||
#include <bootmem.h>
|
||||
#include <program_loading.h>
|
||||
#include <lib.h>
|
||||
#include <fit.h>
|
||||
#include <endian.h>
|
||||
|
||||
|
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <arch/cpu.h>
|
||||
#include <arch/encoding.h>
|
||||
#include <stdint.h>
|
||||
#include <vm.h>
|
||||
|
||||
/* Delegate controls which traps are delegated to the payload. If you
|
||||
|
@@ -1,6 +1,4 @@
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
|
||||
config ARCH_X86
|
||||
bool
|
||||
|
@@ -1,6 +1,4 @@
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
##
|
||||
|
||||
ifeq ($(CONFIG_POSTCAR_STAGE),y)
|
||||
$(eval $(call init_standard_toolchain,postcar))
|
||||
|
@@ -10,11 +10,12 @@
|
||||
|
||||
#include <cpu/x86/cr.h>
|
||||
|
||||
.section .text
|
||||
|
||||
/*
|
||||
* Include the old code for reset vector and protected mode entry. That code has
|
||||
* withstood the test of time.
|
||||
*/
|
||||
#include <arch/x86/prologue.inc>
|
||||
#include <cpu/x86/16bit/entry16.inc>
|
||||
#include <cpu/x86/16bit/reset16.inc>
|
||||
#include <cpu/x86/32bit/entry32.inc>
|
||||
|
@@ -4,7 +4,6 @@
|
||||
#include <fallback.h>
|
||||
#include <program_loading.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *get_fallback(const char *stagelist)
|
||||
{
|
||||
|
@@ -62,13 +62,11 @@
|
||||
|
||||
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
|
||||
_bss = .;
|
||||
#if ENV_STAGE_HAS_BSS_SECTION
|
||||
/* Allow global uninitialized variables for stages without CAR teardown. */
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
#endif
|
||||
. = ALIGN(ARCH_POINTER_ALIGN_SIZE);
|
||||
_ebss = .;
|
||||
_car_unallocated_start = .;
|
||||
|
@@ -23,7 +23,7 @@ _FMAP_SIZE = 0;
|
||||
* The stack area is not shared between stages, but is defined here for
|
||||
* convenience.
|
||||
*/
|
||||
. = CONFIG_X86_RESET_VECTOR - ARCH_STACK_ALIGN_SIZE - _STACK_SIZE - _CONSOLE_SIZE - _TIMESTAMPS_SIZE - _FMAP_SIZE;
|
||||
. = CONFIG_X86_RESET_VECTOR - ARCH_STACK_ALIGN_SIZE - _STACK_SIZE - _CONSOLE_SIZE - _TIMESTAMPS_SIZE - _FMAP_SIZE - VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE;
|
||||
|
||||
_ = ASSERT(. > _eprogram, "Not enough room for .earlyram.data. Try increasing C_ENV_BOOTBLOCK_SIZE, or decreasing either EARLYRAM_BSP_STACK_SIZE or PRERAM_CBMEM_CONSOLE_SIZE.");
|
||||
|
||||
@@ -37,6 +37,11 @@ _ = ASSERT(. > _eprogram, "Not enough room for .earlyram.data. Try increasing C_
|
||||
#if !CONFIG(NO_FMAP_CACHE)
|
||||
FMAP_CACHE(., FMAP_SIZE)
|
||||
#endif
|
||||
|
||||
#if CONFIG(VBOOT_STARTS_IN_BOOTBLOCK)
|
||||
ALIGN_COUNTER(16);
|
||||
VBOOT2_WORK(., VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE)
|
||||
#endif
|
||||
}
|
||||
|
||||
_ = ASSERT(. <= CONFIG_X86_RESET_VECTOR, "Earlyram data regions don't fit below the reset vector!");
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
SECTIONS {
|
||||
. = (0xffffffff - CONFIG_ID_SECTION_OFFSET) - (__id_end - __id_start) + 1;
|
||||
. = (CONFIG_X86_RESET_VECTOR - CONFIG_ID_SECTION_OFFSET) + 0x10 - (__id_end - __id_start);
|
||||
.id (.): {
|
||||
KEEP(*(.id))
|
||||
}
|
||||
|
@@ -5,50 +5,42 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static __always_inline uint8_t read8(
|
||||
const volatile void *addr)
|
||||
static __always_inline uint8_t read8(const volatile void *addr)
|
||||
{
|
||||
return *((volatile uint8_t *)(addr));
|
||||
}
|
||||
|
||||
static __always_inline uint16_t read16(
|
||||
const volatile void *addr)
|
||||
static __always_inline uint16_t read16(const volatile void *addr)
|
||||
{
|
||||
return *((volatile uint16_t *)(addr));
|
||||
}
|
||||
|
||||
static __always_inline uint32_t read32(
|
||||
const volatile void *addr)
|
||||
static __always_inline uint32_t read32(const volatile void *addr)
|
||||
{
|
||||
return *((volatile uint32_t *)(addr));
|
||||
}
|
||||
|
||||
static __always_inline uint64_t read64(
|
||||
const volatile void *addr)
|
||||
static __always_inline uint64_t read64(const volatile void *addr)
|
||||
{
|
||||
return *((volatile uint64_t *)(addr));
|
||||
}
|
||||
|
||||
static __always_inline void write8(volatile void *addr,
|
||||
uint8_t value)
|
||||
static __always_inline void write8(volatile void *addr, uint8_t value)
|
||||
{
|
||||
*((volatile uint8_t *)(addr)) = value;
|
||||
}
|
||||
|
||||
static __always_inline void write16(volatile void *addr,
|
||||
uint16_t value)
|
||||
static __always_inline void write16(volatile void *addr, uint16_t value)
|
||||
{
|
||||
*((volatile uint16_t *)(addr)) = value;
|
||||
}
|
||||
|
||||
static __always_inline void write32(volatile void *addr,
|
||||
uint32_t value)
|
||||
static __always_inline void write32(volatile void *addr, uint32_t value)
|
||||
{
|
||||
*((volatile uint32_t *)(addr)) = value;
|
||||
}
|
||||
|
||||
static __always_inline void write64(volatile void *addr,
|
||||
uint64_t value)
|
||||
static __always_inline void write64(volatile void *addr, uint64_t value)
|
||||
{
|
||||
*((volatile uint64_t *)(addr)) = value;
|
||||
}
|
||||
|
@@ -1,6 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <cpu/x86/post_code.h>
|
||||
|
||||
.section ".rom.data", "a", @progbits
|
||||
.section ".rom.text", "ax", @progbits
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config COMMONLIB_STORAGE
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
ifeq ($(CONFIG_COMMONLIB_STORAGE),y)
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_AMD_AGESA
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
subdirs-$(CONFIG_CPU_AMD_AGESA_FAMILY14) += family14
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_AMD_AGESA_FAMILY14
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
romstage-y += fixme.c
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_AMD_AGESA_FAMILY15_TN
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
romstage-y += fixme.c
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_AMD_AGESA_FAMILY16_KB
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
romstage-y += fixme.c
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_AMD_PI_00630F01
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
romstage-y += fixme.c
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_AMD_PI_00660F01
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
romstage-y += fixme.c
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_AMD_PI_00730F01
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
romstage-y += fixme.c
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_AMD_PI
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
subdirs-$(CONFIG_CPU_AMD_PI_00630F01) += 00630F01
|
||||
|
@@ -12,7 +12,7 @@ void set_feature_ctrl_lock(void);
|
||||
/*
|
||||
* Init CPPC block with MSRs for Intel Enhanced Speed Step Technology.
|
||||
* Version 2 is suggested--this function's implementation of version 3
|
||||
* may have room for improvment.
|
||||
* may have room for improvement.
|
||||
*/
|
||||
struct cppc_config;
|
||||
void cpu_init_cppc_config(struct cppc_config *config, u32 version);
|
||||
|
@@ -8,7 +8,7 @@ fit_pointer:
|
||||
.long 0
|
||||
.previous
|
||||
|
||||
.section ".rom.data", "a", @progbits
|
||||
.section .text
|
||||
.align 16
|
||||
.global fit_table
|
||||
.global fit_table_end
|
||||
|
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
#include "haswell.h"
|
||||
|
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <console/console.h>
|
||||
#include <cf9_reset.h>
|
||||
#include <cpu/x86/bist.h>
|
||||
|
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <cpu/intel/speedstep.h>
|
||||
|
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <cpu/x86/msr.h>
|
||||
#include <cpu/intel/speedstep.h>
|
||||
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_INTEL_SLOT_1
|
||||
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_QEMU_POWER8
|
||||
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config CPU_QEMU_X86
|
||||
|
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cbmem.h>
|
||||
#include <symbols.h>
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <symbols.h>
|
||||
|
||||
|
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
#include <arch/rom_segs.h>
|
||||
#include <cpu/x86/post_code.h>
|
||||
|
||||
/* Symbol _start16bit must be aligned to 4kB to start AP CPUs with
|
||||
* Startup IPI message without RAM.
|
||||
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
bootblock-y += name.c
|
||||
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
ramstage-y += smm_module_loader.c
|
||||
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
menu "Devices"
|
||||
@@ -585,6 +583,22 @@ config PCIEXP_HOTPLUG_PREFETCH_MEM
|
||||
child devices. This size should be page-aligned. The default is
|
||||
256 MiB.
|
||||
|
||||
config PCIEXP_HOTPLUG_PREFETCH_MEM_ABOVE_4G
|
||||
bool
|
||||
depends on RESOURCE_ALLOCATOR_V4
|
||||
default y if !PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G
|
||||
default n
|
||||
help
|
||||
This enables prefetch memory allocation above 4G boundary for the
|
||||
hotplug resources.
|
||||
|
||||
config PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G
|
||||
bool "PCI Express Hotplug Prefetch Memory Allocation below 4G boundary"
|
||||
default n
|
||||
help
|
||||
This enables prefetch memory allocation below 4G boundary for the
|
||||
hotplug resources.
|
||||
|
||||
config PCIEXP_HOTPLUG_IO
|
||||
hex "PCI Express Hotplug I/O Space"
|
||||
default 0x2000
|
||||
@@ -779,4 +793,22 @@ config SOFTWARE_I2C
|
||||
I2C controller is not (yet) available. The platform code needs to
|
||||
provide bindings to manually toggle I2C lines.
|
||||
|
||||
config RESOURCE_ALLOCATOR_V3
|
||||
bool
|
||||
default n
|
||||
help
|
||||
This config option enables resource allocator v3 which performs
|
||||
top down allocation of resources in a single MMIO window. This is the
|
||||
old resource allocator meant to be used only until the broken AMD
|
||||
chipsets are fixed. DO NOT USE THIS FOR ANY NEW CHIPSETS!
|
||||
|
||||
config RESOURCE_ALLOCATOR_V4
|
||||
bool
|
||||
default n if RESOURCE_ALLOCATOR_V3
|
||||
default y if !RESOURCE_ALLOCATOR_V3
|
||||
help
|
||||
This config option enables resource allocator v4 which uses multiple
|
||||
ranges for allocating resources. This allows allocation of resources
|
||||
above 4G boundary as well.
|
||||
|
||||
endmenu
|
||||
|
@@ -58,3 +58,7 @@ bootblock-y += mmio.c
|
||||
verstage-y += mmio.c
|
||||
romstage-y += mmio.c
|
||||
ramstage-y += mmio.c
|
||||
|
||||
ramstage-y += resource_allocator_common.c
|
||||
ramstage-$(CONFIG_RESOURCE_ALLOCATOR_V3) += resource_allocator_v3.c
|
||||
ramstage-$(CONFIG_RESOURCE_ALLOCATOR_V4) += resource_allocator_v4.c
|
||||
|
@@ -145,33 +145,6 @@ struct device *alloc_find_dev(struct bus *parent, struct device_path *path)
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Round a number up to an alignment.
|
||||
*
|
||||
* @param val The starting value.
|
||||
* @param pow Alignment as a power of two.
|
||||
* @return Rounded up number.
|
||||
*/
|
||||
static resource_t round(resource_t val, unsigned long pow)
|
||||
{
|
||||
resource_t mask;
|
||||
mask = (1ULL << pow) - 1ULL;
|
||||
val += mask;
|
||||
val &= ~mask;
|
||||
return val;
|
||||
}
|
||||
|
||||
static const char *resource2str(struct resource *res)
|
||||
{
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
return "io";
|
||||
if (res->flags & IORESOURCE_PREFETCH)
|
||||
return "prefmem";
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
return "mem";
|
||||
return "undefined";
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the resources on all devices of a given bus.
|
||||
*
|
||||
@@ -209,523 +182,6 @@ static void read_resources(struct bus *bus)
|
||||
dev_path(bus->dev), bus->secondary, bus->link_num);
|
||||
}
|
||||
|
||||
struct pick_largest_state {
|
||||
struct resource *last;
|
||||
const struct device *result_dev;
|
||||
struct resource *result;
|
||||
int seen_last;
|
||||
};
|
||||
|
||||
static void pick_largest_resource(void *gp, struct device *dev,
|
||||
struct resource *resource)
|
||||
{
|
||||
struct pick_largest_state *state = gp;
|
||||
struct resource *last;
|
||||
|
||||
last = state->last;
|
||||
|
||||
/* Be certain to pick the successor to last. */
|
||||
if (resource == last) {
|
||||
state->seen_last = 1;
|
||||
return;
|
||||
}
|
||||
if (resource->flags & IORESOURCE_FIXED)
|
||||
return; /* Skip it. */
|
||||
if (last && ((last->align < resource->align) ||
|
||||
((last->align == resource->align) &&
|
||||
(last->size < resource->size)) ||
|
||||
((last->align == resource->align) &&
|
||||
(last->size == resource->size) && (!state->seen_last)))) {
|
||||
return;
|
||||
}
|
||||
if (!state->result ||
|
||||
(state->result->align < resource->align) ||
|
||||
((state->result->align == resource->align) &&
|
||||
(state->result->size < resource->size))) {
|
||||
state->result_dev = dev;
|
||||
state->result = resource;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct device *largest_resource(struct bus *bus,
|
||||
struct resource **result_res,
|
||||
unsigned long type_mask,
|
||||
unsigned long type)
|
||||
{
|
||||
struct pick_largest_state state;
|
||||
|
||||
state.last = *result_res;
|
||||
state.result_dev = NULL;
|
||||
state.result = NULL;
|
||||
state.seen_last = 0;
|
||||
|
||||
search_bus_resources(bus, type_mask, type, pick_largest_resource,
|
||||
&state);
|
||||
|
||||
*result_res = state.result;
|
||||
return state.result_dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is the guts of the resource allocator.
|
||||
*
|
||||
* The problem.
|
||||
* - Allocate resource locations for every device.
|
||||
* - Don't overlap, and follow the rules of bridges.
|
||||
* - Don't overlap with resources in fixed locations.
|
||||
* - Be efficient so we don't have ugly strategies.
|
||||
*
|
||||
* The strategy.
|
||||
* - Devices that have fixed addresses are the minority so don't
|
||||
* worry about them too much. Instead only use part of the address
|
||||
* space for devices with programmable addresses. This easily handles
|
||||
* everything except bridges.
|
||||
*
|
||||
* - PCI devices are required to have their sizes and their alignments
|
||||
* equal. In this case an optimal solution to the packing problem
|
||||
* exists. Allocate all devices from highest alignment to least
|
||||
* alignment or vice versa. Use this.
|
||||
*
|
||||
* - So we can handle more than PCI run two allocation passes on bridges. The
|
||||
* first to see how large the resources are behind the bridge, and what
|
||||
* their alignment requirements are. The second to assign a safe address to
|
||||
* the devices behind the bridge. This allows us to treat a bridge as just
|
||||
* a device with a couple of resources, and not need to special case it in
|
||||
* the allocator. Also this allows handling of other types of bridges.
|
||||
*
|
||||
* @param bus The bus we are traversing.
|
||||
* @param bridge The bridge resource which must contain the bus' resources.
|
||||
* @param type_mask This value gets ANDed with the resource type.
|
||||
* @param type This value must match the result of the AND.
|
||||
* @return TODO
|
||||
*/
|
||||
static void compute_resources(struct bus *bus, struct resource *bridge,
|
||||
unsigned long type_mask, unsigned long type)
|
||||
{
|
||||
const struct device *dev;
|
||||
struct resource *resource;
|
||||
resource_t base;
|
||||
base = round(bridge->base, bridge->align);
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
printk(BIOS_SPEW, "%s %s: base: %llx size: %llx align: %d gran: %d"
|
||||
" limit: %llx\n", dev_path(bus->dev), resource2str(bridge),
|
||||
base, bridge->size, bridge->align,
|
||||
bridge->gran, bridge->limit);
|
||||
|
||||
/* For each child which is a bridge, compute the resource needs. */
|
||||
for (dev = bus->children; dev; dev = dev->sibling) {
|
||||
struct resource *child_bridge;
|
||||
|
||||
if (!dev->link_list)
|
||||
continue;
|
||||
|
||||
/* Find the resources with matching type flags. */
|
||||
for (child_bridge = dev->resource_list; child_bridge;
|
||||
child_bridge = child_bridge->next) {
|
||||
struct bus* link;
|
||||
|
||||
if (!(child_bridge->flags & IORESOURCE_BRIDGE)
|
||||
|| (child_bridge->flags & type_mask) != type)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Split prefetchable memory if combined. Many domains
|
||||
* use the same address space for prefetchable memory
|
||||
* and non-prefetchable memory. Bridges below them need
|
||||
* it separated. Add the PREFETCH flag to the type_mask
|
||||
* and type.
|
||||
*/
|
||||
link = dev->link_list;
|
||||
while (link && link->link_num !=
|
||||
IOINDEX_LINK(child_bridge->index))
|
||||
link = link->next;
|
||||
|
||||
if (link == NULL) {
|
||||
printk(BIOS_ERR, "link %ld not found on %s\n",
|
||||
IOINDEX_LINK(child_bridge->index),
|
||||
dev_path(dev));
|
||||
}
|
||||
|
||||
compute_resources(link, child_bridge,
|
||||
type_mask | IORESOURCE_PREFETCH,
|
||||
type | (child_bridge->flags &
|
||||
IORESOURCE_PREFETCH));
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember we haven't found anything yet. */
|
||||
resource = NULL;
|
||||
|
||||
/*
|
||||
* Walk through all the resources on the current bus and compute the
|
||||
* amount of address space taken by them. Take granularity and
|
||||
* alignment into account.
|
||||
*/
|
||||
while ((dev = largest_resource(bus, &resource, type_mask, type))) {
|
||||
|
||||
/* Size 0 resources can be skipped. */
|
||||
if (!resource->size)
|
||||
continue;
|
||||
|
||||
/* Propagate the resource alignment to the bridge resource. */
|
||||
if (resource->align > bridge->align)
|
||||
bridge->align = resource->align;
|
||||
|
||||
/* Propagate the resource limit to the bridge register. */
|
||||
if (bridge->limit > resource->limit)
|
||||
bridge->limit = resource->limit;
|
||||
|
||||
/* Warn if it looks like APICs aren't declared. */
|
||||
if ((resource->limit == 0xffffffff) &&
|
||||
(resource->flags & IORESOURCE_ASSIGNED)) {
|
||||
printk(BIOS_ERR,
|
||||
"Resource limit looks wrong! (no APIC?)\n");
|
||||
printk(BIOS_ERR, "%s %02lx limit %08llx\n",
|
||||
dev_path(dev), resource->index, resource->limit);
|
||||
}
|
||||
|
||||
if (resource->flags & IORESOURCE_IO) {
|
||||
/*
|
||||
* Don't allow potential aliases over the legacy PCI
|
||||
* expansion card addresses. The legacy PCI decodes
|
||||
* only 10 bits, uses 0x100 - 0x3ff. Therefore, only
|
||||
* 0x00 - 0xff can be used out of each 0x400 block of
|
||||
* I/O space.
|
||||
*/
|
||||
if ((base & 0x300) != 0) {
|
||||
base = (base & ~0x3ff) + 0x400;
|
||||
}
|
||||
/*
|
||||
* Don't allow allocations in the VGA I/O range.
|
||||
* PCI has special cases for that.
|
||||
*/
|
||||
else if ((base >= 0x3b0) && (base <= 0x3df)) {
|
||||
base = 0x3e0;
|
||||
}
|
||||
}
|
||||
/* Base must be aligned. */
|
||||
base = round(base, resource->align);
|
||||
resource->base = base;
|
||||
base += resource->size;
|
||||
|
||||
printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n",
|
||||
dev_path(dev), resource->index, resource->base,
|
||||
resource->base + resource->size - 1,
|
||||
resource2str(resource));
|
||||
}
|
||||
|
||||
/*
|
||||
* A PCI bridge resource does not need to be a power of two size, but
|
||||
* it does have a minimum granularity. Round the size up to that
|
||||
* minimum granularity so we know not to place something else at an
|
||||
* address positively decoded by the bridge.
|
||||
*/
|
||||
bridge->size = round(base, bridge->gran) -
|
||||
round(bridge->base, bridge->align);
|
||||
|
||||
printk(BIOS_SPEW, "%s %s: base: %llx size: %llx align: %d gran: %d"
|
||||
" limit: %llx done\n", dev_path(bus->dev),
|
||||
resource2str(bridge),
|
||||
base, bridge->size, bridge->align, bridge->gran, bridge->limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is the second part of the resource allocator.
|
||||
*
|
||||
* See the compute_resources function for a more detailed explanation.
|
||||
*
|
||||
* This function assigns the resources a value.
|
||||
*
|
||||
* @param bus The bus we are traversing.
|
||||
* @param bridge The bridge resource which must contain the bus' resources.
|
||||
* @param type_mask This value gets ANDed with the resource type.
|
||||
* @param type This value must match the result of the AND.
|
||||
*
|
||||
* @see compute_resources
|
||||
*/
|
||||
static void allocate_resources(struct bus *bus, struct resource *bridge,
|
||||
unsigned long type_mask, unsigned long type)
|
||||
{
|
||||
const struct device *dev;
|
||||
struct resource *resource;
|
||||
resource_t base;
|
||||
base = bridge->base;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
printk(BIOS_SPEW, "%s %s: base:%llx size:%llx align:%d gran:%d "
|
||||
"limit:%llx\n", dev_path(bus->dev),
|
||||
resource2str(bridge),
|
||||
base, bridge->size, bridge->align, bridge->gran, bridge->limit);
|
||||
|
||||
/* Remember we haven't found anything yet. */
|
||||
resource = NULL;
|
||||
|
||||
/*
|
||||
* Walk through all the resources on the current bus and allocate them
|
||||
* address space.
|
||||
*/
|
||||
while ((dev = largest_resource(bus, &resource, type_mask, type))) {
|
||||
|
||||
/* Propagate the bridge limit to the resource register. */
|
||||
if (resource->limit > bridge->limit)
|
||||
resource->limit = bridge->limit;
|
||||
|
||||
/* Size 0 resources can be skipped. */
|
||||
if (!resource->size) {
|
||||
/* Set the base to limit so it doesn't confuse tolm. */
|
||||
resource->base = resource->limit;
|
||||
resource->flags |= IORESOURCE_ASSIGNED;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resource->flags & IORESOURCE_IO) {
|
||||
/*
|
||||
* Don't allow potential aliases over the legacy PCI
|
||||
* expansion card addresses. The legacy PCI decodes
|
||||
* only 10 bits, uses 0x100 - 0x3ff. Therefore, only
|
||||
* 0x00 - 0xff can be used out of each 0x400 block of
|
||||
* I/O space.
|
||||
*/
|
||||
if ((base & 0x300) != 0) {
|
||||
base = (base & ~0x3ff) + 0x400;
|
||||
}
|
||||
/*
|
||||
* Don't allow allocations in the VGA I/O range.
|
||||
* PCI has special cases for that.
|
||||
*/
|
||||
else if ((base >= 0x3b0) && (base <= 0x3df)) {
|
||||
base = 0x3e0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((round(base, resource->align) + resource->size - 1) <=
|
||||
resource->limit) {
|
||||
/* Base must be aligned. */
|
||||
base = round(base, resource->align);
|
||||
resource->base = base;
|
||||
resource->limit = resource->base + resource->size - 1;
|
||||
resource->flags |= IORESOURCE_ASSIGNED;
|
||||
resource->flags &= ~IORESOURCE_STORED;
|
||||
base += resource->size;
|
||||
} else {
|
||||
printk(BIOS_ERR, "!! Resource didn't fit !!\n");
|
||||
printk(BIOS_ERR, " aligned base %llx size %llx "
|
||||
"limit %llx\n", round(base, resource->align),
|
||||
resource->size, resource->limit);
|
||||
printk(BIOS_ERR, " %llx needs to be <= %llx "
|
||||
"(limit)\n", (round(base, resource->align) +
|
||||
resource->size) - 1, resource->limit);
|
||||
printk(BIOS_ERR, " %s%s %02lx * [0x%llx - 0x%llx]"
|
||||
" %s\n", (resource->flags & IORESOURCE_ASSIGNED)
|
||||
? "Assigned: " : "", dev_path(dev),
|
||||
resource->index, resource->base,
|
||||
resource->base + resource->size - 1,
|
||||
resource2str(resource));
|
||||
}
|
||||
|
||||
printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n",
|
||||
dev_path(dev), resource->index, resource->base,
|
||||
resource->size ? resource->base + resource->size - 1 :
|
||||
resource->base, resource2str(resource));
|
||||
}
|
||||
|
||||
/*
|
||||
* A PCI bridge resource does not need to be a power of two size, but
|
||||
* it does have a minimum granularity. Round the size up to that
|
||||
* minimum granularity so we know not to place something else at an
|
||||
* address positively decoded by the bridge.
|
||||
*/
|
||||
|
||||
bridge->flags |= IORESOURCE_ASSIGNED;
|
||||
|
||||
printk(BIOS_SPEW, "%s %s: next_base: %llx size: %llx align: %d "
|
||||
"gran: %d done\n", dev_path(bus->dev),
|
||||
resource2str(bridge), base, bridge->size, bridge->align,
|
||||
bridge->gran);
|
||||
|
||||
/* For each child which is a bridge, allocate_resources. */
|
||||
for (dev = bus->children; dev; dev = dev->sibling) {
|
||||
struct resource *child_bridge;
|
||||
|
||||
if (!dev->link_list)
|
||||
continue;
|
||||
|
||||
/* Find the resources with matching type flags. */
|
||||
for (child_bridge = dev->resource_list; child_bridge;
|
||||
child_bridge = child_bridge->next) {
|
||||
struct bus* link;
|
||||
|
||||
if (!(child_bridge->flags & IORESOURCE_BRIDGE) ||
|
||||
(child_bridge->flags & type_mask) != type)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Split prefetchable memory if combined. Many domains
|
||||
* use the same address space for prefetchable memory
|
||||
* and non-prefetchable memory. Bridges below them need
|
||||
* it separated. Add the PREFETCH flag to the type_mask
|
||||
* and type.
|
||||
*/
|
||||
link = dev->link_list;
|
||||
while (link && link->link_num !=
|
||||
IOINDEX_LINK(child_bridge->index))
|
||||
link = link->next;
|
||||
if (link == NULL)
|
||||
printk(BIOS_ERR, "link %ld not found on %s\n",
|
||||
IOINDEX_LINK(child_bridge->index),
|
||||
dev_path(dev));
|
||||
|
||||
allocate_resources(link, child_bridge,
|
||||
type_mask | IORESOURCE_PREFETCH,
|
||||
type | (child_bridge->flags &
|
||||
IORESOURCE_PREFETCH));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int resource_is(struct resource *res, u32 type)
|
||||
{
|
||||
return (res->flags & IORESOURCE_TYPE_MASK) == type;
|
||||
}
|
||||
|
||||
struct constraints {
|
||||
struct resource io, mem;
|
||||
};
|
||||
|
||||
static struct resource *resource_limit(struct constraints *limits,
|
||||
struct resource *res)
|
||||
{
|
||||
struct resource *lim = NULL;
|
||||
|
||||
/* MEM, or I/O - skip any others. */
|
||||
if (resource_is(res, IORESOURCE_MEM))
|
||||
lim = &limits->mem;
|
||||
else if (resource_is(res, IORESOURCE_IO))
|
||||
lim = &limits->io;
|
||||
|
||||
return lim;
|
||||
}
|
||||
|
||||
static void constrain_resources(const struct device *dev,
|
||||
struct constraints* limits)
|
||||
{
|
||||
const struct device *child;
|
||||
struct resource *res;
|
||||
struct resource *lim;
|
||||
struct bus *link;
|
||||
|
||||
/* Constrain limits based on the fixed resources of this device. */
|
||||
for (res = dev->resource_list; res; res = res->next) {
|
||||
if (!(res->flags & IORESOURCE_FIXED))
|
||||
continue;
|
||||
if (!res->size) {
|
||||
/* It makes no sense to have 0-sized, fixed resources.*/
|
||||
printk(BIOS_ERR, "skipping %s@%lx fixed resource, "
|
||||
"size=0!\n", dev_path(dev), res->index);
|
||||
continue;
|
||||
}
|
||||
|
||||
lim = resource_limit(limits, res);
|
||||
if (!lim)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Is it a fixed resource outside the current known region?
|
||||
* If so, we don't have to consider it - it will be handled
|
||||
* correctly and doesn't affect current region's limits.
|
||||
*/
|
||||
if (((res->base + res->size -1) < lim->base)
|
||||
|| (res->base > lim->limit))
|
||||
continue;
|
||||
|
||||
printk(BIOS_SPEW, "%s: %s %02lx base %08llx limit %08llx %s (fixed)\n",
|
||||
__func__, dev_path(dev), res->index, res->base,
|
||||
res->base + res->size - 1, resource2str(res));
|
||||
|
||||
/*
|
||||
* Choose to be above or below fixed resources. This check is
|
||||
* signed so that "negative" amounts of space are handled
|
||||
* correctly.
|
||||
*/
|
||||
if ((signed long long)(lim->limit - (res->base + res->size -1))
|
||||
> (signed long long)(res->base - lim->base))
|
||||
lim->base = res->base + res->size;
|
||||
else
|
||||
lim->limit = res->base -1;
|
||||
}
|
||||
|
||||
/* Descend into every enabled child and look for fixed resources. */
|
||||
for (link = dev->link_list; link; link = link->next) {
|
||||
for (child = link->children; child; child = child->sibling) {
|
||||
if (child->enabled)
|
||||
constrain_resources(child, limits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void avoid_fixed_resources(const struct device *dev)
|
||||
{
|
||||
struct constraints limits;
|
||||
struct resource *res;
|
||||
struct resource *lim;
|
||||
|
||||
printk(BIOS_SPEW, "%s: %s\n", __func__, dev_path(dev));
|
||||
|
||||
/* Initialize constraints to maximum size. */
|
||||
limits.io.base = 0;
|
||||
limits.io.limit = 0xffffffffffffffffULL;
|
||||
limits.mem.base = 0;
|
||||
limits.mem.limit = 0xffffffffffffffffULL;
|
||||
|
||||
/* Constrain the limits to dev's initial resources. */
|
||||
for (res = dev->resource_list; res; res = res->next) {
|
||||
if ((res->flags & IORESOURCE_FIXED))
|
||||
continue;
|
||||
printk(BIOS_SPEW, "%s:@%s %02lx limit %08llx\n", __func__,
|
||||
dev_path(dev), res->index, res->limit);
|
||||
|
||||
lim = resource_limit(&limits, res);
|
||||
if (!lim)
|
||||
continue;
|
||||
|
||||
if (res->base > lim->base)
|
||||
lim->base = res->base;
|
||||
if (res->limit < lim->limit)
|
||||
lim->limit = res->limit;
|
||||
}
|
||||
|
||||
/* Look through the tree for fixed resources and update the limits. */
|
||||
constrain_resources(dev, &limits);
|
||||
|
||||
/* Update dev's resources with new limits. */
|
||||
for (res = dev->resource_list; res; res = res->next) {
|
||||
if ((res->flags & IORESOURCE_FIXED))
|
||||
continue;
|
||||
|
||||
lim = resource_limit(&limits, res);
|
||||
if (!lim)
|
||||
continue;
|
||||
|
||||
/* Is the resource outside the limits? */
|
||||
if (lim->base > res->base)
|
||||
res->base = lim->base;
|
||||
if (res->limit > lim->limit)
|
||||
res->limit = lim->limit;
|
||||
|
||||
/* MEM resources need to start at the highest address manageable. */
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
res->base = resource_max(res);
|
||||
|
||||
printk(BIOS_SPEW, "%s:@%s %02lx base %08llx limit %08llx\n",
|
||||
__func__, dev_path(dev), res->index, res->base, res->limit);
|
||||
}
|
||||
}
|
||||
|
||||
struct device *vga_pri = NULL;
|
||||
static void set_vga_bridge_bits(void)
|
||||
{
|
||||
@@ -996,9 +452,7 @@ void dev_enumerate(void)
|
||||
*/
|
||||
void dev_configure(void)
|
||||
{
|
||||
struct resource *res;
|
||||
const struct device *root;
|
||||
const struct device *child;
|
||||
|
||||
set_vga_bridge_bits();
|
||||
|
||||
@@ -1020,53 +474,8 @@ void dev_configure(void)
|
||||
|
||||
print_resource_tree(root, BIOS_SPEW, "After reading.");
|
||||
|
||||
/* Compute resources for all domains. */
|
||||
for (child = root->link_list->children; child; child = child->sibling) {
|
||||
if (!(child->path.type == DEVICE_PATH_DOMAIN))
|
||||
continue;
|
||||
post_log_path(child);
|
||||
for (res = child->resource_list; res; res = res->next) {
|
||||
if (res->flags & IORESOURCE_FIXED)
|
||||
continue;
|
||||
if (res->flags & IORESOURCE_MEM) {
|
||||
compute_resources(child->link_list,
|
||||
res, IORESOURCE_TYPE_MASK, IORESOURCE_MEM);
|
||||
continue;
|
||||
}
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
compute_resources(child->link_list,
|
||||
res, IORESOURCE_TYPE_MASK, IORESOURCE_IO);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
allocate_resources(root);
|
||||
|
||||
/* For all domains. */
|
||||
for (child = root->link_list->children; child; child=child->sibling)
|
||||
if (child->path.type == DEVICE_PATH_DOMAIN)
|
||||
avoid_fixed_resources(child);
|
||||
|
||||
/* Store the computed resource allocations into device registers ... */
|
||||
printk(BIOS_INFO, "Setting resources...\n");
|
||||
for (child = root->link_list->children; child; child = child->sibling) {
|
||||
if (!(child->path.type == DEVICE_PATH_DOMAIN))
|
||||
continue;
|
||||
post_log_path(child);
|
||||
for (res = child->resource_list; res; res = res->next) {
|
||||
if (res->flags & IORESOURCE_FIXED)
|
||||
continue;
|
||||
if (res->flags & IORESOURCE_MEM) {
|
||||
allocate_resources(child->link_list,
|
||||
res, IORESOURCE_TYPE_MASK, IORESOURCE_MEM);
|
||||
continue;
|
||||
}
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
allocate_resources(child->link_list,
|
||||
res, IORESOURCE_TYPE_MASK, IORESOURCE_IO);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
assign_resources(root->link_list);
|
||||
printk(BIOS_INFO, "Done setting resources.\n");
|
||||
print_resource_tree(root, BIOS_SPEW, "After assigning values.");
|
||||
|
@@ -156,6 +156,12 @@ static int path_eq(const struct device_path *path1,
|
||||
case DEVICE_PATH_MMIO:
|
||||
equal = (path1->mmio.addr == path2->mmio.addr);
|
||||
break;
|
||||
case DEVICE_PATH_ESPI:
|
||||
equal = (path1->espi.addr == path2->espi.addr);
|
||||
break;
|
||||
case DEVICE_PATH_LPC:
|
||||
equal = (path1->lpc.addr == path2->lpc.addr);
|
||||
break;
|
||||
default:
|
||||
printk(BIOS_ERR, "Unknown device type: %d\n", path1->type);
|
||||
break;
|
||||
@@ -221,7 +227,7 @@ DEVTREE_CONST struct device *pcidev_path_on_bus(unsigned int bus, pci_devfn_t de
|
||||
DEVTREE_CONST struct bus *pci_root_bus(void)
|
||||
{
|
||||
DEVTREE_CONST struct device *pci_domain;
|
||||
MAYBE_STATIC_BSS DEVTREE_CONST struct bus *pci_root = NULL;
|
||||
static DEVTREE_CONST struct bus *pci_root;
|
||||
|
||||
if (pci_root)
|
||||
return pci_root;
|
||||
|
@@ -215,6 +215,14 @@ const char *dev_path(const struct device *dev)
|
||||
snprintf(buffer, sizeof(buffer), "MMIO: %08lx",
|
||||
dev->path.mmio.addr);
|
||||
break;
|
||||
case DEVICE_PATH_ESPI:
|
||||
snprintf(buffer, sizeof(buffer), "ESPI: %08lx",
|
||||
dev->path.espi.addr);
|
||||
break;
|
||||
case DEVICE_PATH_LPC:
|
||||
snprintf(buffer, sizeof(buffer), "LPC: %08lx",
|
||||
dev->path.lpc.addr);
|
||||
break;
|
||||
default:
|
||||
printk(BIOS_ERR, "Unknown device path type: %d\n",
|
||||
dev->path.type);
|
||||
@@ -875,6 +883,13 @@ void tolm_test(void *gp, struct device *dev, struct resource *new)
|
||||
|
||||
best = *best_p;
|
||||
|
||||
/*
|
||||
* If resource is not allocated any space i.e. size is zero,
|
||||
* then do not consider this resource in tolm calculations.
|
||||
*/
|
||||
if (new->size == 0)
|
||||
return;
|
||||
|
||||
if (!best || (best->base > new->base))
|
||||
best = new;
|
||||
|
||||
@@ -885,9 +900,9 @@ u32 find_pci_tolm(struct bus *bus)
|
||||
{
|
||||
struct resource *min = NULL;
|
||||
u32 tolm;
|
||||
unsigned long mask_match = IORESOURCE_MEM | IORESOURCE_ASSIGNED;
|
||||
|
||||
search_bus_resources(bus, IORESOURCE_MEM, IORESOURCE_MEM,
|
||||
tolm_test, &min);
|
||||
search_bus_resources(bus, mask_match, mask_match, tolm_test, &min);
|
||||
|
||||
tolm = 0xffffffffUL;
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
subdirs-$(CONFIG_PCI_OPTION_ROM_RUN_YABEL) += x86emu
|
||||
|
@@ -1,5 +1,3 @@
|
||||
##
|
||||
##
|
||||
## SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
ramstage-$(CONFIG_PCI_OPTION_ROM_RUN_REALMODE) += x86.c
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include <device/pci_ops.h>
|
||||
#include <bootmode.h>
|
||||
#include <console/console.h>
|
||||
#include <cpu/cpu.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
@@ -433,22 +434,93 @@ void pci_domain_read_resources(struct device *dev)
|
||||
|
||||
/* Initialize the system-wide memory resources constraints. */
|
||||
res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
|
||||
res->limit = 0xffffffffULL;
|
||||
res->limit = (1ULL << cpu_phys_address_size()) - 1;
|
||||
res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE |
|
||||
IORESOURCE_ASSIGNED;
|
||||
}
|
||||
|
||||
static void pci_set_resource(struct device *dev, struct resource *resource)
|
||||
void pci_domain_set_resources(struct device *dev)
|
||||
{
|
||||
assign_resources(dev->link_list);
|
||||
}
|
||||
|
||||
static void pci_store_resource(const struct device *const dev,
|
||||
const struct resource *const resource)
|
||||
{
|
||||
unsigned long base_lo, base_hi;
|
||||
|
||||
base_lo = resource->base & 0xffffffff;
|
||||
base_hi = (resource->base >> 32) & 0xffffffff;
|
||||
|
||||
/*
|
||||
* Some chipsets allow us to set/clear the I/O bit
|
||||
* (e.g. VIA 82C686A). So set it to be safe.
|
||||
*/
|
||||
if (resource->flags & IORESOURCE_IO)
|
||||
base_lo |= PCI_BASE_ADDRESS_SPACE_IO;
|
||||
|
||||
pci_write_config32(dev, resource->index, base_lo);
|
||||
if (resource->flags & IORESOURCE_PCI64)
|
||||
pci_write_config32(dev, resource->index + 4, base_hi);
|
||||
}
|
||||
|
||||
static void pci_store_bridge_resource(const struct device *const dev,
|
||||
struct resource *const resource)
|
||||
{
|
||||
resource_t base, end;
|
||||
|
||||
/*
|
||||
* PCI bridges have no enable bit. They are disabled if the base of
|
||||
* the range is greater than the limit. If the size is zero, disable
|
||||
* by setting the base = limit and end = limit - 2^gran.
|
||||
*/
|
||||
if (resource->size == 0) {
|
||||
base = resource->limit;
|
||||
end = resource->limit - (1 << resource->gran);
|
||||
resource->base = base;
|
||||
} else {
|
||||
base = resource->base;
|
||||
end = resource_end(resource);
|
||||
}
|
||||
|
||||
if (resource->index == PCI_IO_BASE) {
|
||||
/* Set the I/O ranges. */
|
||||
pci_write_config8(dev, PCI_IO_BASE, base >> 8);
|
||||
pci_write_config16(dev, PCI_IO_BASE_UPPER16, base >> 16);
|
||||
pci_write_config8(dev, PCI_IO_LIMIT, end >> 8);
|
||||
pci_write_config16(dev, PCI_IO_LIMIT_UPPER16, end >> 16);
|
||||
} else if (resource->index == PCI_MEMORY_BASE) {
|
||||
/* Set the memory range. */
|
||||
pci_write_config16(dev, PCI_MEMORY_BASE, base >> 16);
|
||||
pci_write_config16(dev, PCI_MEMORY_LIMIT, end >> 16);
|
||||
} else if (resource->index == PCI_PREF_MEMORY_BASE) {
|
||||
/* Set the prefetchable memory range. */
|
||||
pci_write_config16(dev, PCI_PREF_MEMORY_BASE, base >> 16);
|
||||
pci_write_config32(dev, PCI_PREF_BASE_UPPER32, base >> 32);
|
||||
pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT, end >> 16);
|
||||
pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32, end >> 32);
|
||||
} else {
|
||||
/* Don't let me think I stored the resource. */
|
||||
resource->flags &= ~IORESOURCE_STORED;
|
||||
printk(BIOS_ERR, "ERROR: invalid resource->index %lx\n", resource->index);
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_set_resource(struct device *dev, struct resource *resource)
|
||||
{
|
||||
/* Make certain the resource has actually been assigned a value. */
|
||||
if (!(resource->flags & IORESOURCE_ASSIGNED)) {
|
||||
if (resource->flags & IORESOURCE_BRIDGE) {
|
||||
/* If a bridge resource has no value assigned,
|
||||
we can treat it like an empty resource. */
|
||||
resource->size = 0;
|
||||
} else {
|
||||
printk(BIOS_ERR, "ERROR: %s %02lx %s size: 0x%010llx not "
|
||||
"assigned\n", dev_path(dev), resource->index,
|
||||
resource_type(resource), resource->size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this resource is fixed don't worry about it. */
|
||||
if (resource->flags & IORESOURCE_FIXED)
|
||||
@@ -476,62 +548,13 @@ static void pci_set_resource(struct device *dev, struct resource *resource)
|
||||
dev->command |= PCI_COMMAND_MASTER;
|
||||
}
|
||||
|
||||
/* Get the base address. */
|
||||
base = resource->base;
|
||||
|
||||
/* Get the end. */
|
||||
end = resource_end(resource);
|
||||
|
||||
/* Now store the resource. */
|
||||
resource->flags |= IORESOURCE_STORED;
|
||||
|
||||
/*
|
||||
* PCI bridges have no enable bit. They are disabled if the base of
|
||||
* the range is greater than the limit. If the size is zero, disable
|
||||
* by setting the base = limit and end = limit - 2^gran.
|
||||
*/
|
||||
if (resource->size == 0 && (resource->flags & IORESOURCE_PCI_BRIDGE)) {
|
||||
base = resource->limit;
|
||||
end = resource->limit - (1 << resource->gran);
|
||||
resource->base = base;
|
||||
}
|
||||
|
||||
if (!(resource->flags & IORESOURCE_PCI_BRIDGE)) {
|
||||
unsigned long base_lo, base_hi;
|
||||
|
||||
/*
|
||||
* Some chipsets allow us to set/clear the I/O bit
|
||||
* (e.g. VIA 82C686A). So set it to be safe.
|
||||
*/
|
||||
base_lo = base & 0xffffffff;
|
||||
base_hi = (base >> 32) & 0xffffffff;
|
||||
if (resource->flags & IORESOURCE_IO)
|
||||
base_lo |= PCI_BASE_ADDRESS_SPACE_IO;
|
||||
pci_write_config32(dev, resource->index, base_lo);
|
||||
if (resource->flags & IORESOURCE_PCI64)
|
||||
pci_write_config32(dev, resource->index + 4, base_hi);
|
||||
} else if (resource->index == PCI_IO_BASE) {
|
||||
/* Set the I/O ranges. */
|
||||
pci_write_config8(dev, PCI_IO_BASE, base >> 8);
|
||||
pci_write_config16(dev, PCI_IO_BASE_UPPER16, base >> 16);
|
||||
pci_write_config8(dev, PCI_IO_LIMIT, end >> 8);
|
||||
pci_write_config16(dev, PCI_IO_LIMIT_UPPER16, end >> 16);
|
||||
} else if (resource->index == PCI_MEMORY_BASE) {
|
||||
/* Set the memory range. */
|
||||
pci_write_config16(dev, PCI_MEMORY_BASE, base >> 16);
|
||||
pci_write_config16(dev, PCI_MEMORY_LIMIT, end >> 16);
|
||||
} else if (resource->index == PCI_PREF_MEMORY_BASE) {
|
||||
/* Set the prefetchable memory range. */
|
||||
pci_write_config16(dev, PCI_PREF_MEMORY_BASE, base >> 16);
|
||||
pci_write_config32(dev, PCI_PREF_BASE_UPPER32, base >> 32);
|
||||
pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT, end >> 16);
|
||||
pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32, end >> 32);
|
||||
} else {
|
||||
/* Don't let me think I stored the resource. */
|
||||
resource->flags &= ~IORESOURCE_STORED;
|
||||
printk(BIOS_ERR, "ERROR: invalid resource->index %lx\n",
|
||||
resource->index);
|
||||
}
|
||||
if (resource->flags & IORESOURCE_PCI_BRIDGE)
|
||||
pci_store_bridge_resource(dev, resource);
|
||||
else
|
||||
pci_store_resource(dev, resource);
|
||||
|
||||
report_resource_stored(dev, resource, "");
|
||||
}
|
||||
@@ -764,17 +787,19 @@ struct device_operations default_pci_ops_dev = {
|
||||
};
|
||||
|
||||
/** Default device operations for PCI bridges */
|
||||
static struct pci_operations pci_bus_ops_pci = {
|
||||
.set_subsystem = 0,
|
||||
};
|
||||
|
||||
struct device_operations default_pci_ops_bus = {
|
||||
.read_resources = pci_bus_read_resources,
|
||||
.set_resources = pci_dev_set_resources,
|
||||
.enable_resources = pci_bus_enable_resources,
|
||||
.scan_bus = pci_scan_bridge,
|
||||
.reset_bus = pci_bus_reset,
|
||||
.ops_pci = &pci_bus_ops_pci,
|
||||
};
|
||||
|
||||
/** Default device operations for PCI devices marked 'hidden' */
|
||||
static struct device_operations default_hidden_pci_ops_dev = {
|
||||
.read_resources = noop_read_resources,
|
||||
.set_resources = noop_set_resources,
|
||||
.scan_bus = scan_static_bus,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1145,6 +1170,46 @@ unsigned int pci_match_simple_dev(struct device *dev, pci_devfn_t sdev)
|
||||
dev->path.pci.devfn == PCI_DEV2DEVFN(sdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* PCI devices that are marked as "hidden" do not get probed. However, the same
|
||||
* initialization logic is still performed as if it were. This is useful when
|
||||
* devices would like to be described in the devicetree.cb file, and/or present
|
||||
* static PCI resources to the allocator, but the platform firmware hides the
|
||||
* device (makes the device invisible to PCI enumeration) before PCI enumeration
|
||||
* takes place.
|
||||
*
|
||||
* The expected semantics of PCI devices marked as 'hidden':
|
||||
* 1) The device is actually present under the specified BDF
|
||||
* 2) The device config space can still be accessed somehow, but the Vendor ID
|
||||
* indicates there is no device there (it reads as 0xffffffff).
|
||||
* 3) The device may still consume PCI resources. Typically, these would have
|
||||
* been hardcoded elsewhere.
|
||||
*
|
||||
* @param dev Pointer to the device structure.
|
||||
*/
|
||||
static void pci_scan_hidden_device(struct device *dev)
|
||||
{
|
||||
if (dev->chip_ops && dev->chip_ops->enable_dev)
|
||||
dev->chip_ops->enable_dev(dev);
|
||||
|
||||
/*
|
||||
* If chip_ops->enable_dev did not set dev->ops, then set to a default
|
||||
* .ops, because PCI enumeration is effectively being skipped, therefore
|
||||
* no PCI driver will bind to this device. However, children may want to
|
||||
* be enumerated, so this provides scan_static_bus for the .scan_bus
|
||||
* callback.
|
||||
*/
|
||||
if (dev->ops == NULL)
|
||||
dev->ops = &default_hidden_pci_ops_dev;
|
||||
|
||||
if (dev->ops->enable)
|
||||
dev->ops->enable(dev);
|
||||
|
||||
/* Display the device almost as if it were probed normally */
|
||||
printk(BIOS_DEBUG, "%s [0000/%04x] hidden%s\n", dev_path(dev),
|
||||
dev->device, dev->ops ? "" : " No operations");
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a PCI bus.
|
||||
*
|
||||
@@ -1189,6 +1254,14 @@ void pci_scan_bus(struct bus *bus, unsigned int min_devfn,
|
||||
/* First thing setup the device structure. */
|
||||
dev = pci_scan_get_dev(bus, devfn);
|
||||
|
||||
/* Devices marked 'hidden' do not get probed */
|
||||
if (dev && dev->hidden) {
|
||||
pci_scan_hidden_device(dev);
|
||||
|
||||
/* Skip pci_probe_dev, go to next devfn */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* See if a device is present and setup the device structure. */
|
||||
dev = pci_probe_dev(dev, bus, devfn);
|
||||
|
||||
@@ -1212,8 +1285,12 @@ void pci_scan_bus(struct bus *bus, unsigned int min_devfn,
|
||||
|
||||
prev = &bus->children;
|
||||
for (dev = bus->children; dev; dev = dev->sibling) {
|
||||
/* If we read valid vendor id, it is not leftover device. */
|
||||
if (dev->vendor != 0) {
|
||||
/*
|
||||
* The device is only considered leftover if it is not hidden
|
||||
* and it has a Vendor ID of 0 (the default for a device that
|
||||
* could not be probed).
|
||||
*/
|
||||
if (dev->vendor != 0 || dev->hidden) {
|
||||
prev = &dev->sibling;
|
||||
continue;
|
||||
}
|
||||
|
@@ -512,7 +512,7 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev)
|
||||
{
|
||||
struct resource *resource;
|
||||
|
||||
// Add extra memory space
|
||||
/* Add extra memory space */
|
||||
resource = new_resource(dev, 0x10);
|
||||
resource->size = CONFIG_PCIEXP_HOTPLUG_MEM;
|
||||
resource->align = 12;
|
||||
@@ -520,7 +520,7 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev)
|
||||
resource->limit = 0xffffffff;
|
||||
resource->flags |= IORESOURCE_MEM;
|
||||
|
||||
// Add extra prefetchable memory space
|
||||
/* Add extra prefetchable memory space */
|
||||
resource = new_resource(dev, 0x14);
|
||||
resource->size = CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM;
|
||||
resource->align = 12;
|
||||
@@ -528,7 +528,11 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev)
|
||||
resource->limit = 0xffffffffffffffff;
|
||||
resource->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
|
||||
|
||||
// Add extra I/O space
|
||||
/* Set resource flag requesting allocation above 4G boundary. */
|
||||
if (CONFIG(PCIEXP_HOTPLUG_PREFETCH_MEM_ABOVE_4G))
|
||||
resource->flags |= IORESOURCE_ABOVE_4G;
|
||||
|
||||
/* Add extra I/O space */
|
||||
resource = new_resource(dev, 0x18);
|
||||
resource->size = CONFIG_PCIEXP_HOTPLUG_IO;
|
||||
resource->align = 12;
|
||||
|
61
src/device/resource_allocator_common.c
Normal file
61
src/device/resource_allocator_common.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <console/console.h>
|
||||
#include <device/device.h>
|
||||
|
||||
struct pick_largest_state {
|
||||
struct resource *last;
|
||||
const struct device *result_dev;
|
||||
struct resource *result;
|
||||
int seen_last;
|
||||
};
|
||||
|
||||
static void pick_largest_resource(void *gp, struct device *dev,
|
||||
struct resource *resource)
|
||||
{
|
||||
struct pick_largest_state *state = gp;
|
||||
struct resource *last;
|
||||
|
||||
last = state->last;
|
||||
|
||||
/* Be certain to pick the successor to last. */
|
||||
if (resource == last) {
|
||||
state->seen_last = 1;
|
||||
return;
|
||||
}
|
||||
if (resource->flags & IORESOURCE_FIXED)
|
||||
return; /* Skip it. */
|
||||
if (last && ((last->align < resource->align) ||
|
||||
((last->align == resource->align) &&
|
||||
(last->size < resource->size)) ||
|
||||
((last->align == resource->align) &&
|
||||
(last->size == resource->size) && (!state->seen_last)))) {
|
||||
return;
|
||||
}
|
||||
if (!state->result ||
|
||||
(state->result->align < resource->align) ||
|
||||
((state->result->align == resource->align) &&
|
||||
(state->result->size < resource->size))) {
|
||||
state->result_dev = dev;
|
||||
state->result = resource;
|
||||
}
|
||||
}
|
||||
|
||||
const struct device *largest_resource(struct bus *bus,
|
||||
struct resource **result_res,
|
||||
unsigned long type_mask,
|
||||
unsigned long type)
|
||||
{
|
||||
struct pick_largest_state state;
|
||||
|
||||
state.last = *result_res;
|
||||
state.result_dev = NULL;
|
||||
state.result = NULL;
|
||||
state.seen_last = 0;
|
||||
|
||||
search_bus_resources(bus, type_mask, type, pick_largest_resource,
|
||||
&state);
|
||||
|
||||
*result_res = state.result;
|
||||
return state.result_dev;
|
||||
}
|
542
src/device/resource_allocator_v3.c
Normal file
542
src/device/resource_allocator_v3.c
Normal file
@@ -0,0 +1,542 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <console/console.h>
|
||||
#include <device/device.h>
|
||||
#include <post.h>
|
||||
|
||||
/**
|
||||
* Round a number up to an alignment.
|
||||
*
|
||||
* @param val The starting value.
|
||||
* @param pow Alignment as a power of two.
|
||||
* @return Rounded up number.
|
||||
*/
|
||||
static resource_t round(resource_t val, unsigned long pow)
|
||||
{
|
||||
resource_t mask;
|
||||
mask = (1ULL << pow) - 1ULL;
|
||||
val += mask;
|
||||
val &= ~mask;
|
||||
return val;
|
||||
}
|
||||
|
||||
static const char *resource2str(struct resource *res)
|
||||
{
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
return "io";
|
||||
if (res->flags & IORESOURCE_PREFETCH)
|
||||
return "prefmem";
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
return "mem";
|
||||
return "undefined";
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is the guts of the resource allocator.
|
||||
*
|
||||
* The problem.
|
||||
* - Allocate resource locations for every device.
|
||||
* - Don't overlap, and follow the rules of bridges.
|
||||
* - Don't overlap with resources in fixed locations.
|
||||
* - Be efficient so we don't have ugly strategies.
|
||||
*
|
||||
* The strategy.
|
||||
* - Devices that have fixed addresses are the minority so don't
|
||||
* worry about them too much. Instead only use part of the address
|
||||
* space for devices with programmable addresses. This easily handles
|
||||
* everything except bridges.
|
||||
*
|
||||
* - PCI devices are required to have their sizes and their alignments
|
||||
* equal. In this case an optimal solution to the packing problem
|
||||
* exists. Allocate all devices from highest alignment to least
|
||||
* alignment or vice versa. Use this.
|
||||
*
|
||||
* - So we can handle more than PCI run two allocation passes on bridges. The
|
||||
* first to see how large the resources are behind the bridge, and what
|
||||
* their alignment requirements are. The second to assign a safe address to
|
||||
* the devices behind the bridge. This allows us to treat a bridge as just
|
||||
* a device with a couple of resources, and not need to special case it in
|
||||
* the allocator. Also this allows handling of other types of bridges.
|
||||
*
|
||||
* @param bus The bus we are traversing.
|
||||
* @param bridge The bridge resource which must contain the bus' resources.
|
||||
* @param type_mask This value gets ANDed with the resource type.
|
||||
* @param type This value must match the result of the AND.
|
||||
* @return TODO
|
||||
*/
|
||||
static void compute_resources(struct bus *bus, struct resource *bridge,
|
||||
unsigned long type_mask, unsigned long type)
|
||||
{
|
||||
const struct device *dev;
|
||||
struct resource *resource;
|
||||
resource_t base;
|
||||
base = round(bridge->base, bridge->align);
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
printk(BIOS_SPEW, "%s %s: base: %llx size: %llx align: %d gran: %d"
|
||||
" limit: %llx\n", dev_path(bus->dev), resource2str(bridge),
|
||||
base, bridge->size, bridge->align,
|
||||
bridge->gran, bridge->limit);
|
||||
|
||||
/* For each child which is a bridge, compute the resource needs. */
|
||||
for (dev = bus->children; dev; dev = dev->sibling) {
|
||||
struct resource *child_bridge;
|
||||
|
||||
if (!dev->link_list)
|
||||
continue;
|
||||
|
||||
/* Find the resources with matching type flags. */
|
||||
for (child_bridge = dev->resource_list; child_bridge;
|
||||
child_bridge = child_bridge->next) {
|
||||
struct bus* link;
|
||||
|
||||
if (!(child_bridge->flags & IORESOURCE_BRIDGE)
|
||||
|| (child_bridge->flags & type_mask) != type)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Split prefetchable memory if combined. Many domains
|
||||
* use the same address space for prefetchable memory
|
||||
* and non-prefetchable memory. Bridges below them need
|
||||
* it separated. Add the PREFETCH flag to the type_mask
|
||||
* and type.
|
||||
*/
|
||||
link = dev->link_list;
|
||||
while (link && link->link_num !=
|
||||
IOINDEX_LINK(child_bridge->index))
|
||||
link = link->next;
|
||||
|
||||
if (link == NULL) {
|
||||
printk(BIOS_ERR, "link %ld not found on %s\n",
|
||||
IOINDEX_LINK(child_bridge->index),
|
||||
dev_path(dev));
|
||||
}
|
||||
|
||||
compute_resources(link, child_bridge,
|
||||
type_mask | IORESOURCE_PREFETCH,
|
||||
type | (child_bridge->flags &
|
||||
IORESOURCE_PREFETCH));
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember we haven't found anything yet. */
|
||||
resource = NULL;
|
||||
|
||||
/*
|
||||
* Walk through all the resources on the current bus and compute the
|
||||
* amount of address space taken by them. Take granularity and
|
||||
* alignment into account.
|
||||
*/
|
||||
while ((dev = largest_resource(bus, &resource, type_mask, type))) {
|
||||
|
||||
/* Size 0 resources can be skipped. */
|
||||
if (!resource->size)
|
||||
continue;
|
||||
|
||||
/* Propagate the resource alignment to the bridge resource. */
|
||||
if (resource->align > bridge->align)
|
||||
bridge->align = resource->align;
|
||||
|
||||
/* Propagate the resource limit to the bridge register. */
|
||||
if (bridge->limit > resource->limit)
|
||||
bridge->limit = resource->limit;
|
||||
|
||||
/* Warn if it looks like APICs aren't declared. */
|
||||
if ((resource->limit == 0xffffffff) &&
|
||||
(resource->flags & IORESOURCE_ASSIGNED)) {
|
||||
printk(BIOS_ERR,
|
||||
"Resource limit looks wrong! (no APIC?)\n");
|
||||
printk(BIOS_ERR, "%s %02lx limit %08llx\n",
|
||||
dev_path(dev), resource->index, resource->limit);
|
||||
}
|
||||
|
||||
if (resource->flags & IORESOURCE_IO) {
|
||||
/*
|
||||
* Don't allow potential aliases over the legacy PCI
|
||||
* expansion card addresses. The legacy PCI decodes
|
||||
* only 10 bits, uses 0x100 - 0x3ff. Therefore, only
|
||||
* 0x00 - 0xff can be used out of each 0x400 block of
|
||||
* I/O space.
|
||||
*/
|
||||
if ((base & 0x300) != 0) {
|
||||
base = (base & ~0x3ff) + 0x400;
|
||||
}
|
||||
/*
|
||||
* Don't allow allocations in the VGA I/O range.
|
||||
* PCI has special cases for that.
|
||||
*/
|
||||
else if ((base >= 0x3b0) && (base <= 0x3df)) {
|
||||
base = 0x3e0;
|
||||
}
|
||||
}
|
||||
/* Base must be aligned. */
|
||||
base = round(base, resource->align);
|
||||
resource->base = base;
|
||||
base += resource->size;
|
||||
|
||||
printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n",
|
||||
dev_path(dev), resource->index, resource->base,
|
||||
resource->base + resource->size - 1,
|
||||
resource2str(resource));
|
||||
}
|
||||
|
||||
/*
|
||||
* A PCI bridge resource does not need to be a power of two size, but
|
||||
* it does have a minimum granularity. Round the size up to that
|
||||
* minimum granularity so we know not to place something else at an
|
||||
* address positively decoded by the bridge.
|
||||
*/
|
||||
bridge->size = round(base, bridge->gran) -
|
||||
round(bridge->base, bridge->align);
|
||||
|
||||
printk(BIOS_SPEW, "%s %s: base: %llx size: %llx align: %d gran: %d"
|
||||
" limit: %llx done\n", dev_path(bus->dev),
|
||||
resource2str(bridge),
|
||||
base, bridge->size, bridge->align, bridge->gran, bridge->limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is the second part of the resource allocator.
|
||||
*
|
||||
* See the compute_resources function for a more detailed explanation.
|
||||
*
|
||||
* This function assigns the resources a value.
|
||||
*
|
||||
* @param bus The bus we are traversing.
|
||||
* @param bridge The bridge resource which must contain the bus' resources.
|
||||
* @param type_mask This value gets ANDed with the resource type.
|
||||
* @param type This value must match the result of the AND.
|
||||
*
|
||||
* @see compute_resources
|
||||
*/
|
||||
static void __allocate_resources(struct bus *bus, struct resource *bridge,
|
||||
unsigned long type_mask, unsigned long type)
|
||||
{
|
||||
const struct device *dev;
|
||||
struct resource *resource;
|
||||
resource_t base;
|
||||
base = bridge->base;
|
||||
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
printk(BIOS_SPEW, "%s %s: base:%llx size:%llx align:%d gran:%d "
|
||||
"limit:%llx\n", dev_path(bus->dev),
|
||||
resource2str(bridge),
|
||||
base, bridge->size, bridge->align, bridge->gran, bridge->limit);
|
||||
|
||||
/* Remember we haven't found anything yet. */
|
||||
resource = NULL;
|
||||
|
||||
/*
|
||||
* Walk through all the resources on the current bus and allocate them
|
||||
* address space.
|
||||
*/
|
||||
while ((dev = largest_resource(bus, &resource, type_mask, type))) {
|
||||
|
||||
/* Propagate the bridge limit to the resource register. */
|
||||
if (resource->limit > bridge->limit)
|
||||
resource->limit = bridge->limit;
|
||||
|
||||
/* Size 0 resources can be skipped. */
|
||||
if (!resource->size)
|
||||
continue;
|
||||
|
||||
if (resource->flags & IORESOURCE_IO) {
|
||||
/*
|
||||
* Don't allow potential aliases over the legacy PCI
|
||||
* expansion card addresses. The legacy PCI decodes
|
||||
* only 10 bits, uses 0x100 - 0x3ff. Therefore, only
|
||||
* 0x00 - 0xff can be used out of each 0x400 block of
|
||||
* I/O space.
|
||||
*/
|
||||
if ((base & 0x300) != 0) {
|
||||
base = (base & ~0x3ff) + 0x400;
|
||||
}
|
||||
/*
|
||||
* Don't allow allocations in the VGA I/O range.
|
||||
* PCI has special cases for that.
|
||||
*/
|
||||
else if ((base >= 0x3b0) && (base <= 0x3df)) {
|
||||
base = 0x3e0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((round(base, resource->align) + resource->size - 1) <=
|
||||
resource->limit) {
|
||||
/* Base must be aligned. */
|
||||
base = round(base, resource->align);
|
||||
resource->base = base;
|
||||
resource->limit = resource->base + resource->size - 1;
|
||||
resource->flags |= IORESOURCE_ASSIGNED;
|
||||
resource->flags &= ~IORESOURCE_STORED;
|
||||
base += resource->size;
|
||||
} else {
|
||||
printk(BIOS_ERR, "!! Resource didn't fit !!\n");
|
||||
printk(BIOS_ERR, " aligned base %llx size %llx "
|
||||
"limit %llx\n", round(base, resource->align),
|
||||
resource->size, resource->limit);
|
||||
printk(BIOS_ERR, " %llx needs to be <= %llx "
|
||||
"(limit)\n", (round(base, resource->align) +
|
||||
resource->size) - 1, resource->limit);
|
||||
printk(BIOS_ERR, " %s%s %02lx * [0x%llx - 0x%llx]"
|
||||
" %s\n", (resource->flags & IORESOURCE_ASSIGNED)
|
||||
? "Assigned: " : "", dev_path(dev),
|
||||
resource->index, resource->base,
|
||||
resource->base + resource->size - 1,
|
||||
resource2str(resource));
|
||||
}
|
||||
|
||||
printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n",
|
||||
dev_path(dev), resource->index, resource->base,
|
||||
resource->size ? resource->base + resource->size - 1 :
|
||||
resource->base, resource2str(resource));
|
||||
}
|
||||
|
||||
/*
|
||||
* A PCI bridge resource does not need to be a power of two size, but
|
||||
* it does have a minimum granularity. Round the size up to that
|
||||
* minimum granularity so we know not to place something else at an
|
||||
* address positively decoded by the bridge.
|
||||
*/
|
||||
|
||||
bridge->flags |= IORESOURCE_ASSIGNED;
|
||||
|
||||
printk(BIOS_SPEW, "%s %s: next_base: %llx size: %llx align: %d "
|
||||
"gran: %d done\n", dev_path(bus->dev),
|
||||
resource2str(bridge), base, bridge->size, bridge->align,
|
||||
bridge->gran);
|
||||
|
||||
/* For each child which is a bridge, __allocate_resources. */
|
||||
for (dev = bus->children; dev; dev = dev->sibling) {
|
||||
struct resource *child_bridge;
|
||||
|
||||
if (!dev->link_list)
|
||||
continue;
|
||||
|
||||
/* Find the resources with matching type flags. */
|
||||
for (child_bridge = dev->resource_list; child_bridge;
|
||||
child_bridge = child_bridge->next) {
|
||||
struct bus* link;
|
||||
|
||||
if (!(child_bridge->flags & IORESOURCE_BRIDGE) ||
|
||||
(child_bridge->flags & type_mask) != type)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Split prefetchable memory if combined. Many domains
|
||||
* use the same address space for prefetchable memory
|
||||
* and non-prefetchable memory. Bridges below them need
|
||||
* it separated. Add the PREFETCH flag to the type_mask
|
||||
* and type.
|
||||
*/
|
||||
link = dev->link_list;
|
||||
while (link && link->link_num !=
|
||||
IOINDEX_LINK(child_bridge->index))
|
||||
link = link->next;
|
||||
if (link == NULL)
|
||||
printk(BIOS_ERR, "link %ld not found on %s\n",
|
||||
IOINDEX_LINK(child_bridge->index),
|
||||
dev_path(dev));
|
||||
|
||||
__allocate_resources(link, child_bridge,
|
||||
type_mask | IORESOURCE_PREFETCH,
|
||||
type | (child_bridge->flags &
|
||||
IORESOURCE_PREFETCH));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int resource_is(struct resource *res, u32 type)
|
||||
{
|
||||
return (res->flags & IORESOURCE_TYPE_MASK) == type;
|
||||
}
|
||||
|
||||
struct constraints {
|
||||
struct resource io, mem;
|
||||
};
|
||||
|
||||
static struct resource *resource_limit(struct constraints *limits,
|
||||
struct resource *res)
|
||||
{
|
||||
struct resource *lim = NULL;
|
||||
|
||||
/* MEM, or I/O - skip any others. */
|
||||
if (resource_is(res, IORESOURCE_MEM))
|
||||
lim = &limits->mem;
|
||||
else if (resource_is(res, IORESOURCE_IO))
|
||||
lim = &limits->io;
|
||||
|
||||
return lim;
|
||||
}
|
||||
|
||||
static void constrain_resources(const struct device *dev,
|
||||
struct constraints* limits)
|
||||
{
|
||||
const struct device *child;
|
||||
struct resource *res;
|
||||
struct resource *lim;
|
||||
struct bus *link;
|
||||
|
||||
/* Constrain limits based on the fixed resources of this device. */
|
||||
for (res = dev->resource_list; res; res = res->next) {
|
||||
if (!(res->flags & IORESOURCE_FIXED))
|
||||
continue;
|
||||
if (!res->size) {
|
||||
/* It makes no sense to have 0-sized, fixed resources.*/
|
||||
printk(BIOS_ERR, "skipping %s@%lx fixed resource, "
|
||||
"size=0!\n", dev_path(dev), res->index);
|
||||
continue;
|
||||
}
|
||||
|
||||
lim = resource_limit(limits, res);
|
||||
if (!lim)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Is it a fixed resource outside the current known region?
|
||||
* If so, we don't have to consider it - it will be handled
|
||||
* correctly and doesn't affect current region's limits.
|
||||
*/
|
||||
if (((res->base + res->size -1) < lim->base)
|
||||
|| (res->base > lim->limit))
|
||||
continue;
|
||||
|
||||
printk(BIOS_SPEW, "%s: %s %02lx base %08llx limit %08llx %s (fixed)\n",
|
||||
__func__, dev_path(dev), res->index, res->base,
|
||||
res->base + res->size - 1, resource2str(res));
|
||||
|
||||
/*
|
||||
* Choose to be above or below fixed resources. This check is
|
||||
* signed so that "negative" amounts of space are handled
|
||||
* correctly.
|
||||
*/
|
||||
if ((signed long long)(lim->limit - (res->base + res->size -1))
|
||||
> (signed long long)(res->base - lim->base))
|
||||
lim->base = res->base + res->size;
|
||||
else
|
||||
lim->limit = res->base -1;
|
||||
}
|
||||
|
||||
/* Descend into every enabled child and look for fixed resources. */
|
||||
for (link = dev->link_list; link; link = link->next) {
|
||||
for (child = link->children; child; child = child->sibling) {
|
||||
if (child->enabled)
|
||||
constrain_resources(child, limits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void avoid_fixed_resources(const struct device *dev)
|
||||
{
|
||||
struct constraints limits;
|
||||
struct resource *res;
|
||||
struct resource *lim;
|
||||
|
||||
printk(BIOS_SPEW, "%s: %s\n", __func__, dev_path(dev));
|
||||
|
||||
/* Initialize constraints to maximum size. */
|
||||
limits.io.base = 0;
|
||||
limits.io.limit = 0xffffffffffffffffULL;
|
||||
limits.mem.base = 0;
|
||||
limits.mem.limit = 0xffffffffffffffffULL;
|
||||
|
||||
/* Constrain the limits to dev's initial resources. */
|
||||
for (res = dev->resource_list; res; res = res->next) {
|
||||
if ((res->flags & IORESOURCE_FIXED))
|
||||
continue;
|
||||
printk(BIOS_SPEW, "%s:@%s %02lx limit %08llx\n", __func__,
|
||||
dev_path(dev), res->index, res->limit);
|
||||
|
||||
lim = resource_limit(&limits, res);
|
||||
if (!lim)
|
||||
continue;
|
||||
|
||||
if (res->base > lim->base)
|
||||
lim->base = res->base;
|
||||
if (res->limit < lim->limit)
|
||||
lim->limit = res->limit;
|
||||
}
|
||||
|
||||
/* Look through the tree for fixed resources and update the limits. */
|
||||
constrain_resources(dev, &limits);
|
||||
|
||||
/* Update dev's resources with new limits. */
|
||||
for (res = dev->resource_list; res; res = res->next) {
|
||||
if ((res->flags & IORESOURCE_FIXED))
|
||||
continue;
|
||||
|
||||
lim = resource_limit(&limits, res);
|
||||
if (!lim)
|
||||
continue;
|
||||
|
||||
/* Is the resource outside the limits? */
|
||||
if (lim->base > res->base)
|
||||
res->base = lim->base;
|
||||
if (res->limit > lim->limit)
|
||||
res->limit = lim->limit;
|
||||
|
||||
/* MEM resources need to start at the highest address manageable. */
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
res->base = resource_max(res);
|
||||
|
||||
printk(BIOS_SPEW, "%s:@%s %02lx base %08llx limit %08llx\n",
|
||||
__func__, dev_path(dev), res->index, res->base, res->limit);
|
||||
}
|
||||
}
|
||||
|
||||
void allocate_resources(const struct device *root)
|
||||
{
|
||||
struct resource *res;
|
||||
const struct device *child;
|
||||
|
||||
/* Compute resources for all domains. */
|
||||
for (child = root->link_list->children; child; child = child->sibling) {
|
||||
if (!(child->path.type == DEVICE_PATH_DOMAIN))
|
||||
continue;
|
||||
post_log_path(child);
|
||||
for (res = child->resource_list; res; res = res->next) {
|
||||
if (res->flags & IORESOURCE_FIXED)
|
||||
continue;
|
||||
if (res->flags & IORESOURCE_MEM) {
|
||||
compute_resources(child->link_list,
|
||||
res, IORESOURCE_TYPE_MASK, IORESOURCE_MEM);
|
||||
continue;
|
||||
}
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
compute_resources(child->link_list,
|
||||
res, IORESOURCE_TYPE_MASK, IORESOURCE_IO);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* For all domains. */
|
||||
for (child = root->link_list->children; child; child=child->sibling)
|
||||
if (child->path.type == DEVICE_PATH_DOMAIN)
|
||||
avoid_fixed_resources(child);
|
||||
|
||||
/* Store the computed resource allocations into device registers ... */
|
||||
printk(BIOS_INFO, "Setting resources...\n");
|
||||
for (child = root->link_list->children; child; child = child->sibling) {
|
||||
if (!(child->path.type == DEVICE_PATH_DOMAIN))
|
||||
continue;
|
||||
post_log_path(child);
|
||||
for (res = child->resource_list; res; res = res->next) {
|
||||
if (res->flags & IORESOURCE_FIXED)
|
||||
continue;
|
||||
if (res->flags & IORESOURCE_MEM) {
|
||||
__allocate_resources(child->link_list,
|
||||
res, IORESOURCE_TYPE_MASK, IORESOURCE_MEM);
|
||||
continue;
|
||||
}
|
||||
if (res->flags & IORESOURCE_IO) {
|
||||
__allocate_resources(child->link_list,
|
||||
res, IORESOURCE_TYPE_MASK, IORESOURCE_IO);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
685
src/device/resource_allocator_v4.c
Normal file
685
src/device/resource_allocator_v4.c
Normal file
@@ -0,0 +1,685 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <console/console.h>
|
||||
#include <device/device.h>
|
||||
#include <memrange.h>
|
||||
#include <post.h>
|
||||
|
||||
/**
|
||||
* Round a number up to an alignment.
|
||||
*
|
||||
* @param val The starting value.
|
||||
* @param pow Alignment as a power of two.
|
||||
* @return Rounded up number.
|
||||
*/
|
||||
static resource_t round(resource_t val, unsigned long pow)
|
||||
{
|
||||
return ALIGN_UP(val, POWER_OF_2(pow));
|
||||
}
|
||||
|
||||
static const char *resource2str(const struct resource *res)
|
||||
{
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
return "io";
|
||||
if (res->flags & IORESOURCE_PREFETCH)
|
||||
return "prefmem";
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
return "mem";
|
||||
return "undefined";
|
||||
}
|
||||
|
||||
static bool dev_has_children(const struct device *dev)
|
||||
{
|
||||
const struct bus *bus = dev->link_list;
|
||||
return bus && bus->children;
|
||||
}
|
||||
|
||||
#define res_printk(depth, str, ...) printk(BIOS_DEBUG, "%*c"str, depth, ' ', __VA_ARGS__)
|
||||
|
||||
/*
|
||||
* During pass 1, once all the requirements for downstream devices of a bridge are gathered,
|
||||
* this function calculates the overall resource requirement for the bridge. It starts by
|
||||
* picking the largest resource requirement downstream for the given resource type and works by
|
||||
* adding requirements in descending order.
|
||||
*
|
||||
* Additionally, it takes alignment and limits of the downstream devices into consideration and
|
||||
* ensures that they get propagated to the bridge resource. This is required to guarantee that
|
||||
* the upstream bridge/domain honors the limit and alignment requirements for this bridge based
|
||||
* on the tightest constraints downstream.
|
||||
*/
|
||||
static void update_bridge_resource(const struct device *bridge, struct resource *bridge_res,
|
||||
unsigned long type_match, int print_depth)
|
||||
{
|
||||
const struct device *child;
|
||||
struct resource *child_res;
|
||||
resource_t base;
|
||||
bool first_child_res = true;
|
||||
const unsigned long type_mask = IORESOURCE_TYPE_MASK | IORESOURCE_PREFETCH;
|
||||
struct bus *bus = bridge->link_list;
|
||||
|
||||
child_res = NULL;
|
||||
|
||||
/*
|
||||
* `base` keeps track of where the next allocation for child resource can take place
|
||||
* from within the bridge resource window. Since the bridge resource window allocation
|
||||
* is not performed yet, it can start at 0. Base gets updated every time a resource
|
||||
* requirement is accounted for in the loop below. After scanning all these resources,
|
||||
* base will indicate the total size requirement for the current bridge resource
|
||||
* window.
|
||||
*/
|
||||
base = 0;
|
||||
|
||||
res_printk(print_depth, "%s %s: size: %llx align: %d gran: %d limit: %llx\n",
|
||||
dev_path(bridge), resource2str(bridge_res), bridge_res->size,
|
||||
bridge_res->align, bridge_res->gran, bridge_res->limit);
|
||||
|
||||
while ((child = largest_resource(bus, &child_res, type_mask, type_match))) {
|
||||
|
||||
/* Size 0 resources can be skipped. */
|
||||
if (!child_res->size)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Propagate the resource alignment to the bridge resource if this is the first
|
||||
* child resource with non-zero size being considered. For all other children
|
||||
* resources, alignment is taken care of by updating the base to round up as per
|
||||
* the child resource alignment. It is guaranteed that pass 2 follows the exact
|
||||
* same method of picking the resource for allocation using
|
||||
* largest_resource(). Thus, as long as the alignment for first child resource
|
||||
* is propagated up to the bridge resource, it can be guaranteed that the
|
||||
* alignment for all resources is appropriately met.
|
||||
*/
|
||||
if (first_child_res && (child_res->align > bridge_res->align))
|
||||
bridge_res->align = child_res->align;
|
||||
|
||||
first_child_res = false;
|
||||
|
||||
/*
|
||||
* Propagate the resource limit to the bridge resource only if child resource
|
||||
* limit is non-zero. If a downstream device has stricter requirements
|
||||
* w.r.t. limits for any resource, that constraint needs to be propagated back
|
||||
* up to the downstream bridges of the domain. This guarantees that the resource
|
||||
* allocation which starts at the domain level takes into account all these
|
||||
* constraints thus working on a global view.
|
||||
*/
|
||||
if (child_res->limit && (child_res->limit < bridge_res->limit))
|
||||
bridge_res->limit = child_res->limit;
|
||||
|
||||
/*
|
||||
* Propagate the downstream resource request to allocate above 4G boundary to
|
||||
* upstream bridge resource. This ensures that during pass 2, the resource
|
||||
* allocator at domain level has a global view of all the downstream device
|
||||
* requirements and thus address space is allocated as per updated flags in the
|
||||
* bridge resource.
|
||||
*
|
||||
* Since the bridge resource is a single window, all the downstream resources of
|
||||
* this bridge resource will be allocated space above 4G boundary.
|
||||
*/
|
||||
if (child_res->flags & IORESOURCE_ABOVE_4G)
|
||||
bridge_res->flags |= IORESOURCE_ABOVE_4G;
|
||||
|
||||
/*
|
||||
* Alignment value of 0 means that the child resource has no alignment
|
||||
* requirements and so the base value remains unchanged here.
|
||||
*/
|
||||
base = round(base, child_res->align);
|
||||
|
||||
res_printk(print_depth + 1, "%s %02lx * [0x%llx - 0x%llx] %s\n",
|
||||
dev_path(child), child_res->index, base, base + child_res->size - 1,
|
||||
resource2str(child_res));
|
||||
|
||||
base += child_res->size;
|
||||
}
|
||||
|
||||
/*
|
||||
* After all downstream device resources are scanned, `base` represents the total size
|
||||
* requirement for the current bridge resource window. This size needs to be rounded up
|
||||
* to the granularity requirement of the bridge to ensure that the upstream
|
||||
* bridge/domain allocates big enough window.
|
||||
*/
|
||||
bridge_res->size = round(base, bridge_res->gran);
|
||||
|
||||
res_printk(print_depth, "%s %s: size: %llx align: %d gran: %d limit: %llx done\n",
|
||||
dev_path(bridge), resource2str(bridge_res), bridge_res->size,
|
||||
bridge_res->align, bridge_res->gran, bridge_res->limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* During pass 1, resource allocator at bridge level gathers requirements from downstream
|
||||
* devices and updates its own resource windows for the provided resource type.
|
||||
*/
|
||||
static void compute_bridge_resources(const struct device *bridge, unsigned long type_match,
|
||||
int print_depth)
|
||||
{
|
||||
const struct device *child;
|
||||
struct resource *res;
|
||||
struct bus *bus = bridge->link_list;
|
||||
const unsigned long type_mask = IORESOURCE_TYPE_MASK | IORESOURCE_PREFETCH;
|
||||
|
||||
for (res = bridge->resource_list; res; res = res->next) {
|
||||
if (!(res->flags & IORESOURCE_BRIDGE))
|
||||
continue;
|
||||
|
||||
if ((res->flags & type_mask) != type_match)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Ensure that the resource requirements for all downstream bridges are
|
||||
* gathered before updating the window for current bridge resource.
|
||||
*/
|
||||
for (child = bus->children; child; child = child->sibling) {
|
||||
if (!dev_has_children(child))
|
||||
continue;
|
||||
compute_bridge_resources(child, type_match, print_depth + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the window for current bridge resource now that all downstream
|
||||
* requirements are gathered.
|
||||
*/
|
||||
update_bridge_resource(bridge, res, type_match, print_depth);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* During pass 1, resource allocator walks down the entire sub-tree of a domain. It gathers
|
||||
* resource requirements for every downstream bridge by looking at the resource requests of its
|
||||
* children. Thus, the requirement gathering begins at the leaf devices and is propagated back
|
||||
* up to the downstream bridges of the domain.
|
||||
*
|
||||
* At domain level, it identifies every downstream bridge and walks down that bridge to gather
|
||||
* requirements for each resource type i.e. i/o, mem and prefmem. Since bridges have separate
|
||||
* windows for mem and prefmem, requirements for each need to be collected separately.
|
||||
*
|
||||
* Domain resource windows are fixed ranges and hence requirement gathering does not result in
|
||||
* any changes to these fixed ranges.
|
||||
*/
|
||||
static void compute_domain_resources(const struct device *domain)
|
||||
{
|
||||
const struct device *child;
|
||||
const int print_depth = 1;
|
||||
|
||||
if (domain->link_list == NULL)
|
||||
return;
|
||||
|
||||
for (child = domain->link_list->children; child; child = child->sibling) {
|
||||
|
||||
/* Skip if this is not a bridge or has no children under it. */
|
||||
if (!dev_has_children(child))
|
||||
continue;
|
||||
|
||||
compute_bridge_resources(child, IORESOURCE_IO, print_depth);
|
||||
compute_bridge_resources(child, IORESOURCE_MEM, print_depth);
|
||||
compute_bridge_resources(child, IORESOURCE_MEM | IORESOURCE_PREFETCH,
|
||||
print_depth);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char get_alignment_by_resource_type(const struct resource *res)
|
||||
{
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
return 12; /* Page-aligned --> log2(4KiB) */
|
||||
else if (res->flags & IORESOURCE_IO)
|
||||
return 0; /* No special alignment required --> log2(1) */
|
||||
|
||||
die("Unexpected resource type: flags(%d)!\n", res->flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the resource is NULL or if the resource is not assigned, then it cannot be used for
|
||||
* allocation for downstream devices.
|
||||
*/
|
||||
static bool is_resource_invalid(const struct resource *res)
|
||||
{
|
||||
return (res == NULL) || !(res->flags & IORESOURCE_ASSIGNED);
|
||||
}
|
||||
|
||||
static void initialize_domain_io_resource_memranges(struct memranges *ranges,
|
||||
const struct resource *res,
|
||||
unsigned long memrange_type)
|
||||
{
|
||||
memranges_insert(ranges, res->base, res->limit - res->base + 1, memrange_type);
|
||||
}
|
||||
|
||||
static void initialize_domain_mem_resource_memranges(struct memranges *ranges,
|
||||
const struct resource *res,
|
||||
unsigned long memrange_type)
|
||||
{
|
||||
resource_t res_base;
|
||||
resource_t res_limit;
|
||||
|
||||
const resource_t limit_4g = 0xffffffff;
|
||||
|
||||
res_base = res->base;
|
||||
res_limit = res->limit;
|
||||
|
||||
/*
|
||||
* Split the resource into two separate ranges if it crosses the 4G boundary. Memrange
|
||||
* type is set differently to ensure that memrange does not merge these two ranges. For
|
||||
* the range above 4G boundary, given memrange type is ORed with IORESOURCE_ABOVE_4G.
|
||||
*/
|
||||
if (res_base <= limit_4g) {
|
||||
|
||||
resource_t range_limit;
|
||||
|
||||
/* Clip the resource limit at 4G boundary if necessary. */
|
||||
range_limit = MIN(res_limit, limit_4g);
|
||||
memranges_insert(ranges, res_base, range_limit - res_base + 1, memrange_type);
|
||||
|
||||
/*
|
||||
* If the resource lies completely below the 4G boundary, nothing more needs to
|
||||
* be done.
|
||||
*/
|
||||
if (res_limit <= limit_4g)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the resource window crosses the 4G boundary, then update res_base to add
|
||||
* another entry for the range above the boundary.
|
||||
*/
|
||||
res_base = limit_4g + 1;
|
||||
}
|
||||
|
||||
if (res_base > res_limit)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If resource lies completely above the 4G boundary or if the resource was clipped to
|
||||
* add two separate ranges, the range above 4G boundary has the resource flag
|
||||
* IORESOURCE_ABOVE_4G set. This allows domain to handle any downstream requests for
|
||||
* resource allocation above 4G differently.
|
||||
*/
|
||||
memranges_insert(ranges, res_base, res_limit - res_base + 1,
|
||||
memrange_type | IORESOURCE_ABOVE_4G);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function initializes memranges for domain device. If the resource crosses 4G boundary,
|
||||
* then this function splits it into two ranges -- one for the window below 4G and the other for
|
||||
* the window above 4G. The latter range has IORESOURCE_ABOVE_4G flag set to satisfy resource
|
||||
* requests from downstream devices for allocations above 4G.
|
||||
*/
|
||||
static void initialize_domain_memranges(struct memranges *ranges, const struct resource *res,
|
||||
unsigned long memrange_type)
|
||||
{
|
||||
unsigned char align = get_alignment_by_resource_type(res);
|
||||
|
||||
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
|
||||
|
||||
if (is_resource_invalid(res))
|
||||
return;
|
||||
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
initialize_domain_io_resource_memranges(ranges, res, memrange_type);
|
||||
else
|
||||
initialize_domain_mem_resource_memranges(ranges, res, memrange_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function initializes memranges for bridge device. Unlike domain, bridge does not need to
|
||||
* care about resource window crossing 4G boundary. This is handled by the resource allocator at
|
||||
* domain level to ensure that all downstream bridges are allocated space either above or below
|
||||
* 4G boundary as per the state of IORESOURCE_ABOVE_4G for the respective bridge resource.
|
||||
*
|
||||
* So, this function creates a single range of the entire resource window available for the
|
||||
* bridge resource. Thus all downstream resources of the bridge for the given resource type get
|
||||
* allocated space from the same window. If there is any downstream resource of the bridge which
|
||||
* requests allocation above 4G, then all other downstream resources of the same type under the
|
||||
* bridge get allocated above 4G.
|
||||
*/
|
||||
static void initialize_bridge_memranges(struct memranges *ranges, const struct resource *res,
|
||||
unsigned long memrange_type)
|
||||
{
|
||||
unsigned char align = get_alignment_by_resource_type(res);
|
||||
|
||||
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
|
||||
|
||||
if (is_resource_invalid(res))
|
||||
return;
|
||||
|
||||
memranges_insert(ranges, res->base, res->limit - res->base + 1, memrange_type);
|
||||
}
|
||||
|
||||
static void print_resource_ranges(const struct device *dev, const struct memranges *ranges)
|
||||
{
|
||||
const struct range_entry *r;
|
||||
|
||||
printk(BIOS_INFO, " %s: Resource ranges:\n", dev_path(dev));
|
||||
|
||||
if (memranges_is_empty(ranges))
|
||||
printk(BIOS_INFO, " * EMPTY!!\n");
|
||||
|
||||
memranges_each_entry(r, ranges) {
|
||||
printk(BIOS_INFO, " * Base: %llx, Size: %llx, Tag: %lx\n",
|
||||
range_entry_base(r), range_entry_size(r), range_entry_tag(r));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where the actual allocation of resources happens during pass 2. Given the list of
|
||||
* memory ranges corresponding to the resource of given type, it finds the biggest unallocated
|
||||
* resource using the type mask on the downstream bus. This continues in a descending
|
||||
* order until all resources of given type are allocated address space within the current
|
||||
* resource window.
|
||||
*/
|
||||
static void allocate_child_resources(struct bus *bus, struct memranges *ranges,
|
||||
unsigned long type_mask, unsigned long type_match)
|
||||
{
|
||||
struct resource *resource = NULL;
|
||||
const struct device *dev;
|
||||
|
||||
while ((dev = largest_resource(bus, &resource, type_mask, type_match))) {
|
||||
|
||||
if (!resource->size)
|
||||
continue;
|
||||
|
||||
if (memranges_steal(ranges, resource->limit, resource->size, resource->align,
|
||||
type_match, &resource->base) == false) {
|
||||
printk(BIOS_ERR, " ERROR: Resource didn't fit!!! ");
|
||||
printk(BIOS_DEBUG, " %s %02lx * size: 0x%llx limit: %llx %s\n",
|
||||
dev_path(dev), resource->index,
|
||||
resource->size, resource->limit, resource2str(resource));
|
||||
continue;
|
||||
}
|
||||
|
||||
resource->limit = resource->base + resource->size - 1;
|
||||
resource->flags |= IORESOURCE_ASSIGNED;
|
||||
|
||||
printk(BIOS_DEBUG, " %s %02lx * [0x%llx - 0x%llx] limit: %llx %s\n",
|
||||
dev_path(dev), resource->index, resource->base,
|
||||
resource->size ? resource->base + resource->size - 1 :
|
||||
resource->base, resource->limit, resource2str(resource));
|
||||
}
|
||||
}
|
||||
|
||||
static void update_constraints(struct memranges *ranges, const struct device *dev,
|
||||
const struct resource *res)
|
||||
{
|
||||
if (!res->size)
|
||||
return;
|
||||
|
||||
printk(BIOS_DEBUG, " %s: %s %02lx base %08llx limit %08llx %s (fixed)\n",
|
||||
__func__, dev_path(dev), res->index, res->base,
|
||||
res->base + res->size - 1, resource2str(res));
|
||||
|
||||
memranges_create_hole(ranges, res->base, res->size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the entire tree to identify any fixed resources allocated by any device to
|
||||
* ensure that the address map for domain resources are appropriately updated.
|
||||
*
|
||||
* Domains can typically provide memrange for entire address space. So, this function
|
||||
* punches holes in the address space for all fixed resources that are already
|
||||
* defined. Both IO and normal memory resources are added as fixed. Both need to be
|
||||
* removed from address space where dynamic resource allocations are sourced.
|
||||
*/
|
||||
static void avoid_fixed_resources(struct memranges *ranges, const struct device *dev,
|
||||
unsigned long mask_match)
|
||||
{
|
||||
const struct resource *res;
|
||||
const struct device *child;
|
||||
const struct bus *bus;
|
||||
|
||||
for (res = dev->resource_list; res != NULL; res = res->next) {
|
||||
if ((res->flags & mask_match) != mask_match)
|
||||
continue;
|
||||
update_constraints(ranges, dev, res);
|
||||
}
|
||||
|
||||
bus = dev->link_list;
|
||||
if (bus == NULL)
|
||||
return;
|
||||
|
||||
for (child = bus->children; child != NULL; child = child->sibling)
|
||||
avoid_fixed_resources(ranges, child, mask_match);
|
||||
}
|
||||
|
||||
static void constrain_domain_resources(const struct device *domain, struct memranges *ranges,
|
||||
unsigned long type)
|
||||
{
|
||||
unsigned long mask_match = type | IORESOURCE_FIXED;
|
||||
|
||||
if (type == IORESOURCE_IO) {
|
||||
/*
|
||||
* Don't allow allocations in the VGA I/O range. PCI has special cases for
|
||||
* that.
|
||||
*/
|
||||
memranges_create_hole(ranges, 0x3b0, 0x3df - 0x3b0 + 1);
|
||||
|
||||
/*
|
||||
* Resource allocator no longer supports the legacy behavior where I/O resource
|
||||
* allocation is guaranteed to avoid aliases over legacy PCI expansion card
|
||||
* addresses.
|
||||
*/
|
||||
}
|
||||
|
||||
avoid_fixed_resources(ranges, domain, mask_match);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function creates a list of memranges of given type using the resource that is
|
||||
* provided. If the given resource is NULL or if the resource window size is 0, then it creates
|
||||
* an empty list. This results in resource allocation for that resource type failing for all
|
||||
* downstream devices since there is nothing to allocate from.
|
||||
*
|
||||
* In case of domain, it applies additional constraints to ensure that the memranges do not
|
||||
* overlap any of the fixed resources under that domain. Domain typically seems to provide
|
||||
* memrange for entire address space. Thus, it is up to the chipset to add DRAM and all other
|
||||
* windows which cannot be used for resource allocation as fixed resources.
|
||||
*/
|
||||
static void setup_resource_ranges(const struct device *dev, const struct resource *res,
|
||||
unsigned long type, struct memranges *ranges)
|
||||
{
|
||||
printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx\n",
|
||||
dev_path(dev), resource2str(res), res->base, res->size, res->align,
|
||||
res->gran, res->limit);
|
||||
|
||||
if (dev->path.type == DEVICE_PATH_DOMAIN) {
|
||||
initialize_domain_memranges(ranges, res, type);
|
||||
constrain_domain_resources(dev, ranges, type);
|
||||
} else {
|
||||
initialize_bridge_memranges(ranges, res, type);
|
||||
}
|
||||
|
||||
print_resource_ranges(dev, ranges);
|
||||
}
|
||||
|
||||
static void cleanup_resource_ranges(const struct device *dev, struct memranges *ranges,
|
||||
const struct resource *res)
|
||||
{
|
||||
memranges_teardown(ranges);
|
||||
printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx done\n",
|
||||
dev_path(dev), resource2str(res), res->base, res->size, res->align,
|
||||
res->gran, res->limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass 2 of resource allocator at the bridge level loops through all the resources for the
|
||||
* bridge and generates a list of memory ranges similar to that at the domain level. However,
|
||||
* there is no need to apply any additional constraints since the window allocated to the bridge
|
||||
* is guaranteed to be non-overlapping by the allocator at domain level.
|
||||
*
|
||||
* Allocation at the bridge level works the same as at domain level (starts with the biggest
|
||||
* resource requirement from downstream devices and continues in descending order). One major
|
||||
* difference at the bridge level is that it considers prefmem resources separately from mem
|
||||
* resources.
|
||||
*
|
||||
* Once allocation at the current bridge is complete, resource allocator continues walking down
|
||||
* the downstream bridges until it hits the leaf devices.
|
||||
*/
|
||||
static void allocate_bridge_resources(const struct device *bridge)
|
||||
{
|
||||
struct memranges ranges;
|
||||
const struct resource *res;
|
||||
struct bus *bus = bridge->link_list;
|
||||
unsigned long type_match;
|
||||
struct device *child;
|
||||
const unsigned long type_mask = IORESOURCE_TYPE_MASK | IORESOURCE_PREFETCH;
|
||||
|
||||
for (res = bridge->resource_list; res; res = res->next) {
|
||||
if (!res->size)
|
||||
continue;
|
||||
|
||||
if (!(res->flags & IORESOURCE_BRIDGE))
|
||||
continue;
|
||||
|
||||
type_match = res->flags & type_mask;
|
||||
|
||||
setup_resource_ranges(bridge, res, type_match, &ranges);
|
||||
allocate_child_resources(bus, &ranges, type_mask, type_match);
|
||||
cleanup_resource_ranges(bridge, &ranges, res);
|
||||
}
|
||||
|
||||
for (child = bus->children; child; child = child->sibling) {
|
||||
if (!dev_has_children(child))
|
||||
continue;
|
||||
|
||||
allocate_bridge_resources(child);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct resource *find_domain_resource(const struct device *domain,
|
||||
unsigned long type)
|
||||
{
|
||||
const struct resource *res;
|
||||
|
||||
for (res = domain->resource_list; res; res = res->next) {
|
||||
if (res->flags & IORESOURCE_FIXED)
|
||||
continue;
|
||||
|
||||
if ((res->flags & IORESOURCE_TYPE_MASK) == type)
|
||||
return res;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass 2 of resource allocator begins at the domain level. Every domain has two types of
|
||||
* resources - io and mem. For each of these resources, this function creates a list of memory
|
||||
* ranges that can be used for downstream resource allocation. This list is constrained to
|
||||
* remove any fixed resources in the domain sub-tree of the given resource type. It then uses
|
||||
* the memory ranges to apply best fit on the resource requirements of the downstream devices.
|
||||
*
|
||||
* Once resources are allocated to all downstream devices of the domain, it walks down each
|
||||
* downstream bridge to continue the same process until resources are allocated to all devices
|
||||
* under the domain.
|
||||
*/
|
||||
static void allocate_domain_resources(const struct device *domain)
|
||||
{
|
||||
struct memranges ranges;
|
||||
struct device *child;
|
||||
const struct resource *res;
|
||||
|
||||
/* Resource type I/O */
|
||||
res = find_domain_resource(domain, IORESOURCE_IO);
|
||||
if (res) {
|
||||
setup_resource_ranges(domain, res, IORESOURCE_IO, &ranges);
|
||||
allocate_child_resources(domain->link_list, &ranges, IORESOURCE_TYPE_MASK,
|
||||
IORESOURCE_IO);
|
||||
cleanup_resource_ranges(domain, &ranges, res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resource type Mem:
|
||||
* Domain does not distinguish between mem and prefmem resources. Thus, the resource
|
||||
* allocation at domain level considers mem and prefmem together when finding the best
|
||||
* fit based on the biggest resource requirement.
|
||||
*
|
||||
* However, resource requests for allocation above 4G boundary need to be handled
|
||||
* separately if the domain resource window crosses this boundary. There is a single
|
||||
* window for resource of type IORESOURCE_MEM. When creating memranges, this resource
|
||||
* is split into two separate ranges -- one for the window below 4G boundary and other
|
||||
* for the window above 4G boundary (with IORESOURCE_ABOVE_4G flag set). Thus, when
|
||||
* allocating child resources, requests for below and above the 4G boundary are handled
|
||||
* separately by setting the type_mask and type_match to allocate_child_resources()
|
||||
* accordingly.
|
||||
*/
|
||||
res = find_domain_resource(domain, IORESOURCE_MEM);
|
||||
if (res) {
|
||||
setup_resource_ranges(domain, res, IORESOURCE_MEM, &ranges);
|
||||
allocate_child_resources(domain->link_list, &ranges,
|
||||
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
|
||||
IORESOURCE_MEM);
|
||||
allocate_child_resources(domain->link_list, &ranges,
|
||||
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
|
||||
IORESOURCE_MEM | IORESOURCE_ABOVE_4G);
|
||||
cleanup_resource_ranges(domain, &ranges, res);
|
||||
}
|
||||
|
||||
for (child = domain->link_list->children; child; child = child->sibling) {
|
||||
if (!dev_has_children(child))
|
||||
continue;
|
||||
|
||||
/* Continue allocation for all downstream bridges. */
|
||||
allocate_bridge_resources(child);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function forms the guts of the resource allocator. It walks through the entire device
|
||||
* tree for each domain two times.
|
||||
*
|
||||
* Every domain has a fixed set of ranges. These ranges cannot be relaxed based on the
|
||||
* requirements of the downstream devices. They represent the available windows from which
|
||||
* resources can be allocated to the different devices under the domain.
|
||||
*
|
||||
* In order to identify the requirements of downstream devices, resource allocator walks in a
|
||||
* DFS fashion. It gathers the requirements from leaf devices and propagates those back up
|
||||
* to their upstream bridges until the requirements for all the downstream devices of the domain
|
||||
* are gathered. This is referred to as pass 1 of resource allocator.
|
||||
*
|
||||
* Once the requirements for all the devices under the domain are gathered, resource allocator
|
||||
* walks a second time to allocate resources to downstream devices as per the
|
||||
* requirements. It always picks the biggest resource request as per the type (i/o and mem) to
|
||||
* allocate space from its fixed window to the immediate downstream device of the domain. In
|
||||
* order to accomplish best fit for the resources, a list of ranges is maintained by each
|
||||
* resource type (i/o and mem). Domain does not differentiate between mem and prefmem. Since
|
||||
* they are allocated space from the same window, the resource allocator at the domain level
|
||||
* ensures that the biggest requirement is selected indepedent of the prefetch type. Once the
|
||||
* resource allocation for all immediate downstream devices is complete at the domain level,
|
||||
* resource allocator walks down the subtree for each downstream bridge to continue the
|
||||
* allocation process at the bridge level. Since bridges have separate windows for i/o, mem and
|
||||
* prefmem, best fit algorithm at bridge level looks for the biggest requirement considering
|
||||
* prefmem resources separately from non-prefmem resources. This continues until resource
|
||||
* allocation is performed for all downstream bridges in the domain sub-tree. This is referred
|
||||
* to as pass 2 of resource allocator.
|
||||
*
|
||||
* Some rules that are followed by the resource allocator:
|
||||
* - Allocate resource locations for every device as long as the requirements can be satisfied.
|
||||
* - If a resource cannot be allocated any address space, then that resource needs to be
|
||||
* properly updated to ensure that it does not incorrectly overlap some address space reserved
|
||||
* for a different purpose.
|
||||
* - Don't overlap with resources in fixed locations.
|
||||
* - Don't overlap and follow the rules of bridges -- downstream devices of bridges should use
|
||||
* parts of the address space allocated to the bridge.
|
||||
*/
|
||||
void allocate_resources(const struct device *root)
|
||||
{
|
||||
const struct device *child;
|
||||
|
||||
if ((root == NULL) || (root->link_list == NULL))
|
||||
return;
|
||||
|
||||
for (child = root->link_list->children; child; child = child->sibling) {
|
||||
|
||||
if (child->path.type != DEVICE_PATH_DOMAIN)
|
||||
continue;
|
||||
|
||||
post_log_path(child);
|
||||
|
||||
/* Pass 1 - Gather requirements. */
|
||||
printk(BIOS_INFO, "==== Resource allocator: %s - Pass 1 (gathering requirements) ===\n",
|
||||
dev_path(child));
|
||||
compute_domain_resources(child);
|
||||
|
||||
/* Pass 2 - Allocate resources as per gathered requirements. */
|
||||
printk(BIOS_INFO, "=== Resource allocator: %s - Pass 2 (allocating resources) ===\n",
|
||||
dev_path(child));
|
||||
allocate_domain_resources(child);
|
||||
|
||||
printk(BIOS_INFO, "=== Resource allocator: %s - resource allocation complete ===\n",
|
||||
dev_path(child));
|
||||
}
|
||||
}
|
@@ -1,5 +1,3 @@
|
||||
#
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config DRIVERS_AMD_PI
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user