├── Kbuild ├── Makefile ├── README.md ├── amd-apml.h ├── apml_alertl.c ├── apml_alertl.h ├── apml_sbtsi.c ├── patches └── 0001-i3c-Export-struct-bus_type-i3c_bus_type-to-include-h.patch ├── sbrmi-common.c ├── sbrmi-common.h └── sbrmi.c /Kbuild: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # Kbuild for AMD APML driver 4 | # 5 | # Copyright (C) 2022 Advanced Micro Devices, Inc. 6 | # 7 | 8 | obj-m += apml_sbrmi.o 9 | apml_sbrmi-objs = sbrmi.o sbrmi-common.o 10 | obj-m += apml_sbtsi.o 11 | obj-m += apml_alertl.o 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # Makefile for AMD APML (SBRMI and SBTSI) drivers 4 | # 5 | # Copyright (C) 2022 Advanced Micro Devices, Inc. 6 | # 7 | 8 | # If KDIR is not specified, assume the development source link 9 | # is in the modules directory for the running kernel 10 | KDIR ?= /lib/modules/`uname -r`/build 11 | 12 | default: 13 | $(MAKE) -C $(KDIR) M=$$PWD modules 14 | 15 | debug: 16 | $(MAKE) CFLAGS_MODULE=-DDEBUG -C $(KDIR) M=$$PWD modules 17 | 18 | modules: default 19 | 20 | modules_install: 21 | $(MAKE) -C $(KDIR) M=$$PWD modules_install 22 | 23 | clean: 24 | $(MAKE) -C $(KDIR) M=$$PWD clean 25 | 26 | help: 27 | @echo "\nThe following make targets are supported:\n" 28 | @echo "default\t\tBuild the driver module (or if no make target is supplied)" 29 | @echo "modules\t\tSame as default" 30 | @echo "debug\t\tBuild the driver module with debug output" 31 | @echo "modules_install\tBuild and install the driver module" 32 | @echo "clean" 33 | @echo 34 | 35 | .PHONY: default debug modules modules_install clean help 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | .. SPDX-License-Identifier: GPL-2.0 2 | # amd apml modules (apml_sbtsi, apml_sbrmi and apml_alert) 3 | 4 | amd-apml: APML interface drivers for BMC 5 | 6 | EPYC processors from AMD provide APML interface for 7 | BMC users to monitor and configure the system parameters 8 | via the Advanced Platform Management List (APML) interface 9 | defined in EPYC processor PPR. 10 | 11 | This chapter defines custom protocols over i2c/i3c bus 12 | - Mailbox 13 | - CPUID [RO] 14 | - MCA MSR {RO] 15 | - RMI/TSI register [RW] 16 | 17 | module_i2c_i3c based sbrmi and sbtsi modules, which 18 | are probed as i2c or i3c client devices, depending on the 19 | platforms DTS. 20 | 21 | https://developer.amd.com/resources/epyc-resources/epyc-specifications 22 | 23 | APMl library provides C API fo the user space application on top of this 24 | module. 25 | 26 | 27 | Disclaimer 28 | =========== 29 | 30 | The amd apml modules are supported only on AMD Family 19h (including 31 | third-generation AMD EPYC processors (codenamed "Milan")) or later 32 | CPUs. Using the amd apml modules on earlier CPUs could produce unexpected 33 | results, and may cause the processor to operate outside of your motherboard 34 | or system specifications. Correspondingly, defaults to only executing on 35 | AMD Family 19h Model (0h ~ 1Fh & 30h ~ 3Fh) server line of processors. 36 | 37 | Interface 38 | --------- 39 | 40 | Both apml_sbtsi and apml_sbrmi modules register a misc_device 41 | to provide ioctl interface to user space, allowing them 42 | to run these custom protocols. 43 | 44 | apml_sbtsi module registers hwmon sensors for monitoring 45 | current temperature, managing max and min thresholds. 46 | 47 | apml_sbrmi module registers hwmon sensors for monitoring 48 | power_cap_max, current power consumption and managing 49 | power_cap. 50 | 51 | 52 | Build and Install 53 | ----------------- 54 | 55 | Kernel development packages for the running kernel need to be installed 56 | prior to building the amd apml modules. A Makefile is provided which should 57 | work with most kernel source trees. 58 | 59 | To cross compile for arm based BMC 60 | 61 | export CC=arm-openbmc-linux-gnueabi-gcc # Or similar 62 | export ARCH=arm 63 | KDIR= 64 | 65 | To build the kernel module: 66 | 67 | #> make 68 | 69 | To install the kernel module: 70 | 71 | #> sudo make modules_install 72 | 73 | To clean the kernel module build directory: 74 | 75 | #> make clean 76 | 77 | 78 | Note: There is a fix required in the upstream linux kerenl header to handle the i3c_dev. 79 | the patch is kept in patches/ folder of this repo. 80 | 81 | Loading 82 | ------- 83 | 84 | If the apml modules were installed you should use the modprobe command to 85 | load the module. 86 | 87 | #> sudo modprobe apml_sbrmi apml_sbtsi 88 | 89 | The apml modules can also be loaded using insmod if the module was not 90 | installed: 91 | 92 | #> sudo insmod ./apml_sbrmi.ko 93 | #> sudo insmod ./apml_sbtsi.ko 94 | 95 | APML_ALERTL 96 | =========== 97 | Disclaimer: apml_alert module is currently experimental and may change in the future 98 | 99 | EPYC processors from AMD provide APML ALERT_L for BMC users to monitor events. 100 | 101 | |-------------------| 102 | | socket SBRMI|==== i2c/i3c bus 103 | | SBTSI|==== i2c/i3c bus 104 | | Alert_L|---- gpio line 105 | |-------------------| 106 | 107 | APML Alert_L is asserted in multiple events: 108 | 1) Machine Check Exception occurs within the system 109 | 2) The processor alerts the SBI on system fatal error event 110 | 3) Set by hardware as a result of a 0x71/0x72/0x73 command completion 111 | 4) Set by firmware to indicate the completion of a mailbox operation 112 | 5) Temperature Alert 113 | 114 | apml_alertl module defines an interface for user space to register their PID for 115 | notifications and an ISR which identifies the source of the interrupt and signals 116 | user space application. 117 | 118 | apml_alertl module depends on apml_sbrmi module for identifying the source. 119 | 120 | DTS node definition for Alert_L module 121 | -------------------------------------- 122 | 123 | required: 124 | - compatible 125 | - status 126 | - gpios: GPIO associated with the Alert_L of the socket 127 | - sbrmi: Array of RMI devices on the system 128 | 129 | Example: 130 | 131 | / { 132 | /* Alert_L associated with socket 0 */ 133 | alertl_sock0 { 134 | compatible = "apml-alertl"; 135 | status = "okay"; 136 | gpios = <&gpio0 ASPEED_GPIO(I, 7) GPIO_ACTIVE_LOW>; 137 | sbrmi = <&sbrmi_p0_1 &sbrmi_p1_1>; 138 | }; 139 | 140 | /* Alert_L associated with socket 1 */ 141 | alertl_sock1 { 142 | compatible = "apml-alertl"; 143 | status = "okay"; 144 | gpios = <&gpio0 ASPEED_GPIO(U, 4) GPIO_ACTIVE_LOW>; 145 | sbrmi = <&sbrmi_p1_1 &sbrmi_p0_1>; 146 | }; 147 | }; 148 | 149 | Loading 150 | ------- 151 | To install apml_alertl driver builtin as module, user can use the modprobe or insmod 152 | command 153 | 154 | #> sudo modprobe apml_alertl 155 | 156 | #> sudo insmod ./apml_alertl.ko 157 | 158 | Note: Dependency on apml_sbrmi.ko module 159 | 160 | Unloading 161 | --------- 162 | 163 | #> sudo rmmod apml_alertl 164 | 165 | If the driver is inbuilt can be removed/inserted by running bind/unbind command. 166 | 167 | #> cd /sys/bus/platform/drivers/alertl 168 | #> echo alertl_rmi# > unbind/bind 169 | 170 | USAGE 171 | ----- 172 | 173 | User need to register the PID of the process with the apml_alertl module, 174 | by writing the PID to the debugfs entry, /sys/kernel/debug/apml_alert/ras_fatal_pid 175 | 176 | #> echo $PID > /sys/kernel/debug/apml_alert/$alert_source 177 | 178 | User application needs to wait for the signal from the apml_alertl module. 179 | 180 | Signal from module carries a "struct kernel_siginfo" with following data 181 | 182 | - si_int: is filled with the event data 183 | [15:0] = ras_status register 184 | [23:16] = rmi static address 185 | 186 | - si_signo: 44 187 | 188 | Currently the kernel driver send signal only in event, RAS status register bit set. 189 | 190 | - In case of RAS fatal error the status register BIT(1) will 191 | set, and ISR clears the bit to avoid interfere with alerts during the ISR. 192 | 193 | Future versions of the driver may include support for other events mentioned above. 194 | -------------------------------------------------------------------------------- /amd-apml.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* 3 | * Copyright (C) 2021-2022 Advanced Micro Devices, Inc. 4 | */ 5 | #ifndef _AMD_APML_H_ 6 | #define _AMD_APML_H_ 7 | 8 | #include 9 | 10 | /* 11 | * Currently signal 33 to 64 are unused, 12 | * using user signal number from that range 13 | */ 14 | #define USR_SIGNAL 44 15 | 16 | enum apml_protocol { 17 | APML_CPUID = 0x1000, 18 | APML_MCA_MSR, 19 | APML_REG, 20 | }; 21 | 22 | /* These are byte indexes into data_in and data_out arrays */ 23 | #define RD_WR_DATA_INDEX 0 24 | #define REG_OFF_INDEX 0 25 | #define REG_VAL_INDEX 4 26 | #define THREAD_LOW_INDEX 4 27 | #define THREAD_HI_INDEX 5 28 | #define EXT_FUNC_INDEX 6 29 | #define RD_FLAG_INDEX 7 30 | 31 | #define MB_DATA_SIZE 4 32 | 33 | struct apml_message { 34 | /* message ids: 35 | * Mailbox Messages: 0x0 ... 0x999 36 | * APML_CPUID: 0x1000 37 | * APML_MCA_MSR: 0x1001 38 | * APML_REG: 0x1002 (RMI & TSI reg access) 39 | */ 40 | __u32 cmd; 41 | 42 | /* 43 | * 8 bit data for reg read, 44 | * 32 bit data in case of mailbox, 45 | * upto 64 bit in case of cpuid and mca msr 46 | */ 47 | union { 48 | __u64 cpu_msr_out; 49 | __u32 mb_out[2]; 50 | __u8 reg_out[8]; 51 | } data_out; 52 | 53 | /* 54 | * [0]...[3] mailbox 32bit input 55 | * cpuid & mca msr, 56 | * rmi rd/wr: reg_offset 57 | * [4][5] cpuid & mca msr: thread 58 | * [4] rmi reg wr: value 59 | * [6] cpuid: ext function & read eax/ebx or ecx/edx 60 | * [7:0] -> bits [7:4] -> ext function & 61 | * bit [0] read eax/ebx or ecx/edx 62 | * [7] read/write functionality 63 | */ 64 | union { 65 | __u64 cpu_msr_in; 66 | __u32 mb_in[2]; 67 | __u8 reg_in[8]; 68 | } data_in; 69 | /* 70 | * Status code is returned in case of CPUID/MCA access 71 | * Error code is returned in case of soft mailbox 72 | */ 73 | __u32 fw_ret_code; 74 | } __attribute__((packed)); 75 | 76 | /* ioctl command for mailbox msgs using generic _IOWR */ 77 | #define SBRMI_BASE_IOCTL_NR 0xF9 78 | #define SBRMI_IOCTL_CMD _IOWR(SBRMI_BASE_IOCTL_NR, 0, struct apml_message) 79 | 80 | #endif /*_AMD_APML_H_*/ 81 | -------------------------------------------------------------------------------- /apml_alertl.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * alert_l.c - Alert_l driver for AMD APML devices 4 | * 5 | * Copyright (C) 2023-2024 Advanced Micro Devices, Inc. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "sbrmi-common.h" 19 | #include "apml_alertl.h" 20 | 21 | #define DRIVER_NAME "apml_alertl" 22 | 23 | #define RAS_STATUS_REG 0x4C 24 | #define STATUS_REG 0x2 25 | #define RAS_ALERT_STATUS BIT(1) 26 | 27 | MODULE_ALIAS("apml_alertl:" DRIVER_NAME); 28 | 29 | #ifdef CONFIG_DEBUG_FS 30 | struct dentry *amd_apml; 31 | struct task_struct *p_task; 32 | u64 proc_pid; 33 | #endif 34 | 35 | static irqreturn_t alert_l_irq_thread_handler(int irq, void *dev_id) 36 | { 37 | struct apml_alertl_data *oob_adata = dev_id; 38 | struct apml_message msg = { 0 }; 39 | struct kernel_siginfo info = {0}; 40 | unsigned int status = 0; 41 | int ret, i; 42 | 43 | /* 44 | * Read RAS Status register to identify the RAS error 45 | * Currently only RAS fatal error is supported 46 | */ 47 | 48 | for (i = 0; i < oob_adata->num_of_rmi_devs; i++) { 49 | if (!oob_adata->rmi_dev[i] || !oob_adata->rmi_dev[i]->regmap) 50 | continue; 51 | msg.data_in.reg_in[REG_OFF_INDEX] = RAS_STATUS_REG; 52 | 53 | ret = regmap_read(oob_adata->rmi_dev[i]->regmap, 54 | msg.data_in.reg_in[REG_OFF_INDEX], 55 | &status); 56 | if (ret < 0) 57 | return ret; 58 | if (status) 59 | break; 60 | } 61 | 62 | if (!status) 63 | return IRQ_HANDLED; 64 | 65 | /* For RAS errors, signal the registered program*/ 66 | info.si_signo = USR_SIGNAL; 67 | info.si_int = status | (oob_adata->rmi_dev[i]->dev_static_addr << 16); 68 | 69 | pr_debug("Sending signal to the process, RAS bit is set sigint is %x\n", info.si_int); 70 | p_task = pid_task(find_get_pid(proc_pid), PIDTYPE_PID); 71 | if (p_task) { 72 | ret = send_sig_info(USR_SIGNAL, &info, p_task); 73 | if (ret < 0) 74 | pr_err("Sending signal to the process, unsuccessful\n"); 75 | /* RAS status(0x4c) and Status register(0x2) bits clear is 76 | * required even if sending signal to user application 77 | * fails. 78 | * So no return even if signal send fails. 79 | */ 80 | } 81 | 82 | /* Clear the RAS Status register */ 83 | if (!oob_adata->rmi_dev[i] || !oob_adata->rmi_dev[i]->regmap) 84 | return -ENODEV; 85 | 86 | mutex_lock(&oob_adata->rmi_dev[i]->lock); 87 | msg.data_in.reg_in[REG_OFF_INDEX] = RAS_STATUS_REG; 88 | ret = regmap_write(oob_adata->rmi_dev[i]->regmap, 89 | msg.data_in.reg_in[REG_OFF_INDEX], 90 | status); 91 | 92 | msg.data_in.reg_in[REG_OFF_INDEX] = STATUS_REG; 93 | ret = regmap_write(oob_adata->rmi_dev[i]->regmap, 94 | msg.data_in.reg_in[REG_OFF_INDEX], 95 | RAS_ALERT_STATUS); 96 | mutex_unlock(&oob_adata->rmi_dev[i]->lock); 97 | if (ret < 0) 98 | return ret; 99 | 100 | return IRQ_HANDLED; 101 | } 102 | 103 | #ifdef CONFIG_DEBUG_FS 104 | static int proc_pid_store(void *data, u64 value) 105 | { 106 | struct task_struct *ptask; 107 | int ret = 0; 108 | 109 | if (value == 0) 110 | return -EINVAL; 111 | /* The new value will override the previous value */ 112 | proc_pid = value; 113 | ptask = pid_task(find_get_pid(proc_pid), PIDTYPE_PID); 114 | if (!ptask) { 115 | pr_err("PID not found\n"); 116 | return -EINVAL; 117 | } 118 | 119 | return ret; 120 | } 121 | 122 | static int proc_pid_show(void *data, u64 *value) 123 | { 124 | *value = proc_pid; 125 | return 0; 126 | } 127 | 128 | DEFINE_DEBUGFS_ATTRIBUTE(proc_pid_fops, proc_pid_show, proc_pid_store, "%llu\n"); 129 | #endif 130 | 131 | static void *get_apml_dev_byphandle(struct device_node *dnode, 132 | const char *phandle_name, 133 | int index) 134 | { 135 | struct device_node *d_node; 136 | struct device *dev; 137 | void *apml_dev; 138 | 139 | if (!phandle_name || !dnode) 140 | return NULL; 141 | 142 | d_node = of_parse_phandle(dnode, phandle_name, index); 143 | if (IS_ERR_OR_NULL(d_node)) 144 | return NULL; 145 | 146 | dev = bus_find_device(&i3c_bus_type, NULL, d_node, sbrmi_match_i3c); 147 | if (!dev) { 148 | dev = bus_find_device(&i2c_bus_type, NULL, d_node, sbrmi_match_i2c); 149 | if (IS_ERR_OR_NULL(dev)) { 150 | of_node_put(d_node); 151 | return NULL; 152 | } 153 | } 154 | 155 | of_node_put(d_node); 156 | apml_dev = dev_get_drvdata(dev); 157 | if (IS_ERR_OR_NULL(apml_dev)) 158 | return NULL; 159 | 160 | return apml_dev; 161 | } 162 | 163 | static int apml_alertl_probe(struct platform_device *pdev) 164 | { 165 | struct device *dev = &pdev->dev; 166 | struct device_node *dnode = dev->of_node; 167 | struct apml_sbrmi_device **rmi_dev; 168 | struct apml_alertl_data *oob_alert; 169 | struct gpio_desc *alertl_gpiod; 170 | u32 irq_num; 171 | u32 num_dev = 0; 172 | int ret = 0; 173 | int i = 0; 174 | 175 | /* Allocate memory to oob_alert_data structure */ 176 | oob_alert = devm_kzalloc(dev, sizeof(struct apml_alertl_data), 177 | GFP_KERNEL); 178 | if (!oob_alert) 179 | return -ENOMEM; 180 | /* identify the number of devices associated with each alert */ 181 | num_dev = of_property_count_elems_of_size(dnode, "sbrmi", 182 | sizeof(phandle)); 183 | oob_alert->num_of_rmi_devs = num_dev; 184 | 185 | /* Allocate memory as per the number of rmi devices */ 186 | rmi_dev = devm_kzalloc(dev, num_dev * sizeof(struct apml_sbrmi_device), GFP_KERNEL); 187 | if (!rmi_dev) 188 | return -ENOMEM; 189 | oob_alert->rmi_dev = rmi_dev; 190 | 191 | /* 192 | * For each of the Alerts get the device associated 193 | * Currently the ALert_L driver identification is only supported 194 | * over I3C. We can add property in dts to identify the bus type 195 | */ 196 | 197 | for (i = 0; i < num_dev; i++) { 198 | rmi_dev[i] = get_apml_dev_byphandle(pdev->dev.of_node, "sbrmi", i); 199 | if (!rmi_dev[i]) { 200 | pr_err("Error getting APML SBRMI device. Exiting\n"); 201 | return -EINVAL; 202 | } 203 | } 204 | 205 | /* Get the alert_l gpios, irq_number for the GPIO and register ISR*/ 206 | alertl_gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN); 207 | if (IS_ERR(alertl_gpiod)) { 208 | dev_err(&pdev->dev, "Unable to retrieve gpio\n"); 209 | return PTR_ERR(alertl_gpiod); 210 | } 211 | 212 | ret = gpiod_to_irq(alertl_gpiod); 213 | if (ret < 0) { 214 | dev_err(dev, "No corresponding irq for gpio error: %d\n", ret); 215 | return ret; 216 | } 217 | irq_num = ret; 218 | pr_debug("Register IRQ:%u\n", irq_num); 219 | ret = devm_request_threaded_irq(dev, irq_num, 220 | NULL, 221 | (void *)alert_l_irq_thread_handler, 222 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 223 | "apml_irq", oob_alert); 224 | if (ret) { 225 | pr_err("Cannot register IRQ:%u\n", irq_num); 226 | return ret; 227 | } 228 | 229 | /* Set the platform data to pdev */ 230 | platform_set_drvdata(pdev, oob_alert); 231 | 232 | /* 233 | * Create a sys entry to register user application PID 234 | * Only one debugfs entry is created for all apml alerts on 235 | * the platform (the idea of debugfs entry is only for RAS consumers) 236 | */ 237 | #ifdef CONFIG_DEBUG_FS 238 | pr_debug("Creating debugfs files"); 239 | if (!amd_apml) { 240 | amd_apml = debugfs_create_dir("apml_alertl", NULL); 241 | if (IS_ERR_OR_NULL(amd_apml)) 242 | return -ENOMEM; 243 | debugfs_create_file("ras_fatal_pid", 0600, amd_apml, oob_alert, &proc_pid_fops); 244 | } 245 | #endif 246 | return 0; 247 | } 248 | 249 | static int alert_remove(struct platform_device *pdev) 250 | { 251 | #ifdef CONFIG_DEBUG_FS 252 | if (amd_apml) { 253 | debugfs_remove_recursive(amd_apml); 254 | amd_apml = NULL; 255 | } 256 | #endif 257 | return 0; 258 | } 259 | 260 | static const struct of_device_id apml_alertl_dt_ids[] = { 261 | { .compatible = "apml-alertl", }, 262 | {}, 263 | }; 264 | MODULE_DEVICE_TABLE(of, apml_alertl_dt_ids); 265 | 266 | static struct platform_driver apml_alertl_driver = { 267 | .driver = { 268 | .name = DRIVER_NAME, 269 | .of_match_table = of_match_ptr(apml_alertl_dt_ids), 270 | }, 271 | .probe = apml_alertl_probe, 272 | .remove = alert_remove, 273 | }; 274 | 275 | module_platform_driver(apml_alertl_driver); 276 | MODULE_AUTHOR("Akshay Gupta "); 277 | MODULE_AUTHOR("Naveenkrishna Chatradhi "); 278 | MODULE_DESCRIPTION("AMD APML ALERT_L Driver"); 279 | MODULE_LICENSE("GPL"); 280 | -------------------------------------------------------------------------------- /apml_alertl.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 | /* 3 | * Copyright (C) 2023-2024 Advanced Micro Devices, Inc. 4 | */ 5 | 6 | #ifndef _AMD_APML_ALERT_L__ 7 | #define _AMD_APML_ALERT_L__ 8 | 9 | #include "sbrmi-common.h" 10 | 11 | /* 12 | * TODO: Handle TSI alerts 13 | */ 14 | struct apml_alertl_data { 15 | struct apml_sbrmi_device **rmi_dev; 16 | u8 num_of_rmi_devs; 17 | } __packed; 18 | 19 | #endif /*_AMD_APML_ALERT_L__*/ 20 | -------------------------------------------------------------------------------- /apml_sbtsi.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * apml_sbtsi.c - hwmon driver for a SBI Temperature Sensor Interface (SB-TSI) 4 | * compliant AMD SoC temperature device. 5 | * Also register to misc driver with an IOCTL. 6 | * 7 | * Copyright (c) 2020, Google Inc. 8 | * Copyright (c) 2020, Kun Yi 9 | * Copyright (C) 2022 Advanced Micro Devices, Inc. 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "amd-apml.h" 28 | 29 | /* 30 | * SB-TSI registers only support SMBus byte data access. "_INT" registers are 31 | * the integer part of a temperature value or limit, and "_DEC" registers are 32 | * corresponding decimal parts. 33 | */ 34 | #define SBTSI_REG_TEMP_INT 0x01 /* RO */ 35 | #define SBTSI_REG_STATUS 0x02 /* RO */ 36 | #define SBTSI_REG_CONFIG 0x03 /* RO */ 37 | #define SBTSI_REG_TEMP_HIGH_INT 0x07 /* RW */ 38 | #define SBTSI_REG_TEMP_LOW_INT 0x08 /* RW */ 39 | #define SBTSI_REG_TEMP_DEC 0x10 /* RW */ 40 | #define SBTSI_REG_TEMP_HIGH_DEC 0x13 /* RW */ 41 | #define SBTSI_REG_TEMP_LOW_DEC 0x14 /* RW */ 42 | 43 | #define SBTSI_CONFIG_READ_ORDER_SHIFT 5 44 | 45 | #define SBTSI_TEMP_MIN 0 46 | #define SBTSI_TEMP_MAX 255875 47 | 48 | /* 49 | * SBTSI_STEP_INC Fractional portion of temperature 50 | * One increment of these bits is equivalent to a step of 0.125 °C 51 | * 52 | * SBTSI_INT_OFFSET Integer offset for temperature value 53 | * 54 | * SBTSI_DEC_OFFSET offset for decimal bits in register[7:5] 55 | * 56 | * SBTSI_DEC_MASK Mask for decimal value 57 | */ 58 | #define SBTSI_STEP_INC 125 59 | #define SBTSI_INT_OFFSET 3 60 | #define SBTSI_DEC_OFFSET 5 61 | #define SBTSI_DEC_MASK 0x7 62 | 63 | struct apml_sbtsi_device { 64 | struct miscdevice sbtsi_misc_dev; 65 | struct regmap *regmap; 66 | struct mutex lock; 67 | u8 dev_static_addr; 68 | } __packed; 69 | 70 | /* 71 | * From SB-TSI spec: CPU temperature readings and limit registers encode the 72 | * temperature in increments of 0.125 from 0 to 255.875. The "high byte" 73 | * register encodes the base-2 of the integer portion, and the upper 3 bits of 74 | * the "low byte" encode in base-2 the decimal portion. 75 | * 76 | * e.g. INT=0x19, DEC=0x20 represents 25.125 degrees Celsius 77 | * 78 | * Therefore temperature in millidegree Celsius = 79 | * (INT + DEC / 256) * 1000 = (INT * 8 + DEC / 32) * 125 80 | */ 81 | static inline int sbtsi_reg_to_mc(s32 integer, s32 decimal) 82 | { 83 | return ((integer << SBTSI_INT_OFFSET) + 84 | (decimal >> SBTSI_DEC_OFFSET)) * SBTSI_STEP_INC; 85 | } 86 | 87 | /* 88 | * Inversely, given temperature in millidegree Celsius 89 | * INT = (TEMP / 125) / 8 90 | * DEC = ((TEMP / 125) % 8) * 32 91 | * Caller have to make sure temp doesn't exceed 255875, the max valid value. 92 | */ 93 | static inline void sbtsi_mc_to_reg(s32 temp, u8 *integer, u8 *decimal) 94 | { 95 | temp /= SBTSI_STEP_INC; 96 | *integer = temp >> SBTSI_INT_OFFSET; 97 | *decimal = (temp & SBTSI_DEC_MASK) << SBTSI_DEC_OFFSET; 98 | } 99 | 100 | static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type, 101 | u32 attr, int channel, long *val) 102 | { 103 | struct apml_sbtsi_device *tsi_dev = dev_get_drvdata(dev); 104 | unsigned int temp_int, temp_dec, cfg; 105 | int ret; 106 | 107 | switch (attr) { 108 | case hwmon_temp_input: 109 | /* 110 | * ReadOrder bit specifies the reading order of integer and 111 | * decimal part of CPU temp for atomic reads. If bit == 0, 112 | * reading integer part triggers latching of the decimal part, 113 | * so integer part should be read first. If bit == 1, read 114 | * order should be reversed. 115 | */ 116 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_CONFIG, &cfg); 117 | if (ret < 0) 118 | return ret; 119 | 120 | mutex_lock(&tsi_dev->lock); 121 | if (cfg & BIT(SBTSI_CONFIG_READ_ORDER_SHIFT)) { 122 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_TEMP_DEC, &temp_dec); 123 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_TEMP_INT, &temp_int); 124 | } else { 125 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_TEMP_INT, &temp_int); 126 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_TEMP_DEC, &temp_dec); 127 | } 128 | mutex_unlock(&tsi_dev->lock); 129 | break; 130 | case hwmon_temp_max: 131 | mutex_lock(&tsi_dev->lock); 132 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_TEMP_HIGH_INT, &temp_int); 133 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_TEMP_HIGH_DEC, &temp_dec); 134 | mutex_unlock(&tsi_dev->lock); 135 | break; 136 | case hwmon_temp_min: 137 | mutex_lock(&tsi_dev->lock); 138 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_TEMP_LOW_INT, &temp_int); 139 | ret = regmap_read(tsi_dev->regmap, SBTSI_REG_TEMP_LOW_DEC, &temp_dec); 140 | mutex_unlock(&tsi_dev->lock); 141 | break; 142 | default: 143 | return -EINVAL; 144 | } 145 | 146 | if (ret < 0) 147 | return ret; 148 | 149 | *val = sbtsi_reg_to_mc(temp_int, temp_dec); 150 | 151 | return 0; 152 | } 153 | 154 | static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type, 155 | u32 attr, int channel, long val) 156 | { 157 | struct apml_sbtsi_device *tsi_dev = dev_get_drvdata(dev); 158 | unsigned int temp_int, temp_dec; 159 | int reg_int, reg_dec, err; 160 | 161 | switch (attr) { 162 | case hwmon_temp_max: 163 | reg_int = SBTSI_REG_TEMP_HIGH_INT; 164 | reg_dec = SBTSI_REG_TEMP_HIGH_DEC; 165 | break; 166 | case hwmon_temp_min: 167 | reg_int = SBTSI_REG_TEMP_LOW_INT; 168 | reg_dec = SBTSI_REG_TEMP_LOW_DEC; 169 | break; 170 | default: 171 | return -EINVAL; 172 | } 173 | 174 | val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX); 175 | sbtsi_mc_to_reg(val, (u8 *)&temp_int, (u8 *)&temp_dec); 176 | 177 | mutex_lock(&tsi_dev->lock); 178 | err = regmap_write(tsi_dev->regmap, reg_int, temp_int); 179 | if (err) 180 | goto exit; 181 | 182 | err = regmap_write(tsi_dev->regmap, reg_dec, temp_dec); 183 | exit: 184 | mutex_unlock(&tsi_dev->lock); 185 | return err; 186 | } 187 | 188 | static umode_t sbtsi_is_visible(const void *data, 189 | enum hwmon_sensor_types type, 190 | u32 attr, int channel) 191 | { 192 | switch (type) { 193 | case hwmon_temp: 194 | switch (attr) { 195 | case hwmon_temp_input: 196 | return 0444; 197 | case hwmon_temp_min: 198 | return 0644; 199 | case hwmon_temp_max: 200 | return 0644; 201 | } 202 | break; 203 | default: 204 | break; 205 | } 206 | return 0; 207 | } 208 | 209 | static const struct hwmon_channel_info *sbtsi_info[] = { 210 | HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), 211 | HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX), 212 | NULL 213 | }; 214 | 215 | static const struct hwmon_ops sbtsi_hwmon_ops = { 216 | .is_visible = sbtsi_is_visible, 217 | .read = sbtsi_read, 218 | .write = sbtsi_write, 219 | }; 220 | 221 | static const struct hwmon_chip_info sbtsi_chip_info = { 222 | .ops = &sbtsi_hwmon_ops, 223 | .info = sbtsi_info, 224 | }; 225 | 226 | static long sbtsi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 227 | { 228 | int __user *arguser = (int __user *)arg; 229 | struct apml_message msg = { 0 }; 230 | struct apml_sbtsi_device *tsi_dev; 231 | int ret; 232 | 233 | if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct apml_message))) 234 | return -EFAULT; 235 | 236 | if (msg.cmd != APML_REG) 237 | return -EINVAL; 238 | 239 | tsi_dev = container_of(fp->private_data, struct apml_sbtsi_device, sbtsi_misc_dev); 240 | if (!tsi_dev) 241 | return -EFAULT; 242 | 243 | mutex_lock(&tsi_dev->lock); 244 | 245 | if (!msg.data_in.reg_in[RD_FLAG_INDEX]) { 246 | ret = regmap_write(tsi_dev->regmap, 247 | msg.data_in.reg_in[REG_OFF_INDEX], 248 | msg.data_in.reg_in[REG_VAL_INDEX]); 249 | } else { 250 | ret = regmap_read(tsi_dev->regmap, 251 | msg.data_in.reg_in[REG_OFF_INDEX], 252 | (int *)&msg.data_out.reg_out[RD_WR_DATA_INDEX]); 253 | if (ret) 254 | goto out; 255 | 256 | if (copy_to_user(arguser, &msg, sizeof(struct apml_message))) 257 | ret = -EFAULT; 258 | } 259 | out: 260 | mutex_unlock(&tsi_dev->lock); 261 | return ret; 262 | } 263 | 264 | static const struct file_operations sbtsi_fops = { 265 | .owner = THIS_MODULE, 266 | .unlocked_ioctl = sbtsi_ioctl, 267 | .compat_ioctl = sbtsi_ioctl, 268 | }; 269 | 270 | static int create_misc_tsi_device(struct apml_sbtsi_device *tsi_dev, 271 | struct device *dev) 272 | { 273 | int ret; 274 | 275 | tsi_dev->sbtsi_misc_dev.name = devm_kasprintf(dev, GFP_KERNEL, 276 | "sbtsi-%x", tsi_dev->dev_static_addr); 277 | tsi_dev->sbtsi_misc_dev.minor = MISC_DYNAMIC_MINOR; 278 | tsi_dev->sbtsi_misc_dev.fops = &sbtsi_fops; 279 | tsi_dev->sbtsi_misc_dev.parent = dev; 280 | tsi_dev->sbtsi_misc_dev.nodename = devm_kasprintf(dev, GFP_KERNEL, 281 | "sbtsi-%x", tsi_dev->dev_static_addr); 282 | tsi_dev->sbtsi_misc_dev.mode = 0600; 283 | 284 | ret = misc_register(&tsi_dev->sbtsi_misc_dev); 285 | if (ret) 286 | return ret; 287 | 288 | dev_info(dev, "register %s device\n", tsi_dev->sbtsi_misc_dev.name); 289 | return ret; 290 | } 291 | 292 | static int sbtsi_i3c_probe(struct i3c_device *i3cdev) 293 | { 294 | struct device *dev = &i3cdev->dev; 295 | struct device *hwmon_dev; 296 | struct apml_sbtsi_device *tsi_dev; 297 | struct regmap_config sbtsi_i3c_regmap_config = { 298 | .reg_bits = 8, 299 | .val_bits = 8, 300 | }; 301 | struct regmap *regmap; 302 | 303 | regmap = devm_regmap_init_i3c(i3cdev, &sbtsi_i3c_regmap_config); 304 | if (IS_ERR(regmap)) { 305 | dev_err(&i3cdev->dev, "Failed to register i3c regmap %d\n", 306 | (int)PTR_ERR(regmap)); 307 | return PTR_ERR(regmap); 308 | } 309 | 310 | tsi_dev = devm_kzalloc(dev, sizeof(struct apml_sbtsi_device), GFP_KERNEL); 311 | if (!tsi_dev) 312 | return -ENOMEM; 313 | 314 | tsi_dev->regmap = regmap; 315 | mutex_init(&tsi_dev->lock); 316 | 317 | dev_set_drvdata(dev, (void *)tsi_dev); 318 | hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbtsi_i3c", tsi_dev, 319 | &sbtsi_chip_info, NULL); 320 | 321 | if (!hwmon_dev) 322 | return PTR_ERR_OR_ZERO(hwmon_dev); 323 | 324 | /* Need to verify for the static address for i3cdev */ 325 | tsi_dev->dev_static_addr = i3cdev->desc->info.static_addr; 326 | 327 | return create_misc_tsi_device(tsi_dev, dev); 328 | } 329 | 330 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) 331 | static int sbtsi_i2c_probe(struct i2c_client *client, 332 | const struct i2c_device_id *tsi_id) 333 | #else 334 | static int sbtsi_i2c_probe(struct i2c_client *client) 335 | #endif 336 | { 337 | struct device *dev = &client->dev; 338 | struct device *hwmon_dev; 339 | struct apml_sbtsi_device *tsi_dev; 340 | struct regmap_config sbtsi_i2c_regmap_config = { 341 | .reg_bits = 8, 342 | .val_bits = 8, 343 | }; 344 | 345 | tsi_dev = devm_kzalloc(dev, sizeof(struct apml_sbtsi_device), GFP_KERNEL); 346 | if (!tsi_dev) 347 | return -ENOMEM; 348 | 349 | mutex_init(&tsi_dev->lock); 350 | tsi_dev->regmap = devm_regmap_init_i2c(client, &sbtsi_i2c_regmap_config); 351 | if (IS_ERR(tsi_dev->regmap)) 352 | return PTR_ERR(tsi_dev->regmap); 353 | 354 | dev_set_drvdata(dev, (void *)tsi_dev); 355 | 356 | hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, 357 | tsi_dev, 358 | &sbtsi_chip_info, 359 | NULL); 360 | 361 | if (!hwmon_dev) 362 | return PTR_ERR_OR_ZERO(hwmon_dev); 363 | 364 | tsi_dev->dev_static_addr = client->addr; 365 | 366 | return create_misc_tsi_device(tsi_dev, dev); 367 | } 368 | 369 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) 370 | static int sbtsi_i3c_remove(struct i3c_device *i3cdev) 371 | #else 372 | static void sbtsi_i3c_remove(struct i3c_device *i3cdev) 373 | #endif 374 | { 375 | struct apml_sbtsi_device *tsi_dev = dev_get_drvdata(&i3cdev->dev); 376 | 377 | if (tsi_dev) 378 | misc_deregister(&tsi_dev->sbtsi_misc_dev); 379 | 380 | dev_info(&i3cdev->dev, "Removed sbtsi-i3c driver\n"); 381 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) 382 | return 0; 383 | #endif 384 | } 385 | 386 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) 387 | static int sbtsi_i2c_remove(struct i2c_client *client) 388 | #else 389 | static void sbtsi_i2c_remove(struct i2c_client *client) 390 | #endif 391 | { 392 | struct apml_sbtsi_device *tsi_dev = dev_get_drvdata(&client->dev); 393 | 394 | if (tsi_dev) 395 | misc_deregister(&tsi_dev->sbtsi_misc_dev); 396 | 397 | dev_info(&client->dev, "Removed sbtsi driver\n"); 398 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) 399 | return 0; 400 | #endif 401 | } 402 | 403 | static const struct i3c_device_id sbtsi_i3c_id[] = { 404 | I3C_DEVICE_EXTRA_INFO(0x112, 0, 0x1, NULL), 405 | {} 406 | }; 407 | MODULE_DEVICE_TABLE(i3c, sbtsi_i3c_id); 408 | 409 | static struct i3c_driver sbtsi_i3c_driver = { 410 | .driver = { 411 | .name = "sbtsi_i3c", 412 | }, 413 | .probe = sbtsi_i3c_probe, 414 | .remove = sbtsi_i3c_remove, 415 | .id_table = sbtsi_i3c_id, 416 | }; 417 | 418 | static const struct i2c_device_id sbtsi_id[] = { 419 | {"sbtsi", 0}, 420 | {} 421 | }; 422 | MODULE_DEVICE_TABLE(i2c, sbtsi_id); 423 | 424 | static const struct of_device_id __maybe_unused sbtsi_of_match[] = { 425 | { 426 | .compatible = "amd,sbtsi", 427 | }, 428 | { }, 429 | }; 430 | MODULE_DEVICE_TABLE(of, sbtsi_of_match); 431 | 432 | static struct i2c_driver sbtsi_driver = { 433 | .class = I2C_CLASS_HWMON, 434 | .driver = { 435 | .name = "sbtsi", 436 | .of_match_table = of_match_ptr(sbtsi_of_match), 437 | }, 438 | .probe = sbtsi_i2c_probe, 439 | .remove = sbtsi_i2c_remove, 440 | .id_table = sbtsi_id, 441 | }; 442 | 443 | module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver) 444 | 445 | MODULE_AUTHOR("Kun Yi "); 446 | MODULE_DESCRIPTION("Hwmon driver for AMD SB-TSI emulated sensor"); 447 | MODULE_LICENSE("GPL"); 448 | -------------------------------------------------------------------------------- /patches/0001-i3c-Export-struct-bus_type-i3c_bus_type-to-include-h.patch: -------------------------------------------------------------------------------- 1 | From 1c1cc4879a67e28ad9fb080d4fa4383a525af7cd Mon Sep 17 00:00:00 2001 2 | From: sathya priya kumar 3 | Date: Thu, 22 Feb 2024 02:44:58 -0500 4 | Subject: i3c: move i3c_bus_type definition to linux header 5 | 6 | At present, "struct bus_type i3c_bus_type" is defined in 7 | driver/i3c/internals.h. 8 | Moving it to a standard include path, this way i3c client devices 9 | outside driver/i3c/ can utilize it. 10 | 11 | Signed-off-by: Akshay Gupta 12 | Signed-off-by: Naveen Krishna Chatradhi 13 | --- 14 | drivers/i3c/internals.h | 1 - 15 | drivers/i3c/master.c | 1 + 16 | include/linux/i3c/device.h | 1 + 17 | 3 files changed, 2 insertions(+), 1 deletion(-) 18 | 19 | diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h 20 | index 908a807badaf..658f6303da05 100644 21 | --- a/drivers/i3c/internals.h 22 | +++ b/drivers/i3c/internals.h 23 | @@ -10,7 +10,6 @@ 24 | 25 | #include 26 | 27 | -extern struct bus_type i3c_bus_type; 28 | 29 | void i3c_bus_normaluse_lock(struct i3c_bus *bus); 30 | void i3c_bus_normaluse_unlock(struct i3c_bus *bus); 31 | diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c 32 | index 87283e4a4607..e7f35e48dc30 100644 33 | --- a/drivers/i3c/master.c 34 | +++ b/drivers/i3c/master.c 35 | @@ -340,6 +340,7 @@ struct bus_type i3c_bus_type = { 36 | .probe = i3c_device_probe, 37 | .remove = i3c_device_remove, 38 | }; 39 | +EXPORT_SYMBOL_GPL(i3c_bus_type); 40 | 41 | static enum i3c_addr_slot_status 42 | i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr) 43 | diff --git a/include/linux/i3c/device.h b/include/linux/i3c/device.h 44 | index 90fa83464f00..df685520d11b 100644 45 | --- a/include/linux/i3c/device.h 46 | +++ b/include/linux/i3c/device.h 47 | @@ -15,6 +15,7 @@ 48 | #include 49 | #include 50 | 51 | +extern struct bus_type i3c_bus_type; 52 | /** 53 | * enum i3c_error_code - I3C error codes 54 | * 55 | -- 56 | 2.39.3 57 | 58 | -------------------------------------------------------------------------------- /sbrmi-common.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * sbrmi-common.c - file defining SB-RMI protocols 4 | * compliant AMD SoC device. 5 | * 6 | * Copyright (C) 2021-2022 Advanced Micro Devices, Inc. 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "sbrmi-common.h" 14 | 15 | /* Mask for Status Register bit[1] */ 16 | #define SW_ALERT_MASK 0x2 17 | /* Mask to check H/W Alert status bit */ 18 | #define HW_ALERT_MASK 0x80 19 | 20 | /* Software Interrupt for triggering */ 21 | #define START_CMD 0x80 22 | #define TRIGGER_MAILBOX 0x01 23 | 24 | /* Mailbox error with additional data */ 25 | #define ERR_WITH_DATA 0x5 26 | 27 | /* Default message lengths as per APML command protocol */ 28 | /* MSR */ 29 | #define MSR_RD_REG_LEN 0xa 30 | #define MSR_WR_REG_LEN 0x8 31 | #define MSR_WR_REG_LEN_v21 0x9 32 | #define MSR_RD_DATA_LEN 0x8 33 | #define MSR_WR_DATA_LEN 0x7 34 | #define MSR_WR_DATA_LEN_v21 0x8 35 | /* CPUID */ 36 | #define CPUID_RD_DATA_LEN 0x8 37 | #define CPUID_WR_DATA_LEN 0x8 38 | #define CPUID_WR_DATA_LEN_v21 0x9 39 | #define CPUID_RD_REG_LEN 0xa 40 | #define CPUID_WR_REG_LEN 0x9 41 | #define CPUID_WR_REG_LEN_v21 0xa 42 | 43 | /* CPUID MSR Command Ids */ 44 | #define CPUID_MCA_CMD 0x73 45 | #define RD_CPUID_CMD 0x91 46 | #define RD_MCA_CMD 0x86 47 | 48 | /* SB-RMI registers */ 49 | enum sbrmi_reg { 50 | SBRMI_REV = 0x0, 51 | SBRMI_CTRL = 0x01, 52 | SBRMI_STATUS, 53 | SBRMI_OUTBNDMSG0 = 0x30, 54 | SBRMI_OUTBNDMSG1, 55 | SBRMI_OUTBNDMSG2, 56 | SBRMI_OUTBNDMSG3, 57 | SBRMI_OUTBNDMSG4, 58 | SBRMI_OUTBNDMSG5, 59 | SBRMI_OUTBNDMSG6, 60 | SBRMI_OUTBNDMSG7, 61 | SBRMI_INBNDMSG0, 62 | SBRMI_INBNDMSG1, 63 | SBRMI_INBNDMSG2, 64 | SBRMI_INBNDMSG3, 65 | SBRMI_INBNDMSG4, 66 | SBRMI_INBNDMSG5, 67 | SBRMI_INBNDMSG6, 68 | SBRMI_INBNDMSG7, 69 | SBRMI_SW_INTERRUPT, 70 | SBRMI_THREAD128CS = 0x4b, 71 | }; 72 | 73 | /* input for bulk write to v21 of CPUID and MSR protocol */ 74 | struct cpu_msr_indata_v21 { 75 | u8 wr_len; /* const value */ 76 | u8 rd_len; /* const value */ 77 | u8 proto_cmd; /* const value */ 78 | u16 thread; /* thread number */ 79 | union { 80 | u8 reg_offset[4]; /* input value */ 81 | u32 value; 82 | }; 83 | u8 ext; /* extended function */ 84 | } __packed; 85 | 86 | /* input for bulk write to v20 of CPUID and MSR protocol */ 87 | struct cpu_msr_indata { 88 | u8 wr_len; /* const value */ 89 | u8 rd_len; /* const value */ 90 | u8 proto_cmd; /* const value */ 91 | u8 thread; /* thread number */ 92 | union { 93 | u8 reg_offset[4]; /* input value */ 94 | u32 value; 95 | }; 96 | u8 ext; /* extended function */ 97 | } __packed; 98 | 99 | /* output for bulk read from CPUID and MSR protocol */ 100 | struct cpu_msr_outdata { 101 | u8 num_bytes; /* number of bytes return */ 102 | u8 status; /* Protocol status code */ 103 | union { 104 | u64 value; 105 | u8 reg_data[8]; 106 | }; 107 | } __packed; 108 | 109 | #define prepare_mca_msr_input_message(input, thread_id, data_in, wr_data_len) \ 110 | input.rd_len = MSR_RD_DATA_LEN, \ 111 | input.wr_len = wr_data_len, \ 112 | input.proto_cmd = RD_MCA_CMD, \ 113 | input.thread = thread_id << 1, \ 114 | input.value = data_in 115 | 116 | #define prepare_cpuid_input_message(input, thread_id, func, ext_func, wr_data_len) \ 117 | input.rd_len = CPUID_RD_DATA_LEN, \ 118 | input.wr_len = wr_data_len, \ 119 | input.proto_cmd = RD_CPUID_CMD, \ 120 | input.thread = thread_id << 1, \ 121 | input.value = func, \ 122 | input.ext = ext_func 123 | 124 | static int sbrmi_get_rev(struct apml_sbrmi_device *rmi_dev) 125 | { 126 | struct apml_message msg = { 0 }; 127 | int ret; 128 | 129 | msg.data_in.reg_in[REG_OFF_INDEX] = SBRMI_REV; 130 | msg.data_in.reg_in[RD_FLAG_INDEX] = 1; 131 | ret = regmap_read(rmi_dev->regmap, 132 | msg.data_in.reg_in[REG_OFF_INDEX], 133 | &msg.data_out.mb_out[RD_WR_DATA_INDEX]); 134 | if (ret < 0) 135 | return ret; 136 | 137 | rmi_dev->rev = msg.data_out.reg_out[RD_WR_DATA_INDEX]; 138 | return 0; 139 | } 140 | 141 | /* 142 | * For Mailbox command software alert status bit is set by firmware 143 | * to indicate command completion 144 | * For RMI Rev 0x20, new h/w status bit is introduced. which is used 145 | * by firmware to indicate completion of commands (0x71, 0x72, 0x73). 146 | * wait for the status bit to be set by the firmware before 147 | * reading the data out. 148 | */ 149 | static int sbrmi_wait_status(struct apml_sbrmi_device *rmi_dev, 150 | int *status, int mask) 151 | { 152 | int ret, retry = 100; 153 | 154 | do { 155 | ret = regmap_read(rmi_dev->regmap, SBRMI_STATUS, status); 156 | if (ret < 0) 157 | return ret; 158 | 159 | if (*status & mask) 160 | break; 161 | 162 | /* Wait 1~2 second for firmware to return data out */ 163 | if (retry > 95) 164 | usleep_range(50, 100); 165 | else 166 | usleep_range(10000, 20000); 167 | } while (retry--); 168 | 169 | if (retry < 0) 170 | ret = -ETIMEDOUT; 171 | return ret; 172 | } 173 | 174 | static int msr_datain_v20(struct apml_sbrmi_device *rmi_dev, 175 | struct apml_message *msg) 176 | { 177 | struct cpu_msr_indata input = {0}; 178 | int ret, val = 0; 179 | u16 thread; 180 | 181 | thread = msg->data_in.reg_in[THREAD_LOW_INDEX] | 182 | msg->data_in.reg_in[THREAD_HI_INDEX] << 8; 183 | 184 | /* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */ 185 | if (thread > 127) { 186 | thread -= 128; 187 | val = 1; 188 | } 189 | ret = regmap_write(rmi_dev->regmap, SBRMI_THREAD128CS, val); 190 | if (ret < 0) 191 | return ret; 192 | 193 | prepare_mca_msr_input_message(input, thread, 194 | msg->data_in.mb_in[RD_WR_DATA_INDEX], 195 | MSR_WR_DATA_LEN); 196 | 197 | ret = regmap_bulk_write(rmi_dev->regmap, CPUID_MCA_CMD, 198 | &input, MSR_WR_REG_LEN); 199 | return ret; 200 | } 201 | 202 | static int msr_datain_v21(struct apml_sbrmi_device *rmi_dev, 203 | struct apml_message *msg) 204 | { 205 | struct cpu_msr_indata_v21 input = {0}; 206 | int ret; 207 | u16 thread; 208 | 209 | thread = msg->data_in.reg_in[THREAD_LOW_INDEX] | 210 | msg->data_in.reg_in[THREAD_HI_INDEX] << 8; 211 | 212 | prepare_mca_msr_input_message(input, thread, 213 | msg->data_in.mb_in[RD_WR_DATA_INDEX], 214 | MSR_WR_DATA_LEN_v21); 215 | 216 | ret = regmap_bulk_write(rmi_dev->regmap, CPUID_MCA_CMD, 217 | &input, MSR_WR_REG_LEN_v21); 218 | return ret; 219 | } 220 | 221 | /* MCA MSR protocol */ 222 | int rmi_mca_msr_read(struct apml_sbrmi_device *rmi_dev, 223 | struct apml_message *msg) 224 | { 225 | struct cpu_msr_outdata output = {0}; 226 | int ret; 227 | int hw_status; 228 | 229 | if (!rmi_dev->regmap) 230 | return ENODEV; 231 | 232 | /* cache the rev value to identify if protocol is supported or not */ 233 | if (!rmi_dev->rev) { 234 | ret = sbrmi_get_rev(rmi_dev); 235 | if (ret < 0) 236 | return ret; 237 | } 238 | 239 | switch(rmi_dev->rev) { 240 | /* MCA MSR protocol for REV 0x10 is not supported*/ 241 | case 0x10: 242 | return -EOPNOTSUPP; 243 | case 0x20: 244 | ret = msr_datain_v20(rmi_dev, msg); 245 | if (ret < 0) 246 | goto exit_unlock; 247 | 248 | break; 249 | case 0x21: 250 | ret = msr_datain_v21(rmi_dev, msg); 251 | if (ret < 0) 252 | goto exit_unlock; 253 | break; 254 | default: 255 | return -EOPNOTSUPP; 256 | } 257 | 258 | ret = sbrmi_wait_status(rmi_dev, &hw_status, HW_ALERT_MASK); 259 | if (ret < 0) 260 | goto exit_unlock; 261 | 262 | ret = regmap_bulk_read(rmi_dev->regmap, CPUID_MCA_CMD, 263 | &output, MSR_RD_REG_LEN); 264 | if (ret < 0) 265 | goto exit_unlock; 266 | 267 | ret = regmap_write(rmi_dev->regmap, SBRMI_STATUS, 268 | HW_ALERT_MASK); 269 | if (ret < 0) 270 | goto exit_unlock; 271 | 272 | if (output.num_bytes != MSR_RD_REG_LEN - 1) { 273 | ret = -EMSGSIZE; 274 | goto exit_unlock; 275 | } 276 | if (output.status) { 277 | ret = -EPROTOTYPE; 278 | msg->fw_ret_code = output.status; 279 | goto exit_unlock; 280 | } 281 | msg->data_out.cpu_msr_out = output.value; 282 | 283 | exit_unlock: 284 | return ret; 285 | } 286 | 287 | static int cpuid_datain_v20(struct apml_sbrmi_device *rmi_dev, 288 | struct apml_message *msg) 289 | { 290 | struct cpu_msr_indata input = {0}; 291 | int ret, val = 0; 292 | u16 thread; 293 | 294 | thread = msg->data_in.reg_in[THREAD_LOW_INDEX] | 295 | msg->data_in.reg_in[THREAD_HI_INDEX] << 8; 296 | 297 | /* Thread > 127, Thread128 CS register, 1'b1 needs to be set to 1 */ 298 | if (thread > 127) { 299 | thread -= 128; 300 | val = 1; 301 | } 302 | ret = regmap_write(rmi_dev->regmap, SBRMI_THREAD128CS, val); 303 | if (ret < 0) 304 | return ret; 305 | prepare_cpuid_input_message(input, thread, 306 | msg->data_in.mb_in[RD_WR_DATA_INDEX], 307 | msg->data_in.reg_in[EXT_FUNC_INDEX], 308 | CPUID_WR_DATA_LEN); 309 | 310 | ret = regmap_bulk_write(rmi_dev->regmap, CPUID_MCA_CMD, 311 | &input, CPUID_WR_REG_LEN); 312 | return ret; 313 | } 314 | 315 | static int cpuid_datain_v21(struct apml_sbrmi_device *rmi_dev, 316 | struct apml_message *msg) 317 | { 318 | struct cpu_msr_indata_v21 input = {0}; 319 | int ret; 320 | u16 thread; 321 | 322 | thread = msg->data_in.reg_in[THREAD_LOW_INDEX] | 323 | msg->data_in.reg_in[THREAD_HI_INDEX] << 8; 324 | 325 | prepare_cpuid_input_message(input, thread, 326 | msg->data_in.mb_in[RD_WR_DATA_INDEX], 327 | msg->data_in.reg_in[EXT_FUNC_INDEX], 328 | CPUID_WR_DATA_LEN_v21); 329 | 330 | ret = regmap_bulk_write(rmi_dev->regmap, CPUID_MCA_CMD, 331 | &input, CPUID_WR_REG_LEN_v21); 332 | return ret; 333 | } 334 | 335 | /* CPUID protocol */ 336 | int rmi_cpuid_read(struct apml_sbrmi_device *rmi_dev, 337 | struct apml_message *msg) 338 | { 339 | struct cpu_msr_outdata output = {0}; 340 | int ret, hw_status; 341 | 342 | if (!rmi_dev->regmap) 343 | return ENODEV; 344 | 345 | /* cache the rev value to identify if protocol is supported or not */ 346 | if (!rmi_dev->rev) { 347 | ret = sbrmi_get_rev(rmi_dev); 348 | if (ret < 0) 349 | return ret; 350 | } 351 | 352 | switch(rmi_dev->rev) { 353 | /* CPUID protocol for REV 0x10 is not supported*/ 354 | case 0x10: 355 | return -EOPNOTSUPP; 356 | case 0x20: 357 | ret = cpuid_datain_v20(rmi_dev, msg); 358 | if (ret < 0) 359 | goto exit_unlock; 360 | break; 361 | case 0x21: 362 | ret = cpuid_datain_v21(rmi_dev, msg); 363 | if (ret < 0) 364 | goto exit_unlock; 365 | break; 366 | default: 367 | return -EOPNOTSUPP; 368 | } 369 | 370 | ret = sbrmi_wait_status(rmi_dev, &hw_status, HW_ALERT_MASK); 371 | if (ret < 0) 372 | goto exit_unlock; 373 | 374 | ret = regmap_bulk_read(rmi_dev->regmap, CPUID_MCA_CMD, 375 | &output, CPUID_RD_REG_LEN); 376 | if (ret < 0) 377 | goto exit_unlock; 378 | 379 | ret = regmap_write(rmi_dev->regmap, SBRMI_STATUS, 380 | HW_ALERT_MASK); 381 | if (ret < 0) 382 | goto exit_unlock; 383 | 384 | if (output.num_bytes != CPUID_RD_REG_LEN - 1) { 385 | ret = -EMSGSIZE; 386 | goto exit_unlock; 387 | } 388 | if (output.status) { 389 | ret = -EPROTOTYPE; 390 | msg->fw_ret_code = output.status; 391 | goto exit_unlock; 392 | } 393 | msg->data_out.cpu_msr_out = output.value; 394 | exit_unlock: 395 | return ret; 396 | } 397 | 398 | static int esmi_oob_clear_status_alert(struct apml_sbrmi_device *rmi_dev) 399 | { 400 | int sw_status, ret; 401 | 402 | ret = regmap_read(rmi_dev->regmap, SBRMI_STATUS, 403 | &sw_status); 404 | if (ret < 0) 405 | return ret; 406 | 407 | if (!(sw_status & SW_ALERT_MASK)) 408 | return 0; 409 | 410 | return regmap_write(rmi_dev->regmap, SBRMI_STATUS, 411 | SW_ALERT_MASK); 412 | } 413 | 414 | int rmi_mailbox_xfer(struct apml_sbrmi_device *rmi_dev, 415 | struct apml_message *msg) 416 | { 417 | unsigned int bytes = 0, ec = 0; 418 | int i, ret; 419 | int sw_status; 420 | u8 byte = 0; 421 | 422 | if (!rmi_dev->regmap) 423 | return ENODEV; 424 | 425 | msg->fw_ret_code = 0; 426 | 427 | ret = esmi_oob_clear_status_alert(rmi_dev); 428 | if (ret < 0) 429 | goto exit_unlock; 430 | 431 | /* Indicate firmware a command is to be serviced */ 432 | ret = regmap_write(rmi_dev->regmap, SBRMI_INBNDMSG7, START_CMD); 433 | if (ret < 0) 434 | goto exit_unlock; 435 | 436 | /* Write the command to SBRMI::InBndMsg_inst0 */ 437 | ret = regmap_write(rmi_dev->regmap, SBRMI_INBNDMSG0, msg->cmd); 438 | if (ret < 0) 439 | goto exit_unlock; 440 | 441 | /* 442 | * For both read and write the initiator (BMC) writes 443 | * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1] 444 | * SBRMI_x3C(MSB):SBRMI_x39(LSB) 445 | */ 446 | for (i = 0; i < MB_DATA_SIZE; i++) { 447 | byte = msg->data_in.reg_in[i]; 448 | ret = regmap_write(rmi_dev->regmap, SBRMI_INBNDMSG1 + i, byte); 449 | if (ret < 0) 450 | goto exit_unlock; 451 | } 452 | 453 | /* 454 | * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to 455 | * perform the requested read or write command 456 | */ 457 | ret = regmap_write(rmi_dev->regmap, SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX); 458 | if (ret) 459 | goto exit_unlock; 460 | 461 | /* 462 | * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate 463 | * an ALERT (if enabled) to initiator (BMC) to indicate completion 464 | * of the requested command 465 | */ 466 | ret = sbrmi_wait_status(rmi_dev, &sw_status, SW_ALERT_MASK); 467 | if (ret) 468 | goto exit_unlock; 469 | 470 | ret = regmap_read(rmi_dev->regmap, SBRMI_OUTBNDMSG7, &ec); 471 | if (ret || (ec && ec != ERR_WITH_DATA)) 472 | goto exit_clear_alert; 473 | 474 | /* 475 | * For a read operation, the initiator (BMC) reads the firmware 476 | * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1] 477 | * {SBRMI_x34(MSB):SBRMI_x31(LSB)}. 478 | */ 479 | if (msg->data_in.reg_in[RD_FLAG_INDEX]) { 480 | for (i = 0; i < MB_DATA_SIZE; i++) { 481 | ret = regmap_read(rmi_dev->regmap, 482 | SBRMI_OUTBNDMSG1 + i, &bytes); 483 | if (ret < 0) 484 | break; 485 | msg->data_out.reg_out[i] = bytes; 486 | } 487 | } 488 | exit_clear_alert: 489 | /* 490 | * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the 491 | * ALERT to initiator 492 | */ 493 | ret = regmap_write(rmi_dev->regmap, SBRMI_STATUS, 494 | SW_ALERT_MASK); 495 | if (ec) { 496 | ret = -EPROTOTYPE; 497 | msg->fw_ret_code = ec; 498 | } 499 | exit_unlock: 500 | return ret; 501 | } 502 | -------------------------------------------------------------------------------- /sbrmi-common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* 3 | * Copyright (C) 2021-2022 Advanced Micro Devices, Inc. 4 | */ 5 | 6 | #ifndef _AMD_APML_SBRMI_H_ 7 | #define _AMD_APML_SBRMI_H_ 8 | 9 | #include 10 | #include "amd-apml.h" 11 | 12 | /* Each client has this additional data */ 13 | /* in_progress: set during any transaction, mailbox/cpuid/mcamsr/readreg, 14 | * to indicate a transaction is in progress. 15 | * no_new_trans: set in rmmod/unbind path to indicate, 16 | * not to accept new transactions 17 | */ 18 | struct apml_sbrmi_device { 19 | struct miscdevice sbrmi_misc_dev; 20 | struct completion misc_fops_done; 21 | struct i3c_device *i3cdev; 22 | struct i2c_client *client; 23 | struct regmap *regmap; 24 | struct mutex lock; 25 | u32 pwr_limit_max; 26 | atomic_t in_progress; 27 | atomic_t no_new_trans; 28 | u8 rev; 29 | u8 dev_static_addr; 30 | } __packed; 31 | 32 | int rmi_mca_msr_read(struct apml_sbrmi_device *rmi_dev, 33 | struct apml_message *msg); 34 | int rmi_cpuid_read(struct apml_sbrmi_device *rmi_dev, 35 | struct apml_message *msg); 36 | int rmi_mailbox_xfer(struct apml_sbrmi_device *rmi_dev, 37 | struct apml_message *msg); 38 | int sbrmi_match_i3c(struct device *dev, const void *data); 39 | int sbrmi_match_i2c(struct device *dev, const void *data); 40 | #endif /*_AMD_APML_SBRMI_H_*/ 41 | -------------------------------------------------------------------------------- /sbrmi.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * sbrmi.c - hwmon driver for a SB-RMI mailbox 4 | * compliant AMD SoC device. 5 | * 6 | * Copyright (C) 2021-2022 Advanced Micro Devices, Inc. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "sbrmi-common.h" 25 | 26 | /* Do not allow setting negative power limit */ 27 | #define SBRMI_PWR_MIN 0 28 | 29 | /* Try 2 byte address size before switching to 1 byte */ 30 | #define MAX_RETRY 5 31 | 32 | 33 | /* SBRMI REVISION REG */ 34 | #define SBRMI_REV 0x0 35 | 36 | #define MAX_WAIT_TIME_SEC (3) 37 | 38 | /* SBRMI registers data out is 1 byte */ 39 | #define SBRMI_REG_DATA_SIZE 0x1 40 | /* Default SBRMI register address is 1 byte */ 41 | #define SBRMI_REG_ADDR_SIZE_DEF 0x1 42 | /* TURIN SBRMI register address is 2 byte */ 43 | #define SBRMI_REG_ADDR_SIZE_TWO_BYTE 0x2 44 | 45 | /* Two xfers, one write and one read require to read the data */ 46 | #define I3C_I2C_MSG_XFER_SIZE 0x2 47 | 48 | static int configure_regmap(struct apml_sbrmi_device *rmi_dev); 49 | 50 | enum sbrmi_msg_id { 51 | SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1, 52 | SBRMI_WRITE_PKG_PWR_LIMIT, 53 | SBRMI_READ_PKG_PWR_LIMIT, 54 | SBRMI_READ_PKG_MAX_PWR_LIMIT, 55 | }; 56 | 57 | static int sbrmi_get_max_pwr_limit(struct apml_sbrmi_device *rmi_dev) 58 | { 59 | struct apml_message msg = { 0 }; 60 | int ret = 0; 61 | 62 | msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT; 63 | msg.data_in.reg_in[RD_FLAG_INDEX] = 1; 64 | ret = rmi_mailbox_xfer(rmi_dev, &msg); 65 | if (ret < 0) 66 | return ret; 67 | rmi_dev->pwr_limit_max = msg.data_out.mb_out[RD_WR_DATA_INDEX]; 68 | 69 | return ret; 70 | } 71 | 72 | static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, 73 | u32 attr, int channel, long *val) 74 | { 75 | struct apml_sbrmi_device *rmi_dev = dev_get_drvdata(dev); 76 | struct apml_message msg = { 0 }; 77 | int ret = 0; 78 | 79 | if (type != hwmon_power) 80 | return -EINVAL; 81 | /* Configure regmap if not configured yet */ 82 | if (!rmi_dev->regmap) { 83 | ret = configure_regmap(rmi_dev); 84 | if (ret < 0) { 85 | pr_err("regmap configuration failed with return value:%d in hwmon read ops\n", ret); 86 | return ret; 87 | } 88 | } 89 | 90 | mutex_lock(&rmi_dev->lock); 91 | msg.data_in.reg_in[RD_FLAG_INDEX] = 1; 92 | 93 | switch (attr) { 94 | case hwmon_power_input: 95 | msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; 96 | ret = rmi_mailbox_xfer(rmi_dev, &msg); 97 | break; 98 | case hwmon_power_cap: 99 | msg.cmd = SBRMI_READ_PKG_PWR_LIMIT; 100 | ret = rmi_mailbox_xfer(rmi_dev, &msg); 101 | break; 102 | case hwmon_power_cap_max: 103 | if (!rmi_dev->pwr_limit_max) { 104 | /* Cache maximum power limit */ 105 | ret = sbrmi_get_max_pwr_limit(rmi_dev); 106 | } 107 | msg.data_out.mb_out[RD_WR_DATA_INDEX] = rmi_dev->pwr_limit_max; 108 | break; 109 | default: 110 | ret = -EINVAL; 111 | } 112 | if (!ret) 113 | /* hwmon power attributes are in microWatt */ 114 | *val = (long)msg.data_out.mb_out[RD_WR_DATA_INDEX] * 1000; 115 | 116 | mutex_unlock(&rmi_dev->lock); 117 | return ret; 118 | } 119 | 120 | static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, 121 | u32 attr, int channel, long val) 122 | { 123 | struct apml_sbrmi_device *rmi_dev = dev_get_drvdata(dev); 124 | struct apml_message msg = { 0 }; 125 | int ret; 126 | 127 | if (type != hwmon_power && attr != hwmon_power_cap) 128 | return -EINVAL; 129 | /* Configure regmap if not configured yet */ 130 | if (!rmi_dev->regmap) { 131 | ret = configure_regmap(rmi_dev); 132 | if (ret < 0) { 133 | pr_err("regmap configuration failed with return value:%d in hwmon write ops\n", ret); 134 | return ret; 135 | } 136 | } 137 | /* 138 | * hwmon power attributes are in microWatt 139 | * mailbox read/write is in mWatt 140 | */ 141 | val /= 1000; 142 | 143 | val = clamp_val(val, SBRMI_PWR_MIN, rmi_dev->pwr_limit_max); 144 | 145 | msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT; 146 | msg.data_in.mb_in[RD_WR_DATA_INDEX] = val; 147 | msg.data_in.reg_in[RD_FLAG_INDEX] = 0; 148 | 149 | mutex_lock(&rmi_dev->lock); 150 | ret = rmi_mailbox_xfer(rmi_dev, &msg); 151 | mutex_unlock(&rmi_dev->lock); 152 | return ret; 153 | } 154 | 155 | static umode_t sbrmi_is_visible(const void *data, 156 | enum hwmon_sensor_types type, 157 | u32 attr, int channel) 158 | { 159 | switch (type) { 160 | case hwmon_power: 161 | switch (attr) { 162 | case hwmon_power_input: 163 | case hwmon_power_cap_max: 164 | return 0444; 165 | case hwmon_power_cap: 166 | return 0644; 167 | } 168 | break; 169 | default: 170 | break; 171 | } 172 | return 0; 173 | } 174 | 175 | static const struct hwmon_channel_info *sbrmi_info[] = { 176 | HWMON_CHANNEL_INFO(power, 177 | HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), 178 | NULL 179 | }; 180 | 181 | static const struct hwmon_ops sbrmi_hwmon_ops = { 182 | .is_visible = sbrmi_is_visible, 183 | .read = sbrmi_read, 184 | .write = sbrmi_write, 185 | }; 186 | 187 | static const struct hwmon_chip_info sbrmi_chip_info = { 188 | .ops = &sbrmi_hwmon_ops, 189 | .info = sbrmi_info, 190 | }; 191 | 192 | static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 193 | { 194 | int __user *arguser = (int __user *)arg; 195 | struct apml_message msg = { 0 }; 196 | bool read = false; 197 | int ret = -EFAULT; 198 | struct apml_sbrmi_device *rmi_dev; 199 | 200 | rmi_dev = fp->private_data; 201 | if (!rmi_dev) 202 | return -ENODEV; 203 | 204 | /* 205 | * If device remove/unbind is called do not allow new transaction 206 | */ 207 | if (atomic_read(&rmi_dev->no_new_trans)) 208 | return -EBUSY; 209 | /* Copy the structure from user */ 210 | if (copy_struct_from_user(&msg, sizeof(msg), arguser, 211 | sizeof(struct apml_message))) 212 | return ret; 213 | /* 214 | * Only one I2C/I3C transaction can happen at 215 | * one time. Take lock across so no two protocol is 216 | * invoked at same time, modifying the register value. 217 | */ 218 | mutex_lock(&rmi_dev->lock); 219 | /* Verify device unbind/remove is not invoked */ 220 | if (atomic_read(&rmi_dev->no_new_trans)) { 221 | mutex_unlock(&rmi_dev->lock); 222 | return -EBUSY; 223 | } 224 | 225 | /* Is this a read/monitor/get request */ 226 | if (msg.data_in.reg_in[RD_FLAG_INDEX]) 227 | read = true; 228 | 229 | /* 230 | * Set the in_progress variable to true, to wait for 231 | * completion during unbind/remove of driver 232 | */ 233 | atomic_set(&rmi_dev->in_progress, 1); 234 | 235 | switch (msg.cmd) { 236 | case 0 ... 0x999: 237 | /* Mailbox protocol */ 238 | ret = rmi_mailbox_xfer(rmi_dev, &msg); 239 | break; 240 | case APML_CPUID: 241 | ret = rmi_cpuid_read(rmi_dev, &msg); 242 | break; 243 | case APML_MCA_MSR: 244 | /* MCAMSR protocol */ 245 | ret = rmi_mca_msr_read(rmi_dev, &msg); 246 | break; 247 | case APML_REG: 248 | /* REG R/W */ 249 | if (read) { 250 | ret = regmap_read(rmi_dev->regmap, 251 | msg.data_in.mb_in[REG_OFF_INDEX], 252 | &msg.data_out.mb_out[RD_WR_DATA_INDEX]); 253 | } else { 254 | ret = regmap_write(rmi_dev->regmap, 255 | msg.data_in.mb_in[REG_OFF_INDEX], 256 | msg.data_in.reg_in[REG_VAL_INDEX]); 257 | } 258 | break; 259 | default: 260 | break; 261 | } 262 | 263 | /* Send complete only if device is unbinded/remove */ 264 | if (atomic_read(&rmi_dev->no_new_trans)) 265 | complete(&rmi_dev->misc_fops_done); 266 | 267 | atomic_set(&rmi_dev->in_progress, 0); 268 | mutex_unlock(&rmi_dev->lock); 269 | 270 | /* Copy results back to user only for get/monitor commands and firmware failures */ 271 | if ((read && !ret) || ret == -EPROTOTYPE) { 272 | if (copy_to_user(arguser, &msg, sizeof(struct apml_message))) 273 | ret = -EFAULT; 274 | } 275 | return ret; 276 | } 277 | 278 | static int sbrmi_open(struct inode *inode, struct file *filp) 279 | { 280 | struct miscdevice *mdev = filp->private_data; 281 | struct apml_sbrmi_device *rmi_dev = container_of(mdev, struct apml_sbrmi_device, 282 | sbrmi_misc_dev); 283 | int ret = 0; 284 | 285 | if (!rmi_dev) 286 | return -ENODEV; 287 | 288 | if (!rmi_dev->regmap) { 289 | ret = configure_regmap(rmi_dev); 290 | if (ret < 0) { 291 | pr_err("regmap configuration failed with return value:%d in misc dev open\n", ret); 292 | return ret; 293 | } 294 | } 295 | filp->private_data = rmi_dev; 296 | return 0; 297 | } 298 | 299 | static int sbrmi_release(struct inode *inode, struct file *filp) 300 | { 301 | filp->private_data = NULL; 302 | 303 | return 0; 304 | } 305 | 306 | static const struct file_operations sbrmi_fops = { 307 | .owner = THIS_MODULE, 308 | .open = sbrmi_open, 309 | .release = sbrmi_release, 310 | .unlocked_ioctl = sbrmi_ioctl, 311 | .compat_ioctl = sbrmi_ioctl, 312 | }; 313 | 314 | static int create_misc_rmi_device(struct apml_sbrmi_device *rmi_dev, 315 | struct device *dev) 316 | { 317 | int ret; 318 | 319 | rmi_dev->sbrmi_misc_dev.name = devm_kasprintf(dev, GFP_KERNEL, 320 | "sbrmi-%x", rmi_dev->dev_static_addr); 321 | rmi_dev->sbrmi_misc_dev.minor = MISC_DYNAMIC_MINOR; 322 | rmi_dev->sbrmi_misc_dev.fops = &sbrmi_fops; 323 | rmi_dev->sbrmi_misc_dev.parent = dev; 324 | rmi_dev->sbrmi_misc_dev.nodename = devm_kasprintf(dev, GFP_KERNEL, 325 | "sbrmi-%x", rmi_dev->dev_static_addr); 326 | rmi_dev->sbrmi_misc_dev.mode = 0600; 327 | 328 | ret = misc_register(&rmi_dev->sbrmi_misc_dev); 329 | if (ret) 330 | return ret; 331 | 332 | dev_info(dev, "register %s device\n", rmi_dev->sbrmi_misc_dev.name); 333 | return ret; 334 | } 335 | 336 | static int sbrmi_i2c_reg_read(struct i2c_client *i2cdev, int reg_size, u32 *val) 337 | { 338 | struct i2c_msg xfer[I3C_I2C_MSG_XFER_SIZE]; 339 | int reg = SBRMI_REV; 340 | int val_size = SBRMI_REG_DATA_SIZE; 341 | 342 | xfer[0].addr = i2cdev->addr; 343 | xfer[0].flags = 0; 344 | xfer[0].len = reg_size; 345 | xfer[0].buf = (void *)® 346 | 347 | xfer[1].addr = i2cdev->addr; 348 | xfer[1].flags = I2C_M_RD; 349 | xfer[1].len = val_size; 350 | xfer[1].buf = (void *)val; 351 | 352 | return i2c_transfer(i2cdev->adapter, xfer, I3C_I2C_MSG_XFER_SIZE); 353 | } 354 | 355 | static int sbrmi_i2c_identify_reg_addr_size(struct i2c_client *i2c, u32 *size, u32 *rev) 356 | { 357 | u32 reg_size; 358 | int ret, i; 359 | 360 | reg_size = SBRMI_REG_ADDR_SIZE_TWO_BYTE; 361 | 362 | /* 363 | * Sending 1 byte address size in Turin cause unrecoverable error 364 | * Before trying to switch to 1 bytes, retry. 365 | */ 366 | for (i = 0; i < MAX_RETRY; i++) { 367 | ret = sbrmi_i2c_reg_read(i2c, reg_size, rev); 368 | if (ret != I3C_I2C_MSG_XFER_SIZE) { 369 | usleep_range(10000, 20000); 370 | continue; 371 | } else { 372 | break; 373 | } 374 | } 375 | if (ret != I3C_I2C_MSG_XFER_SIZE) { 376 | reg_size = SBRMI_REG_ADDR_SIZE_DEF; 377 | ret = sbrmi_i2c_reg_read(i2c, reg_size, rev); 378 | if (ret != I3C_I2C_MSG_XFER_SIZE) { 379 | pr_err("I2C reg read failed with return value:%d\n", ret); 380 | return ret; 381 | } 382 | } 383 | 384 | if (*rev == 0x21) 385 | *size = SBRMI_REG_ADDR_SIZE_TWO_BYTE; 386 | else 387 | *size = SBRMI_REG_ADDR_SIZE_DEF; 388 | return ret; 389 | } 390 | 391 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) 392 | static int sbrmi_i2c_probe(struct i2c_client *client, 393 | const struct i2c_device_id *rmi_id) 394 | #else 395 | static int sbrmi_i2c_probe(struct i2c_client *client) 396 | #endif 397 | { 398 | struct device *dev = &client->dev; 399 | struct device *hwmon_dev; 400 | struct apml_sbrmi_device *rmi_dev; 401 | 402 | rmi_dev = devm_kzalloc(dev, sizeof(struct apml_sbrmi_device), GFP_KERNEL); 403 | if (!rmi_dev) 404 | return -ENOMEM; 405 | 406 | atomic_set(&rmi_dev->in_progress, 0); 407 | atomic_set(&rmi_dev->no_new_trans, 0); 408 | rmi_dev->client = client; 409 | mutex_init(&rmi_dev->lock); 410 | 411 | dev_set_drvdata(dev, (void *)rmi_dev); 412 | 413 | hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, 414 | rmi_dev, 415 | &sbrmi_chip_info, 416 | NULL); 417 | 418 | if (!hwmon_dev) 419 | return PTR_ERR_OR_ZERO(hwmon_dev); 420 | 421 | rmi_dev->dev_static_addr = client->addr; 422 | 423 | init_completion(&rmi_dev->misc_fops_done); 424 | return create_misc_rmi_device(rmi_dev, dev); 425 | } 426 | 427 | static int sbrmi_i3c_reg_read(struct i3c_device *i3cdev, int reg_size, u32 *val) 428 | { 429 | struct i3c_priv_xfer xfers[I3C_I2C_MSG_XFER_SIZE]; 430 | int reg = SBRMI_REV; 431 | int val_size = SBRMI_REG_DATA_SIZE; 432 | 433 | xfers[0].rnw = false; 434 | xfers[0].len = reg_size; 435 | xfers[0].data.out = ® 436 | 437 | xfers[1].rnw = true; 438 | xfers[1].len = val_size; 439 | xfers[1].data.in = val; 440 | 441 | return i3c_device_do_priv_xfers(i3cdev, xfers, I3C_I2C_MSG_XFER_SIZE); 442 | } 443 | 444 | static int sbrmi_i3c_identify_reg_addr_size(struct i3c_device *i3cdev, u32 *size, u32 *rev) 445 | { 446 | u32 reg_size; 447 | int ret, i; 448 | 449 | reg_size = SBRMI_REG_ADDR_SIZE_TWO_BYTE; 450 | for (i = 0; i < MAX_RETRY; i++) { 451 | ret = sbrmi_i3c_reg_read(i3cdev, reg_size, rev); 452 | if (ret < 0) { 453 | usleep_range(10000, 20000); 454 | continue; 455 | } else { 456 | break; 457 | } 458 | } 459 | if (ret < 0) { 460 | reg_size = SBRMI_REG_ADDR_SIZE_DEF; 461 | ret = sbrmi_i3c_reg_read(i3cdev, reg_size, rev); 462 | if (ret < 0) { 463 | pr_err("I3C reg read failed with return value:%d\n", ret); 464 | return ret; 465 | } 466 | } 467 | 468 | if (*rev == 0x21) 469 | *size = SBRMI_REG_ADDR_SIZE_TWO_BYTE; 470 | else 471 | *size = SBRMI_REG_ADDR_SIZE_DEF; 472 | return ret; 473 | } 474 | 475 | static int init_rmi_regmap(struct apml_sbrmi_device *rmi_dev, u32 size, u32 rev) 476 | { 477 | struct regmap_config sbrmi_regmap_config = { 478 | .reg_bits = 8 * size, 479 | .val_bits = 8, 480 | .reg_format_endian = REGMAP_ENDIAN_LITTLE, 481 | }; 482 | struct regmap *regmap; 483 | 484 | if (rmi_dev->i3cdev) { 485 | regmap = devm_regmap_init_i3c(rmi_dev->i3cdev, 486 | &sbrmi_regmap_config); 487 | if (IS_ERR(regmap)) { 488 | dev_err(&rmi_dev->i3cdev->dev, 489 | "Failed to register i3c regmap %d\n", 490 | (int)PTR_ERR(regmap)); 491 | return PTR_ERR(regmap); 492 | } 493 | } else if (rmi_dev->client) { 494 | regmap = devm_regmap_init_i2c(rmi_dev->client, 495 | &sbrmi_regmap_config); 496 | if (IS_ERR(regmap)) 497 | return PTR_ERR(regmap); 498 | } else { 499 | return -ENODEV; 500 | } 501 | 502 | rmi_dev->regmap = regmap; 503 | rmi_dev->rev = rev; 504 | return 0; 505 | } 506 | 507 | /* 508 | * configure_regmap call should happen in probe, however if the server is power off, 509 | * regmap configuration may fail and hence driver probe will fail. 510 | * configure the regmap in hwmon/ioctl 511 | */ 512 | static int configure_regmap(struct apml_sbrmi_device *rmi_dev) 513 | { 514 | u32 size; 515 | u32 rev = 0; 516 | int ret = 0; 517 | 518 | if (rmi_dev->i3cdev) { 519 | ret = sbrmi_i3c_identify_reg_addr_size(rmi_dev->i3cdev, &size, &rev); 520 | if (ret < 0) { 521 | pr_err("Reg size identification failed with return value:%d\n", ret); 522 | return ret; 523 | } 524 | } else if (rmi_dev->client) { 525 | ret = sbrmi_i2c_identify_reg_addr_size(rmi_dev->client, &size, &rev); 526 | if (ret < 0) { 527 | pr_err("Reg size identification failed with return value:%d\n", ret); 528 | return ret; 529 | } 530 | } else { 531 | return ret; 532 | } 533 | ret = init_rmi_regmap(rmi_dev, size, rev); 534 | return ret; 535 | } 536 | 537 | static int sbrmi_i3c_probe(struct i3c_device *i3cdev) 538 | { 539 | struct device *dev = &i3cdev->dev; 540 | struct device *hwmon_dev; 541 | struct apml_sbrmi_device *rmi_dev; 542 | 543 | rmi_dev = devm_kzalloc(dev, sizeof(struct apml_sbrmi_device), GFP_KERNEL); 544 | if (!rmi_dev) 545 | return -ENOMEM; 546 | 547 | atomic_set(&rmi_dev->in_progress, 0); 548 | atomic_set(&rmi_dev->no_new_trans, 0); 549 | rmi_dev->i3cdev = i3cdev; 550 | mutex_init(&rmi_dev->lock); 551 | 552 | dev_set_drvdata(dev, (void *)rmi_dev); 553 | 554 | hwmon_dev = devm_hwmon_device_register_with_info(dev, "sbrmi_i3c", rmi_dev, 555 | &sbrmi_chip_info, NULL); 556 | 557 | if (!hwmon_dev) 558 | return PTR_ERR_OR_ZERO(hwmon_dev); 559 | 560 | /* Need to verify for the static address for i3cdev */ 561 | rmi_dev->dev_static_addr = i3cdev->desc->info.static_addr; 562 | 563 | init_completion(&rmi_dev->misc_fops_done); 564 | return create_misc_rmi_device(rmi_dev, dev); 565 | } 566 | 567 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) 568 | static int sbrmi_i2c_remove(struct i2c_client *client) 569 | #else 570 | static void sbrmi_i2c_remove(struct i2c_client *client) 571 | #endif 572 | { 573 | struct apml_sbrmi_device *rmi_dev = dev_get_drvdata(&client->dev); 574 | 575 | if (!rmi_dev) 576 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) 577 | return 0; 578 | #else 579 | return; 580 | #endif 581 | 582 | /* 583 | * Set the no_new_trans so no new transaction can 584 | * occur in sbrmi_ioctl 585 | */ 586 | atomic_set(&rmi_dev->no_new_trans, 1); 587 | /* 588 | * If any transaction is in progress wait for the 589 | * transaction to get complete 590 | * Max wait for 3 sec for any pending transaction to 591 | * complete 592 | */ 593 | if (atomic_read(&rmi_dev->in_progress)) 594 | wait_for_completion_timeout(&rmi_dev->misc_fops_done, 595 | MAX_WAIT_TIME_SEC * HZ); 596 | misc_deregister(&rmi_dev->sbrmi_misc_dev); 597 | /* Assign fops and parent of misc dev to NULL */ 598 | rmi_dev->sbrmi_misc_dev.fops = NULL; 599 | rmi_dev->sbrmi_misc_dev.parent = NULL; 600 | 601 | dev_info(&client->dev, "Removed sbrmi driver\n"); 602 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) 603 | return 0; 604 | #endif 605 | } 606 | 607 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) 608 | static int sbrmi_i3c_remove(struct i3c_device *i3cdev) 609 | #else 610 | static void sbrmi_i3c_remove(struct i3c_device *i3cdev) 611 | #endif 612 | { 613 | struct apml_sbrmi_device *rmi_dev = dev_get_drvdata(&i3cdev->dev); 614 | 615 | if (!rmi_dev) 616 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) 617 | return 0; 618 | #else 619 | return; 620 | #endif 621 | 622 | /* 623 | * Set the no_new_trans so no new transaction can 624 | * occur in sbrmi_ioctl 625 | */ 626 | atomic_set(&rmi_dev->no_new_trans, 1); 627 | /* 628 | * If any transaction is in progress wait for the 629 | * transaction to get complete 630 | * Max wait for 3 sec for any pending transaction to 631 | * complete 632 | */ 633 | if (atomic_read(&rmi_dev->in_progress)) 634 | wait_for_completion_timeout(&rmi_dev->misc_fops_done, 635 | MAX_WAIT_TIME_SEC * HZ); 636 | misc_deregister(&rmi_dev->sbrmi_misc_dev); 637 | /* Assign fops and parent of misc dev to NULL */ 638 | rmi_dev->sbrmi_misc_dev.fops = NULL; 639 | rmi_dev->sbrmi_misc_dev.parent = NULL; 640 | 641 | dev_info(&i3cdev->dev, "Removed sbrmi_i3c driver\n"); 642 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) 643 | return 0; 644 | #endif 645 | } 646 | 647 | static const struct i2c_device_id sbrmi_id[] = { 648 | {"sbrmi", 0}, 649 | {} 650 | }; 651 | MODULE_DEVICE_TABLE(i2c, sbrmi_id); 652 | 653 | static const struct of_device_id __maybe_unused sbrmi_of_match[] = { 654 | { 655 | .compatible = "amd,sbrmi", 656 | }, 657 | { }, 658 | }; 659 | MODULE_DEVICE_TABLE(of, sbrmi_of_match); 660 | 661 | static const struct i3c_device_id sbrmi_i3c_id[] = { 662 | I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x2, NULL), 663 | {} 664 | }; 665 | MODULE_DEVICE_TABLE(i3c, sbrmi_i3c_id); 666 | 667 | static struct i2c_driver sbrmi_driver = { 668 | .class = I2C_CLASS_HWMON, 669 | .driver = { 670 | .name = "sbrmi", 671 | .of_match_table = of_match_ptr(sbrmi_of_match), 672 | }, 673 | .probe = sbrmi_i2c_probe, 674 | .remove = sbrmi_i2c_remove, 675 | .id_table = sbrmi_id, 676 | }; 677 | 678 | static struct i3c_driver sbrmi_i3c_driver = { 679 | .driver = { 680 | .name = "sbrmi_i3c", 681 | }, 682 | .probe = sbrmi_i3c_probe, 683 | .remove = sbrmi_i3c_remove, 684 | .id_table = sbrmi_i3c_id, 685 | }; 686 | 687 | module_i3c_i2c_driver(sbrmi_i3c_driver, &sbrmi_driver) 688 | 689 | int sbrmi_match_i3c(struct device *dev, const void *data) 690 | { 691 | const struct device_node *node = (const struct device_node *)data; 692 | 693 | if (dev->of_node == node && dev->driver == &sbrmi_i3c_driver.driver) 694 | return 1; 695 | return 0; 696 | } 697 | EXPORT_SYMBOL_GPL(sbrmi_match_i3c); 698 | 699 | int sbrmi_match_i2c(struct device *dev, const void *data) 700 | { 701 | const struct device_node *node = (const struct device_node *)data; 702 | 703 | if (dev->of_node == node && dev->driver == &sbrmi_driver.driver) 704 | return 1; 705 | return 0; 706 | } 707 | EXPORT_SYMBOL_GPL(sbrmi_match_i2c); 708 | 709 | MODULE_AUTHOR("Akshay Gupta "); 710 | MODULE_AUTHOR("Naveenkrishna Chatradhi "); 711 | MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor"); 712 | MODULE_LICENSE("GPL"); 713 | --------------------------------------------------------------------------------