Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/shaggy...
[pandora-kernel.git] / drivers / char / ipmi / ipmi_poweroff.c
index 786a280..9d23136 100644 (file)
@@ -31,7 +31,6 @@
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/proc_fs.h>
@@ -44,6 +43,9 @@
 
 #define PFX "IPMI poweroff: "
 
+static void ipmi_po_smi_gone(int if_num);
+static void ipmi_po_new_smi(int if_num, struct device *device);
+
 /* Definitions for controlling power off (if the system supports it).  It
  * conveniently matches the IPMI chassis control values. */
 #define IPMI_CHASSIS_POWER_DOWN                0       /* power down, the default. */
 /* the IPMI data command */
 static int poweroff_powercycle;
 
+/* Which interface to use, -1 means the first we see. */
+static int ifnum_to_use = -1;
+
+/* Our local state. */
+static int ready;
+static ipmi_user_t ipmi_user;
+static int ipmi_ifnum;
+static void (*specific_poweroff_func)(ipmi_user_t user);
+
+/* Holds the old poweroff function so we can restore it on removal. */
+static void (*old_poweroff_func)(void);
+
+static int set_param_ifnum(const char *val, struct kernel_param *kp)
+{
+       int rv = param_set_int(val, kp);
+       if (rv)
+               return rv;
+       if ((ifnum_to_use < 0) || (ifnum_to_use == ipmi_ifnum))
+               return 0;
+
+       ipmi_po_smi_gone(ipmi_ifnum);
+       ipmi_po_new_smi(ifnum_to_use, NULL);
+       return 0;
+}
+
+module_param_call(ifnum_to_use, set_param_ifnum, param_get_int,
+                 &ifnum_to_use, 0644);
+MODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog "
+                "timer.  Setting to -1 defaults to the first registered "
+                "interface");
+
 /* parameter definition to allow user to flag power cycle */
 module_param(poweroff_powercycle, int, 0644);
 MODULE_PARM_DESC(poweroff_powercycle, " Set to non-zero to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down.");
@@ -143,6 +176,42 @@ static int ipmi_request_in_rc_mode(ipmi_user_t            user,
 #define IPMI_ATCA_GET_ADDR_INFO_CMD    0x01
 #define IPMI_PICMG_ID                  0
 
+#define IPMI_NETFN_OEM                         0x2e
+#define IPMI_ATCA_PPS_GRACEFUL_RESTART         0x11
+#define IPMI_ATCA_PPS_IANA                     "\x00\x40\x0A"
+#define IPMI_MOTOROLA_MANUFACTURER_ID          0x0000A1
+#define IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID      0x0051
+
+static void (*atca_oem_poweroff_hook)(ipmi_user_t user);
+
+static void pps_poweroff_atca (ipmi_user_t user)
+{
+        struct ipmi_system_interface_addr smi_addr;
+        struct kernel_ipmi_msg            send_msg;
+        int                               rv;
+        /*
+         * Configure IPMI address for local access
+         */
+        smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+        smi_addr.channel = IPMI_BMC_CHANNEL;
+        smi_addr.lun = 0;
+
+        printk(KERN_INFO PFX "PPS powerdown hook used");
+
+        send_msg.netfn = IPMI_NETFN_OEM;
+        send_msg.cmd = IPMI_ATCA_PPS_GRACEFUL_RESTART;
+        send_msg.data = IPMI_ATCA_PPS_IANA;
+        send_msg.data_len = 3;
+        rv = ipmi_request_in_rc_mode(user,
+                                  (struct ipmi_addr *) &smi_addr,
+                                   &send_msg);
+        if (rv && rv != IPMI_UNKNOWN_ERR_COMPLETION_CODE) {
+                printk(KERN_ERR PFX "Unable to send ATCA ,"
+                       " IPMI error 0x%x\n", rv);
+        }
+       return;
+}
+
 static int ipmi_atca_detect (ipmi_user_t user)
 {
        struct ipmi_system_interface_addr smi_addr;
@@ -168,6 +237,13 @@ static int ipmi_atca_detect (ipmi_user_t user)
        rv = ipmi_request_wait_for_response(user,
                                            (struct ipmi_addr *) &smi_addr,
                                            &send_msg);
+
+        printk(KERN_INFO PFX "ATCA Detect mfg 0x%X prod 0x%X\n", mfg_id, prod_id);
+        if((mfg_id == IPMI_MOTOROLA_MANUFACTURER_ID)
+            && (prod_id == IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID)) {
+               printk(KERN_INFO PFX "Installing Pigeon Point Systems Poweroff Hook\n");
+               atca_oem_poweroff_hook = pps_poweroff_atca;
+       }
        return !rv;
 }
 
@@ -201,12 +277,19 @@ static void ipmi_poweroff_atca (ipmi_user_t user)
        rv = ipmi_request_in_rc_mode(user,
                                     (struct ipmi_addr *) &smi_addr,
                                     &send_msg);
-       if (rv) {
+        /** At this point, the system may be shutting down, and most
+         ** serial drivers (if used) will have interrupts turned off
+         ** it may be better to ignore IPMI_UNKNOWN_ERR_COMPLETION_CODE
+         ** return code
+         **/
+        if (rv && rv != IPMI_UNKNOWN_ERR_COMPLETION_CODE) {
                printk(KERN_ERR PFX "Unable to send ATCA powerdown message,"
                       " IPMI error 0x%x\n", rv);
                goto out;
        }
 
+       if(atca_oem_poweroff_hook)
+               return atca_oem_poweroff_hook(user);
  out:
        return;
 }
@@ -346,7 +429,7 @@ static int ipmi_dell_chassis_detect (ipmi_user_t user)
 {
        const char ipmi_version_major = ipmi_version & 0xF;
        const char ipmi_version_minor = (ipmi_version >> 4) & 0xF;
-       const char mfr[3]=DELL_IANA_MFR_ID;
+       const char mfr[3] = DELL_IANA_MFR_ID;
        if (!memcmp(mfr, &mfg_id, sizeof(mfr)) &&
            ipmi_version_major <= 1 &&
            ipmi_version_minor < 5)
@@ -441,15 +524,6 @@ static struct poweroff_function poweroff_functions[] = {
                      / sizeof(struct poweroff_function))
 
 
-/* Our local state. */
-static int ready = 0;
-static ipmi_user_t ipmi_user;
-static void (*specific_poweroff_func)(ipmi_user_t user) = NULL;
-
-/* Holds the old poweroff function so we can restore it on removal. */
-static void (*old_poweroff_func)(void);
-
-
 /* Called on a powerdown request. */
 static void ipmi_poweroff_function (void)
 {
@@ -474,6 +548,9 @@ static void ipmi_po_new_smi(int if_num, struct device *device)
        if (ready)
                return;
 
+       if ((ifnum_to_use >= 0) && (ifnum_to_use != if_num))
+               return;
+
        rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL,
                              &ipmi_user);
        if (rv) {
@@ -482,6 +559,8 @@ static void ipmi_po_new_smi(int if_num, struct device *device)
                return;
        }
 
+       ipmi_ifnum = if_num;
+
         /*
          * Do a get device ide and store some results, since this is
         * used by several functions.
@@ -542,9 +621,15 @@ static void ipmi_po_new_smi(int if_num, struct device *device)
 
 static void ipmi_po_smi_gone(int if_num)
 {
-       /* This can never be called, because once poweroff driver is
-          registered, the interface can't go away until the power
-          driver is unregistered. */
+       if (!ready)
+               return;
+
+       if (ipmi_ifnum != if_num)
+               return;
+
+       ready = 0;
+       ipmi_destroy_user(ipmi_user);
+       pm_power_off = old_poweroff_func;
 }
 
 static struct ipmi_smi_watcher smi_watcher =
@@ -617,9 +702,9 @@ static int ipmi_poweroff_init (void)
                printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv);
                goto out_err;
        }
-#endif
 
  out_err:
+#endif
        return rv;
 }