Documentation/drivers: Add ACPI five-level fan control documentation
Document the ACPI thermal zone pattern used across multiple mainboards that implement five-level fan control via power resource state machines. This pattern is used by 9 mainboards including Google Chromebooks (beltino, jecht variants), Samsung stumpy, and Intel reference boards (wtm2, baskingridge, emeraldlake2). The documentation covers: - Power resource state machine implementation - Temperature management via PECI/SuperIO - Active and passive cooling policies - Critical FNP4._OFF no-op requirement for Windows compatibility - Implementation variations and checklist for new boards Initial framework generated by Cursor AI, heavily edited thereafter. Change-Id: I4174a4552c97fb85a894f5362948d57057cacb81 Signed-off-by: Matt DeVillier <matt.devillier@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/89843 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Sean Rhodes <sean@starlabs.systems>
This commit is contained in:
parent
47c4da36c4
commit
8cc8f219bf
2 changed files with 388 additions and 0 deletions
387
Documentation/drivers/acpi_fan_control.md
Normal file
387
Documentation/drivers/acpi_fan_control.md
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
# ACPI Active Cooling with Five-Level Fan Control
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes an ACPI-based thermal management pattern used across multiple coreboot mainboards. The implementation uses ACPI thermal zones with active cooling policies to control fan speed based on CPU temperature through a five-level power resource state machine.
|
||||
|
||||
This pattern is particularly prevalent on Intel-based mainboards using SuperIO environmental controllers for fan PWM control.
|
||||
|
||||
## Mainboards Using This Pattern
|
||||
|
||||
The following mainboards implement this five-level ACPI fan control pattern:
|
||||
|
||||
### Google Chromebooks
|
||||
- **google/beltino** - Haswell Chromebox
|
||||
- All variants (mccloud, monroe, panther, tricky, zako) use a single implementation
|
||||
- **google/jecht** - Broadwell Chromebox
|
||||
- Each variant (jecht, rikku, guado, tidus) has a unique implementation
|
||||
|
||||
### Samsung
|
||||
- **samsung/stumpy** - Sandy Bridge Chromebox
|
||||
|
||||
### Intel Reference Boards
|
||||
- **intel/wtm2** - Haswell ULT reference board
|
||||
- **intel/baskingridge** - Haswell desktop reference board
|
||||
- **intel/emeraldlake2** - Ivy Bridge reference board
|
||||
|
||||
## Architecture
|
||||
|
||||
### Hardware Components
|
||||
|
||||
Common hardware elements across implementations:
|
||||
|
||||
- **Temperature Sensor**: SuperIO TMPIN3 reads CPU temperature via PECI (Platform Environment Control Interface)
|
||||
- **Fan Controller**: SuperIO Environmental Controller (ENVC) with PWM output
|
||||
- Fan2 (F2PS) on Google Chromebooks
|
||||
- Fan3 (F3PS) on Intel/Samsung boards
|
||||
- **CPU**: Provides temperature data as an offset from Tj_max (maximum junction temperature)
|
||||
|
||||
### ACPI Components
|
||||
|
||||
- **Thermal Zone**: `\_TZ.THRM` - Main thermal management zone
|
||||
- **Fan Devices**: `FAN0` through `FAN4` - Five fan speed levels
|
||||
- **Power Resources**: `FNP0` through `FNP4` - Control fan state transitions
|
||||
- **Active Cooling Levels**: `_AC0` through `_AC4` - Temperature thresholds for each fan level
|
||||
|
||||
## Fan Speed Levels
|
||||
|
||||
The system implements **5 fan speed levels** (0-4), where:
|
||||
|
||||
- **Level 0**: Maximum fan speed (highest cooling, activated at highest temperature)
|
||||
- **Level 1**: High fan speed
|
||||
- **Level 2**: Medium fan speed
|
||||
- **Level 3**: Low fan speed
|
||||
- **Level 4**: Minimum fan speed (idle/baseline cooling, default state)
|
||||
|
||||
The system starts at **Level 4** (minimum speed) and increases to lower-numbered levels as temperature rises.
|
||||
|
||||
## Temperature Management
|
||||
|
||||
### Temperature Reading Process
|
||||
|
||||
1. **Raw PECI Reading**: Read from `\_SB.PCI0.LPCB.SIO.ENVC.TIN3`
|
||||
- Returns a value representing offset from Tj_max
|
||||
- Value 0x80 indicates "no reading available"
|
||||
- Values 0 or 255 are invalid
|
||||
|
||||
2. **Temperature Calculation**:
|
||||
```
|
||||
actual_temperature = Tj_max - (255 - raw_value)
|
||||
```
|
||||
|
||||
3. **Conversion to ACPI Format**: Convert from Celsius to deci-Kelvin:
|
||||
```
|
||||
deci_kelvin = (celsius * 10) + 2732
|
||||
```
|
||||
|
||||
### Critical Temperature Handling
|
||||
|
||||
Most implementations include safety logic in the `_TMP` method:
|
||||
- If temperature reaches `\TMAX` (Tj_max), logs a critical event
|
||||
- Waits 1 second for sensor re-poll
|
||||
- Re-reads temperature to confirm
|
||||
- Reports the current temperature to the OS
|
||||
|
||||
### Temperature Thresholds
|
||||
|
||||
Thresholds are defined in board-specific headers (typically `thermal.h`):
|
||||
|
||||
- `FAN0_THRESHOLD_ON/OFF` - Trigger points for maximum fan speed
|
||||
- `FAN1_THRESHOLD_ON/OFF` - Trigger points for high fan speed
|
||||
- `FAN2_THRESHOLD_ON/OFF` - Trigger points for medium fan speed
|
||||
- `FAN3_THRESHOLD_ON/OFF` - Trigger points for low fan speed
|
||||
- `FAN4_PWM` - PWM value for minimum fan speed
|
||||
|
||||
Each level has hysteresis (different ON/OFF thresholds) to prevent rapid cycling.
|
||||
|
||||
## Active Cooling Implementation
|
||||
|
||||
### Active Cooling Methods (`_ACx`)
|
||||
|
||||
Each `_ACx` method returns a temperature threshold:
|
||||
- When system temperature **exceeds** the threshold, the OS activates the corresponding fan level
|
||||
- When temperature **falls below** the threshold, the OS may deactivate that level
|
||||
|
||||
**Hysteresis Logic**:
|
||||
```
|
||||
Method (_AC0) {
|
||||
If (\FLVL <= 0) {
|
||||
Return (CTOK (FAN0_THRESHOLD_OFF)) // Higher temp to turn off
|
||||
} Else {
|
||||
Return (CTOK (FAN0_THRESHOLD_ON)) // Lower temp to turn on
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This prevents oscillation by requiring different temperatures to activate vs. deactivate a fan level.
|
||||
|
||||
### Active Cooling Lists (`_ALx`)
|
||||
|
||||
Each `_ALx` associates a cooling threshold with a fan device:
|
||||
```
|
||||
Name (_AL0, Package () { FAN0 }) // FAN0 activated at _AC0 threshold
|
||||
Name (_AL1, Package () { FAN1 }) // FAN1 activated at _AC1 threshold
|
||||
Name (_AL2, Package () { FAN2 }) // FAN2 activated at _AC2 threshold
|
||||
Name (_AL3, Package () { FAN3 }) // FAN3 activated at _AC3 threshold
|
||||
Name (_AL4, Package () { FAN4 }) // FAN4 activated at _AC4 threshold
|
||||
```
|
||||
|
||||
## Power Resource State Machine
|
||||
|
||||
Each fan level has an associated power resource (`FNP0` through `FNP4`) that manages state transitions.
|
||||
|
||||
### State Transitions
|
||||
|
||||
**FNP0-FNP3** (Levels 0-3):
|
||||
- `_STA`: Returns 1 (ON) if `\FLVL <= level`, else 0 (OFF)
|
||||
- `_ON`: Transitions **to** this level from a higher-numbered level
|
||||
- `_OFF`: Transitions **away** from this level to the next higher-numbered level
|
||||
|
||||
Example for FNP0 (maximum cooling):
|
||||
```
|
||||
PowerResource (FNP0, 0, 0) {
|
||||
Method (_STA) {
|
||||
If (\FLVL <= 0) { Return (1) } // Active if at max cooling
|
||||
Else { Return (0) }
|
||||
}
|
||||
Method (_ON) {
|
||||
If (!_STA ()) { // If not already active
|
||||
\FLVL = 0 // Set to max cooling
|
||||
\_SB.PCI0.LPCB.SIO.ENVC.F2PS = FAN0_PWM // Set fan PWM
|
||||
Notify (\_TZ.THRM, 0x81) // Notify thermal zone
|
||||
}
|
||||
}
|
||||
Method (_OFF) {
|
||||
If (_STA ()) { // If currently active
|
||||
\FLVL = 1 // Transition to level 1
|
||||
\_SB.PCI0.LPCB.SIO.ENVC.F2PS = FAN1_PWM // Set corresponding PWM
|
||||
Notify (\_TZ.THRM, 0x81) // Notify thermal zone
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**FNP4** (Minimum cooling - Level 4):
|
||||
- `_STA`: Returns 1 if `\FLVL <= 4` (always true in normal operation)
|
||||
- `_ON`: Transitions to minimum cooling state
|
||||
- `_OFF`: **No-op** - This is the minimum state, cannot transition lower
|
||||
|
||||
### Critical: FNP4._OFF Must Be a No-Op
|
||||
|
||||
This is **essential for Windows compatibility**:
|
||||
|
||||
**Problem**: Early implementations had `_OFF` setting `\FLVL = 4` and PWM values, identical to `_ON`. This violated ACPI power resource requirements:
|
||||
- After `_ON` is called, `_STA` must eventually return 1 (ON)
|
||||
- After `_OFF` is called, `_STA` must eventually return 0 (OFF)
|
||||
|
||||
Since both methods resulted in `\FLVL = 4`, and `_STA` returns 1 when `\FLVL <= 4`, the power resource could never properly transition to OFF state.
|
||||
|
||||
**Solution**: Make `_OFF` a no-op since FAN4 is the minimum cooling state:
|
||||
```
|
||||
PowerResource (FNP4, 0, 0) {
|
||||
Method (_STA) {
|
||||
If (\FLVL <= 4) { Return (1) }
|
||||
Else { Return (0) }
|
||||
}
|
||||
Method (_ON) {
|
||||
If (!_STA ()) {
|
||||
\FLVL = 4
|
||||
\_SB.PCI0.LPCB.SIO.ENVC.F2PS = FAN4_PWM
|
||||
Notify (\_TZ.THRM, 0x81)
|
||||
}
|
||||
}
|
||||
Method (_OFF) {
|
||||
// FAN4 is the minimum cooling state (idle/lowest fan speed)
|
||||
// There is no lower state to transition to, so _OFF is a no-op
|
||||
// to maintain proper ACPI power resource state machine semantics
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This maintains proper ACPI state machine semantics and ensures Windows compatibility while maintaining Linux compatibility.
|
||||
|
||||
## Passive Cooling
|
||||
|
||||
In addition to active (fan-based) cooling, most implementations support passive cooling:
|
||||
|
||||
- `_PSV`: Returns passive cooling threshold temperature
|
||||
- `_PSL`: Returns list of processors to throttle
|
||||
- `_TC1`, `_TC2`: Thermal constants for passive cooling algorithm
|
||||
- `_TSP`: Thermal sampling period (typically 20 deciseconds = 2 seconds)
|
||||
|
||||
When temperature exceeds `_PSV` threshold, the OS throttles CPUs listed in `_PSL` to reduce heat generation.
|
||||
|
||||
## Polling and Notification
|
||||
|
||||
- `_TZP`: Thermal zone polling period (typically 100 deciseconds = 10 seconds)
|
||||
- OS polls `_TMP` at this interval to check temperature
|
||||
|
||||
- `Notify (\_TZ.THRM, 0x81)`: Thermal zone change notification
|
||||
- Sent whenever fan level changes
|
||||
- Tells OS to re-evaluate thermal zone immediately
|
||||
|
||||
## Initialization
|
||||
|
||||
The `_INI` method runs when the thermal zone is initialized:
|
||||
|
||||
```
|
||||
Method (_INI) {
|
||||
\FLVL = 4 // Start at minimum cooling
|
||||
\_SB.PCI0.LPCB.SIO.ENVC.F2PS = FAN4_PWM // Set initial fan PWM
|
||||
Notify (\_TZ.THRM, 0x81) // Initial notification
|
||||
}
|
||||
```
|
||||
|
||||
## Operating System Interaction
|
||||
|
||||
### Thermal Policy Flow
|
||||
|
||||
1. **OS boots** → Executes `_INI` → Fan starts at Level 4
|
||||
2. **OS polls `_TMP`** periodically → Gets current temperature
|
||||
3. **Temperature rises** → Exceeds `_AC3` threshold
|
||||
4. **OS calls `FAN3._ON`** → Power resource FNP3 activated → `\FLVL = 3`
|
||||
5. **Temperature continues rising** → Exceeds `_AC2` threshold
|
||||
6. **OS calls `FAN2._ON`** → Power resource FNP2 activated → `\FLVL = 2`
|
||||
7. **Temperature drops** → Falls below `_AC2` threshold
|
||||
8. **OS calls `FAN2._OFF`** → `\FLVL = 3` → Returns to Level 3
|
||||
9. **Cycle continues** based on temperature changes
|
||||
|
||||
### Critical Temperature
|
||||
|
||||
If temperature reaches `_CRT` threshold:
|
||||
- OS initiates emergency shutdown
|
||||
- Prevents hardware damage from overheating
|
||||
|
||||
## Global Variables
|
||||
|
||||
Standard variables used across implementations:
|
||||
|
||||
- `\FLVL`: Current fan level (0-4)
|
||||
- `\TMAX`: Maximum junction temperature (Tj_max)
|
||||
- `\TCRT`: Critical shutdown temperature
|
||||
- `\TPSV`: Passive cooling threshold temperature
|
||||
|
||||
## Configuration
|
||||
|
||||
Fan thresholds and PWM values are defined in board-specific headers, typically:
|
||||
```
|
||||
src/mainboard/<vendor>/<board>/include/thermal.h
|
||||
```
|
||||
or
|
||||
```
|
||||
src/mainboard/<vendor>/<board>/variants/<variant>/include/variant/thermal.h
|
||||
```
|
||||
|
||||
Example configuration:
|
||||
```c
|
||||
#define FAN0_THRESHOLD_ON 75 // Temperature to activate max fan (°C)
|
||||
#define FAN0_THRESHOLD_OFF 65 // Temperature to deactivate max fan (°C)
|
||||
#define FAN0_PWM 0xFF // PWM duty cycle value (max)
|
||||
|
||||
#define FAN1_THRESHOLD_ON 65
|
||||
#define FAN1_THRESHOLD_OFF 55
|
||||
#define FAN1_PWM 0xC0
|
||||
|
||||
#define FAN2_THRESHOLD_ON 55
|
||||
#define FAN2_THRESHOLD_OFF 45
|
||||
#define FAN2_PWM 0x80
|
||||
|
||||
#define FAN3_THRESHOLD_ON 45
|
||||
#define FAN3_THRESHOLD_OFF 35
|
||||
#define FAN3_PWM 0x40
|
||||
|
||||
#define FAN4_PWM 0x20 // Idle fan speed
|
||||
```
|
||||
|
||||
## Implementation Variations
|
||||
|
||||
While the core pattern is consistent, there are some variations:
|
||||
|
||||
### PWM Output Selection
|
||||
- **Google boards**: Use Fan2 PWM (`F2PS`)
|
||||
- **Intel/Samsung boards**: Use Fan3 PWM (`F3PS`)
|
||||
|
||||
### Guard Checks
|
||||
Some implementations wrap state changes with `_STA()` checks:
|
||||
```
|
||||
Method (_ON) {
|
||||
If (!_STA ()) { // Only change state if not already active
|
||||
// ... state change
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Others omit the guard and always perform the state change.
|
||||
|
||||
### Temperature Reading
|
||||
- Most implementations read from SuperIO TMPIN3 via PECI
|
||||
- Some (like intel/wtm2) use simplified stub implementations for reference
|
||||
|
||||
### Dynamic Thermal Tables
|
||||
The google/jecht/tidus variant includes multiple thermal tables that can be switched based on system temperature sensors, allowing more sophisticated thermal management.
|
||||
|
||||
## Compatibility Notes
|
||||
|
||||
### Linux
|
||||
- More lenient ACPI parser
|
||||
- Tolerates minor state machine violations
|
||||
- Worked with buggy FNP4._OFF implementations
|
||||
|
||||
### Windows
|
||||
- Stricter ACPI compliance checking
|
||||
- Requires proper power resource state machine behavior
|
||||
- **Requires the FNP4._OFF no-op fix** to function correctly
|
||||
- May disable thermal zone entirely if ACPI violations detected
|
||||
|
||||
## Debugging
|
||||
|
||||
To debug fan control issues:
|
||||
|
||||
1. **Check ACPI errors**: Look for thermal zone errors in OS logs
|
||||
- Linux: `dmesg | grep -i acpi` or check `/sys/class/thermal/`
|
||||
- Windows: Event Viewer → System → ACPI errors
|
||||
|
||||
2. **Monitor temperature**: Use OS tools to check `_TMP` readings
|
||||
- Linux: `/sys/class/thermal/thermal_zone*/temp`
|
||||
- Windows: HWiNFO64, HWMonitor
|
||||
|
||||
3. **Check fan level**: Monitor `\FLVL` value (ACPI debugger or custom logging)
|
||||
|
||||
4. **Verify thresholds**: Ensure threshold values are appropriate for the hardware
|
||||
|
||||
5. **Test state transitions**: Verify each fan level activates at correct temperature
|
||||
|
||||
6. **ACPI table inspection**: Decompile DSDT/SSDT tables with `acpidump` and `iasl` to verify implementation
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
When implementing this pattern on a new board:
|
||||
|
||||
- [ ] Define all 5 fan threshold pairs (ON/OFF) with appropriate hysteresis
|
||||
- [ ] Define PWM values for all 5 fan levels
|
||||
- [ ] Implement temperature sensor reading (typically PECI via SuperIO)
|
||||
- [ ] Implement CTOK conversion method (°C to deci-Kelvin)
|
||||
- [ ] Create all 5 PowerResource objects (FNP0-FNP4)
|
||||
- [ ] **Critical**: Ensure FNP4._OFF is a no-op (not setting state)
|
||||
- [ ] Create all 5 Fan Device objects (FAN0-FAN4) with correct `_PR0` references
|
||||
- [ ] Implement _ACx methods with hysteresis logic
|
||||
- [ ] Define _ALx packages linking to fan devices
|
||||
- [ ] Implement _INI to set initial state
|
||||
- [ ] Implement _TMP with error handling
|
||||
- [ ] Define _CRT, _PSV, _PSL for critical/passive cooling
|
||||
- [ ] Set appropriate _TZP polling interval
|
||||
- [ ] Test on both Linux and Windows
|
||||
|
||||
## References
|
||||
|
||||
- [ACPI Specification 6.5 - Thermal Management](https://uefi.org/specs/ACPI/6.5/)
|
||||
- [ACPI Specification - ThermalZone Objects](https://uefi.org/specs/ACPI/6.5/11_Thermal_Management.html)
|
||||
- [ACPI Specification - PowerResource Objects](https://uefi.org/specs/ACPI/6.5/07_Power_and_Performance_Mgmt.html#power-resources)
|
||||
- Intel PECI Specification
|
||||
- SuperIO vendor datasheets (ITE, Nuvoton, Winbond, etc.)
|
||||
|
||||
## See Also
|
||||
|
||||
- [Intel DPTF](dptf.md) - More sophisticated Intel Dynamic Platform and Thermal Framework
|
||||
- [ACPI GPIO Documentation](../acpi/gpio.md)
|
||||
|
||||
|
|
@ -18,6 +18,7 @@ Some of the drivers currently available include:
|
|||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
ACPI Five-Level Fan Control <acpi_fan_control.md>
|
||||
CFR <cfr.md>
|
||||
CFR use within coreboot <cfr_internal.md>
|
||||
Intel DPTF <dptf.md>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue