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 | 	url = https://review.coreboot.org/amd_blobs.git | ||||||
| 	update = none | 	update = none | ||||||
| 	ignore = dirty | 	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 | ## Device drivers | ||||||
|  |  | ||||||
| Let's take a look at an example entry from | 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 | device pci 15.0 on | ||||||
|   | |||||||
| @@ -6,3 +6,4 @@ they allow to easily reuse existing code accross platforms. | |||||||
|  |  | ||||||
| * [IPMI KCS](ipmi_kcs.md) | * [IPMI KCS](ipmi_kcs.md) | ||||||
| * [SMMSTORE](smmstore.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 | are inserted into the CBFS with custom compression. The bootblock usually doesn't | ||||||
| have compression while the ramstage and payload are compressed with LZMA. | 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 | 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. | 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. | The script 'util/gitconfig/rebase.sh' can be used to help automate this. | ||||||
| Other tags such as 'Commit-Queue' can simply be removed. | 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 | 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_C	# or | ||||||
|     select GFX_GMA_ANALOG_I2C_HDMI_D |     select GFX_GMA_ANALOG_I2C_HDMI_D | ||||||
|  |  | ||||||
| Beside Kconfig options, *libgfxinit* needs to know which ports are | *libgfxinit* needs to know which ports are implemented on a board | ||||||
| implemented on a board and should be probed for displays. The mapping | and should be probed for displays. There are two mechanisms to | ||||||
| between the physical ports and these entries depends on the hardware | constrain the list of ports to probe, 1. port presence straps on | ||||||
| implementation and can be recovered by testing or studying the output | the mainboard, and 2. a list of ports provided by *coreboot* (see | ||||||
| of `intelvbttool` or `intel_vbt_decode`. | 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: | Each board has to implement the package `GMA.Mainboard` with a list: | ||||||
|  |  | ||||||
|     ports : HW.GFX.GMA.Display_Probing.Port_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) | - [CN81XX EVB SFF](cavium/cn8100_sff_evb.md) | ||||||
|  |  | ||||||
|  | ## Dell | ||||||
|  |  | ||||||
|  | - [OptiPlex 9010 SFF](dell/optiplex_9010.md) | ||||||
|  |  | ||||||
| ## Emulation | ## Emulation | ||||||
|  |  | ||||||
| The boards in this section are not real mainboards, but emulators. | 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 | [me_cleaner]: ../../northbridge/intel/sandybridge/me_cleaner.md | ||||||
| [external programmer]: ../../flash_tutorial/index.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. |       ask for testing. | ||||||
| - [ ] Test the commit selected for release. | - [ ] Test the commit selected for release. | ||||||
| - [ ] Update release notes with actual commit id, push to repo. | - [ ] 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 release script. | ||||||
| - [ ] Run vboot_list script. | - [ ] Run vboot_list script. | ||||||
| - [ ] Test the release from the actual release tarballs. | - [ ] 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. | 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 | Specification.  Deviations, e.g., no FSP-T support, shall be published | ||||||
| in an Integration Guide. | 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 | ## Footnotes | ||||||
|  |  | ||||||
| 1. *AMD Platform Security Processor BIOS Architecture Design Guide | 1. *AMD Platform Security Processor BIOS Architecture Design Guide | ||||||
|   | |||||||
| @@ -279,41 +279,6 @@ tests/lib/string-test and tests/device/i2c-test: | |||||||
|                         ├── i2c.o |                         ├── 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 | ### Writing new tests | ||||||
| Full description of how to write unit tests and Cmocka API description is out of | Our tutorial series has [detailed guidelines](../tutorial/part3.md) for writing | ||||||
| the scope of this document. There are other documents related to this | unit tests. | ||||||
| [Cmocka API](https://api.cmocka.org/) and |  | ||||||
| [Mocks](https://lwn.net/Articles/558106/). |  | ||||||
|   | |||||||
| @@ -2,3 +2,4 @@ | |||||||
|  |  | ||||||
| * [Part 1: Starting from scratch](part1.md) | * [Part 1: Starting from scratch](part1.md) | ||||||
| * [Part 2: Submitting a patch to coreboot.org](part2.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 2012 Red Hat Inc. | ||||||
| Copyright 2013 Google Inc. | Copyright 2013 Google Inc. | ||||||
| Copyright 2014 Google Inc. | Copyright 2014 Google Inc. | ||||||
|  | Copyright 2014 The Chromium OS Authors. All rights reserved. | ||||||
| Copyright 2015 Google Inc. | Copyright 2015 Google Inc. | ||||||
| Copyright 2015, Google Inc. | Copyright 2015, Google Inc. | ||||||
| Copyright 2016 Jonathan Neuschäfer <j.neuschaefer@gmx.net> | Copyright 2016 Jonathan Neuschäfer <j.neuschaefer@gmx.net> | ||||||
| Copyright 2016 The Chromium OS Authors. All rights reserved. | Copyright 2016 The Chromium OS Authors. All rights reserved. | ||||||
|  | Copyright 2017-2019 Eltan B.V. | ||||||
| Copyright 2017 Google Inc. | Copyright 2017 Google Inc. | ||||||
| Copyright 2018 Generated Code | Copyright 2018 Generated Code | ||||||
| Copyright 2018-present Facebook, Inc. | Copyright 2018-present Facebook, Inc. | ||||||
| Copyright 2019 9Elements Agency GmbH <patrick.rudolph@9elements.com> | 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) 2002 David S. Peterson.  All rights reserved. | ||||||
| Copyright (c) 2003-2016  Cavium Inc. (support@cavium.com). All rights | Copyright (c) 2003-2016  Cavium Inc. (support@cavium.com). All rights | ||||||
| Copyright (c) 2003-2017  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 Advanced Micro Devices, Inc. | ||||||
| Copyright (c) 2008, Google Inc. | Copyright (c) 2008, Google Inc. | ||||||
| Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net> | 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) 2009-2012, Code Aurora Forum. All rights reserved. | ||||||
| Copyright (c) 2010-2017, The Regents of the University of California | Copyright (c) 2010-2017, The Regents of the University of California | ||||||
| Copyright (c) 2010, Code Aurora Forum. All rights reserved. | Copyright (c) 2010, Code Aurora Forum. All rights reserved. | ||||||
| Copyright (C) 2010 coresystems GmbH | Copyright (C) 2010 coresystems GmbH | ||||||
| Copyright (c) 2010 Per Odlund <per.odlund@armagedon.se> | 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, 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-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 - 2014 The Linux Foundation. All rights reserved. | ||||||
| Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. | Copyright (c) 2011-2019 The Linux Foundation. All rights reserved. | ||||||
| Copyright (c) 2011, Google Inc. | 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, 2019 The Linux Foundation. | ||||||
| Copyright (c) 2012 - 2013, 2015 The Linux Foundation. All rights reserved. | Copyright (c) 2012 - 2013, 2015 The Linux Foundation. All rights reserved. | ||||||
| Copyright (c) 2012 - 2013 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, 2018, The Linux Foundation. All rights reserved. | ||||||
| Copyright (C) 2016 Google Inc. | Copyright (C) 2016 Google Inc. | ||||||
| Copyright (c) 2016, The Regents of the University of California (Regents). | 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 - 2019 The Linux Foundation. All rights reserved. | ||||||
|  | Copyright (c) 2018 Eltan B.V. | ||||||
| Copyright (c) 2018, HardenedLinux. | Copyright (c) 2018, HardenedLinux. | ||||||
| Copyright (C) 2018, The Linux Foundation.  All rights reserved. | Copyright (C) 2018, The Linux Foundation.  All rights reserved. | ||||||
| Copyright Dave Airlie <airlied@redhat.com> | Copyright Dave Airlie <airlied@redhat.com> | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,33 +1,4 @@ | |||||||
| ## | ## SPDX-License-Identifier: BSD-3-Clause | ||||||
| ## |  | ||||||
| ## 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. |  | ||||||
| ## |  | ||||||
|  |  | ||||||
| ifneq ($(words $(CURDIR)),1) | ifneq ($(words $(CURDIR)),1) | ||||||
|     $(error Error: Path to the main directory cannot contain spaces) |     $(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" | 	@printf "    HOSTCC     $(subst $(obj)/,,$(@))\n" | ||||||
| 	$(HOSTCC) $(HOSTCFLAGS) -DCONFIG_ROM_SIZE=$(CONFIG_ROM_SIZE) -o $@ $< | 	$(HOSTCC) $(HOSTCFLAGS) -DCONFIG_ROM_SIZE=$(CONFIG_ROM_SIZE) -o $@ $< | ||||||
|  |  | ||||||
|  | APCB_EDIT_TOOL:=$(top)/util/apcb/apcb_edit.py | ||||||
|  |  | ||||||
| CBOOTIMAGE:=$(objutil)/cbootimage/cbootimage | CBOOTIMAGE:=$(objutil)/cbootimage/cbootimage | ||||||
|  |  | ||||||
| FUTILITY?=$(objutil)/futility/futility | FUTILITY?=$(objutil)/futility/futility | ||||||
| @@ -596,6 +598,17 @@ bootblock-y+=$(DEVICETREE_STATIC_C) | |||||||
| postcar-y+=$(DEVICETREE_STATIC_C) | postcar-y+=$(DEVICETREE_STATIC_C) | ||||||
| smm-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 up rules | ||||||
| clean-abuild: | 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; | 	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: |  * Transform a vector: | ||||||
|  * 	x' = x * a_x + offset_x |  * 	x' = x * a_x + offset_x | ||||||
| @@ -82,7 +111,7 @@ static int transform_vector(struct vector *out, | |||||||
| 			    const struct scale *a, | 			    const struct scale *a, | ||||||
| 			    const struct vector *offset) | 			    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; | 		return CBGFX_ERROR_INVALID_PARAMETER; | ||||||
| 	out->x = a->x.n * in->x / a->x.d + offset->x; | 	out->x = a->x.n * in->x / a->x.d + offset->x; | ||||||
| 	out->y = a->y.n * in->y / a->y.d + offset->y; | 	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) | int draw_box(const struct rect *box, const struct rgb_color *rgb) | ||||||
| { | { | ||||||
| 	struct vector top_left; | 	struct vector top_left; | ||||||
| 	struct vector size; |  | ||||||
| 	struct vector p, t; | 	struct vector p, t; | ||||||
|  |  | ||||||
| 	if (cbgfx_init()) | 	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, }, | 		.x = { .n = box->offset.x, .d = CANVAS_SCALE, }, | ||||||
| 		.y = { .n = box->offset.y, .d = CANVAS_SCALE, } | 		.y = { .n = box->offset.y, .d = CANVAS_SCALE, } | ||||||
| 	}; | 	}; | ||||||
| 	const struct scale size_s = { | 	const struct scale bottom_right_s = { | ||||||
| 		.x = { .n = box->size.x, .d = CANVAS_SCALE, }, | 		.x = { .n = box->offset.x + box->size.x, .d = CANVAS_SCALE, }, | ||||||
| 		.y = { .n = box->size.y, .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(&top_left, &canvas.size, &top_left_s, &canvas.offset); | ||||||
| 	transform_vector(&size, &canvas.size, &size_s, &vzero); | 	transform_vector(&t, &canvas.size, &bottom_right_s, &canvas.offset); | ||||||
| 	add_vectors(&t, &top_left, &size); |  | ||||||
| 	if (within_box(&t, &canvas) < 0) { | 	if (within_box(&t, &canvas) < 0) { | ||||||
| 		LOG("Box exceeds canvas boundary\n"); | 		LOG("Box exceeds canvas boundary\n"); | ||||||
| 		return CBGFX_ERROR_BOUNDARY; | 		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 *thickness, | ||||||
| 		     const struct fraction *radius) | 		     const struct fraction *radius) | ||||||
| { | { | ||||||
|  | 	struct scale pos_end_rel; | ||||||
| 	struct vector top_left; | 	struct vector top_left; | ||||||
| 	struct vector size; |  | ||||||
| 	struct vector p, t; | 	struct vector p, t; | ||||||
|  |  | ||||||
| 	if (cbgfx_init()) | 	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); | 	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(&top_left, &canvas.size, pos_rel, &canvas.offset); | ||||||
| 	transform_vector(&size, &canvas.size, dim_rel, &vzero); | 	transform_vector(&t, &canvas.size, &pos_end_rel, &canvas.offset); | ||||||
| 	add_vectors(&t, &top_left, &size); |  | ||||||
| 	if (within_box(&t, &canvas) < 0) { | 	if (within_box(&t, &canvas) < 0) { | ||||||
| 		LOG("Box exceeds canvas boundary\n"); | 		LOG("Box exceeds canvas boundary\n"); | ||||||
| 		return CBGFX_ERROR_BOUNDARY; | 		return CBGFX_ERROR_BOUNDARY; | ||||||
|   | |||||||
| @@ -45,9 +45,9 @@ typedef long ptrdiff_t; | |||||||
| #define INT64_MAX	(9223372036854775807LL) | #define INT64_MAX	(9223372036854775807LL) | ||||||
|  |  | ||||||
| #define INT8_MIN	(-INT8_MAX - 1) | #define INT8_MIN	(-INT8_MAX - 1) | ||||||
| #define INT16_MIN	(-INT16_MIN - 1) | #define INT16_MIN	(-INT16_MAX - 1) | ||||||
| #define INT32_MIN	(-INT32_MIN - 1) | #define INT32_MIN	(-INT32_MAX - 1) | ||||||
| #define INT64_MIN	(-INT64_MIN - 1) | #define INT64_MIN	(-INT64_MAX - 1) | ||||||
|  |  | ||||||
| #define UINT8_MAX	(255) | #define UINT8_MAX	(255) | ||||||
| #define UINT16_MAX	(65535) | #define UINT16_MAX	(65535) | ||||||
|   | |||||||
| @@ -491,10 +491,10 @@ unsigned long long int strtoull(const char *ptr, char **endptr, int base) | |||||||
| 		else if (ptr[0] == '0') { | 		else if (ptr[0] == '0') { | ||||||
| 			base = 8; | 			base = 8; | ||||||
| 			ptr++; | 			ptr++; | ||||||
| 		} | 		} else { | ||||||
| 		else |  | ||||||
| 			base = 10; | 			base = 10; | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	/* Base 16 allows the 0x on front - so skip over it */ | 	/* Base 16 allows the 0x on front - so skip over it */ | ||||||
|  |  | ||||||
| @@ -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 int strtoul(const char *ptr, char **endptr, int base) | ||||||
| { | { | ||||||
| 	unsigned long long val = strtoull(ptr, endptr, 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; | 	return val; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -598,8 +599,6 @@ char* strtok_r(char *str, const char *delim, char **ptr) | |||||||
| 	return start; | 	return start; | ||||||
| } | } | ||||||
|  |  | ||||||
| static char **strtok_global; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Extract first token in string str that is delimited by a character in tokens. |  * Extract first token in string str that is delimited by a character in tokens. | ||||||
|  * Destroys str, eliminates the token delimiter and uses global state. |  * Destroys str, eliminates the token delimiter and uses global state. | ||||||
| @@ -609,7 +608,9 @@ static char **strtok_global; | |||||||
|  */ |  */ | ||||||
| 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 | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| mainmenu "coreboot configuration" | mainmenu "coreboot configuration" | ||||||
|   | |||||||
| @@ -6,9 +6,11 @@ ramstage-y += acpi.c | |||||||
| ramstage-y += acpigen.c | ramstage-y += acpigen.c | ||||||
| ramstage-y += acpigen_dsm.c | ramstage-y += acpigen_dsm.c | ||||||
| ramstage-y += acpigen_ps2_keybd.c | ramstage-y += acpigen_ps2_keybd.c | ||||||
|  | ramstage-y += acpigen_usb.c | ||||||
| ramstage-y += device.c | ramstage-y += device.c | ||||||
| ramstage-y += pld.c | ramstage-y += pld.c | ||||||
| ramstage-y += sata.c | ramstage-y += sata.c | ||||||
|  | ramstage-y += soundwire.c | ||||||
|  |  | ||||||
| ifneq ($(wildcard src/mainboard/$(MAINBOARDDIR)/acpi_tables.c),) | ifneq ($(wildcard src/mainboard/$(MAINBOARDDIR)/acpi_tables.c),) | ||||||
| ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/acpi_tables.c | ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/acpi_tables.c | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ | |||||||
| #include <device/device.h> | #include <device/device.h> | ||||||
| #include <device/pci_def.h> | #include <device/pci_def.h> | ||||||
| #include <device/pci_type.h> | #include <device/pci_type.h> | ||||||
|  | #include <device/soundwire.h> | ||||||
|  |  | ||||||
| static char *gencurrent; | static char *gencurrent; | ||||||
|  |  | ||||||
| @@ -1885,3 +1886,30 @@ void acpigen_write_ADR_pci_device(const struct device *dev) | |||||||
| 	assert(dev->path.type == DEVICE_PATH_PCI); | 	assert(dev->path.type == DEVICE_PATH_PCI); | ||||||
| 	acpigen_write_ADR_pci_devfn(dev->path.pci.devfn); | 	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(0xd0, KEY_DOWN), | ||||||
| 	KEYMAP(0xcd, KEY_RIGHT), | 	KEYMAP(0xcd, KEY_RIGHT), | ||||||
| 	KEYMAP(0xc8, KEY_UP), | 	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, | 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 | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
|  |  | ||||||
| ############################################################################### | ############################################################################### | ||||||
| # ARM specific options | # ARM specific options | ||||||
|   | |||||||
| @@ -1,8 +1,4 @@ | |||||||
| ################################################################################ |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
| ############################################################################### |  | ||||||
|  |  | ||||||
| armv4_flags = -marm -march=armv4t -I$(src)/arch/arm/include/armv4/ \ | armv4_flags = -marm -march=armv4t -I$(src)/arch/arm/include/armv4/ \ | ||||||
| 	-D__COREBOOT_ARM_ARCH__=4 | 	-D__COREBOOT_ARM_ARCH__=4 | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ | |||||||
|  * Reference: ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition |  * Reference: ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <stdint.h> |  | ||||||
|  |  | ||||||
| #include <arch/cache.h> | #include <arch/cache.h> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,4 @@ | |||||||
| ################################################################################ |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
| ############################################################################### |  | ||||||
|  |  | ||||||
| armv7_flags = -mthumb -I$(src)/arch/arm/include/armv7/ -D__COREBOOT_ARM_ARCH__=7 | 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__ | 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 |  * cache.c: Cache maintenance routines for ARMv7-M | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <stdint.h> |  | ||||||
|  |  | ||||||
| #include <arch/cache.h> | #include <arch/cache.h> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,4 @@ | |||||||
| ################################################################################ |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
| ################################################################################ |  | ||||||
|  |  | ||||||
| libgcc_files = ashldi3.S lib1funcs.S lshrdi3.S muldi3.S ucmpdi2.S uldivmod.S | libgcc_files = ashldi3.S lib1funcs.S lshrdi3.S muldi3.S ucmpdi2.S uldivmod.S | ||||||
| libgcc_files += udivmoddi4.c umoddi3.c | libgcc_files += udivmoddi4.c umoddi3.c | ||||||
|   | |||||||
| @@ -1,8 +1,4 @@ | |||||||
| ################################################################################ |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
| ################################################################################ |  | ||||||
|  |  | ||||||
| ################################################################################ | ################################################################################ | ||||||
| # Take care of subdirectories | # Take care of subdirectories | ||||||
|   | |||||||
| @@ -1,8 +1,4 @@ | |||||||
| ################################################################################ |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
| ################################################################################ |  | ||||||
|  |  | ||||||
| ifeq ($(CONFIG_ARCH_ARMV8_EXTENSION),0) | ifeq ($(CONFIG_ARCH_ARMV8_EXTENSION),0) | ||||||
| march = armv8-a | march = armv8-a | ||||||
|   | |||||||
| @@ -1,8 +1,4 @@ | |||||||
| ################################################################################ |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
| ################################################################################ |  | ||||||
|  |  | ||||||
| ppc64_flags = -I$(src)/arch/ppc64/ -mbig-endian -mcpu=power8 -mtune=power8 | ppc64_flags = -I$(src)/arch/ppc64/ -mbig-endian -mcpu=power8 -mtune=power8 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,4 @@ | |||||||
| ################################################################################ |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
| ################################################################################ |  | ||||||
|  |  | ||||||
| ################################################################################ | ################################################################################ | ||||||
| ## RISC-V specific options | ## RISC-V specific options | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ | |||||||
| #include <console/console.h> | #include <console/console.h> | ||||||
| #include <bootmem.h> | #include <bootmem.h> | ||||||
| #include <program_loading.h> | #include <program_loading.h> | ||||||
| #include <lib.h> |  | ||||||
| #include <fit.h> | #include <fit.h> | ||||||
| #include <endian.h> | #include <endian.h> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ | |||||||
|  |  | ||||||
| #include <arch/cpu.h> | #include <arch/cpu.h> | ||||||
| #include <arch/encoding.h> | #include <arch/encoding.h> | ||||||
| #include <stdint.h> |  | ||||||
| #include <vm.h> | #include <vm.h> | ||||||
|  |  | ||||||
| /* Delegate controls which traps are delegated to the payload. If you | /* Delegate controls which traps are delegated to the payload. If you | ||||||
|   | |||||||
| @@ -1,6 +1,4 @@ | |||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
|  |  | ||||||
| config ARCH_X86 | config ARCH_X86 | ||||||
| 	bool | 	bool | ||||||
|   | |||||||
| @@ -1,6 +1,4 @@ | |||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
| ## |  | ||||||
|  |  | ||||||
| ifeq ($(CONFIG_POSTCAR_STAGE),y) | ifeq ($(CONFIG_POSTCAR_STAGE),y) | ||||||
| $(eval $(call init_standard_toolchain,postcar)) | $(eval $(call init_standard_toolchain,postcar)) | ||||||
|   | |||||||
| @@ -10,11 +10,12 @@ | |||||||
|  |  | ||||||
| #include <cpu/x86/cr.h> | #include <cpu/x86/cr.h> | ||||||
|  |  | ||||||
|  | .section .text | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Include the old code for reset vector and protected mode entry. That code has |  * Include the old code for reset vector and protected mode entry. That code has | ||||||
|  * withstood the test of time. |  * withstood the test of time. | ||||||
|  */ |  */ | ||||||
| #include <arch/x86/prologue.inc> |  | ||||||
| #include <cpu/x86/16bit/entry16.inc> | #include <cpu/x86/16bit/entry16.inc> | ||||||
| #include <cpu/x86/16bit/reset16.inc> | #include <cpu/x86/16bit/reset16.inc> | ||||||
| #include <cpu/x86/32bit/entry32.inc> | #include <cpu/x86/32bit/entry32.inc> | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ | |||||||
| #include <fallback.h> | #include <fallback.h> | ||||||
| #include <program_loading.h> | #include <program_loading.h> | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| #include <string.h> |  | ||||||
|  |  | ||||||
| static const char *get_fallback(const char *stagelist) | static const char *get_fallback(const char *stagelist) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -62,13 +62,11 @@ | |||||||
|  |  | ||||||
| 	. = ALIGN(ARCH_POINTER_ALIGN_SIZE); | 	. = ALIGN(ARCH_POINTER_ALIGN_SIZE); | ||||||
| 	_bss = .; | 	_bss = .; | ||||||
| #if ENV_STAGE_HAS_BSS_SECTION |  | ||||||
| 	/* Allow global uninitialized variables for stages without CAR teardown. */ | 	/* Allow global uninitialized variables for stages without CAR teardown. */ | ||||||
| 	*(.bss) | 	*(.bss) | ||||||
| 	*(.bss.*) | 	*(.bss.*) | ||||||
| 	*(.sbss) | 	*(.sbss) | ||||||
| 	*(.sbss.*) | 	*(.sbss.*) | ||||||
| #endif |  | ||||||
| 	. = ALIGN(ARCH_POINTER_ALIGN_SIZE); | 	. = ALIGN(ARCH_POINTER_ALIGN_SIZE); | ||||||
| 	_ebss = .; | 	_ebss = .; | ||||||
| 	_car_unallocated_start = .; | 	_car_unallocated_start = .; | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ _FMAP_SIZE = 0; | |||||||
|  * The stack area is not shared between stages, but is defined here for |  * The stack area is not shared between stages, but is defined here for | ||||||
|  * convenience. |  * 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."); | _ = 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) | 	#if !CONFIG(NO_FMAP_CACHE) | ||||||
| 		FMAP_CACHE(., FMAP_SIZE) | 		FMAP_CACHE(., FMAP_SIZE) | ||||||
| 	#endif | 	#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!"); | _ = 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 */ | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
| SECTIONS { | 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 (.): { | 	.id (.): { | ||||||
| 		KEEP(*(.id)) | 		KEEP(*(.id)) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -5,50 +5,42 @@ | |||||||
|  |  | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  |  | ||||||
| static __always_inline uint8_t read8( | static __always_inline uint8_t read8(const volatile void *addr) | ||||||
| 	const volatile void *addr) |  | ||||||
| { | { | ||||||
| 	return *((volatile uint8_t *)(addr)); | 	return *((volatile uint8_t *)(addr)); | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline uint16_t read16( | static __always_inline uint16_t read16(const volatile void *addr) | ||||||
| 	const volatile void *addr) |  | ||||||
| { | { | ||||||
| 	return *((volatile uint16_t *)(addr)); | 	return *((volatile uint16_t *)(addr)); | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline uint32_t read32( | static __always_inline uint32_t read32(const volatile void *addr) | ||||||
| 	const volatile void *addr) |  | ||||||
| { | { | ||||||
| 	return *((volatile uint32_t *)(addr)); | 	return *((volatile uint32_t *)(addr)); | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline uint64_t read64( | static __always_inline uint64_t read64(const volatile void *addr) | ||||||
| 	const volatile void *addr) |  | ||||||
| { | { | ||||||
| 	return *((volatile uint64_t *)(addr)); | 	return *((volatile uint64_t *)(addr)); | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline void write8(volatile void *addr, | static __always_inline void write8(volatile void *addr, uint8_t value) | ||||||
| 	uint8_t value) |  | ||||||
| { | { | ||||||
| 	*((volatile uint8_t *)(addr)) = value; | 	*((volatile uint8_t *)(addr)) = value; | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline void write16(volatile void *addr, | static __always_inline void write16(volatile void *addr, uint16_t value) | ||||||
| 	uint16_t value) |  | ||||||
| { | { | ||||||
| 	*((volatile uint16_t *)(addr)) = value; | 	*((volatile uint16_t *)(addr)) = value; | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline void write32(volatile void *addr, | static __always_inline void write32(volatile void *addr, uint32_t value) | ||||||
| 	uint32_t value) |  | ||||||
| { | { | ||||||
| 	*((volatile uint32_t *)(addr)) = value; | 	*((volatile uint32_t *)(addr)) = value; | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline void write64(volatile void *addr, | static __always_inline void write64(volatile void *addr, uint64_t value) | ||||||
| 	uint64_t value) |  | ||||||
| { | { | ||||||
| 	*((volatile uint64_t *)(addr)) = 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 | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config COMMONLIB_STORAGE | config COMMONLIB_STORAGE | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| ifeq ($(CONFIG_COMMONLIB_STORAGE),y) | ifeq ($(CONFIG_COMMONLIB_STORAGE),y) | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_AMD_AGESA | config CPU_AMD_AGESA | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| subdirs-$(CONFIG_CPU_AMD_AGESA_FAMILY14) += family14 | subdirs-$(CONFIG_CPU_AMD_AGESA_FAMILY14) += family14 | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_AMD_AGESA_FAMILY14 | config CPU_AMD_AGESA_FAMILY14 | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| romstage-y += fixme.c | romstage-y += fixme.c | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_AMD_AGESA_FAMILY15_TN | config CPU_AMD_AGESA_FAMILY15_TN | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| romstage-y += fixme.c | romstage-y += fixme.c | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_AMD_AGESA_FAMILY16_KB | config CPU_AMD_AGESA_FAMILY16_KB | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| romstage-y += fixme.c | romstage-y += fixme.c | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_AMD_PI_00630F01 | config CPU_AMD_PI_00630F01 | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| romstage-y += fixme.c | romstage-y += fixme.c | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_AMD_PI_00660F01 | config CPU_AMD_PI_00660F01 | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| romstage-y += fixme.c | romstage-y += fixme.c | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_AMD_PI_00730F01 | config CPU_AMD_PI_00730F01 | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| romstage-y += fixme.c | romstage-y += fixme.c | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_AMD_PI | config CPU_AMD_PI | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| # |  | ||||||
| # |  | ||||||
| # SPDX-License-Identifier: GPL-2.0-only | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| subdirs-$(CONFIG_CPU_AMD_PI_00630F01) += 00630F01 | 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. |  * Init CPPC block with MSRs for Intel Enhanced Speed Step Technology. | ||||||
|  * Version 2 is suggested--this function's implementation of version 3 |  * Version 2 is suggested--this function's implementation of version 3 | ||||||
|  * may have room for improvment. |  * may have room for improvement. | ||||||
|  */ |  */ | ||||||
| struct cppc_config; | struct cppc_config; | ||||||
| void cpu_init_cppc_config(struct cppc_config *config, u32 version); | void cpu_init_cppc_config(struct cppc_config *config, u32 version); | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ fit_pointer: | |||||||
| .long 0 | .long 0 | ||||||
| .previous | .previous | ||||||
|  |  | ||||||
| .section ".rom.data", "a", @progbits | .section .text | ||||||
| .align 16 | .align 16 | ||||||
| .global fit_table | .global fit_table | ||||||
| .global fit_table_end | .global fit_table_end | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <arch/cpu.h> | #include <arch/cpu.h> | ||||||
| #include <cpu/x86/msr.h> | #include <cpu/x86/msr.h> | ||||||
| #include "haswell.h" | #include "haswell.h" | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <console/console.h> | #include <console/console.h> | ||||||
| #include <cf9_reset.h> | #include <cf9_reset.h> | ||||||
| #include <cpu/x86/bist.h> | #include <cpu/x86/bist.h> | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <arch/cpu.h> | #include <arch/cpu.h> | ||||||
| #include <cpu/x86/msr.h> | #include <cpu/x86/msr.h> | ||||||
| #include <cpu/intel/speedstep.h> | #include <cpu/intel/speedstep.h> | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <arch/cpu.h> | #include <arch/cpu.h> | ||||||
| #include <cpu/x86/msr.h> | #include <cpu/x86/msr.h> | ||||||
| #include <cpu/intel/speedstep.h> | #include <cpu/intel/speedstep.h> | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ## |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_INTEL_SLOT_1 | config CPU_INTEL_SLOT_1 | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ## |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_QEMU_POWER8 | config CPU_QEMU_POWER8 | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ## |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config CPU_QEMU_X86 | config CPU_QEMU_X86 | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  |  | ||||||
| #include <stddef.h> |  | ||||||
| #include <cbmem.h> | #include <cbmem.h> | ||||||
| #include <symbols.h> | #include <symbols.h> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| /* SPDX-License-Identifier: GPL-2.0-or-later */ | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||||||
|  |  | ||||||
| #include <stddef.h> |  | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <symbols.h> | #include <symbols.h> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,6 +26,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <arch/rom_segs.h> | #include <arch/rom_segs.h> | ||||||
|  | #include <cpu/x86/post_code.h> | ||||||
|  |  | ||||||
| /* Symbol _start16bit must be aligned to 4kB to start AP CPUs with | /* Symbol _start16bit must be aligned to 4kB to start AP CPUs with | ||||||
|  * Startup IPI message without RAM. |  * Startup IPI message without RAM. | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ## |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| bootblock-y += name.c | bootblock-y += name.c | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ## |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| ramstage-y += smm_module_loader.c | ramstage-y += smm_module_loader.c | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ## |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| menu "Devices" | menu "Devices" | ||||||
| @@ -585,6 +583,22 @@ config PCIEXP_HOTPLUG_PREFETCH_MEM | |||||||
| 	  child devices. This size should be page-aligned. The default is | 	  child devices. This size should be page-aligned. The default is | ||||||
| 	  256 MiB. | 	  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 | config PCIEXP_HOTPLUG_IO | ||||||
| 	hex "PCI Express Hotplug I/O Space" | 	hex "PCI Express Hotplug I/O Space" | ||||||
| 	default 0x2000 | 	default 0x2000 | ||||||
| @@ -779,4 +793,22 @@ config SOFTWARE_I2C | |||||||
| 	  I2C controller is not (yet) available. The platform code needs to | 	  I2C controller is not (yet) available. The platform code needs to | ||||||
| 	  provide bindings to manually toggle I2C lines. | 	  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 | endmenu | ||||||
|   | |||||||
| @@ -58,3 +58,7 @@ bootblock-y += mmio.c | |||||||
| verstage-y += mmio.c | verstage-y += mmio.c | ||||||
| romstage-y += mmio.c | romstage-y += mmio.c | ||||||
| ramstage-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; | 	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. |  * 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); | 	       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; | struct device *vga_pri = NULL; | ||||||
| static void set_vga_bridge_bits(void) | static void set_vga_bridge_bits(void) | ||||||
| { | { | ||||||
| @@ -996,9 +452,7 @@ void dev_enumerate(void) | |||||||
|  */ |  */ | ||||||
| void dev_configure(void) | void dev_configure(void) | ||||||
| { | { | ||||||
| 	struct resource *res; |  | ||||||
| 	const struct device *root; | 	const struct device *root; | ||||||
| 	const struct device *child; |  | ||||||
|  |  | ||||||
| 	set_vga_bridge_bits(); | 	set_vga_bridge_bits(); | ||||||
|  |  | ||||||
| @@ -1020,53 +474,8 @@ void dev_configure(void) | |||||||
|  |  | ||||||
| 	print_resource_tree(root, BIOS_SPEW, "After reading."); | 	print_resource_tree(root, BIOS_SPEW, "After reading."); | ||||||
|  |  | ||||||
| 	/* Compute resources for all domains. */ | 	allocate_resources(root); | ||||||
| 	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; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	assign_resources(root->link_list); | 	assign_resources(root->link_list); | ||||||
| 	printk(BIOS_INFO, "Done setting resources.\n"); | 	printk(BIOS_INFO, "Done setting resources.\n"); | ||||||
| 	print_resource_tree(root, BIOS_SPEW, "After assigning values."); | 	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: | 	case DEVICE_PATH_MMIO: | ||||||
| 		equal = (path1->mmio.addr == path2->mmio.addr); | 		equal = (path1->mmio.addr == path2->mmio.addr); | ||||||
| 		break; | 		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: | 	default: | ||||||
| 		printk(BIOS_ERR, "Unknown device type: %d\n", path1->type); | 		printk(BIOS_ERR, "Unknown device type: %d\n", path1->type); | ||||||
| 		break; | 		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 bus *pci_root_bus(void) | ||||||
| { | { | ||||||
| 	DEVTREE_CONST struct device *pci_domain; | 	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) | 	if (pci_root) | ||||||
| 		return pci_root; | 		return pci_root; | ||||||
|   | |||||||
| @@ -215,6 +215,14 @@ const char *dev_path(const struct device *dev) | |||||||
| 			snprintf(buffer, sizeof(buffer), "MMIO: %08lx", | 			snprintf(buffer, sizeof(buffer), "MMIO: %08lx", | ||||||
| 				 dev->path.mmio.addr); | 				 dev->path.mmio.addr); | ||||||
| 			break; | 			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: | 		default: | ||||||
| 			printk(BIOS_ERR, "Unknown device path type: %d\n", | 			printk(BIOS_ERR, "Unknown device path type: %d\n", | ||||||
| 			       dev->path.type); | 			       dev->path.type); | ||||||
| @@ -875,6 +883,13 @@ void tolm_test(void *gp, struct device *dev, struct resource *new) | |||||||
|  |  | ||||||
| 	best = *best_p; | 	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)) | 	if (!best || (best->base > new->base)) | ||||||
| 		best = new; | 		best = new; | ||||||
|  |  | ||||||
| @@ -885,9 +900,9 @@ u32 find_pci_tolm(struct bus *bus) | |||||||
| { | { | ||||||
| 	struct resource *min = NULL; | 	struct resource *min = NULL; | ||||||
| 	u32 tolm; | 	u32 tolm; | ||||||
|  | 	unsigned long mask_match = IORESOURCE_MEM | IORESOURCE_ASSIGNED; | ||||||
|  |  | ||||||
| 	search_bus_resources(bus, IORESOURCE_MEM, IORESOURCE_MEM, | 	search_bus_resources(bus, mask_match, mask_match, tolm_test, &min); | ||||||
| 			     tolm_test, &min); |  | ||||||
|  |  | ||||||
| 	tolm = 0xffffffffUL; | 	tolm = 0xffffffffUL; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ## |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| subdirs-$(CONFIG_PCI_OPTION_ROM_RUN_YABEL) += x86emu | subdirs-$(CONFIG_PCI_OPTION_ROM_RUN_YABEL) += x86emu | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| ## |  | ||||||
| ## |  | ||||||
| ## SPDX-License-Identifier: GPL-2.0-only | ## SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| ramstage-$(CONFIG_PCI_OPTION_ROM_RUN_REALMODE) += x86.c | ramstage-$(CONFIG_PCI_OPTION_ROM_RUN_REALMODE) += x86.c | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ | |||||||
| #include <device/pci_ops.h> | #include <device/pci_ops.h> | ||||||
| #include <bootmode.h> | #include <bootmode.h> | ||||||
| #include <console/console.h> | #include <console/console.h> | ||||||
|  | #include <cpu/cpu.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| @@ -433,22 +434,93 @@ void pci_domain_read_resources(struct device *dev) | |||||||
|  |  | ||||||
| 	/* Initialize the system-wide memory resources constraints. */ | 	/* Initialize the system-wide memory resources constraints. */ | ||||||
| 	res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0)); | 	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 | | 	res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | | ||||||
| 		     IORESOURCE_ASSIGNED; | 		     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; | 	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. */ | 	/* Make certain the resource has actually been assigned a value. */ | ||||||
| 	if (!(resource->flags & IORESOURCE_ASSIGNED)) { | 	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 " | 			printk(BIOS_ERR, "ERROR: %s %02lx %s size: 0x%010llx not " | ||||||
| 			       "assigned\n", dev_path(dev), resource->index, | 			       "assigned\n", dev_path(dev), resource->index, | ||||||
| 			       resource_type(resource), resource->size); | 			       resource_type(resource), resource->size); | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	/* If this resource is fixed don't worry about it. */ | 	/* If this resource is fixed don't worry about it. */ | ||||||
| 	if (resource->flags & IORESOURCE_FIXED) | 	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; | 			dev->command |= PCI_COMMAND_MASTER; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* Get the base address. */ |  | ||||||
| 	base = resource->base; |  | ||||||
|  |  | ||||||
| 	/* Get the end. */ |  | ||||||
| 	end = resource_end(resource); |  | ||||||
|  |  | ||||||
| 	/* Now store the resource. */ | 	/* Now store the resource. */ | ||||||
| 	resource->flags |= IORESOURCE_STORED; | 	resource->flags |= IORESOURCE_STORED; | ||||||
|  |  | ||||||
| 	/* | 	if (resource->flags & IORESOURCE_PCI_BRIDGE) | ||||||
| 	 * PCI bridges have no enable bit. They are disabled if the base of | 		pci_store_bridge_resource(dev, resource); | ||||||
| 	 * the range is greater than the limit. If the size is zero, disable | 	else | ||||||
| 	 * by setting the base = limit and end = limit - 2^gran. | 		pci_store_resource(dev, resource); | ||||||
| 	 */ |  | ||||||
| 	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); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	report_resource_stored(dev, resource, ""); | 	report_resource_stored(dev, resource, ""); | ||||||
| } | } | ||||||
| @@ -764,17 +787,19 @@ struct device_operations default_pci_ops_dev = { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| /** Default device operations for PCI bridges */ | /** Default device operations for PCI bridges */ | ||||||
| static struct pci_operations pci_bus_ops_pci = { |  | ||||||
| 	.set_subsystem = 0, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct device_operations default_pci_ops_bus = { | struct device_operations default_pci_ops_bus = { | ||||||
| 	.read_resources   = pci_bus_read_resources, | 	.read_resources   = pci_bus_read_resources, | ||||||
| 	.set_resources    = pci_dev_set_resources, | 	.set_resources    = pci_dev_set_resources, | ||||||
| 	.enable_resources = pci_bus_enable_resources, | 	.enable_resources = pci_bus_enable_resources, | ||||||
| 	.scan_bus         = pci_scan_bridge, | 	.scan_bus         = pci_scan_bridge, | ||||||
| 	.reset_bus        = pci_bus_reset, | 	.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); | 			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. |  * 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. */ | 		/* First thing setup the device structure. */ | ||||||
| 		dev = pci_scan_get_dev(bus, devfn); | 		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. */ | 		/* See if a device is present and setup the device structure. */ | ||||||
| 		dev = pci_probe_dev(dev, bus, devfn); | 		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; | 	prev = &bus->children; | ||||||
| 	for (dev = bus->children; dev; dev = dev->sibling) { | 	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; | 			prev = &dev->sibling; | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -512,7 +512,7 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev) | |||||||
| { | { | ||||||
| 	struct resource *resource; | 	struct resource *resource; | ||||||
|  |  | ||||||
| 	// Add extra memory space | 	/* Add extra memory space */ | ||||||
| 	resource = new_resource(dev, 0x10); | 	resource = new_resource(dev, 0x10); | ||||||
| 	resource->size = CONFIG_PCIEXP_HOTPLUG_MEM; | 	resource->size = CONFIG_PCIEXP_HOTPLUG_MEM; | ||||||
| 	resource->align = 12; | 	resource->align = 12; | ||||||
| @@ -520,7 +520,7 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev) | |||||||
| 	resource->limit = 0xffffffff; | 	resource->limit = 0xffffffff; | ||||||
| 	resource->flags |= IORESOURCE_MEM; | 	resource->flags |= IORESOURCE_MEM; | ||||||
|  |  | ||||||
| 	// Add extra prefetchable memory space | 	/* Add extra prefetchable memory space */ | ||||||
| 	resource = new_resource(dev, 0x14); | 	resource = new_resource(dev, 0x14); | ||||||
| 	resource->size = CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM; | 	resource->size = CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM; | ||||||
| 	resource->align = 12; | 	resource->align = 12; | ||||||
| @@ -528,7 +528,11 @@ static void pciexp_hotplug_dummy_read_resources(struct device *dev) | |||||||
| 	resource->limit = 0xffffffffffffffff; | 	resource->limit = 0xffffffffffffffff; | ||||||
| 	resource->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; | 	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 = new_resource(dev, 0x18); | ||||||
| 	resource->size = CONFIG_PCIEXP_HOTPLUG_IO; | 	resource->size = CONFIG_PCIEXP_HOTPLUG_IO; | ||||||
| 	resource->align = 12; | 	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 | # SPDX-License-Identifier: GPL-2.0-only | ||||||
|  |  | ||||||
| config DRIVERS_AMD_PI | 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