blob: 020566571e07606b203a8f6d5d7d7de8b9afcae8 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/s390/cio/css.c
3 * driver for channel subsystem
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Cornelia Huck7e9db9e2008-07-14 09:58:44 +02005 * Copyright IBM Corp. 2002,2008
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Author(s): Arnd Bergmann (arndb@de.ibm.com)
Cornelia Huck4ce3b302006-01-14 13:21:04 -08007 * Cornelia Huck (cornelia.huck@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 */
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/device.h>
12#include <linux/slab.h>
13#include <linux/errno.h>
14#include <linux/list.h>
Cornelia Hucka55360d2007-10-12 16:11:20 +020015#include <linux/reboot.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
17#include "css.h"
18#include "cio.h"
19#include "cio_debug.h"
20#include "ioasm.h"
21#include "chsc.h"
Peter Oberparleiter40154b82006-06-29 14:57:03 +020022#include "device.h"
Peter Oberparleiter83b33702007-04-27 16:01:34 +020023#include "idset.h"
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +020024#include "chp.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
Linus Torvalds1da177e2005-04-16 15:20:36 -070026int css_init_done = 0;
Peter Oberparleiter40154b82006-06-29 14:57:03 +020027static int need_reprobe = 0;
Cornelia Huckfb6958a2006-01-06 00:19:25 -080028static int max_ssid = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Cornelia Huck7c9f4e32007-10-12 16:11:13 +020030struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Cornelia Hucka28c6942006-01-06 00:19:23 -080032int css_characteristics_avail = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Heiko Carstens4d284ca2007-02-05 21:18:53 +010034int
Cornelia Huckf97a56f2006-01-06 00:19:22 -080035for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
36{
37 struct subchannel_id schid;
38 int ret;
39
40 init_subchannel_id(&schid);
41 ret = -ENODEV;
42 do {
Cornelia Huckfb6958a2006-01-06 00:19:25 -080043 do {
44 ret = fn(schid, data);
45 if (ret)
46 break;
47 } while (schid.sch_no++ < __MAX_SUBCHANNEL);
48 schid.sch_no = 0;
49 } while (schid.ssid++ < max_ssid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -080050 return ret;
51}
52
Peter Oberparleitere82a1562008-01-26 14:10:48 +010053struct cb_data {
54 void *data;
55 struct idset *set;
56 int (*fn_known_sch)(struct subchannel *, void *);
57 int (*fn_unknown_sch)(struct subchannel_id, void *);
58};
59
60static int call_fn_known_sch(struct device *dev, void *data)
61{
62 struct subchannel *sch = to_subchannel(dev);
63 struct cb_data *cb = data;
64 int rc = 0;
65
66 idset_sch_del(cb->set, sch->schid);
67 if (cb->fn_known_sch)
68 rc = cb->fn_known_sch(sch, cb->data);
69 return rc;
70}
71
72static int call_fn_unknown_sch(struct subchannel_id schid, void *data)
73{
74 struct cb_data *cb = data;
75 int rc = 0;
76
77 if (idset_sch_contains(cb->set, schid))
78 rc = cb->fn_unknown_sch(schid, cb->data);
79 return rc;
80}
81
82int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
83 int (*fn_unknown)(struct subchannel_id,
84 void *), void *data)
85{
86 struct cb_data cb;
87 int rc;
88
89 cb.set = idset_sch_new();
90 if (!cb.set)
91 return -ENOMEM;
92 idset_fill(cb.set);
93 cb.data = data;
94 cb.fn_known_sch = fn_known;
95 cb.fn_unknown_sch = fn_unknown;
96 /* Process registered subchannels. */
97 rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch);
98 if (rc)
99 goto out;
100 /* Process unregistered subchannels. */
101 if (fn_unknown)
102 rc = for_each_subchannel(call_fn_unknown_sch, &cb);
103out:
104 idset_free(cb.set);
105
106 return rc;
107}
108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109static struct subchannel *
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800110css_alloc_subchannel(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
112 struct subchannel *sch;
113 int ret;
114
115 sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA);
116 if (sch == NULL)
117 return ERR_PTR(-ENOMEM);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800118 ret = cio_validate_subchannel (sch, schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 if (ret < 0) {
120 kfree(sch);
121 return ERR_PTR(ret);
122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 return sch;
124}
125
126static void
127css_free_subchannel(struct subchannel *sch)
128{
129 if (sch) {
130 /* Reset intparm to zeroes. */
131 sch->schib.pmcw.intparm = 0;
132 cio_modify(sch);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100133 kfree(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 kfree(sch);
135 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
138static void
139css_subchannel_release(struct device *dev)
140{
141 struct subchannel *sch;
142
143 sch = to_subchannel(dev);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100144 if (!cio_is_console(sch->schid)) {
145 kfree(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 kfree(sch);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100147 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
Cornelia Huck07c6a332007-07-27 12:29:10 +0200150static int css_sch_device_register(struct subchannel *sch)
Cornelia Huck6ab48792006-07-12 16:39:50 +0200151{
152 int ret;
153
154 mutex_lock(&sch->reg_mutex);
155 ret = device_register(&sch->dev);
156 mutex_unlock(&sch->reg_mutex);
157 return ret;
158}
159
160void css_sch_device_unregister(struct subchannel *sch)
161{
162 mutex_lock(&sch->reg_mutex);
163 device_unregister(&sch->dev);
164 mutex_unlock(&sch->reg_mutex);
165}
166
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200167static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
168{
169 int i;
170 int mask;
171
172 memset(ssd, 0, sizeof(struct chsc_ssd_info));
173 ssd->path_mask = pmcw->pim;
174 for (i = 0; i < 8; i++) {
175 mask = 0x80 >> i;
176 if (pmcw->pim & mask) {
177 chp_id_init(&ssd->chpid[i]);
178 ssd->chpid[i].id = pmcw->chpid[i];
179 }
180 }
181}
182
183static void ssd_register_chpids(struct chsc_ssd_info *ssd)
184{
185 int i;
186 int mask;
187
188 for (i = 0; i < 8; i++) {
189 mask = 0x80 >> i;
190 if (ssd->path_mask & mask)
191 if (!chp_is_registered(ssd->chpid[i]))
192 chp_new(ssd->chpid[i]);
193 }
194}
195
196void css_update_ssd_info(struct subchannel *sch)
197{
198 int ret;
199
200 if (cio_is_console(sch->schid)) {
201 /* Console is initialized too early for functions requiring
202 * memory allocation. */
203 ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
204 } else {
205 ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info);
206 if (ret)
207 ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
208 ssd_register_chpids(&sch->ssd_info);
209 }
210}
211
Cornelia Huck7e9db9e2008-07-14 09:58:44 +0200212static ssize_t type_show(struct device *dev, struct device_attribute *attr,
213 char *buf)
214{
215 struct subchannel *sch = to_subchannel(dev);
216
217 return sprintf(buf, "%01x\n", sch->st);
218}
219
220static DEVICE_ATTR(type, 0444, type_show, NULL);
221
222static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
223 char *buf)
224{
225 struct subchannel *sch = to_subchannel(dev);
226
227 return sprintf(buf, "css:t%01X\n", sch->st);
228}
229
230static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
231
232static struct attribute *subch_attrs[] = {
233 &dev_attr_type.attr,
234 &dev_attr_modalias.attr,
235 NULL,
236};
237
238static struct attribute_group subch_attr_group = {
239 .attrs = subch_attrs,
240};
241
242static struct attribute_group *default_subch_attr_groups[] = {
243 &subch_attr_group,
244 NULL,
245};
246
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200247static int css_register_subchannel(struct subchannel *sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248{
249 int ret;
250
251 /* Initialize the subchannel structure */
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200252 sch->dev.parent = &channel_subsystems[0]->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 sch->dev.bus = &css_bus_type;
254 sch->dev.release = &css_subchannel_release;
Cornelia Huck7e9db9e2008-07-14 09:58:44 +0200255 sch->dev.groups = default_subch_attr_groups;
Cornelia Huck5bf04b22007-10-22 12:52:41 +0200256 /*
257 * We don't want to generate uevents for I/O subchannels that don't
258 * have a working ccw device behind them since they will be
259 * unregistered before they can be used anyway, so we delay the add
260 * uevent until after device recognition was successful.
Cornelia Huck7e9db9e2008-07-14 09:58:44 +0200261 * Note that we suppress the uevent for all subchannel types;
262 * the subchannel driver can decide itself when it wants to inform
263 * userspace of its existence.
Cornelia Huck5bf04b22007-10-22 12:52:41 +0200264 */
Cornelia Huck7e9db9e2008-07-14 09:58:44 +0200265 sch->dev.uevent_suppress = 1;
Peter Oberparleiter7ad6a242007-04-27 16:01:35 +0200266 css_update_ssd_info(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 /* make it known to the system */
Cornelia Huck6ab48792006-07-12 16:39:50 +0200268 ret = css_sch_device_register(sch);
Cornelia Huck7674da72006-12-08 15:54:21 +0100269 if (ret) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +0200270 CIO_MSG_EVENT(0, "Could not register sch 0.%x.%04x: %d\n",
271 sch->schid.ssid, sch->schid.sch_no, ret);
Cornelia Huck7674da72006-12-08 15:54:21 +0100272 return ret;
273 }
Cornelia Huck7e9db9e2008-07-14 09:58:44 +0200274 if (!sch->driver) {
275 /*
276 * No driver matched. Generate the uevent now so that
277 * a fitting driver module may be loaded based on the
278 * modalias.
279 */
280 sch->dev.uevent_suppress = 0;
281 kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
282 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 return ret;
284}
285
Cornelia Huckc820de32008-07-14 09:58:45 +0200286int css_probe_device(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287{
288 int ret;
289 struct subchannel *sch;
290
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800291 sch = css_alloc_subchannel(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 if (IS_ERR(sch))
293 return PTR_ERR(sch);
294 ret = css_register_subchannel(sch);
295 if (ret)
296 css_free_subchannel(sch);
297 return ret;
298}
299
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700300static int
301check_subchannel(struct device * dev, void * data)
302{
303 struct subchannel *sch;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800304 struct subchannel_id *schid = data;
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700305
306 sch = to_subchannel(dev);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800307 return schid_equal(&sch->schid, schid);
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700308}
309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310struct subchannel *
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800311get_subchannel_by_schid(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 struct device *dev;
314
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700315 dev = bus_find_device(&css_bus_type, NULL,
Cornelia Huck12975ae2006-10-11 15:31:47 +0200316 &schid, check_subchannel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700318 return dev ? to_subchannel(dev) : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319}
320
Cornelia Huckb279a4f2008-01-26 14:10:45 +0100321/**
322 * css_sch_is_valid() - check if a subchannel is valid
323 * @schib: subchannel information block for the subchannel
324 */
325int css_sch_is_valid(struct schib *schib)
326{
327 if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv)
328 return 0;
329 return 1;
330}
331EXPORT_SYMBOL_GPL(css_sch_is_valid);
332
Peter Oberparleiter564337f2006-09-20 16:00:01 +0200333static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
334{
335 struct schib schib;
336
337 if (!slow) {
338 /* Will be done on the slow path. */
339 return -EAGAIN;
340 }
Cornelia Huckb279a4f2008-01-26 14:10:45 +0100341 if (stsch_err(schid, &schib) || !css_sch_is_valid(&schib)) {
Peter Oberparleiter564337f2006-09-20 16:00:01 +0200342 /* Unusable - ignore. */
343 return 0;
344 }
345 CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, "
346 "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER);
347
348 return css_probe_device(schid);
349}
350
Cornelia Huckc820de32008-07-14 09:58:45 +0200351static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
352{
353 int ret = 0;
354
355 if (sch->driver) {
356 if (sch->driver->sch_event)
357 ret = sch->driver->sch_event(sch, slow);
358 else
359 dev_dbg(&sch->dev,
360 "Got subchannel machine check but "
361 "no sch_event handler provided.\n");
362 }
363 return ret;
364}
365
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200366static void css_evaluate_subchannel(struct subchannel_id schid, int slow)
Peter Oberparleiter564337f2006-09-20 16:00:01 +0200367{
368 struct subchannel *sch;
369 int ret;
370
371 sch = get_subchannel_by_schid(schid);
372 if (sch) {
373 ret = css_evaluate_known_subchannel(sch, slow);
374 put_device(&sch->dev);
375 } else
376 ret = css_evaluate_new_subchannel(schid, slow);
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200377 if (ret == -EAGAIN)
378 css_schedule_eval(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
380
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200381static struct idset *slow_subchannel_set;
382static spinlock_t slow_subchannel_lock;
383
384static int __init slow_subchannel_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385{
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200386 spin_lock_init(&slow_subchannel_lock);
387 slow_subchannel_set = idset_sch_new();
388 if (!slow_subchannel_set) {
Cornelia Hucke556bbb2007-07-27 12:29:19 +0200389 CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n");
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200390 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 }
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200392 return 0;
393}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100395static int slow_eval_known_fn(struct subchannel *sch, void *data)
396{
397 int eval;
398 int rc;
399
400 spin_lock_irq(&slow_subchannel_lock);
401 eval = idset_sch_contains(slow_subchannel_set, sch->schid);
402 idset_sch_del(slow_subchannel_set, sch->schid);
403 spin_unlock_irq(&slow_subchannel_lock);
404 if (eval) {
405 rc = css_evaluate_known_subchannel(sch, 1);
406 if (rc == -EAGAIN)
407 css_schedule_eval(sch->schid);
408 }
409 return 0;
410}
411
412static int slow_eval_unknown_fn(struct subchannel_id schid, void *data)
413{
414 int eval;
415 int rc = 0;
416
417 spin_lock_irq(&slow_subchannel_lock);
418 eval = idset_sch_contains(slow_subchannel_set, schid);
419 idset_sch_del(slow_subchannel_set, schid);
420 spin_unlock_irq(&slow_subchannel_lock);
421 if (eval) {
422 rc = css_evaluate_new_subchannel(schid, 1);
423 switch (rc) {
424 case -EAGAIN:
425 css_schedule_eval(schid);
426 rc = 0;
427 break;
428 case -ENXIO:
429 case -ENOMEM:
430 case -EIO:
431 /* These should abort looping */
432 break;
433 default:
434 rc = 0;
435 }
436 }
437 return rc;
438}
439
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200440static void css_slow_path_func(struct work_struct *unused)
441{
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200442 CIO_TRACE_EVENT(4, "slowpath");
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100443 for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn,
444 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445}
446
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200447static DECLARE_WORK(slow_path_work, css_slow_path_func);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448struct workqueue_struct *slow_path_wq;
449
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200450void css_schedule_eval(struct subchannel_id schid)
451{
452 unsigned long flags;
453
454 spin_lock_irqsave(&slow_subchannel_lock, flags);
455 idset_sch_add(slow_subchannel_set, schid);
456 queue_work(slow_path_wq, &slow_path_work);
457 spin_unlock_irqrestore(&slow_subchannel_lock, flags);
458}
459
460void css_schedule_eval_all(void)
461{
462 unsigned long flags;
463
464 spin_lock_irqsave(&slow_subchannel_lock, flags);
465 idset_fill(slow_subchannel_set);
466 queue_work(slow_path_wq, &slow_path_work);
467 spin_unlock_irqrestore(&slow_subchannel_lock, flags);
468}
469
Cornelia Huck22806dc2008-04-17 07:45:59 +0200470void css_wait_for_slow_path(void)
471{
472 flush_workqueue(ccw_device_notify_work);
473 flush_workqueue(slow_path_wq);
474}
475
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200476/* Reprobe subchannel if unregistered. */
477static int reprobe_subchannel(struct subchannel_id schid, void *data)
478{
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200479 int ret;
480
Cornelia Hucke556bbb2007-07-27 12:29:19 +0200481 CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n",
482 schid.ssid, schid.sch_no);
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200483 if (need_reprobe)
484 return -EAGAIN;
485
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200486 ret = css_probe_device(schid);
487 switch (ret) {
488 case 0:
489 break;
490 case -ENXIO:
491 case -ENOMEM:
Peter Oberparleiter671756162007-12-04 16:09:02 +0100492 case -EIO:
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200493 /* These should abort looping */
494 break;
495 default:
496 ret = 0;
497 }
498
499 return ret;
500}
501
502/* Work function used to reprobe all unregistered subchannels. */
Al Viro4927b3f2006-12-06 19:18:20 +0000503static void reprobe_all(struct work_struct *unused)
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200504{
505 int ret;
506
Michael Ernst139b83d2008-05-07 09:22:54 +0200507 CIO_MSG_EVENT(4, "reprobe start\n");
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200508
509 need_reprobe = 0;
510 /* Make sure initial subchannel scan is done. */
511 wait_event(ccw_device_init_wq,
512 atomic_read(&ccw_device_init_count) == 0);
Peter Oberparleitere82a1562008-01-26 14:10:48 +0100513 ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL);
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200514
Michael Ernst139b83d2008-05-07 09:22:54 +0200515 CIO_MSG_EVENT(4, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200516 need_reprobe);
517}
518
Heiko Carstens2b67fc42007-02-05 21:16:47 +0100519static DECLARE_WORK(css_reprobe_work, reprobe_all);
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200520
521/* Schedule reprobing of all unregistered subchannels. */
522void css_schedule_reprobe(void)
523{
524 need_reprobe = 1;
Cornelia Huckc5d4a992007-11-20 11:13:41 +0100525 queue_work(slow_path_wq, &css_reprobe_work);
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200526}
527
528EXPORT_SYMBOL_GPL(css_schedule_reprobe);
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 * Called from the machine check handler for subchannel report words.
532 */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200533void css_process_crw(int rsid1, int rsid2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534{
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800535 struct subchannel_id mchk_schid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800537 CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
538 rsid1, rsid2);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800539 init_subchannel_id(&mchk_schid);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800540 mchk_schid.sch_no = rsid1;
541 if (rsid2 != 0)
542 mchk_schid.ssid = (rsid2 >> 8) & 3;
543
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 /*
545 * Since we are always presented with IPI in the CRW, we have to
546 * use stsch() to find out if the subchannel in question has come
547 * or gone.
548 */
Peter Oberparleiter83b33702007-04-27 16:01:34 +0200549 css_evaluate_subchannel(mchk_schid, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550}
551
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800552static int __init
553__init_channel_subsystem(struct subchannel_id schid, void *data)
554{
555 struct subchannel *sch;
556 int ret;
557
558 if (cio_is_console(schid))
559 sch = cio_get_console_subchannel();
560 else {
561 sch = css_alloc_subchannel(schid);
562 if (IS_ERR(sch))
563 ret = PTR_ERR(sch);
564 else
565 ret = 0;
566 switch (ret) {
567 case 0:
568 break;
569 case -ENOMEM:
570 panic("Out of memory in init_channel_subsystem\n");
571 /* -ENXIO: no more subchannels. */
572 case -ENXIO:
573 return ret;
Greg Smithe843e282006-03-14 19:50:17 -0800574 /* -EIO: this subchannel set not supported. */
575 case -EIO:
576 return ret;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800577 default:
578 return 0;
579 }
580 }
581 /*
582 * We register ALL valid subchannels in ioinfo, even those
583 * that have been present before init_channel_subsystem.
584 * These subchannels can't have been registered yet (kmalloc
585 * not working) so we do it now. This is true e.g. for the
586 * console subchannel.
587 */
588 css_register_subchannel(sch);
589 return 0;
590}
591
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592static void __init
Cornelia Hucka28c6942006-01-06 00:19:23 -0800593css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
Cornelia Hucka28c6942006-01-06 00:19:23 -0800595 if (css_characteristics_avail && css_general_characteristics.mcss) {
596 css->global_pgid.pgid_high.ext_cssid.version = 0x80;
597 css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
598 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599#ifdef CONFIG_SMP
Cornelia Hucka28c6942006-01-06 00:19:23 -0800600 css->global_pgid.pgid_high.cpu_addr = hard_smp_processor_id();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601#else
Cornelia Hucka28c6942006-01-06 00:19:23 -0800602 css->global_pgid.pgid_high.cpu_addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603#endif
604 }
Cornelia Hucka28c6942006-01-06 00:19:23 -0800605 css->global_pgid.cpu_id = ((cpuid_t *) __LC_CPUID)->ident;
606 css->global_pgid.cpu_model = ((cpuid_t *) __LC_CPUID)->machine;
607 css->global_pgid.tod_high = tod_high;
608
609}
610
Cornelia Huck3b793062006-01-06 00:19:26 -0800611static void
612channel_subsystem_release(struct device *dev)
613{
614 struct channel_subsystem *css;
615
616 css = to_css(dev);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800617 mutex_destroy(&css->mutex);
Cornelia Huck3b793062006-01-06 00:19:26 -0800618 kfree(css);
619}
620
Cornelia Huck495a5b42006-03-24 03:15:14 -0800621static ssize_t
622css_cm_enable_show(struct device *dev, struct device_attribute *attr,
623 char *buf)
624{
625 struct channel_subsystem *css = to_css(dev);
Michael Ernst8284fb192008-04-17 07:46:01 +0200626 int ret;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800627
628 if (!css)
629 return 0;
Michael Ernst8284fb192008-04-17 07:46:01 +0200630 mutex_lock(&css->mutex);
631 ret = sprintf(buf, "%x\n", css->cm_enabled);
632 mutex_unlock(&css->mutex);
633 return ret;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800634}
635
636static ssize_t
637css_cm_enable_store(struct device *dev, struct device_attribute *attr,
638 const char *buf, size_t count)
639{
640 struct channel_subsystem *css = to_css(dev);
641 int ret;
Cornelia Huck2f972202008-04-30 13:38:33 +0200642 unsigned long val;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800643
Cornelia Huck2f972202008-04-30 13:38:33 +0200644 ret = strict_strtoul(buf, 16, &val);
645 if (ret)
646 return ret;
Michael Ernst8284fb192008-04-17 07:46:01 +0200647 mutex_lock(&css->mutex);
Cornelia Huck2f972202008-04-30 13:38:33 +0200648 switch (val) {
649 case 0:
Cornelia Huck495a5b42006-03-24 03:15:14 -0800650 ret = css->cm_enabled ? chsc_secm(css, 0) : 0;
651 break;
Cornelia Huck2f972202008-04-30 13:38:33 +0200652 case 1:
Cornelia Huck495a5b42006-03-24 03:15:14 -0800653 ret = css->cm_enabled ? 0 : chsc_secm(css, 1);
654 break;
655 default:
656 ret = -EINVAL;
657 }
Michael Ernst8284fb192008-04-17 07:46:01 +0200658 mutex_unlock(&css->mutex);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800659 return ret < 0 ? ret : count;
660}
661
662static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store);
663
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100664static int __init setup_css(int nr)
Cornelia Hucka28c6942006-01-06 00:19:23 -0800665{
666 u32 tod_high;
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100667 int ret;
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200668 struct channel_subsystem *css;
Cornelia Hucka28c6942006-01-06 00:19:23 -0800669
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200670 css = channel_subsystems[nr];
671 memset(css, 0, sizeof(struct channel_subsystem));
672 css->pseudo_subchannel =
673 kzalloc(sizeof(*css->pseudo_subchannel), GFP_KERNEL);
674 if (!css->pseudo_subchannel)
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100675 return -ENOMEM;
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200676 css->pseudo_subchannel->dev.parent = &css->device;
677 css->pseudo_subchannel->dev.release = css_subchannel_release;
678 sprintf(css->pseudo_subchannel->dev.bus_id, "defunct");
679 ret = cio_create_sch_lock(css->pseudo_subchannel);
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100680 if (ret) {
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200681 kfree(css->pseudo_subchannel);
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100682 return ret;
683 }
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200684 mutex_init(&css->mutex);
685 css->valid = 1;
686 css->cssid = nr;
687 sprintf(css->device.bus_id, "css%x", nr);
688 css->device.release = channel_subsystem_release;
Cornelia Hucka28c6942006-01-06 00:19:23 -0800689 tod_high = (u32) (get_clock() >> 32);
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200690 css_generate_pgid(css, tod_high);
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100691 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692}
693
Cornelia Hucka55360d2007-10-12 16:11:20 +0200694static int css_reboot_event(struct notifier_block *this,
695 unsigned long event,
696 void *ptr)
697{
698 int ret, i;
699
700 ret = NOTIFY_DONE;
701 for (i = 0; i <= __MAX_CSSID; i++) {
702 struct channel_subsystem *css;
703
704 css = channel_subsystems[i];
Michael Ernst8284fb192008-04-17 07:46:01 +0200705 mutex_lock(&css->mutex);
Cornelia Hucka55360d2007-10-12 16:11:20 +0200706 if (css->cm_enabled)
707 if (chsc_secm(css, 0))
708 ret = NOTIFY_BAD;
Michael Ernst8284fb192008-04-17 07:46:01 +0200709 mutex_unlock(&css->mutex);
Cornelia Hucka55360d2007-10-12 16:11:20 +0200710 }
711
712 return ret;
713}
714
715static struct notifier_block css_reboot_notifier = {
716 .notifier_call = css_reboot_event,
717};
718
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719/*
720 * Now that the driver core is running, we can setup our channel subsystem.
721 * The struct subchannel's are created during probing (except for the
722 * static console subchannel).
723 */
724static int __init
725init_channel_subsystem (void)
726{
Cornelia Hucka28c6942006-01-06 00:19:23 -0800727 int ret, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
Cornelia Huck4434a382007-07-27 12:29:21 +0200729 ret = chsc_determine_css_characteristics();
730 if (ret == -ENOMEM)
731 goto out; /* No need to continue. */
732 if (ret == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 css_characteristics_avail = 1;
734
Cornelia Huck4434a382007-07-27 12:29:21 +0200735 ret = chsc_alloc_sei_area();
736 if (ret)
737 goto out;
738
739 ret = slow_subchannel_init();
740 if (ret)
741 goto out;
742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 if ((ret = bus_register(&css_bus_type)))
744 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800746 /* Try to enable MSS. */
747 ret = chsc_enable_facility(CHSC_SDA_OC_MSS);
748 switch (ret) {
749 case 0: /* Success. */
750 max_ssid = __MAX_SSID;
751 break;
752 case -ENOMEM:
753 goto out_bus;
754 default:
755 max_ssid = 0;
756 }
Cornelia Hucka28c6942006-01-06 00:19:23 -0800757 /* Setup css structure. */
758 for (i = 0; i <= __MAX_CSSID; i++) {
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200759 struct channel_subsystem *css;
760
761 css = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL);
762 if (!css) {
Cornelia Hucka28c6942006-01-06 00:19:23 -0800763 ret = -ENOMEM;
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800764 goto out_unregister;
Cornelia Hucka28c6942006-01-06 00:19:23 -0800765 }
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200766 channel_subsystems[i] = css;
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100767 ret = setup_css(i);
Cornelia Hucka28c6942006-01-06 00:19:23 -0800768 if (ret)
769 goto out_free;
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200770 ret = device_register(&css->device);
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100771 if (ret)
772 goto out_free_all;
Cornelia Huck7e560812006-07-12 16:40:19 +0200773 if (css_characteristics_avail &&
774 css_chsc_characteristics.secm) {
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200775 ret = device_create_file(&css->device,
Cornelia Huck7e560812006-07-12 16:40:19 +0200776 &dev_attr_cm_enable);
777 if (ret)
778 goto out_device;
779 }
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200780 ret = device_register(&css->pseudo_subchannel->dev);
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100781 if (ret)
782 goto out_file;
Cornelia Hucka28c6942006-01-06 00:19:23 -0800783 }
Cornelia Hucka55360d2007-10-12 16:11:20 +0200784 ret = register_reboot_notifier(&css_reboot_notifier);
785 if (ret)
786 goto out_pseudo;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 css_init_done = 1;
788
789 ctl_set_bit(6, 28);
790
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800791 for_each_subchannel(__init_channel_subsystem, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 return 0;
Cornelia Hucka55360d2007-10-12 16:11:20 +0200793out_pseudo:
794 device_unregister(&channel_subsystems[i]->pseudo_subchannel->dev);
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100795out_file:
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200796 device_remove_file(&channel_subsystems[i]->device,
797 &dev_attr_cm_enable);
Cornelia Huck7e560812006-07-12 16:40:19 +0200798out_device:
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200799 device_unregister(&channel_subsystems[i]->device);
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100800out_free_all:
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200801 kfree(channel_subsystems[i]->pseudo_subchannel->lock);
802 kfree(channel_subsystems[i]->pseudo_subchannel);
Cornelia Hucka28c6942006-01-06 00:19:23 -0800803out_free:
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200804 kfree(channel_subsystems[i]);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800805out_unregister:
Cornelia Hucka28c6942006-01-06 00:19:23 -0800806 while (i > 0) {
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200807 struct channel_subsystem *css;
808
Cornelia Hucka28c6942006-01-06 00:19:23 -0800809 i--;
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200810 css = channel_subsystems[i];
811 device_unregister(&css->pseudo_subchannel->dev);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800812 if (css_characteristics_avail && css_chsc_characteristics.secm)
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200813 device_remove_file(&css->device,
Cornelia Huck495a5b42006-03-24 03:15:14 -0800814 &dev_attr_cm_enable);
Cornelia Huck7c9f4e32007-10-12 16:11:13 +0200815 device_unregister(&css->device);
Cornelia Hucka28c6942006-01-06 00:19:23 -0800816 }
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800817out_bus:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 bus_unregister(&css_bus_type);
819out:
Cornelia Huck4434a382007-07-27 12:29:21 +0200820 chsc_free_sei_area();
821 kfree(slow_subchannel_set);
822 printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
823 ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 return ret;
825}
826
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100827int sch_is_pseudo_sch(struct subchannel *sch)
828{
829 return sch == to_css(sch->dev.parent)->pseudo_subchannel;
830}
831
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832/*
833 * find a driver for a subchannel. They identify by the subchannel
834 * type with the exception that the console subchannel driver has its own
835 * subchannel type although the device is an i/o subchannel
836 */
837static int
838css_bus_match (struct device *dev, struct device_driver *drv)
839{
Cornelia Huck084325d2008-01-26 14:10:38 +0100840 struct subchannel *sch = to_subchannel(dev);
841 struct css_driver *driver = to_cssdriver(drv);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
843 if (sch->st == driver->subchannel_type)
844 return 1;
845
846 return 0;
847}
848
Cornelia Huck98c13c22008-01-26 14:10:40 +0100849static int css_probe(struct device *dev)
Cornelia Huck8bbace72006-01-11 10:56:22 +0100850{
851 struct subchannel *sch;
Cornelia Huck98c13c22008-01-26 14:10:40 +0100852 int ret;
Cornelia Huck8bbace72006-01-11 10:56:22 +0100853
854 sch = to_subchannel(dev);
Cornelia Huck084325d2008-01-26 14:10:38 +0100855 sch->driver = to_cssdriver(dev->driver);
Cornelia Huck98c13c22008-01-26 14:10:40 +0100856 ret = sch->driver->probe ? sch->driver->probe(sch) : 0;
857 if (ret)
858 sch->driver = NULL;
859 return ret;
Cornelia Huck8bbace72006-01-11 10:56:22 +0100860}
861
Cornelia Huck98c13c22008-01-26 14:10:40 +0100862static int css_remove(struct device *dev)
863{
864 struct subchannel *sch;
865 int ret;
866
867 sch = to_subchannel(dev);
868 ret = sch->driver->remove ? sch->driver->remove(sch) : 0;
869 sch->driver = NULL;
870 return ret;
871}
872
873static void css_shutdown(struct device *dev)
Cornelia Huck8bbace72006-01-11 10:56:22 +0100874{
875 struct subchannel *sch;
876
877 sch = to_subchannel(dev);
Cornelia Huck98c13c22008-01-26 14:10:40 +0100878 if (sch->driver && sch->driver->shutdown)
Cornelia Huck8bbace72006-01-11 10:56:22 +0100879 sch->driver->shutdown(sch);
880}
881
Cornelia Huck7e9db9e2008-07-14 09:58:44 +0200882static int css_uevent(struct device *dev, struct kobj_uevent_env *env)
883{
884 struct subchannel *sch = to_subchannel(dev);
885 int ret;
886
887 ret = add_uevent_var(env, "ST=%01X", sch->st);
888 if (ret)
889 return ret;
890 ret = add_uevent_var(env, "MODALIAS=css:t%01X", sch->st);
891 return ret;
892}
893
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894struct bus_type css_bus_type = {
Cornelia Huck8bbace72006-01-11 10:56:22 +0100895 .name = "css",
896 .match = css_bus_match,
897 .probe = css_probe,
898 .remove = css_remove,
899 .shutdown = css_shutdown,
Cornelia Huck7e9db9e2008-07-14 09:58:44 +0200900 .uevent = css_uevent,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901};
902
Cornelia Huck25b7bb52008-01-26 14:10:41 +0100903/**
904 * css_driver_register - register a css driver
905 * @cdrv: css driver to register
906 *
907 * This is mainly a wrapper around driver_register that sets name
908 * and bus_type in the embedded struct device_driver correctly.
909 */
910int css_driver_register(struct css_driver *cdrv)
911{
912 cdrv->drv.name = cdrv->name;
913 cdrv->drv.bus = &css_bus_type;
Cornelia Huck4beee642008-01-26 14:10:47 +0100914 cdrv->drv.owner = cdrv->owner;
Cornelia Huck25b7bb52008-01-26 14:10:41 +0100915 return driver_register(&cdrv->drv);
916}
917EXPORT_SYMBOL_GPL(css_driver_register);
918
919/**
920 * css_driver_unregister - unregister a css driver
921 * @cdrv: css driver to unregister
922 *
923 * This is a wrapper around driver_unregister.
924 */
925void css_driver_unregister(struct css_driver *cdrv)
926{
927 driver_unregister(&cdrv->drv);
928}
929EXPORT_SYMBOL_GPL(css_driver_unregister);
930
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931subsys_initcall(init_channel_subsystem);
932
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933MODULE_LICENSE("GPL");
934EXPORT_SYMBOL(css_bus_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935EXPORT_SYMBOL_GPL(css_characteristics_avail);