diff options
-rw-r--r-- | drivers/s390/char/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/char/sclp_chp.c | 196 | ||||
-rw-r--r-- | drivers/s390/cio/chp.c | 250 | ||||
-rw-r--r-- | drivers/s390/cio/chp.h | 20 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.c | 44 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.h | 2 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 1 | ||||
-rw-r--r-- | drivers/s390/cio/cio.h | 1 | ||||
-rw-r--r-- | drivers/s390/cio/css.h | 4 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/device_ops.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/ioasm.h | 2 | ||||
-rw-r--r-- | include/asm-s390/chpid.h (renamed from drivers/s390/cio/chpid.h) | 10 | ||||
-rw-r--r-- | include/asm-s390/cio.h | 1 | ||||
-rw-r--r-- | include/asm-s390/sclp.h | 12 |
15 files changed, 534 insertions, 15 deletions
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 293e667b50f2..5fd581c22db3 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile | |||
@@ -3,7 +3,7 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ | 5 | obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ |
6 | sclp_info.o | 6 | sclp_info.o sclp_chp.o |
7 | 7 | ||
8 | obj-$(CONFIG_TN3270) += raw3270.o | 8 | obj-$(CONFIG_TN3270) += raw3270.o |
9 | obj-$(CONFIG_TN3270_CONSOLE) += con3270.o | 9 | obj-$(CONFIG_TN3270_CONSOLE) += con3270.o |
diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c new file mode 100644 index 000000000000..a66b914519b5 --- /dev/null +++ b/drivers/s390/char/sclp_chp.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/sclp_chp.c | ||
3 | * | ||
4 | * Copyright IBM Corp. 2007 | ||
5 | * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | ||
6 | */ | ||
7 | |||
8 | #include <linux/types.h> | ||
9 | #include <linux/gfp.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/completion.h> | ||
12 | #include <asm/sclp.h> | ||
13 | #include <asm/chpid.h> | ||
14 | |||
15 | #include "sclp.h" | ||
16 | |||
17 | #define TAG "sclp_chp: " | ||
18 | |||
19 | #define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001 | ||
20 | #define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001 | ||
21 | #define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001 | ||
22 | |||
23 | static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid) | ||
24 | { | ||
25 | return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8; | ||
26 | } | ||
27 | |||
28 | static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid) | ||
29 | { | ||
30 | return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8; | ||
31 | } | ||
32 | |||
33 | static void chp_callback(struct sclp_req *req, void *data) | ||
34 | { | ||
35 | struct completion *completion = data; | ||
36 | |||
37 | complete(completion); | ||
38 | } | ||
39 | |||
40 | struct chp_cfg_sccb { | ||
41 | struct sccb_header header; | ||
42 | u8 ccm; | ||
43 | u8 reserved[6]; | ||
44 | u8 cssid; | ||
45 | } __attribute__((packed)); | ||
46 | |||
47 | struct chp_cfg_data { | ||
48 | struct chp_cfg_sccb sccb; | ||
49 | struct sclp_req req; | ||
50 | struct completion completion; | ||
51 | } __attribute__((packed)); | ||
52 | |||
53 | static int do_configure(sclp_cmdw_t cmd) | ||
54 | { | ||
55 | struct chp_cfg_data *data; | ||
56 | int rc; | ||
57 | |||
58 | /* Prepare sccb. */ | ||
59 | data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
60 | if (!data) | ||
61 | return -ENOMEM; | ||
62 | data->sccb.header.length = sizeof(struct chp_cfg_sccb); | ||
63 | data->req.command = cmd; | ||
64 | data->req.sccb = &(data->sccb); | ||
65 | data->req.status = SCLP_REQ_FILLED; | ||
66 | data->req.callback = chp_callback; | ||
67 | data->req.callback_data = &(data->completion); | ||
68 | init_completion(&data->completion); | ||
69 | |||
70 | /* Perform sclp request. */ | ||
71 | rc = sclp_add_request(&(data->req)); | ||
72 | if (rc) | ||
73 | goto out; | ||
74 | wait_for_completion(&data->completion); | ||
75 | |||
76 | /* Check response .*/ | ||
77 | if (data->req.status != SCLP_REQ_DONE) { | ||
78 | printk(KERN_WARNING TAG "configure channel-path request failed " | ||
79 | "(status=0x%02x)\n", data->req.status); | ||
80 | rc = -EIO; | ||
81 | goto out; | ||
82 | } | ||
83 | switch (data->sccb.header.response_code) { | ||
84 | case 0x0020: | ||
85 | case 0x0120: | ||
86 | case 0x0440: | ||
87 | case 0x0450: | ||
88 | break; | ||
89 | default: | ||
90 | printk(KERN_WARNING TAG "configure channel-path failed " | ||
91 | "(cmd=0x%08x, response=0x%04x)\n", cmd, | ||
92 | data->sccb.header.response_code); | ||
93 | rc = -EIO; | ||
94 | break; | ||
95 | } | ||
96 | out: | ||
97 | free_page((unsigned long) data); | ||
98 | |||
99 | return rc; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * sclp_chp_configure - perform configure channel-path sclp command | ||
104 | * @chpid: channel-path ID | ||
105 | * | ||
106 | * Perform configure channel-path command sclp command for specified chpid. | ||
107 | * Return 0 after command successfully finished, non-zero otherwise. | ||
108 | */ | ||
109 | int sclp_chp_configure(struct chp_id chpid) | ||
110 | { | ||
111 | return do_configure(get_configure_cmdw(chpid)); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * sclp_chp_deconfigure - perform deconfigure channel-path sclp command | ||
116 | * @chpid: channel-path ID | ||
117 | * | ||
118 | * Perform deconfigure channel-path command sclp command for specified chpid | ||
119 | * and wait for completion. On success return 0. Return non-zero otherwise. | ||
120 | */ | ||
121 | int sclp_chp_deconfigure(struct chp_id chpid) | ||
122 | { | ||
123 | return do_configure(get_deconfigure_cmdw(chpid)); | ||
124 | } | ||
125 | |||
126 | struct chp_info_sccb { | ||
127 | struct sccb_header header; | ||
128 | u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; | ||
129 | u8 standby[SCLP_CHP_INFO_MASK_SIZE]; | ||
130 | u8 configured[SCLP_CHP_INFO_MASK_SIZE]; | ||
131 | u8 ccm; | ||
132 | u8 reserved[6]; | ||
133 | u8 cssid; | ||
134 | } __attribute__((packed)); | ||
135 | |||
136 | struct chp_info_data { | ||
137 | struct chp_info_sccb sccb; | ||
138 | struct sclp_req req; | ||
139 | struct completion completion; | ||
140 | } __attribute__((packed)); | ||
141 | |||
142 | /** | ||
143 | * sclp_chp_read_info - perform read channel-path information sclp command | ||
144 | * @info: resulting channel-path information data | ||
145 | * | ||
146 | * Perform read channel-path information sclp command and wait for completion. | ||
147 | * On success, store channel-path information in @info and return 0. Return | ||
148 | * non-zero otherwise. | ||
149 | */ | ||
150 | int sclp_chp_read_info(struct sclp_chp_info *info) | ||
151 | { | ||
152 | struct chp_info_data *data; | ||
153 | int rc; | ||
154 | |||
155 | /* Prepare sccb. */ | ||
156 | data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
157 | if (!data) | ||
158 | return -ENOMEM; | ||
159 | data->sccb.header.length = sizeof(struct chp_info_sccb); | ||
160 | data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION; | ||
161 | data->req.sccb = &(data->sccb); | ||
162 | data->req.status = SCLP_REQ_FILLED; | ||
163 | data->req.callback = chp_callback; | ||
164 | data->req.callback_data = &(data->completion); | ||
165 | init_completion(&data->completion); | ||
166 | |||
167 | /* Perform sclp request. */ | ||
168 | rc = sclp_add_request(&(data->req)); | ||
169 | if (rc) | ||
170 | goto out; | ||
171 | wait_for_completion(&data->completion); | ||
172 | |||
173 | /* Check response .*/ | ||
174 | if (data->req.status != SCLP_REQ_DONE) { | ||
175 | printk(KERN_WARNING TAG "read channel-path info request failed " | ||
176 | "(status=0x%02x)\n", data->req.status); | ||
177 | rc = -EIO; | ||
178 | goto out; | ||
179 | } | ||
180 | if (data->sccb.header.response_code != 0x0010) { | ||
181 | printk(KERN_WARNING TAG "read channel-path info failed " | ||
182 | "(response=0x%04x)\n", data->sccb.header.response_code); | ||
183 | rc = -EIO; | ||
184 | goto out; | ||
185 | } | ||
186 | memcpy(info->recognized, data->sccb.recognized, | ||
187 | SCLP_CHP_INFO_MASK_SIZE); | ||
188 | memcpy(info->standby, data->sccb.standby, | ||
189 | SCLP_CHP_INFO_MASK_SIZE); | ||
190 | memcpy(info->configured, data->sccb.configured, | ||
191 | SCLP_CHP_INFO_MASK_SIZE); | ||
192 | out: | ||
193 | free_page((unsigned long) data); | ||
194 | |||
195 | return rc; | ||
196 | } | ||
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 8a5839147f8a..0e92c8c89860 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c | |||
@@ -10,9 +10,14 @@ | |||
10 | #include <linux/bug.h> | 10 | #include <linux/bug.h> |
11 | #include <linux/workqueue.h> | 11 | #include <linux/workqueue.h> |
12 | #include <linux/spinlock.h> | 12 | #include <linux/spinlock.h> |
13 | #include <linux/init.h> | ||
14 | #include <linux/jiffies.h> | ||
15 | #include <linux/wait.h> | ||
16 | #include <linux/mutex.h> | ||
13 | #include <asm/errno.h> | 17 | #include <asm/errno.h> |
18 | #include <asm/chpid.h> | ||
19 | #include <asm/sclp.h> | ||
14 | 20 | ||
15 | #include "chpid.h" | ||
16 | #include "cio.h" | 21 | #include "cio.h" |
17 | #include "css.h" | 22 | #include "css.h" |
18 | #include "ioasm.h" | 23 | #include "ioasm.h" |
@@ -20,6 +25,32 @@ | |||
20 | #include "chp.h" | 25 | #include "chp.h" |
21 | 26 | ||
22 | #define to_channelpath(device) container_of(device, struct channel_path, dev) | 27 | #define to_channelpath(device) container_of(device, struct channel_path, dev) |
28 | #define CHP_INFO_UPDATE_INTERVAL 1*HZ | ||
29 | |||
30 | enum cfg_task_t { | ||
31 | cfg_none, | ||
32 | cfg_configure, | ||
33 | cfg_deconfigure | ||
34 | }; | ||
35 | |||
36 | /* Map for pending configure tasks. */ | ||
37 | static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1]; | ||
38 | static DEFINE_MUTEX(cfg_lock); | ||
39 | static int cfg_busy; | ||
40 | |||
41 | /* Map for channel-path status. */ | ||
42 | static struct sclp_chp_info chp_info; | ||
43 | static DEFINE_MUTEX(info_lock); | ||
44 | |||
45 | /* Time after which channel-path status may be outdated. */ | ||
46 | static unsigned long chp_info_expires; | ||
47 | |||
48 | /* Workqueue to perform pending configure tasks. */ | ||
49 | static struct workqueue_struct *chp_wq; | ||
50 | static struct work_struct cfg_work; | ||
51 | |||
52 | /* Wait queue for configure completion events. */ | ||
53 | static wait_queue_head_t cfg_wait_queue; | ||
23 | 54 | ||
24 | /* Return channel_path struct for given chpid. */ | 55 | /* Return channel_path struct for given chpid. */ |
25 | static inline struct channel_path *chpid_to_chp(struct chp_id chpid) | 56 | static inline struct channel_path *chpid_to_chp(struct chp_id chpid) |
@@ -251,6 +282,43 @@ static ssize_t chp_status_write(struct device *dev, | |||
251 | 282 | ||
252 | static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); | 283 | static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); |
253 | 284 | ||
285 | static ssize_t chp_configure_show(struct device *dev, | ||
286 | struct device_attribute *attr, char *buf) | ||
287 | { | ||
288 | struct channel_path *cp; | ||
289 | int status; | ||
290 | |||
291 | cp = container_of(dev, struct channel_path, dev); | ||
292 | status = chp_info_get_status(cp->chpid); | ||
293 | if (status < 0) | ||
294 | return status; | ||
295 | |||
296 | return snprintf(buf, PAGE_SIZE, "%d\n", status); | ||
297 | } | ||
298 | |||
299 | static int cfg_wait_idle(void); | ||
300 | |||
301 | static ssize_t chp_configure_write(struct device *dev, | ||
302 | struct device_attribute *attr, | ||
303 | const char *buf, size_t count) | ||
304 | { | ||
305 | struct channel_path *cp; | ||
306 | int val; | ||
307 | char delim; | ||
308 | |||
309 | if (sscanf(buf, "%d %c", &val, &delim) != 1) | ||
310 | return -EINVAL; | ||
311 | if (val != 0 && val != 1) | ||
312 | return -EINVAL; | ||
313 | cp = container_of(dev, struct channel_path, dev); | ||
314 | chp_cfg_schedule(cp->chpid, val); | ||
315 | cfg_wait_idle(); | ||
316 | |||
317 | return count; | ||
318 | } | ||
319 | |||
320 | static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write); | ||
321 | |||
254 | static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, | 322 | static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, |
255 | char *buf) | 323 | char *buf) |
256 | { | 324 | { |
@@ -293,6 +361,7 @@ static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); | |||
293 | 361 | ||
294 | static struct attribute * chp_attrs[] = { | 362 | static struct attribute * chp_attrs[] = { |
295 | &dev_attr_status.attr, | 363 | &dev_attr_status.attr, |
364 | &dev_attr_configure.attr, | ||
296 | &dev_attr_type.attr, | 365 | &dev_attr_type.attr, |
297 | &dev_attr_cmg.attr, | 366 | &dev_attr_cmg.attr, |
298 | &dev_attr_shared.attr, | 367 | &dev_attr_shared.attr, |
@@ -323,6 +392,8 @@ int chp_new(struct chp_id chpid) | |||
323 | struct channel_path *chp; | 392 | struct channel_path *chp; |
324 | int ret; | 393 | int ret; |
325 | 394 | ||
395 | if (chp_is_registered(chpid)) | ||
396 | return 0; | ||
326 | chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); | 397 | chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); |
327 | if (!chp) | 398 | if (!chp) |
328 | return -ENOMEM; | 399 | return -ENOMEM; |
@@ -435,3 +506,180 @@ int chp_process_crw(int id, int status) | |||
435 | return 0; | 506 | return 0; |
436 | } | 507 | } |
437 | } | 508 | } |
509 | |||
510 | static inline int info_bit_num(struct chp_id id) | ||
511 | { | ||
512 | return id.id + id.cssid * (__MAX_CHPID + 1); | ||
513 | } | ||
514 | |||
515 | /* Force chp_info refresh on next call to info_validate(). */ | ||
516 | static void info_expire(void) | ||
517 | { | ||
518 | mutex_lock(&info_lock); | ||
519 | chp_info_expires = jiffies - 1; | ||
520 | mutex_unlock(&info_lock); | ||
521 | } | ||
522 | |||
523 | /* Ensure that chp_info is up-to-date. */ | ||
524 | static int info_update(void) | ||
525 | { | ||
526 | int rc; | ||
527 | |||
528 | mutex_lock(&info_lock); | ||
529 | rc = 0; | ||
530 | if (time_after(jiffies, chp_info_expires)) { | ||
531 | /* Data is too old, update. */ | ||
532 | rc = sclp_chp_read_info(&chp_info); | ||
533 | chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ; | ||
534 | } | ||
535 | mutex_unlock(&info_lock); | ||
536 | |||
537 | return rc; | ||
538 | } | ||
539 | |||
540 | /** | ||
541 | * chp_info_get_status - retrieve configure status of a channel-path | ||
542 | * @chpid: channel-path ID | ||
543 | * | ||
544 | * On success, return 0 for standby, 1 for configured, 2 for reserved, | ||
545 | * 3 for not recognized. Return negative error code on error. | ||
546 | */ | ||
547 | int chp_info_get_status(struct chp_id chpid) | ||
548 | { | ||
549 | int rc; | ||
550 | int bit; | ||
551 | |||
552 | rc = info_update(); | ||
553 | if (rc) | ||
554 | return rc; | ||
555 | |||
556 | bit = info_bit_num(chpid); | ||
557 | mutex_lock(&info_lock); | ||
558 | if (!chp_test_bit(chp_info.recognized, bit)) | ||
559 | rc = CHP_STATUS_NOT_RECOGNIZED; | ||
560 | else if (chp_test_bit(chp_info.configured, bit)) | ||
561 | rc = CHP_STATUS_CONFIGURED; | ||
562 | else if (chp_test_bit(chp_info.standby, bit)) | ||
563 | rc = CHP_STATUS_STANDBY; | ||
564 | else | ||
565 | rc = CHP_STATUS_RESERVED; | ||
566 | mutex_unlock(&info_lock); | ||
567 | |||
568 | return rc; | ||
569 | } | ||
570 | |||
571 | /* Return configure task for chpid. */ | ||
572 | static enum cfg_task_t cfg_get_task(struct chp_id chpid) | ||
573 | { | ||
574 | return chp_cfg_task[chpid.cssid][chpid.id]; | ||
575 | } | ||
576 | |||
577 | /* Set configure task for chpid. */ | ||
578 | static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg) | ||
579 | { | ||
580 | chp_cfg_task[chpid.cssid][chpid.id] = cfg; | ||
581 | } | ||
582 | |||
583 | /* Perform one configure/deconfigure request. Reschedule work function until | ||
584 | * last request. */ | ||
585 | static void cfg_func(struct work_struct *work) | ||
586 | { | ||
587 | struct chp_id chpid; | ||
588 | enum cfg_task_t t; | ||
589 | |||
590 | mutex_lock(&cfg_lock); | ||
591 | t = cfg_none; | ||
592 | chp_id_for_each(&chpid) { | ||
593 | t = cfg_get_task(chpid); | ||
594 | if (t != cfg_none) { | ||
595 | cfg_set_task(chpid, cfg_none); | ||
596 | break; | ||
597 | } | ||
598 | } | ||
599 | mutex_unlock(&cfg_lock); | ||
600 | |||
601 | switch (t) { | ||
602 | case cfg_configure: | ||
603 | sclp_chp_configure(chpid); | ||
604 | info_expire(); | ||
605 | chsc_chp_online(chpid); | ||
606 | break; | ||
607 | case cfg_deconfigure: | ||
608 | sclp_chp_deconfigure(chpid); | ||
609 | info_expire(); | ||
610 | chsc_chp_offline(chpid); | ||
611 | break; | ||
612 | case cfg_none: | ||
613 | /* Get updated information after last change. */ | ||
614 | info_update(); | ||
615 | mutex_lock(&cfg_lock); | ||
616 | cfg_busy = 0; | ||
617 | mutex_unlock(&cfg_lock); | ||
618 | wake_up_interruptible(&cfg_wait_queue); | ||
619 | return; | ||
620 | } | ||
621 | queue_work(chp_wq, &cfg_work); | ||
622 | } | ||
623 | |||
624 | /** | ||
625 | * chp_cfg_schedule - schedule chpid configuration request | ||
626 | * @chpid - channel-path ID | ||
627 | * @configure - Non-zero for configure, zero for deconfigure | ||
628 | * | ||
629 | * Schedule a channel-path configuration/deconfiguration request. | ||
630 | */ | ||
631 | void chp_cfg_schedule(struct chp_id chpid, int configure) | ||
632 | { | ||
633 | CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id, | ||
634 | configure); | ||
635 | mutex_lock(&cfg_lock); | ||
636 | cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure); | ||
637 | cfg_busy = 1; | ||
638 | mutex_unlock(&cfg_lock); | ||
639 | queue_work(chp_wq, &cfg_work); | ||
640 | } | ||
641 | |||
642 | /** | ||
643 | * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request | ||
644 | * @chpid - channel-path ID | ||
645 | * | ||
646 | * Cancel an active channel-path deconfiguration request if it has not yet | ||
647 | * been performed. | ||
648 | */ | ||
649 | void chp_cfg_cancel_deconfigure(struct chp_id chpid) | ||
650 | { | ||
651 | CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id); | ||
652 | mutex_lock(&cfg_lock); | ||
653 | if (cfg_get_task(chpid) == cfg_deconfigure) | ||
654 | cfg_set_task(chpid, cfg_none); | ||
655 | mutex_unlock(&cfg_lock); | ||
656 | } | ||
657 | |||
658 | static int cfg_wait_idle(void) | ||
659 | { | ||
660 | if (wait_event_interruptible(cfg_wait_queue, !cfg_busy)) | ||
661 | return -ERESTARTSYS; | ||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | static int __init chp_init(void) | ||
666 | { | ||
667 | struct chp_id chpid; | ||
668 | |||
669 | chp_wq = create_singlethread_workqueue("cio_chp"); | ||
670 | if (!chp_wq) | ||
671 | return -ENOMEM; | ||
672 | INIT_WORK(&cfg_work, cfg_func); | ||
673 | init_waitqueue_head(&cfg_wait_queue); | ||
674 | if (info_update()) | ||
675 | return 0; | ||
676 | /* Register available channel-paths. */ | ||
677 | chp_id_for_each(&chpid) { | ||
678 | if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED) | ||
679 | chp_new(chpid); | ||
680 | } | ||
681 | |||
682 | return 0; | ||
683 | } | ||
684 | |||
685 | subsys_initcall(chp_init); | ||
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index ac2b1a9c3bc2..862af69d9707 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h | |||
@@ -10,10 +10,23 @@ | |||
10 | 10 | ||
11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
12 | #include <linux/device.h> | 12 | #include <linux/device.h> |
13 | 13 | #include <asm/chpid.h> | |
14 | #include "chpid.h" | ||
15 | #include "chsc.h" | 14 | #include "chsc.h" |
16 | 15 | ||
16 | #define CHP_STATUS_STANDBY 0 | ||
17 | #define CHP_STATUS_CONFIGURED 1 | ||
18 | #define CHP_STATUS_RESERVED 2 | ||
19 | #define CHP_STATUS_NOT_RECOGNIZED 3 | ||
20 | |||
21 | static inline int chp_test_bit(u8 *bitmap, int num) | ||
22 | { | ||
23 | int byte = num >> 3; | ||
24 | int mask = 128 >> (num & 7); | ||
25 | |||
26 | return (bitmap[byte] & mask) ? 1 : 0; | ||
27 | } | ||
28 | |||
29 | |||
17 | struct channel_path { | 30 | struct channel_path { |
18 | struct chp_id chpid; | 31 | struct chp_id chpid; |
19 | int state; | 32 | int state; |
@@ -33,5 +46,8 @@ int chp_process_crw(int id, int available); | |||
33 | void chp_remove_cmg_attr(struct channel_path *chp); | 46 | void chp_remove_cmg_attr(struct channel_path *chp); |
34 | int chp_add_cmg_attr(struct channel_path *chp); | 47 | int chp_add_cmg_attr(struct channel_path *chp); |
35 | int chp_new(struct chp_id chpid); | 48 | int chp_new(struct chp_id chpid); |
49 | void chp_cfg_schedule(struct chp_id chpid, int configure); | ||
50 | void chp_cfg_cancel_deconfigure(struct chp_id chpid); | ||
51 | int chp_info_get_status(struct chp_id chpid); | ||
36 | 52 | ||
37 | #endif /* S390_CHP_H */ | 53 | #endif /* S390_CHP_H */ |
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index d99f525eac08..3dec460bba27 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -15,12 +15,12 @@ | |||
15 | #include <linux/device.h> | 15 | #include <linux/device.h> |
16 | 16 | ||
17 | #include <asm/cio.h> | 17 | #include <asm/cio.h> |
18 | #include <asm/chpid.h> | ||
18 | 19 | ||
19 | #include "css.h" | 20 | #include "css.h" |
20 | #include "cio.h" | 21 | #include "cio.h" |
21 | #include "cio_debug.h" | 22 | #include "cio_debug.h" |
22 | #include "ioasm.h" | 23 | #include "ioasm.h" |
23 | #include "chpid.h" | ||
24 | #include "chp.h" | 24 | #include "chp.h" |
25 | #include "chsc.h" | 25 | #include "chsc.h" |
26 | 26 | ||
@@ -498,6 +498,45 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | |||
498 | return rc; | 498 | return rc; |
499 | } | 499 | } |
500 | 500 | ||
501 | struct chp_config_data { | ||
502 | u8 map[32]; | ||
503 | u8 op; | ||
504 | u8 pc; | ||
505 | }; | ||
506 | |||
507 | static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) | ||
508 | { | ||
509 | struct chp_config_data *data; | ||
510 | struct chp_id chpid; | ||
511 | int num; | ||
512 | |||
513 | CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); | ||
514 | if (sei_area->rs != 0) | ||
515 | return 0; | ||
516 | data = (struct chp_config_data *) &(sei_area->ccdf); | ||
517 | chp_id_init(&chpid); | ||
518 | for (num = 0; num <= __MAX_CHPID; num++) { | ||
519 | if (!chp_test_bit(data->map, num)) | ||
520 | continue; | ||
521 | chpid.id = num; | ||
522 | printk(KERN_WARNING "cio: processing configure event %d for " | ||
523 | "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id); | ||
524 | switch (data->op) { | ||
525 | case 0: | ||
526 | chp_cfg_schedule(chpid, 1); | ||
527 | break; | ||
528 | case 1: | ||
529 | chp_cfg_schedule(chpid, 0); | ||
530 | break; | ||
531 | case 2: | ||
532 | chp_cfg_cancel_deconfigure(chpid); | ||
533 | break; | ||
534 | } | ||
535 | } | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
501 | static int chsc_process_sei(struct chsc_sei_area *sei_area) | 540 | static int chsc_process_sei(struct chsc_sei_area *sei_area) |
502 | { | 541 | { |
503 | int rc; | 542 | int rc; |
@@ -514,6 +553,9 @@ static int chsc_process_sei(struct chsc_sei_area *sei_area) | |||
514 | case 2: /* i/o resource accessibiliy */ | 553 | case 2: /* i/o resource accessibiliy */ |
515 | rc = chsc_process_sei_res_acc(sei_area); | 554 | rc = chsc_process_sei_res_acc(sei_area); |
516 | break; | 555 | break; |
556 | case 8: /* channel-path-configuration notification */ | ||
557 | rc = chsc_process_sei_chp_config(sei_area); | ||
558 | break; | ||
517 | default: /* other stuff */ | 559 | default: /* other stuff */ |
518 | CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", | 560 | CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", |
519 | sei_area->cc); | 561 | sei_area->cc); |
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 0e40defc6087..322586f27cc0 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <linux/device.h> | 5 | #include <linux/device.h> |
6 | #include "chpid.h" | 6 | #include <asm/chpid.h> |
7 | 7 | ||
8 | #define CHSC_SDA_OC_MSS 0x2 | 8 | #define CHSC_SDA_OC_MSS 0x2 |
9 | 9 | ||
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 7dd0649c95d1..ea1defba5693 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <asm/setup.h> | 22 | #include <asm/setup.h> |
23 | #include <asm/reset.h> | 23 | #include <asm/reset.h> |
24 | #include <asm/ipl.h> | 24 | #include <asm/ipl.h> |
25 | #include <asm/chpid.h> | ||
25 | #include "airq.h" | 26 | #include "airq.h" |
26 | #include "cio.h" | 27 | #include "cio.h" |
27 | #include "css.h" | 28 | #include "css.h" |
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 35154a210357..e62ab5c52863 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include "schid.h" | 4 | #include "schid.h" |
5 | #include <linux/mutex.h> | 5 | #include <linux/mutex.h> |
6 | #include <linux/device.h> | ||
6 | 7 | ||
7 | /* | 8 | /* |
8 | * where we put the ssd info | 9 | * where we put the ssd info |
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 4319e1ced791..b2b1a265c602 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h | |||
@@ -5,8 +5,10 @@ | |||
5 | #include <linux/wait.h> | 5 | #include <linux/wait.h> |
6 | #include <linux/workqueue.h> | 6 | #include <linux/workqueue.h> |
7 | #include <linux/device.h> | 7 | #include <linux/device.h> |
8 | #include <linux/types.h> | ||
8 | 9 | ||
9 | #include <asm/cio.h> | 10 | #include <asm/cio.h> |
11 | #include <asm/chpid.h> | ||
10 | 12 | ||
11 | #include "schid.h" | 13 | #include "schid.h" |
12 | 14 | ||
@@ -149,8 +151,6 @@ extern void css_reiterate_subchannels(void); | |||
149 | 151 | ||
150 | #define __MAX_SUBCHANNEL 65535 | 152 | #define __MAX_SUBCHANNEL 65535 |
151 | #define __MAX_SSID 3 | 153 | #define __MAX_SSID 3 |
152 | #define __MAX_CHPID 255 | ||
153 | #define __MAX_CSSID 0 | ||
154 | 154 | ||
155 | struct channel_subsystem { | 155 | struct channel_subsystem { |
156 | u8 cssid; | 156 | u8 cssid; |
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index db3d1b990f58..d6226881d0df 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -15,6 +15,7 @@ | |||
15 | 15 | ||
16 | #include <asm/ccwdev.h> | 16 | #include <asm/ccwdev.h> |
17 | #include <asm/cio.h> | 17 | #include <asm/cio.h> |
18 | #include <asm/chpid.h> | ||
18 | 19 | ||
19 | #include "cio.h" | 20 | #include "cio.h" |
20 | #include "cio_debug.h" | 21 | #include "cio_debug.h" |
@@ -22,7 +23,6 @@ | |||
22 | #include "device.h" | 23 | #include "device.h" |
23 | #include "chsc.h" | 24 | #include "chsc.h" |
24 | #include "ioasm.h" | 25 | #include "ioasm.h" |
25 | #include "chpid.h" | ||
26 | #include "chp.h" | 26 | #include "chp.h" |
27 | 27 | ||
28 | int | 28 | int |
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index d819ae2ee9ae..16f59fcb66b1 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c | |||
@@ -16,13 +16,13 @@ | |||
16 | 16 | ||
17 | #include <asm/ccwdev.h> | 17 | #include <asm/ccwdev.h> |
18 | #include <asm/idals.h> | 18 | #include <asm/idals.h> |
19 | #include <asm/chpid.h> | ||
19 | 20 | ||
20 | #include "cio.h" | 21 | #include "cio.h" |
21 | #include "cio_debug.h" | 22 | #include "cio_debug.h" |
22 | #include "css.h" | 23 | #include "css.h" |
23 | #include "chsc.h" | 24 | #include "chsc.h" |
24 | #include "device.h" | 25 | #include "device.h" |
25 | #include "chpid.h" | ||
26 | #include "chp.h" | 26 | #include "chp.h" |
27 | 27 | ||
28 | int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) | 28 | int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) |
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index 78c7db6daa84..7153dd959082 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h | |||
@@ -1,8 +1,8 @@ | |||
1 | #ifndef S390_CIO_IOASM_H | 1 | #ifndef S390_CIO_IOASM_H |
2 | #define S390_CIO_IOASM_H | 2 | #define S390_CIO_IOASM_H |
3 | 3 | ||
4 | #include <asm/chpid.h> | ||
4 | #include "schid.h" | 5 | #include "schid.h" |
5 | #include "chpid.h" | ||
6 | 6 | ||
7 | /* | 7 | /* |
8 | * TPI info structure | 8 | * TPI info structure |
diff --git a/drivers/s390/cio/chpid.h b/include/asm-s390/chpid.h index 44cc00ed9b59..b203336fd892 100644 --- a/drivers/s390/cio/chpid.h +++ b/include/asm-s390/chpid.h | |||
@@ -5,12 +5,14 @@ | |||
5 | * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 5 | * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #ifndef S390_CHP_ID_H | 8 | #ifndef _ASM_S390_CHPID_H |
9 | #define S390_CHP_ID_H S390_CHP_ID_H | 9 | #define _ASM_S390_CHPID_H _ASM_S390_CHPID_H |
10 | 10 | ||
11 | #include <linux/string.h> | 11 | #include <linux/string.h> |
12 | #include <asm/types.h> | 12 | #include <asm/types.h> |
13 | #include "css.h" | 13 | #include <asm/cio.h> |
14 | |||
15 | #define __MAX_CHPID 255 | ||
14 | 16 | ||
15 | struct chp_id { | 17 | struct chp_id { |
16 | u8 reserved1; | 18 | u8 reserved1; |
@@ -48,4 +50,4 @@ static inline int chp_id_is_valid(struct chp_id *chpid) | |||
48 | #define chp_id_for_each(c) \ | 50 | #define chp_id_for_each(c) \ |
49 | for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c)) | 51 | for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c)) |
50 | 52 | ||
51 | #endif /* S390_CHP_ID_H */ | 53 | #endif /* _ASM_S390_CHPID_H */ |
diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h index 0db017bc7d09..f738d2827582 100644 --- a/include/asm-s390/cio.h +++ b/include/asm-s390/cio.h | |||
@@ -13,6 +13,7 @@ | |||
13 | #ifdef __KERNEL__ | 13 | #ifdef __KERNEL__ |
14 | 14 | ||
15 | #define LPM_ANYPATH 0xff | 15 | #define LPM_ANYPATH 0xff |
16 | #define __MAX_CSSID 0 | ||
16 | 17 | ||
17 | /* | 18 | /* |
18 | * subchannel status word | 19 | * subchannel status word |
diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h index 468b97018405..3996daaa8f54 100644 --- a/include/asm-s390/sclp.h +++ b/include/asm-s390/sclp.h | |||
@@ -9,6 +9,7 @@ | |||
9 | #define _ASM_S390_SCLP_H | 9 | #define _ASM_S390_SCLP_H |
10 | 10 | ||
11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
12 | #include <asm/chpid.h> | ||
12 | 13 | ||
13 | struct sccb_header { | 14 | struct sccb_header { |
14 | u16 length; | 15 | u16 length; |
@@ -33,7 +34,18 @@ struct sclp_readinfo_sccb { | |||
33 | u8 _reserved3[4096 - 112]; /* 112-4095 */ | 34 | u8 _reserved3[4096 - 112]; /* 112-4095 */ |
34 | } __attribute__((packed, aligned(4096))); | 35 | } __attribute__((packed, aligned(4096))); |
35 | 36 | ||
37 | #define SCLP_CHP_INFO_MASK_SIZE 32 | ||
38 | |||
39 | struct sclp_chp_info { | ||
40 | u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; | ||
41 | u8 standby[SCLP_CHP_INFO_MASK_SIZE]; | ||
42 | u8 configured[SCLP_CHP_INFO_MASK_SIZE]; | ||
43 | }; | ||
44 | |||
36 | extern struct sclp_readinfo_sccb s390_readinfo_sccb; | 45 | extern struct sclp_readinfo_sccb s390_readinfo_sccb; |
37 | extern void sclp_readinfo_early(void); | 46 | extern void sclp_readinfo_early(void); |
47 | extern int sclp_chp_configure(struct chp_id chpid); | ||
48 | extern int sclp_chp_deconfigure(struct chp_id chpid); | ||
49 | extern int sclp_chp_read_info(struct sclp_chp_info *info); | ||
38 | 50 | ||
39 | #endif /* _ASM_S390_SCLP_H */ | 51 | #endif /* _ASM_S390_SCLP_H */ |