.. SPDX-License-Identifier: GPL-2.0
amd-apml: APML interface drivers for BMC
EPYC processors from AMD provide APML interface for BMC users to monitor and configure the system parameters via the Advanced Platform Management List (APML) interface defined in EPYC processor PPR.
This chapter defines custom protocols over i2c/i3c bus
- Mailbox
- CPUID [RO]
- MCA MSR {RO]
- RMI/TSI register [RW]
module_i2c_i3c based sbrmi and sbtsi modules, which are probed as i2c or i3c client devices, depending on the platforms DTS.
https://developer.amd.com/resources/epyc-resources/epyc-specifications
APMl library provides C API fo the user space application on top of this module.
The amd apml modules are supported only on AMD Family 19h (including third-generation AMD EPYC processors (codenamed "Milan")) or later CPUs. Using the amd apml modules on earlier CPUs could produce unexpected results, and may cause the processor to operate outside of your motherboard or system specifications. Correspondingly, defaults to only executing on AMD Family 19h Model (0h ~ 1Fh & 30h ~ 3Fh) server line of processors.
Both apml_sbtsi and apml_sbrmi modules register a misc_device to provide ioctl interface to user space, allowing them to run these custom protocols.
apml_sbtsi module registers hwmon sensors for monitoring current temperature, managing max and min thresholds.
apml_sbrmi module registers hwmon sensors for monitoring power_cap_max, current power consumption and managing power_cap. It also reports per-UMC DIMM thermal sensor temperatures (TS0 and TS1) when platform firmware supports mailbox command 0x48 (SBRMI_READ_DIMM_THERMAL_SENSOR).
Each populated UMC/DDRPHY instance exposes two on-DIMM thermal sensors, TS0 and TS1. The driver reads them via the SB-RMI mailbox and publishes up to 32 hwmon temperature channels per socket:
- Channels 0-15: TS0 for UMC instances 0-15
- Channels 16-31: TS1 for UMC instances 0-15
Each channel exposes temp(N+1)_input (milli-degrees Celsius) and temp(N+1)_label in sysfs, where N is the 0-based hwmon channel index. Labels follow the pattern DIMM_TS0_UMCn and DIMM_TS1_UMCn (n = 0..15). Only channels corresponding to populated UMC instances are visible; unpopulated channels return -EINVAL.
Mode 1 DIMM_ADDRESS encoding (mailbox data byte):
- Bit[7]: 1 (Mode 1)
- Bit[6]: TS select (0 = TS0, 1 = TS1)
- Bit[3:0]: UMC/DDRPHY instance ID (0-15)
When the optional dimm-ids device-tree property is omitted, the driver derives addresses using the legacy 0x80-based encoding for all 16 UMC instances (0x80/0xC0 for UMC0 TS0/TS1, 0x81/0xC1 for UMC1, and so on). When dimm-ids is present, each entry supplies the TS0 base address for one UMC in array order; the driver sets bit[6] to form the corresponding TS1 address (TS1 = TS0 | 0x40).
The apml_sbrmi node is probed as an I2C or I3C client depending on the platform bus wiring. See Documentation/amd,sbrmi.yaml for the full devicetree binding schema.
required:
- compatible: must be "amd,sbrmi"
- reg: I2C slave address or I3C dynamic address
optional:
- dimm-ids: array of TS0 mailbox addresses, one per populated UMC instance (1..16 entries). Entry count sets how many UMC instances expose TS0 and TS1 hwmon channels.
examples:
&i3c4 { sbrmi_p0_sp8: sbrmi@0,2240000111A { reg = <0x0 0x224 0x0000111A>; assigned-address = <0x3c>; /* Sixteen populated UMC instances; alternating TS0 layout */ dimm-ids = <0x80 0x90 0x81 0x91 0x82 0x92 0x83 0x93 0x84 0x94 0x85 0x95 0x86 0x96 0x87 0x97>; }; };
/* Eight populated UMC instances on a single socket */ &i3c4 { sbrmi@0,2240000111A { reg = <0x0 0x224 0x0000111A>; assigned-address = <0x3c>; dimm-ids = <0x80 0x90 0x81 0x91 0x82 0x92 0x83 0x93>; }; };
/* Legacy encoding: omit dimm-ids to use 0x80-based addresses */ &i2c15 { sbrmi@3c { compatible = "amd,sbrmi"; reg = <0x3c>; }; };
After probe, DIMM temperatures appear under the hwmon device. Each hwmon channel N (0-based) maps to temp(N+1)_input and temp(N+1)_label in sysfs, for example:
#> cat /sys/class/hwmon/hwmonN/temp1_input /* channel 0, DIMM_TS0_UMC0 / #> cat /sys/class/hwmon/hwmonN/temp1_label #> cat /sys/class/hwmon/hwmonN/temp17_input / channel 16, DIMM_TS1_UMC0 */ #> cat /sys/class/hwmon/hwmonN/temp17_label
Kernel development packages for the running kernel need to be installed prior to building the amd apml modules. A Makefile is provided which should work with most kernel source trees.
To cross compile for arm based BMC
export CC=arm-openbmc-linux-gnueabi-gcc # Or similar export ARCH=arm KDIR=
To build the kernel module:
#> make
To install the kernel module:
#> sudo make modules_install
To clean the kernel module build directory:
#> make clean
Note: There is a fix required in the upstream linux kerenl header to handle the i3c_dev. the patch is kept in patches/ folder of this repo.
If the apml modules were installed you should use the modprobe command to load the module.
#> sudo modprobe apml_sbrmi apml_sbtsi
The apml modules can also be loaded using insmod if the module was not installed:
#> sudo insmod ./apml_sbrmi.ko #> sudo insmod ./apml_sbtsi.ko
Disclaimer: apml_alert module is currently experimental and may change in the future
EPYC processors from AMD provide APML ALERT_L for BMC users to monitor events.
|-------------------| | socket SBRMI|==== i2c/i3c bus | SBTSI|==== i2c/i3c bus | Alert_L|---- gpio line |-------------------|
APML Alert_L is asserted in multiple events:
- Machine Check Exception occurs within the system
- The processor alerts the SBI on system fatal error event
- Set by hardware as a result of a 0x71/0x72/0x73 command completion
- Set by firmware to indicate the completion of a mailbox operation
- Temperature Alert
apml_alertl module defines an interface for user space to register their PID for notifications and an ISR which identifies the source of the interrupt and signals user space application.
apml_alertl module depends on apml_sbrmi module for identifying the source.
required:
- compatible
- status
- gpios: GPIO associated with the Alert_L of the socket
- sbrmi: Array of RMI devices on the system
Example:
/ { /* Alert_L associated with socket 0 */ alertl_sock0 { compatible = "apml-alertl"; status = "okay"; gpios = <&gpio0 ASPEED_GPIO(I, 7) GPIO_ACTIVE_LOW>; sbrmi = <&sbrmi_p0_1 &sbrmi_p1_1>; };
/* Alert_L associated with socket 1 */
alertl_sock1 {
compatible = "apml-alertl";
status = "okay";
gpios = <&gpio0 ASPEED_GPIO(U, 4) GPIO_ACTIVE_LOW>;
sbrmi = <&sbrmi_p1_1 &sbrmi_p0_1>;
};
};
To install apml_alertl driver builtin as module, user can use the modprobe or insmod command
#> sudo modprobe apml_alertl
#> sudo insmod ./apml_alertl.ko
Note: Dependency on apml_sbrmi.ko module
#> sudo rmmod apml_alertl
If the driver is inbuilt can be removed/inserted by running bind/unbind command.
#> cd /sys/bus/platform/drivers/alertl #> echo alertl_rmi# > unbind/bind
User need to register the PID of the process with the apml_alertl module, by writing the PID to the debugfs entry, /sys/kernel/debug/apml_alert/ras_fatal_pid
#> echo $PID > /sys/kernel/debug/apml_alert/$alert_source
User application needs to wait for the signal from the apml_alertl module.
Signal from module carries a "struct kernel_siginfo" with following data
-
si_int: is filled with the event data [15:0] = ras_status register [23:16] = rmi static address
-
si_signo: 44
Currently the kernel driver send signal only in event, RAS status register bit set.
- In case of RAS fatal error the status register BIT(1) will set, and ISR clears the bit to avoid interfere with alerts during the ISR.
Future versions of the driver may include support for other events mentioned above.