162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ipmi_poweroff.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * MontaVista IPMI Poweroff extension to sys_reboot 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: MontaVista Software, Inc. 862306a36Sopenharmony_ci * Steven Dake <sdake@mvista.com> 962306a36Sopenharmony_ci * Corey Minyard <cminyard@mvista.com> 1062306a36Sopenharmony_ci * source@mvista.com 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright 2002,2004 MontaVista Software Inc. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define pr_fmt(fmt) "IPMI poweroff: " fmt 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/moduleparam.h> 1962306a36Sopenharmony_ci#include <linux/proc_fs.h> 2062306a36Sopenharmony_ci#include <linux/string.h> 2162306a36Sopenharmony_ci#include <linux/completion.h> 2262306a36Sopenharmony_ci#include <linux/pm.h> 2362306a36Sopenharmony_ci#include <linux/kdev_t.h> 2462306a36Sopenharmony_ci#include <linux/ipmi.h> 2562306a36Sopenharmony_ci#include <linux/ipmi_smi.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic void ipmi_po_smi_gone(int if_num); 2862306a36Sopenharmony_cistatic void ipmi_po_new_smi(int if_num, struct device *device); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Definitions for controlling power off (if the system supports it). It 3162306a36Sopenharmony_ci * conveniently matches the IPMI chassis control values. */ 3262306a36Sopenharmony_ci#define IPMI_CHASSIS_POWER_DOWN 0 /* power down, the default. */ 3362306a36Sopenharmony_ci#define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* the IPMI data command */ 3662306a36Sopenharmony_cistatic int poweroff_powercycle; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Which interface to use, -1 means the first we see. */ 3962306a36Sopenharmony_cistatic int ifnum_to_use = -1; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Our local state. */ 4262306a36Sopenharmony_cistatic int ready; 4362306a36Sopenharmony_cistatic struct ipmi_user *ipmi_user; 4462306a36Sopenharmony_cistatic int ipmi_ifnum; 4562306a36Sopenharmony_cistatic void (*specific_poweroff_func)(struct ipmi_user *user); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Holds the old poweroff function so we can restore it on removal. */ 4862306a36Sopenharmony_cistatic void (*old_poweroff_func)(void); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int set_param_ifnum(const char *val, const struct kernel_param *kp) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int rv = param_set_int(val, kp); 5362306a36Sopenharmony_ci if (rv) 5462306a36Sopenharmony_ci return rv; 5562306a36Sopenharmony_ci if ((ifnum_to_use < 0) || (ifnum_to_use == ipmi_ifnum)) 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ipmi_po_smi_gone(ipmi_ifnum); 5962306a36Sopenharmony_ci ipmi_po_new_smi(ifnum_to_use, NULL); 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cimodule_param_call(ifnum_to_use, set_param_ifnum, param_get_int, 6462306a36Sopenharmony_ci &ifnum_to_use, 0644); 6562306a36Sopenharmony_ciMODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog " 6662306a36Sopenharmony_ci "timer. Setting to -1 defaults to the first registered " 6762306a36Sopenharmony_ci "interface"); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* parameter definition to allow user to flag power cycle */ 7062306a36Sopenharmony_cimodule_param(poweroff_powercycle, int, 0644); 7162306a36Sopenharmony_ciMODULE_PARM_DESC(poweroff_powercycle, 7262306a36Sopenharmony_ci " Set to non-zero to enable power cycle instead of power" 7362306a36Sopenharmony_ci " down. Power cycle is contingent on hardware support," 7462306a36Sopenharmony_ci " otherwise it defaults back to power down."); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Stuff from the get device id command. */ 7762306a36Sopenharmony_cistatic unsigned int mfg_id; 7862306a36Sopenharmony_cistatic unsigned int prod_id; 7962306a36Sopenharmony_cistatic unsigned char capabilities; 8062306a36Sopenharmony_cistatic unsigned char ipmi_version; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * We use our own messages for this operation, we don't let the system 8462306a36Sopenharmony_ci * allocate them, since we may be in a panic situation. The whole 8562306a36Sopenharmony_ci * thing is single-threaded, anyway, so multiple messages are not 8662306a36Sopenharmony_ci * required. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic atomic_t dummy_count = ATOMIC_INIT(0); 8962306a36Sopenharmony_cistatic void dummy_smi_free(struct ipmi_smi_msg *msg) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci atomic_dec(&dummy_count); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_cistatic void dummy_recv_free(struct ipmi_recv_msg *msg) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci atomic_dec(&dummy_count); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_cistatic struct ipmi_smi_msg halt_smi_msg = INIT_IPMI_SMI_MSG(dummy_smi_free); 9862306a36Sopenharmony_cistatic struct ipmi_recv_msg halt_recv_msg = INIT_IPMI_RECV_MSG(dummy_recv_free); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* 10262306a36Sopenharmony_ci * Code to send a message and wait for the response. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct completion *comp = recv_msg->user_msg_data; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (comp) 11062306a36Sopenharmony_ci complete(comp); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const struct ipmi_user_hndl ipmi_poweroff_handler = { 11462306a36Sopenharmony_ci .ipmi_recv_hndl = receive_handler 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int ipmi_request_wait_for_response(struct ipmi_user *user, 11962306a36Sopenharmony_ci struct ipmi_addr *addr, 12062306a36Sopenharmony_ci struct kernel_ipmi_msg *send_msg) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int rv; 12362306a36Sopenharmony_ci struct completion comp; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci init_completion(&comp); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &comp, 12862306a36Sopenharmony_ci &halt_smi_msg, &halt_recv_msg, 0); 12962306a36Sopenharmony_ci if (rv) 13062306a36Sopenharmony_ci return rv; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci wait_for_completion(&comp); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return halt_recv_msg.msg.data[0]; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* Wait for message to complete, spinning. */ 13862306a36Sopenharmony_cistatic int ipmi_request_in_rc_mode(struct ipmi_user *user, 13962306a36Sopenharmony_ci struct ipmi_addr *addr, 14062306a36Sopenharmony_ci struct kernel_ipmi_msg *send_msg) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int rv; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci atomic_set(&dummy_count, 2); 14562306a36Sopenharmony_ci rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, 14662306a36Sopenharmony_ci &halt_smi_msg, &halt_recv_msg, 0); 14762306a36Sopenharmony_ci if (rv) { 14862306a36Sopenharmony_ci atomic_set(&dummy_count, 0); 14962306a36Sopenharmony_ci return rv; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * Spin until our message is done. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci while (atomic_read(&dummy_count) > 0) { 15662306a36Sopenharmony_ci ipmi_poll_interface(user); 15762306a36Sopenharmony_ci cpu_relax(); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return halt_recv_msg.msg.data[0]; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * ATCA Support 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define IPMI_NETFN_ATCA 0x2c 16862306a36Sopenharmony_ci#define IPMI_ATCA_SET_POWER_CMD 0x11 16962306a36Sopenharmony_ci#define IPMI_ATCA_GET_ADDR_INFO_CMD 0x01 17062306a36Sopenharmony_ci#define IPMI_PICMG_ID 0 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci#define IPMI_NETFN_OEM 0x2e 17362306a36Sopenharmony_ci#define IPMI_ATCA_PPS_GRACEFUL_RESTART 0x11 17462306a36Sopenharmony_ci#define IPMI_ATCA_PPS_IANA "\x00\x40\x0A" 17562306a36Sopenharmony_ci#define IPMI_MOTOROLA_MANUFACTURER_ID 0x0000A1 17662306a36Sopenharmony_ci#define IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID 0x0051 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void (*atca_oem_poweroff_hook)(struct ipmi_user *user); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void pps_poweroff_atca(struct ipmi_user *user) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct ipmi_system_interface_addr smi_addr; 18362306a36Sopenharmony_ci struct kernel_ipmi_msg send_msg; 18462306a36Sopenharmony_ci int rv; 18562306a36Sopenharmony_ci /* 18662306a36Sopenharmony_ci * Configure IPMI address for local access 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 18962306a36Sopenharmony_ci smi_addr.channel = IPMI_BMC_CHANNEL; 19062306a36Sopenharmony_ci smi_addr.lun = 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci pr_info("PPS powerdown hook used\n"); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_OEM; 19562306a36Sopenharmony_ci send_msg.cmd = IPMI_ATCA_PPS_GRACEFUL_RESTART; 19662306a36Sopenharmony_ci send_msg.data = IPMI_ATCA_PPS_IANA; 19762306a36Sopenharmony_ci send_msg.data_len = 3; 19862306a36Sopenharmony_ci rv = ipmi_request_in_rc_mode(user, 19962306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 20062306a36Sopenharmony_ci &send_msg); 20162306a36Sopenharmony_ci if (rv && rv != IPMI_UNKNOWN_ERR_COMPLETION_CODE) 20262306a36Sopenharmony_ci pr_err("Unable to send ATCA, IPMI error 0x%x\n", rv); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int ipmi_atca_detect(struct ipmi_user *user) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct ipmi_system_interface_addr smi_addr; 21062306a36Sopenharmony_ci struct kernel_ipmi_msg send_msg; 21162306a36Sopenharmony_ci int rv; 21262306a36Sopenharmony_ci unsigned char data[1]; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * Configure IPMI address for local access 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 21862306a36Sopenharmony_ci smi_addr.channel = IPMI_BMC_CHANNEL; 21962306a36Sopenharmony_ci smi_addr.lun = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * Use get address info to check and see if we are ATCA 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_ATCA; 22562306a36Sopenharmony_ci send_msg.cmd = IPMI_ATCA_GET_ADDR_INFO_CMD; 22662306a36Sopenharmony_ci data[0] = IPMI_PICMG_ID; 22762306a36Sopenharmony_ci send_msg.data = data; 22862306a36Sopenharmony_ci send_msg.data_len = sizeof(data); 22962306a36Sopenharmony_ci rv = ipmi_request_wait_for_response(user, 23062306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 23162306a36Sopenharmony_ci &send_msg); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci pr_info("ATCA Detect mfg 0x%X prod 0x%X\n", mfg_id, prod_id); 23462306a36Sopenharmony_ci if ((mfg_id == IPMI_MOTOROLA_MANUFACTURER_ID) 23562306a36Sopenharmony_ci && (prod_id == IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID)) { 23662306a36Sopenharmony_ci pr_info("Installing Pigeon Point Systems Poweroff Hook\n"); 23762306a36Sopenharmony_ci atca_oem_poweroff_hook = pps_poweroff_atca; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci return !rv; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic void ipmi_poweroff_atca(struct ipmi_user *user) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct ipmi_system_interface_addr smi_addr; 24562306a36Sopenharmony_ci struct kernel_ipmi_msg send_msg; 24662306a36Sopenharmony_ci int rv; 24762306a36Sopenharmony_ci unsigned char data[4]; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * Configure IPMI address for local access 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 25362306a36Sopenharmony_ci smi_addr.channel = IPMI_BMC_CHANNEL; 25462306a36Sopenharmony_ci smi_addr.lun = 0; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci pr_info("Powering down via ATCA power command\n"); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * Power down 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_ATCA; 26262306a36Sopenharmony_ci send_msg.cmd = IPMI_ATCA_SET_POWER_CMD; 26362306a36Sopenharmony_ci data[0] = IPMI_PICMG_ID; 26462306a36Sopenharmony_ci data[1] = 0; /* FRU id */ 26562306a36Sopenharmony_ci data[2] = 0; /* Power Level */ 26662306a36Sopenharmony_ci data[3] = 0; /* Don't change saved presets */ 26762306a36Sopenharmony_ci send_msg.data = data; 26862306a36Sopenharmony_ci send_msg.data_len = sizeof(data); 26962306a36Sopenharmony_ci rv = ipmi_request_in_rc_mode(user, 27062306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 27162306a36Sopenharmony_ci &send_msg); 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * At this point, the system may be shutting down, and most 27462306a36Sopenharmony_ci * serial drivers (if used) will have interrupts turned off 27562306a36Sopenharmony_ci * it may be better to ignore IPMI_UNKNOWN_ERR_COMPLETION_CODE 27662306a36Sopenharmony_ci * return code 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci if (rv && rv != IPMI_UNKNOWN_ERR_COMPLETION_CODE) { 27962306a36Sopenharmony_ci pr_err("Unable to send ATCA powerdown message, IPMI error 0x%x\n", 28062306a36Sopenharmony_ci rv); 28162306a36Sopenharmony_ci goto out; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (atca_oem_poweroff_hook) 28562306a36Sopenharmony_ci atca_oem_poweroff_hook(user); 28662306a36Sopenharmony_ci out: 28762306a36Sopenharmony_ci return; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* 29162306a36Sopenharmony_ci * CPI1 Support 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci#define IPMI_NETFN_OEM_1 0xf8 29562306a36Sopenharmony_ci#define OEM_GRP_CMD_SET_RESET_STATE 0x84 29662306a36Sopenharmony_ci#define OEM_GRP_CMD_SET_POWER_STATE 0x82 29762306a36Sopenharmony_ci#define IPMI_NETFN_OEM_8 0xf8 29862306a36Sopenharmony_ci#define OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL 0x80 29962306a36Sopenharmony_ci#define OEM_GRP_CMD_GET_SLOT_GA 0xa3 30062306a36Sopenharmony_ci#define IPMI_NETFN_SENSOR_EVT 0x10 30162306a36Sopenharmony_ci#define IPMI_CMD_GET_EVENT_RECEIVER 0x01 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci#define IPMI_CPI1_PRODUCT_ID 0x000157 30462306a36Sopenharmony_ci#define IPMI_CPI1_MANUFACTURER_ID 0x0108 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int ipmi_cpi1_detect(struct ipmi_user *user) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID) 30962306a36Sopenharmony_ci && (prod_id == IPMI_CPI1_PRODUCT_ID)); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void ipmi_poweroff_cpi1(struct ipmi_user *user) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct ipmi_system_interface_addr smi_addr; 31562306a36Sopenharmony_ci struct ipmi_ipmb_addr ipmb_addr; 31662306a36Sopenharmony_ci struct kernel_ipmi_msg send_msg; 31762306a36Sopenharmony_ci int rv; 31862306a36Sopenharmony_ci unsigned char data[1]; 31962306a36Sopenharmony_ci int slot; 32062306a36Sopenharmony_ci unsigned char hotswap_ipmb; 32162306a36Sopenharmony_ci unsigned char aer_addr; 32262306a36Sopenharmony_ci unsigned char aer_lun; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* 32562306a36Sopenharmony_ci * Configure IPMI address for local access 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 32862306a36Sopenharmony_ci smi_addr.channel = IPMI_BMC_CHANNEL; 32962306a36Sopenharmony_ci smi_addr.lun = 0; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci pr_info("Powering down via CPI1 power command\n"); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * Get IPMI ipmb address 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_OEM_8 >> 2; 33762306a36Sopenharmony_ci send_msg.cmd = OEM_GRP_CMD_GET_SLOT_GA; 33862306a36Sopenharmony_ci send_msg.data = NULL; 33962306a36Sopenharmony_ci send_msg.data_len = 0; 34062306a36Sopenharmony_ci rv = ipmi_request_in_rc_mode(user, 34162306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 34262306a36Sopenharmony_ci &send_msg); 34362306a36Sopenharmony_ci if (rv) 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci slot = halt_recv_msg.msg.data[1]; 34662306a36Sopenharmony_ci hotswap_ipmb = (slot > 9) ? (0xb0 + 2 * slot) : (0xae + 2 * slot); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * Get active event receiver 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_SENSOR_EVT >> 2; 35262306a36Sopenharmony_ci send_msg.cmd = IPMI_CMD_GET_EVENT_RECEIVER; 35362306a36Sopenharmony_ci send_msg.data = NULL; 35462306a36Sopenharmony_ci send_msg.data_len = 0; 35562306a36Sopenharmony_ci rv = ipmi_request_in_rc_mode(user, 35662306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 35762306a36Sopenharmony_ci &send_msg); 35862306a36Sopenharmony_ci if (rv) 35962306a36Sopenharmony_ci goto out; 36062306a36Sopenharmony_ci aer_addr = halt_recv_msg.msg.data[1]; 36162306a36Sopenharmony_ci aer_lun = halt_recv_msg.msg.data[2]; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* 36462306a36Sopenharmony_ci * Setup IPMB address target instead of local target 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; 36762306a36Sopenharmony_ci ipmb_addr.channel = 0; 36862306a36Sopenharmony_ci ipmb_addr.slave_addr = aer_addr; 36962306a36Sopenharmony_ci ipmb_addr.lun = aer_lun; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* 37262306a36Sopenharmony_ci * Send request hotswap control to remove blade from dpv 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_OEM_8 >> 2; 37562306a36Sopenharmony_ci send_msg.cmd = OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL; 37662306a36Sopenharmony_ci send_msg.data = &hotswap_ipmb; 37762306a36Sopenharmony_ci send_msg.data_len = 1; 37862306a36Sopenharmony_ci ipmi_request_in_rc_mode(user, 37962306a36Sopenharmony_ci (struct ipmi_addr *) &ipmb_addr, 38062306a36Sopenharmony_ci &send_msg); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* 38362306a36Sopenharmony_ci * Set reset asserted 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_OEM_1 >> 2; 38662306a36Sopenharmony_ci send_msg.cmd = OEM_GRP_CMD_SET_RESET_STATE; 38762306a36Sopenharmony_ci send_msg.data = data; 38862306a36Sopenharmony_ci data[0] = 1; /* Reset asserted state */ 38962306a36Sopenharmony_ci send_msg.data_len = 1; 39062306a36Sopenharmony_ci rv = ipmi_request_in_rc_mode(user, 39162306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 39262306a36Sopenharmony_ci &send_msg); 39362306a36Sopenharmony_ci if (rv) 39462306a36Sopenharmony_ci goto out; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci * Power down 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_OEM_1 >> 2; 40062306a36Sopenharmony_ci send_msg.cmd = OEM_GRP_CMD_SET_POWER_STATE; 40162306a36Sopenharmony_ci send_msg.data = data; 40262306a36Sopenharmony_ci data[0] = 1; /* Power down state */ 40362306a36Sopenharmony_ci send_msg.data_len = 1; 40462306a36Sopenharmony_ci rv = ipmi_request_in_rc_mode(user, 40562306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 40662306a36Sopenharmony_ci &send_msg); 40762306a36Sopenharmony_ci if (rv) 40862306a36Sopenharmony_ci goto out; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci out: 41162306a36Sopenharmony_ci return; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* 41562306a36Sopenharmony_ci * ipmi_dell_chassis_detect() 41662306a36Sopenharmony_ci * Dell systems with IPMI < 1.5 don't set the chassis capability bit 41762306a36Sopenharmony_ci * but they can handle a chassis poweroff or powercycle command. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci#define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00} 42162306a36Sopenharmony_cistatic int ipmi_dell_chassis_detect(struct ipmi_user *user) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci const char ipmi_version_major = ipmi_version & 0xF; 42462306a36Sopenharmony_ci const char ipmi_version_minor = (ipmi_version >> 4) & 0xF; 42562306a36Sopenharmony_ci const char mfr[3] = DELL_IANA_MFR_ID; 42662306a36Sopenharmony_ci if (!memcmp(mfr, &mfg_id, sizeof(mfr)) && 42762306a36Sopenharmony_ci ipmi_version_major <= 1 && 42862306a36Sopenharmony_ci ipmi_version_minor < 5) 42962306a36Sopenharmony_ci return 1; 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/* 43462306a36Sopenharmony_ci * ipmi_hp_chassis_detect() 43562306a36Sopenharmony_ci * HP PA-RISC servers rp3410/rp3440, the C8000 workstation and the rx2600 and 43662306a36Sopenharmony_ci * zx6000 machines support IPMI vers 1 and don't set the chassis capability bit 43762306a36Sopenharmony_ci * but they can handle a chassis poweroff or powercycle command. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci#define HP_IANA_MFR_ID 0x0b 44162306a36Sopenharmony_ci#define HP_BMC_PROD_ID 0x8201 44262306a36Sopenharmony_cistatic int ipmi_hp_chassis_detect(struct ipmi_user *user) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci if (mfg_id == HP_IANA_MFR_ID 44562306a36Sopenharmony_ci && prod_id == HP_BMC_PROD_ID 44662306a36Sopenharmony_ci && ipmi_version == 1) 44762306a36Sopenharmony_ci return 1; 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/* 45262306a36Sopenharmony_ci * Standard chassis support 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci#define IPMI_NETFN_CHASSIS_REQUEST 0 45662306a36Sopenharmony_ci#define IPMI_CHASSIS_CONTROL_CMD 0x02 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int ipmi_chassis_detect(struct ipmi_user *user) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci /* Chassis support, use it. */ 46162306a36Sopenharmony_ci return (capabilities & 0x80); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic void ipmi_poweroff_chassis(struct ipmi_user *user) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct ipmi_system_interface_addr smi_addr; 46762306a36Sopenharmony_ci struct kernel_ipmi_msg send_msg; 46862306a36Sopenharmony_ci int rv; 46962306a36Sopenharmony_ci unsigned char data[1]; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * Configure IPMI address for local access 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 47562306a36Sopenharmony_ci smi_addr.channel = IPMI_BMC_CHANNEL; 47662306a36Sopenharmony_ci smi_addr.lun = 0; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci powercyclefailed: 47962306a36Sopenharmony_ci pr_info("Powering %s via IPMI chassis control command\n", 48062306a36Sopenharmony_ci (poweroff_powercycle ? "cycle" : "down")); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* 48362306a36Sopenharmony_ci * Power down 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; 48662306a36Sopenharmony_ci send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; 48762306a36Sopenharmony_ci if (poweroff_powercycle) 48862306a36Sopenharmony_ci data[0] = IPMI_CHASSIS_POWER_CYCLE; 48962306a36Sopenharmony_ci else 49062306a36Sopenharmony_ci data[0] = IPMI_CHASSIS_POWER_DOWN; 49162306a36Sopenharmony_ci send_msg.data = data; 49262306a36Sopenharmony_ci send_msg.data_len = sizeof(data); 49362306a36Sopenharmony_ci rv = ipmi_request_in_rc_mode(user, 49462306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 49562306a36Sopenharmony_ci &send_msg); 49662306a36Sopenharmony_ci if (rv) { 49762306a36Sopenharmony_ci if (poweroff_powercycle) { 49862306a36Sopenharmony_ci /* power cycle failed, default to power down */ 49962306a36Sopenharmony_ci pr_err("Unable to send chassis power cycle message, IPMI error 0x%x\n", 50062306a36Sopenharmony_ci rv); 50162306a36Sopenharmony_ci poweroff_powercycle = 0; 50262306a36Sopenharmony_ci goto powercyclefailed; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci pr_err("Unable to send chassis power down message, IPMI error 0x%x\n", 50662306a36Sopenharmony_ci rv); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/* Table of possible power off functions. */ 51262306a36Sopenharmony_cistruct poweroff_function { 51362306a36Sopenharmony_ci char *platform_type; 51462306a36Sopenharmony_ci int (*detect)(struct ipmi_user *user); 51562306a36Sopenharmony_ci void (*poweroff_func)(struct ipmi_user *user); 51662306a36Sopenharmony_ci}; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic struct poweroff_function poweroff_functions[] = { 51962306a36Sopenharmony_ci { .platform_type = "ATCA", 52062306a36Sopenharmony_ci .detect = ipmi_atca_detect, 52162306a36Sopenharmony_ci .poweroff_func = ipmi_poweroff_atca }, 52262306a36Sopenharmony_ci { .platform_type = "CPI1", 52362306a36Sopenharmony_ci .detect = ipmi_cpi1_detect, 52462306a36Sopenharmony_ci .poweroff_func = ipmi_poweroff_cpi1 }, 52562306a36Sopenharmony_ci { .platform_type = "chassis", 52662306a36Sopenharmony_ci .detect = ipmi_dell_chassis_detect, 52762306a36Sopenharmony_ci .poweroff_func = ipmi_poweroff_chassis }, 52862306a36Sopenharmony_ci { .platform_type = "chassis", 52962306a36Sopenharmony_ci .detect = ipmi_hp_chassis_detect, 53062306a36Sopenharmony_ci .poweroff_func = ipmi_poweroff_chassis }, 53162306a36Sopenharmony_ci /* Chassis should generally be last, other things should override 53262306a36Sopenharmony_ci it. */ 53362306a36Sopenharmony_ci { .platform_type = "chassis", 53462306a36Sopenharmony_ci .detect = ipmi_chassis_detect, 53562306a36Sopenharmony_ci .poweroff_func = ipmi_poweroff_chassis }, 53662306a36Sopenharmony_ci}; 53762306a36Sopenharmony_ci#define NUM_PO_FUNCS ARRAY_SIZE(poweroff_functions) 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* Called on a powerdown request. */ 54162306a36Sopenharmony_cistatic void ipmi_poweroff_function(void) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci if (!ready) 54462306a36Sopenharmony_ci return; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* Use run-to-completion mode, since interrupts may be off. */ 54762306a36Sopenharmony_ci specific_poweroff_func(ipmi_user); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/* Wait for an IPMI interface to be installed, the first one installed 55162306a36Sopenharmony_ci will be grabbed by this code and used to perform the powerdown. */ 55262306a36Sopenharmony_cistatic void ipmi_po_new_smi(int if_num, struct device *device) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct ipmi_system_interface_addr smi_addr; 55562306a36Sopenharmony_ci struct kernel_ipmi_msg send_msg; 55662306a36Sopenharmony_ci int rv; 55762306a36Sopenharmony_ci int i; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (ready) 56062306a36Sopenharmony_ci return; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if ((ifnum_to_use >= 0) && (ifnum_to_use != if_num)) 56362306a36Sopenharmony_ci return; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, 56662306a36Sopenharmony_ci &ipmi_user); 56762306a36Sopenharmony_ci if (rv) { 56862306a36Sopenharmony_ci pr_err("could not create IPMI user, error %d\n", rv); 56962306a36Sopenharmony_ci return; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ipmi_ifnum = if_num; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * Do a get device ide and store some results, since this is 57662306a36Sopenharmony_ci * used by several functions. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; 57962306a36Sopenharmony_ci smi_addr.channel = IPMI_BMC_CHANNEL; 58062306a36Sopenharmony_ci smi_addr.lun = 0; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci send_msg.netfn = IPMI_NETFN_APP_REQUEST; 58362306a36Sopenharmony_ci send_msg.cmd = IPMI_GET_DEVICE_ID_CMD; 58462306a36Sopenharmony_ci send_msg.data = NULL; 58562306a36Sopenharmony_ci send_msg.data_len = 0; 58662306a36Sopenharmony_ci rv = ipmi_request_wait_for_response(ipmi_user, 58762306a36Sopenharmony_ci (struct ipmi_addr *) &smi_addr, 58862306a36Sopenharmony_ci &send_msg); 58962306a36Sopenharmony_ci if (rv) { 59062306a36Sopenharmony_ci pr_err("Unable to send IPMI get device id info, IPMI error 0x%x\n", 59162306a36Sopenharmony_ci rv); 59262306a36Sopenharmony_ci goto out_err; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (halt_recv_msg.msg.data_len < 12) { 59662306a36Sopenharmony_ci pr_err("(chassis) IPMI get device id info too short, was %d bytes, needed %d bytes\n", 59762306a36Sopenharmony_ci halt_recv_msg.msg.data_len, 12); 59862306a36Sopenharmony_ci goto out_err; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci mfg_id = (halt_recv_msg.msg.data[7] 60262306a36Sopenharmony_ci | (halt_recv_msg.msg.data[8] << 8) 60362306a36Sopenharmony_ci | (halt_recv_msg.msg.data[9] << 16)); 60462306a36Sopenharmony_ci prod_id = (halt_recv_msg.msg.data[10] 60562306a36Sopenharmony_ci | (halt_recv_msg.msg.data[11] << 8)); 60662306a36Sopenharmony_ci capabilities = halt_recv_msg.msg.data[6]; 60762306a36Sopenharmony_ci ipmi_version = halt_recv_msg.msg.data[5]; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* Scan for a poweroff method */ 61162306a36Sopenharmony_ci for (i = 0; i < NUM_PO_FUNCS; i++) { 61262306a36Sopenharmony_ci if (poweroff_functions[i].detect(ipmi_user)) 61362306a36Sopenharmony_ci goto found; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci out_err: 61762306a36Sopenharmony_ci pr_err("Unable to find a poweroff function that will work, giving up\n"); 61862306a36Sopenharmony_ci ipmi_destroy_user(ipmi_user); 61962306a36Sopenharmony_ci return; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci found: 62262306a36Sopenharmony_ci pr_info("Found a %s style poweroff function\n", 62362306a36Sopenharmony_ci poweroff_functions[i].platform_type); 62462306a36Sopenharmony_ci specific_poweroff_func = poweroff_functions[i].poweroff_func; 62562306a36Sopenharmony_ci old_poweroff_func = pm_power_off; 62662306a36Sopenharmony_ci pm_power_off = ipmi_poweroff_function; 62762306a36Sopenharmony_ci ready = 1; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic void ipmi_po_smi_gone(int if_num) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci if (!ready) 63362306a36Sopenharmony_ci return; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (ipmi_ifnum != if_num) 63662306a36Sopenharmony_ci return; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ready = 0; 63962306a36Sopenharmony_ci ipmi_destroy_user(ipmi_user); 64062306a36Sopenharmony_ci pm_power_off = old_poweroff_func; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic struct ipmi_smi_watcher smi_watcher = { 64462306a36Sopenharmony_ci .owner = THIS_MODULE, 64562306a36Sopenharmony_ci .new_smi = ipmi_po_new_smi, 64662306a36Sopenharmony_ci .smi_gone = ipmi_po_smi_gone 64762306a36Sopenharmony_ci}; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 65162306a36Sopenharmony_ci#include <linux/sysctl.h> 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic struct ctl_table ipmi_table[] = { 65462306a36Sopenharmony_ci { .procname = "poweroff_powercycle", 65562306a36Sopenharmony_ci .data = &poweroff_powercycle, 65662306a36Sopenharmony_ci .maxlen = sizeof(poweroff_powercycle), 65762306a36Sopenharmony_ci .mode = 0644, 65862306a36Sopenharmony_ci .proc_handler = proc_dointvec }, 65962306a36Sopenharmony_ci { } 66062306a36Sopenharmony_ci}; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic struct ctl_table_header *ipmi_table_header; 66362306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/* 66662306a36Sopenharmony_ci * Startup and shutdown functions. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_cistatic int __init ipmi_poweroff_init(void) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci int rv; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci pr_info("Copyright (C) 2004 MontaVista Software - IPMI Powerdown via sys_reboot\n"); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (poweroff_powercycle) 67562306a36Sopenharmony_ci pr_info("Power cycle is enabled\n"); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 67862306a36Sopenharmony_ci ipmi_table_header = register_sysctl("dev/ipmi", ipmi_table); 67962306a36Sopenharmony_ci if (!ipmi_table_header) { 68062306a36Sopenharmony_ci pr_err("Unable to register powercycle sysctl\n"); 68162306a36Sopenharmony_ci rv = -ENOMEM; 68262306a36Sopenharmony_ci goto out_err; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci#endif 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci rv = ipmi_smi_watcher_register(&smi_watcher); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 68962306a36Sopenharmony_ci if (rv) { 69062306a36Sopenharmony_ci unregister_sysctl_table(ipmi_table_header); 69162306a36Sopenharmony_ci pr_err("Unable to register SMI watcher: %d\n", rv); 69262306a36Sopenharmony_ci goto out_err; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci out_err: 69662306a36Sopenharmony_ci#endif 69762306a36Sopenharmony_ci return rv; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci#ifdef MODULE 70162306a36Sopenharmony_cistatic void __exit ipmi_poweroff_cleanup(void) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci int rv; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 70662306a36Sopenharmony_ci unregister_sysctl_table(ipmi_table_header); 70762306a36Sopenharmony_ci#endif 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci ipmi_smi_watcher_unregister(&smi_watcher); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (ready) { 71262306a36Sopenharmony_ci rv = ipmi_destroy_user(ipmi_user); 71362306a36Sopenharmony_ci if (rv) 71462306a36Sopenharmony_ci pr_err("could not cleanup the IPMI user: 0x%x\n", rv); 71562306a36Sopenharmony_ci pm_power_off = old_poweroff_func; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_cimodule_exit(ipmi_poweroff_cleanup); 71962306a36Sopenharmony_ci#endif 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cimodule_init(ipmi_poweroff_init); 72262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 72362306a36Sopenharmony_ciMODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); 72462306a36Sopenharmony_ciMODULE_DESCRIPTION("IPMI Poweroff extension to sys_reboot"); 725