Merge branch 'topic/hda' into for-linus
[linux-2.6] / drivers / s390 / cio / crw.c
1 /*
2  *   Channel report handling code
3  *
4  *    Copyright IBM Corp. 2000,2009
5  *    Author(s): Ingo Adlung <adlung@de.ibm.com>,
6  *               Martin Schwidefsky <schwidefsky@de.ibm.com>,
7  *               Cornelia Huck <cornelia.huck@de.ibm.com>,
8  *               Heiko Carstens <heiko.carstens@de.ibm.com>,
9  */
10
11 #include <linux/semaphore.h>
12 #include <linux/mutex.h>
13 #include <linux/kthread.h>
14 #include <linux/init.h>
15 #include <asm/crw.h>
16
17 static struct semaphore crw_semaphore;
18 static DEFINE_MUTEX(crw_handler_mutex);
19 static crw_handler_t crw_handlers[NR_RSCS];
20
21 /**
22  * crw_register_handler() - register a channel report word handler
23  * @rsc: reporting source code to handle
24  * @handler: handler to be registered
25  *
26  * Returns %0 on success and a negative error value otherwise.
27  */
28 int crw_register_handler(int rsc, crw_handler_t handler)
29 {
30         int rc = 0;
31
32         if ((rsc < 0) || (rsc >= NR_RSCS))
33                 return -EINVAL;
34         mutex_lock(&crw_handler_mutex);
35         if (crw_handlers[rsc])
36                 rc = -EBUSY;
37         else
38                 crw_handlers[rsc] = handler;
39         mutex_unlock(&crw_handler_mutex);
40         return rc;
41 }
42
43 /**
44  * crw_unregister_handler() - unregister a channel report word handler
45  * @rsc: reporting source code to handle
46  */
47 void crw_unregister_handler(int rsc)
48 {
49         if ((rsc < 0) || (rsc >= NR_RSCS))
50                 return;
51         mutex_lock(&crw_handler_mutex);
52         crw_handlers[rsc] = NULL;
53         mutex_unlock(&crw_handler_mutex);
54 }
55
56 /*
57  * Retrieve CRWs and call function to handle event.
58  */
59 static int crw_collect_info(void *unused)
60 {
61         struct crw crw[2];
62         int ccode;
63         unsigned int chain;
64         int ignore;
65
66 repeat:
67         ignore = down_interruptible(&crw_semaphore);
68         chain = 0;
69         while (1) {
70                 crw_handler_t handler;
71
72                 if (unlikely(chain > 1)) {
73                         struct crw tmp_crw;
74
75                         printk(KERN_WARNING"%s: Code does not support more "
76                                "than two chained crws; please report to "
77                                "linux390@de.ibm.com!\n", __func__);
78                         ccode = stcrw(&tmp_crw);
79                         printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
80                                "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
81                                __func__, tmp_crw.slct, tmp_crw.oflw,
82                                tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc,
83                                tmp_crw.erc, tmp_crw.rsid);
84                         printk(KERN_WARNING"%s: This was crw number %x in the "
85                                "chain\n", __func__, chain);
86                         if (ccode != 0)
87                                 break;
88                         chain = tmp_crw.chn ? chain + 1 : 0;
89                         continue;
90                 }
91                 ccode = stcrw(&crw[chain]);
92                 if (ccode != 0)
93                         break;
94                 printk(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
95                        "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
96                        crw[chain].slct, crw[chain].oflw, crw[chain].chn,
97                        crw[chain].rsc, crw[chain].anc, crw[chain].erc,
98                        crw[chain].rsid);
99                 /* Check for overflows. */
100                 if (crw[chain].oflw) {
101                         int i;
102
103                         pr_debug("%s: crw overflow detected!\n", __func__);
104                         mutex_lock(&crw_handler_mutex);
105                         for (i = 0; i < NR_RSCS; i++) {
106                                 if (crw_handlers[i])
107                                         crw_handlers[i](NULL, NULL, 1);
108                         }
109                         mutex_unlock(&crw_handler_mutex);
110                         chain = 0;
111                         continue;
112                 }
113                 if (crw[0].chn && !chain) {
114                         chain++;
115                         continue;
116                 }
117                 mutex_lock(&crw_handler_mutex);
118                 handler = crw_handlers[crw[chain].rsc];
119                 if (handler)
120                         handler(&crw[0], chain ? &crw[1] : NULL, 0);
121                 mutex_unlock(&crw_handler_mutex);
122                 /* chain is always 0 or 1 here. */
123                 chain = crw[chain].chn ? chain + 1 : 0;
124         }
125         goto repeat;
126         return 0;
127 }
128
129 void crw_handle_channel_report(void)
130 {
131         up(&crw_semaphore);
132 }
133
134 /*
135  * Separate initcall needed for semaphore initialization since
136  * crw_handle_channel_report might be called before crw_machine_check_init.
137  */
138 static int __init crw_init_semaphore(void)
139 {
140         init_MUTEX_LOCKED(&crw_semaphore);
141         return 0;
142 }
143 pure_initcall(crw_init_semaphore);
144
145 /*
146  * Machine checks for the channel subsystem must be enabled
147  * after the channel subsystem is initialized
148  */
149 static int __init crw_machine_check_init(void)
150 {
151         struct task_struct *task;
152
153         task = kthread_run(crw_collect_info, NULL, "kmcheck");
154         if (IS_ERR(task))
155                 return PTR_ERR(task);
156         ctl_set_bit(14, 28);    /* enable channel report MCH */
157         return 0;
158 }
159 device_initcall(crw_machine_check_init);