Merge remote-tracking branch 'iwlwifi-fixes/master' into NEXT
[sfrench/cifs-2.6.git] / arch / powerpc / platforms / powernv / opal-hmi.c
1 /*
2  * OPAL hypervisor Maintenance interrupt handling support in PowreNV.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Copyright 2014 IBM Corporation
18  * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
19  */
20
21 #undef DEBUG
22
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/of.h>
26 #include <linux/mm.h>
27 #include <linux/slab.h>
28
29 #include <asm/opal.h>
30 #include <asm/cputable.h>
31
32 static int opal_hmi_handler_nb_init;
33 struct OpalHmiEvtNode {
34         struct list_head list;
35         struct OpalHMIEvent hmi_evt;
36 };
37 static LIST_HEAD(opal_hmi_evt_list);
38 static DEFINE_SPINLOCK(opal_hmi_evt_lock);
39
40 static void print_hmi_event_info(struct OpalHMIEvent *hmi_evt)
41 {
42         const char *level, *sevstr, *error_info;
43         static const char *hmi_error_types[] = {
44                 "Malfunction Alert",
45                 "Processor Recovery done",
46                 "Processor recovery occurred again",
47                 "Processor recovery occurred for masked error",
48                 "Timer facility experienced an error",
49                 "TFMR SPR is corrupted",
50                 "UPS (Uniterrupted Power System) Overflow indication",
51                 "An XSCOM operation failure",
52                 "An XSCOM operation completed",
53                 "SCOM has set a reserved FIR bit to cause recovery",
54                 "Debug trigger has set a reserved FIR bit to cause recovery",
55                 "A hypervisor resource error occurred"
56         };
57
58         /* Print things out */
59         if (hmi_evt->version != OpalHMIEvt_V1) {
60                 pr_err("HMI Interrupt, Unknown event version %d !\n",
61                         hmi_evt->version);
62                 return;
63         }
64         switch (hmi_evt->severity) {
65         case OpalHMI_SEV_NO_ERROR:
66                 level = KERN_INFO;
67                 sevstr = "Harmless";
68                 break;
69         case OpalHMI_SEV_WARNING:
70                 level = KERN_WARNING;
71                 sevstr = "";
72                 break;
73         case OpalHMI_SEV_ERROR_SYNC:
74                 level = KERN_ERR;
75                 sevstr = "Severe";
76                 break;
77         case OpalHMI_SEV_FATAL:
78         default:
79                 level = KERN_ERR;
80                 sevstr = "Fatal";
81                 break;
82         }
83
84         printk("%s%s Hypervisor Maintenance interrupt [%s]\n",
85                 level, sevstr,
86                 hmi_evt->disposition == OpalHMI_DISPOSITION_RECOVERED ?
87                 "Recovered" : "Not recovered");
88         error_info = hmi_evt->type < ARRAY_SIZE(hmi_error_types) ?
89                         hmi_error_types[hmi_evt->type]
90                         : "Unknown";
91         printk("%s Error detail: %s\n", level, error_info);
92         printk("%s      HMER: %016llx\n", level, be64_to_cpu(hmi_evt->hmer));
93         if ((hmi_evt->type == OpalHMI_ERROR_TFAC) ||
94                 (hmi_evt->type == OpalHMI_ERROR_TFMR_PARITY))
95                 printk("%s      TFMR: %016llx\n", level,
96                                                 be64_to_cpu(hmi_evt->tfmr));
97 }
98
99 static void hmi_event_handler(struct work_struct *work)
100 {
101         unsigned long flags;
102         struct OpalHMIEvent *hmi_evt;
103         struct OpalHmiEvtNode *msg_node;
104         uint8_t disposition;
105
106         spin_lock_irqsave(&opal_hmi_evt_lock, flags);
107         while (!list_empty(&opal_hmi_evt_list)) {
108                 msg_node = list_entry(opal_hmi_evt_list.next,
109                                            struct OpalHmiEvtNode, list);
110                 list_del(&msg_node->list);
111                 spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
112
113                 hmi_evt = (struct OpalHMIEvent *) &msg_node->hmi_evt;
114                 print_hmi_event_info(hmi_evt);
115                 disposition = hmi_evt->disposition;
116                 kfree(msg_node);
117
118                 /*
119                  * Check if HMI event has been recovered or not. If not
120                  * then we can't continue, invoke panic.
121                  */
122                 if (disposition != OpalHMI_DISPOSITION_RECOVERED)
123                         panic("Unrecoverable HMI exception");
124
125                 spin_lock_irqsave(&opal_hmi_evt_lock, flags);
126         }
127         spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
128 }
129
130 static DECLARE_WORK(hmi_event_work, hmi_event_handler);
131 /*
132  * opal_handle_hmi_event - notifier handler that queues up HMI events
133  * to be preocessed later.
134  */
135 static int opal_handle_hmi_event(struct notifier_block *nb,
136                           unsigned long msg_type, void *msg)
137 {
138         unsigned long flags;
139         struct OpalHMIEvent *hmi_evt;
140         struct opal_msg *hmi_msg = msg;
141         struct OpalHmiEvtNode *msg_node;
142
143         /* Sanity Checks */
144         if (msg_type != OPAL_MSG_HMI_EVT)
145                 return 0;
146
147         /* HMI event info starts from param[0] */
148         hmi_evt = (struct OpalHMIEvent *)&hmi_msg->params[0];
149
150         /* Delay the logging of HMI events to workqueue. */
151         msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC);
152         if (!msg_node) {
153                 pr_err("HMI: out of memory, Opal message event not handled\n");
154                 return -ENOMEM;
155         }
156         memcpy(&msg_node->hmi_evt, hmi_evt, sizeof(struct OpalHMIEvent));
157
158         spin_lock_irqsave(&opal_hmi_evt_lock, flags);
159         list_add(&msg_node->list, &opal_hmi_evt_list);
160         spin_unlock_irqrestore(&opal_hmi_evt_lock, flags);
161
162         schedule_work(&hmi_event_work);
163         return 0;
164 }
165
166 static struct notifier_block opal_hmi_handler_nb = {
167         .notifier_call  = opal_handle_hmi_event,
168         .next           = NULL,
169         .priority       = 0,
170 };
171
172 static int __init opal_hmi_handler_init(void)
173 {
174         int ret;
175
176         if (!opal_hmi_handler_nb_init) {
177                 ret = opal_message_notifier_register(
178                                 OPAL_MSG_HMI_EVT, &opal_hmi_handler_nb);
179                 if (ret) {
180                         pr_err("%s: Can't register OPAL event notifier (%d)\n",
181                                __func__, ret);
182                         return ret;
183                 }
184                 opal_hmi_handler_nb_init = 1;
185         }
186         return 0;
187 }
188 subsys_initcall(opal_hmi_handler_init);