blob: c3fc205e3bc08e40a88370f6ba9958f9ea009f8c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/s390/cio/device.c
3 * bus driver for ccw devices
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 * Martin Schwidefsky (schwidefsky@de.ibm.com)
10 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/spinlock.h>
14#include <linux/errno.h>
15#include <linux/err.h>
16#include <linux/slab.h>
17#include <linux/list.h>
18#include <linux/device.h>
19#include <linux/workqueue.h>
20
21#include <asm/ccwdev.h>
22#include <asm/cio.h>
Tim Schmielau4e57b682005-10-30 15:03:48 -080023#include <asm/param.h> /* HZ */
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
25#include "cio.h"
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +010026#include "cio_debug.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include "css.h"
28#include "device.h"
29#include "ioasm.h"
30
31/******************* bus type handling ***********************/
32
33/* The Linux driver model distinguishes between a bus type and
34 * the bus itself. Of course we only have one channel
35 * subsystem driver and one channel system per machine, but
36 * we still use the abstraction. T.R. says it's a good idea. */
37static int
38ccw_bus_match (struct device * dev, struct device_driver * drv)
39{
40 struct ccw_device *cdev = to_ccwdev(dev);
41 struct ccw_driver *cdrv = to_ccwdrv(drv);
42 const struct ccw_device_id *ids = cdrv->ids, *found;
43
44 if (!ids)
45 return 0;
46
47 found = ccw_device_id_match(ids, &cdev->id);
48 if (!found)
49 return 0;
50
51 cdev->id.driver_info = found->driver_info;
52
53 return 1;
54}
55
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +020056/* Store modalias string delimited by prefix/suffix string into buffer with
57 * specified size. Return length of resulting string (excluding trailing '\0')
58 * even if string doesn't fit buffer (snprintf semantics). */
59static int snprint_alias(char *buf, size_t size, const char *prefix,
60 struct ccw_device_id *id, const char *suffix)
61{
62 int len;
63
64 len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type,
65 id->cu_model);
66 if (len > size)
67 return len;
68 buf += len;
69 size -= len;
70
71 if (id->dev_type != 0)
72 len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type,
73 id->dev_model, suffix);
74 else
75 len += snprintf(buf, size, "dtdm%s", suffix);
76
77 return len;
78}
79
80/* Set up environment variables for ccw device uevent. Return 0 on success,
81 * non-zero otherwise. */
82static int ccw_uevent(struct device *dev, char **envp, int num_envp,
83 char *buffer, int buffer_size)
Linus Torvalds1da177e2005-04-16 15:20:36 -070084{
85 struct ccw_device *cdev = to_ccwdev(dev);
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +020086 struct ccw_device_id *id = &(cdev->id);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 int i = 0;
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +020088 int len;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +020090 /* CU_TYPE= */
91 len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1;
92 if (len > buffer_size || i >= num_envp)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 envp[i++] = buffer;
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +020095 buffer += len;
96 buffer_size -= len;
97
98 /* CU_MODEL= */
99 len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1;
100 if (len > buffer_size || i >= num_envp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 return -ENOMEM;
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +0200102 envp[i++] = buffer;
103 buffer += len;
104 buffer_size -= len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
106 /* The next two can be zero, that's ok for us */
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +0200107 /* DEV_TYPE= */
108 len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1;
109 if (len > buffer_size || i >= num_envp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 return -ENOMEM;
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +0200111 envp[i++] = buffer;
112 buffer += len;
113 buffer_size -= len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +0200115 /* DEV_MODEL= */
116 len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X",
117 (unsigned char) id->dev_model) + 1;
118 if (len > buffer_size || i >= num_envp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 return -ENOMEM;
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +0200120 envp[i++] = buffer;
121 buffer += len;
122 buffer_size -= len;
123
124 /* MODALIAS= */
125 len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1;
126 if (len > buffer_size || i >= num_envp)
127 return -ENOMEM;
128 envp[i++] = buffer;
129 buffer += len;
130 buffer_size -= len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200132 envp[i] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
134 return 0;
135}
136
Cornelia Huck8bbace72006-01-11 10:56:22 +0100137struct bus_type ccw_bus_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
Cornelia Huck8bbace72006-01-11 10:56:22 +0100139static int io_subchannel_probe (struct subchannel *);
140static int io_subchannel_remove (struct subchannel *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141static int io_subchannel_notify(struct device *, int);
142static void io_subchannel_verify(struct device *);
143static void io_subchannel_ioterm(struct device *);
Cornelia Huck8bbace72006-01-11 10:56:22 +0100144static void io_subchannel_shutdown(struct subchannel *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
146struct css_driver io_subchannel_driver = {
147 .subchannel_type = SUBCHANNEL_TYPE_IO,
148 .drv = {
149 .name = "io_subchannel",
150 .bus = &css_bus_type,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 },
152 .irq = io_subchannel_irq,
153 .notify = io_subchannel_notify,
154 .verify = io_subchannel_verify,
155 .termination = io_subchannel_ioterm,
Cornelia Huck8bbace72006-01-11 10:56:22 +0100156 .probe = io_subchannel_probe,
157 .remove = io_subchannel_remove,
158 .shutdown = io_subchannel_shutdown,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159};
160
161struct workqueue_struct *ccw_device_work;
162struct workqueue_struct *ccw_device_notify_work;
Peter Oberparleiter40154b82006-06-29 14:57:03 +0200163wait_queue_head_t ccw_device_init_wq;
164atomic_t ccw_device_init_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
166static int __init
167init_ccw_bus_type (void)
168{
169 int ret;
170
171 init_waitqueue_head(&ccw_device_init_wq);
172 atomic_set(&ccw_device_init_count, 0);
173
174 ccw_device_work = create_singlethread_workqueue("cio");
175 if (!ccw_device_work)
176 return -ENOMEM; /* FIXME: better errno ? */
177 ccw_device_notify_work = create_singlethread_workqueue("cio_notify");
178 if (!ccw_device_notify_work) {
179 ret = -ENOMEM; /* FIXME: better errno ? */
180 goto out_err;
181 }
182 slow_path_wq = create_singlethread_workqueue("kslowcrw");
183 if (!slow_path_wq) {
184 ret = -ENOMEM; /* FIXME: better errno ? */
185 goto out_err;
186 }
187 if ((ret = bus_register (&ccw_bus_type)))
188 goto out_err;
189
190 if ((ret = driver_register(&io_subchannel_driver.drv)))
191 goto out_err;
192
193 wait_event(ccw_device_init_wq,
194 atomic_read(&ccw_device_init_count) == 0);
195 flush_workqueue(ccw_device_work);
196 return 0;
197out_err:
198 if (ccw_device_work)
199 destroy_workqueue(ccw_device_work);
200 if (ccw_device_notify_work)
201 destroy_workqueue(ccw_device_notify_work);
202 if (slow_path_wq)
203 destroy_workqueue(slow_path_wq);
204 return ret;
205}
206
207static void __exit
208cleanup_ccw_bus_type (void)
209{
210 driver_unregister(&io_subchannel_driver.drv);
211 bus_unregister(&ccw_bus_type);
212 destroy_workqueue(ccw_device_notify_work);
213 destroy_workqueue(ccw_device_work);
214}
215
216subsys_initcall(init_ccw_bus_type);
217module_exit(cleanup_ccw_bus_type);
218
219/************************ device handling **************************/
220
221/*
222 * A ccw_device has some interfaces in sysfs in addition to the
223 * standard ones.
224 * The following entries are designed to export the information which
225 * resided in 2.4 in /proc/subchannels. Subchannel and device number
226 * are obvious, so they don't have an entry :)
227 * TODO: Split chpids and pimpampom up? Where is "in use" in the tree?
228 */
229static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400230chpids_show (struct device * dev, struct device_attribute *attr, char * buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231{
232 struct subchannel *sch = to_subchannel(dev);
233 struct ssd_info *ssd = &sch->ssd_info;
234 ssize_t ret = 0;
235 int chp;
236
Cornelia Huck32c5b052007-02-05 21:16:56 +0100237 for (chp = 0; chp < 8; chp++)
238 ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 ret += sprintf (buf+ret, "\n");
240 return min((ssize_t)PAGE_SIZE, ret);
241}
242
243static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400244pimpampom_show (struct device * dev, struct device_attribute *attr, char * buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
246 struct subchannel *sch = to_subchannel(dev);
247 struct pmcw *pmcw = &sch->schib.pmcw;
248
249 return sprintf (buf, "%02x %02x %02x\n",
250 pmcw->pim, pmcw->pam, pmcw->pom);
251}
252
253static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400254devtype_show (struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255{
256 struct ccw_device *cdev = to_ccwdev(dev);
257 struct ccw_device_id *id = &(cdev->id);
258
259 if (id->dev_type != 0)
260 return sprintf(buf, "%04x/%02x\n",
261 id->dev_type, id->dev_model);
262 else
263 return sprintf(buf, "n/a\n");
264}
265
266static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400267cutype_show (struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
269 struct ccw_device *cdev = to_ccwdev(dev);
270 struct ccw_device_id *id = &(cdev->id);
271
272 return sprintf(buf, "%04x/%02x\n",
273 id->cu_type, id->cu_model);
274}
275
276static ssize_t
Bastian Blankf1fc78a2005-10-30 15:00:12 -0800277modalias_show (struct device *dev, struct device_attribute *attr, char *buf)
278{
279 struct ccw_device *cdev = to_ccwdev(dev);
280 struct ccw_device_id *id = &(cdev->id);
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +0200281 int len;
Bastian Blankf1fc78a2005-10-30 15:00:12 -0800282
Peter Oberparleiterdb0c2d52006-09-20 15:59:49 +0200283 len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1;
284
285 return len > PAGE_SIZE ? PAGE_SIZE : len;
Bastian Blankf1fc78a2005-10-30 15:00:12 -0800286}
287
288static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400289online_show (struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
291 struct ccw_device *cdev = to_ccwdev(dev);
292
293 return sprintf(buf, cdev->online ? "1\n" : "0\n");
294}
295
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100296int ccw_device_is_orphan(struct ccw_device *cdev)
297{
298 return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));
299}
300
Cornelia Huck7674da72006-12-08 15:54:21 +0100301static void ccw_device_unregister(struct work_struct *work)
302{
303 struct ccw_device_private *priv;
304 struct ccw_device *cdev;
305
306 priv = container_of(work, struct ccw_device_private, kick_work);
307 cdev = priv->cdev;
308 if (test_and_clear_bit(1, &cdev->private->registered))
309 device_unregister(&cdev->dev);
310 put_device(&cdev->dev);
311}
312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313static void
314ccw_device_remove_disconnected(struct ccw_device *cdev)
315{
316 struct subchannel *sch;
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100317 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 /*
319 * Forced offline in disconnected state means
320 * 'throw away device'.
321 */
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100322 if (ccw_device_is_orphan(cdev)) {
323 /* Deregister ccw device. */
324 spin_lock_irqsave(cdev->ccwlock, flags);
325 cdev->private->state = DEV_STATE_NOT_OPER;
326 spin_unlock_irqrestore(cdev->ccwlock, flags);
327 if (get_device(&cdev->dev)) {
328 PREPARE_WORK(&cdev->private->kick_work,
329 ccw_device_unregister);
330 queue_work(ccw_device_work, &cdev->private->kick_work);
331 }
332 return ;
333 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 sch = to_subchannel(cdev->dev.parent);
Cornelia Huck6ab48792006-07-12 16:39:50 +0200335 css_sch_device_unregister(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 /* Reset intparm to zeroes. */
337 sch->schib.pmcw.intparm = 0;
338 cio_modify(sch);
339 put_device(&sch->dev);
340}
341
342int
343ccw_device_set_offline(struct ccw_device *cdev)
344{
345 int ret;
346
347 if (!cdev)
348 return -ENODEV;
349 if (!cdev->online || !cdev->drv)
350 return -EINVAL;
351
352 if (cdev->drv->set_offline) {
353 ret = cdev->drv->set_offline(cdev);
354 if (ret != 0)
355 return ret;
356 }
357 cdev->online = 0;
358 spin_lock_irq(cdev->ccwlock);
359 ret = ccw_device_offline(cdev);
360 if (ret == -ENODEV) {
361 if (cdev->private->state != DEV_STATE_NOT_OPER) {
362 cdev->private->state = DEV_STATE_OFFLINE;
363 dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
364 }
365 spin_unlock_irq(cdev->ccwlock);
366 return ret;
367 }
368 spin_unlock_irq(cdev->ccwlock);
369 if (ret == 0)
370 wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
371 else {
372 pr_debug("ccw_device_offline returned %d, device %s\n",
373 ret, cdev->dev.bus_id);
374 cdev->online = 1;
375 }
376 return ret;
377}
378
379int
380ccw_device_set_online(struct ccw_device *cdev)
381{
382 int ret;
383
384 if (!cdev)
385 return -ENODEV;
386 if (cdev->online || !cdev->drv)
387 return -EINVAL;
388
389 spin_lock_irq(cdev->ccwlock);
390 ret = ccw_device_online(cdev);
391 spin_unlock_irq(cdev->ccwlock);
392 if (ret == 0)
393 wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
394 else {
395 pr_debug("ccw_device_online returned %d, device %s\n",
396 ret, cdev->dev.bus_id);
397 return ret;
398 }
399 if (cdev->private->state != DEV_STATE_ONLINE)
400 return -ENODEV;
401 if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) {
402 cdev->online = 1;
403 return 0;
404 }
405 spin_lock_irq(cdev->ccwlock);
406 ret = ccw_device_offline(cdev);
407 spin_unlock_irq(cdev->ccwlock);
408 if (ret == 0)
409 wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
410 else
411 pr_debug("ccw_device_offline returned %d, device %s\n",
412 ret, cdev->dev.bus_id);
Cornelia Huck6cadb782006-02-17 13:52:49 -0800413 return (ret == 0) ? -ENODEV : ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414}
415
Cornelia Huckf5ba6c82007-04-27 16:01:30 +0200416static void online_store_handle_offline(struct ccw_device *cdev)
417{
418 if (cdev->private->state == DEV_STATE_DISCONNECTED)
419 ccw_device_remove_disconnected(cdev);
420 else if (cdev->drv && cdev->drv->set_offline)
421 ccw_device_set_offline(cdev);
422}
423
424static int online_store_recog_and_online(struct ccw_device *cdev)
425{
426 int ret;
427
428 /* Do device recognition, if needed. */
429 if (cdev->id.cu_type == 0) {
430 ret = ccw_device_recognition(cdev);
431 if (ret) {
432 printk(KERN_WARNING"Couldn't start recognition "
433 "for device %s (ret=%d)\n",
434 cdev->dev.bus_id, ret);
435 return ret;
436 }
437 wait_event(cdev->private->wait_q,
438 cdev->private->flags.recog_done);
439 }
440 if (cdev->drv && cdev->drv->set_online)
441 ccw_device_set_online(cdev);
442 return 0;
443}
444static void online_store_handle_online(struct ccw_device *cdev, int force)
445{
446 int ret;
447
448 ret = online_store_recog_and_online(cdev);
449 if (ret)
450 return;
451 if (force && cdev->private->state == DEV_STATE_BOXED) {
452 ret = ccw_device_stlck(cdev);
453 if (ret) {
454 printk(KERN_WARNING"ccw_device_stlck for device %s "
455 "returned %d!\n", cdev->dev.bus_id, ret);
456 return;
457 }
458 if (cdev->id.cu_type == 0)
459 cdev->private->state = DEV_STATE_NOT_OPER;
460 online_store_recog_and_online(cdev);
461 }
462
463}
464
465static ssize_t online_store (struct device *dev, struct device_attribute *attr,
466 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467{
468 struct ccw_device *cdev = to_ccwdev(dev);
Cornelia Huckf5ba6c82007-04-27 16:01:30 +0200469 int i, force;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 char *tmp;
471
Martin Schwidefsky973bd992006-01-06 00:19:07 -0800472 if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 return -EAGAIN;
474
475 if (cdev->drv && !try_module_get(cdev->drv->owner)) {
476 atomic_set(&cdev->private->onoff, 0);
477 return -EINVAL;
478 }
479 if (!strncmp(buf, "force\n", count)) {
480 force = 1;
481 i = 1;
482 } else {
483 force = 0;
484 i = simple_strtoul(buf, &tmp, 16);
485 }
Cornelia Huckf5ba6c82007-04-27 16:01:30 +0200486
487 switch (i) {
488 case 0:
489 online_store_handle_offline(cdev);
490 break;
491 case 1:
492 online_store_handle_online(cdev, force);
493 break;
494 default:
495 count = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 if (cdev->drv)
498 module_put(cdev->drv->owner);
499 atomic_set(&cdev->private->onoff, 0);
500 return count;
501}
502
503static ssize_t
Yani Ioannou3fd3c0a2005-05-17 06:43:27 -0400504available_show (struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505{
506 struct ccw_device *cdev = to_ccwdev(dev);
507 struct subchannel *sch;
508
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100509 if (ccw_device_is_orphan(cdev))
510 return sprintf(buf, "no device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 switch (cdev->private->state) {
512 case DEV_STATE_BOXED:
513 return sprintf(buf, "boxed\n");
514 case DEV_STATE_DISCONNECTED:
515 case DEV_STATE_DISCONNECTED_SENSE_ID:
516 case DEV_STATE_NOT_OPER:
517 sch = to_subchannel(dev->parent);
518 if (!sch->lpm)
519 return sprintf(buf, "no path\n");
520 else
521 return sprintf(buf, "no device\n");
522 default:
523 /* All other states considered fine. */
524 return sprintf(buf, "good\n");
525 }
526}
527
528static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);
529static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);
530static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
531static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);
Bastian Blankf1fc78a2005-10-30 15:00:12 -0800532static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533static DEVICE_ATTR(online, 0644, online_show, online_store);
534extern struct device_attribute dev_attr_cmb_enable;
535static DEVICE_ATTR(availability, 0444, available_show, NULL);
536
537static struct attribute * subch_attrs[] = {
538 &dev_attr_chpids.attr,
539 &dev_attr_pimpampom.attr,
540 NULL,
541};
542
543static struct attribute_group subch_attr_group = {
544 .attrs = subch_attrs,
545};
546
Cornelia Huck529192f2006-12-08 15:55:57 +0100547struct attribute_group *subch_attr_groups[] = {
548 &subch_attr_group,
549 NULL,
550};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
552static struct attribute * ccwdev_attrs[] = {
553 &dev_attr_devtype.attr,
554 &dev_attr_cutype.attr,
Bastian Blankf1fc78a2005-10-30 15:00:12 -0800555 &dev_attr_modalias.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 &dev_attr_online.attr,
557 &dev_attr_cmb_enable.attr,
558 &dev_attr_availability.attr,
559 NULL,
560};
561
562static struct attribute_group ccwdev_attr_group = {
563 .attrs = ccwdev_attrs,
564};
565
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100566static int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567device_add_files (struct device *dev)
568{
569 return sysfs_create_group(&dev->kobj, &ccwdev_attr_group);
570}
571
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100572static void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573device_remove_files(struct device *dev)
574{
575 sysfs_remove_group(&dev->kobj, &ccwdev_attr_group);
576}
577
578/* this is a simple abstraction for device_register that sets the
579 * correct bus type and adds the bus specific files */
Cornelia Huck3c9da7b2006-10-27 12:39:33 +0200580static int ccw_device_register(struct ccw_device *cdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581{
582 struct device *dev = &cdev->dev;
583 int ret;
584
585 dev->bus = &ccw_bus_type;
586
587 if ((ret = device_add(dev)))
588 return ret;
589
590 set_bit(1, &cdev->private->registered);
591 if ((ret = device_add_files(dev))) {
592 if (test_and_clear_bit(1, &cdev->private->registered))
593 device_del(dev);
594 }
595 return ret;
596}
597
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700598struct match_data {
Cornelia Huck78964262006-10-11 15:31:38 +0200599 struct ccw_dev_id dev_id;
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700600 struct ccw_device * sibling;
601};
602
603static int
604match_devno(struct device * dev, void * data)
605{
Cornelia Huck78964262006-10-11 15:31:38 +0200606 struct match_data * d = data;
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700607 struct ccw_device * cdev;
608
609 cdev = to_ccwdev(dev);
610 if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100611 !ccw_device_is_orphan(cdev) &&
Cornelia Huck78964262006-10-11 15:31:38 +0200612 ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) &&
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100613 (cdev != d->sibling))
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700614 return 1;
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700615 return 0;
616}
617
Cornelia Huck78964262006-10-11 15:31:38 +0200618static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id,
619 struct ccw_device *sibling)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 struct device *dev;
Heiko Carstens292888c2006-08-30 14:33:35 +0200622 struct match_data data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
Cornelia Huck78964262006-10-11 15:31:38 +0200624 data.dev_id = *dev_id;
Heiko Carstens292888c2006-08-30 14:33:35 +0200625 data.sibling = sibling;
Cornelia Hucke5945b42005-10-11 08:28:59 -0700626 dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700628 return dev ? to_ccwdev(dev) : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629}
630
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100631static int match_orphan(struct device *dev, void *data)
632{
633 struct ccw_dev_id *dev_id;
634 struct ccw_device *cdev;
635
636 dev_id = data;
637 cdev = to_ccwdev(dev);
638 return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id);
639}
640
641static struct ccw_device *
642get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css,
643 struct ccw_dev_id *dev_id)
644{
645 struct device *dev;
646
647 dev = device_find_child(&css->pseudo_subchannel->dev, dev_id,
648 match_orphan);
649
650 return dev ? to_ccwdev(dev) : NULL;
651}
652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653static void
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100654ccw_device_add_changed(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655{
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100656 struct ccw_device_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 struct ccw_device *cdev;
658
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100659 priv = container_of(work, struct ccw_device_private, kick_work);
660 cdev = priv->cdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 if (device_add(&cdev->dev)) {
662 put_device(&cdev->dev);
663 return;
664 }
665 set_bit(1, &cdev->private->registered);
666 if (device_add_files(&cdev->dev)) {
667 if (test_and_clear_bit(1, &cdev->private->registered))
668 device_unregister(&cdev->dev);
669 }
670}
671
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100672void ccw_device_do_unreg_rereg(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673{
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100674 struct ccw_device_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 struct ccw_device *cdev;
676 struct subchannel *sch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100678 priv = container_of(work, struct ccw_device_private, kick_work);
679 cdev = priv->cdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 sch = to_subchannel(cdev->dev.parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 device_remove_files(&cdev->dev);
683 if (test_and_clear_bit(1, &cdev->private->registered))
684 device_del(&cdev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 PREPARE_WORK(&cdev->private->kick_work,
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100686 ccw_device_add_changed);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 queue_work(ccw_device_work, &cdev->private->kick_work);
688}
689
690static void
691ccw_device_release(struct device *dev)
692{
693 struct ccw_device *cdev;
694
695 cdev = to_ccwdev(dev);
696 kfree(cdev->private);
697 kfree(cdev);
698}
699
Cornelia Huck7674da72006-12-08 15:54:21 +0100700static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
701{
702 struct ccw_device *cdev;
703
704 cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
705 if (cdev) {
706 cdev->private = kzalloc(sizeof(struct ccw_device_private),
707 GFP_KERNEL | GFP_DMA);
708 if (cdev->private)
709 return cdev;
710 }
711 kfree(cdev);
712 return ERR_PTR(-ENOMEM);
713}
714
715static int io_subchannel_initialize_dev(struct subchannel *sch,
716 struct ccw_device *cdev)
717{
718 cdev->private->cdev = cdev;
719 atomic_set(&cdev->private->onoff, 0);
720 cdev->dev.parent = &sch->dev;
721 cdev->dev.release = ccw_device_release;
722 INIT_LIST_HEAD(&cdev->private->kick_work.entry);
723 /* Do first half of device_register. */
724 device_initialize(&cdev->dev);
725 if (!get_device(&sch->dev)) {
726 if (cdev->dev.release)
727 cdev->dev.release(&cdev->dev);
728 return -ENODEV;
729 }
730 return 0;
731}
732
733static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch)
734{
735 struct ccw_device *cdev;
736 int ret;
737
738 cdev = io_subchannel_allocate_dev(sch);
739 if (!IS_ERR(cdev)) {
740 ret = io_subchannel_initialize_dev(sch, cdev);
741 if (ret) {
742 kfree(cdev);
743 cdev = ERR_PTR(ret);
744 }
745 }
746 return cdev;
747}
748
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +0100749static int io_subchannel_recog(struct ccw_device *, struct subchannel *);
750
751static void sch_attach_device(struct subchannel *sch,
752 struct ccw_device *cdev)
753{
754 spin_lock_irq(sch->lock);
755 sch->dev.driver_data = cdev;
756 cdev->private->schid = sch->schid;
757 cdev->ccwlock = sch->lock;
758 device_trigger_reprobe(sch);
759 spin_unlock_irq(sch->lock);
760}
761
762static void sch_attach_disconnected_device(struct subchannel *sch,
763 struct ccw_device *cdev)
764{
765 struct subchannel *other_sch;
766 int ret;
767
768 other_sch = to_subchannel(get_device(cdev->dev.parent));
769 ret = device_move(&cdev->dev, &sch->dev);
770 if (ret) {
771 CIO_MSG_EVENT(2, "Moving disconnected device 0.%x.%04x failed "
772 "(ret=%d)!\n", cdev->private->dev_id.ssid,
773 cdev->private->dev_id.devno, ret);
774 put_device(&other_sch->dev);
775 return;
776 }
777 other_sch->dev.driver_data = NULL;
778 /* No need to keep a subchannel without ccw device around. */
779 css_sch_device_unregister(other_sch);
780 put_device(&other_sch->dev);
781 sch_attach_device(sch, cdev);
782}
783
784static void sch_attach_orphaned_device(struct subchannel *sch,
785 struct ccw_device *cdev)
786{
787 int ret;
788
789 /* Try to move the ccw device to its new subchannel. */
790 ret = device_move(&cdev->dev, &sch->dev);
791 if (ret) {
792 CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
793 "failed (ret=%d)!\n",
794 cdev->private->dev_id.ssid,
795 cdev->private->dev_id.devno, ret);
796 return;
797 }
798 sch_attach_device(sch, cdev);
799}
800
801static void sch_create_and_recog_new_device(struct subchannel *sch)
802{
803 struct ccw_device *cdev;
804
805 /* Need to allocate a new ccw device. */
806 cdev = io_subchannel_create_ccwdev(sch);
807 if (IS_ERR(cdev)) {
808 /* OK, we did everything we could... */
809 css_sch_device_unregister(sch);
810 return;
811 }
812 spin_lock_irq(sch->lock);
813 sch->dev.driver_data = cdev;
814 spin_unlock_irq(sch->lock);
815 /* Start recognition for the new ccw device. */
816 if (io_subchannel_recog(cdev, sch)) {
817 spin_lock_irq(sch->lock);
818 sch->dev.driver_data = NULL;
819 spin_unlock_irq(sch->lock);
820 if (cdev->dev.release)
821 cdev->dev.release(&cdev->dev);
822 css_sch_device_unregister(sch);
823 }
824}
825
826
827void ccw_device_move_to_orphanage(struct work_struct *work)
828{
829 struct ccw_device_private *priv;
830 struct ccw_device *cdev;
831 struct ccw_device *replacing_cdev;
832 struct subchannel *sch;
833 int ret;
834 struct channel_subsystem *css;
835 struct ccw_dev_id dev_id;
836
837 priv = container_of(work, struct ccw_device_private, kick_work);
838 cdev = priv->cdev;
839 sch = to_subchannel(cdev->dev.parent);
840 css = to_css(sch->dev.parent);
841 dev_id.devno = sch->schib.pmcw.dev;
842 dev_id.ssid = sch->schid.ssid;
843
844 /*
845 * Move the orphaned ccw device to the orphanage so the replacing
846 * ccw device can take its place on the subchannel.
847 */
848 ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);
849 if (ret) {
850 CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
851 "(ret=%d)!\n", cdev->private->dev_id.ssid,
852 cdev->private->dev_id.devno, ret);
853 return;
854 }
855 cdev->ccwlock = css->pseudo_subchannel->lock;
856 /*
857 * Search for the replacing ccw device
858 * - among the disconnected devices
859 * - in the orphanage
860 */
861 replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
862 if (replacing_cdev) {
863 sch_attach_disconnected_device(sch, replacing_cdev);
864 return;
865 }
866 replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
867 if (replacing_cdev) {
868 sch_attach_orphaned_device(sch, replacing_cdev);
869 return;
870 }
871 sch_create_and_recog_new_device(sch);
872}
873
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874/*
875 * Register recognized device.
876 */
877static void
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100878io_subchannel_register(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879{
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100880 struct ccw_device_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 struct ccw_device *cdev;
882 struct subchannel *sch;
883 int ret;
884 unsigned long flags;
885
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100886 priv = container_of(work, struct ccw_device_private, kick_work);
887 cdev = priv->cdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 sch = to_subchannel(cdev->dev.parent);
889
Cornelia Huck47af5512006-12-04 15:41:07 +0100890 /*
891 * io_subchannel_register() will also be called after device
892 * recognition has been done for a boxed device (which will already
893 * be registered). We need to reprobe since we may now have sense id
894 * information.
895 */
Cornelia Huckb0744bd2005-06-25 14:55:27 -0700896 if (klist_node_attached(&cdev->dev.knode_parent)) {
Cornelia Huck47af5512006-12-04 15:41:07 +0100897 if (!cdev->drv) {
898 ret = device_reprobe(&cdev->dev);
899 if (ret)
900 /* We can't do much here. */
901 dev_info(&cdev->dev, "device_reprobe() returned"
902 " %d\n", ret);
903 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 goto out;
905 }
906 /* make it known to the system */
907 ret = ccw_device_register(cdev);
908 if (ret) {
909 printk (KERN_WARNING "%s: could not register %s\n",
910 __func__, cdev->dev.bus_id);
911 put_device(&cdev->dev);
Cornelia Huck2ec22982006-12-08 15:54:26 +0100912 spin_lock_irqsave(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 sch->dev.driver_data = NULL;
Cornelia Huck2ec22982006-12-08 15:54:26 +0100914 spin_unlock_irqrestore(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 kfree (cdev->private);
916 kfree (cdev);
917 put_device(&sch->dev);
918 if (atomic_dec_and_test(&ccw_device_init_count))
919 wake_up(&ccw_device_init_wq);
920 return;
921 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 put_device(&cdev->dev);
923out:
924 cdev->private->flags.recog_done = 1;
925 put_device(&sch->dev);
926 wake_up(&cdev->private->wait_q);
927 if (atomic_dec_and_test(&ccw_device_init_count))
928 wake_up(&ccw_device_init_wq);
929}
930
931void
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100932ccw_device_call_sch_unregister(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933{
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100934 struct ccw_device_private *priv;
935 struct ccw_device *cdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 struct subchannel *sch;
937
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100938 priv = container_of(work, struct ccw_device_private, kick_work);
939 cdev = priv->cdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 sch = to_subchannel(cdev->dev.parent);
Cornelia Huck6ab48792006-07-12 16:39:50 +0200941 css_sch_device_unregister(sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 /* Reset intparm to zeroes. */
943 sch->schib.pmcw.intparm = 0;
944 cio_modify(sch);
945 put_device(&cdev->dev);
946 put_device(&sch->dev);
947}
948
949/*
950 * subchannel recognition done. Called from the state machine.
951 */
952void
953io_subchannel_recog_done(struct ccw_device *cdev)
954{
955 struct subchannel *sch;
956
957 if (css_init_done == 0) {
958 cdev->private->flags.recog_done = 1;
959 return;
960 }
961 switch (cdev->private->state) {
962 case DEV_STATE_NOT_OPER:
963 cdev->private->flags.recog_done = 1;
964 /* Remove device found not operational. */
965 if (!get_device(&cdev->dev))
966 break;
967 sch = to_subchannel(cdev->dev.parent);
968 PREPARE_WORK(&cdev->private->kick_work,
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100969 ccw_device_call_sch_unregister);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 queue_work(slow_path_wq, &cdev->private->kick_work);
971 if (atomic_dec_and_test(&ccw_device_init_count))
972 wake_up(&ccw_device_init_wq);
973 break;
974 case DEV_STATE_BOXED:
975 /* Device did not respond in time. */
976 case DEV_STATE_OFFLINE:
977 /*
978 * We can't register the device in interrupt context so
979 * we schedule a work item.
980 */
981 if (!get_device(&cdev->dev))
982 break;
983 PREPARE_WORK(&cdev->private->kick_work,
Martin Schwidefskyc1637532006-12-08 15:53:57 +0100984 io_subchannel_register);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 queue_work(slow_path_wq, &cdev->private->kick_work);
986 break;
987 }
988}
989
990static int
991io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
992{
993 int rc;
994 struct ccw_device_private *priv;
995
996 sch->dev.driver_data = cdev;
997 sch->driver = &io_subchannel_driver;
Cornelia Huck2ec22982006-12-08 15:54:26 +0100998 cdev->ccwlock = sch->lock;
Cornelia Huckfb6958a2006-01-06 00:19:25 -0800999
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 /* Init private data. */
1001 priv = cdev->private;
Cornelia Huck78964262006-10-11 15:31:38 +02001002 priv->dev_id.devno = sch->schib.pmcw.dev;
1003 priv->dev_id.ssid = sch->schid.ssid;
1004 priv->schid = sch->schid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 priv->state = DEV_STATE_NOT_OPER;
1006 INIT_LIST_HEAD(&priv->cmb_list);
1007 init_waitqueue_head(&priv->wait_q);
1008 init_timer(&priv->timer);
1009
1010 /* Set an initial name for the device. */
Cornelia Huckfb6958a2006-01-06 00:19:25 -08001011 snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
1012 sch->schid.ssid, sch->schib.pmcw.dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014 /* Increase counter of devices currently in recognition. */
1015 atomic_inc(&ccw_device_init_count);
1016
1017 /* Start async. device sensing. */
Cornelia Huck2ec22982006-12-08 15:54:26 +01001018 spin_lock_irq(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 rc = ccw_device_recognition(cdev);
Cornelia Huck2ec22982006-12-08 15:54:26 +01001020 spin_unlock_irq(sch->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 if (rc) {
1022 if (atomic_dec_and_test(&ccw_device_init_count))
1023 wake_up(&ccw_device_init_wq);
1024 }
1025 return rc;
1026}
1027
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +01001028static void ccw_device_move_to_sch(struct work_struct *work)
1029{
1030 struct ccw_device_private *priv;
1031 int rc;
1032 struct subchannel *sch;
1033 struct ccw_device *cdev;
1034 struct subchannel *former_parent;
1035
1036 priv = container_of(work, struct ccw_device_private, kick_work);
1037 sch = priv->sch;
1038 cdev = priv->cdev;
1039 former_parent = ccw_device_is_orphan(cdev) ?
1040 NULL : to_subchannel(get_device(cdev->dev.parent));
1041 mutex_lock(&sch->reg_mutex);
1042 /* Try to move the ccw device to its new subchannel. */
1043 rc = device_move(&cdev->dev, &sch->dev);
1044 mutex_unlock(&sch->reg_mutex);
1045 if (rc) {
1046 CIO_MSG_EVENT(2, "Moving device 0.%x.%04x to subchannel "
1047 "0.%x.%04x failed (ret=%d)!\n",
1048 cdev->private->dev_id.ssid,
1049 cdev->private->dev_id.devno, sch->schid.ssid,
1050 sch->schid.sch_no, rc);
1051 css_sch_device_unregister(sch);
1052 goto out;
1053 }
1054 if (former_parent) {
1055 spin_lock_irq(former_parent->lock);
1056 former_parent->dev.driver_data = NULL;
1057 spin_unlock_irq(former_parent->lock);
1058 css_sch_device_unregister(former_parent);
1059 /* Reset intparm to zeroes. */
1060 former_parent->schib.pmcw.intparm = 0;
1061 cio_modify(former_parent);
1062 }
1063 sch_attach_device(sch, cdev);
1064out:
1065 if (former_parent)
1066 put_device(&former_parent->dev);
1067 put_device(&cdev->dev);
1068}
1069
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070static int
Cornelia Huck8bbace72006-01-11 10:56:22 +01001071io_subchannel_probe (struct subchannel *sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 struct ccw_device *cdev;
1074 int rc;
1075 unsigned long flags;
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +01001076 struct ccw_dev_id dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 if (sch->dev.driver_data) {
1079 /*
1080 * This subchannel already has an associated ccw_device.
1081 * Register it and exit. This happens for all early
1082 * device, e.g. the console.
1083 */
1084 cdev = sch->dev.driver_data;
1085 device_initialize(&cdev->dev);
1086 ccw_device_register(cdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 /*
1088 * Check if the device is already online. If it is
1089 * the reference count needs to be corrected
1090 * (see ccw_device_online and css_init_done for the
1091 * ugly details).
1092 */
1093 if (cdev->private->state != DEV_STATE_NOT_OPER &&
1094 cdev->private->state != DEV_STATE_OFFLINE &&
1095 cdev->private->state != DEV_STATE_BOXED)
1096 get_device(&cdev->dev);
1097 return 0;
1098 }
Cornelia Huckd7b5a4c92006-12-08 15:54:28 +01001099 /*
1100 * First check if a fitting device may be found amongst the
1101 * disconnected devices or in the orphanage.
1102 */
1103 dev_id.devno = sch->schib.pmcw.dev;
1104 dev_id.ssid = sch->schid.ssid;
1105 cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
1106 if (!cdev)
1107 cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
1108 &dev_id);
1109 if (cdev) {
1110 /*
1111 * Schedule moving the device until when we have a registered
1112 * subchannel to move to and succeed the probe. We can
1113 * unregister later again, when the probe is through.
1114 */
1115 cdev->private->sch = sch;
1116 PREPARE_WORK(&cdev->private->kick_work,
1117 ccw_device_move_to_sch);
1118 queue_work(slow_path_wq, &cdev->private->kick_work);
1119 return 0;
1120 }
Cornelia Huck7674da72006-12-08 15:54:21 +01001121 cdev = io_subchannel_create_ccwdev(sch);
1122 if (IS_ERR(cdev))
1123 return PTR_ERR(cdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124
Cornelia Huck8bbace72006-01-11 10:56:22 +01001125 rc = io_subchannel_recog(cdev, sch);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 if (rc) {
Cornelia Huck2ec22982006-12-08 15:54:26 +01001127 spin_lock_irqsave(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 sch->dev.driver_data = NULL;
Cornelia Huck2ec22982006-12-08 15:54:26 +01001129 spin_unlock_irqrestore(sch->lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 if (cdev->dev.release)
1131 cdev->dev.release(&cdev->dev);
1132 }
1133
1134 return rc;
1135}
1136
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137static int
Cornelia Huck8bbace72006-01-11 10:56:22 +01001138io_subchannel_remove (struct subchannel *sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139{
1140 struct ccw_device *cdev;
1141 unsigned long flags;
1142
Cornelia Huck8bbace72006-01-11 10:56:22 +01001143 if (!sch->dev.driver_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 return 0;
Cornelia Huck8bbace72006-01-11 10:56:22 +01001145 cdev = sch->dev.driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 /* Set ccw device to not operational and drop reference. */
1147 spin_lock_irqsave(cdev->ccwlock, flags);
Cornelia Huck8bbace72006-01-11 10:56:22 +01001148 sch->dev.driver_data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 cdev->private->state = DEV_STATE_NOT_OPER;
1150 spin_unlock_irqrestore(cdev->ccwlock, flags);
1151 /*
1152 * Put unregistration on workqueue to avoid livelocks on the css bus
1153 * semaphore.
1154 */
1155 if (get_device(&cdev->dev)) {
1156 PREPARE_WORK(&cdev->private->kick_work,
Martin Schwidefskyc1637532006-12-08 15:53:57 +01001157 ccw_device_unregister);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 queue_work(ccw_device_work, &cdev->private->kick_work);
1159 }
1160 return 0;
1161}
1162
1163static int
1164io_subchannel_notify(struct device *dev, int event)
1165{
1166 struct ccw_device *cdev;
1167
1168 cdev = dev->driver_data;
1169 if (!cdev)
1170 return 0;
1171 if (!cdev->drv)
1172 return 0;
1173 if (!cdev->online)
1174 return 0;
1175 return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
1176}
1177
1178static void
1179io_subchannel_verify(struct device *dev)
1180{
1181 struct ccw_device *cdev;
1182
1183 cdev = dev->driver_data;
1184 if (cdev)
1185 dev_fsm_event(cdev, DEV_EVENT_VERIFY);
1186}
1187
1188static void
1189io_subchannel_ioterm(struct device *dev)
1190{
1191 struct ccw_device *cdev;
1192
1193 cdev = dev->driver_data;
1194 if (!cdev)
1195 return;
Cornelia Huckd23861f2006-12-04 15:41:04 +01001196 /* Internal I/O will be retried by the interrupt handler. */
1197 if (cdev->private->flags.intretry)
1198 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 cdev->private->state = DEV_STATE_CLEAR_VERIFY;
1200 if (cdev->handler)
1201 cdev->handler(cdev, cdev->private->intparm,
1202 ERR_PTR(-EIO));
1203}
1204
1205static void
Cornelia Huck8bbace72006-01-11 10:56:22 +01001206io_subchannel_shutdown(struct subchannel *sch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 struct ccw_device *cdev;
1209 int ret;
1210
Cornelia Huck8bbace72006-01-11 10:56:22 +01001211 cdev = sch->dev.driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212
Cornelia Hucka8237fc2006-01-06 00:19:21 -08001213 if (cio_is_console(sch->schid))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 return;
1215 if (!sch->schib.pmcw.ena)
1216 /* Nothing to do. */
1217 return;
1218 ret = cio_disable_subchannel(sch);
1219 if (ret != -EBUSY)
1220 /* Subchannel is disabled, we're done. */
1221 return;
1222 cdev->private->state = DEV_STATE_QUIESCE;
1223 if (cdev->handler)
1224 cdev->handler(cdev, cdev->private->intparm,
1225 ERR_PTR(-EIO));
1226 ret = ccw_device_cancel_halt_clear(cdev);
1227 if (ret == -EBUSY) {
1228 ccw_device_set_timeout(cdev, HZ/10);
1229 wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
1230 }
1231 cio_disable_subchannel(sch);
1232}
1233
1234#ifdef CONFIG_CCW_CONSOLE
1235static struct ccw_device console_cdev;
1236static struct ccw_device_private console_private;
1237static int console_cdev_in_use;
1238
Cornelia Huck2ec22982006-12-08 15:54:26 +01001239static DEFINE_SPINLOCK(ccw_console_lock);
1240
1241spinlock_t * cio_get_console_lock(void)
1242{
1243 return &ccw_console_lock;
1244}
1245
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246static int
1247ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
1248{
1249 int rc;
1250
1251 /* Initialize the ccw_device structure. */
Heiko Carstens292888c2006-08-30 14:33:35 +02001252 cdev->dev.parent= &sch->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 rc = io_subchannel_recog(cdev, sch);
1254 if (rc)
1255 return rc;
1256
1257 /* Now wait for the async. recognition to come to an end. */
1258 spin_lock_irq(cdev->ccwlock);
1259 while (!dev_fsm_final_state(cdev))
1260 wait_cons_dev();
1261 rc = -EIO;
1262 if (cdev->private->state != DEV_STATE_OFFLINE)
1263 goto out_unlock;
1264 ccw_device_online(cdev);
1265 while (!dev_fsm_final_state(cdev))
1266 wait_cons_dev();
1267 if (cdev->private->state != DEV_STATE_ONLINE)
1268 goto out_unlock;
1269 rc = 0;
1270out_unlock:
1271 spin_unlock_irq(cdev->ccwlock);
1272 return 0;
1273}
1274
1275struct ccw_device *
1276ccw_device_probe_console(void)
1277{
1278 struct subchannel *sch;
1279 int ret;
1280
1281 if (xchg(&console_cdev_in_use, 1) != 0)
Peter Oberparleiter600b5d12006-02-01 03:06:35 -08001282 return ERR_PTR(-EBUSY);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 sch = cio_probe_console();
1284 if (IS_ERR(sch)) {
1285 console_cdev_in_use = 0;
1286 return (void *) sch;
1287 }
1288 memset(&console_cdev, 0, sizeof(struct ccw_device));
1289 memset(&console_private, 0, sizeof(struct ccw_device_private));
1290 console_cdev.private = &console_private;
Martin Schwidefskyc1637532006-12-08 15:53:57 +01001291 console_private.cdev = &console_cdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 ret = ccw_device_console_enable(&console_cdev, sch);
1293 if (ret) {
1294 cio_release_console();
1295 console_cdev_in_use = 0;
1296 return ERR_PTR(ret);
1297 }
1298 console_cdev.online = 1;
1299 return &console_cdev;
1300}
1301#endif
1302
1303/*
1304 * get ccw_device matching the busid, but only if owned by cdrv
1305 */
Cornelia Huckb0744bd2005-06-25 14:55:27 -07001306static int
1307__ccwdev_check_busid(struct device *dev, void *id)
1308{
1309 char *bus_id;
1310
Cornelia Huck12975ae2006-10-11 15:31:47 +02001311 bus_id = id;
Cornelia Huckb0744bd2005-06-25 14:55:27 -07001312
1313 return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0);
1314}
1315
1316
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317struct ccw_device *
1318get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id)
1319{
Cornelia Huckb0744bd2005-06-25 14:55:27 -07001320 struct device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 struct device_driver *drv;
1322
1323 drv = get_driver(&cdrv->driver);
1324 if (!drv)
Cornelia Huckb0744bd2005-06-25 14:55:27 -07001325 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
Cornelia Huckb0744bd2005-06-25 14:55:27 -07001327 dev = driver_find_device(drv, NULL, (void *)bus_id,
1328 __ccwdev_check_busid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 put_driver(drv);
1330
Heiko Carstensd2c993d2006-07-12 16:41:55 +02001331 return dev ? to_ccwdev(dev) : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332}
1333
1334/************************** device driver handling ************************/
1335
1336/* This is the implementation of the ccw_driver class. The probe, remove
1337 * and release methods are initially very similar to the device_driver
1338 * implementations, with the difference that they have ccw_device
1339 * arguments.
1340 *
1341 * A ccw driver also contains the information that is needed for
1342 * device matching.
1343 */
1344static int
1345ccw_device_probe (struct device *dev)
1346{
1347 struct ccw_device *cdev = to_ccwdev(dev);
1348 struct ccw_driver *cdrv = to_ccwdrv(dev->driver);
1349 int ret;
1350
1351 cdev->drv = cdrv; /* to let the driver call _set_online */
1352
1353 ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV;
1354
1355 if (ret) {
Heiko Carstensd2c993d2006-07-12 16:41:55 +02001356 cdev->drv = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 return ret;
1358 }
1359
1360 return 0;
1361}
1362
1363static int
1364ccw_device_remove (struct device *dev)
1365{
1366 struct ccw_device *cdev = to_ccwdev(dev);
1367 struct ccw_driver *cdrv = cdev->drv;
1368 int ret;
1369
1370 pr_debug("removing device %s\n", cdev->dev.bus_id);
1371 if (cdrv->remove)
1372 cdrv->remove(cdev);
1373 if (cdev->online) {
1374 cdev->online = 0;
1375 spin_lock_irq(cdev->ccwlock);
1376 ret = ccw_device_offline(cdev);
1377 spin_unlock_irq(cdev->ccwlock);
1378 if (ret == 0)
1379 wait_event(cdev->private->wait_q,
1380 dev_fsm_final_state(cdev));
1381 else
1382 //FIXME: we can't fail!
1383 pr_debug("ccw_device_offline returned %d, device %s\n",
1384 ret, cdev->dev.bus_id);
1385 }
1386 ccw_device_set_timeout(cdev, 0);
Heiko Carstensd2c993d2006-07-12 16:41:55 +02001387 cdev->drv = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 return 0;
1389}
1390
Cornelia Huck8bbace72006-01-11 10:56:22 +01001391struct bus_type ccw_bus_type = {
1392 .name = "ccw",
1393 .match = ccw_bus_match,
1394 .uevent = ccw_uevent,
1395 .probe = ccw_device_probe,
1396 .remove = ccw_device_remove,
1397};
1398
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399int
1400ccw_driver_register (struct ccw_driver *cdriver)
1401{
1402 struct device_driver *drv = &cdriver->driver;
1403
1404 drv->bus = &ccw_bus_type;
1405 drv->name = cdriver->name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406
1407 return driver_register(drv);
1408}
1409
1410void
1411ccw_driver_unregister (struct ccw_driver *cdriver)
1412{
1413 driver_unregister(&cdriver->driver);
1414}
1415
Cornelia Hucka8237fc2006-01-06 00:19:21 -08001416/* Helper func for qdio. */
1417struct subchannel_id
1418ccw_device_get_subchannel_id(struct ccw_device *cdev)
1419{
1420 struct subchannel *sch;
1421
1422 sch = to_subchannel(cdev->dev.parent);
1423 return sch->schid;
1424}
1425
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426MODULE_LICENSE("GPL");
1427EXPORT_SYMBOL(ccw_device_set_online);
1428EXPORT_SYMBOL(ccw_device_set_offline);
1429EXPORT_SYMBOL(ccw_driver_register);
1430EXPORT_SYMBOL(ccw_driver_unregister);
1431EXPORT_SYMBOL(get_ccwdev_by_busid);
1432EXPORT_SYMBOL(ccw_bus_type);
1433EXPORT_SYMBOL(ccw_device_work);
1434EXPORT_SYMBOL(ccw_device_notify_work);
Cornelia Hucka8237fc2006-01-06 00:19:21 -08001435EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);