blob: b6225cbbbee7f24faa9d0a71d1459d0c66da76b9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/s390/cio/css.c
3 * driver for channel subsystem
4 * $Revision: 1.85 $
5 *
6 * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
7 * IBM Corporation
8 * Author(s): Arnd Bergmann (arndb@de.ibm.com)
9 * Cornelia Huck (cohuck@de.ibm.com)
10 */
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/device.h>
14#include <linux/slab.h>
15#include <linux/errno.h>
16#include <linux/list.h>
17
18#include "css.h"
19#include "cio.h"
20#include "cio_debug.h"
21#include "ioasm.h"
22#include "chsc.h"
23
Linus Torvalds1da177e2005-04-16 15:20:36 -070024int need_rescan = 0;
25int css_init_done = 0;
26
Cornelia Hucka28c6942006-01-06 00:19:23 -080027struct channel_subsystem *css[__MAX_CSSID + 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Cornelia Hucka28c6942006-01-06 00:19:23 -080029int css_characteristics_avail = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
Cornelia Huckf97a56f2006-01-06 00:19:22 -080031inline int
32for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
33{
34 struct subchannel_id schid;
35 int ret;
36
37 init_subchannel_id(&schid);
38 ret = -ENODEV;
39 do {
40 ret = fn(schid, data);
41 if (ret)
42 break;
43 } while (schid.sch_no++ < __MAX_SUBCHANNEL);
44 return ret;
45}
46
Linus Torvalds1da177e2005-04-16 15:20:36 -070047static struct subchannel *
Cornelia Hucka8237fc2006-01-06 00:19:21 -080048css_alloc_subchannel(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -070049{
50 struct subchannel *sch;
51 int ret;
52
53 sch = kmalloc (sizeof (*sch), GFP_KERNEL | GFP_DMA);
54 if (sch == NULL)
55 return ERR_PTR(-ENOMEM);
Cornelia Hucka8237fc2006-01-06 00:19:21 -080056 ret = cio_validate_subchannel (sch, schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 if (ret < 0) {
58 kfree(sch);
59 return ERR_PTR(ret);
60 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
62 if (sch->st != SUBCHANNEL_TYPE_IO) {
63 /* For now we ignore all non-io subchannels. */
64 kfree(sch);
65 return ERR_PTR(-EINVAL);
66 }
67
68 /*
69 * Set intparm to subchannel address.
70 * This is fine even on 64bit since the subchannel is always located
71 * under 2G.
72 */
73 sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
74 ret = cio_modify(sch);
75 if (ret) {
76 kfree(sch);
77 return ERR_PTR(ret);
78 }
79 return sch;
80}
81
82static void
83css_free_subchannel(struct subchannel *sch)
84{
85 if (sch) {
86 /* Reset intparm to zeroes. */
87 sch->schib.pmcw.intparm = 0;
88 cio_modify(sch);
89 kfree(sch);
90 }
91
92}
93
94static void
95css_subchannel_release(struct device *dev)
96{
97 struct subchannel *sch;
98
99 sch = to_subchannel(dev);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800100 if (!cio_is_console(sch->schid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 kfree(sch);
102}
103
104extern int css_get_ssd_info(struct subchannel *sch);
105
106static int
107css_register_subchannel(struct subchannel *sch)
108{
109 int ret;
110
111 /* Initialize the subchannel structure */
Cornelia Hucka28c6942006-01-06 00:19:23 -0800112 sch->dev.parent = &css[0]->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 sch->dev.bus = &css_bus_type;
114 sch->dev.release = &css_subchannel_release;
115
116 /* make it known to the system */
117 ret = device_register(&sch->dev);
118 if (ret)
119 printk (KERN_WARNING "%s: could not register %s\n",
120 __func__, sch->dev.bus_id);
121 else
122 css_get_ssd_info(sch);
123 return ret;
124}
125
126int
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800127css_probe_device(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
129 int ret;
130 struct subchannel *sch;
131
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800132 sch = css_alloc_subchannel(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 if (IS_ERR(sch))
134 return PTR_ERR(sch);
135 ret = css_register_subchannel(sch);
136 if (ret)
137 css_free_subchannel(sch);
138 return ret;
139}
140
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700141static int
142check_subchannel(struct device * dev, void * data)
143{
144 struct subchannel *sch;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800145 struct subchannel_id *schid = data;
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700146
147 sch = to_subchannel(dev);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800148 return schid_equal(&sch->schid, schid);
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700149}
150
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151struct subchannel *
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800152get_subchannel_by_schid(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 struct device *dev;
155
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700156 dev = bus_find_device(&css_bus_type, NULL,
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800157 (void *)&schid, check_subchannel);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700159 return dev ? to_subchannel(dev) : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160}
161
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700162
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163static inline int
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800164css_get_subchannel_status(struct subchannel *sch, struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165{
166 struct schib schib;
167 int cc;
168
169 cc = stsch(schid, &schib);
170 if (cc)
171 return CIO_GONE;
172 if (!schib.pmcw.dnv)
173 return CIO_GONE;
174 if (sch && sch->schib.pmcw.dnv &&
175 (schib.pmcw.dev != sch->schib.pmcw.dev))
176 return CIO_REVALIDATE;
177 if (sch && !sch->lpm)
178 return CIO_NO_PATH;
179 return CIO_OPER;
180}
181
182static int
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800183css_evaluate_subchannel(struct subchannel_id schid, int slow)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
185 int event, ret, disc;
186 struct subchannel *sch;
187 unsigned long flags;
188
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800189 sch = get_subchannel_by_schid(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 disc = sch ? device_is_disconnected(sch) : 0;
191 if (disc && slow) {
192 if (sch)
193 put_device(&sch->dev);
194 return 0; /* Already processed. */
195 }
196 /*
197 * We've got a machine check, so running I/O won't get an interrupt.
198 * Kill any pending timers.
199 */
200 if (sch)
201 device_kill_pending_timer(sch);
202 if (!disc && !slow) {
203 if (sch)
204 put_device(&sch->dev);
205 return -EAGAIN; /* Will be done on the slow path. */
206 }
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800207 event = css_get_subchannel_status(sch, schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 CIO_MSG_EVENT(4, "Evaluating schid %04x, event %d, %s, %s path.\n",
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800209 schid.sch_no, event,
210 sch?(disc?"disconnected":"normal"):"unknown",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 slow?"slow":"fast");
212 switch (event) {
213 case CIO_NO_PATH:
214 case CIO_GONE:
215 if (!sch) {
216 /* Never used this subchannel. Ignore. */
217 ret = 0;
218 break;
219 }
220 if (disc && (event == CIO_NO_PATH)) {
221 /*
222 * Uargh, hack again. Because we don't get a machine
223 * check on configure on, our path bookkeeping can
224 * be out of date here (it's fine while we only do
225 * logical varying or get chsc machine checks). We
226 * need to force reprobing or we might miss devices
227 * coming operational again. It won't do harm in real
228 * no path situations.
229 */
230 spin_lock_irqsave(&sch->lock, flags);
231 device_trigger_reprobe(sch);
232 spin_unlock_irqrestore(&sch->lock, flags);
233 ret = 0;
234 break;
235 }
236 if (sch->driver && sch->driver->notify &&
237 sch->driver->notify(&sch->dev, event)) {
238 cio_disable_subchannel(sch);
239 device_set_disconnected(sch);
240 ret = 0;
241 break;
242 }
243 /*
244 * Unregister subchannel.
245 * The device will be killed automatically.
246 */
247 cio_disable_subchannel(sch);
248 device_unregister(&sch->dev);
249 /* Reset intparm to zeroes. */
250 sch->schib.pmcw.intparm = 0;
251 cio_modify(sch);
252 put_device(&sch->dev);
253 ret = 0;
254 break;
255 case CIO_REVALIDATE:
256 /*
257 * Revalidation machine check. Sick.
258 * We don't notify the driver since we have to throw the device
259 * away in any case.
260 */
261 if (!disc) {
262 device_unregister(&sch->dev);
263 /* Reset intparm to zeroes. */
264 sch->schib.pmcw.intparm = 0;
265 cio_modify(sch);
266 put_device(&sch->dev);
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800267 ret = css_probe_device(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 } else {
269 /*
270 * We can't immediately deregister the disconnected
271 * device since it might block.
272 */
273 spin_lock_irqsave(&sch->lock, flags);
274 device_trigger_reprobe(sch);
275 spin_unlock_irqrestore(&sch->lock, flags);
276 ret = 0;
277 }
278 break;
279 case CIO_OPER:
280 if (disc) {
281 spin_lock_irqsave(&sch->lock, flags);
282 /* Get device operational again. */
283 device_trigger_reprobe(sch);
284 spin_unlock_irqrestore(&sch->lock, flags);
285 }
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800286 ret = sch ? 0 : css_probe_device(schid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 break;
288 default:
289 BUG();
290 ret = 0;
291 }
292 return ret;
293}
294
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800295static int
296css_rescan_devices(struct subchannel_id schid, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297{
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800298 return css_evaluate_subchannel(schid, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299}
300
301struct slow_subchannel {
302 struct list_head slow_list;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800303 struct subchannel_id schid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304};
305
306static LIST_HEAD(slow_subchannels_head);
307static DEFINE_SPINLOCK(slow_subchannel_lock);
308
309static void
310css_trigger_slow_path(void)
311{
312 CIO_TRACE_EVENT(4, "slowpath");
313
314 if (need_rescan) {
315 need_rescan = 0;
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800316 for_each_subchannel(css_rescan_devices, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 return;
318 }
319
320 spin_lock_irq(&slow_subchannel_lock);
321 while (!list_empty(&slow_subchannels_head)) {
322 struct slow_subchannel *slow_sch =
323 list_entry(slow_subchannels_head.next,
324 struct slow_subchannel, slow_list);
325
326 list_del_init(slow_subchannels_head.next);
327 spin_unlock_irq(&slow_subchannel_lock);
328 css_evaluate_subchannel(slow_sch->schid, 1);
329 spin_lock_irq(&slow_subchannel_lock);
330 kfree(slow_sch);
331 }
332 spin_unlock_irq(&slow_subchannel_lock);
333}
334
335typedef void (*workfunc)(void *);
336DECLARE_WORK(slow_path_work, (workfunc)css_trigger_slow_path, NULL);
337struct workqueue_struct *slow_path_wq;
338
339/*
340 * Rescan for new devices. FIXME: This is slow.
341 * This function is called when we have lost CRWs due to overflows and we have
342 * to do subchannel housekeeping.
343 */
344void
345css_reiterate_subchannels(void)
346{
347 css_clear_subchannel_slow_list();
348 need_rescan = 1;
349}
350
351/*
352 * Called from the machine check handler for subchannel report words.
353 */
354int
355css_process_crw(int irq)
356{
357 int ret;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800358 struct subchannel_id mchk_schid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
360 CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq);
361
362 if (need_rescan)
363 /* We need to iterate all subchannels anyway. */
364 return -EAGAIN;
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800365
366 init_subchannel_id(&mchk_schid);
367 mchk_schid.sch_no = irq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 /*
369 * Since we are always presented with IPI in the CRW, we have to
370 * use stsch() to find out if the subchannel in question has come
371 * or gone.
372 */
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800373 ret = css_evaluate_subchannel(mchk_schid, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 if (ret == -EAGAIN) {
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800375 if (css_enqueue_subchannel_slow(mchk_schid)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 css_clear_subchannel_slow_list();
377 need_rescan = 1;
378 }
379 }
380 return ret;
381}
382
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800383static int __init
384__init_channel_subsystem(struct subchannel_id schid, void *data)
385{
386 struct subchannel *sch;
387 int ret;
388
389 if (cio_is_console(schid))
390 sch = cio_get_console_subchannel();
391 else {
392 sch = css_alloc_subchannel(schid);
393 if (IS_ERR(sch))
394 ret = PTR_ERR(sch);
395 else
396 ret = 0;
397 switch (ret) {
398 case 0:
399 break;
400 case -ENOMEM:
401 panic("Out of memory in init_channel_subsystem\n");
402 /* -ENXIO: no more subchannels. */
403 case -ENXIO:
404 return ret;
405 default:
406 return 0;
407 }
408 }
409 /*
410 * We register ALL valid subchannels in ioinfo, even those
411 * that have been present before init_channel_subsystem.
412 * These subchannels can't have been registered yet (kmalloc
413 * not working) so we do it now. This is true e.g. for the
414 * console subchannel.
415 */
416 css_register_subchannel(sch);
417 return 0;
418}
419
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420static void __init
Cornelia Hucka28c6942006-01-06 00:19:23 -0800421css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422{
Cornelia Hucka28c6942006-01-06 00:19:23 -0800423 if (css_characteristics_avail && css_general_characteristics.mcss) {
424 css->global_pgid.pgid_high.ext_cssid.version = 0x80;
425 css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
426 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427#ifdef CONFIG_SMP
Cornelia Hucka28c6942006-01-06 00:19:23 -0800428 css->global_pgid.pgid_high.cpu_addr = hard_smp_processor_id();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429#else
Cornelia Hucka28c6942006-01-06 00:19:23 -0800430 css->global_pgid.pgid_high.cpu_addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431#endif
432 }
Cornelia Hucka28c6942006-01-06 00:19:23 -0800433 css->global_pgid.cpu_id = ((cpuid_t *) __LC_CPUID)->ident;
434 css->global_pgid.cpu_model = ((cpuid_t *) __LC_CPUID)->machine;
435 css->global_pgid.tod_high = tod_high;
436
437}
438
439static inline void __init
440setup_css(int nr)
441{
442 u32 tod_high;
443
444 memset(css[nr], 0, sizeof(struct channel_subsystem));
445 css[nr]->valid = 1;
446 css[nr]->cssid = nr;
447 sprintf(css[nr]->device.bus_id, "css%x", nr);
448 tod_high = (u32) (get_clock() >> 32);
449 css_generate_pgid(css[nr], tod_high);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450}
451
452/*
453 * Now that the driver core is running, we can setup our channel subsystem.
454 * The struct subchannel's are created during probing (except for the
455 * static console subchannel).
456 */
457static int __init
458init_channel_subsystem (void)
459{
Cornelia Hucka28c6942006-01-06 00:19:23 -0800460 int ret, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
462 if (chsc_determine_css_characteristics() == 0)
463 css_characteristics_avail = 1;
464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 if ((ret = bus_register(&css_bus_type)))
466 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
Cornelia Hucka28c6942006-01-06 00:19:23 -0800468 /* Setup css structure. */
469 for (i = 0; i <= __MAX_CSSID; i++) {
470 css[i] = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL);
471 if (!css[i]) {
472 ret = -ENOMEM;
473 goto out_bus;
474 }
475 setup_css(i);
476 ret = device_register(&css[i]->device);
477 if (ret)
478 goto out_free;
479 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 css_init_done = 1;
481
482 ctl_set_bit(6, 28);
483
Cornelia Huckf97a56f2006-01-06 00:19:22 -0800484 for_each_subchannel(__init_channel_subsystem, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 return 0;
Cornelia Hucka28c6942006-01-06 00:19:23 -0800486out_free:
487 kfree(css[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488out_bus:
Cornelia Hucka28c6942006-01-06 00:19:23 -0800489 while (i > 0) {
490 i--;
491 device_unregister(&css[i]->device);
492 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 bus_unregister(&css_bus_type);
494out:
495 return ret;
496}
497
498/*
499 * find a driver for a subchannel. They identify by the subchannel
500 * type with the exception that the console subchannel driver has its own
501 * subchannel type although the device is an i/o subchannel
502 */
503static int
504css_bus_match (struct device *dev, struct device_driver *drv)
505{
506 struct subchannel *sch = container_of (dev, struct subchannel, dev);
507 struct css_driver *driver = container_of (drv, struct css_driver, drv);
508
509 if (sch->st == driver->subchannel_type)
510 return 1;
511
512 return 0;
513}
514
515struct bus_type css_bus_type = {
516 .name = "css",
517 .match = &css_bus_match,
518};
519
520subsys_initcall(init_channel_subsystem);
521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522int
Cornelia Hucka8237fc2006-01-06 00:19:21 -0800523css_enqueue_subchannel_slow(struct subchannel_id schid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524{
525 struct slow_subchannel *new_slow_sch;
526 unsigned long flags;
527
528 new_slow_sch = kmalloc(sizeof(struct slow_subchannel), GFP_ATOMIC);
529 if (!new_slow_sch)
530 return -ENOMEM;
531 memset(new_slow_sch, 0, sizeof(struct slow_subchannel));
532 new_slow_sch->schid = schid;
533 spin_lock_irqsave(&slow_subchannel_lock, flags);
534 list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head);
535 spin_unlock_irqrestore(&slow_subchannel_lock, flags);
536 return 0;
537}
538
539void
540css_clear_subchannel_slow_list(void)
541{
542 unsigned long flags;
543
544 spin_lock_irqsave(&slow_subchannel_lock, flags);
545 while (!list_empty(&slow_subchannels_head)) {
546 struct slow_subchannel *slow_sch =
547 list_entry(slow_subchannels_head.next,
548 struct slow_subchannel, slow_list);
549
550 list_del_init(slow_subchannels_head.next);
551 kfree(slow_sch);
552 }
553 spin_unlock_irqrestore(&slow_subchannel_lock, flags);
554}
555
556
557
558int
559css_slow_subchannels_exist(void)
560{
561 return (!list_empty(&slow_subchannels_head));
562}
563
564MODULE_LICENSE("GPL");
565EXPORT_SYMBOL(css_bus_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566EXPORT_SYMBOL_GPL(css_characteristics_avail);