blob: 1d3be80797f81e1ed119a08394f872cc20700236 [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 *
5 * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
6 * IBM Corporation
7 * Author(s): Arnd Bergmann (arndb@de.ibm.com)
Cornelia Huck4ce3b302006-01-14 13:21:04 -08008 * Cornelia Huck (cornelia.huck@de.ibm.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
10#include <linux/module.h>
11#include <linux/init.h>
12#include <linux/device.h>
13#include <linux/slab.h>
14#include <linux/errno.h>
15#include <linux/list.h>
16
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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
Linus Torvalds1da177e2005-04-16 15:20:36 -070024int need_rescan = 0;
25int css_init_done = 0;
Peter Oberparleiter40154b82006-06-29 14:57:03 +020026static int need_reprobe = 0;
Cornelia Huckfb6958a2006-01-06 00:19:25 -080027static int max_ssid = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Cornelia Hucka28c6942006-01-06 00:19:23 -080029struct channel_subsystem *css[__MAX_CSSID + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
Cornelia Hucka28c6942006-01-06 00:19:23 -080031int css_characteristics_avail = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Cornelia Huckf97a56f2006-01-06 00:19:22 -080033inline int
34for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
35{
36 struct subchannel_id schid;
37 int ret;
38
39 init_subchannel_id(&schid);
40 ret = -ENODEV;
41 do {
Cornelia Huckfb6958a2006-01-06 00:19:25 -080042 do {
43 ret = fn(schid, data);
44 if (ret)
45 break;
46 } while (schid.sch_no++ < __MAX_SUBCHANNEL);
47 schid.sch_no = 0;
48 } while (schid.ssid++ < max_ssid);
Cornelia Huckf97a56f2006-01-06 00:19:22 -080049 return ret;
50}
51
Linus Torvalds1da177e2005-04-16 15:20:36 -070052static struct subchannel *
Cornelia Hucka8237fc2006-01-06 00:19:21 -080053css_alloc_subchannel(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -070054{
55 struct subchannel *sch;
56 int ret;
57
58 sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA);
59 if (sch == NULL)
60 return ERR_PTR(-ENOMEM);
Cornelia Hucka8237fc2006-01-06 00:19:21 -080061 ret = cio_validate_subchannel (sch, schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 if (ret < 0) {
63 kfree(sch);
64 return ERR_PTR(ret);
65 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
67 if (sch->st != SUBCHANNEL_TYPE_IO) {
68 /* For now we ignore all non-io subchannels. */
69 kfree(sch);
70 return ERR_PTR(-EINVAL);
71 }
72
73 /*
74 * Set intparm to subchannel address.
75 * This is fine even on 64bit since the subchannel is always located
76 * under 2G.
77 */
78 sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
79 ret = cio_modify(sch);
80 if (ret) {
81 kfree(sch);
82 return ERR_PTR(ret);
83 }
84 return sch;
85}
86
87static void
88css_free_subchannel(struct subchannel *sch)
89{
90 if (sch) {
91 /* Reset intparm to zeroes. */
92 sch->schib.pmcw.intparm = 0;
93 cio_modify(sch);
94 kfree(sch);
95 }
96
97}
98
99static void
100css_subchannel_release(struct device *dev)
101{
102 struct subchannel *sch;
103
104 sch = to_subchannel(dev);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800105 if (!cio_is_console(sch->schid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 kfree(sch);
107}
108
109extern int css_get_ssd_info(struct subchannel *sch);
110
111static int
112css_register_subchannel(struct subchannel *sch)
113{
114 int ret;
115
116 /* Initialize the subchannel structure */
Cornelia Hucka28c6942006-01-06 00:19:23 -0800117 sch->dev.parent = &css[0]->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 sch->dev.bus = &css_bus_type;
119 sch->dev.release = &css_subchannel_release;
120
121 /* make it known to the system */
122 ret = device_register(&sch->dev);
123 if (ret)
124 printk (KERN_WARNING "%s: could not register %s\n",
125 __func__, sch->dev.bus_id);
126 else
127 css_get_ssd_info(sch);
128 return ret;
129}
130
131int
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800132css_probe_device(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133{
134 int ret;
135 struct subchannel *sch;
136
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800137 sch = css_alloc_subchannel(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 if (IS_ERR(sch))
139 return PTR_ERR(sch);
140 ret = css_register_subchannel(sch);
141 if (ret)
142 css_free_subchannel(sch);
143 return ret;
144}
145
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700146static int
147check_subchannel(struct device * dev, void * data)
148{
149 struct subchannel *sch;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800150 struct subchannel_id *schid = data;
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700151
152 sch = to_subchannel(dev);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800153 return schid_equal(&sch->schid, schid);
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700154}
155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156struct subchannel *
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800157get_subchannel_by_schid(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 struct device *dev;
160
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700161 dev = bus_find_device(&css_bus_type, NULL,
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800162 (void *)&schid, check_subchannel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700164 return dev ? to_subchannel(dev) : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165}
166
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700167
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168static inline int
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800169css_get_subchannel_status(struct subchannel *sch, struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170{
171 struct schib schib;
172 int cc;
173
174 cc = stsch(schid, &schib);
175 if (cc)
176 return CIO_GONE;
177 if (!schib.pmcw.dnv)
178 return CIO_GONE;
179 if (sch && sch->schib.pmcw.dnv &&
180 (schib.pmcw.dev != sch->schib.pmcw.dev))
181 return CIO_REVALIDATE;
182 if (sch && !sch->lpm)
183 return CIO_NO_PATH;
184 return CIO_OPER;
185}
186
187static int
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800188css_evaluate_subchannel(struct subchannel_id schid, int slow)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
190 int event, ret, disc;
191 struct subchannel *sch;
192 unsigned long flags;
193
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800194 sch = get_subchannel_by_schid(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 disc = sch ? device_is_disconnected(sch) : 0;
196 if (disc && slow) {
197 if (sch)
198 put_device(&sch->dev);
199 return 0; /* Already processed. */
200 }
201 /*
202 * We've got a machine check, so running I/O won't get an interrupt.
203 * Kill any pending timers.
204 */
205 if (sch)
206 device_kill_pending_timer(sch);
207 if (!disc && !slow) {
208 if (sch)
209 put_device(&sch->dev);
210 return -EAGAIN; /* Will be done on the slow path. */
211 }
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800212 event = css_get_subchannel_status(sch, schid);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800213 CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
214 schid.ssid, schid.sch_no, event,
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800215 sch?(disc?"disconnected":"normal"):"unknown",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 slow?"slow":"fast");
217 switch (event) {
218 case CIO_NO_PATH:
219 case CIO_GONE:
220 if (!sch) {
221 /* Never used this subchannel. Ignore. */
222 ret = 0;
223 break;
224 }
225 if (disc && (event == CIO_NO_PATH)) {
226 /*
227 * Uargh, hack again. Because we don't get a machine
228 * check on configure on, our path bookkeeping can
229 * be out of date here (it's fine while we only do
230 * logical varying or get chsc machine checks). We
231 * need to force reprobing or we might miss devices
232 * coming operational again. It won't do harm in real
233 * no path situations.
234 */
235 spin_lock_irqsave(&sch->lock, flags);
236 device_trigger_reprobe(sch);
237 spin_unlock_irqrestore(&sch->lock, flags);
238 ret = 0;
239 break;
240 }
241 if (sch->driver && sch->driver->notify &&
242 sch->driver->notify(&sch->dev, event)) {
243 cio_disable_subchannel(sch);
244 device_set_disconnected(sch);
245 ret = 0;
246 break;
247 }
248 /*
249 * Unregister subchannel.
250 * The device will be killed automatically.
251 */
252 cio_disable_subchannel(sch);
253 device_unregister(&sch->dev);
254 /* Reset intparm to zeroes. */
255 sch->schib.pmcw.intparm = 0;
256 cio_modify(sch);
257 put_device(&sch->dev);
258 ret = 0;
259 break;
260 case CIO_REVALIDATE:
261 /*
262 * Revalidation machine check. Sick.
263 * We don't notify the driver since we have to throw the device
264 * away in any case.
265 */
266 if (!disc) {
267 device_unregister(&sch->dev);
268 /* Reset intparm to zeroes. */
269 sch->schib.pmcw.intparm = 0;
270 cio_modify(sch);
271 put_device(&sch->dev);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800272 ret = css_probe_device(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 } else {
274 /*
275 * We can't immediately deregister the disconnected
276 * device since it might block.
277 */
278 spin_lock_irqsave(&sch->lock, flags);
279 device_trigger_reprobe(sch);
280 spin_unlock_irqrestore(&sch->lock, flags);
281 ret = 0;
282 }
283 break;
284 case CIO_OPER:
285 if (disc) {
286 spin_lock_irqsave(&sch->lock, flags);
287 /* Get device operational again. */
288 device_trigger_reprobe(sch);
289 spin_unlock_irqrestore(&sch->lock, flags);
290 }
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800291 ret = sch ? 0 : css_probe_device(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 break;
293 default:
294 BUG();
295 ret = 0;
296 }
297 return ret;
298}
299
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800300static int
301css_rescan_devices(struct subchannel_id schid, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800303 return css_evaluate_subchannel(schid, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304}
305
306struct slow_subchannel {
307 struct list_head slow_list;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800308 struct subchannel_id schid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309};
310
311static LIST_HEAD(slow_subchannels_head);
312static DEFINE_SPINLOCK(slow_subchannel_lock);
313
314static void
315css_trigger_slow_path(void)
316{
317 CIO_TRACE_EVENT(4, "slowpath");
318
319 if (need_rescan) {
320 need_rescan = 0;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800321 for_each_subchannel(css_rescan_devices, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 return;
323 }
324
325 spin_lock_irq(&slow_subchannel_lock);
326 while (!list_empty(&slow_subchannels_head)) {
327 struct slow_subchannel *slow_sch =
328 list_entry(slow_subchannels_head.next,
329 struct slow_subchannel, slow_list);
330
331 list_del_init(slow_subchannels_head.next);
332 spin_unlock_irq(&slow_subchannel_lock);
333 css_evaluate_subchannel(slow_sch->schid, 1);
334 spin_lock_irq(&slow_subchannel_lock);
335 kfree(slow_sch);
336 }
337 spin_unlock_irq(&slow_subchannel_lock);
338}
339
340typedef void (*workfunc)(void *);
341DECLARE_WORK(slow_path_work, (workfunc)css_trigger_slow_path, NULL);
342struct workqueue_struct *slow_path_wq;
343
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200344/* Reprobe subchannel if unregistered. */
345static int reprobe_subchannel(struct subchannel_id schid, void *data)
346{
347 struct subchannel *sch;
348 int ret;
349
350 CIO_DEBUG(KERN_INFO, 6, "cio: reprobe 0.%x.%04x\n",
351 schid.ssid, schid.sch_no);
352 if (need_reprobe)
353 return -EAGAIN;
354
355 sch = get_subchannel_by_schid(schid);
356 if (sch) {
357 /* Already known. */
358 put_device(&sch->dev);
359 return 0;
360 }
361
362 ret = css_probe_device(schid);
363 switch (ret) {
364 case 0:
365 break;
366 case -ENXIO:
367 case -ENOMEM:
368 /* These should abort looping */
369 break;
370 default:
371 ret = 0;
372 }
373
374 return ret;
375}
376
377/* Work function used to reprobe all unregistered subchannels. */
378static void reprobe_all(void *data)
379{
380 int ret;
381
382 CIO_MSG_EVENT(2, "reprobe start\n");
383
384 need_reprobe = 0;
385 /* Make sure initial subchannel scan is done. */
386 wait_event(ccw_device_init_wq,
387 atomic_read(&ccw_device_init_count) == 0);
388 ret = for_each_subchannel(reprobe_subchannel, NULL);
389
390 CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
391 need_reprobe);
392}
393
394DECLARE_WORK(css_reprobe_work, reprobe_all, NULL);
395
396/* Schedule reprobing of all unregistered subchannels. */
397void css_schedule_reprobe(void)
398{
399 need_reprobe = 1;
400 queue_work(ccw_device_work, &css_reprobe_work);
401}
402
403EXPORT_SYMBOL_GPL(css_schedule_reprobe);
404
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405/*
406 * Rescan for new devices. FIXME: This is slow.
407 * This function is called when we have lost CRWs due to overflows and we have
408 * to do subchannel housekeeping.
409 */
410void
411css_reiterate_subchannels(void)
412{
413 css_clear_subchannel_slow_list();
414 need_rescan = 1;
415}
416
417/*
418 * Called from the machine check handler for subchannel report words.
419 */
420int
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800421css_process_crw(int rsid1, int rsid2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422{
423 int ret;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800424 struct subchannel_id mchk_schid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800426 CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
427 rsid1, rsid2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
429 if (need_rescan)
430 /* We need to iterate all subchannels anyway. */
431 return -EAGAIN;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800432
433 init_subchannel_id(&mchk_schid);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800434 mchk_schid.sch_no = rsid1;
435 if (rsid2 != 0)
436 mchk_schid.ssid = (rsid2 >> 8) & 3;
437
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 /*
439 * Since we are always presented with IPI in the CRW, we have to
440 * use stsch() to find out if the subchannel in question has come
441 * or gone.
442 */
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800443 ret = css_evaluate_subchannel(mchk_schid, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 if (ret == -EAGAIN) {
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800445 if (css_enqueue_subchannel_slow(mchk_schid)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 css_clear_subchannel_slow_list();
447 need_rescan = 1;
448 }
449 }
450 return ret;
451}
452
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800453static int __init
454__init_channel_subsystem(struct subchannel_id schid, void *data)
455{
456 struct subchannel *sch;
457 int ret;
458
459 if (cio_is_console(schid))
460 sch = cio_get_console_subchannel();
461 else {
462 sch = css_alloc_subchannel(schid);
463 if (IS_ERR(sch))
464 ret = PTR_ERR(sch);
465 else
466 ret = 0;
467 switch (ret) {
468 case 0:
469 break;
470 case -ENOMEM:
471 panic("Out of memory in init_channel_subsystem\n");
472 /* -ENXIO: no more subchannels. */
473 case -ENXIO:
474 return ret;
Greg Smithe843e282006-03-14 19:50:17 -0800475 /* -EIO: this subchannel set not supported. */
476 case -EIO:
477 return ret;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800478 default:
479 return 0;
480 }
481 }
482 /*
483 * We register ALL valid subchannels in ioinfo, even those
484 * that have been present before init_channel_subsystem.
485 * These subchannels can't have been registered yet (kmalloc
486 * not working) so we do it now. This is true e.g. for the
487 * console subchannel.
488 */
489 css_register_subchannel(sch);
490 return 0;
491}
492
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493static void __init
Cornelia Hucka28c6942006-01-06 00:19:23 -0800494css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495{
Cornelia Hucka28c6942006-01-06 00:19:23 -0800496 if (css_characteristics_avail && css_general_characteristics.mcss) {
497 css->global_pgid.pgid_high.ext_cssid.version = 0x80;
498 css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
499 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500#ifdef CONFIG_SMP
Cornelia Hucka28c6942006-01-06 00:19:23 -0800501 css->global_pgid.pgid_high.cpu_addr = hard_smp_processor_id();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502#else
Cornelia Hucka28c6942006-01-06 00:19:23 -0800503 css->global_pgid.pgid_high.cpu_addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504#endif
505 }
Cornelia Hucka28c6942006-01-06 00:19:23 -0800506 css->global_pgid.cpu_id = ((cpuid_t *) __LC_CPUID)->ident;
507 css->global_pgid.cpu_model = ((cpuid_t *) __LC_CPUID)->machine;
508 css->global_pgid.tod_high = tod_high;
509
510}
511
Cornelia Huck3b793062006-01-06 00:19:26 -0800512static void
513channel_subsystem_release(struct device *dev)
514{
515 struct channel_subsystem *css;
516
517 css = to_css(dev);
Cornelia Huck495a5b42006-03-24 03:15:14 -0800518 mutex_destroy(&css->mutex);
Cornelia Huck3b793062006-01-06 00:19:26 -0800519 kfree(css);
520}
521
Cornelia Huck495a5b42006-03-24 03:15:14 -0800522static ssize_t
523css_cm_enable_show(struct device *dev, struct device_attribute *attr,
524 char *buf)
525{
526 struct channel_subsystem *css = to_css(dev);
527
528 if (!css)
529 return 0;
530 return sprintf(buf, "%x\n", css->cm_enabled);
531}
532
533static ssize_t
534css_cm_enable_store(struct device *dev, struct device_attribute *attr,
535 const char *buf, size_t count)
536{
537 struct channel_subsystem *css = to_css(dev);
538 int ret;
539
540 switch (buf[0]) {
541 case '0':
542 ret = css->cm_enabled ? chsc_secm(css, 0) : 0;
543 break;
544 case '1':
545 ret = css->cm_enabled ? 0 : chsc_secm(css, 1);
546 break;
547 default:
548 ret = -EINVAL;
549 }
550 return ret < 0 ? ret : count;
551}
552
553static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store);
554
Cornelia Hucka28c6942006-01-06 00:19:23 -0800555static inline void __init
556setup_css(int nr)
557{
558 u32 tod_high;
559
560 memset(css[nr], 0, sizeof(struct channel_subsystem));
Cornelia Huck495a5b42006-03-24 03:15:14 -0800561 mutex_init(&css[nr]->mutex);
Cornelia Hucka28c6942006-01-06 00:19:23 -0800562 css[nr]->valid = 1;
563 css[nr]->cssid = nr;
564 sprintf(css[nr]->device.bus_id, "css%x", nr);
Cornelia Huck3b793062006-01-06 00:19:26 -0800565 css[nr]->device.release = channel_subsystem_release;
Cornelia Hucka28c6942006-01-06 00:19:23 -0800566 tod_high = (u32) (get_clock() >> 32);
567 css_generate_pgid(css[nr], tod_high);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568}
569
570/*
571 * Now that the driver core is running, we can setup our channel subsystem.
572 * The struct subchannel's are created during probing (except for the
573 * static console subchannel).
574 */
575static int __init
576init_channel_subsystem (void)
577{
Cornelia Hucka28c6942006-01-06 00:19:23 -0800578 int ret, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
580 if (chsc_determine_css_characteristics() == 0)
581 css_characteristics_avail = 1;
582
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 if ((ret = bus_register(&css_bus_type)))
584 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800586 /* Try to enable MSS. */
587 ret = chsc_enable_facility(CHSC_SDA_OC_MSS);
588 switch (ret) {
589 case 0: /* Success. */
590 max_ssid = __MAX_SSID;
591 break;
592 case -ENOMEM:
593 goto out_bus;
594 default:
595 max_ssid = 0;
596 }
Cornelia Hucka28c6942006-01-06 00:19:23 -0800597 /* Setup css structure. */
598 for (i = 0; i <= __MAX_CSSID; i++) {
599 css[i] = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL);
600 if (!css[i]) {
601 ret = -ENOMEM;
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800602 goto out_unregister;
Cornelia Hucka28c6942006-01-06 00:19:23 -0800603 }
604 setup_css(i);
605 ret = device_register(&css[i]->device);
606 if (ret)
607 goto out_free;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800608 if (css_characteristics_avail && css_chsc_characteristics.secm)
609 device_create_file(&css[i]->device,
610 &dev_attr_cm_enable);
Cornelia Hucka28c6942006-01-06 00:19:23 -0800611 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 css_init_done = 1;
613
614 ctl_set_bit(6, 28);
615
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800616 for_each_subchannel(__init_channel_subsystem, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 return 0;
Cornelia Hucka28c6942006-01-06 00:19:23 -0800618out_free:
619 kfree(css[i]);
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800620out_unregister:
Cornelia Hucka28c6942006-01-06 00:19:23 -0800621 while (i > 0) {
622 i--;
Cornelia Huck495a5b42006-03-24 03:15:14 -0800623 if (css_characteristics_avail && css_chsc_characteristics.secm)
624 device_remove_file(&css[i]->device,
625 &dev_attr_cm_enable);
Cornelia Hucka28c6942006-01-06 00:19:23 -0800626 device_unregister(&css[i]->device);
627 }
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800628out_bus:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 bus_unregister(&css_bus_type);
630out:
631 return ret;
632}
633
634/*
635 * find a driver for a subchannel. They identify by the subchannel
636 * type with the exception that the console subchannel driver has its own
637 * subchannel type although the device is an i/o subchannel
638 */
639static int
640css_bus_match (struct device *dev, struct device_driver *drv)
641{
642 struct subchannel *sch = container_of (dev, struct subchannel, dev);
643 struct css_driver *driver = container_of (drv, struct css_driver, drv);
644
645 if (sch->st == driver->subchannel_type)
646 return 1;
647
648 return 0;
649}
650
Cornelia Huck8bbace72006-01-11 10:56:22 +0100651static int
652css_probe (struct device *dev)
653{
654 struct subchannel *sch;
655
656 sch = to_subchannel(dev);
657 sch->driver = container_of (dev->driver, struct css_driver, drv);
658 return (sch->driver->probe ? sch->driver->probe(sch) : 0);
659}
660
661static int
662css_remove (struct device *dev)
663{
664 struct subchannel *sch;
665
666 sch = to_subchannel(dev);
667 return (sch->driver->remove ? sch->driver->remove(sch) : 0);
668}
669
670static void
671css_shutdown (struct device *dev)
672{
673 struct subchannel *sch;
674
675 sch = to_subchannel(dev);
676 if (sch->driver->shutdown)
677 sch->driver->shutdown(sch);
678}
679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680struct bus_type css_bus_type = {
Cornelia Huck8bbace72006-01-11 10:56:22 +0100681 .name = "css",
682 .match = css_bus_match,
683 .probe = css_probe,
684 .remove = css_remove,
685 .shutdown = css_shutdown,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686};
687
688subsys_initcall(init_channel_subsystem);
689
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690int
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800691css_enqueue_subchannel_slow(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692{
693 struct slow_subchannel *new_slow_sch;
694 unsigned long flags;
695
Eric Sesterhenn88abaab2006-03-24 03:15:31 -0800696 new_slow_sch = kzalloc(sizeof(struct slow_subchannel), GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 if (!new_slow_sch)
698 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 new_slow_sch->schid = schid;
700 spin_lock_irqsave(&slow_subchannel_lock, flags);
701 list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head);
702 spin_unlock_irqrestore(&slow_subchannel_lock, flags);
703 return 0;
704}
705
706void
707css_clear_subchannel_slow_list(void)
708{
709 unsigned long flags;
710
711 spin_lock_irqsave(&slow_subchannel_lock, flags);
712 while (!list_empty(&slow_subchannels_head)) {
713 struct slow_subchannel *slow_sch =
714 list_entry(slow_subchannels_head.next,
715 struct slow_subchannel, slow_list);
716
717 list_del_init(slow_subchannels_head.next);
718 kfree(slow_sch);
719 }
720 spin_unlock_irqrestore(&slow_subchannel_lock, flags);
721}
722
723
724
725int
726css_slow_subchannels_exist(void)
727{
728 return (!list_empty(&slow_subchannels_head));
729}
730
731MODULE_LICENSE("GPL");
732EXPORT_SYMBOL(css_bus_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733EXPORT_SYMBOL_GPL(css_characteristics_avail);