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 <asm/semaphore.h>
351da177e4SLinus Torvalds #include <linux/kdev_t.h>
361da177e4SLinus Torvalds #include <linux/module.h>
373b625943SCorey Minyard #include <linux/moduleparam.h>
383b625943SCorey Minyard #include <linux/proc_fs.h>
391da177e4SLinus Torvalds #include <linux/string.h>
401da177e4SLinus Torvalds #include <linux/ipmi.h>
411da177e4SLinus Torvalds #include <linux/ipmi_smi.h>
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #define PFX "IPMI poweroff: "
441da177e4SLinus Torvalds #define IPMI_POWEROFF_VERSION	"v33"
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds /* Where to we insert our poweroff function? */
471da177e4SLinus Torvalds extern void (*pm_power_off)(void);
481da177e4SLinus Torvalds 
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 */
553b625943SCorey Minyard static int poweroff_control = IPMI_CHASSIS_POWER_DOWN;
563b625943SCorey Minyard 
573b625943SCorey Minyard /* parameter definition to allow user to flag power cycle */
583b625943SCorey Minyard module_param(poweroff_control, int, IPMI_CHASSIS_POWER_DOWN);
593b625943SCorey Minyard MODULE_PARM_DESC(poweroff_control, " Set to 2 to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down.");
603b625943SCorey Minyard 
611da177e4SLinus Torvalds /* Stuff from the get device id command. */
621da177e4SLinus Torvalds static unsigned int mfg_id;
631da177e4SLinus Torvalds static unsigned int prod_id;
641da177e4SLinus Torvalds static unsigned char capabilities;
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds /* We use our own messages for this operation, we don't let the system
671da177e4SLinus Torvalds    allocate them, since we may be in a panic situation.  The whole
681da177e4SLinus Torvalds    thing is single-threaded, anyway, so multiple messages are not
691da177e4SLinus Torvalds    required. */
701da177e4SLinus Torvalds static void dummy_smi_free(struct ipmi_smi_msg *msg)
711da177e4SLinus Torvalds {
721da177e4SLinus Torvalds }
731da177e4SLinus Torvalds static void dummy_recv_free(struct ipmi_recv_msg *msg)
741da177e4SLinus Torvalds {
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds static struct ipmi_smi_msg halt_smi_msg =
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds 	.done = dummy_smi_free
791da177e4SLinus Torvalds };
801da177e4SLinus Torvalds static struct ipmi_recv_msg halt_recv_msg =
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds 	.done = dummy_recv_free
831da177e4SLinus Torvalds };
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /*
871da177e4SLinus Torvalds  * Code to send a message and wait for the reponse.
881da177e4SLinus Torvalds  */
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data)
911da177e4SLinus Torvalds {
921da177e4SLinus Torvalds 	struct semaphore *sem = recv_msg->user_msg_data;
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	if (sem)
951da177e4SLinus Torvalds 		up(sem);
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds static struct ipmi_user_hndl ipmi_poweroff_handler =
991da177e4SLinus Torvalds {
1001da177e4SLinus Torvalds 	.ipmi_recv_hndl = receive_handler
1011da177e4SLinus Torvalds };
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds static int ipmi_request_wait_for_response(ipmi_user_t            user,
1051da177e4SLinus Torvalds 					  struct ipmi_addr       *addr,
1061da177e4SLinus Torvalds 					  struct kernel_ipmi_msg *send_msg)
1071da177e4SLinus Torvalds {
1081da177e4SLinus Torvalds 	int              rv;
1091da177e4SLinus Torvalds 	struct semaphore sem;
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	sema_init (&sem, 0);
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 	rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem,
1141da177e4SLinus Torvalds 				      &halt_smi_msg, &halt_recv_msg, 0);
1151da177e4SLinus Torvalds 	if (rv)
1161da177e4SLinus Torvalds 		return rv;
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	down (&sem);
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	return halt_recv_msg.msg.data[0];
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds /* We are in run-to-completion mode, no semaphore is desired. */
1241da177e4SLinus Torvalds static int ipmi_request_in_rc_mode(ipmi_user_t            user,
1251da177e4SLinus Torvalds 				   struct ipmi_addr       *addr,
1261da177e4SLinus Torvalds 				   struct kernel_ipmi_msg *send_msg)
1271da177e4SLinus Torvalds {
1281da177e4SLinus Torvalds 	int              rv;
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL,
1311da177e4SLinus Torvalds 				      &halt_smi_msg, &halt_recv_msg, 0);
1321da177e4SLinus Torvalds 	if (rv)
1331da177e4SLinus Torvalds 		return rv;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	return halt_recv_msg.msg.data[0];
1361da177e4SLinus Torvalds }
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds /*
1391da177e4SLinus Torvalds  * ATCA Support
1401da177e4SLinus Torvalds  */
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds #define IPMI_NETFN_ATCA			0x2c
1431da177e4SLinus Torvalds #define IPMI_ATCA_SET_POWER_CMD		0x11
1441da177e4SLinus Torvalds #define IPMI_ATCA_GET_ADDR_INFO_CMD	0x01
1451da177e4SLinus Torvalds #define IPMI_PICMG_ID			0
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds static int ipmi_atca_detect (ipmi_user_t user)
1481da177e4SLinus Torvalds {
1491da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
1501da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
1511da177e4SLinus Torvalds 	int                               rv;
1521da177e4SLinus Torvalds 	unsigned char                     data[1];
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds         /*
1551da177e4SLinus Torvalds          * Configure IPMI address for local access
1561da177e4SLinus Torvalds          */
1571da177e4SLinus Torvalds         smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
1581da177e4SLinus Torvalds         smi_addr.channel = IPMI_BMC_CHANNEL;
1591da177e4SLinus Torvalds         smi_addr.lun = 0;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	/*
1621da177e4SLinus Torvalds 	 * Use get address info to check and see if we are ATCA
1631da177e4SLinus Torvalds 	 */
1641da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_ATCA;
1651da177e4SLinus Torvalds 	send_msg.cmd = IPMI_ATCA_GET_ADDR_INFO_CMD;
1661da177e4SLinus Torvalds 	data[0] = IPMI_PICMG_ID;
1671da177e4SLinus Torvalds 	send_msg.data = data;
1681da177e4SLinus Torvalds 	send_msg.data_len = sizeof(data);
1691da177e4SLinus Torvalds 	rv = ipmi_request_wait_for_response(user,
1701da177e4SLinus Torvalds 					    (struct ipmi_addr *) &smi_addr,
1711da177e4SLinus Torvalds 					    &send_msg);
1721da177e4SLinus Torvalds 	return !rv;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds static void ipmi_poweroff_atca (ipmi_user_t user)
1761da177e4SLinus Torvalds {
1771da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
1781da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
1791da177e4SLinus Torvalds 	int                               rv;
1801da177e4SLinus Torvalds 	unsigned char                     data[4];
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds         /*
1831da177e4SLinus Torvalds          * Configure IPMI address for local access
1841da177e4SLinus Torvalds          */
1851da177e4SLinus Torvalds         smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
1861da177e4SLinus Torvalds         smi_addr.channel = IPMI_BMC_CHANNEL;
1871da177e4SLinus Torvalds         smi_addr.lun = 0;
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	printk(KERN_INFO PFX "Powering down via ATCA power command\n");
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds 	/*
1921da177e4SLinus Torvalds 	 * Power down
1931da177e4SLinus Torvalds 	 */
1941da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_ATCA;
1951da177e4SLinus Torvalds 	send_msg.cmd = IPMI_ATCA_SET_POWER_CMD;
1961da177e4SLinus Torvalds 	data[0] = IPMI_PICMG_ID;
1971da177e4SLinus Torvalds 	data[1] = 0; /* FRU id */
1981da177e4SLinus Torvalds 	data[2] = 0; /* Power Level */
1991da177e4SLinus Torvalds 	data[3] = 0; /* Don't change saved presets */
2001da177e4SLinus Torvalds 	send_msg.data = data;
2011da177e4SLinus Torvalds 	send_msg.data_len = sizeof (data);
2021da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
2031da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
2041da177e4SLinus Torvalds 				     &send_msg);
2051da177e4SLinus Torvalds 	if (rv) {
2061da177e4SLinus Torvalds 		printk(KERN_ERR PFX "Unable to send ATCA powerdown message,"
2071da177e4SLinus Torvalds 		       " IPMI error 0x%x\n", rv);
2081da177e4SLinus Torvalds 		goto out;
2091da177e4SLinus Torvalds 	}
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds  out:
2121da177e4SLinus Torvalds 	return;
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds /*
2161da177e4SLinus Torvalds  * CPI1 Support
2171da177e4SLinus Torvalds  */
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds #define IPMI_NETFN_OEM_1				0xf8
2201da177e4SLinus Torvalds #define OEM_GRP_CMD_SET_RESET_STATE		0x84
2211da177e4SLinus Torvalds #define OEM_GRP_CMD_SET_POWER_STATE		0x82
2221da177e4SLinus Torvalds #define IPMI_NETFN_OEM_8				0xf8
2231da177e4SLinus Torvalds #define OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL	0x80
2241da177e4SLinus Torvalds #define OEM_GRP_CMD_GET_SLOT_GA			0xa3
2251da177e4SLinus Torvalds #define IPMI_NETFN_SENSOR_EVT			0x10
2261da177e4SLinus Torvalds #define IPMI_CMD_GET_EVENT_RECEIVER		0x01
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds #define IPMI_CPI1_PRODUCT_ID		0x000157
2291da177e4SLinus Torvalds #define IPMI_CPI1_MANUFACTURER_ID	0x0108
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds static int ipmi_cpi1_detect (ipmi_user_t user)
2321da177e4SLinus Torvalds {
2331da177e4SLinus Torvalds 	return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID)
2341da177e4SLinus Torvalds 		&& (prod_id == IPMI_CPI1_PRODUCT_ID));
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds static void ipmi_poweroff_cpi1 (ipmi_user_t user)
2381da177e4SLinus Torvalds {
2391da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
2401da177e4SLinus Torvalds 	struct ipmi_ipmb_addr             ipmb_addr;
2411da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
2421da177e4SLinus Torvalds 	int                               rv;
2431da177e4SLinus Torvalds 	unsigned char                     data[1];
2441da177e4SLinus Torvalds 	int                               slot;
2451da177e4SLinus Torvalds 	unsigned char                     hotswap_ipmb;
2461da177e4SLinus Torvalds 	unsigned char                     aer_addr;
2471da177e4SLinus Torvalds 	unsigned char                     aer_lun;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds         /*
2501da177e4SLinus Torvalds          * Configure IPMI address for local access
2511da177e4SLinus Torvalds          */
2521da177e4SLinus Torvalds         smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
2531da177e4SLinus Torvalds         smi_addr.channel = IPMI_BMC_CHANNEL;
2541da177e4SLinus Torvalds         smi_addr.lun = 0;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	printk(KERN_INFO PFX "Powering down via CPI1 power command\n");
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds 	/*
2591da177e4SLinus Torvalds 	 * Get IPMI ipmb address
2601da177e4SLinus Torvalds 	 */
2611da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_OEM_8 >> 2;
2621da177e4SLinus Torvalds 	send_msg.cmd = OEM_GRP_CMD_GET_SLOT_GA;
2631da177e4SLinus Torvalds 	send_msg.data = NULL;
2641da177e4SLinus Torvalds 	send_msg.data_len = 0;
2651da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
2661da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
2671da177e4SLinus Torvalds 				     &send_msg);
2681da177e4SLinus Torvalds 	if (rv)
2691da177e4SLinus Torvalds 		goto out;
2701da177e4SLinus Torvalds 	slot = halt_recv_msg.msg.data[1];
2711da177e4SLinus Torvalds 	hotswap_ipmb = (slot > 9) ? (0xb0 + 2 * slot) : (0xae + 2 * slot);
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	/*
2741da177e4SLinus Torvalds 	 * Get active event receiver
2751da177e4SLinus Torvalds 	 */
2761da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_SENSOR_EVT >> 2;
2771da177e4SLinus Torvalds 	send_msg.cmd = IPMI_CMD_GET_EVENT_RECEIVER;
2781da177e4SLinus Torvalds 	send_msg.data = NULL;
2791da177e4SLinus Torvalds 	send_msg.data_len = 0;
2801da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
2811da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
2821da177e4SLinus Torvalds 				     &send_msg);
2831da177e4SLinus Torvalds 	if (rv)
2841da177e4SLinus Torvalds 		goto out;
2851da177e4SLinus Torvalds 	aer_addr = halt_recv_msg.msg.data[1];
2861da177e4SLinus Torvalds 	aer_lun = halt_recv_msg.msg.data[2];
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	/*
2891da177e4SLinus Torvalds 	 * Setup IPMB address target instead of local target
2901da177e4SLinus Torvalds 	 */
2911da177e4SLinus Torvalds 	ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE;
2921da177e4SLinus Torvalds 	ipmb_addr.channel = 0;
2931da177e4SLinus Torvalds 	ipmb_addr.slave_addr = aer_addr;
2941da177e4SLinus Torvalds 	ipmb_addr.lun = aer_lun;
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	/*
2971da177e4SLinus Torvalds 	 * Send request hotswap control to remove blade from dpv
2981da177e4SLinus Torvalds 	 */
2991da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_OEM_8 >> 2;
3001da177e4SLinus Torvalds 	send_msg.cmd = OEM_GRP_CMD_REQUEST_HOTSWAP_CTRL;
3011da177e4SLinus Torvalds 	send_msg.data = &hotswap_ipmb;
3021da177e4SLinus Torvalds 	send_msg.data_len = 1;
3031da177e4SLinus Torvalds 	ipmi_request_in_rc_mode(user,
3041da177e4SLinus Torvalds 				(struct ipmi_addr *) &ipmb_addr,
3051da177e4SLinus Torvalds 				&send_msg);
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	/*
3081da177e4SLinus Torvalds 	 * Set reset asserted
3091da177e4SLinus Torvalds 	 */
3101da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_OEM_1 >> 2;
3111da177e4SLinus Torvalds 	send_msg.cmd = OEM_GRP_CMD_SET_RESET_STATE;
3121da177e4SLinus Torvalds 	send_msg.data = data;
3131da177e4SLinus Torvalds 	data[0] = 1; /* Reset asserted state */
3141da177e4SLinus Torvalds 	send_msg.data_len = 1;
3151da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
3161da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
3171da177e4SLinus Torvalds 				     &send_msg);
3181da177e4SLinus Torvalds 	if (rv)
3191da177e4SLinus Torvalds 		goto out;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	/*
3221da177e4SLinus Torvalds 	 * Power down
3231da177e4SLinus Torvalds 	 */
3241da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_OEM_1 >> 2;
3251da177e4SLinus Torvalds 	send_msg.cmd = OEM_GRP_CMD_SET_POWER_STATE;
3261da177e4SLinus Torvalds 	send_msg.data = data;
3271da177e4SLinus Torvalds 	data[0] = 1; /* Power down state */
3281da177e4SLinus Torvalds 	send_msg.data_len = 1;
3291da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
3301da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
3311da177e4SLinus Torvalds 				     &send_msg);
3321da177e4SLinus Torvalds 	if (rv)
3331da177e4SLinus Torvalds 		goto out;
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds  out:
3361da177e4SLinus Torvalds 	return;
3371da177e4SLinus Torvalds }
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds /*
3401da177e4SLinus Torvalds  * Standard chassis support
3411da177e4SLinus Torvalds  */
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds #define IPMI_NETFN_CHASSIS_REQUEST	0
3441da177e4SLinus Torvalds #define IPMI_CHASSIS_CONTROL_CMD	0x02
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds static int ipmi_chassis_detect (ipmi_user_t user)
3471da177e4SLinus Torvalds {
3481da177e4SLinus Torvalds 	/* Chassis support, use it. */
3491da177e4SLinus Torvalds 	return (capabilities & 0x80);
3501da177e4SLinus Torvalds }
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds static void ipmi_poweroff_chassis (ipmi_user_t user)
3531da177e4SLinus Torvalds {
3541da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
3551da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
3561da177e4SLinus Torvalds 	int                               rv;
3571da177e4SLinus Torvalds 	unsigned char                     data[1];
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds         /*
3601da177e4SLinus Torvalds          * Configure IPMI address for local access
3611da177e4SLinus Torvalds          */
3621da177e4SLinus Torvalds         smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
3631da177e4SLinus Torvalds         smi_addr.channel = IPMI_BMC_CHANNEL;
3641da177e4SLinus Torvalds         smi_addr.lun = 0;
3651da177e4SLinus Torvalds 
3663b625943SCorey Minyard  powercyclefailed:
3673b625943SCorey Minyard 	printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n",
3683b625943SCorey Minyard 		((poweroff_control != IPMI_CHASSIS_POWER_CYCLE) ? "down" : "cycle"));
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	/*
3711da177e4SLinus Torvalds 	 * Power down
3721da177e4SLinus Torvalds 	 */
3731da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST;
3741da177e4SLinus Torvalds 	send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD;
3753b625943SCorey Minyard 	data[0] = poweroff_control;
3761da177e4SLinus Torvalds 	send_msg.data = data;
3771da177e4SLinus Torvalds 	send_msg.data_len = sizeof(data);
3781da177e4SLinus Torvalds 	rv = ipmi_request_in_rc_mode(user,
3791da177e4SLinus Torvalds 				     (struct ipmi_addr *) &smi_addr,
3801da177e4SLinus Torvalds 				     &send_msg);
3811da177e4SLinus Torvalds 	if (rv) {
3823b625943SCorey Minyard 		switch (poweroff_control) {
3833b625943SCorey Minyard 			case IPMI_CHASSIS_POWER_CYCLE:
3843b625943SCorey Minyard 				/* power cycle failed, default to power down */
3853b625943SCorey Minyard 				printk(KERN_ERR PFX "Unable to send chassis power " \
3863b625943SCorey Minyard 					"cycle message, IPMI error 0x%x\n", rv);
3873b625943SCorey Minyard 				poweroff_control = IPMI_CHASSIS_POWER_DOWN;
3883b625943SCorey Minyard 				goto powercyclefailed;
3893b625943SCorey Minyard 
3903b625943SCorey Minyard 			case IPMI_CHASSIS_POWER_DOWN:
3913b625943SCorey Minyard 			default:
3923b625943SCorey Minyard 				printk(KERN_ERR PFX "Unable to send chassis power " \
3933b625943SCorey Minyard 					"down message, IPMI error 0x%x\n", rv);
3943b625943SCorey Minyard 				break;
3953b625943SCorey Minyard 		}
3961da177e4SLinus Torvalds 	}
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds 	return;
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds /* Table of possible power off functions. */
4031da177e4SLinus Torvalds struct poweroff_function {
4041da177e4SLinus Torvalds 	char *platform_type;
4051da177e4SLinus Torvalds 	int  (*detect)(ipmi_user_t user);
4061da177e4SLinus Torvalds 	void (*poweroff_func)(ipmi_user_t user);
4071da177e4SLinus Torvalds };
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds static struct poweroff_function poweroff_functions[] = {
4101da177e4SLinus Torvalds 	{ .platform_type	= "ATCA",
4111da177e4SLinus Torvalds 	  .detect		= ipmi_atca_detect,
4121da177e4SLinus Torvalds 	  .poweroff_func	= ipmi_poweroff_atca },
4131da177e4SLinus Torvalds 	{ .platform_type	= "CPI1",
4141da177e4SLinus Torvalds 	  .detect		= ipmi_cpi1_detect,
4151da177e4SLinus Torvalds 	  .poweroff_func	= ipmi_poweroff_cpi1 },
4161da177e4SLinus Torvalds 	/* Chassis should generally be last, other things should override
4171da177e4SLinus Torvalds 	   it. */
4181da177e4SLinus Torvalds 	{ .platform_type	= "chassis",
4191da177e4SLinus Torvalds 	  .detect		= ipmi_chassis_detect,
4201da177e4SLinus Torvalds 	  .poweroff_func	= ipmi_poweroff_chassis },
4211da177e4SLinus Torvalds };
4221da177e4SLinus Torvalds #define NUM_PO_FUNCS (sizeof(poweroff_functions) \
4231da177e4SLinus Torvalds 		      / sizeof(struct poweroff_function))
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds /* Our local state. */
4271da177e4SLinus Torvalds static int ready = 0;
4281da177e4SLinus Torvalds static ipmi_user_t ipmi_user;
4291da177e4SLinus Torvalds static void (*specific_poweroff_func)(ipmi_user_t user) = NULL;
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds /* Holds the old poweroff function so we can restore it on removal. */
4321da177e4SLinus Torvalds static void (*old_poweroff_func)(void);
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds /* Called on a powerdown request. */
4361da177e4SLinus Torvalds static void ipmi_poweroff_function (void)
4371da177e4SLinus Torvalds {
4381da177e4SLinus Torvalds 	if (!ready)
4391da177e4SLinus Torvalds 		return;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	/* Use run-to-completion mode, since interrupts may be off. */
4421da177e4SLinus Torvalds 	ipmi_user_set_run_to_completion(ipmi_user, 1);
4431da177e4SLinus Torvalds 	specific_poweroff_func(ipmi_user);
4441da177e4SLinus Torvalds 	ipmi_user_set_run_to_completion(ipmi_user, 0);
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds /* Wait for an IPMI interface to be installed, the first one installed
4481da177e4SLinus Torvalds    will be grabbed by this code and used to perform the powerdown. */
4491da177e4SLinus Torvalds static void ipmi_po_new_smi(int if_num)
4501da177e4SLinus Torvalds {
4511da177e4SLinus Torvalds 	struct ipmi_system_interface_addr smi_addr;
4521da177e4SLinus Torvalds 	struct kernel_ipmi_msg            send_msg;
4531da177e4SLinus Torvalds 	int                               rv;
4541da177e4SLinus Torvalds 	int                               i;
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 	if (ready)
4571da177e4SLinus Torvalds 		return;
4581da177e4SLinus Torvalds 
4593b625943SCorey Minyard 	rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL,
4603b625943SCorey Minyard 			      &ipmi_user);
4611da177e4SLinus Torvalds 	if (rv) {
4621da177e4SLinus Torvalds 		printk(KERN_ERR PFX "could not create IPMI user, error %d\n",
4631da177e4SLinus Torvalds 		       rv);
4641da177e4SLinus Torvalds 		return;
4651da177e4SLinus Torvalds 	}
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds         /*
4681da177e4SLinus Torvalds          * Do a get device ide and store some results, since this is
4691da177e4SLinus Torvalds 	 * used by several functions.
4701da177e4SLinus Torvalds          */
4711da177e4SLinus Torvalds         smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
4721da177e4SLinus Torvalds         smi_addr.channel = IPMI_BMC_CHANNEL;
4731da177e4SLinus Torvalds         smi_addr.lun = 0;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	send_msg.netfn = IPMI_NETFN_APP_REQUEST;
4761da177e4SLinus Torvalds 	send_msg.cmd = IPMI_GET_DEVICE_ID_CMD;
4771da177e4SLinus Torvalds 	send_msg.data = NULL;
4781da177e4SLinus Torvalds 	send_msg.data_len = 0;
4791da177e4SLinus Torvalds 	rv = ipmi_request_wait_for_response(ipmi_user,
4801da177e4SLinus Torvalds 					    (struct ipmi_addr *) &smi_addr,
4811da177e4SLinus Torvalds 					    &send_msg);
4821da177e4SLinus Torvalds 	if (rv) {
4831da177e4SLinus Torvalds 		printk(KERN_ERR PFX "Unable to send IPMI get device id info,"
4841da177e4SLinus Torvalds 		       " IPMI error 0x%x\n", rv);
4851da177e4SLinus Torvalds 		goto out_err;
4861da177e4SLinus Torvalds 	}
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 	if (halt_recv_msg.msg.data_len < 12) {
4891da177e4SLinus Torvalds 		printk(KERN_ERR PFX "(chassis) IPMI get device id info too,"
4901da177e4SLinus Torvalds 		       " short, was %d bytes, needed %d bytes\n",
4911da177e4SLinus Torvalds 		       halt_recv_msg.msg.data_len, 12);
4921da177e4SLinus Torvalds 		goto out_err;
4931da177e4SLinus Torvalds 	}
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 	mfg_id = (halt_recv_msg.msg.data[7]
4961da177e4SLinus Torvalds 		  | (halt_recv_msg.msg.data[8] << 8)
4971da177e4SLinus Torvalds 		  | (halt_recv_msg.msg.data[9] << 16));
4981da177e4SLinus Torvalds 	prod_id = (halt_recv_msg.msg.data[10]
4991da177e4SLinus Torvalds 		   | (halt_recv_msg.msg.data[11] << 8));
5001da177e4SLinus Torvalds 	capabilities = halt_recv_msg.msg.data[6];
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 	/* Scan for a poweroff method */
5041da177e4SLinus Torvalds 	for (i=0; i<NUM_PO_FUNCS; i++) {
5051da177e4SLinus Torvalds 		if (poweroff_functions[i].detect(ipmi_user))
5061da177e4SLinus Torvalds 			goto found;
5071da177e4SLinus Torvalds 	}
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds  out_err:
5101da177e4SLinus Torvalds 	printk(KERN_ERR PFX "Unable to find a poweroff function that"
5111da177e4SLinus Torvalds 	       " will work, giving up\n");
5121da177e4SLinus Torvalds 	ipmi_destroy_user(ipmi_user);
5131da177e4SLinus Torvalds 	return;
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds  found:
5161da177e4SLinus Torvalds 	printk(KERN_INFO PFX "Found a %s style poweroff function\n",
5171da177e4SLinus Torvalds 	       poweroff_functions[i].platform_type);
5181da177e4SLinus Torvalds 	specific_poweroff_func = poweroff_functions[i].poweroff_func;
5191da177e4SLinus Torvalds 	old_poweroff_func = pm_power_off;
5201da177e4SLinus Torvalds 	pm_power_off = ipmi_poweroff_function;
5211da177e4SLinus Torvalds 	ready = 1;
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds static void ipmi_po_smi_gone(int if_num)
5251da177e4SLinus Torvalds {
5261da177e4SLinus Torvalds 	/* This can never be called, because once poweroff driver is
5271da177e4SLinus Torvalds 	   registered, the interface can't go away until the power
5281da177e4SLinus Torvalds 	   driver is unregistered. */
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds static struct ipmi_smi_watcher smi_watcher =
5321da177e4SLinus Torvalds {
5331da177e4SLinus Torvalds 	.owner    = THIS_MODULE,
5341da177e4SLinus Torvalds 	.new_smi  = ipmi_po_new_smi,
5351da177e4SLinus Torvalds 	.smi_gone = ipmi_po_smi_gone
5361da177e4SLinus Torvalds };
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds 
5393b625943SCorey Minyard #ifdef CONFIG_PROC_FS
5403b625943SCorey Minyard /* displays properties to proc */
5413b625943SCorey Minyard static int proc_read_chassctrl(char *page, char **start, off_t off, int count,
5423b625943SCorey Minyard 			       int *eof, void *data)
5433b625943SCorey Minyard {
5443b625943SCorey Minyard 	return sprintf(page, "%d\t[ 0=powerdown 2=powercycle ]\n",
5453b625943SCorey Minyard 			poweroff_control);
5463b625943SCorey Minyard }
5473b625943SCorey Minyard 
5483b625943SCorey Minyard /* process property writes from proc */
5493b625943SCorey Minyard static int proc_write_chassctrl(struct file *file, const char *buffer,
5503b625943SCorey Minyard 			        unsigned long count, void *data)
5513b625943SCorey Minyard {
5523b625943SCorey Minyard 	int          rv = count;
5533b625943SCorey Minyard 	unsigned int newval = 0;
5543b625943SCorey Minyard 
5553b625943SCorey Minyard 	sscanf(buffer, "%d", &newval);
5563b625943SCorey Minyard 	switch (newval) {
5573b625943SCorey Minyard 		case IPMI_CHASSIS_POWER_CYCLE:
5583b625943SCorey Minyard 			printk(KERN_INFO PFX "power cycle is now enabled\n");
5593b625943SCorey Minyard 			poweroff_control = newval;
5603b625943SCorey Minyard 			break;
5613b625943SCorey Minyard 
5623b625943SCorey Minyard 		case IPMI_CHASSIS_POWER_DOWN:
5633b625943SCorey Minyard 			poweroff_control = IPMI_CHASSIS_POWER_DOWN;
5643b625943SCorey Minyard 			break;
5653b625943SCorey Minyard 
5663b625943SCorey Minyard 		default:
5673b625943SCorey Minyard 			rv = -EINVAL;
5683b625943SCorey Minyard 			break;
5693b625943SCorey Minyard 	}
5703b625943SCorey Minyard 
5713b625943SCorey Minyard 	return rv;
5723b625943SCorey Minyard }
5733b625943SCorey Minyard #endif /* CONFIG_PROC_FS */
5743b625943SCorey Minyard 
5751da177e4SLinus Torvalds /*
5761da177e4SLinus Torvalds  * Startup and shutdown functions.
5771da177e4SLinus Torvalds  */
5781da177e4SLinus Torvalds static int ipmi_poweroff_init (void)
5791da177e4SLinus Torvalds {
5801da177e4SLinus Torvalds 	int                   rv;
5813b625943SCorey Minyard 	struct proc_dir_entry *file;
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds 	printk ("Copyright (C) 2004 MontaVista Software -"
5841da177e4SLinus Torvalds 		" IPMI Powerdown via sys_reboot version "
5851da177e4SLinus Torvalds 		IPMI_POWEROFF_VERSION ".\n");
5861da177e4SLinus Torvalds 
5873b625943SCorey Minyard 	switch (poweroff_control) {
5883b625943SCorey Minyard 		case IPMI_CHASSIS_POWER_CYCLE:
5893b625943SCorey Minyard 			printk(KERN_INFO PFX "Power cycle is enabled.\n");
5903b625943SCorey Minyard 			break;
5911da177e4SLinus Torvalds 
5923b625943SCorey Minyard 		case IPMI_CHASSIS_POWER_DOWN:
5933b625943SCorey Minyard 		default:
5943b625943SCorey Minyard 			poweroff_control = IPMI_CHASSIS_POWER_DOWN;
5953b625943SCorey Minyard 			break;
5963b625943SCorey Minyard 	}
5973b625943SCorey Minyard 
5983b625943SCorey Minyard 	rv = ipmi_smi_watcher_register(&smi_watcher);
5993b625943SCorey Minyard 	if (rv) {
6003b625943SCorey Minyard 		printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv);
6013b625943SCorey Minyard 		goto out_err;
6023b625943SCorey Minyard 	}
6033b625943SCorey Minyard 
6043b625943SCorey Minyard #ifdef CONFIG_PROC_FS
6053b625943SCorey Minyard 	file = create_proc_entry("poweroff_control", 0, proc_ipmi_root);
6063b625943SCorey Minyard 	if (!file) {
6073b625943SCorey Minyard 		printk(KERN_ERR PFX "Unable to create proc power control\n");
6083b625943SCorey Minyard 	} else {
6093b625943SCorey Minyard 		file->nlink = 1;
6103b625943SCorey Minyard 		file->read_proc = proc_read_chassctrl;
6113b625943SCorey Minyard 		file->write_proc = proc_write_chassctrl;
6123b625943SCorey Minyard 		file->owner = THIS_MODULE;
6133b625943SCorey Minyard 	}
6143b625943SCorey Minyard #endif
6153b625943SCorey Minyard 
6163b625943SCorey Minyard  out_err:
6171da177e4SLinus Torvalds 	return rv;
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds #ifdef MODULE
6211da177e4SLinus Torvalds static __exit void ipmi_poweroff_cleanup(void)
6221da177e4SLinus Torvalds {
6231da177e4SLinus Torvalds 	int rv;
6241da177e4SLinus Torvalds 
6253b625943SCorey Minyard #ifdef CONFIG_PROC_FS
6263b625943SCorey Minyard 	remove_proc_entry("poweroff_control", proc_ipmi_root);
6273b625943SCorey Minyard #endif
6283b625943SCorey Minyard 
6291da177e4SLinus Torvalds 	ipmi_smi_watcher_unregister(&smi_watcher);
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds 	if (ready) {
6321da177e4SLinus Torvalds 		rv = ipmi_destroy_user(ipmi_user);
6331da177e4SLinus Torvalds 		if (rv)
6341da177e4SLinus Torvalds 			printk(KERN_ERR PFX "could not cleanup the IPMI"
6351da177e4SLinus Torvalds 			       " user: 0x%x\n", rv);
6361da177e4SLinus Torvalds 		pm_power_off = old_poweroff_func;
6371da177e4SLinus Torvalds 	}
6381da177e4SLinus Torvalds }
6391da177e4SLinus Torvalds module_exit(ipmi_poweroff_cleanup);
6401da177e4SLinus Torvalds #endif
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds module_init(ipmi_poweroff_init);
6431da177e4SLinus Torvalds MODULE_LICENSE("GPL");
644