snsc_event.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * SN Platform system controller communication support
  3. *
  4. * This file is subject to the terms and conditions of the GNU General Public
  5. * License. See the file "COPYING" in the main directory of this archive
  6. * for more details.
  7. *
  8. * Copyright (C) 2004-2006 Silicon Graphics, Inc. All rights reserved.
  9. */
  10. /*
  11. * System controller event handler
  12. *
  13. * These routines deal with environmental events arriving from the
  14. * system controllers.
  15. */
  16. #include <linux/interrupt.h>
  17. #include <linux/sched.h>
  18. #include <asm/byteorder.h>
  19. #include <asm/sn/sn_sal.h>
  20. #include <asm/unaligned.h>
  21. #include "snsc.h"
  22. static struct subch_data_s *event_sd;
  23. void scdrv_event(unsigned long);
  24. DECLARE_TASKLET(sn_sysctl_event, scdrv_event, 0);
  25. /*
  26. * scdrv_event_interrupt
  27. *
  28. * Pull incoming environmental events off the physical link to the
  29. * system controller and put them in a temporary holding area in SAL.
  30. * Schedule scdrv_event() to move them along to their ultimate
  31. * destination.
  32. */
  33. static irqreturn_t
  34. scdrv_event_interrupt(int irq, void *subch_data)
  35. {
  36. struct subch_data_s *sd = subch_data;
  37. unsigned long flags;
  38. int status;
  39. spin_lock_irqsave(&sd->sd_rlock, flags);
  40. status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch);
  41. if ((status > 0) && (status & SAL_IROUTER_INTR_RECV)) {
  42. tasklet_schedule(&sn_sysctl_event);
  43. }
  44. spin_unlock_irqrestore(&sd->sd_rlock, flags);
  45. return IRQ_HANDLED;
  46. }
  47. /*
  48. * scdrv_parse_event
  49. *
  50. * Break an event (as read from SAL) into useful pieces so we can decide
  51. * what to do with it.
  52. */
  53. static int
  54. scdrv_parse_event(char *event, int *src, int *code, int *esp_code, char *desc)
  55. {
  56. char *desc_end;
  57. /* record event source address */
  58. *src = get_unaligned_be32(event);
  59. event += 4; /* move on to event code */
  60. /* record the system controller's event code */
  61. *code = get_unaligned_be32(event);
  62. event += 4; /* move on to event arguments */
  63. /* how many arguments are in the packet? */
  64. if (*event++ != 2) {
  65. /* if not 2, give up */
  66. return -1;
  67. }
  68. /* parse out the ESP code */
  69. if (*event++ != IR_ARG_INT) {
  70. /* not an integer argument, so give up */
  71. return -1;
  72. }
  73. *esp_code = get_unaligned_be32(event);
  74. event += 4;
  75. /* parse out the event description */
  76. if (*event++ != IR_ARG_ASCII) {
  77. /* not an ASCII string, so give up */
  78. return -1;
  79. }
  80. event[CHUNKSIZE-1] = '\0'; /* ensure this string ends! */
  81. event += 2; /* skip leading CR/LF */
  82. desc_end = desc + sprintf(desc, "%s", event);
  83. /* strip trailing CR/LF (if any) */
  84. for (desc_end--;
  85. (desc_end != desc) && ((*desc_end == 0xd) || (*desc_end == 0xa));
  86. desc_end--) {
  87. *desc_end = '\0';
  88. }
  89. return 0;
  90. }
  91. /*
  92. * scdrv_event_severity
  93. *
  94. * Figure out how urgent a message we should write to the console/syslog
  95. * via printk.
  96. */
  97. static char *
  98. scdrv_event_severity(int code)
  99. {
  100. int ev_class = (code & EV_CLASS_MASK);
  101. int ev_severity = (code & EV_SEVERITY_MASK);
  102. char *pk_severity = KERN_NOTICE;
  103. switch (ev_class) {
  104. case EV_CLASS_POWER:
  105. switch (ev_severity) {
  106. case EV_SEVERITY_POWER_LOW_WARNING:
  107. case EV_SEVERITY_POWER_HIGH_WARNING:
  108. pk_severity = KERN_WARNING;
  109. break;
  110. case EV_SEVERITY_POWER_HIGH_FAULT:
  111. case EV_SEVERITY_POWER_LOW_FAULT:
  112. pk_severity = KERN_ALERT;
  113. break;
  114. }
  115. break;
  116. case EV_CLASS_FAN:
  117. switch (ev_severity) {
  118. case EV_SEVERITY_FAN_WARNING:
  119. pk_severity = KERN_WARNING;
  120. break;
  121. case EV_SEVERITY_FAN_FAULT:
  122. pk_severity = KERN_CRIT;
  123. break;
  124. }
  125. break;
  126. case EV_CLASS_TEMP:
  127. switch (ev_severity) {
  128. case EV_SEVERITY_TEMP_ADVISORY:
  129. pk_severity = KERN_WARNING;
  130. break;
  131. case EV_SEVERITY_TEMP_CRITICAL:
  132. pk_severity = KERN_CRIT;
  133. break;
  134. case EV_SEVERITY_TEMP_FAULT:
  135. pk_severity = KERN_ALERT;
  136. break;
  137. }
  138. break;
  139. case EV_CLASS_ENV:
  140. pk_severity = KERN_ALERT;
  141. break;
  142. case EV_CLASS_TEST_FAULT:
  143. pk_severity = KERN_ALERT;
  144. break;
  145. case EV_CLASS_TEST_WARNING:
  146. pk_severity = KERN_WARNING;
  147. break;
  148. case EV_CLASS_PWRD_NOTIFY:
  149. pk_severity = KERN_ALERT;
  150. break;
  151. }
  152. return pk_severity;
  153. }
  154. /*
  155. * scdrv_dispatch_event
  156. *
  157. * Do the right thing with an incoming event. That's often nothing
  158. * more than printing it to the system log. For power-down notifications
  159. * we start a graceful shutdown.
  160. */
  161. static void
  162. scdrv_dispatch_event(char *event, int len)
  163. {
  164. static int snsc_shutting_down = 0;
  165. int code, esp_code, src, class;
  166. char desc[CHUNKSIZE];
  167. char *severity;
  168. if (scdrv_parse_event(event, &src, &code, &esp_code, desc) < 0) {
  169. /* ignore uninterpretible event */
  170. return;
  171. }
  172. /* how urgent is the message? */
  173. severity = scdrv_event_severity(code);
  174. class = (code & EV_CLASS_MASK);
  175. if (class == EV_CLASS_PWRD_NOTIFY || code == ENV_PWRDN_PEND) {
  176. if (snsc_shutting_down)
  177. return;
  178. snsc_shutting_down = 1;
  179. /* give a message for each type of event */
  180. if (class == EV_CLASS_PWRD_NOTIFY)
  181. printk(KERN_NOTICE "Power off indication received."
  182. " Sending SIGPWR to init...\n");
  183. else if (code == ENV_PWRDN_PEND)
  184. printk(KERN_CRIT "WARNING: Shutting down the system"
  185. " due to a critical environmental condition."
  186. " Sending SIGPWR to init...\n");
  187. /* give a SIGPWR signal to init proc */
  188. kill_cad_pid(SIGPWR, 0);
  189. } else {
  190. /* print to system log */
  191. printk("%s|$(0x%x)%s\n", severity, esp_code, desc);
  192. }
  193. }
  194. /*
  195. * scdrv_event
  196. *
  197. * Called as a tasklet when an event arrives from the L1. Read the event
  198. * from where it's temporarily stored in SAL and call scdrv_dispatch_event()
  199. * to send it on its way. Keep trying to read events until SAL indicates
  200. * that there are no more immediately available.
  201. */
  202. void
  203. scdrv_event(unsigned long dummy)
  204. {
  205. int status;
  206. int len;
  207. unsigned long flags;
  208. struct subch_data_s *sd = event_sd;
  209. /* anything to read? */
  210. len = CHUNKSIZE;
  211. spin_lock_irqsave(&sd->sd_rlock, flags);
  212. status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch,
  213. sd->sd_rb, &len);
  214. while (!(status < 0)) {
  215. spin_unlock_irqrestore(&sd->sd_rlock, flags);
  216. scdrv_dispatch_event(sd->sd_rb, len);
  217. len = CHUNKSIZE;
  218. spin_lock_irqsave(&sd->sd_rlock, flags);
  219. status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch,
  220. sd->sd_rb, &len);
  221. }
  222. spin_unlock_irqrestore(&sd->sd_rlock, flags);
  223. }
  224. /*
  225. * scdrv_event_init
  226. *
  227. * Sets up a system controller subchannel to begin receiving event
  228. * messages. This is sort of a specialized version of scdrv_open()
  229. * in drivers/char/sn_sysctl.c.
  230. */
  231. void
  232. scdrv_event_init(struct sysctl_data_s *scd)
  233. {
  234. int rv;
  235. event_sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL);
  236. if (event_sd == NULL) {
  237. printk(KERN_WARNING "%s: couldn't allocate subchannel info"
  238. " for event monitoring\n", __func__);
  239. return;
  240. }
  241. /* initialize subch_data_s fields */
  242. event_sd->sd_nasid = scd->scd_nasid;
  243. spin_lock_init(&event_sd->sd_rlock);
  244. /* ask the system controllers to send events to this node */
  245. event_sd->sd_subch = ia64_sn_sysctl_event_init(scd->scd_nasid);
  246. if (event_sd->sd_subch < 0) {
  247. kfree(event_sd);
  248. printk(KERN_WARNING "%s: couldn't open event subchannel\n",
  249. __func__);
  250. return;
  251. }
  252. /* hook event subchannel up to the system controller interrupt */
  253. rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt,
  254. IRQF_SHARED | IRQF_DISABLED,
  255. "system controller events", event_sd);
  256. if (rv) {
  257. printk(KERN_WARNING "%s: irq request failed (%d)\n",
  258. __func__, rv);
  259. ia64_sn_irtr_close(event_sd->sd_nasid, event_sd->sd_subch);
  260. kfree(event_sd);
  261. return;
  262. }
  263. }