11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * ipmi_poweroff.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * MontaVista IPMI Poweroff extension to sys_reboot
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Author: MontaVista Software, Inc.
71da177e4SLinus Torvalds  *         Steven Dake <sdake@mvista.com>
81da177e4SLinus Torvalds  *         Corey Minyard <cminyard@mvista.com>
91da177e4SLinus Torvalds  *         source@mvista.com
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Copyright 2002,2004 MontaVista Software Inc.
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  *  This program is free software; you can redistribute it and/or modify it
141da177e4SLinus Torvalds  *  under the terms of the GNU General Public License as published by the
151da177e4SLinus Torvalds  *  Free Software Foundation; either version 2 of the License, or (at your
161da177e4SLinus Torvalds  *  option) any later version.
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  *
191da177e4SLinus Torvalds  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
201da177e4SLinus Torvalds  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
211da177e4SLinus Torvalds  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
221da177e4SLinus Torvalds  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
231da177e4SLinus Torvalds  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
241da177e4SLinus Torvalds  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
251da177e4SLinus Torvalds  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
261da177e4SLinus Torvalds  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
271da177e4SLinus Torvalds  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
281da177e4SLinus Torvalds  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
291da177e4SLinus Torvalds  *
301da177e4SLinus Torvalds  *  You should have received a copy of the GNU General Public License along
311da177e4SLinus Torvalds  *  with this program; if not, write to the Free Software Foundation, Inc.,
321da177e4SLinus Torvalds  *  675 Mass Ave, Cambridge, MA 02139, USA.
331da177e4SLinus Torvalds  */
341da177e4SLinus Torvalds #include <linux/module.h>
353b625943SCorey Minyard #include <linux/moduleparam.h>
363b625943SCorey Minyard #include <linux/proc_fs.h>
371da177e4SLinus Torvalds #include <linux/string.h>
3877cf3973SCorey Minyard #include <linux/completion.h>
39e933b6d6SOlaf Hering #include <linux/pm.h>
4077cf3973SCorey Minyard #include <linux/kdev_t.h>
411da177e4SLinus Torvalds #include <linux/ipmi.h>
421da177e4SLinus Torvalds #include <linux/ipmi_smi.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #define PFX "IPMI poweroff: "
451da177e4SLinus Torvalds 
46b2c03941SCorey Minyard static void ipmi_po_smi_gone(int if_num);
47b2c03941SCorey Minyard static void ipmi_po_new_smi(int if_num, struct device *device);
48b2c03941SCorey Minyard 
493b625943SCorey Minyard /* Definitions for controlling power off (if the system supports it).  It
503b625943SCorey Minyard  * conveniently matches the IPMI chassis control values. */
513b625943SCorey Minyard #define IPMI_CHASSIS_POWER_DOWN		0	/* power down, the default. */
523b625943SCorey Minyard #define IPMI_CHASSIS_POWER_CYCLE	0x02	/* power cycle */
533b625943SCorey Minyard 
543b625943SCorey Minyard /* the IPMI data command */
558c702e16SCorey Minyard static int poweroff_powercycle;
563b625943SCorey Minyard 
57b2c03941SCorey Minyard /* Which interface to use, -1 means the first we see. */
58b2c03941SCorey Minyard static int ifnum_to_use = -1;
59b2c03941SCorey Minyard 
60b2c03941SCorey Minyard /* Our local state. */
610c8204b3SRandy Dunlap static int ready;
62b2c03941SCorey Minyard static ipmi_user_t ipmi_user;
63b2c03941SCorey Minyard static int ipmi_ifnum;
640c8204b3SRandy Dunlap static void (*specific_poweroff_func)(ipmi_user_t user);
65b2c03941SCorey Minyard 
66b2c03941SCorey Minyard /* Holds the old poweroff function so we can restore it on removal. */
67b2c03941SCorey Minyard static void (*old_poweroff_func)(void);
68b2c03941SCorey Minyard 
69b2c03941SCorey Minyard static int set_param_ifnum(const char *val, struct kernel_param *kp)
70b2c03941SCorey Minyard {
71b2c03941SCorey Minyard 	int rv = param_set_int(val, kp);
72b2c03941SCorey Minyard 	if (rv)
73b2c03941SCorey Minyard 		return rv;
74b2c03941SCorey Minyard 	if ((ifnum_to_use < 0) || (ifnum_to_use == ipmi_ifnum))
75b2c03941SCorey Minyard 		return 0;
76b2c03941SCorey Minyard 
77b2c03941SCorey Minyard 	ipmi_po_smi_gone(ipmi_ifnum);
78b2c03941SCorey Minyard 	ipmi_po_new_smi(ifnum_to_use, NULL);
79b2c03941SCorey Minyard 	return 0;
80b2c03941SCorey Minyard }
81b2c03941SCorey Minyard 
82b2c03941SCorey Minyard module_param_call(ifnum_to_use, set_param_ifnum, param_get_int,
83b2c03941SCorey Minyard 		  &ifnum_to_use, 0644);
84b2c03941SCorey Minyard MODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog "
85b2c03941SCorey Minyard 		 "timer.  Setting to -1 defaults to the first registered "
86b2c03941SCorey Minyard 		 "interface");
87b2c03941SCorey Minyard 
883b625943SCorey Minyard /* parameter definition to allow user to flag power cycle */
89a9d014afSCorey Minyard module_param(poweroff_powercycle, int, 0644);
9036c7dc44SCorey Minyard MODULE_PARM_DESC(poweroff_powercycle,
9136c7dc44SCorey Minyard 		 " Set to non-zero to enable power cycle instead of power"
9236c7dc44SCorey Minyard 		 " down. Power cycle is contingent on hardware support,"
9336c7dc44SCorey Minyard 		 " otherwise it defaults back to power down.");
943b625943SCorey Minyard 
951da177e4SLinus Torvalds /* Stuff from the get device id command. */
961da177e4SLinus Torvalds static unsigned int mfg_id;
971da177e4SLinus Torvalds static unsigned int prod_id;
981da177e4SLinus Torvalds static unsigned char capabilities;
99168524d6SCorey Minyard static unsigned char ipmi_version;
1001da177e4SLinus Torvalds 
10136c7dc44SCorey Minyard /*
10236c7dc44SCorey Minyard  * We use our own messages for this operation, we don't let the system
10336c7dc44SCorey Minyard  * allocate them, since we may be in a panic situation.  The whole
10436c7dc44SCorey Minyard  * thing is single-threaded, anyway, so multiple messages are not
10536c7dc44SCorey Minyard  * required.
10636c7dc44SCorey Minyard  */
107bda4c30aSCorey Minyard static atomic_t dummy_count = ATOMIC_INIT(0);
1081da177e4SLinus Torvalds static void dummy_smi_free(struct ipmi_smi_msg *msg)
1091da177e4SLinus Torvalds {
110bda4c30aSCorey Minyard 	atomic_dec(&dummy_count);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds static void dummy_recv_free(struct ipmi_recv_msg *msg)
1131da177e4SLinus Torvalds {
114bda4c30aSCorey Minyard 	atomic_dec(&dummy_count);
1151da177e4SLinus Torvalds }
11636c7dc44SCorey Minyard static struct ipmi_smi_msg halt_smi_msg = {
1171da177e4SLinus Torvalds 	.done = dummy_smi_free
1181da177e4SLinus Torvalds };
11936c7dc44SCorey Minyard static struct ipmi_recv_msg halt_recv_msg = {
1201da177e4SLinus Torvalds 	.done = dummy_recv_free
1211da177e4SLinus Torvalds };
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds /*
1251da177e4SLinus Torvalds  * Code to send a message and wait for the reponse.
1261da177e4SLinus Torvalds  */
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data)
1291da177e4SLinus Torvalds {
13077cf3973SCorey Minyard 	struct completion *comp = recv_msg->user_msg_data;
1311da177e4SLinus Torvalds 
13277cf3973SCorey Minyard 	if (comp)
13377cf3973SCorey Minyard 		complete(comp);
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
13636c7dc44SCorey Minyard static struct ipmi_user_hndl ipmi_poweroff_handler = {
1371da177e4SLinus Torvalds 	.ipmi_recv_hndl = receive_handler
1381da177e4SLinus Torvalds };
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds static int ipmi_request_wait_for_response(ipmi_user_t            user,
1421da177e4SLinus Torvalds 					  struct ipmi_addr       *addr,
1431da177e4SLinus Torvalds 					  struct kernel_ipmi_msg *send_msg)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	int               rv;
14677cf3973SCorey Minyard 	struct completion comp;
1471da177e4SLinus Torvalds 
14877cf3973SCorey Minyard 	init_completion(&comp);
1491da177e4SLinus Torvalds 
15077cf3973SCorey Minyard 	rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &comp,
1511da177e4SLinus Torvalds 				      &halt_smi_msg, &halt_recv_msg, 0);
1521da177e4SLinus Torvalds 	if (rv)
1531da177e4SLinus Torvalds 		return rv;
1541da177e4SLinus Torvalds 
15577cf3973SCorey Minyard 	wait_for_completion(&comp);
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	return halt_recv_msg.msg.data[0];
1581da177e4SLinus Torvalds }
1591da177e4SLinus Torvalds 
160bda4c30aSCorey Minyard /* Wait for message to complete, spinning. */
1611da177e4SLinus Torvalds static int ipmi_request_in_rc_mode(ipmi_user_t            user,
1621da177e4SLinus Torvalds 				   struct ipmi_addr       *addr,
1631da177e4SLinus Torvalds 				   struct kernel_ipmi_msg *send_msg)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds 	int rv;
1661da177e4SLinus Torvalds 
167bda4c30aSCorey Minyard 	atomic_set(&dummy_count, 2);
1681da177e4SLinus Torvalds 	rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL,
1691da177e4SLinus Torvalds 				      &halt_smi_msg, &halt_recv_msg, 0);
170bda4c30aSCorey Minyard 	if (rv) {
171bda4c30aSCorey Minyard 		atomic_set(&dummy_count, 0);
1721da177e4SLinus Torvalds 		return rv;
173bda4c30aSCorey Minyard 	}
174bda4c30aSCorey Minyard 
175bda4c30aSCorey Minyard 	/*
176bda4c30aSCorey Minyard 	 * Spin until our message is done.
177bda4c30aSCorey Minyard 	 */
178bda4c30aSCorey Minyard 	while (atomic_read(&dummy_count) > 0) {
179bda4c30aSCorey Minyard 		ipmi_poll_interface(user);
180bda4c30aSCorey Minyard 		cpu_relax();
181bda4c30aSCorey Minyard 	}
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	return halt_recv_msg.msg.data[0];
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds /*
1871da177e4SLinus Torvalds  * ATCA Support
1881da177e4SLinus Torvalds  */
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds #define IPMI_NETFN_ATCA			0x2c
1911da177e4SLinus Torvalds #define IPMI_ATCA_SET_POWER_CMD		0x11
1921da177e4SLinus Torvalds #define IPMI_ATCA_GET_ADDR_INFO_CMD	0x01
1931da177e4SLinus Torvalds #define IPMI_PICMG_ID			0
1941da177e4SLinus Torvalds 
1957d8ba966SCorey Minyard #define IPMI_NETFN_OEM				0x2e
1967d8ba966SCorey Minyard #define IPMI_ATCA_PPS_GRACEFUL_RESTART		0x11
1977d8ba966SCorey Minyard #define IPMI_ATCA_PPS_IANA			"\x00\x40\x0A"
1987d8ba966SCorey Minyard #define IPMI_MOTOROLA_MANUFACTURER_ID		0x0000A1
1997d8ba966SCorey Minyard #define IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID	0x0051
2007d8ba966SCorey Minyard 
2010c8204b3SRandy Dunlap static void (*atca_oem_poweroff_hook)(ipmi_user_t user);
2027d8ba966SCorey Minyard 
2037d8ba966SCorey Minyard static void pps_poweroff_atca(ipmi_user_t user)
2047d8ba966SCorey Minyard {
2057d8ba966SCorey Minyard 	struct ipmi_system_interface_addr smi_addr;
2067d8ba966SCorey Minyard 	struct kernel_ipmi_msg            send_msg;
2077d8ba966SCorey Minyard 	int                               rv;
2087d8ba966SCorey Minyard 	/*
2097d8ba966SCorey Minyard 	 * Configure IPMI address for local access
2107d8ba966SCorey Minyard 	 */
2117d8ba966SCorey Minyard 	smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
2127d8ba966SCorey Minyard 	smi_addr.channel = IPMI_BMC_CHANNEL;
2137d8ba966SCorey Minyard 	smi_addr.lun = 0;
2147d8ba966SCorey Minyard 
2157d8ba966SCorey Minyard 	printk(KERN_INFO PFX "PPS powerdown hook used");
2167d8ba966SCorey Minyard 
2177d8ba966SCorey Minyard 	send_msg.netfn = IPMI_NETFN_OEM;
2187d8ba966SCorey Minyard 	send_msg.cmd = IPMI_ATCA_PPS_GRACEFUL_RESTART;
2197d8ba966SCorey Minyard 	send_msg.data = IPMI_ATCA_PPS_IANA;
2207d8ba966SCorey Minyard 	send_msg.data_len = 3;
2217d8ba966SCorey Minyard 	rv = ipmi_request_in_rc_mode(user,
2227d8ba966SCorey Minyard 				     (struct ipmi_addr *) &smi_addr,
2237d8ba966SCorey Minyard 				     &send_msg);
2247d8ba966SCorey Minyard 	if (rv && rv != IPMI_UNKNOWN_ERR_COMPLETION_CODE) {
2257d8ba966SCorey Minyard 		printk(KERN_ERR PFX "Unable to send ATCA ,"
2267d8ba966SCorey Minyard 		       " IPMI error 0x%x\n", rv);
2277d8ba966SCorey Minyard 	}
2287d8ba966SCorey Minyard 	return;
2297d8ba966SCorey Minyard }
2307d8ba966SCorey Minyard 
2311da177e4SLinus Torvalds static int ipmi_atca_detect(ipmi_user_t user)
2321da177e4SLinus Torvalds {
2331da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
2341da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
2351da177e4SLinus Torvalds 	int                               rv;
2361da177e4SLinus Torvalds 	unsigned char                     data[1];
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 	/*
2391da177e4SLinus Torvalds 	 * Configure IPMI address for local access
2401da177e4SLinus Torvalds 	 */
2411da177e4SLinus Torvalds 	smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
2421da177e4SLinus Torvalds 	smi_addr.channel = IPMI_BMC_CHANNEL;
2431da177e4SLinus Torvalds 	smi_addr.lun = 0;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	/*
2461da177e4SLinus Torvalds 	 * Use get address info to check and see if we are ATCA
2471da177e4SLinus Torvalds 	 */
2481da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_ATCA;
2491da177e4SLinus Torvalds 	send_msg.cmd = IPMI_ATCA_GET_ADDR_INFO_CMD;
2501da177e4SLinus Torvalds 	data[0] = IPMI_PICMG_ID;
2511da177e4SLinus Torvalds 	send_msg.data = data;
2521da177e4SLinus Torvalds 	send_msg.data_len = sizeof(data);
2531da177e4SLinus Torvalds 	rv = ipmi_request_wait_for_response(user,
2541da177e4SLinus Torvalds 					    (struct ipmi_addr *) &smi_addr,
2551da177e4SLinus Torvalds 					    &send_msg);
2567d8ba966SCorey Minyard 
25736c7dc44SCorey Minyard 	printk(KERN_INFO PFX "ATCA Detect mfg 0x%X prod 0x%X\n",
25836c7dc44SCorey Minyard 	       mfg_id, prod_id);
2597d8ba966SCorey Minyard 	if ((mfg_id == IPMI_MOTOROLA_MANUFACTURER_ID)
2607d8ba966SCorey Minyard 	    && (prod_id == IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID)) {
26136c7dc44SCorey Minyard 		printk(KERN_INFO PFX
26236c7dc44SCorey Minyard 		       "Installing Pigeon Point Systems Poweroff Hook\n");
2637d8ba966SCorey Minyard 		atca_oem_poweroff_hook = pps_poweroff_atca;
2647d8ba966SCorey Minyard 	}
2651da177e4SLinus Torvalds 	return !rv;
2661da177e4SLinus Torvalds }
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds static void ipmi_poweroff_atca(ipmi_user_t user)
2691da177e4SLinus Torvalds {
2701da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
2711da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
2721da177e4SLinus Torvalds 	int                               rv;
2731da177e4SLinus Torvalds 	unsigned char                     data[4];
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds 	/*
2761da177e4SLinus Torvalds 	 * Configure IPMI address for local access
2771da177e4SLinus Torvalds 	 */
2781da177e4SLinus Torvalds 	smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
2791da177e4SLinus Torvalds 	smi_addr.channel = IPMI_BMC_CHANNEL;
2801da177e4SLinus Torvalds 	smi_addr.lun = 0;
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds 	printk(KERN_INFO PFX "Powering down via ATCA power command\n");
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	/*
2851da177e4SLinus Torvalds 	 * Power down
2861da177e4SLinus Torvalds 	 */
2871da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_ATCA;
2881da177e4SLinus Torvalds 	send_msg.cmd = IPMI_ATCA_SET_POWER_CMD;
2891da177e4SLinus Torvalds 	data[0] = IPMI_PICMG_ID;
2901da177e4SLinus Torvalds 	data[1] = 0; /* FRU id */
2911da177e4SLinus Torvalds 	data[2] = 0; /* Power Level */
2921da177e4SLinus Torvalds 	data[3] = 0; /* Don't change saved presets */
2931da177e4SLinus Torvalds 	send_msg.data = data;
2941da177e4SLinus Torvalds 	send_msg.data_len = sizeof(data);
2951da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
2961da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
2971da177e4SLinus Torvalds 				     &send_msg);
29836c7dc44SCorey Minyard 	/*
29936c7dc44SCorey Minyard 	 * At this point, the system may be shutting down, and most
30036c7dc44SCorey Minyard 	 * serial drivers (if used) will have interrupts turned off
30136c7dc44SCorey Minyard 	 * it may be better to ignore IPMI_UNKNOWN_ERR_COMPLETION_CODE
30236c7dc44SCorey Minyard 	 * return code
30336c7dc44SCorey Minyard 	 */
3047d8ba966SCorey Minyard 	if (rv && rv != IPMI_UNKNOWN_ERR_COMPLETION_CODE) {
3051da177e4SLinus Torvalds 		printk(KERN_ERR PFX "Unable to send ATCA powerdown message,"
3061da177e4SLinus Torvalds 		       " IPMI error 0x%x\n", rv);
3071da177e4SLinus Torvalds 		goto out;
3081da177e4SLinus Torvalds 	}
3091da177e4SLinus Torvalds 
3107d8ba966SCorey Minyard 	if (atca_oem_poweroff_hook)
311adf535eeSAdrian Bunk 		atca_oem_poweroff_hook(user);
3121da177e4SLinus Torvalds  out:
3131da177e4SLinus Torvalds 	return;
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds /*
3171da177e4SLinus Torvalds  * CPI1 Support
3181da177e4SLinus Torvalds  */
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds #define IPMI_NETFN_OEM_1				0xf8
3211da177e4SLinus Torvalds #define OEM_GRP_CMD_SET_RESET_STATE		0x84
3221da177e4SLinus Torvalds #define OEM_GRP_CMD_SET_POWER_STATE		0x82
3231da177e4SLinus Torvalds #define IPMI_NETFN_OEM_8				0xf8
3241da177e4SLinus Torvalds #define OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL	0x80
3251da177e4SLinus Torvalds #define OEM_GRP_CMD_GET_SLOT_GA			0xa3
3261da177e4SLinus Torvalds #define IPMI_NETFN_SENSOR_EVT			0x10
3271da177e4SLinus Torvalds #define IPMI_CMD_GET_EVENT_RECEIVER		0x01
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds #define IPMI_CPI1_PRODUCT_ID		0x000157
3301da177e4SLinus Torvalds #define IPMI_CPI1_MANUFACTURER_ID	0x0108
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds static int ipmi_cpi1_detect(ipmi_user_t user)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds 	return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID)
3351da177e4SLinus Torvalds 		&& (prod_id == IPMI_CPI1_PRODUCT_ID));
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds static void ipmi_poweroff_cpi1(ipmi_user_t user)
3391da177e4SLinus Torvalds {
3401da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
3411da177e4SLinus Torvalds 	struct ipmi_ipmb_addr             ipmb_addr;
3421da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
3431da177e4SLinus Torvalds 	int                               rv;
3441da177e4SLinus Torvalds 	unsigned char                     data[1];
3451da177e4SLinus Torvalds 	int                               slot;
3461da177e4SLinus Torvalds 	unsigned char                     hotswap_ipmb;
3471da177e4SLinus Torvalds 	unsigned char                     aer_addr;
3481da177e4SLinus Torvalds 	unsigned char                     aer_lun;
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 	/*
3511da177e4SLinus Torvalds 	 * Configure IPMI address for local access
3521da177e4SLinus Torvalds 	 */
3531da177e4SLinus Torvalds 	smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
3541da177e4SLinus Torvalds 	smi_addr.channel = IPMI_BMC_CHANNEL;
3551da177e4SLinus Torvalds 	smi_addr.lun = 0;
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 	printk(KERN_INFO PFX "Powering down via CPI1 power command\n");
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 	/*
3601da177e4SLinus Torvalds 	 * Get IPMI ipmb address
3611da177e4SLinus Torvalds 	 */
3621da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_OEM_8 >> 2;
3631da177e4SLinus Torvalds 	send_msg.cmd = OEM_GRP_CMD_GET_SLOT_GA;
3641da177e4SLinus Torvalds 	send_msg.data = NULL;
3651da177e4SLinus Torvalds 	send_msg.data_len = 0;
3661da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
3671da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
3681da177e4SLinus Torvalds 				     &send_msg);
3691da177e4SLinus Torvalds 	if (rv)
3701da177e4SLinus Torvalds 		goto out;
3711da177e4SLinus Torvalds 	slot = halt_recv_msg.msg.data[1];
3721da177e4SLinus Torvalds 	hotswap_ipmb = (slot > 9) ? (0xb0 + 2 * slot) : (0xae + 2 * slot);
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 	/*
3751da177e4SLinus Torvalds 	 * Get active event receiver
3761da177e4SLinus Torvalds 	 */
3771da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_SENSOR_EVT >> 2;
3781da177e4SLinus Torvalds 	send_msg.cmd = IPMI_CMD_GET_EVENT_RECEIVER;
3791da177e4SLinus Torvalds 	send_msg.data = NULL;
3801da177e4SLinus Torvalds 	send_msg.data_len = 0;
3811da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
3821da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
3831da177e4SLinus Torvalds 				     &send_msg);
3841da177e4SLinus Torvalds 	if (rv)
3851da177e4SLinus Torvalds 		goto out;
3861da177e4SLinus Torvalds 	aer_addr = halt_recv_msg.msg.data[1];
3871da177e4SLinus Torvalds 	aer_lun = halt_recv_msg.msg.data[2];
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	/*
3901da177e4SLinus Torvalds 	 * Setup IPMB address target instead of local target
3911da177e4SLinus Torvalds 	 */
3921da177e4SLinus Torvalds 	ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE;
3931da177e4SLinus Torvalds 	ipmb_addr.channel = 0;
3941da177e4SLinus Torvalds 	ipmb_addr.slave_addr = aer_addr;
3951da177e4SLinus Torvalds 	ipmb_addr.lun = aer_lun;
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds 	/*
3981da177e4SLinus Torvalds 	 * Send request hotswap control to remove blade from dpv
3991da177e4SLinus Torvalds 	 */
4001da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_OEM_8 >> 2;
4011da177e4SLinus Torvalds 	send_msg.cmd = OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL;
4021da177e4SLinus Torvalds 	send_msg.data = &hotswap_ipmb;
4031da177e4SLinus Torvalds 	send_msg.data_len = 1;
4041da177e4SLinus Torvalds 	ipmi_request_in_rc_mode(user,
4051da177e4SLinus Torvalds 				(struct ipmi_addr *) &ipmb_addr,
4061da177e4SLinus Torvalds 				&send_msg);
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	/*
4091da177e4SLinus Torvalds 	 * Set reset asserted
4101da177e4SLinus Torvalds 	 */
4111da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_OEM_1 >> 2;
4121da177e4SLinus Torvalds 	send_msg.cmd = OEM_GRP_CMD_SET_RESET_STATE;
4131da177e4SLinus Torvalds 	send_msg.data = data;
4141da177e4SLinus Torvalds 	data[0] = 1; /* Reset asserted state */
4151da177e4SLinus Torvalds 	send_msg.data_len = 1;
4161da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
4171da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
4181da177e4SLinus Torvalds 				     &send_msg);
4191da177e4SLinus Torvalds 	if (rv)
4201da177e4SLinus Torvalds 		goto out;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	/*
4231da177e4SLinus Torvalds 	 * Power down
4241da177e4SLinus Torvalds 	 */
4251da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_OEM_1 >> 2;
4261da177e4SLinus Torvalds 	send_msg.cmd = OEM_GRP_CMD_SET_POWER_STATE;
4271da177e4SLinus Torvalds 	send_msg.data = data;
4281da177e4SLinus Torvalds 	data[0] = 1; /* Power down state */
4291da177e4SLinus Torvalds 	send_msg.data_len = 1;
4301da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
4311da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
4321da177e4SLinus Torvalds 				     &send_msg);
4331da177e4SLinus Torvalds 	if (rv)
4341da177e4SLinus Torvalds 		goto out;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds  out:
4371da177e4SLinus Torvalds 	return;
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds /*
441168524d6SCorey Minyard  * ipmi_dell_chassis_detect()
442168524d6SCorey Minyard  * Dell systems with IPMI < 1.5 don't set the chassis capability bit
443168524d6SCorey Minyard  * but they can handle a chassis poweroff or powercycle command.
444168524d6SCorey Minyard  */
445168524d6SCorey Minyard 
446168524d6SCorey Minyard #define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00}
447168524d6SCorey Minyard static int ipmi_dell_chassis_detect(ipmi_user_t user)
448168524d6SCorey Minyard {
449168524d6SCorey Minyard 	const char ipmi_version_major = ipmi_version & 0xF;
450168524d6SCorey Minyard 	const char ipmi_version_minor = (ipmi_version >> 4) & 0xF;
451168524d6SCorey Minyard 	const char mfr[3] = DELL_IANA_MFR_ID;
452168524d6SCorey Minyard 	if (!memcmp(mfr, &mfg_id, sizeof(mfr)) &&
453168524d6SCorey Minyard 	    ipmi_version_major <= 1 &&
454168524d6SCorey Minyard 	    ipmi_version_minor < 5)
455168524d6SCorey Minyard 		return 1;
456168524d6SCorey Minyard 	return 0;
457168524d6SCorey Minyard }
458168524d6SCorey Minyard 
459168524d6SCorey Minyard /*
4601da177e4SLinus Torvalds  * Standard chassis support
4611da177e4SLinus Torvalds  */
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds #define IPMI_NETFN_CHASSIS_REQUEST	0
4641da177e4SLinus Torvalds #define IPMI_CHASSIS_CONTROL_CMD	0x02
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds static int ipmi_chassis_detect(ipmi_user_t user)
4671da177e4SLinus Torvalds {
4681da177e4SLinus Torvalds 	/* Chassis support, use it. */
4691da177e4SLinus Torvalds 	return (capabilities & 0x80);
4701da177e4SLinus Torvalds }
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds static void ipmi_poweroff_chassis(ipmi_user_t user)
4731da177e4SLinus Torvalds {
4741da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
4751da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
4761da177e4SLinus Torvalds 	int                               rv;
4771da177e4SLinus Torvalds 	unsigned char                     data[1];
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	/*
4801da177e4SLinus Torvalds 	 * Configure IPMI address for local access
4811da177e4SLinus Torvalds 	 */
4821da177e4SLinus Torvalds 	smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
4831da177e4SLinus Torvalds 	smi_addr.channel = IPMI_BMC_CHANNEL;
4841da177e4SLinus Torvalds 	smi_addr.lun = 0;
4851da177e4SLinus Torvalds 
4863b625943SCorey Minyard  powercyclefailed:
4873b625943SCorey Minyard 	printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n",
4888c702e16SCorey Minyard 		(poweroff_powercycle ? "cycle" : "down"));
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	/*
4911da177e4SLinus Torvalds 	 * Power down
4921da177e4SLinus Torvalds 	 */
4931da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST;
4941da177e4SLinus Torvalds 	send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
4958c702e16SCorey Minyard 	if (poweroff_powercycle)
4968c702e16SCorey Minyard 		data[0] = IPMI_CHASSIS_POWER_CYCLE;
4978c702e16SCorey Minyard 	else
4988c702e16SCorey Minyard 		data[0] = IPMI_CHASSIS_POWER_DOWN;
4991da177e4SLinus Torvalds 	send_msg.data = data;
5001da177e4SLinus Torvalds 	send_msg.data_len = sizeof(data);
5011da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
5021da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
5031da177e4SLinus Torvalds 				     &send_msg);
5041da177e4SLinus Torvalds 	if (rv) {
5058c702e16SCorey Minyard 		if (poweroff_powercycle) {
5063b625943SCorey Minyard 			/* power cycle failed, default to power down */
5073b625943SCorey Minyard 			printk(KERN_ERR PFX "Unable to send chassis power " \
5083b625943SCorey Minyard 			       "cycle message, IPMI error 0x%x\n", rv);
5098c702e16SCorey Minyard 			poweroff_powercycle = 0;
5103b625943SCorey Minyard 			goto powercyclefailed;
5118c702e16SCorey Minyard 		}
5123b625943SCorey Minyard 
5133b625943SCorey Minyard 		printk(KERN_ERR PFX "Unable to send chassis power " \
5143b625943SCorey Minyard 		       "down message, IPMI error 0x%x\n", rv);
5153b625943SCorey Minyard 	}
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds 
5191da177e4SLinus Torvalds /* Table of possible power off functions. */
5201da177e4SLinus Torvalds struct poweroff_function {
5211da177e4SLinus Torvalds 	char *platform_type;
5221da177e4SLinus Torvalds 	int  (*detect)(ipmi_user_t user);
5231da177e4SLinus Torvalds 	void (*poweroff_func)(ipmi_user_t user);
5241da177e4SLinus Torvalds };
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds static struct poweroff_function poweroff_functions[] = {
5271da177e4SLinus Torvalds 	{ .platform_type	= "ATCA",
5281da177e4SLinus Torvalds 	  .detect		= ipmi_atca_detect,
5291da177e4SLinus Torvalds 	  .poweroff_func	= ipmi_poweroff_atca },
5301da177e4SLinus Torvalds 	{ .platform_type	= "CPI1",
5311da177e4SLinus Torvalds 	  .detect		= ipmi_cpi1_detect,
5321da177e4SLinus Torvalds 	  .poweroff_func	= ipmi_poweroff_cpi1 },
533168524d6SCorey Minyard 	{ .platform_type	= "chassis",
534168524d6SCorey Minyard 	  .detect		= ipmi_dell_chassis_detect,
535168524d6SCorey Minyard 	  .poweroff_func	= ipmi_poweroff_chassis },
5361da177e4SLinus Torvalds 	/* Chassis should generally be last, other things should override
5371da177e4SLinus Torvalds 	   it. */
5381da177e4SLinus Torvalds 	{ .platform_type	= "chassis",
5391da177e4SLinus Torvalds 	  .detect		= ipmi_chassis_detect,
5401da177e4SLinus Torvalds 	  .poweroff_func	= ipmi_poweroff_chassis },
5411da177e4SLinus Torvalds };
5421da177e4SLinus Torvalds #define NUM_PO_FUNCS (sizeof(poweroff_functions) \
5431da177e4SLinus Torvalds 		      / sizeof(struct poweroff_function))
5441da177e4SLinus Torvalds 
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds /* Called on a powerdown request. */
5471da177e4SLinus Torvalds static void ipmi_poweroff_function(void)
5481da177e4SLinus Torvalds {
5491da177e4SLinus Torvalds 	if (!ready)
5501da177e4SLinus Torvalds 		return;
5511da177e4SLinus Torvalds 
5521da177e4SLinus Torvalds 	/* Use run-to-completion mode, since interrupts may be off. */
5531da177e4SLinus Torvalds 	specific_poweroff_func(ipmi_user);
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds /* Wait for an IPMI interface to be installed, the first one installed
5571da177e4SLinus Torvalds    will be grabbed by this code and used to perform the powerdown. */
55850c812b2SCorey Minyard static void ipmi_po_new_smi(int if_num, struct device *device)
5591da177e4SLinus Torvalds {
5601da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
5611da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
5621da177e4SLinus Torvalds 	int                               rv;
5631da177e4SLinus Torvalds 	int                               i;
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	if (ready)
5661da177e4SLinus Torvalds 		return;
5671da177e4SLinus Torvalds 
568b2c03941SCorey Minyard 	if ((ifnum_to_use >= 0) && (ifnum_to_use != if_num))
569b2c03941SCorey Minyard 		return;
570b2c03941SCorey Minyard 
5713b625943SCorey Minyard 	rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL,
5723b625943SCorey Minyard 			      &ipmi_user);
5731da177e4SLinus Torvalds 	if (rv) {
5741da177e4SLinus Torvalds 		printk(KERN_ERR PFX "could not create IPMI user, error %d\n",
5751da177e4SLinus Torvalds 		       rv);
5761da177e4SLinus Torvalds 		return;
5771da177e4SLinus Torvalds 	}
5781da177e4SLinus Torvalds 
579b2c03941SCorey Minyard 	ipmi_ifnum = if_num;
580b2c03941SCorey Minyard 
5811da177e4SLinus Torvalds 	/*
5821da177e4SLinus Torvalds 	 * Do a get device ide and store some results, since this is
5831da177e4SLinus Torvalds 	 * used by several functions.
5841da177e4SLinus Torvalds 	 */
5851da177e4SLinus Torvalds 	smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
5861da177e4SLinus Torvalds 	smi_addr.channel = IPMI_BMC_CHANNEL;
5871da177e4SLinus Torvalds 	smi_addr.lun = 0;
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_APP_REQUEST;
5901da177e4SLinus Torvalds 	send_msg.cmd = IPMI_GET_DEVICE_ID_CMD;
5911da177e4SLinus Torvalds 	send_msg.data = NULL;
5921da177e4SLinus Torvalds 	send_msg.data_len = 0;
5931da177e4SLinus Torvalds 	rv = ipmi_request_wait_for_response(ipmi_user,
5941da177e4SLinus Torvalds 					    (struct ipmi_addr *) &smi_addr,
5951da177e4SLinus Torvalds 					    &send_msg);
5961da177e4SLinus Torvalds 	if (rv) {
5971da177e4SLinus Torvalds 		printk(KERN_ERR PFX "Unable to send IPMI get device id info,"
5981da177e4SLinus Torvalds 		       " IPMI error 0x%x\n", rv);
5991da177e4SLinus Torvalds 		goto out_err;
6001da177e4SLinus Torvalds 	}
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 	if (halt_recv_msg.msg.data_len < 12) {
6031da177e4SLinus Torvalds 		printk(KERN_ERR PFX "(chassis) IPMI get device id info too,"
6041da177e4SLinus Torvalds 		       " short, was %d bytes, needed %d bytes\n",
6051da177e4SLinus Torvalds 		       halt_recv_msg.msg.data_len, 12);
6061da177e4SLinus Torvalds 		goto out_err;
6071da177e4SLinus Torvalds 	}
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 	mfg_id = (halt_recv_msg.msg.data[7]
6101da177e4SLinus Torvalds 		  | (halt_recv_msg.msg.data[8] << 8)
6111da177e4SLinus Torvalds 		  | (halt_recv_msg.msg.data[9] << 16));
6121da177e4SLinus Torvalds 	prod_id = (halt_recv_msg.msg.data[10]
6131da177e4SLinus Torvalds 		   | (halt_recv_msg.msg.data[11] << 8));
6141da177e4SLinus Torvalds 	capabilities = halt_recv_msg.msg.data[6];
615168524d6SCorey Minyard 	ipmi_version = halt_recv_msg.msg.data[5];
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds 
6181da177e4SLinus Torvalds 	/* Scan for a poweroff method */
6191da177e4SLinus Torvalds 	for (i = 0; i < NUM_PO_FUNCS; i++) {
6201da177e4SLinus Torvalds 		if (poweroff_functions[i].detect(ipmi_user))
6211da177e4SLinus Torvalds 			goto found;
6221da177e4SLinus Torvalds 	}
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds  out_err:
6251da177e4SLinus Torvalds 	printk(KERN_ERR PFX "Unable to find a poweroff function that"
6261da177e4SLinus Torvalds 	       " will work, giving up\n");
6271da177e4SLinus Torvalds 	ipmi_destroy_user(ipmi_user);
6281da177e4SLinus Torvalds 	return;
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds  found:
6311da177e4SLinus Torvalds 	printk(KERN_INFO PFX "Found a %s style poweroff function\n",
6321da177e4SLinus Torvalds 	       poweroff_functions[i].platform_type);
6331da177e4SLinus Torvalds 	specific_poweroff_func = poweroff_functions[i].poweroff_func;
6341da177e4SLinus Torvalds 	old_poweroff_func = pm_power_off;
6351da177e4SLinus Torvalds 	pm_power_off = ipmi_poweroff_function;
6361da177e4SLinus Torvalds 	ready = 1;
6371da177e4SLinus Torvalds }
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds static void ipmi_po_smi_gone(int if_num)
6401da177e4SLinus Torvalds {
641b2c03941SCorey Minyard 	if (!ready)
642b2c03941SCorey Minyard 		return;
643b2c03941SCorey Minyard 
644b2c03941SCorey Minyard 	if (ipmi_ifnum != if_num)
645b2c03941SCorey Minyard 		return;
646b2c03941SCorey Minyard 
647b2c03941SCorey Minyard 	ready = 0;
648b2c03941SCorey Minyard 	ipmi_destroy_user(ipmi_user);
649b2c03941SCorey Minyard 	pm_power_off = old_poweroff_func;
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds 
65236c7dc44SCorey Minyard static struct ipmi_smi_watcher smi_watcher = {
6531da177e4SLinus Torvalds 	.owner    = THIS_MODULE,
6541da177e4SLinus Torvalds 	.new_smi  = ipmi_po_new_smi,
6551da177e4SLinus Torvalds 	.smi_gone = ipmi_po_smi_gone
6561da177e4SLinus Torvalds };
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 
6593b625943SCorey Minyard #ifdef CONFIG_PROC_FS
6608c702e16SCorey Minyard #include <linux/sysctl.h>
6613b625943SCorey Minyard 
6628c702e16SCorey Minyard static ctl_table ipmi_table[] = {
6638c702e16SCorey Minyard 	{ .ctl_name	= DEV_IPMI_POWEROFF_POWERCYCLE,
6648c702e16SCorey Minyard 	  .procname	= "poweroff_powercycle",
6658c702e16SCorey Minyard 	  .data		= &poweroff_powercycle,
6668c702e16SCorey Minyard 	  .maxlen	= sizeof(poweroff_powercycle),
6678c702e16SCorey Minyard 	  .mode		= 0644,
6688c702e16SCorey Minyard 	  .proc_handler	= &proc_dointvec },
6698c702e16SCorey Minyard 	{ }
6708c702e16SCorey Minyard };
6713b625943SCorey Minyard 
6728c702e16SCorey Minyard static ctl_table ipmi_dir_table[] = {
6738c702e16SCorey Minyard 	{ .ctl_name	= DEV_IPMI,
6748c702e16SCorey Minyard 	  .procname	= "ipmi",
6758c702e16SCorey Minyard 	  .mode		= 0555,
6768c702e16SCorey Minyard 	  .child	= ipmi_table },
6778c702e16SCorey Minyard 	{ }
6788c702e16SCorey Minyard };
6793b625943SCorey Minyard 
6808c702e16SCorey Minyard static ctl_table ipmi_root_table[] = {
6818c702e16SCorey Minyard 	{ .ctl_name	= CTL_DEV,
6828c702e16SCorey Minyard 	  .procname	= "dev",
6838c702e16SCorey Minyard 	  .mode		= 0555,
6848c702e16SCorey Minyard 	  .child	= ipmi_dir_table },
6858c702e16SCorey Minyard 	{ }
6868c702e16SCorey Minyard };
6873b625943SCorey Minyard 
6888c702e16SCorey Minyard static struct ctl_table_header *ipmi_table_header;
6893b625943SCorey Minyard #endif /* CONFIG_PROC_FS */
6903b625943SCorey Minyard 
6911da177e4SLinus Torvalds /*
6921da177e4SLinus Torvalds  * Startup and shutdown functions.
6931da177e4SLinus Torvalds  */
6941da177e4SLinus Torvalds static int ipmi_poweroff_init(void)
6951da177e4SLinus Torvalds {
6961da177e4SLinus Torvalds 	int rv;
6971da177e4SLinus Torvalds 
698e5fa4998SDan Aloni 	printk(KERN_INFO "Copyright (C) 2004 MontaVista Software -"
6991fdd75bdSCorey Minyard 	       " IPMI Powerdown via sys_reboot.\n");
7001da177e4SLinus Torvalds 
7018c702e16SCorey Minyard 	if (poweroff_powercycle)
7023b625943SCorey Minyard 		printk(KERN_INFO PFX "Power cycle is enabled.\n");
7031da177e4SLinus Torvalds 
7048c702e16SCorey Minyard #ifdef CONFIG_PROC_FS
7050b4d4147SEric W. Biederman 	ipmi_table_header = register_sysctl_table(ipmi_root_table);
7068c702e16SCorey Minyard 	if (!ipmi_table_header) {
7078c702e16SCorey Minyard 		printk(KERN_ERR PFX "Unable to register powercycle sysctl\n");
7088c702e16SCorey Minyard 		rv = -ENOMEM;
7098c702e16SCorey Minyard 		goto out_err;
7103b625943SCorey Minyard 	}
7118c702e16SCorey Minyard #endif
7123b625943SCorey Minyard 
7133b625943SCorey Minyard 	rv = ipmi_smi_watcher_register(&smi_watcher);
714be4f1bb2SAdrian Bunk 
715be4f1bb2SAdrian Bunk #ifdef CONFIG_PROC_FS
7163b625943SCorey Minyard 	if (rv) {
7178c702e16SCorey Minyard 		unregister_sysctl_table(ipmi_table_header);
7183b625943SCorey Minyard 		printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv);
7193b625943SCorey Minyard 		goto out_err;
7203b625943SCorey Minyard 	}
7213b625943SCorey Minyard 
7223b625943SCorey Minyard  out_err:
7231aa16eeaSRandy Dunlap #endif
7241da177e4SLinus Torvalds 	return rv;
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds #ifdef MODULE
7281da177e4SLinus Torvalds static __exit void ipmi_poweroff_cleanup(void)
7291da177e4SLinus Torvalds {
7301da177e4SLinus Torvalds 	int rv;
7311da177e4SLinus Torvalds 
7323b625943SCorey Minyard #ifdef CONFIG_PROC_FS
7338c702e16SCorey Minyard 	unregister_sysctl_table(ipmi_table_header);
7343b625943SCorey Minyard #endif
7353b625943SCorey Minyard 
7361da177e4SLinus Torvalds 	ipmi_smi_watcher_unregister(&smi_watcher);
7371da177e4SLinus Torvalds 
7381da177e4SLinus Torvalds 	if (ready) {
7391da177e4SLinus Torvalds 		rv = ipmi_destroy_user(ipmi_user);
7401da177e4SLinus Torvalds 		if (rv)
7411da177e4SLinus Torvalds 			printk(KERN_ERR PFX "could not cleanup the IPMI"
7421da177e4SLinus Torvalds 			       " user: 0x%x\n", rv);
7431da177e4SLinus Torvalds 		pm_power_off = old_poweroff_func;
7441da177e4SLinus Torvalds 	}
7451da177e4SLinus Torvalds }
7461da177e4SLinus Torvalds module_exit(ipmi_poweroff_cleanup);
7471da177e4SLinus Torvalds #endif
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds module_init(ipmi_poweroff_init);
7501da177e4SLinus Torvalds MODULE_LICENSE("GPL");
7511fdd75bdSCorey Minyard MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
7521fdd75bdSCorey Minyard MODULE_DESCRIPTION("IPMI Poweroff extension to sys_reboot");
753