From 4c07dfc8954f8f17757eaa150c9010181a6b46bb Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Fri, 13 Jun 2025 12:54:18 +0530 Subject: [PATCH 01/12] misc: amd-apml: Create common header for sbtsi device Move out the sbtsi device structure to a common header file and add device matching helper function for I2C and I3C buses. This is required to add support for the APML Alert_L module which needs access to sbtsi device struct and device matching capability. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta --- drivers/misc/amd-apml/apml_sbtsi.c | 31 +++++++++++++++++++--------- drivers/misc/amd-apml/sbtsi-common.h | 27 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 drivers/misc/amd-apml/sbtsi-common.h diff --git a/drivers/misc/amd-apml/apml_sbtsi.c b/drivers/misc/amd-apml/apml_sbtsi.c index 02e040f352d99b..d0fcb507de8634 100644 --- a/drivers/misc/amd-apml/apml_sbtsi.c +++ b/drivers/misc/amd-apml/apml_sbtsi.c @@ -23,9 +23,9 @@ #include #include #include - #include +#include "sbtsi-common.h" /* * SB-TSI registers only support SMBus byte data access. "_INT" registers are * the integer part of a temperature value or limit, and "_DEC" registers are @@ -69,15 +69,6 @@ #define SBTSI_DEC_OFFSET 5 #define SBTSI_DEC_MASK 0x7 -struct apml_sbtsi_device { - struct miscdevice sbtsi_misc_dev; - struct i2c_client *client; - struct i3c_device *i3cdev; - struct regmap *regmap; - struct mutex lock; - u8 dev_static_addr; -} __packed; - /* * From SB-TSI spec: CPU temperature readings and limit registers encode the * temperature in increments of 0.125 from 0 to 255.875. The "high byte" @@ -666,6 +657,26 @@ static struct i2c_driver sbtsi_driver = { module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver) +int sbtsi_match_i3c(struct device *dev, const void *data) +{ + const struct device_node *node = (const struct device_node *)data; + + if (dev->of_node == node && dev->driver == &sbtsi_i3c_driver.driver) + return 1; + return 0; +} +EXPORT_SYMBOL_GPL(sbtsi_match_i3c); + +int sbtsi_match_i2c(struct device *dev, const void *data) +{ + const struct device_node *node = (const struct device_node *)data; + + if (dev->of_node == node && dev->driver == &sbtsi_driver.driver) + return 1; + return 0; +} +EXPORT_SYMBOL_GPL(sbtsi_match_i2c); + MODULE_AUTHOR("Kun Yi "); MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/misc/amd-apml/sbtsi-common.h b/drivers/misc/amd-apml/sbtsi-common.h new file mode 100644 index 00000000000000..4fc3a33e8639fa --- /dev/null +++ b/drivers/misc/amd-apml/sbtsi-common.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * sbtsi-common.h - hwmon driver for a SBI Temperature Sensor Interface (SB-TSI) + * compliant AMD SoC temperature device. + * Also register to misc driver with an IOCTL. + * + * Copyright (c) 2020, Google Inc. + * Copyright (c) 2020, Kun Yi + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _AMD_SBTSI_COMMON_H_ +#define _AMD_SBTSI_COMMON_H_ + +struct apml_sbtsi_device { + struct miscdevice sbtsi_misc_dev; + struct i2c_client *client; + struct i3c_device *i3cdev; + struct regmap *regmap; + struct mutex lock; //lock for tsi devices + u8 dev_static_addr; +} __packed; + +int sbtsi_match_i2c(struct device *dev, const void *data); +int sbtsi_match_i3c(struct device *dev, const void *data); + +#endif From 8bdabd67e4150fb006e0260772036050716f9e3b Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Fri, 13 Jun 2025 12:53:26 +0530 Subject: [PATCH 02/12] misc: amd-apml: Add support for AMD APML Alert_L platform driver Processors from AMD provide APML ALERT_L for BMC users to monitor events. 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 - High/Low Temperature Alert APML Alert_L module define uevents to notify registered userspace processes of the alert event. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Signed-off-by: sathya priya kumar --- drivers/misc/amd-apml/Kconfig | 16 ++ drivers/misc/amd-apml/Makefile | 1 + drivers/misc/amd-apml/apml_alertl.c | 380 ++++++++++++++++++++++++++++ drivers/misc/amd-apml/apml_alertl.h | 27 ++ include/uapi/linux/amd-apml.h | 32 ++- 5 files changed, 452 insertions(+), 4 deletions(-) create mode 100644 drivers/misc/amd-apml/apml_alertl.c create mode 100644 drivers/misc/amd-apml/apml_alertl.h diff --git a/drivers/misc/amd-apml/Kconfig b/drivers/misc/amd-apml/Kconfig index 6fed94971b0fb1..8a998afcd83e0f 100644 --- a/drivers/misc/amd-apml/Kconfig +++ b/drivers/misc/amd-apml/Kconfig @@ -24,3 +24,19 @@ config APML_SBTSI This driver can also be built as a module. If so, the module will be called apml_sbtsi. + +config APML_ALERTL + tristate "Emulated apml alertl interface driver over i3c bus" + depends on APML_SBRMI && APML_SBTSI + default n + help + This driver provides support for AMD APML Alert_L interface, + enabling alert notification from AMD SoCs to a BMC (Baseboard + Management Controller) over I2C/I3C bus. + + The driver monitors RAS and thermal events by handling alerts + from SBRMI and SBTSI devices, and reports these events to userspace + via uevents. Device configuration is managed via device tree bindings. + + This driver can also be built as a module. If so, the module will + be called apml_alertl.ko. diff --git a/drivers/misc/amd-apml/Makefile b/drivers/misc/amd-apml/Makefile index 7eef318915f122..864ea61baf6d2a 100644 --- a/drivers/misc/amd-apml/Makefile +++ b/drivers/misc/amd-apml/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_APML_SBRMI) += sbrmi.o sbrmi-common.o obj-$(CONFIG_APML_SBTSI) += apml_sbtsi.o +obj-$(CONFIG_APML_ALERTL) += apml_alertl.o diff --git a/drivers/misc/amd-apml/apml_alertl.c b/drivers/misc/amd-apml/apml_alertl.c new file mode 100644 index 00000000000000..8d8abee18a9bd6 --- /dev/null +++ b/drivers/misc/amd-apml/apml_alertl.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * apml_alertl.c - Alert_L driver for AMD APML devices + * + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apml_alertl.h" + +#define DRIVER_NAME "apml_alertl" + +#define RAS_STATUS_REG 0x4C +#define RMI_STATUS_REG 0x2 +#define TSI_STATUS_REG 0x2 +#define RAS_ALERT_STATUS BIT(1) +#define RAS_ALERT_ASYNC BIT(3) + +#define MAX_SOC_LEN 11 +#define MAX_ERR_LEN 18 + +/* SBRMI and SBTSI static address for socket 0 and 1 */ +#define RMI_SOCK_0_DIE_0 0x3c +#define TSI_SOCK_0_DIE_0 0x4c +#define RMI_SOCK_1_DIE_0 0x38 +#define TSI_SOCK_1_DIE_0 0x48 + +MODULE_ALIAS("apml_alertl:" DRIVER_NAME); + +/* Map static address to socket and die index */ +static u8 static_addr_to_socket(u8 static_addr) +{ + /* + * [3:0] = Socket Index + * [7:4] = die Index + * Mapping: + * 0x3c, 0x4c -> Socket 0, die 0, + * 0x38, 0x48 -> Socket 1, die 0, + */ + switch (static_addr) { + case RMI_SOCK_0_DIE_0: + case TSI_SOCK_0_DIE_0: + return 0; + case RMI_SOCK_1_DIE_0: + case TSI_SOCK_1_DIE_0: + return 1; + default: + return 0xFF; + } +} + +/* Send a uevent to userspace for an APML alert */ +static int send_uevent(u8 static_address, u32 alert_src, struct device *dev) +{ + u8 soc_die_num; + char sock[MAX_SOC_LEN]; + char src[MAX_ERR_LEN]; + char *alert_source[] = { sock, src, NULL }; + + soc_die_num = static_addr_to_socket(static_address); + if (soc_die_num == 0xFF) + return -ENODEV; + + snprintf(sock, sizeof(sock), "Socket=0x%x", soc_die_num); + snprintf(src, sizeof(src), "Source=0x%x", alert_src); + + dev_dbg(dev, "Sending uevent: Sock:0x%x Src:0x%x\n", + soc_die_num, alert_src); + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, alert_source); + return 0; +} + +/* Process and handle TSI alerts for all TSI devices */ +static void handle_tsi_alerts(struct apml_alertl_data *oob_adata) +{ + struct device *dev = oob_adata->dev; + struct apml_message msg = { 0 }; + int temp_status, ret, i; + + for (i = 0; i < oob_adata->num_of_tsi_devs; i++) { + temp_status = 0; + if (!oob_adata->tsi_dev[i] || !oob_adata->tsi_dev[i]->regmap) { + dev_dbg(dev, + "TSI device at index %d is NULL or regmap missing\n", + i); + continue; + } + + /* Read TSI Status register to identify the RAS error */ + msg.data_in.reg_in[REG_OFF_INDEX] = TSI_STATUS_REG; + + mutex_lock(&oob_adata->tsi_dev[i]->lock); + ret = regmap_read(oob_adata->tsi_dev[i]->regmap, + msg.data_in.reg_in[REG_OFF_INDEX], + &temp_status); + mutex_unlock(&oob_adata->tsi_dev[i]->lock); + + if (ret < 0) { + dev_dbg(dev, + "Failed to read temperature status of TSI device index %d\n", + i); + continue; + } + + if (!temp_status) + continue; + + ret = send_uevent(oob_adata->tsi_dev[i]->dev_static_addr, + temp_status << 24, dev); + if (ret) + dev_dbg(dev, + "Failed to send uevent for temperature alert TSI device index %d Err: %d\n", + i, ret); + } +} + +/* Process and handle RMI alerts for all RMI devices */ +static void handle_rmi_alerts(struct apml_alertl_data *oob_adata) +{ + struct device *dev = oob_adata->dev; + struct apml_message msg = { 0 }; + int ras_status, ret, i; + + for (i = 0; i < oob_adata->num_of_rmi_devs; i++) { + ras_status = 0; + if (!oob_adata->rmi_dev[i] || !oob_adata->rmi_dev[i]->regmap) { + dev_dbg(dev, + "RMI device at index %d is NULL or regmap missing\n", + i); + continue; + } + + /* Read RAS Status register to identify the RAS error */ + msg.data_in.reg_in[REG_OFF_INDEX] = RAS_STATUS_REG; + + mutex_lock(&oob_adata->rmi_dev[i]->lock); + ret = regmap_read(oob_adata->rmi_dev[i]->regmap, + msg.data_in.reg_in[REG_OFF_INDEX], + &ras_status); + mutex_unlock(&oob_adata->rmi_dev[i]->lock); + + if (ret < 0) { + dev_dbg(dev, "Failed to read RAS status of RMI device index %d\n", i); + continue; + } + + if (!ras_status) + continue; + + ret = send_uevent(oob_adata->rmi_dev[i]->dev_static_addr, ras_status, dev); + if (ret) + dev_dbg(dev, + "Failed to send uevent for RAS alert for device %d Err: %d\n", + i, ret); + + /* Clear the RMI Status and RAS Status register 0x4C */ + mutex_lock(&oob_adata->rmi_dev[i]->lock); + msg.data_in.reg_in[REG_OFF_INDEX] = RAS_STATUS_REG; + ret = regmap_write(oob_adata->rmi_dev[i]->regmap, + msg.data_in.reg_in[REG_OFF_INDEX], + ras_status); + if (ret < 0) + dev_dbg(dev, + "Could not clear RAS status register for device %d\n", + i); + + msg.data_in.reg_in[REG_OFF_INDEX] = RMI_STATUS_REG; + ret = regmap_write(oob_adata->rmi_dev[i]->regmap, + msg.data_in.reg_in[REG_OFF_INDEX], + RAS_ALERT_ASYNC); + mutex_unlock(&oob_adata->rmi_dev[i]->lock); + if (ret < 0) + dev_dbg(dev, + "Could not clear RMI status register at device %d\n", + i); + } +} + +/* Handles Alert_L interrupts by delegating to TSI and RMI alert handlers */ +static irqreturn_t alert_l_irq_thread_handler(int irq, void *dev_id) +{ + struct apml_alertl_data *oob_adata = (struct apml_alertl_data *)dev_id; + struct device *dev; + + dev = oob_adata->dev; + + handle_tsi_alerts(oob_adata); + handle_rmi_alerts(oob_adata); + + return IRQ_HANDLED; +} + +/* Retrieve APML device from device tree */ +static void *get_apml_dev_byphandle(struct device_node *dnode, + const char *phandle_name, + int index) +{ + struct device_node *d_node; + struct device *dev; + void *apml_dev; + + if (!phandle_name || !dnode) + return NULL; + + d_node = of_parse_phandle(dnode, phandle_name, index); + if (IS_ERR_OR_NULL(d_node)) { + pr_err("Failed to parse phandle '%s' at index %d\n", + phandle_name, index); + return NULL; + } + + if (strcmp(phandle_name, "sbrmi") == 0) { + dev = bus_find_device(&i3c_bus_type, NULL, d_node, sbrmi_match_i3c); + if (!dev) { + dev = bus_find_device(&i2c_bus_type, NULL, d_node, sbrmi_match_i2c); + if (IS_ERR_OR_NULL(dev)) { + of_node_put(d_node); + return NULL; + } + } + } else if (strcmp(phandle_name, "sbtsi") == 0) { + dev = bus_find_device(&i3c_bus_type, NULL, d_node, sbtsi_match_i3c); + if (!dev) { + dev = bus_find_device(&i2c_bus_type, NULL, d_node, sbtsi_match_i2c); + if (IS_ERR_OR_NULL(dev)) { + of_node_put(d_node); + return NULL; + } + } + } + + of_node_put(d_node); + apml_dev = dev_get_drvdata(dev); + if (IS_ERR_OR_NULL(apml_dev)) + return NULL; + + return apml_dev; +} + +static int apml_alertl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dnode = dev->of_node; + struct apml_sbrmi_device **rmi_dev; + struct apml_sbtsi_device **tsi_dev; + struct apml_alertl_data *oob_alert; + struct gpio_desc *alertl_gpiod; + char *irq_name; + u32 irq_num; + u8 socket_num; + int ret, i; + + /* Allocate memory to oob_alert_data structure */ + oob_alert = devm_kzalloc(dev, sizeof(struct apml_alertl_data), + GFP_KERNEL); + if (!oob_alert) + return -ENOMEM; + + /* identify the number of devices associated with each RMI alert */ + oob_alert->num_of_rmi_devs = of_property_count_elems_of_size(dnode, "sbrmi", + sizeof(phandle)); + + /* identify the number of devices associated with each TSI alert */ + oob_alert->num_of_tsi_devs = of_property_count_elems_of_size(dnode, "sbtsi", + sizeof(phandle)); + + /* Allocate memory as per the number of RMI devices */ + rmi_dev = devm_kzalloc(dev, oob_alert->num_of_rmi_devs * sizeof(struct apml_sbrmi_device), + GFP_KERNEL); + if (!rmi_dev) + return -ENOMEM; + oob_alert->rmi_dev = rmi_dev; + + /* Allocate memory as per the number of TSI devices */ + tsi_dev = devm_kzalloc(dev, oob_alert->num_of_tsi_devs * sizeof(struct apml_sbtsi_device), + GFP_KERNEL); + if (!tsi_dev) + return -ENOMEM; + + oob_alert->tsi_dev = tsi_dev; + oob_alert->dev = dev; + + /* + * For each of the Alerts get the device associated + * Currently the ALert_L driver identification is only supported + * over I3C. We can add property in dts to identify the bus type + */ + for (i = 0; i < oob_alert->num_of_rmi_devs; i++) { + rmi_dev[i] = get_apml_dev_byphandle(pdev->dev.of_node, "sbrmi", i); + if (!rmi_dev[i]) { + dev_err(dev, "RMI device %d not found\n", i); + return -ENODEV; + } + } + + for (i = 0; i < oob_alert->num_of_tsi_devs; i++) { + tsi_dev[i] = get_apml_dev_byphandle(pdev->dev.of_node, "sbtsi", i); + if (!tsi_dev[i]) { + dev_err(dev, "TSI device %d not found\n", i); + return -ENODEV; + } + } + + /* Get the alert_l gpios, irq_number for the GPIO and register ISR*/ + alertl_gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN); + if (IS_ERR(alertl_gpiod)) { + dev_err(&pdev->dev, "Unable to retrieve gpio\n"); + return PTR_ERR(alertl_gpiod); + } + + irq_num = gpiod_to_irq(alertl_gpiod); + if (irq_num < 0) { + dev_err(dev, "No corresponding IRQ for GPIO, error: %d\n", irq_num); + return irq_num; + } + + if (oob_alert->num_of_rmi_devs > 0 && oob_alert->rmi_dev[0]) + socket_num = static_addr_to_socket(oob_alert->rmi_dev[0]->dev_static_addr); + else if (oob_alert->num_of_tsi_devs > 0 && oob_alert->tsi_dev[0]) + socket_num = static_addr_to_socket(oob_alert->tsi_dev[0]->dev_static_addr); + + irq_name = devm_kasprintf(dev, GFP_KERNEL, "apml_irq%u", socket_num); + if (!irq_name) { + dev_dbg(dev, "Failed to allocate IRQ name\n"); + return -ENOMEM; + } + + dev_dbg(dev, "Register IRQ:%u\n", irq_num); + ret = devm_request_threaded_irq(dev, irq_num, + NULL, + (void *)alert_l_irq_thread_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + irq_name, oob_alert); + if (ret) { + dev_dbg(dev, "Cannot register IRQ:%u\n", irq_num); + return ret; + } + + /* Set the platform data to pdev */ + platform_set_drvdata(pdev, oob_alert); + + return 0; +} + +static int apml_alertl_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id apml_alertl_dt_ids[] = { + {.compatible = "apml-alertl", }, + {}, +}; +MODULE_DEVICE_TABLE(of, apml_alertl_dt_ids); + +static struct platform_driver apml_alertl_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(apml_alertl_dt_ids), + }, + .probe = apml_alertl_probe, + .remove = apml_alertl_remove, +}; + +module_platform_driver(apml_alertl_driver); + +MODULE_AUTHOR("Akshay Gupta "); +MODULE_AUTHOR("Naveenkrishna Chatradhi "); +MODULE_DESCRIPTION("AMD APML ALERT_L Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/amd-apml/apml_alertl.h b/drivers/misc/amd-apml/apml_alertl.h new file mode 100644 index 00000000000000..37568e8b9f5834 --- /dev/null +++ b/drivers/misc/amd-apml/apml_alertl.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _AMD_APML_ALERT_L__ +#define _AMD_APML_ALERT_L__ + +#include "sbrmi-common.h" +#include "sbtsi-common.h" + +/* APML_ALERTL Sends uevent to userspace for temparature + * alert and RAS (fatal and non-fatal) error, including + * environmental data such as socket/die and alertl source + * information. + */ + +struct apml_alertl_data { + struct apml_sbrmi_device **rmi_dev; + struct apml_sbtsi_device **tsi_dev; + struct device *dev; + atomic_t removed; + u8 num_of_rmi_devs; + u8 num_of_tsi_devs; +} __packed; + +#endif /*_AMD_APML_ALERT_L__*/ diff --git a/include/uapi/linux/amd-apml.h b/include/uapi/linux/amd-apml.h index d52f3a172faf83..cfcafe555b0383 100644 --- a/include/uapi/linux/amd-apml.h +++ b/include/uapi/linux/amd-apml.h @@ -1,17 +1,41 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* - * Copyright (C) 2021-2022 Advanced Micro Devices, Inc. + * Copyright (C) 2025 Advanced Micro Devices, Inc. */ #ifndef _AMD_APML_H_ #define _AMD_APML_H_ #include +#ifndef BIT +#define BIT(n) (1U << (n)) +#endif + /* - * Currently signal 33 to 64 are unused, - * using user signal number from that range + * APML RAS and Temperature alert source. Uevent + * returns 32 bit value which is sent to userspace. + * [23:0]: defined for RAS Alerts + * SBRMI RAS register 0x4C. RAS alert register bits(0-6) + * + * [31:24] : defined for Temperature Alerts + * SBTSI Temp register 0x02. socket temperature alert bits(3 & 4) + * + * Note: Additional Alert_L bit definition may be added in future + * for RAS alert extension + * */ -#define USR_SIGNAL 44 + +enum apml_alert_src { + APML_FATAL_ALERT = BIT(0), + APML_FCH_ALERT = BIT(1), + APML_RESET_CTRL_ALERT = BIT(2), + APML_MCA_ALERT = BIT(3), + APML_DRAM_CECC_ALERT = BIT(4), + APML_PCIE_ALERT = BIT(5), + APML_CPU_SHUTDOWN_ALERT = BIT(6), + APML_TEMP_LOW_ALERT = BIT(27), + APML_TEMP_HIGH_ALERT = BIT(28), +}; enum apml_protocol { APML_CPUID = 0x1000, From 14077c851cabcf213a51d724ead7c33dc8058c19 Mon Sep 17 00:00:00 2001 From: sathya priya kumar Date: Tue, 1 Jul 2025 18:22:22 +0530 Subject: [PATCH 03/12] arm64: dts: aspeed: Alert_L changes for Morocco & Congo -Add alertl node for P1 and P2 host processor in Morocco platform. -Add alertl node for P1 host processor in Congo platform. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Signed-off-by: sathya priya kumar --- .../boot/dts/aspeed/aspeed-bmc-amd-congo.dts | 11 ++++++++++ .../dts/aspeed/aspeed-bmc-amd-morocco.dts | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts index c5894a34425d94..10ef59223ac55b 100644 --- a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts +++ b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts @@ -882,6 +882,17 @@ }; }; +/ { + /* Alert_L associated with socket 0 */ + alertl_sock0 { + compatible = "apml-alertl"; + status = "okay"; + gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; + sbrmi = <&sbrmi_p0_iod0>; + sbtsi = <&sbtsi_p0_iod0>; + }; +}; + #ifdef I3C_HUB #define JESD300_SPD_I3C_MODE(bus, index, addr) \ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts index 1c241dfc5ccc41..08951b4d92f654 100644 --- a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts +++ b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts @@ -970,6 +970,26 @@ }; }; +/ { + /* Alert_L associated with socket 0 */ + alertl_sock0 { + compatible = "apml-alertl"; + status = "okay"; + gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; + sbrmi = <&sbrmi_p0_iod0>; + sbtsi = <&sbtsi_p0_iod0>; + }; + + /* Alert_L associated with socket 1 */ + alertl_sock1 { + compatible = "apml-alertl"; + status = "okay"; + gpios = <<pi0_gpio 104 GPIO_ACTIVE_LOW>; + sbrmi = <&sbrmi_p1_iod0>; + sbtsi = <&sbtsi_p1_iod0>; + }; +}; + #ifdef I3C_HUB #define JESD300_SPD_I3C_MODE(bus, index, addr) \ From 056248b72af08d762ad1edfd1d9cbbd763b6ab6f Mon Sep 17 00:00:00 2001 From: sathya priya kumar Date: Tue, 1 Jul 2025 18:26:26 +0530 Subject: [PATCH 04/12] arm64: dts: aspeed: Alert_L changes for Kenya & Nigeria -Add alertl node for P1 and P2 host processor in Nigeria platform. -Add alertl node for P1 host processor in Kenya platform. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Signed-off-by: sathya priya kumar --- .../boot/dts/aspeed/aspeed-bmc-amd-kenya.dts | 11 ++++++++++ .../dts/aspeed/aspeed-bmc-amd-nigeria.dts | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts index ec9f6c44b7b84d..fc673746ed4470 100755 --- a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts +++ b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts @@ -572,6 +572,17 @@ }; }; +/ { + /* Alert_L associated with socket 0 */ + alertl_sock0 { + compatible = "apml-alertl"; + status = "okay"; + gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; + sbrmi = <&sbrmi_p0_iod0>; + sbtsi = <&sbtsi_p0_iod0>; + }; +}; + #ifdef I3C_HUB #define JESD300_SPD_I3C_MODE(bus, index, addr) \ diff --git a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts index f061b9b01acedc..c9b8a2cd11dc5f 100755 --- a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts +++ b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts @@ -741,6 +741,26 @@ }; }; +/ { + /* Alert_L associated with socket 0 */ + alertl_sock0 { + compatible = "apml-alertl"; + status = "okay"; + gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; + sbrmi = <&sbrmi_p0_iod0>; + sbtsi = <&sbtsi_p0_iod0>; + }; + + /* Alert_L associated with socket 1 */ + alertl_sock1 { + compatible = "apml-alertl"; + status = "okay"; + gpios = <<pi0_gpio 104 GPIO_ACTIVE_LOW>; + sbrmi = <&sbrmi_p1_iod0>; + sbtsi = <&sbtsi_p1_iod0>; + }; +}; + #ifdef I3C_HUB #define JESD300_SPD_I3C_MODE(bus, index, addr) \ From c2302e7b3f205f5cb04897a64e43a6072ddd7fee Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Tue, 3 Jun 2025 10:25:25 +0000 Subject: [PATCH 05/12] dt-bindings: misc: apml-alertl: Add APML Alert_L bindings -Document device-tree bindings for APML Alert_L driver. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Signed-off-by: sathya priya kumar (cherry picked from commit 0f54429b0d8f9a2e935fa7c300601444ba54ea15) --- .../bindings/misc/amd,apml-alertl.yaml | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/amd,apml-alertl.yaml diff --git a/Documentation/devicetree/bindings/misc/amd,apml-alertl.yaml b/Documentation/devicetree/bindings/misc/amd,apml-alertl.yaml new file mode 100644 index 00000000000000..d28baa9c0d541f --- /dev/null +++ b/Documentation/devicetree/bindings/misc/amd,apml-alertl.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/amd,apml-alertl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: > + Sideband Remote Management Interface (SB-RMI) compliant + AMD APML Alert_L GPIO. + +maintainers: + - Akshay Gupta + +description: | + Processors from AMD provide APML ALERT_L for BMC users to + monitor events. + 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 + - High/Low Temperature Alert + + APML Alert_L module define uevents to notify userspace of the + alert event. + +properties: + compatible: + const: apml-alertl + + gpio: + maxItems: 1 + description: | + GPIO specifier for APML Alert_L line. Specify GPIO controller, GPIO pin + and polarity. + + sbrmi: + $ref: /schemas/types.yaml#/definitions/phandle + description: ref amd,sbrmi.yaml + + sbtsi: + $ref: /schemas/types.yaml#/definitions/phandle + description: ref amd,sbtsi.yaml + +required: + - compatible + - gpio + - sbrmi + - sbtsi + +additionalProperties: false + +examples: + - | + /* Alert_L associated with Socket 0 */ + alertl_sock0 { + compatible = "apml-alertl"; + status = "okay"; + gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; + sbrmi = <&sbrmi_p0_iod0>; + sbtsi = <&sbtsi_p0_iod0>; + }; +... From e3b34d179cfbb28028848e443d579e477b2e6039 Mon Sep 17 00:00:00 2001 From: Sathya Priya Kumar Date: Wed, 19 Nov 2025 22:46:02 +0530 Subject: [PATCH 06/12] arm64: dts: aspeed: Update Alert_L node for Morocco & Congo Remove explicit sbrmi and sbtsi device references from Alert_L device tree nodes. The updated Alert_L driver now uses dynamic device discovery through the global APML device registry instead of DTS-based device parsing. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: sathya priya kumar Signed-off-by: Akshay Gupta --- arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts | 3 +-- arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts index 10ef59223ac55b..ee2371fa5437b6 100644 --- a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts +++ b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-congo.dts @@ -887,9 +887,8 @@ alertl_sock0 { compatible = "apml-alertl"; status = "okay"; + socket-num = /bits/ 8 <0>; gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; - sbrmi = <&sbrmi_p0_iod0>; - sbtsi = <&sbtsi_p0_iod0>; }; }; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts index 08951b4d92f654..bd178cc7d1393a 100644 --- a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts +++ b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-morocco.dts @@ -975,18 +975,16 @@ alertl_sock0 { compatible = "apml-alertl"; status = "okay"; + socket-num = /bits/ 8 <0>; gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; - sbrmi = <&sbrmi_p0_iod0>; - sbtsi = <&sbtsi_p0_iod0>; }; /* Alert_L associated with socket 1 */ alertl_sock1 { compatible = "apml-alertl"; status = "okay"; + socket-num = /bits/ 8 <1>; gpios = <<pi0_gpio 104 GPIO_ACTIVE_LOW>; - sbrmi = <&sbrmi_p1_iod0>; - sbtsi = <&sbtsi_p1_iod0>; }; }; From 52f4f660746826cc3a62f6cfc93e26d333c607f0 Mon Sep 17 00:00:00 2001 From: Sathya Priya Kumar Date: Wed, 19 Nov 2025 22:49:59 +0530 Subject: [PATCH 07/12] arm64: dts: aspeed: Update Alert_L node for Nigeria and Kenya Remove explicit sbrmi and sbtsi device references from Alert_L device tree nodes. The updated Alert_L driver now uses dynamic device discovery through the global APML device registry instead of DTS-based device parsing. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: sathya priya kumar Signed-off-by: Akshay Gupta --- arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts | 3 +-- arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts index fc673746ed4470..d224ef3c8ac4d3 100755 --- a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts +++ b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-kenya.dts @@ -577,9 +577,8 @@ alertl_sock0 { compatible = "apml-alertl"; status = "okay"; + socket-num = /bits/ 8 <0>; gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; - sbrmi = <&sbrmi_p0_iod0>; - sbtsi = <&sbtsi_p0_iod0>; }; }; diff --git a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts index c9b8a2cd11dc5f..a0bd47a9df6471 100755 --- a/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts +++ b/arch/arm64/boot/dts/aspeed/aspeed-bmc-amd-nigeria.dts @@ -746,18 +746,16 @@ alertl_sock0 { compatible = "apml-alertl"; status = "okay"; + socket-num = /bits/ 8 <0>; gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; - sbrmi = <&sbrmi_p0_iod0>; - sbtsi = <&sbtsi_p0_iod0>; }; /* Alert_L associated with socket 1 */ alertl_sock1 { compatible = "apml-alertl"; status = "okay"; + socket-num = /bits/ 8 <1>; gpios = <<pi0_gpio 104 GPIO_ACTIVE_LOW>; - sbrmi = <&sbrmi_p1_iod0>; - sbtsi = <&sbtsi_p1_iod0>; }; }; From e7d2266db0bb326e88ef07132dab49f78e92946c Mon Sep 17 00:00:00 2001 From: Sathya Priya Kumar Date: Wed, 19 Nov 2025 22:11:59 +0530 Subject: [PATCH 08/12] misc: amd-apml: Add common device registration framework Add centralized device registration system for AMD APML devices. Provides global device list where SBRMI and SBTSI drivers can register themselves, enabling the Alert_L driver to discover and safely process alerts from registered devices. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: sathya priya kumar Signed-off-by: Akshay Gupta --- drivers/misc/amd-apml/Kconfig | 12 ++ drivers/misc/amd-apml/Makefile | 1 + drivers/misc/amd-apml/apml_common.c | 198 ++++++++++++++++++++++++++++ drivers/misc/amd-apml/apml_common.h | 73 ++++++++++ 4 files changed, 284 insertions(+) create mode 100644 drivers/misc/amd-apml/apml_common.c create mode 100644 drivers/misc/amd-apml/apml_common.h diff --git a/drivers/misc/amd-apml/Kconfig b/drivers/misc/amd-apml/Kconfig index 8a998afcd83e0f..508db7cf0d83e8 100644 --- a/drivers/misc/amd-apml/Kconfig +++ b/drivers/misc/amd-apml/Kconfig @@ -3,6 +3,18 @@ # AMD APML BMC interface drivers # +config APML_COMMON + tristate "AMD APML common device registration framework" + default n + help + Enable support for the AMD APML (Advanced Platform Management Link) + common device registration framework. This module provides a centralized + registry where APML devices (SBRMI and SBTSI) can register themselves + for discovery by other APML subsystems. + + This framework enables the Alert_L driver to safely discover and access + registered APML devices for alert processing across multiple sockets. + config APML_SBRMI tristate "Emulated SB-RMI interface driver over i3c bus" depends on I3C && !SENSORS_SBRMI diff --git a/drivers/misc/amd-apml/Makefile b/drivers/misc/amd-apml/Makefile index 864ea61baf6d2a..a8884a496e490a 100644 --- a/drivers/misc/amd-apml/Makefile +++ b/drivers/misc/amd-apml/Makefile @@ -4,6 +4,7 @@ # AMD APML BMC interface drivers # +obj-$(CONFIG_APML_COMMON) += apml_common.o obj-$(CONFIG_APML_SBRMI) += sbrmi.o sbrmi-common.o obj-$(CONFIG_APML_SBTSI) += apml_sbtsi.o obj-$(CONFIG_APML_ALERTL) += apml_alertl.o diff --git a/drivers/misc/amd-apml/apml_common.c b/drivers/misc/amd-apml/apml_common.c new file mode 100644 index 00000000000000..04b3a9d0769772 --- /dev/null +++ b/drivers/misc/amd-apml/apml_common.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * apml-common.c - Common registration system for APML devices + * + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apml_common.h" + +/* Global list of registered APML devices */ +LIST_HEAD(apml_devices); +EXPORT_SYMBOL_GPL(apml_devices); + +/* + * Global Lock: Protects the shared apml_devices list during registration, + * unregistration, and iteration through all registered devices + */ +DEFINE_MUTEX(apml_devices_lock); +EXPORT_SYMBOL_GPL(apml_devices_lock); + +/* Release function for device node reference counting */ +static void apml_device_node_release(struct kref *ref) +{ + struct apml_device_node *node = container_of(ref, struct apml_device_node, ref); + + kfree(node); +} + +/* Get a reference to a device node - increases reference count */ +struct apml_device_node *apml_get_device_node(struct apml_device_node *node) +{ + if (node && kref_get_unless_zero(&node->ref)) + return node; + return NULL; +} +EXPORT_SYMBOL_GPL(apml_get_device_node); + +/* Put a reference to a device node - decreases reference count */ +void apml_put_device_node(struct apml_device_node *node) +{ + if (node) + kref_put(&node->ref, apml_device_node_release); +} +EXPORT_SYMBOL_GPL(apml_put_device_node); + +static void apml_add_device_node(struct apml_device_node *node) +{ + mutex_lock(&apml_devices_lock); + list_add_tail(&node->list, &apml_devices); + mutex_unlock(&apml_devices_lock); +} + +/* Device registration function */ +int apml_register_device(void *device, enum apml_device_type dev_type) +{ + struct apml_device_node *node; + struct apml_sbrmi_device *rmi_dev; + struct apml_sbtsi_device *tsi_dev; + + if (!device) { + pr_info("APML: Cannot register NULL device\n"); + return -EINVAL; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + node->dev_type = dev_type; + kref_init(&node->ref); /* Initialize reference count to 1 */ + + switch (node->dev_type) { + case APML_RMI_DEVICE: + rmi_dev = (struct apml_sbrmi_device *)device; + + if (!rmi_dev->i3cdev && !rmi_dev->client) { + pr_err("APML: Invalid RMI device - no I2C or I3C device\n"); + kfree(node); + return -EINVAL; + } + node->rmi_dev = rmi_dev; + pr_info("APML: Registered SBRMI device at address 0x%x\n", + rmi_dev->i3cdev ? rmi_dev->dev_static_addr : rmi_dev->client->addr); + break; + case APML_TSI_DEVICE: + tsi_dev = (struct apml_sbtsi_device *)device; + + if (!tsi_dev->i3cdev && !tsi_dev->client) { + pr_info("APML: Invalid TSI device - no I2C or I3C device\n"); + kfree(node); + return -EINVAL; + } + node->tsi_dev = tsi_dev; + pr_info("APML: Registered SBTSI device at address 0x%x\n", + tsi_dev->i3cdev ? tsi_dev->dev_static_addr : tsi_dev->client->addr); + break; + default: + kfree(node); + return -EINVAL; + } + + apml_add_device_node(node); + return 0; +} + +/* Register an SBRMI device */ +int apml_register_sbrmi_device(struct apml_sbrmi_device *rmi_dev) +{ + return apml_register_device(rmi_dev, APML_RMI_DEVICE); +} +EXPORT_SYMBOL_GPL(apml_register_sbrmi_device); + +/* Register an SBTSI device*/ +int apml_register_sbtsi_device(struct apml_sbtsi_device *tsi_dev) +{ + return apml_register_device(tsi_dev, APML_TSI_DEVICE); +} +EXPORT_SYMBOL_GPL(apml_register_sbtsi_device); + +/* Device unregistration function*/ +void apml_unregister_device(void *device, enum apml_device_type dev_type) +{ + struct apml_device_node *node, *tmp, *found_node = NULL; + bool found = false; + + if (!device) + return; + + /* Find and mark device as invalid and removing */ + mutex_lock(&apml_devices_lock); + list_for_each_entry(node, &apml_devices, list) { + if (node->dev_type != dev_type) + continue; + + switch (dev_type) { + case APML_RMI_DEVICE: + if (node->rmi_dev == (struct apml_sbrmi_device *)device) + found = true; + break; + case APML_TSI_DEVICE: + if (node->tsi_dev == (struct apml_sbtsi_device *)device) + found = true; + break; + } + + if (found) { + /* Get a reference to keep node alive during removal */ + found_node = apml_get_device_node(node); + break; + } + } + mutex_unlock(&apml_devices_lock); + + if (!found_node) + return; + + /* Remove from list and release reference */ + mutex_lock(&apml_devices_lock); + list_for_each_entry_safe(node, tmp, &apml_devices, list) { + if (node == found_node) { + list_del(&node->list); + break; + } + } + mutex_unlock(&apml_devices_lock); + + /* Release our reference - this may free the node if no other references exist */ + apml_put_device_node(found_node); +} + +/* Unregister an SBRMI device */ +void apml_unregister_sbrmi_device(struct apml_sbrmi_device *rmi_dev) +{ + apml_unregister_device(rmi_dev, APML_RMI_DEVICE); +} +EXPORT_SYMBOL_GPL(apml_unregister_sbrmi_device); + +/* Unregister an SBTSI device */ +void apml_unregister_sbtsi_device(struct apml_sbtsi_device *tsi_dev) +{ + apml_unregister_device(tsi_dev, APML_TSI_DEVICE); +} +EXPORT_SYMBOL_GPL(apml_unregister_sbtsi_device); + +MODULE_AUTHOR("Akshay Gupta "); +MODULE_AUTHOR("sathya priya kumar "); +MODULE_AUTHOR("Naveenkrishna Chatradhi "); +MODULE_DESCRIPTION("AMD APML Common Device Registration"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/amd-apml/apml_common.h b/drivers/misc/amd-apml/apml_common.h new file mode 100644 index 00000000000000..a028e75f9be2fb --- /dev/null +++ b/drivers/misc/amd-apml/apml_common.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. + */ + +#ifndef _AMD_APML_COMMON_H_ +#define _AMD_APML_COMMON_H_ + +#include +#include +#include "sbrmi-common.h" +#include "sbtsi-common.h" + +extern struct list_head apml_devices; +extern struct mutex apml_devices_lock; + +enum apml_device_type { + APML_RMI_DEVICE, + APML_TSI_DEVICE, +}; + +struct apml_device_node { + struct list_head list; + struct kref ref; + enum apml_device_type dev_type; + union { + struct apml_sbrmi_device *rmi_dev; + struct apml_sbtsi_device *tsi_dev; + }; +}; + +/* Function declarations - available when APML_COMMON is built */ +#if IS_ENABLED(CONFIG_APML_COMMON) +int apml_register_sbrmi_device(struct apml_sbrmi_device *rmi_dev); +void apml_unregister_sbrmi_device(struct apml_sbrmi_device *rmi_dev); +int apml_register_sbtsi_device(struct apml_sbtsi_device *tsi_dev); +void apml_unregister_sbtsi_device(struct apml_sbtsi_device *tsi_dev); +struct apml_device_node *apml_get_device_node(struct apml_device_node *node); +void apml_put_device_node(struct apml_device_node *node); +#else +/* Stub functions when APML_COMMON is not available */ +static inline int apml_register_sbrmi_device(struct apml_sbrmi_device *rmi_dev) +{ + return 0; +} + +static inline void apml_unregister_sbrmi_device(struct apml_sbrmi_device *rmi_dev) +{ + return 0; +} + +static inline int apml_register_sbtsi_device(struct apml_sbtsi_device *tsi_dev) +{ + return 0; +} + +static inline void apml_unregister_sbtsi_device(struct apml_sbtsi_device *tsi_dev) +{ + return 0 +} + +static inline struct apml_device_node *apml_get_device_node(struct apml_device_node *node) +{ + return NULL; +} + +static inline void apml_put_device_node(struct apml_device_node *node) +{ + return 0; +} + +#endif /* IS_ENABLED(CONFIG_APML_COMMON) */ +#endif /* _AMD_APML_COMMON_H_ */ From d628617cde87202e0c00f9040ed0556d218dac01 Mon Sep 17 00:00:00 2001 From: Sathya Priya Kumar Date: Wed, 19 Nov 2025 22:15:52 +0530 Subject: [PATCH 09/12] misc: amd-apml: Integrate SBTSI with common device registration framework Add device registration/unregistration calls to SBTSI drivers. This enables Alert_L driver to discover and safely access registered TSI devices for processing temperature alerts. Remove device tree matching functions from SBTSI drivers that are no longer needed after migrating Alert_L driver to use the global device registry from apml_common framework. The legacy Alert_L module used *_match_i2c/*_match_i3c functions to retrieve device node information from device tree nodes. With the new design, Alert_L module devices are now registered during each module's probe function, making the old matching functions obsolete. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: sathya priya kumar Signed-off-by: Akshay Gupta --- drivers/misc/amd-apml/Kconfig | 1 + drivers/misc/amd-apml/apml_sbtsi.c | 57 +++++++++++++++------------- drivers/misc/amd-apml/sbtsi-common.h | 3 -- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/drivers/misc/amd-apml/Kconfig b/drivers/misc/amd-apml/Kconfig index 508db7cf0d83e8..e60c8ac5442098 100644 --- a/drivers/misc/amd-apml/Kconfig +++ b/drivers/misc/amd-apml/Kconfig @@ -29,6 +29,7 @@ config APML_SBRMI config APML_SBTSI tristate "Emulated SB-TSI interface driver over i3c bus" depends on I3C && !SENSORS_SBTSI + depends on APML_COMMON select REGMAP_I3C if I3C help If you say yes here you get support for emulated TSI diff --git a/drivers/misc/amd-apml/apml_sbtsi.c b/drivers/misc/amd-apml/apml_sbtsi.c index d0fcb507de8634..9cb91ea222f2ac 100644 --- a/drivers/misc/amd-apml/apml_sbtsi.c +++ b/drivers/misc/amd-apml/apml_sbtsi.c @@ -23,9 +23,10 @@ #include #include #include -#include -#include "sbtsi-common.h" +#include +#include "apml_common.h" + /* * SB-TSI registers only support SMBus byte data access. "_INT" registers are * the integer part of a temperature value or limit, and "_DEC" registers are @@ -475,6 +476,7 @@ static int sbtsi_i3c_probe(struct i3c_device *i3cdev) }; struct regmap *regmap; const char *hwmon_dev_name; + int ret; dev_err(dev, "SBTSI: PID: %llx\n", i3cdev->desc->info.pid); if (!(I3C_PID_INSTANCE_ID(i3cdev->desc->info.pid) == 0 || @@ -524,7 +526,16 @@ static int sbtsi_i3c_probe(struct i3c_device *i3cdev) return PTR_ERR_OR_ZERO(hwmon_dev); } - return create_misc_tsi_device(tsi_dev, dev); + ret = create_misc_tsi_device(tsi_dev, dev); + if (ret) + return ret; + + /* Register with ALERT_L common system */ + ret = apml_register_sbtsi_device(tsi_dev); + if (ret) + dev_warn(dev, "Failed to register with ALERT_L common system: %d\n", ret); + + return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) @@ -542,6 +553,7 @@ static int sbtsi_i2c_probe(struct i2c_client *client) .val_bits = 8, }; const char *hwmon_dev_name; + int ret; tsi_dev = devm_kzalloc(dev, sizeof(struct apml_sbtsi_device), GFP_KERNEL); if (!tsi_dev) @@ -568,7 +580,16 @@ static int sbtsi_i2c_probe(struct i2c_client *client) if (!hwmon_dev) return PTR_ERR_OR_ZERO(hwmon_dev); - return create_misc_tsi_device(tsi_dev, dev); + ret = create_misc_tsi_device(tsi_dev, dev); + if (ret) + return ret; + + /* Register with ALERT_L common system */ + ret = apml_register_sbtsi_device(tsi_dev); + if (ret) + dev_warn(dev, "Failed to register with ALERT_L common system: %d\n", ret); + + return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) @@ -579,8 +600,10 @@ static void sbtsi_i3c_remove(struct i3c_device *i3cdev) { struct apml_sbtsi_device *tsi_dev = dev_get_drvdata(&i3cdev->dev); - if (tsi_dev) + if (tsi_dev) { + apml_unregister_sbtsi_device(tsi_dev); misc_deregister(&tsi_dev->sbtsi_misc_dev); + } dev_info(&i3cdev->dev, "Removed sbtsi-i3c driver\n"); #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) @@ -596,8 +619,10 @@ static void sbtsi_i2c_remove(struct i2c_client *client) { struct apml_sbtsi_device *tsi_dev = dev_get_drvdata(&client->dev); - if (tsi_dev) + if (tsi_dev) { + apml_unregister_sbtsi_device(tsi_dev); misc_deregister(&tsi_dev->sbtsi_misc_dev); + } dev_info(&client->dev, "Removed sbtsi driver\n"); #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) @@ -657,26 +682,6 @@ static struct i2c_driver sbtsi_driver = { module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver) -int sbtsi_match_i3c(struct device *dev, const void *data) -{ - const struct device_node *node = (const struct device_node *)data; - - if (dev->of_node == node && dev->driver == &sbtsi_i3c_driver.driver) - return 1; - return 0; -} -EXPORT_SYMBOL_GPL(sbtsi_match_i3c); - -int sbtsi_match_i2c(struct device *dev, const void *data) -{ - const struct device_node *node = (const struct device_node *)data; - - if (dev->of_node == node && dev->driver == &sbtsi_driver.driver) - return 1; - return 0; -} -EXPORT_SYMBOL_GPL(sbtsi_match_i2c); - MODULE_AUTHOR("Kun Yi "); MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/misc/amd-apml/sbtsi-common.h b/drivers/misc/amd-apml/sbtsi-common.h index 4fc3a33e8639fa..7fdd1e098ac491 100644 --- a/drivers/misc/amd-apml/sbtsi-common.h +++ b/drivers/misc/amd-apml/sbtsi-common.h @@ -21,7 +21,4 @@ struct apml_sbtsi_device { u8 dev_static_addr; } __packed; -int sbtsi_match_i2c(struct device *dev, const void *data); -int sbtsi_match_i3c(struct device *dev, const void *data); - #endif From 1b788c412d8c1e09a832fdb81cb6b95f3c3f1d41 Mon Sep 17 00:00:00 2001 From: Sathya Priya Kumar Date: Wed, 19 Nov 2025 22:18:44 +0530 Subject: [PATCH 10/12] misc: amd-apml: Integrate SBRMI with common device registration framework Add device registration/unregistration calls to SBRMI drivers. This enables Alert_L driver to discover and safely access registered RMI devices for processing RAS event. Remove device tree matching functions from SBRMI drivers that are no longer needed after migrating Alert_L driver to use the global device registry from apml_common framework. The legacy Alert_L module used *_match_i2c/*_match_i3c functions to retrieve device node information from device tree nodes. With the new design, Alert_L module devices are now registered during each module's probe function, making the old matching functions obsolete. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: sathya priya kumar Signed-off-by: Akshay Gupta --- drivers/misc/amd-apml/Kconfig | 1 + drivers/misc/amd-apml/sbrmi-common.h | 2 -- drivers/misc/amd-apml/sbrmi.c | 48 ++++++++++++++-------------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/drivers/misc/amd-apml/Kconfig b/drivers/misc/amd-apml/Kconfig index e60c8ac5442098..c34e694e2f225f 100644 --- a/drivers/misc/amd-apml/Kconfig +++ b/drivers/misc/amd-apml/Kconfig @@ -18,6 +18,7 @@ config APML_COMMON config APML_SBRMI tristate "Emulated SB-RMI interface driver over i3c bus" depends on I3C && !SENSORS_SBRMI + depends on APML_COMMON select REGMAP_I3C if I3C help If you say yes here you get support for emulated RMI diff --git a/drivers/misc/amd-apml/sbrmi-common.h b/drivers/misc/amd-apml/sbrmi-common.h index c59d3b52fb3946..3abc8357cb44f6 100644 --- a/drivers/misc/amd-apml/sbrmi-common.h +++ b/drivers/misc/amd-apml/sbrmi-common.h @@ -35,6 +35,4 @@ int rmi_cpuid_read(struct apml_sbrmi_device *rmi_dev, struct apml_message *msg); int rmi_mailbox_xfer(struct apml_sbrmi_device *rmi_dev, struct apml_message *msg); -int sbrmi_match_i3c(struct device *dev, const void *data); -int sbrmi_match_i2c(struct device *dev, const void *data); #endif /*_AMD_APML_SBRMI_H_*/ diff --git a/drivers/misc/amd-apml/sbrmi.c b/drivers/misc/amd-apml/sbrmi.c index 83e168a233421e..ecaa0e95b86142 100644 --- a/drivers/misc/amd-apml/sbrmi.c +++ b/drivers/misc/amd-apml/sbrmi.c @@ -22,6 +22,7 @@ #include #include "sbrmi-common.h" +#include "apml_common.h" /* Do not allow setting negative power limit */ #define SBRMI_PWR_MIN 0 @@ -491,6 +492,7 @@ static int sbrmi_i2c_probe(struct i2c_client *client) struct device *hwmon_dev; struct apml_sbrmi_device *rmi_dev; const char *hwmon_dev_name; + int ret; rmi_dev = devm_kzalloc(dev, sizeof(struct apml_sbrmi_device), GFP_KERNEL); if (!rmi_dev) @@ -517,7 +519,15 @@ static int sbrmi_i2c_probe(struct i2c_client *client) return PTR_ERR_OR_ZERO(hwmon_dev); init_completion(&rmi_dev->misc_fops_done); - return create_misc_rmi_device(rmi_dev, dev); + ret = create_misc_rmi_device(rmi_dev, dev); + if (ret) + return ret; + /* Register with ALERT_L common system */ + ret = apml_register_sbrmi_device(rmi_dev); + if (ret) + dev_warn(dev, "Failed to register with ALERT_L common system: %d\n", ret); + + return ret; } static int sbrmi_i3c_reg_read(struct i3c_device *i3cdev, int reg_size, u32 *val) @@ -713,7 +723,15 @@ static int sbrmi_i3c_probe(struct i3c_device *i3cdev) } init_completion(&rmi_dev->misc_fops_done); - return create_misc_rmi_device(rmi_dev, dev); + ret = create_misc_rmi_device(rmi_dev, dev); + if (ret) + return ret; + /* Register with ALERT_L common system */ + ret = apml_register_sbrmi_device(rmi_dev); + if (ret) + dev_warn(dev, "Failed to register with ALERT_L common system: %d\n", ret); + + return ret; } #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) @@ -730,7 +748,8 @@ static void sbrmi_i2c_remove(struct i2c_client *client) #else return; #endif - + /* Unregister from APML common system */ + apml_unregister_sbrmi_device(rmi_dev); /* * Set the no_new_trans so no new transaction can * occur in sbrmi_ioctl @@ -770,7 +789,8 @@ static void sbrmi_i3c_remove(struct i3c_device *i3cdev) #else return; #endif - + /* Unregister from APML common system */ + apml_unregister_sbrmi_device(rmi_dev); /* * Set the no_new_trans so no new transaction can * occur in sbrmi_ioctl @@ -845,26 +865,6 @@ static struct i3c_driver sbrmi_i3c_driver = { module_i3c_i2c_driver(sbrmi_i3c_driver, &sbrmi_driver) -int sbrmi_match_i3c(struct device *dev, const void *data) -{ - const struct device_node *node = (const struct device_node *)data; - - if (dev->of_node == node && dev->driver == &sbrmi_i3c_driver.driver) - return 1; - return 0; -} -EXPORT_SYMBOL_GPL(sbrmi_match_i3c); - -int sbrmi_match_i2c(struct device *dev, const void *data) -{ - const struct device_node *node = (const struct device_node *)data; - - if (dev->of_node == node && dev->driver == &sbrmi_driver.driver) - return 1; - return 0; -} -EXPORT_SYMBOL_GPL(sbrmi_match_i2c); - MODULE_AUTHOR("Akshay Gupta "); MODULE_AUTHOR("Naveenkrishna Chatradhi "); MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor"); From 680bbae370cf216d2aa1746bdbbdc4383d9038f8 Mon Sep 17 00:00:00 2001 From: Sathya Priya Kumar Date: Wed, 19 Nov 2025 22:21:25 +0530 Subject: [PATCH 11/12] misc: amd-apml: Update Alert_L driver to use global device registry Modify device tree-based device discovery in Alert_L driver with centralized device registry from apml_common module. This provides more reliable to sbrmi/sbtsi device access for processing alerts and eliminates DTS parsing. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Signed-off-by: sathya priya kumar --- drivers/misc/amd-apml/Kconfig | 2 +- drivers/misc/amd-apml/apml_alertl.c | 453 ++++++++++++---------------- drivers/misc/amd-apml/apml_alertl.h | 17 +- 3 files changed, 199 insertions(+), 273 deletions(-) diff --git a/drivers/misc/amd-apml/Kconfig b/drivers/misc/amd-apml/Kconfig index c34e694e2f225f..c0543ca63e1d4a 100644 --- a/drivers/misc/amd-apml/Kconfig +++ b/drivers/misc/amd-apml/Kconfig @@ -41,7 +41,7 @@ config APML_SBTSI config APML_ALERTL tristate "Emulated apml alertl interface driver over i3c bus" - depends on APML_SBRMI && APML_SBTSI + depends on (APML_SBRMI || APML_SBTSI) && APML_COMMON default n help This driver provides support for AMD APML Alert_L interface, diff --git a/drivers/misc/amd-apml/apml_alertl.c b/drivers/misc/amd-apml/apml_alertl.c index 8d8abee18a9bd6..ac06caf6948f72 100644 --- a/drivers/misc/amd-apml/apml_alertl.c +++ b/drivers/misc/amd-apml/apml_alertl.c @@ -10,11 +10,12 @@ #include #include #include -#include #include +#include #include -#include #include +#include +#include #include "apml_alertl.h" @@ -25,335 +26,269 @@ #define TSI_STATUS_REG 0x2 #define RAS_ALERT_STATUS BIT(1) #define RAS_ALERT_ASYNC BIT(3) +#define TSI_STATUS_SHIFT 24 -#define MAX_SOC_LEN 11 -#define MAX_ERR_LEN 18 - -/* SBRMI and SBTSI static address for socket 0 and 1 */ -#define RMI_SOCK_0_DIE_0 0x3c -#define TSI_SOCK_0_DIE_0 0x4c -#define RMI_SOCK_1_DIE_0 0x38 -#define TSI_SOCK_1_DIE_0 0x48 +#define ENVP_SRC_INDX 0 +#define ENVP_BUS_NUM_INDX 1 +#define ENVP_PID_INDX 2 +#define ENVP_ADDR_INDX 3 +#define NUM_ENVP 5 MODULE_ALIAS("apml_alertl:" DRIVER_NAME); -/* Map static address to socket and die index */ -static u8 static_addr_to_socket(u8 static_addr) -{ - /* - * [3:0] = Socket Index - * [7:4] = die Index - * Mapping: - * 0x3c, 0x4c -> Socket 0, die 0, - * 0x38, 0x48 -> Socket 1, die 0, - */ - switch (static_addr) { - case RMI_SOCK_0_DIE_0: - case TSI_SOCK_0_DIE_0: - return 0; - case RMI_SOCK_1_DIE_0: - case TSI_SOCK_1_DIE_0: - return 1; - default: - return 0xFF; - } -} - -/* Send a uevent to userspace for an APML alert */ -static int send_uevent(u8 static_address, u32 alert_src, struct device *dev) +/* + * The driver generates uevents for Temperature and RAS alerts (both fatal and non-fatal). + * Event data contains address, bus number, PID (for I3C devices; 0 otherwise), and alert + * source information. See amd-apml.h for alert source details. + */ +static int send_uevent(u8 address, u8 bus_num, u32 alert_src, + u64 pid, struct device *dev) { - u8 soc_die_num; - char sock[MAX_SOC_LEN]; - char src[MAX_ERR_LEN]; - char *alert_source[] = { sock, src, NULL }; + char *alert_source[NUM_ENVP]; - soc_die_num = static_addr_to_socket(static_address); - if (soc_die_num == 0xFF) - return -ENODEV; + alert_source[ENVP_SRC_INDX] = devm_kasprintf(dev, GFP_KERNEL, "SOURCE=0x%08x", alert_src); + alert_source[ENVP_BUS_NUM_INDX] = devm_kasprintf(dev, GFP_KERNEL, "BUS_NUM=%u", bus_num); + alert_source[ENVP_PID_INDX] = devm_kasprintf(dev, GFP_KERNEL, "PID=0x%016llx", pid); + alert_source[ENVP_ADDR_INDX] = devm_kasprintf(dev, GFP_KERNEL, "ADDRESS=0x%02x", address); + alert_source[NUM_ENVP - 1] = NULL; - snprintf(sock, sizeof(sock), "Socket=0x%x", soc_die_num); - snprintf(src, sizeof(src), "Source=0x%x", alert_src); - - dev_dbg(dev, "Sending uevent: Sock:0x%x Src:0x%x\n", - soc_die_num, alert_src); + dev_dbg(dev, "Sending uevent: Addr:0x%x Src:0x%08x\n bus:%d pid: 0x%llx\n", + address, alert_src, bus_num, pid); kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, alert_source); + return 0; } -/* Process and handle TSI alerts for all TSI devices */ -static void handle_tsi_alerts(struct apml_alertl_data *oob_adata) +static int handle_rmi_device_alert(struct apml_device_node *device_node, struct device *dev) { - struct device *dev = oob_adata->dev; - struct apml_message msg = { 0 }; - int temp_status, ret, i; - - for (i = 0; i < oob_adata->num_of_tsi_devs; i++) { - temp_status = 0; - if (!oob_adata->tsi_dev[i] || !oob_adata->tsi_dev[i]->regmap) { - dev_dbg(dev, - "TSI device at index %d is NULL or regmap missing\n", - i); - continue; - } + int status = 0, ret; + u8 addr, bus_num; + u64 pid; - /* Read TSI Status register to identify the RAS error */ - msg.data_in.reg_in[REG_OFF_INDEX] = TSI_STATUS_REG; + if (!device_node->rmi_dev || !device_node->rmi_dev->regmap) { + dev_warn(dev, "Invalid RMI device found\n"); + return -EINVAL; + } - mutex_lock(&oob_adata->tsi_dev[i]->lock); - ret = regmap_read(oob_adata->tsi_dev[i]->regmap, - msg.data_in.reg_in[REG_OFF_INDEX], - &temp_status); - mutex_unlock(&oob_adata->tsi_dev[i]->lock); + /* Protects individual device state and regmap transactions */ + mutex_lock(&device_node->rmi_dev->lock); + /* Read RAS Status register */ + ret = regmap_read(device_node->rmi_dev->regmap, RAS_STATUS_REG, &status); + mutex_unlock(&device_node->rmi_dev->lock); + if (ret) + return ret; - if (ret < 0) { - dev_dbg(dev, - "Failed to read temperature status of TSI device index %d\n", - i); - continue; - } + if (!status) { + /* No alert status - normal condition */ + return ret; + } + /* Extract device information based on bus type (I3C or I2C) */ + if (device_node->rmi_dev->i3cdev) { + /* I3C device path */ + addr = device_node->rmi_dev->dev_static_addr; + bus_num = device_node->rmi_dev->i3cdev->desc->dev->bus->id; + pid = device_node->rmi_dev->i3cdev->desc->info.pid; + } else if (device_node->rmi_dev->client) { + /* I2C device path */ + addr = device_node->rmi_dev->client->addr; + bus_num = device_node->rmi_dev->client->adapter->nr; + pid = 0; /* I2C devices do not have PID */ + } else { + return -EINVAL; + } - if (!temp_status) - continue; + if (!addr) + return -EINVAL; - ret = send_uevent(oob_adata->tsi_dev[i]->dev_static_addr, - temp_status << 24, dev); - if (ret) - dev_dbg(dev, - "Failed to send uevent for temperature alert TSI device index %d Err: %d\n", - i, ret); + /* Send uevent for RAS alert */ + ret = send_uevent(addr, bus_num, status, pid, dev); + if (ret) { + dev_info(dev, "Failed to send uevent for RAS alert (device: 0x%x, err: %d)\n", + addr, ret); } + + /* Clear RAS and RMI status registers */ + mutex_lock(&device_node->rmi_dev->lock); + ret = regmap_write(device_node->rmi_dev->regmap, RAS_STATUS_REG, status); + if (ret) + dev_warn(dev, "Failed to clear RAS status register (device: 0x%x): %d\n", + addr, ret); + + ret = regmap_write(device_node->rmi_dev->regmap, RMI_STATUS_REG, RAS_ALERT_ASYNC); + if (ret) + dev_warn(dev, "Failed to clear RMI status register (device: 0x%x): %d\n", + addr, ret); + + mutex_unlock(&device_node->rmi_dev->lock); + + return ret; /* Alert was processed */ } -/* Process and handle RMI alerts for all RMI devices */ -static void handle_rmi_alerts(struct apml_alertl_data *oob_adata) +/* Handle TSI device alerts */ +static int handle_tsi_device_alert(struct apml_device_node *device_node, struct device *dev) { - struct device *dev = oob_adata->dev; - struct apml_message msg = { 0 }; - int ras_status, ret, i; - - for (i = 0; i < oob_adata->num_of_rmi_devs; i++) { - ras_status = 0; - if (!oob_adata->rmi_dev[i] || !oob_adata->rmi_dev[i]->regmap) { - dev_dbg(dev, - "RMI device at index %d is NULL or regmap missing\n", - i); - continue; + int status = 0, ret; + u8 addr, bus_num; + u64 pid; + + if (!device_node->tsi_dev || !device_node->tsi_dev->regmap) { + dev_warn(dev, "Invalid TSI device found\n"); + return -EINVAL; } - /* Read RAS Status register to identify the RAS error */ - msg.data_in.reg_in[REG_OFF_INDEX] = RAS_STATUS_REG; + /* Protects individual device state and regmap transactions */ + mutex_lock(&device_node->tsi_dev->lock); + /* Read TSI Status register */ + ret = regmap_read(device_node->tsi_dev->regmap, TSI_STATUS_REG, &status); + mutex_unlock(&device_node->tsi_dev->lock); - mutex_lock(&oob_adata->rmi_dev[i]->lock); - ret = regmap_read(oob_adata->rmi_dev[i]->regmap, - msg.data_in.reg_in[REG_OFF_INDEX], - &ras_status); - mutex_unlock(&oob_adata->rmi_dev[i]->lock); + if (ret) { + dev_warn(dev, "Failed to read TSI status from device %d\n", ret); + return ret; + } - if (ret < 0) { - dev_dbg(dev, "Failed to read RAS status of RMI device index %d\n", i); - continue; + if (!status) { + /* No alert status - normal condition */ + return ret; } - if (!ras_status) - continue; + if (device_node->tsi_dev->i3cdev) { + addr = device_node->tsi_dev->dev_static_addr; + bus_num = device_node->tsi_dev->i3cdev->desc->dev->bus->id; + pid = device_node->tsi_dev->i3cdev->desc->info.pid; + } else if (device_node->tsi_dev->client) { + addr = device_node->tsi_dev->client->addr; + bus_num = device_node->tsi_dev->client->adapter->nr; + pid = 0; + } else { + return -EINVAL; + } - ret = send_uevent(oob_adata->rmi_dev[i]->dev_static_addr, ras_status, dev); - if (ret) - dev_dbg(dev, - "Failed to send uevent for RAS alert for device %d Err: %d\n", - i, ret); - - /* Clear the RMI Status and RAS Status register 0x4C */ - mutex_lock(&oob_adata->rmi_dev[i]->lock); - msg.data_in.reg_in[REG_OFF_INDEX] = RAS_STATUS_REG; - ret = regmap_write(oob_adata->rmi_dev[i]->regmap, - msg.data_in.reg_in[REG_OFF_INDEX], - ras_status); - if (ret < 0) - dev_dbg(dev, - "Could not clear RAS status register for device %d\n", - i); - - msg.data_in.reg_in[REG_OFF_INDEX] = RMI_STATUS_REG; - ret = regmap_write(oob_adata->rmi_dev[i]->regmap, - msg.data_in.reg_in[REG_OFF_INDEX], - RAS_ALERT_ASYNC); - mutex_unlock(&oob_adata->rmi_dev[i]->lock); - if (ret < 0) - dev_dbg(dev, - "Could not clear RMI status register at device %d\n", - i); + if (!addr || !bus_num) + return -EINVAL; + + /* Send uevent for temperature alert (shifted to avoid RAS bit overlap) */ + ret = send_uevent(addr, bus_num, status << TSI_STATUS_SHIFT, pid, dev); + if (ret) { + dev_info(dev, "Failed to send uevent for temp alert (device: 0x%x, err: %d)\n", + addr, ret); } + return ret; /* Alert was processed */ } -/* Handles Alert_L interrupts by delegating to TSI and RMI alert handlers */ -static irqreturn_t alert_l_irq_thread_handler(int irq, void *dev_id) +static void handle_apml_alerts(struct device *dev) { - struct apml_alertl_data *oob_adata = (struct apml_alertl_data *)dev_id; - struct device *dev; + struct apml_device_node *device_node; + int ret; - dev = oob_adata->dev; + mutex_lock(&apml_devices_lock); + list_for_each_entry(device_node, &apml_devices, list) { + /* Get a safe reference to the device node */ + if (!kref_get_unless_zero(&device_node->ref)) + continue; - handle_tsi_alerts(oob_adata); - handle_rmi_alerts(oob_adata); + /* Device-specific alert processing */ + switch (device_node->dev_type) { + case APML_RMI_DEVICE: + ret = handle_rmi_device_alert(device_node, dev); + break; + case APML_TSI_DEVICE: + ret = handle_tsi_device_alert(device_node, dev); + break; + default: + dev_warn(dev, "Unknown device type: %d\n", device_node->dev_type); + ret = -EINVAL; + break; + } - return IRQ_HANDLED; + if (ret) { + dev_dbg(dev, "Alert processing failed for device type %d: %d\n", + device_node->dev_type, ret); + } + /* Always release the reference */ + apml_put_device_node(device_node); + } + mutex_unlock(&apml_devices_lock); } -/* Retrieve APML device from device tree */ -static void *get_apml_dev_byphandle(struct device_node *dnode, - const char *phandle_name, - int index) +/* Handles Alert_L interrupts by delegating to unified alert handler */ +static irqreturn_t alert_l_irq_thread_handler(int irq, void *dev_id) { - struct device_node *d_node; - struct device *dev; - void *apml_dev; - - if (!phandle_name || !dnode) - return NULL; - - d_node = of_parse_phandle(dnode, phandle_name, index); - if (IS_ERR_OR_NULL(d_node)) { - pr_err("Failed to parse phandle '%s' at index %d\n", - phandle_name, index); - return NULL; - } - - if (strcmp(phandle_name, "sbrmi") == 0) { - dev = bus_find_device(&i3c_bus_type, NULL, d_node, sbrmi_match_i3c); - if (!dev) { - dev = bus_find_device(&i2c_bus_type, NULL, d_node, sbrmi_match_i2c); - if (IS_ERR_OR_NULL(dev)) { - of_node_put(d_node); - return NULL; - } - } - } else if (strcmp(phandle_name, "sbtsi") == 0) { - dev = bus_find_device(&i3c_bus_type, NULL, d_node, sbtsi_match_i3c); - if (!dev) { - dev = bus_find_device(&i2c_bus_type, NULL, d_node, sbtsi_match_i2c); - if (IS_ERR_OR_NULL(dev)) { - of_node_put(d_node); - return NULL; - } - } - } + struct device *dev = (struct device *)dev_id; - of_node_put(d_node); - apml_dev = dev_get_drvdata(dev); - if (IS_ERR_OR_NULL(apml_dev)) - return NULL; + handle_apml_alerts(dev); - return apml_dev; + return IRQ_HANDLED; } static int apml_alertl_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *dnode = dev->of_node; - struct apml_sbrmi_device **rmi_dev; - struct apml_sbtsi_device **tsi_dev; + struct device_node *np = dev->of_node; struct apml_alertl_data *oob_alert; struct gpio_desc *alertl_gpiod; + int ret; + u8 socket_num = 0; char *irq_name; - u32 irq_num; - u8 socket_num; - int ret, i; - /* Allocate memory to oob_alert_data structure */ - oob_alert = devm_kzalloc(dev, sizeof(struct apml_alertl_data), - GFP_KERNEL); + oob_alert = devm_kzalloc(dev, sizeof(*oob_alert), GFP_KERNEL); if (!oob_alert) return -ENOMEM; - /* identify the number of devices associated with each RMI alert */ - oob_alert->num_of_rmi_devs = of_property_count_elems_of_size(dnode, "sbrmi", - sizeof(phandle)); - - /* identify the number of devices associated with each TSI alert */ - oob_alert->num_of_tsi_devs = of_property_count_elems_of_size(dnode, "sbtsi", - sizeof(phandle)); - - /* Allocate memory as per the number of RMI devices */ - rmi_dev = devm_kzalloc(dev, oob_alert->num_of_rmi_devs * sizeof(struct apml_sbrmi_device), - GFP_KERNEL); - if (!rmi_dev) - return -ENOMEM; - oob_alert->rmi_dev = rmi_dev; - - /* Allocate memory as per the number of TSI devices */ - tsi_dev = devm_kzalloc(dev, oob_alert->num_of_tsi_devs * sizeof(struct apml_sbtsi_device), - GFP_KERNEL); - if (!tsi_dev) - return -ENOMEM; - - oob_alert->tsi_dev = tsi_dev; oob_alert->dev = dev; - /* - * For each of the Alerts get the device associated - * Currently the ALert_L driver identification is only supported - * over I3C. We can add property in dts to identify the bus type - */ - for (i = 0; i < oob_alert->num_of_rmi_devs; i++) { - rmi_dev[i] = get_apml_dev_byphandle(pdev->dev.of_node, "sbrmi", i); - if (!rmi_dev[i]) { - dev_err(dev, "RMI device %d not found\n", i); - return -ENODEV; - } - } - - for (i = 0; i < oob_alert->num_of_tsi_devs; i++) { - tsi_dev[i] = get_apml_dev_byphandle(pdev->dev.of_node, "sbtsi", i); - if (!tsi_dev[i]) { - dev_err(dev, "TSI device %d not found\n", i); - return -ENODEV; - } - } - - /* Get the alert_l gpios, irq_number for the GPIO and register ISR*/ + /* Get the alert_l gpio */ alertl_gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN); - if (IS_ERR(alertl_gpiod)) { - dev_err(&pdev->dev, "Unable to retrieve gpio\n"); + if (IS_ERR(alertl_gpiod)) return PTR_ERR(alertl_gpiod); - } - irq_num = gpiod_to_irq(alertl_gpiod); - if (irq_num < 0) { - dev_err(dev, "No corresponding IRQ for GPIO, error: %d\n", irq_num); - return irq_num; + /* Get IRQ number from GPIO */ + ret = gpiod_to_irq(alertl_gpiod); + if (ret < 0) { + dev_err(dev, + "APML AlertL: No corresponding irq for gpio error: %d\n", + ret); + return ret; } - if (oob_alert->num_of_rmi_devs > 0 && oob_alert->rmi_dev[0]) - socket_num = static_addr_to_socket(oob_alert->rmi_dev[0]->dev_static_addr); - else if (oob_alert->num_of_tsi_devs > 0 && oob_alert->tsi_dev[0]) - socket_num = static_addr_to_socket(oob_alert->tsi_dev[0]->dev_static_addr); - - irq_name = devm_kasprintf(dev, GFP_KERNEL, "apml_irq%u", socket_num); - if (!irq_name) { - dev_dbg(dev, "Failed to allocate IRQ name\n"); - return -ENOMEM; + oob_alert->irq_num = ret; + + /* Try to read socket-id property from DTS */ + ret = of_property_read_u8(np, "socket-num", &socket_num); + if (!ret) { + irq_name = devm_kasprintf(dev, GFP_KERNEL, "apml_irq%u", socket_num); + if (!irq_name) + return -ENOMEM; + } else { + irq_name = devm_kstrdup(dev, "apml_irq", GFP_KERNEL); + if (!irq_name) + return -ENOMEM; } - - dev_dbg(dev, "Register IRQ:%u\n", irq_num); - ret = devm_request_threaded_irq(dev, irq_num, + dev_info(dev, "APML Alert_L for socket %u, IRQ %u\n", socket_num, oob_alert->irq_num); + /* Register threaded IRQ handler */ + ret = devm_request_threaded_irq(dev, oob_alert->irq_num, NULL, - (void *)alert_l_irq_thread_handler, + alert_l_irq_thread_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - irq_name, oob_alert); + irq_name, + dev); if (ret) { - dev_dbg(dev, "Cannot register IRQ:%u\n", irq_num); + dev_err(dev, "Cannot register IRQ:%u\n", oob_alert->irq_num); return ret; } - /* Set the platform data to pdev */ platform_set_drvdata(pdev, oob_alert); - return 0; } static int apml_alertl_remove(struct platform_device *pdev) { + struct apml_alertl_data *alertl_data = platform_get_drvdata(pdev); + + if (alertl_data) + /* Ensure any running interrupt handlers complete */ + synchronize_irq(alertl_data->irq_num); + return 0; } diff --git a/drivers/misc/amd-apml/apml_alertl.h b/drivers/misc/amd-apml/apml_alertl.h index 37568e8b9f5834..880ba2008bdf95 100644 --- a/drivers/misc/amd-apml/apml_alertl.h +++ b/drivers/misc/amd-apml/apml_alertl.h @@ -6,22 +6,13 @@ #ifndef _AMD_APML_ALERT_L__ #define _AMD_APML_ALERT_L__ -#include "sbrmi-common.h" -#include "sbtsi-common.h" - -/* APML_ALERTL Sends uevent to userspace for temparature - * alert and RAS (fatal and non-fatal) error, including - * environmental data such as socket/die and alertl source - * information. - */ +#include "apml_common.h" +/* struct apml_alertl_data - APML Alert_L driver data structure */ struct apml_alertl_data { - struct apml_sbrmi_device **rmi_dev; - struct apml_sbtsi_device **tsi_dev; struct device *dev; - atomic_t removed; - u8 num_of_rmi_devs; - u8 num_of_tsi_devs; + struct gpio_desc *alertl_gpiod; + int irq_num; } __packed; #endif /*_AMD_APML_ALERT_L__*/ From dfb1b37be0fdea6b94a443f249d2601dda903ab0 Mon Sep 17 00:00:00 2001 From: sathya priya kumar Date: Wed, 26 Nov 2025 18:39:38 +0530 Subject: [PATCH 12/12] dt-bindings: misc: apml-alertl: Add APML Alert_L changes -Document device-tree changes for APML Alert_L driver. Reviewed-by: Naveen Krishna Chatradhi Signed-off-by: Akshay Gupta Signed-off-by: sathya priya kumar --- .../bindings/misc/amd,apml-alertl.yaml | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/misc/amd,apml-alertl.yaml b/Documentation/devicetree/bindings/misc/amd,apml-alertl.yaml index d28baa9c0d541f..6aed8ac525d858 100644 --- a/Documentation/devicetree/bindings/misc/amd,apml-alertl.yaml +++ b/Documentation/devicetree/bindings/misc/amd,apml-alertl.yaml @@ -5,8 +5,8 @@ $id: http://devicetree.org/schemas/hwmon/amd,apml-alertl.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: > - Sideband Remote Management Interface (SB-RMI) compliant - AMD APML Alert_L GPIO. + Sideband Remote Management Interface (SB-RMI) and Temperature Sensor Interface + (SB-TSI) compliant AMD APML Alert_L GPIO. maintainers: - Akshay Gupta @@ -34,19 +34,18 @@ properties: GPIO specifier for APML Alert_L line. Specify GPIO controller, GPIO pin and polarity. - sbrmi: - $ref: /schemas/types.yaml#/definitions/phandle - description: ref amd,sbrmi.yaml - - sbtsi: - $ref: /schemas/types.yaml#/definitions/phandle - description: ref amd,sbtsi.yaml + socket-num: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: | + Socket identifier for multi-socket AMD systems. This property identifies + which CPU socket this Alert_L GPIO is associated with. required: - compatible - gpio - - sbrmi - - sbtsi + - socket-num additionalProperties: false @@ -57,7 +56,6 @@ examples: compatible = "apml-alertl"; status = "okay"; gpios = <<pi0_gpio 20 GPIO_ACTIVE_LOW>; - sbrmi = <&sbrmi_p0_iod0>; - sbtsi = <&sbtsi_p0_iod0>; + socket-num = /bits/ 8 <1>; }; ...