18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * acpi_ipmi.c - ACPI IPMI opregion 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010, 2013 Intel Corporation 68c2ecf20Sopenharmony_ci * Author: Zhao Yakui <yakui.zhao@intel.com> 78c2ecf20Sopenharmony_ci * Lv Zheng <lv.zheng@intel.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/acpi.h> 128c2ecf20Sopenharmony_ci#include <linux/ipmi.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zhao Yakui"); 168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI IPMI Opregion driver"); 178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define ACPI_IPMI_OK 0 208c2ecf20Sopenharmony_ci#define ACPI_IPMI_TIMEOUT 0x10 218c2ecf20Sopenharmony_ci#define ACPI_IPMI_UNKNOWN 0x07 228c2ecf20Sopenharmony_ci/* the IPMI timeout is 5s */ 238c2ecf20Sopenharmony_ci#define IPMI_TIMEOUT (5000) 248c2ecf20Sopenharmony_ci#define ACPI_IPMI_MAX_MSG_LENGTH 64 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct acpi_ipmi_device { 278c2ecf20Sopenharmony_ci /* the device list attached to driver_data.ipmi_devices */ 288c2ecf20Sopenharmony_ci struct list_head head; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci /* the IPMI request message list */ 318c2ecf20Sopenharmony_ci struct list_head tx_msg_list; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci spinlock_t tx_msg_lock; 348c2ecf20Sopenharmony_ci acpi_handle handle; 358c2ecf20Sopenharmony_ci struct device *dev; 368c2ecf20Sopenharmony_ci struct ipmi_user *user_interface; 378c2ecf20Sopenharmony_ci int ipmi_ifnum; /* IPMI interface number */ 388c2ecf20Sopenharmony_ci long curr_msgid; 398c2ecf20Sopenharmony_ci bool dead; 408c2ecf20Sopenharmony_ci struct kref kref; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct ipmi_driver_data { 448c2ecf20Sopenharmony_ci struct list_head ipmi_devices; 458c2ecf20Sopenharmony_ci struct ipmi_smi_watcher bmc_events; 468c2ecf20Sopenharmony_ci const struct ipmi_user_hndl ipmi_hndlrs; 478c2ecf20Sopenharmony_ci struct mutex ipmi_lock; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* 508c2ecf20Sopenharmony_ci * NOTE: IPMI System Interface Selection 518c2ecf20Sopenharmony_ci * There is no system interface specified by the IPMI operation 528c2ecf20Sopenharmony_ci * region access. We try to select one system interface with ACPI 538c2ecf20Sopenharmony_ci * handle set. IPMI messages passed from the ACPI codes are sent 548c2ecf20Sopenharmony_ci * to this selected global IPMI system interface. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci struct acpi_ipmi_device *selected_smi; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct acpi_ipmi_msg { 608c2ecf20Sopenharmony_ci struct list_head head; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * General speaking the addr type should be SI_ADDR_TYPE. And 648c2ecf20Sopenharmony_ci * the addr channel should be BMC. 658c2ecf20Sopenharmony_ci * In fact it can also be IPMB type. But we will have to 668c2ecf20Sopenharmony_ci * parse it from the Netfn command buffer. It is so complex 678c2ecf20Sopenharmony_ci * that it is skipped. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci struct ipmi_addr addr; 708c2ecf20Sopenharmony_ci long tx_msgid; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* it is used to track whether the IPMI message is finished */ 738c2ecf20Sopenharmony_ci struct completion tx_complete; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci struct kernel_ipmi_msg tx_message; 768c2ecf20Sopenharmony_ci int msg_done; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* tx/rx data . And copy it from/to ACPI object buffer */ 798c2ecf20Sopenharmony_ci u8 data[ACPI_IPMI_MAX_MSG_LENGTH]; 808c2ecf20Sopenharmony_ci u8 rx_len; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci struct acpi_ipmi_device *device; 838c2ecf20Sopenharmony_ci struct kref kref; 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */ 878c2ecf20Sopenharmony_cistruct acpi_ipmi_buffer { 888c2ecf20Sopenharmony_ci u8 status; 898c2ecf20Sopenharmony_ci u8 length; 908c2ecf20Sopenharmony_ci u8 data[ACPI_IPMI_MAX_MSG_LENGTH]; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void ipmi_register_bmc(int iface, struct device *dev); 948c2ecf20Sopenharmony_cistatic void ipmi_bmc_gone(int iface); 958c2ecf20Sopenharmony_cistatic void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic struct ipmi_driver_data driver_data = { 988c2ecf20Sopenharmony_ci .ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices), 998c2ecf20Sopenharmony_ci .bmc_events = { 1008c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1018c2ecf20Sopenharmony_ci .new_smi = ipmi_register_bmc, 1028c2ecf20Sopenharmony_ci .smi_gone = ipmi_bmc_gone, 1038c2ecf20Sopenharmony_ci }, 1048c2ecf20Sopenharmony_ci .ipmi_hndlrs = { 1058c2ecf20Sopenharmony_ci .ipmi_recv_hndl = ipmi_msg_handler, 1068c2ecf20Sopenharmony_ci }, 1078c2ecf20Sopenharmony_ci .ipmi_lock = __MUTEX_INITIALIZER(driver_data.ipmi_lock) 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic struct acpi_ipmi_device * 1118c2ecf20Sopenharmony_ciipmi_dev_alloc(int iface, struct device *dev, acpi_handle handle) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi_device; 1148c2ecf20Sopenharmony_ci int err; 1158c2ecf20Sopenharmony_ci struct ipmi_user *user; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL); 1188c2ecf20Sopenharmony_ci if (!ipmi_device) 1198c2ecf20Sopenharmony_ci return NULL; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci kref_init(&ipmi_device->kref); 1228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ipmi_device->head); 1238c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ipmi_device->tx_msg_list); 1248c2ecf20Sopenharmony_ci spin_lock_init(&ipmi_device->tx_msg_lock); 1258c2ecf20Sopenharmony_ci ipmi_device->handle = handle; 1268c2ecf20Sopenharmony_ci ipmi_device->dev = get_device(dev); 1278c2ecf20Sopenharmony_ci ipmi_device->ipmi_ifnum = iface; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs, 1308c2ecf20Sopenharmony_ci ipmi_device, &user); 1318c2ecf20Sopenharmony_ci if (err) { 1328c2ecf20Sopenharmony_ci put_device(dev); 1338c2ecf20Sopenharmony_ci kfree(ipmi_device); 1348c2ecf20Sopenharmony_ci return NULL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci ipmi_device->user_interface = user; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return ipmi_device; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void ipmi_dev_release(struct acpi_ipmi_device *ipmi_device) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci ipmi_destroy_user(ipmi_device->user_interface); 1448c2ecf20Sopenharmony_ci put_device(ipmi_device->dev); 1458c2ecf20Sopenharmony_ci kfree(ipmi_device); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void ipmi_dev_release_kref(struct kref *kref) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi = 1518c2ecf20Sopenharmony_ci container_of(kref, struct acpi_ipmi_device, kref); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ipmi_dev_release(ipmi); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void __ipmi_dev_kill(struct acpi_ipmi_device *ipmi_device) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci list_del(&ipmi_device->head); 1598c2ecf20Sopenharmony_ci if (driver_data.selected_smi == ipmi_device) 1608c2ecf20Sopenharmony_ci driver_data.selected_smi = NULL; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * Always setting dead flag after deleting from the list or 1648c2ecf20Sopenharmony_ci * list_for_each_entry() codes must get changed. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci ipmi_device->dead = true; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct acpi_ipmi_device *acpi_ipmi_dev_get(void) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi_device = NULL; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci mutex_lock(&driver_data.ipmi_lock); 1748c2ecf20Sopenharmony_ci if (driver_data.selected_smi) { 1758c2ecf20Sopenharmony_ci ipmi_device = driver_data.selected_smi; 1768c2ecf20Sopenharmony_ci kref_get(&ipmi_device->kref); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci mutex_unlock(&driver_data.ipmi_lock); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return ipmi_device; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void acpi_ipmi_dev_put(struct acpi_ipmi_device *ipmi_device) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci kref_put(&ipmi_device->kref, ipmi_dev_release_kref); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic struct acpi_ipmi_msg *ipmi_msg_alloc(void) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi; 1918c2ecf20Sopenharmony_ci struct acpi_ipmi_msg *ipmi_msg; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ipmi = acpi_ipmi_dev_get(); 1948c2ecf20Sopenharmony_ci if (!ipmi) 1958c2ecf20Sopenharmony_ci return NULL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ipmi_msg = kzalloc(sizeof(struct acpi_ipmi_msg), GFP_KERNEL); 1988c2ecf20Sopenharmony_ci if (!ipmi_msg) { 1998c2ecf20Sopenharmony_ci acpi_ipmi_dev_put(ipmi); 2008c2ecf20Sopenharmony_ci return NULL; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci kref_init(&ipmi_msg->kref); 2048c2ecf20Sopenharmony_ci init_completion(&ipmi_msg->tx_complete); 2058c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ipmi_msg->head); 2068c2ecf20Sopenharmony_ci ipmi_msg->device = ipmi; 2078c2ecf20Sopenharmony_ci ipmi_msg->msg_done = ACPI_IPMI_UNKNOWN; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return ipmi_msg; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void ipmi_msg_release(struct acpi_ipmi_msg *tx_msg) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci acpi_ipmi_dev_put(tx_msg->device); 2158c2ecf20Sopenharmony_ci kfree(tx_msg); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void ipmi_msg_release_kref(struct kref *kref) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct acpi_ipmi_msg *tx_msg = 2218c2ecf20Sopenharmony_ci container_of(kref, struct acpi_ipmi_msg, kref); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci ipmi_msg_release(tx_msg); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic struct acpi_ipmi_msg *acpi_ipmi_msg_get(struct acpi_ipmi_msg *tx_msg) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci kref_get(&tx_msg->kref); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return tx_msg; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void acpi_ipmi_msg_put(struct acpi_ipmi_msg *tx_msg) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci kref_put(&tx_msg->kref, ipmi_msg_release_kref); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci#define IPMI_OP_RGN_NETFN(offset) ((offset >> 8) & 0xff) 2398c2ecf20Sopenharmony_ci#define IPMI_OP_RGN_CMD(offset) (offset & 0xff) 2408c2ecf20Sopenharmony_cistatic int acpi_format_ipmi_request(struct acpi_ipmi_msg *tx_msg, 2418c2ecf20Sopenharmony_ci acpi_physical_address address, 2428c2ecf20Sopenharmony_ci acpi_integer *value) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct kernel_ipmi_msg *msg; 2458c2ecf20Sopenharmony_ci struct acpi_ipmi_buffer *buffer; 2468c2ecf20Sopenharmony_ci struct acpi_ipmi_device *device; 2478c2ecf20Sopenharmony_ci unsigned long flags; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci msg = &tx_msg->tx_message; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * IPMI network function and command are encoded in the address 2538c2ecf20Sopenharmony_ci * within the IPMI OpRegion; see ACPI 4.0, sec 5.5.2.4.3. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci msg->netfn = IPMI_OP_RGN_NETFN(address); 2568c2ecf20Sopenharmony_ci msg->cmd = IPMI_OP_RGN_CMD(address); 2578c2ecf20Sopenharmony_ci msg->data = tx_msg->data; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* 2608c2ecf20Sopenharmony_ci * value is the parameter passed by the IPMI opregion space handler. 2618c2ecf20Sopenharmony_ci * It points to the IPMI request message buffer 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci buffer = (struct acpi_ipmi_buffer *)value; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* copy the tx message data */ 2668c2ecf20Sopenharmony_ci if (buffer->length > ACPI_IPMI_MAX_MSG_LENGTH) { 2678c2ecf20Sopenharmony_ci dev_WARN_ONCE(tx_msg->device->dev, true, 2688c2ecf20Sopenharmony_ci "Unexpected request (msg len %d).\n", 2698c2ecf20Sopenharmony_ci buffer->length); 2708c2ecf20Sopenharmony_ci return -EINVAL; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci msg->data_len = buffer->length; 2738c2ecf20Sopenharmony_ci memcpy(tx_msg->data, buffer->data, msg->data_len); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* 2768c2ecf20Sopenharmony_ci * now the default type is SYSTEM_INTERFACE and channel type is BMC. 2778c2ecf20Sopenharmony_ci * If the netfn is APP_REQUEST and the cmd is SEND_MESSAGE, 2788c2ecf20Sopenharmony_ci * the addr type should be changed to IPMB. Then we will have to parse 2798c2ecf20Sopenharmony_ci * the IPMI request message buffer to get the IPMB address. 2808c2ecf20Sopenharmony_ci * If so, please fix me. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci tx_msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 2838c2ecf20Sopenharmony_ci tx_msg->addr.channel = IPMI_BMC_CHANNEL; 2848c2ecf20Sopenharmony_ci tx_msg->addr.data[0] = 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Get the msgid */ 2878c2ecf20Sopenharmony_ci device = tx_msg->device; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci spin_lock_irqsave(&device->tx_msg_lock, flags); 2908c2ecf20Sopenharmony_ci device->curr_msgid++; 2918c2ecf20Sopenharmony_ci tx_msg->tx_msgid = device->curr_msgid; 2928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&device->tx_msg_lock, flags); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, 2988c2ecf20Sopenharmony_ci acpi_integer *value) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct acpi_ipmi_buffer *buffer; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* 3038c2ecf20Sopenharmony_ci * value is also used as output parameter. It represents the response 3048c2ecf20Sopenharmony_ci * IPMI message returned by IPMI command. 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci buffer = (struct acpi_ipmi_buffer *)value; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * If the flag of msg_done is not set, it means that the IPMI command is 3108c2ecf20Sopenharmony_ci * not executed correctly. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci buffer->status = msg->msg_done; 3138c2ecf20Sopenharmony_ci if (msg->msg_done != ACPI_IPMI_OK) 3148c2ecf20Sopenharmony_ci return; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci * If the IPMI response message is obtained correctly, the status code 3188c2ecf20Sopenharmony_ci * will be ACPI_IPMI_OK 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_ci buffer->length = msg->rx_len; 3218c2ecf20Sopenharmony_ci memcpy(buffer->data, msg->data, msg->rx_len); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void ipmi_flush_tx_msg(struct acpi_ipmi_device *ipmi) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct acpi_ipmi_msg *tx_msg; 3278c2ecf20Sopenharmony_ci unsigned long flags; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* 3308c2ecf20Sopenharmony_ci * NOTE: On-going ipmi_recv_msg 3318c2ecf20Sopenharmony_ci * ipmi_msg_handler() may still be invoked by ipmi_si after 3328c2ecf20Sopenharmony_ci * flushing. But it is safe to do a fast flushing on module_exit() 3338c2ecf20Sopenharmony_ci * without waiting for all ipmi_recv_msg(s) to complete from 3348c2ecf20Sopenharmony_ci * ipmi_msg_handler() as it is ensured by ipmi_si that all 3358c2ecf20Sopenharmony_ci * ipmi_recv_msg(s) are freed after invoking ipmi_destroy_user(). 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipmi->tx_msg_lock, flags); 3388c2ecf20Sopenharmony_ci while (!list_empty(&ipmi->tx_msg_list)) { 3398c2ecf20Sopenharmony_ci tx_msg = list_first_entry(&ipmi->tx_msg_list, 3408c2ecf20Sopenharmony_ci struct acpi_ipmi_msg, 3418c2ecf20Sopenharmony_ci head); 3428c2ecf20Sopenharmony_ci list_del(&tx_msg->head); 3438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* wake up the sleep thread on the Tx msg */ 3468c2ecf20Sopenharmony_ci complete(&tx_msg->tx_complete); 3478c2ecf20Sopenharmony_ci acpi_ipmi_msg_put(tx_msg); 3488c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipmi->tx_msg_lock, flags); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void ipmi_cancel_tx_msg(struct acpi_ipmi_device *ipmi, 3548c2ecf20Sopenharmony_ci struct acpi_ipmi_msg *msg) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct acpi_ipmi_msg *tx_msg, *temp; 3578c2ecf20Sopenharmony_ci bool msg_found = false; 3588c2ecf20Sopenharmony_ci unsigned long flags; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipmi->tx_msg_lock, flags); 3618c2ecf20Sopenharmony_ci list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) { 3628c2ecf20Sopenharmony_ci if (msg == tx_msg) { 3638c2ecf20Sopenharmony_ci msg_found = true; 3648c2ecf20Sopenharmony_ci list_del(&tx_msg->head); 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipmi->tx_msg_lock, flags); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (msg_found) 3718c2ecf20Sopenharmony_ci acpi_ipmi_msg_put(tx_msg); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi_device = user_msg_data; 3778c2ecf20Sopenharmony_ci bool msg_found = false; 3788c2ecf20Sopenharmony_ci struct acpi_ipmi_msg *tx_msg, *temp; 3798c2ecf20Sopenharmony_ci struct device *dev = ipmi_device->dev; 3808c2ecf20Sopenharmony_ci unsigned long flags; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (msg->user != ipmi_device->user_interface) { 3838c2ecf20Sopenharmony_ci dev_warn(dev, 3848c2ecf20Sopenharmony_ci "Unexpected response is returned. returned user %p, expected user %p\n", 3858c2ecf20Sopenharmony_ci msg->user, ipmi_device->user_interface); 3868c2ecf20Sopenharmony_ci goto out_msg; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); 3908c2ecf20Sopenharmony_ci list_for_each_entry_safe(tx_msg, temp, &ipmi_device->tx_msg_list, head) { 3918c2ecf20Sopenharmony_ci if (msg->msgid == tx_msg->tx_msgid) { 3928c2ecf20Sopenharmony_ci msg_found = true; 3938c2ecf20Sopenharmony_ci list_del(&tx_msg->head); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (!msg_found) { 4008c2ecf20Sopenharmony_ci dev_warn(dev, 4018c2ecf20Sopenharmony_ci "Unexpected response (msg id %ld) is returned.\n", 4028c2ecf20Sopenharmony_ci msg->msgid); 4038c2ecf20Sopenharmony_ci goto out_msg; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* copy the response data to Rx_data buffer */ 4078c2ecf20Sopenharmony_ci if (msg->msg.data_len > ACPI_IPMI_MAX_MSG_LENGTH) { 4088c2ecf20Sopenharmony_ci dev_WARN_ONCE(dev, true, 4098c2ecf20Sopenharmony_ci "Unexpected response (msg len %d).\n", 4108c2ecf20Sopenharmony_ci msg->msg.data_len); 4118c2ecf20Sopenharmony_ci goto out_comp; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* response msg is an error msg */ 4158c2ecf20Sopenharmony_ci msg->recv_type = IPMI_RESPONSE_RECV_TYPE; 4168c2ecf20Sopenharmony_ci if (msg->recv_type == IPMI_RESPONSE_RECV_TYPE && 4178c2ecf20Sopenharmony_ci msg->msg.data_len == 1) { 4188c2ecf20Sopenharmony_ci if (msg->msg.data[0] == IPMI_TIMEOUT_COMPLETION_CODE) { 4198c2ecf20Sopenharmony_ci dev_dbg_once(dev, "Unexpected response (timeout).\n"); 4208c2ecf20Sopenharmony_ci tx_msg->msg_done = ACPI_IPMI_TIMEOUT; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci goto out_comp; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci tx_msg->rx_len = msg->msg.data_len; 4268c2ecf20Sopenharmony_ci memcpy(tx_msg->data, msg->msg.data, tx_msg->rx_len); 4278c2ecf20Sopenharmony_ci tx_msg->msg_done = ACPI_IPMI_OK; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ciout_comp: 4308c2ecf20Sopenharmony_ci complete(&tx_msg->tx_complete); 4318c2ecf20Sopenharmony_ci acpi_ipmi_msg_put(tx_msg); 4328c2ecf20Sopenharmony_ciout_msg: 4338c2ecf20Sopenharmony_ci ipmi_free_recv_msg(msg); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic void ipmi_register_bmc(int iface, struct device *dev) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi_device, *temp; 4398c2ecf20Sopenharmony_ci int err; 4408c2ecf20Sopenharmony_ci struct ipmi_smi_info smi_data; 4418c2ecf20Sopenharmony_ci acpi_handle handle; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci err = ipmi_get_smi_info(iface, &smi_data); 4448c2ecf20Sopenharmony_ci if (err) 4458c2ecf20Sopenharmony_ci return; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (smi_data.addr_src != SI_ACPI) 4488c2ecf20Sopenharmony_ci goto err_ref; 4498c2ecf20Sopenharmony_ci handle = smi_data.addr_info.acpi_info.acpi_handle; 4508c2ecf20Sopenharmony_ci if (!handle) 4518c2ecf20Sopenharmony_ci goto err_ref; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ipmi_device = ipmi_dev_alloc(iface, smi_data.dev, handle); 4548c2ecf20Sopenharmony_ci if (!ipmi_device) { 4558c2ecf20Sopenharmony_ci dev_warn(smi_data.dev, "Can't create IPMI user interface\n"); 4568c2ecf20Sopenharmony_ci goto err_ref; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci mutex_lock(&driver_data.ipmi_lock); 4608c2ecf20Sopenharmony_ci list_for_each_entry(temp, &driver_data.ipmi_devices, head) { 4618c2ecf20Sopenharmony_ci /* 4628c2ecf20Sopenharmony_ci * if the corresponding ACPI handle is already added 4638c2ecf20Sopenharmony_ci * to the device list, don't add it again. 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci if (temp->handle == handle) 4668c2ecf20Sopenharmony_ci goto err_lock; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci if (!driver_data.selected_smi) 4698c2ecf20Sopenharmony_ci driver_data.selected_smi = ipmi_device; 4708c2ecf20Sopenharmony_ci list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices); 4718c2ecf20Sopenharmony_ci mutex_unlock(&driver_data.ipmi_lock); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci put_device(smi_data.dev); 4748c2ecf20Sopenharmony_ci return; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cierr_lock: 4778c2ecf20Sopenharmony_ci mutex_unlock(&driver_data.ipmi_lock); 4788c2ecf20Sopenharmony_ci ipmi_dev_release(ipmi_device); 4798c2ecf20Sopenharmony_cierr_ref: 4808c2ecf20Sopenharmony_ci put_device(smi_data.dev); 4818c2ecf20Sopenharmony_ci return; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic void ipmi_bmc_gone(int iface) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi_device, *temp; 4878c2ecf20Sopenharmony_ci bool dev_found = false; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci mutex_lock(&driver_data.ipmi_lock); 4908c2ecf20Sopenharmony_ci list_for_each_entry_safe(ipmi_device, temp, 4918c2ecf20Sopenharmony_ci &driver_data.ipmi_devices, head) { 4928c2ecf20Sopenharmony_ci if (ipmi_device->ipmi_ifnum != iface) { 4938c2ecf20Sopenharmony_ci dev_found = true; 4948c2ecf20Sopenharmony_ci __ipmi_dev_kill(ipmi_device); 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci if (!driver_data.selected_smi) 4998c2ecf20Sopenharmony_ci driver_data.selected_smi = list_first_entry_or_null( 5008c2ecf20Sopenharmony_ci &driver_data.ipmi_devices, 5018c2ecf20Sopenharmony_ci struct acpi_ipmi_device, head); 5028c2ecf20Sopenharmony_ci mutex_unlock(&driver_data.ipmi_lock); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (dev_found) { 5058c2ecf20Sopenharmony_ci ipmi_flush_tx_msg(ipmi_device); 5068c2ecf20Sopenharmony_ci acpi_ipmi_dev_put(ipmi_device); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* 5118c2ecf20Sopenharmony_ci * This is the IPMI opregion space handler. 5128c2ecf20Sopenharmony_ci * @function: indicates the read/write. In fact as the IPMI message is driven 5138c2ecf20Sopenharmony_ci * by command, only write is meaningful. 5148c2ecf20Sopenharmony_ci * @address: This contains the netfn/command of IPMI request message. 5158c2ecf20Sopenharmony_ci * @bits : not used. 5168c2ecf20Sopenharmony_ci * @value : it is an in/out parameter. It points to the IPMI message buffer. 5178c2ecf20Sopenharmony_ci * Before the IPMI message is sent, it represents the actual request 5188c2ecf20Sopenharmony_ci * IPMI message. After the IPMI message is finished, it represents 5198c2ecf20Sopenharmony_ci * the response IPMI message returned by IPMI command. 5208c2ecf20Sopenharmony_ci * @handler_context: IPMI device context. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic acpi_status 5238c2ecf20Sopenharmony_ciacpi_ipmi_space_handler(u32 function, acpi_physical_address address, 5248c2ecf20Sopenharmony_ci u32 bits, acpi_integer *value, 5258c2ecf20Sopenharmony_ci void *handler_context, void *region_context) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct acpi_ipmi_msg *tx_msg; 5288c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi_device; 5298c2ecf20Sopenharmony_ci int err; 5308c2ecf20Sopenharmony_ci acpi_status status; 5318c2ecf20Sopenharmony_ci unsigned long flags; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* 5348c2ecf20Sopenharmony_ci * IPMI opregion message. 5358c2ecf20Sopenharmony_ci * IPMI message is firstly written to the BMC and system software 5368c2ecf20Sopenharmony_ci * can get the respsonse. So it is unmeaningful for the read access 5378c2ecf20Sopenharmony_ci * of IPMI opregion. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci if ((function & ACPI_IO_MASK) == ACPI_READ) 5408c2ecf20Sopenharmony_ci return AE_TYPE; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci tx_msg = ipmi_msg_alloc(); 5438c2ecf20Sopenharmony_ci if (!tx_msg) 5448c2ecf20Sopenharmony_ci return AE_NOT_EXIST; 5458c2ecf20Sopenharmony_ci ipmi_device = tx_msg->device; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (acpi_format_ipmi_request(tx_msg, address, value) != 0) { 5488c2ecf20Sopenharmony_ci ipmi_msg_release(tx_msg); 5498c2ecf20Sopenharmony_ci return AE_TYPE; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci acpi_ipmi_msg_get(tx_msg); 5538c2ecf20Sopenharmony_ci mutex_lock(&driver_data.ipmi_lock); 5548c2ecf20Sopenharmony_ci /* Do not add a tx_msg that can not be flushed. */ 5558c2ecf20Sopenharmony_ci if (ipmi_device->dead) { 5568c2ecf20Sopenharmony_ci mutex_unlock(&driver_data.ipmi_lock); 5578c2ecf20Sopenharmony_ci ipmi_msg_release(tx_msg); 5588c2ecf20Sopenharmony_ci return AE_NOT_EXIST; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); 5618c2ecf20Sopenharmony_ci list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list); 5628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); 5638c2ecf20Sopenharmony_ci mutex_unlock(&driver_data.ipmi_lock); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci err = ipmi_request_settime(ipmi_device->user_interface, 5668c2ecf20Sopenharmony_ci &tx_msg->addr, 5678c2ecf20Sopenharmony_ci tx_msg->tx_msgid, 5688c2ecf20Sopenharmony_ci &tx_msg->tx_message, 5698c2ecf20Sopenharmony_ci NULL, 0, 0, IPMI_TIMEOUT); 5708c2ecf20Sopenharmony_ci if (err) { 5718c2ecf20Sopenharmony_ci status = AE_ERROR; 5728c2ecf20Sopenharmony_ci goto out_msg; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci wait_for_completion(&tx_msg->tx_complete); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci acpi_format_ipmi_response(tx_msg, value); 5778c2ecf20Sopenharmony_ci status = AE_OK; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ciout_msg: 5808c2ecf20Sopenharmony_ci ipmi_cancel_tx_msg(ipmi_device, tx_msg); 5818c2ecf20Sopenharmony_ci acpi_ipmi_msg_put(tx_msg); 5828c2ecf20Sopenharmony_ci return status; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int __init acpi_ipmi_init(void) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci int result; 5888c2ecf20Sopenharmony_ci acpi_status status; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (acpi_disabled) 5918c2ecf20Sopenharmony_ci return 0; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, 5948c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_IPMI, 5958c2ecf20Sopenharmony_ci &acpi_ipmi_space_handler, 5968c2ecf20Sopenharmony_ci NULL, NULL); 5978c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 5988c2ecf20Sopenharmony_ci pr_warn("Can't register IPMI opregion space handle\n"); 5998c2ecf20Sopenharmony_ci return -EINVAL; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci result = ipmi_smi_watcher_register(&driver_data.bmc_events); 6028c2ecf20Sopenharmony_ci if (result) 6038c2ecf20Sopenharmony_ci pr_err("Can't register IPMI system interface watcher\n"); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return result; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic void __exit acpi_ipmi_exit(void) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct acpi_ipmi_device *ipmi_device; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (acpi_disabled) 6138c2ecf20Sopenharmony_ci return; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci ipmi_smi_watcher_unregister(&driver_data.bmc_events); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* 6188c2ecf20Sopenharmony_ci * When one smi_watcher is unregistered, it is only deleted 6198c2ecf20Sopenharmony_ci * from the smi_watcher list. But the smi_gone callback function 6208c2ecf20Sopenharmony_ci * is not called. So explicitly uninstall the ACPI IPMI oregion 6218c2ecf20Sopenharmony_ci * handler and free it. 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_ci mutex_lock(&driver_data.ipmi_lock); 6248c2ecf20Sopenharmony_ci while (!list_empty(&driver_data.ipmi_devices)) { 6258c2ecf20Sopenharmony_ci ipmi_device = list_first_entry(&driver_data.ipmi_devices, 6268c2ecf20Sopenharmony_ci struct acpi_ipmi_device, 6278c2ecf20Sopenharmony_ci head); 6288c2ecf20Sopenharmony_ci __ipmi_dev_kill(ipmi_device); 6298c2ecf20Sopenharmony_ci mutex_unlock(&driver_data.ipmi_lock); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci ipmi_flush_tx_msg(ipmi_device); 6328c2ecf20Sopenharmony_ci acpi_ipmi_dev_put(ipmi_device); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci mutex_lock(&driver_data.ipmi_lock); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci mutex_unlock(&driver_data.ipmi_lock); 6378c2ecf20Sopenharmony_ci acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, 6388c2ecf20Sopenharmony_ci ACPI_ADR_SPACE_IPMI, 6398c2ecf20Sopenharmony_ci &acpi_ipmi_space_handler); 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cimodule_init(acpi_ipmi_init); 6438c2ecf20Sopenharmony_cimodule_exit(acpi_ipmi_exit); 644