1724117b7SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2e6b6e10aSPeter Oberparleiter /*
3b730f3a9SSebastian Ott * Copyright IBM Corp. 1999, 2010
4e6b6e10aSPeter Oberparleiter * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
5e6b6e10aSPeter Oberparleiter * Arnd Bergmann (arndb@de.ibm.com)
6e6b6e10aSPeter Oberparleiter * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
7e6b6e10aSPeter Oberparleiter */
8e6b6e10aSPeter Oberparleiter
9e6b6e10aSPeter Oberparleiter #include <linux/bug.h>
10e6b6e10aSPeter Oberparleiter #include <linux/workqueue.h>
11e6b6e10aSPeter Oberparleiter #include <linux/spinlock.h>
123a4c5d59SHeiko Carstens #include <linux/export.h>
133a4c5d59SHeiko Carstens #include <linux/sched.h>
14e5854a58SPeter Oberparleiter #include <linux/init.h>
15e5854a58SPeter Oberparleiter #include <linux/jiffies.h>
16e5854a58SPeter Oberparleiter #include <linux/wait.h>
17e5854a58SPeter Oberparleiter #include <linux/mutex.h>
18a0ea22c3SCornelia Huck #include <linux/errno.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
20e5854a58SPeter Oberparleiter #include <asm/chpid.h>
21e5854a58SPeter Oberparleiter #include <asm/sclp.h>
22f5daba1dSHeiko Carstens #include <asm/crw.h>
23e6b6e10aSPeter Oberparleiter
24e6b6e10aSPeter Oberparleiter #include "cio.h"
25e6b6e10aSPeter Oberparleiter #include "css.h"
26e6b6e10aSPeter Oberparleiter #include "ioasm.h"
27e6b6e10aSPeter Oberparleiter #include "cio_debug.h"
28e6b6e10aSPeter Oberparleiter #include "chp.h"
29e6b6e10aSPeter Oberparleiter
30e6b6e10aSPeter Oberparleiter #define to_channelpath(device) container_of(device, struct channel_path, dev)
31e5854a58SPeter Oberparleiter #define CHP_INFO_UPDATE_INTERVAL 1*HZ
32e5854a58SPeter Oberparleiter
33e5854a58SPeter Oberparleiter enum cfg_task_t {
34e5854a58SPeter Oberparleiter cfg_none,
35e5854a58SPeter Oberparleiter cfg_configure,
36e5854a58SPeter Oberparleiter cfg_deconfigure
37e5854a58SPeter Oberparleiter };
38e5854a58SPeter Oberparleiter
39e5854a58SPeter Oberparleiter /* Map for pending configure tasks. */
40e5854a58SPeter Oberparleiter static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1];
41837c5220SSebastian Ott static DEFINE_SPINLOCK(cfg_lock);
42e5854a58SPeter Oberparleiter
43e5854a58SPeter Oberparleiter /* Map for channel-path status. */
44e5854a58SPeter Oberparleiter static struct sclp_chp_info chp_info;
45e5854a58SPeter Oberparleiter static DEFINE_MUTEX(info_lock);
46e5854a58SPeter Oberparleiter
47e5854a58SPeter Oberparleiter /* Time after which channel-path status may be outdated. */
48e5854a58SPeter Oberparleiter static unsigned long chp_info_expires;
49e5854a58SPeter Oberparleiter
50e5854a58SPeter Oberparleiter static struct work_struct cfg_work;
51e5854a58SPeter Oberparleiter
52e5854a58SPeter Oberparleiter /* Wait queue for configure completion events. */
533081e616SVineeth Vijayan static DECLARE_WAIT_QUEUE_HEAD(cfg_wait_queue);
54e6b6e10aSPeter Oberparleiter
55e6b6e10aSPeter Oberparleiter /* Set vary state for given chpid. */
set_chp_logically_online(struct chp_id chpid,int onoff)56e6b6e10aSPeter Oberparleiter static void set_chp_logically_online(struct chp_id chpid, int onoff)
57e6b6e10aSPeter Oberparleiter {
58e6b6e10aSPeter Oberparleiter chpid_to_chp(chpid)->state = onoff;
59e6b6e10aSPeter Oberparleiter }
60e6b6e10aSPeter Oberparleiter
61af901ca1SAndré Goddard Rosa /* On success return 0 if channel-path is varied offline, 1 if it is varied
62e6b6e10aSPeter Oberparleiter * online. Return -ENODEV if channel-path is not registered. */
chp_get_status(struct chp_id chpid)63e6b6e10aSPeter Oberparleiter int chp_get_status(struct chp_id chpid)
64e6b6e10aSPeter Oberparleiter {
65e6b6e10aSPeter Oberparleiter return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV);
66e6b6e10aSPeter Oberparleiter }
67e6b6e10aSPeter Oberparleiter
68e6b6e10aSPeter Oberparleiter /**
69e6b6e10aSPeter Oberparleiter * chp_get_sch_opm - return opm for subchannel
70e6b6e10aSPeter Oberparleiter * @sch: subchannel
71e6b6e10aSPeter Oberparleiter *
72e6b6e10aSPeter Oberparleiter * Calculate and return the operational path mask (opm) based on the chpids
73e6b6e10aSPeter Oberparleiter * used by the subchannel and the status of the associated channel-paths.
74e6b6e10aSPeter Oberparleiter */
chp_get_sch_opm(struct subchannel * sch)75e6b6e10aSPeter Oberparleiter u8 chp_get_sch_opm(struct subchannel *sch)
76e6b6e10aSPeter Oberparleiter {
77e6b6e10aSPeter Oberparleiter struct chp_id chpid;
78e6b6e10aSPeter Oberparleiter int opm;
79e6b6e10aSPeter Oberparleiter int i;
80e6b6e10aSPeter Oberparleiter
81e6b6e10aSPeter Oberparleiter opm = 0;
82e6b6e10aSPeter Oberparleiter chp_id_init(&chpid);
83e6b6e10aSPeter Oberparleiter for (i = 0; i < 8; i++) {
84e6b6e10aSPeter Oberparleiter opm <<= 1;
85e6b6e10aSPeter Oberparleiter chpid.id = sch->schib.pmcw.chpid[i];
86e6b6e10aSPeter Oberparleiter if (chp_get_status(chpid) != 0)
87e6b6e10aSPeter Oberparleiter opm |= 1;
88e6b6e10aSPeter Oberparleiter }
89e6b6e10aSPeter Oberparleiter return opm;
90e6b6e10aSPeter Oberparleiter }
9144a1c19eSCornelia Huck EXPORT_SYMBOL_GPL(chp_get_sch_opm);
92e6b6e10aSPeter Oberparleiter
93e6b6e10aSPeter Oberparleiter /**
94e6b6e10aSPeter Oberparleiter * chp_is_registered - check if a channel-path is registered
95e6b6e10aSPeter Oberparleiter * @chpid: channel-path ID
96e6b6e10aSPeter Oberparleiter *
97e6b6e10aSPeter Oberparleiter * Return non-zero if a channel-path with the given chpid is registered,
98e6b6e10aSPeter Oberparleiter * zero otherwise.
99e6b6e10aSPeter Oberparleiter */
chp_is_registered(struct chp_id chpid)100e6b6e10aSPeter Oberparleiter int chp_is_registered(struct chp_id chpid)
101e6b6e10aSPeter Oberparleiter {
102e6b6e10aSPeter Oberparleiter return chpid_to_chp(chpid) != NULL;
103e6b6e10aSPeter Oberparleiter }
104e6b6e10aSPeter Oberparleiter
105e6b6e10aSPeter Oberparleiter /*
106e6b6e10aSPeter Oberparleiter * Function: s390_vary_chpid
107e6b6e10aSPeter Oberparleiter * Varies the specified chpid online or offline
108e6b6e10aSPeter Oberparleiter */
s390_vary_chpid(struct chp_id chpid,int on)109e6b6e10aSPeter Oberparleiter static int s390_vary_chpid(struct chp_id chpid, int on)
110e6b6e10aSPeter Oberparleiter {
111e6b6e10aSPeter Oberparleiter char dbf_text[15];
112e6b6e10aSPeter Oberparleiter int status;
113e6b6e10aSPeter Oberparleiter
114e6b6e10aSPeter Oberparleiter sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid,
115e6b6e10aSPeter Oberparleiter chpid.id);
116e6b6e10aSPeter Oberparleiter CIO_TRACE_EVENT(2, dbf_text);
117e6b6e10aSPeter Oberparleiter
118e6b6e10aSPeter Oberparleiter status = chp_get_status(chpid);
119c78aa6cbSMichael Ernst if (!on && !status)
120c78aa6cbSMichael Ernst return 0;
121e6b6e10aSPeter Oberparleiter
122e6b6e10aSPeter Oberparleiter set_chp_logically_online(chpid, on);
123e6b6e10aSPeter Oberparleiter chsc_chp_vary(chpid, on);
124e6b6e10aSPeter Oberparleiter return 0;
125e6b6e10aSPeter Oberparleiter }
126e6b6e10aSPeter Oberparleiter
127e6b6e10aSPeter Oberparleiter /*
128e6b6e10aSPeter Oberparleiter * Channel measurement related functions
129e6b6e10aSPeter Oberparleiter */
chp_measurement_chars_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)1302c3c8beaSChris Wright static ssize_t chp_measurement_chars_read(struct file *filp,
1312c3c8beaSChris Wright struct kobject *kobj,
13291a69029SZhang Rui struct bin_attribute *bin_attr,
13391a69029SZhang Rui char *buf, loff_t off, size_t count)
134e6b6e10aSPeter Oberparleiter {
135e6b6e10aSPeter Oberparleiter struct channel_path *chp;
136364c8558SHeiko Carstens struct device *device;
137e6b6e10aSPeter Oberparleiter
1380d730b57Schenqiwu device = kobj_to_dev(kobj);
139364c8558SHeiko Carstens chp = to_channelpath(device);
1400d9bfe91SSebastian Ott if (chp->cmg == -1)
141e6b6e10aSPeter Oberparleiter return 0;
142e6b6e10aSPeter Oberparleiter
1430d9bfe91SSebastian Ott return memory_read_from_buffer(buf, count, &off, &chp->cmg_chars,
1440d9bfe91SSebastian Ott sizeof(chp->cmg_chars));
145e6b6e10aSPeter Oberparleiter }
146e6b6e10aSPeter Oberparleiter
147ac34bbc3SBhumika Goyal static const struct bin_attribute chp_measurement_chars_attr = {
148e6b6e10aSPeter Oberparleiter .attr = {
149e6b6e10aSPeter Oberparleiter .name = "measurement_chars",
150e6b6e10aSPeter Oberparleiter .mode = S_IRUSR,
151e6b6e10aSPeter Oberparleiter },
152e6b6e10aSPeter Oberparleiter .size = sizeof(struct cmg_chars),
153e6b6e10aSPeter Oberparleiter .read = chp_measurement_chars_read,
154e6b6e10aSPeter Oberparleiter };
155e6b6e10aSPeter Oberparleiter
chp_measurement_copy_block(struct cmg_entry * buf,struct channel_subsystem * css,struct chp_id chpid)156e6b6e10aSPeter Oberparleiter static void chp_measurement_copy_block(struct cmg_entry *buf,
157e6b6e10aSPeter Oberparleiter struct channel_subsystem *css,
158e6b6e10aSPeter Oberparleiter struct chp_id chpid)
159e6b6e10aSPeter Oberparleiter {
160e6b6e10aSPeter Oberparleiter void *area;
161e6b6e10aSPeter Oberparleiter struct cmg_entry *entry, reference_buf;
162e6b6e10aSPeter Oberparleiter int idx;
163e6b6e10aSPeter Oberparleiter
164e6b6e10aSPeter Oberparleiter if (chpid.id < 128) {
165e6b6e10aSPeter Oberparleiter area = css->cub_addr1;
166e6b6e10aSPeter Oberparleiter idx = chpid.id;
167e6b6e10aSPeter Oberparleiter } else {
168e6b6e10aSPeter Oberparleiter area = css->cub_addr2;
169e6b6e10aSPeter Oberparleiter idx = chpid.id - 128;
170e6b6e10aSPeter Oberparleiter }
171e6b6e10aSPeter Oberparleiter entry = area + (idx * sizeof(struct cmg_entry));
172e6b6e10aSPeter Oberparleiter do {
173e6b6e10aSPeter Oberparleiter memcpy(buf, entry, sizeof(*entry));
174e6b6e10aSPeter Oberparleiter memcpy(&reference_buf, entry, sizeof(*entry));
175e6b6e10aSPeter Oberparleiter } while (reference_buf.values[0] != buf->values[0]);
176e6b6e10aSPeter Oberparleiter }
177e6b6e10aSPeter Oberparleiter
chp_measurement_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)1782c3c8beaSChris Wright static ssize_t chp_measurement_read(struct file *filp, struct kobject *kobj,
17991a69029SZhang Rui struct bin_attribute *bin_attr,
18091a69029SZhang Rui char *buf, loff_t off, size_t count)
181e6b6e10aSPeter Oberparleiter {
182e6b6e10aSPeter Oberparleiter struct channel_path *chp;
183e6b6e10aSPeter Oberparleiter struct channel_subsystem *css;
184364c8558SHeiko Carstens struct device *device;
185e6b6e10aSPeter Oberparleiter unsigned int size;
186e6b6e10aSPeter Oberparleiter
1870d730b57Schenqiwu device = kobj_to_dev(kobj);
188364c8558SHeiko Carstens chp = to_channelpath(device);
189e6b6e10aSPeter Oberparleiter css = to_css(chp->dev.parent);
190e6b6e10aSPeter Oberparleiter
191e6b6e10aSPeter Oberparleiter size = sizeof(struct cmg_entry);
192e6b6e10aSPeter Oberparleiter
193e6b6e10aSPeter Oberparleiter /* Only allow single reads. */
194e6b6e10aSPeter Oberparleiter if (off || count < size)
195e6b6e10aSPeter Oberparleiter return 0;
196e6b6e10aSPeter Oberparleiter chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid);
197e6b6e10aSPeter Oberparleiter count = size;
198e6b6e10aSPeter Oberparleiter return count;
199e6b6e10aSPeter Oberparleiter }
200e6b6e10aSPeter Oberparleiter
201ac34bbc3SBhumika Goyal static const struct bin_attribute chp_measurement_attr = {
202e6b6e10aSPeter Oberparleiter .attr = {
203e6b6e10aSPeter Oberparleiter .name = "measurement",
204e6b6e10aSPeter Oberparleiter .mode = S_IRUSR,
205e6b6e10aSPeter Oberparleiter },
206e6b6e10aSPeter Oberparleiter .size = sizeof(struct cmg_entry),
207e6b6e10aSPeter Oberparleiter .read = chp_measurement_read,
208e6b6e10aSPeter Oberparleiter };
209e6b6e10aSPeter Oberparleiter
chp_remove_cmg_attr(struct channel_path * chp)210e6b6e10aSPeter Oberparleiter void chp_remove_cmg_attr(struct channel_path *chp)
211e6b6e10aSPeter Oberparleiter {
212e6b6e10aSPeter Oberparleiter device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
213e6b6e10aSPeter Oberparleiter device_remove_bin_file(&chp->dev, &chp_measurement_attr);
214e6b6e10aSPeter Oberparleiter }
215e6b6e10aSPeter Oberparleiter
chp_add_cmg_attr(struct channel_path * chp)216e6b6e10aSPeter Oberparleiter int chp_add_cmg_attr(struct channel_path *chp)
217e6b6e10aSPeter Oberparleiter {
218e6b6e10aSPeter Oberparleiter int ret;
219e6b6e10aSPeter Oberparleiter
220e6b6e10aSPeter Oberparleiter ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
221e6b6e10aSPeter Oberparleiter if (ret)
222e6b6e10aSPeter Oberparleiter return ret;
223e6b6e10aSPeter Oberparleiter ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
224e6b6e10aSPeter Oberparleiter if (ret)
225e6b6e10aSPeter Oberparleiter device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
226e6b6e10aSPeter Oberparleiter return ret;
227e6b6e10aSPeter Oberparleiter }
228e6b6e10aSPeter Oberparleiter
229e6b6e10aSPeter Oberparleiter /*
230e6b6e10aSPeter Oberparleiter * Files for the channel path entries.
231e6b6e10aSPeter Oberparleiter */
chp_status_show(struct device * dev,struct device_attribute * attr,char * buf)232e6b6e10aSPeter Oberparleiter static ssize_t chp_status_show(struct device *dev,
233e6b6e10aSPeter Oberparleiter struct device_attribute *attr, char *buf)
234e6b6e10aSPeter Oberparleiter {
23505469607SCornelia Huck struct channel_path *chp = to_channelpath(dev);
236b730f3a9SSebastian Ott int status;
237e6b6e10aSPeter Oberparleiter
238b730f3a9SSebastian Ott mutex_lock(&chp->lock);
239b730f3a9SSebastian Ott status = chp->state;
240b730f3a9SSebastian Ott mutex_unlock(&chp->lock);
241b730f3a9SSebastian Ott
242b730f3a9SSebastian Ott return status ? sprintf(buf, "online\n") : sprintf(buf, "offline\n");
243e6b6e10aSPeter Oberparleiter }
244e6b6e10aSPeter Oberparleiter
chp_status_write(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)245e6b6e10aSPeter Oberparleiter static ssize_t chp_status_write(struct device *dev,
246e6b6e10aSPeter Oberparleiter struct device_attribute *attr,
247e6b6e10aSPeter Oberparleiter const char *buf, size_t count)
248e6b6e10aSPeter Oberparleiter {
24905469607SCornelia Huck struct channel_path *cp = to_channelpath(dev);
250e6b6e10aSPeter Oberparleiter char cmd[10];
251e6b6e10aSPeter Oberparleiter int num_args;
252e6b6e10aSPeter Oberparleiter int error;
253e6b6e10aSPeter Oberparleiter
254e6b6e10aSPeter Oberparleiter num_args = sscanf(buf, "%5s", cmd);
255e6b6e10aSPeter Oberparleiter if (!num_args)
256e6b6e10aSPeter Oberparleiter return count;
257e6b6e10aSPeter Oberparleiter
258c749d8c0SVineeth Vijayan /* Wait until previous actions have settled. */
259c749d8c0SVineeth Vijayan css_wait_for_slow_path();
260c749d8c0SVineeth Vijayan
2613f8bfd9aSRasmus Villemoes if (!strncasecmp(cmd, "on", 2) || !strcmp(cmd, "1")) {
262b730f3a9SSebastian Ott mutex_lock(&cp->lock);
263e6b6e10aSPeter Oberparleiter error = s390_vary_chpid(cp->chpid, 1);
264b730f3a9SSebastian Ott mutex_unlock(&cp->lock);
2653f8bfd9aSRasmus Villemoes } else if (!strncasecmp(cmd, "off", 3) || !strcmp(cmd, "0")) {
266b730f3a9SSebastian Ott mutex_lock(&cp->lock);
267e6b6e10aSPeter Oberparleiter error = s390_vary_chpid(cp->chpid, 0);
268b730f3a9SSebastian Ott mutex_unlock(&cp->lock);
269b730f3a9SSebastian Ott } else
270e6b6e10aSPeter Oberparleiter error = -EINVAL;
271e6b6e10aSPeter Oberparleiter
272e6b6e10aSPeter Oberparleiter return error < 0 ? error : count;
273e6b6e10aSPeter Oberparleiter }
274e6b6e10aSPeter Oberparleiter
275e6b6e10aSPeter Oberparleiter static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
276e6b6e10aSPeter Oberparleiter
chp_configure_show(struct device * dev,struct device_attribute * attr,char * buf)277e5854a58SPeter Oberparleiter static ssize_t chp_configure_show(struct device *dev,
278e5854a58SPeter Oberparleiter struct device_attribute *attr, char *buf)
279e5854a58SPeter Oberparleiter {
280e5854a58SPeter Oberparleiter struct channel_path *cp;
281e5854a58SPeter Oberparleiter int status;
282e5854a58SPeter Oberparleiter
28305469607SCornelia Huck cp = to_channelpath(dev);
284e5854a58SPeter Oberparleiter status = chp_info_get_status(cp->chpid);
285e5854a58SPeter Oberparleiter if (status < 0)
286e5854a58SPeter Oberparleiter return status;
287e5854a58SPeter Oberparleiter
2884b9e0436SQing Wang return sysfs_emit(buf, "%d\n", status);
289e5854a58SPeter Oberparleiter }
290e5854a58SPeter Oberparleiter
291e5854a58SPeter Oberparleiter static int cfg_wait_idle(void);
292e5854a58SPeter Oberparleiter
chp_configure_write(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)293e5854a58SPeter Oberparleiter static ssize_t chp_configure_write(struct device *dev,
294e5854a58SPeter Oberparleiter struct device_attribute *attr,
295e5854a58SPeter Oberparleiter const char *buf, size_t count)
296e5854a58SPeter Oberparleiter {
297e5854a58SPeter Oberparleiter struct channel_path *cp;
298e5854a58SPeter Oberparleiter int val;
299e5854a58SPeter Oberparleiter char delim;
300e5854a58SPeter Oberparleiter
301e5854a58SPeter Oberparleiter if (sscanf(buf, "%d %c", &val, &delim) != 1)
302e5854a58SPeter Oberparleiter return -EINVAL;
303e5854a58SPeter Oberparleiter if (val != 0 && val != 1)
304e5854a58SPeter Oberparleiter return -EINVAL;
30505469607SCornelia Huck cp = to_channelpath(dev);
306e5854a58SPeter Oberparleiter chp_cfg_schedule(cp->chpid, val);
307e5854a58SPeter Oberparleiter cfg_wait_idle();
308e5854a58SPeter Oberparleiter
309e5854a58SPeter Oberparleiter return count;
310e5854a58SPeter Oberparleiter }
311e5854a58SPeter Oberparleiter
312e5854a58SPeter Oberparleiter static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write);
313e5854a58SPeter Oberparleiter
chp_type_show(struct device * dev,struct device_attribute * attr,char * buf)314e6b6e10aSPeter Oberparleiter static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr,
315e6b6e10aSPeter Oberparleiter char *buf)
316e6b6e10aSPeter Oberparleiter {
31705469607SCornelia Huck struct channel_path *chp = to_channelpath(dev);
318b730f3a9SSebastian Ott u8 type;
319e6b6e10aSPeter Oberparleiter
320b730f3a9SSebastian Ott mutex_lock(&chp->lock);
321b730f3a9SSebastian Ott type = chp->desc.desc;
322b730f3a9SSebastian Ott mutex_unlock(&chp->lock);
323b730f3a9SSebastian Ott return sprintf(buf, "%x\n", type);
324e6b6e10aSPeter Oberparleiter }
325e6b6e10aSPeter Oberparleiter
326e6b6e10aSPeter Oberparleiter static DEVICE_ATTR(type, 0444, chp_type_show, NULL);
327e6b6e10aSPeter Oberparleiter
chp_cmg_show(struct device * dev,struct device_attribute * attr,char * buf)328e6b6e10aSPeter Oberparleiter static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr,
329e6b6e10aSPeter Oberparleiter char *buf)
330e6b6e10aSPeter Oberparleiter {
331e6b6e10aSPeter Oberparleiter struct channel_path *chp = to_channelpath(dev);
332e6b6e10aSPeter Oberparleiter
333e6b6e10aSPeter Oberparleiter if (!chp)
334e6b6e10aSPeter Oberparleiter return 0;
335e6b6e10aSPeter Oberparleiter if (chp->cmg == -1) /* channel measurements not available */
336e6b6e10aSPeter Oberparleiter return sprintf(buf, "unknown\n");
337e6b6e10aSPeter Oberparleiter return sprintf(buf, "%x\n", chp->cmg);
338e6b6e10aSPeter Oberparleiter }
339e6b6e10aSPeter Oberparleiter
340e6b6e10aSPeter Oberparleiter static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
341e6b6e10aSPeter Oberparleiter
chp_shared_show(struct device * dev,struct device_attribute * attr,char * buf)342e6b6e10aSPeter Oberparleiter static ssize_t chp_shared_show(struct device *dev,
343e6b6e10aSPeter Oberparleiter struct device_attribute *attr, char *buf)
344e6b6e10aSPeter Oberparleiter {
345e6b6e10aSPeter Oberparleiter struct channel_path *chp = to_channelpath(dev);
346e6b6e10aSPeter Oberparleiter
347e6b6e10aSPeter Oberparleiter if (!chp)
348e6b6e10aSPeter Oberparleiter return 0;
349e6b6e10aSPeter Oberparleiter if (chp->shared == -1) /* channel measurements not available */
350e6b6e10aSPeter Oberparleiter return sprintf(buf, "unknown\n");
351e6b6e10aSPeter Oberparleiter return sprintf(buf, "%x\n", chp->shared);
352e6b6e10aSPeter Oberparleiter }
353e6b6e10aSPeter Oberparleiter
354e6b6e10aSPeter Oberparleiter static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
355e6b6e10aSPeter Oberparleiter
chp_chid_show(struct device * dev,struct device_attribute * attr,char * buf)356ba54229dSPeter Oberparleiter static ssize_t chp_chid_show(struct device *dev, struct device_attribute *attr,
357ba54229dSPeter Oberparleiter char *buf)
358ba54229dSPeter Oberparleiter {
359ba54229dSPeter Oberparleiter struct channel_path *chp = to_channelpath(dev);
360ba54229dSPeter Oberparleiter ssize_t rc;
361ba54229dSPeter Oberparleiter
362ba54229dSPeter Oberparleiter mutex_lock(&chp->lock);
363ba54229dSPeter Oberparleiter if (chp->desc_fmt1.flags & 0x10)
364ba54229dSPeter Oberparleiter rc = sprintf(buf, "%04x\n", chp->desc_fmt1.chid);
365ba54229dSPeter Oberparleiter else
366ba54229dSPeter Oberparleiter rc = 0;
367ba54229dSPeter Oberparleiter mutex_unlock(&chp->lock);
368ba54229dSPeter Oberparleiter
369ba54229dSPeter Oberparleiter return rc;
370ba54229dSPeter Oberparleiter }
371ba54229dSPeter Oberparleiter static DEVICE_ATTR(chid, 0444, chp_chid_show, NULL);
372ba54229dSPeter Oberparleiter
chp_chid_external_show(struct device * dev,struct device_attribute * attr,char * buf)373ba54229dSPeter Oberparleiter static ssize_t chp_chid_external_show(struct device *dev,
374ba54229dSPeter Oberparleiter struct device_attribute *attr, char *buf)
375ba54229dSPeter Oberparleiter {
376ba54229dSPeter Oberparleiter struct channel_path *chp = to_channelpath(dev);
377ba54229dSPeter Oberparleiter ssize_t rc;
378ba54229dSPeter Oberparleiter
379ba54229dSPeter Oberparleiter mutex_lock(&chp->lock);
380ba54229dSPeter Oberparleiter if (chp->desc_fmt1.flags & 0x10)
381ba54229dSPeter Oberparleiter rc = sprintf(buf, "%x\n", chp->desc_fmt1.flags & 0x8 ? 1 : 0);
382ba54229dSPeter Oberparleiter else
383ba54229dSPeter Oberparleiter rc = 0;
384ba54229dSPeter Oberparleiter mutex_unlock(&chp->lock);
385ba54229dSPeter Oberparleiter
386ba54229dSPeter Oberparleiter return rc;
387ba54229dSPeter Oberparleiter }
388ba54229dSPeter Oberparleiter static DEVICE_ATTR(chid_external, 0444, chp_chid_external_show, NULL);
389ba54229dSPeter Oberparleiter
chp_esc_show(struct device * dev,struct device_attribute * attr,char * buf)3908b8b0915SSebastian Ott static ssize_t chp_esc_show(struct device *dev,
3918b8b0915SSebastian Ott struct device_attribute *attr, char *buf)
3928b8b0915SSebastian Ott {
3938b8b0915SSebastian Ott struct channel_path *chp = to_channelpath(dev);
3948b8b0915SSebastian Ott ssize_t rc;
3958b8b0915SSebastian Ott
3968b8b0915SSebastian Ott mutex_lock(&chp->lock);
3978b8b0915SSebastian Ott rc = sprintf(buf, "%x\n", chp->desc_fmt1.esc);
3988b8b0915SSebastian Ott mutex_unlock(&chp->lock);
3998b8b0915SSebastian Ott
4008b8b0915SSebastian Ott return rc;
4018b8b0915SSebastian Ott }
4028b8b0915SSebastian Ott static DEVICE_ATTR(esc, 0444, chp_esc_show, NULL);
4038b8b0915SSebastian Ott
util_string_read(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)404b9dd6524SSebastian Ott static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
405b9dd6524SSebastian Ott struct bin_attribute *attr, char *buf,
406b9dd6524SSebastian Ott loff_t off, size_t count)
407b9dd6524SSebastian Ott {
408b9dd6524SSebastian Ott struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
409b9dd6524SSebastian Ott ssize_t rc;
410b9dd6524SSebastian Ott
411b9dd6524SSebastian Ott mutex_lock(&chp->lock);
412b9dd6524SSebastian Ott rc = memory_read_from_buffer(buf, count, &off, chp->desc_fmt3.util_str,
413b9dd6524SSebastian Ott sizeof(chp->desc_fmt3.util_str));
414b9dd6524SSebastian Ott mutex_unlock(&chp->lock);
415b9dd6524SSebastian Ott
416b9dd6524SSebastian Ott return rc;
417b9dd6524SSebastian Ott }
418b9dd6524SSebastian Ott static BIN_ATTR_RO(util_string,
419b9dd6524SSebastian Ott sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
420b9dd6524SSebastian Ott
421b9dd6524SSebastian Ott static struct bin_attribute *chp_bin_attrs[] = {
422b9dd6524SSebastian Ott &bin_attr_util_string,
423b9dd6524SSebastian Ott NULL,
424b9dd6524SSebastian Ott };
425b9dd6524SSebastian Ott
426e6b6e10aSPeter Oberparleiter static struct attribute *chp_attrs[] = {
427e6b6e10aSPeter Oberparleiter &dev_attr_status.attr,
428e5854a58SPeter Oberparleiter &dev_attr_configure.attr,
429e6b6e10aSPeter Oberparleiter &dev_attr_type.attr,
430e6b6e10aSPeter Oberparleiter &dev_attr_cmg.attr,
431e6b6e10aSPeter Oberparleiter &dev_attr_shared.attr,
432ba54229dSPeter Oberparleiter &dev_attr_chid.attr,
433ba54229dSPeter Oberparleiter &dev_attr_chid_external.attr,
4348b8b0915SSebastian Ott &dev_attr_esc.attr,
435e6b6e10aSPeter Oberparleiter NULL,
436e6b6e10aSPeter Oberparleiter };
437e6b6e10aSPeter Oberparleiter static struct attribute_group chp_attr_group = {
438e6b6e10aSPeter Oberparleiter .attrs = chp_attrs,
439b9dd6524SSebastian Ott .bin_attrs = chp_bin_attrs,
440e6b6e10aSPeter Oberparleiter };
441ed35ba9aSSebastian Ott static const struct attribute_group *chp_attr_groups[] = {
442ed35ba9aSSebastian Ott &chp_attr_group,
443ed35ba9aSSebastian Ott NULL,
444ed35ba9aSSebastian Ott };
445e6b6e10aSPeter Oberparleiter
chp_release(struct device * dev)446e6b6e10aSPeter Oberparleiter static void chp_release(struct device *dev)
447e6b6e10aSPeter Oberparleiter {
448e6b6e10aSPeter Oberparleiter struct channel_path *cp;
449e6b6e10aSPeter Oberparleiter
45005469607SCornelia Huck cp = to_channelpath(dev);
451e6b6e10aSPeter Oberparleiter kfree(cp);
452e6b6e10aSPeter Oberparleiter }
453e6b6e10aSPeter Oberparleiter
454e6b6e10aSPeter Oberparleiter /**
455cce0eaccSPeter Oberparleiter * chp_update_desc - update channel-path description
456364e3f90SSebastian Ott * @chp: channel-path
457cce0eaccSPeter Oberparleiter *
4589f3d6d7aSSebastian Ott * Update the channel-path description of the specified channel-path
4599f3d6d7aSSebastian Ott * including channel measurement related information.
460cce0eaccSPeter Oberparleiter * Return zero on success, non-zero otherwise.
461cce0eaccSPeter Oberparleiter */
chp_update_desc(struct channel_path * chp)462cce0eaccSPeter Oberparleiter int chp_update_desc(struct channel_path *chp)
463cce0eaccSPeter Oberparleiter {
464cce0eaccSPeter Oberparleiter int rc;
465cce0eaccSPeter Oberparleiter
466ded27d8dSSebastian Ott rc = chsc_determine_fmt0_channel_path_desc(chp->chpid, &chp->desc);
467cce0eaccSPeter Oberparleiter if (rc)
468cce0eaccSPeter Oberparleiter return rc;
469cce0eaccSPeter Oberparleiter
4700b601373SSebastian Ott /*
4710b601373SSebastian Ott * Fetching the following data is optional. Not all machines or
4720b601373SSebastian Ott * hypervisors implement the required chsc commands.
4730b601373SSebastian Ott */
4740b601373SSebastian Ott chsc_determine_fmt1_channel_path_desc(chp->chpid, &chp->desc_fmt1);
475fcc6dd4bSSebastian Ott chsc_determine_fmt3_channel_path_desc(chp->chpid, &chp->desc_fmt3);
4760b601373SSebastian Ott chsc_get_channel_measurement_chars(chp);
4779f3d6d7aSSebastian Ott
4780b601373SSebastian Ott return 0;
479cce0eaccSPeter Oberparleiter }
480cce0eaccSPeter Oberparleiter
481cce0eaccSPeter Oberparleiter /**
482e6b6e10aSPeter Oberparleiter * chp_new - register a new channel-path
483364e3f90SSebastian Ott * @chpid: channel-path ID
484e6b6e10aSPeter Oberparleiter *
485e6b6e10aSPeter Oberparleiter * Create and register data structure representing new channel-path. Return
486e6b6e10aSPeter Oberparleiter * zero on success, non-zero otherwise.
487e6b6e10aSPeter Oberparleiter */
chp_new(struct chp_id chpid)488e6b6e10aSPeter Oberparleiter int chp_new(struct chp_id chpid)
489e6b6e10aSPeter Oberparleiter {
49098cc43abSSebastian Ott struct channel_subsystem *css = css_by_id(chpid.cssid);
491e6b6e10aSPeter Oberparleiter struct channel_path *chp;
49287dc8a01SSebastian Ott int ret = 0;
493e6b6e10aSPeter Oberparleiter
49487dc8a01SSebastian Ott mutex_lock(&css->mutex);
495e5854a58SPeter Oberparleiter if (chp_is_registered(chpid))
49687dc8a01SSebastian Ott goto out;
497e6b6e10aSPeter Oberparleiter
49887dc8a01SSebastian Ott chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL);
49987dc8a01SSebastian Ott if (!chp) {
50087dc8a01SSebastian Ott ret = -ENOMEM;
50187dc8a01SSebastian Ott goto out;
50287dc8a01SSebastian Ott }
503e6b6e10aSPeter Oberparleiter /* fill in status, etc. */
504e6b6e10aSPeter Oberparleiter chp->chpid = chpid;
505e6b6e10aSPeter Oberparleiter chp->state = 1;
50698cc43abSSebastian Ott chp->dev.parent = &css->device;
507ed35ba9aSSebastian Ott chp->dev.groups = chp_attr_groups;
508e6b6e10aSPeter Oberparleiter chp->dev.release = chp_release;
509b730f3a9SSebastian Ott mutex_init(&chp->lock);
510e6b6e10aSPeter Oberparleiter
511e6b6e10aSPeter Oberparleiter /* Obtain channel path description and fill it in. */
512cce0eaccSPeter Oberparleiter ret = chp_update_desc(chp);
513e6b6e10aSPeter Oberparleiter if (ret)
514e6b6e10aSPeter Oberparleiter goto out_free;
515c9182e0fSPeter Oberparleiter if ((chp->desc.flags & 0x80) == 0) {
516c9182e0fSPeter Oberparleiter ret = -ENODEV;
517c9182e0fSPeter Oberparleiter goto out_free;
518c9182e0fSPeter Oberparleiter }
519ec004407SMichael Ernst dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id);
520e6b6e10aSPeter Oberparleiter
521e6b6e10aSPeter Oberparleiter /* make it known to the system */
522e6b6e10aSPeter Oberparleiter ret = device_register(&chp->dev);
523e6b6e10aSPeter Oberparleiter if (ret) {
524e556bbbdSCornelia Huck CIO_MSG_EVENT(0, "Could not register chp%x.%02x: %d\n",
525e556bbbdSCornelia Huck chpid.cssid, chpid.id, ret);
526c6304933SSebastian Ott put_device(&chp->dev);
527c6304933SSebastian Ott goto out;
528e6b6e10aSPeter Oberparleiter }
52987dc8a01SSebastian Ott
53098cc43abSSebastian Ott if (css->cm_enabled) {
531e6b6e10aSPeter Oberparleiter ret = chp_add_cmg_attr(chp);
532e6b6e10aSPeter Oberparleiter if (ret) {
533e6b6e10aSPeter Oberparleiter device_unregister(&chp->dev);
534a2164b81SCornelia Huck goto out;
535e6b6e10aSPeter Oberparleiter }
536e6b6e10aSPeter Oberparleiter }
53798cc43abSSebastian Ott css->chps[chpid.id] = chp;
538a2164b81SCornelia Huck goto out;
539e6b6e10aSPeter Oberparleiter out_free:
540e6b6e10aSPeter Oberparleiter kfree(chp);
541a2164b81SCornelia Huck out:
54287dc8a01SSebastian Ott mutex_unlock(&css->mutex);
543e6b6e10aSPeter Oberparleiter return ret;
544e6b6e10aSPeter Oberparleiter }
545e6b6e10aSPeter Oberparleiter
546e6b6e10aSPeter Oberparleiter /**
547e6b6e10aSPeter Oberparleiter * chp_get_chp_desc - return newly allocated channel-path description
548e6b6e10aSPeter Oberparleiter * @chpid: channel-path ID
549e6b6e10aSPeter Oberparleiter *
550e6b6e10aSPeter Oberparleiter * On success return a newly allocated copy of the channel-path description
551e6b6e10aSPeter Oberparleiter * data associated with the given channel-path ID. Return %NULL on error.
552e6b6e10aSPeter Oberparleiter */
chp_get_chp_desc(struct chp_id chpid)553ded27d8dSSebastian Ott struct channel_path_desc_fmt0 *chp_get_chp_desc(struct chp_id chpid)
554e6b6e10aSPeter Oberparleiter {
555e6b6e10aSPeter Oberparleiter struct channel_path *chp;
556ded27d8dSSebastian Ott struct channel_path_desc_fmt0 *desc;
557e6b6e10aSPeter Oberparleiter
558e6b6e10aSPeter Oberparleiter chp = chpid_to_chp(chpid);
559e6b6e10aSPeter Oberparleiter if (!chp)
560e6b6e10aSPeter Oberparleiter return NULL;
561ded27d8dSSebastian Ott desc = kmalloc(sizeof(*desc), GFP_KERNEL);
562e6b6e10aSPeter Oberparleiter if (!desc)
563e6b6e10aSPeter Oberparleiter return NULL;
564b730f3a9SSebastian Ott
565b730f3a9SSebastian Ott mutex_lock(&chp->lock);
566ded27d8dSSebastian Ott memcpy(desc, &chp->desc, sizeof(*desc));
567b730f3a9SSebastian Ott mutex_unlock(&chp->lock);
568e6b6e10aSPeter Oberparleiter return desc;
569e6b6e10aSPeter Oberparleiter }
570e6b6e10aSPeter Oberparleiter
571e6b6e10aSPeter Oberparleiter /**
572e6b6e10aSPeter Oberparleiter * chp_process_crw - process channel-path status change
573c1156189SCornelia Huck * @crw0: channel report-word to handler
574c1156189SCornelia Huck * @crw1: second channel-report word (always NULL)
575c1156189SCornelia Huck * @overflow: crw overflow indication
576e6b6e10aSPeter Oberparleiter *
577e6b6e10aSPeter Oberparleiter * Handle channel-report-words indicating that the status of a channel-path
578e6b6e10aSPeter Oberparleiter * has changed.
579e6b6e10aSPeter Oberparleiter */
chp_process_crw(struct crw * crw0,struct crw * crw1,int overflow)580c1156189SCornelia Huck static void chp_process_crw(struct crw *crw0, struct crw *crw1,
581c1156189SCornelia Huck int overflow)
582e6b6e10aSPeter Oberparleiter {
583e6b6e10aSPeter Oberparleiter struct chp_id chpid;
584e6b6e10aSPeter Oberparleiter
585c1156189SCornelia Huck if (overflow) {
586c1156189SCornelia Huck css_schedule_eval_all();
587c1156189SCornelia Huck return;
588c1156189SCornelia Huck }
589c1156189SCornelia Huck CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
590c1156189SCornelia Huck "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
591c1156189SCornelia Huck crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
592c1156189SCornelia Huck crw0->erc, crw0->rsid);
593c1156189SCornelia Huck /*
594c1156189SCornelia Huck * Check for solicited machine checks. These are
595c1156189SCornelia Huck * created by reset channel path and need not be
596c1156189SCornelia Huck * handled here.
597c1156189SCornelia Huck */
598c1156189SCornelia Huck if (crw0->slct) {
599c1156189SCornelia Huck CIO_CRW_EVENT(2, "solicited machine check for "
600c1156189SCornelia Huck "channel path %02X\n", crw0->rsid);
601c1156189SCornelia Huck return;
602c1156189SCornelia Huck }
603e6b6e10aSPeter Oberparleiter chp_id_init(&chpid);
604c1156189SCornelia Huck chpid.id = crw0->rsid;
605c1156189SCornelia Huck switch (crw0->erc) {
606c1156189SCornelia Huck case CRW_ERC_IPARM: /* Path has come. */
6072daace78SDong Jia Shi case CRW_ERC_INIT:
608e6b6e10aSPeter Oberparleiter chp_new(chpid);
60983b3370cSPeter Oberparleiter chsc_chp_online(chpid);
610c1156189SCornelia Huck break;
611c1156189SCornelia Huck case CRW_ERC_PERRI: /* Path has gone. */
612c1156189SCornelia Huck case CRW_ERC_PERRN:
613e6b6e10aSPeter Oberparleiter chsc_chp_offline(chpid);
614c1156189SCornelia Huck break;
615c1156189SCornelia Huck default:
616c1156189SCornelia Huck CIO_CRW_EVENT(2, "Don't know how to handle erc=%x\n",
617c1156189SCornelia Huck crw0->erc);
618c1156189SCornelia Huck }
619e6b6e10aSPeter Oberparleiter }
620e5854a58SPeter Oberparleiter
chp_ssd_get_mask(struct chsc_ssd_info * ssd,struct chp_link * link)62199611f87SCornelia Huck int chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct chp_link *link)
622c820de39SCornelia Huck {
623c820de39SCornelia Huck int i;
624c820de39SCornelia Huck int mask;
625c820de39SCornelia Huck
626c820de39SCornelia Huck for (i = 0; i < 8; i++) {
627c820de39SCornelia Huck mask = 0x80 >> i;
628c820de39SCornelia Huck if (!(ssd->path_mask & mask))
629c820de39SCornelia Huck continue;
63099611f87SCornelia Huck if (!chp_id_is_equal(&ssd->chpid[i], &link->chpid))
631c820de39SCornelia Huck continue;
632c820de39SCornelia Huck if ((ssd->fla_valid_mask & mask) &&
63399611f87SCornelia Huck ((ssd->fla[i] & link->fla_mask) != link->fla))
634c820de39SCornelia Huck continue;
635c820de39SCornelia Huck return mask;
636c820de39SCornelia Huck }
637c820de39SCornelia Huck return 0;
638c820de39SCornelia Huck }
639c820de39SCornelia Huck EXPORT_SYMBOL_GPL(chp_ssd_get_mask);
640c820de39SCornelia Huck
info_bit_num(struct chp_id id)641e5854a58SPeter Oberparleiter static inline int info_bit_num(struct chp_id id)
642e5854a58SPeter Oberparleiter {
643e5854a58SPeter Oberparleiter return id.id + id.cssid * (__MAX_CHPID + 1);
644e5854a58SPeter Oberparleiter }
645e5854a58SPeter Oberparleiter
646e5854a58SPeter Oberparleiter /* Force chp_info refresh on next call to info_validate(). */
info_expire(void)647e5854a58SPeter Oberparleiter static void info_expire(void)
648e5854a58SPeter Oberparleiter {
649e5854a58SPeter Oberparleiter mutex_lock(&info_lock);
650e5854a58SPeter Oberparleiter chp_info_expires = jiffies - 1;
651e5854a58SPeter Oberparleiter mutex_unlock(&info_lock);
652e5854a58SPeter Oberparleiter }
653e5854a58SPeter Oberparleiter
654e5854a58SPeter Oberparleiter /* Ensure that chp_info is up-to-date. */
info_update(void)655e5854a58SPeter Oberparleiter static int info_update(void)
656e5854a58SPeter Oberparleiter {
657e5854a58SPeter Oberparleiter int rc;
658e5854a58SPeter Oberparleiter
659e5854a58SPeter Oberparleiter mutex_lock(&info_lock);
660e5854a58SPeter Oberparleiter rc = 0;
661e5854a58SPeter Oberparleiter if (time_after(jiffies, chp_info_expires)) {
662e5854a58SPeter Oberparleiter /* Data is too old, update. */
663e5854a58SPeter Oberparleiter rc = sclp_chp_read_info(&chp_info);
664*051a7980SPeter Oberparleiter if (!rc)
665e5854a58SPeter Oberparleiter chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL;
666e5854a58SPeter Oberparleiter }
667e5854a58SPeter Oberparleiter mutex_unlock(&info_lock);
668e5854a58SPeter Oberparleiter
669e5854a58SPeter Oberparleiter return rc;
670e5854a58SPeter Oberparleiter }
671e5854a58SPeter Oberparleiter
672e5854a58SPeter Oberparleiter /**
673e5854a58SPeter Oberparleiter * chp_info_get_status - retrieve configure status of a channel-path
674e5854a58SPeter Oberparleiter * @chpid: channel-path ID
675e5854a58SPeter Oberparleiter *
676e5854a58SPeter Oberparleiter * On success, return 0 for standby, 1 for configured, 2 for reserved,
677e5854a58SPeter Oberparleiter * 3 for not recognized. Return negative error code on error.
chp_info_get_status(struct chp_id chpid)678e5854a58SPeter Oberparleiter */
679e5854a58SPeter Oberparleiter int chp_info_get_status(struct chp_id chpid)
680e5854a58SPeter Oberparleiter {
681e5854a58SPeter Oberparleiter int rc;
682e5854a58SPeter Oberparleiter int bit;
683e5854a58SPeter Oberparleiter
684e5854a58SPeter Oberparleiter rc = info_update();
685e5854a58SPeter Oberparleiter if (rc)
686e5854a58SPeter Oberparleiter return rc;
687e5854a58SPeter Oberparleiter
688e5854a58SPeter Oberparleiter bit = info_bit_num(chpid);
689e5854a58SPeter Oberparleiter mutex_lock(&info_lock);
690e5854a58SPeter Oberparleiter if (!chp_test_bit(chp_info.recognized, bit))
691e5854a58SPeter Oberparleiter rc = CHP_STATUS_NOT_RECOGNIZED;
692e5854a58SPeter Oberparleiter else if (chp_test_bit(chp_info.configured, bit))
693e5854a58SPeter Oberparleiter rc = CHP_STATUS_CONFIGURED;
694e5854a58SPeter Oberparleiter else if (chp_test_bit(chp_info.standby, bit))
695e5854a58SPeter Oberparleiter rc = CHP_STATUS_STANDBY;
696e5854a58SPeter Oberparleiter else
697e5854a58SPeter Oberparleiter rc = CHP_STATUS_RESERVED;
698e5854a58SPeter Oberparleiter mutex_unlock(&info_lock);
699e5854a58SPeter Oberparleiter
700e5854a58SPeter Oberparleiter return rc;
701e5854a58SPeter Oberparleiter }
702e5854a58SPeter Oberparleiter
cfg_get_task(struct chp_id chpid)703e5854a58SPeter Oberparleiter /* Return configure task for chpid. */
704e5854a58SPeter Oberparleiter static enum cfg_task_t cfg_get_task(struct chp_id chpid)
705e5854a58SPeter Oberparleiter {
706e5854a58SPeter Oberparleiter return chp_cfg_task[chpid.cssid][chpid.id];
707e5854a58SPeter Oberparleiter }
708e5854a58SPeter Oberparleiter
cfg_set_task(struct chp_id chpid,enum cfg_task_t cfg)709e5854a58SPeter Oberparleiter /* Set configure task for chpid. */
710e5854a58SPeter Oberparleiter static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg)
711e5854a58SPeter Oberparleiter {
712e5854a58SPeter Oberparleiter chp_cfg_task[chpid.cssid][chpid.id] = cfg;
713e5854a58SPeter Oberparleiter }
714e5854a58SPeter Oberparleiter
chp_cfg_fetch_task(struct chp_id * chpid)7154475aeb8SSebastian Ott /* Fetch the first configure task. Set chpid accordingly. */
7164475aeb8SSebastian Ott static enum cfg_task_t chp_cfg_fetch_task(struct chp_id *chpid)
7174475aeb8SSebastian Ott {
7184475aeb8SSebastian Ott enum cfg_task_t t = cfg_none;
7194475aeb8SSebastian Ott
7204475aeb8SSebastian Ott chp_id_for_each(chpid) {
7214475aeb8SSebastian Ott t = cfg_get_task(*chpid);
7224475aeb8SSebastian Ott if (t != cfg_none)
7234475aeb8SSebastian Ott break;
7244475aeb8SSebastian Ott }
7254475aeb8SSebastian Ott
7264475aeb8SSebastian Ott return t;
7274475aeb8SSebastian Ott }
7284475aeb8SSebastian Ott
729e5854a58SPeter Oberparleiter /* Perform one configure/deconfigure request. Reschedule work function until
cfg_func(struct work_struct * work)730e5854a58SPeter Oberparleiter * last request. */
731e5854a58SPeter Oberparleiter static void cfg_func(struct work_struct *work)
732e5854a58SPeter Oberparleiter {
733e5854a58SPeter Oberparleiter struct chp_id chpid;
734e5854a58SPeter Oberparleiter enum cfg_task_t t;
735683c5418SPeter Oberparleiter int rc;
736e5854a58SPeter Oberparleiter
737837c5220SSebastian Ott spin_lock(&cfg_lock);
7384475aeb8SSebastian Ott t = chp_cfg_fetch_task(&chpid);
739837c5220SSebastian Ott spin_unlock(&cfg_lock);
740e5854a58SPeter Oberparleiter
741e5854a58SPeter Oberparleiter switch (t) {
742e5854a58SPeter Oberparleiter case cfg_configure:
743683c5418SPeter Oberparleiter rc = sclp_chp_configure(chpid);
744683c5418SPeter Oberparleiter if (rc)
745683c5418SPeter Oberparleiter CIO_MSG_EVENT(2, "chp: sclp_chp_configure(%x.%02x)="
746683c5418SPeter Oberparleiter "%d\n", chpid.cssid, chpid.id, rc);
747683c5418SPeter Oberparleiter else {
748e5854a58SPeter Oberparleiter info_expire();
749e5854a58SPeter Oberparleiter chsc_chp_online(chpid);
750683c5418SPeter Oberparleiter }
751e5854a58SPeter Oberparleiter break;
752e5854a58SPeter Oberparleiter case cfg_deconfigure:
753683c5418SPeter Oberparleiter rc = sclp_chp_deconfigure(chpid);
754683c5418SPeter Oberparleiter if (rc)
755683c5418SPeter Oberparleiter CIO_MSG_EVENT(2, "chp: sclp_chp_deconfigure(%x.%02x)="
756683c5418SPeter Oberparleiter "%d\n", chpid.cssid, chpid.id, rc);
757683c5418SPeter Oberparleiter else {
758e5854a58SPeter Oberparleiter info_expire();
759e5854a58SPeter Oberparleiter chsc_chp_offline(chpid);
760683c5418SPeter Oberparleiter }
761e5854a58SPeter Oberparleiter break;
762e5854a58SPeter Oberparleiter case cfg_none:
763e5854a58SPeter Oberparleiter /* Get updated information after last change. */
764e5854a58SPeter Oberparleiter info_update();
765e5854a58SPeter Oberparleiter wake_up_interruptible(&cfg_wait_queue);
766e5854a58SPeter Oberparleiter return;
767e5854a58SPeter Oberparleiter }
7684475aeb8SSebastian Ott spin_lock(&cfg_lock);
7694475aeb8SSebastian Ott if (t == cfg_get_task(chpid))
7704475aeb8SSebastian Ott cfg_set_task(chpid, cfg_none);
7714475aeb8SSebastian Ott spin_unlock(&cfg_lock);
7727b1058bcSBhaktipriya Shridhar schedule_work(&cfg_work);
773e5854a58SPeter Oberparleiter }
774e5854a58SPeter Oberparleiter
775e5854a58SPeter Oberparleiter /**
776e5854a58SPeter Oberparleiter * chp_cfg_schedule - schedule chpid configuration request
777364e3f90SSebastian Ott * @chpid: channel-path ID
778364e3f90SSebastian Ott * @configure: Non-zero for configure, zero for deconfigure
779e5854a58SPeter Oberparleiter *
780e5854a58SPeter Oberparleiter * Schedule a channel-path configuration/deconfiguration request.
chp_cfg_schedule(struct chp_id chpid,int configure)781e5854a58SPeter Oberparleiter */
782e5854a58SPeter Oberparleiter void chp_cfg_schedule(struct chp_id chpid, int configure)
783e5854a58SPeter Oberparleiter {
784e5854a58SPeter Oberparleiter CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id,
785e5854a58SPeter Oberparleiter configure);
786837c5220SSebastian Ott spin_lock(&cfg_lock);
787e5854a58SPeter Oberparleiter cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure);
788837c5220SSebastian Ott spin_unlock(&cfg_lock);
7897b1058bcSBhaktipriya Shridhar schedule_work(&cfg_work);
790e5854a58SPeter Oberparleiter }
791e5854a58SPeter Oberparleiter
792e5854a58SPeter Oberparleiter /**
793e5854a58SPeter Oberparleiter * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request
794364e3f90SSebastian Ott * @chpid: channel-path ID
795e5854a58SPeter Oberparleiter *
796e5854a58SPeter Oberparleiter * Cancel an active channel-path deconfiguration request if it has not yet
797e5854a58SPeter Oberparleiter * been performed.
chp_cfg_cancel_deconfigure(struct chp_id chpid)798e5854a58SPeter Oberparleiter */
799e5854a58SPeter Oberparleiter void chp_cfg_cancel_deconfigure(struct chp_id chpid)
800e5854a58SPeter Oberparleiter {
801e5854a58SPeter Oberparleiter CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id);
802837c5220SSebastian Ott spin_lock(&cfg_lock);
803e5854a58SPeter Oberparleiter if (cfg_get_task(chpid) == cfg_deconfigure)
804e5854a58SPeter Oberparleiter cfg_set_task(chpid, cfg_none);
805837c5220SSebastian Ott spin_unlock(&cfg_lock);
806e5854a58SPeter Oberparleiter }
cfg_idle(void)807e5854a58SPeter Oberparleiter
8084475aeb8SSebastian Ott static bool cfg_idle(void)
8094475aeb8SSebastian Ott {
8104475aeb8SSebastian Ott struct chp_id chpid;
8114475aeb8SSebastian Ott enum cfg_task_t t;
8124475aeb8SSebastian Ott
8134475aeb8SSebastian Ott spin_lock(&cfg_lock);
8144475aeb8SSebastian Ott t = chp_cfg_fetch_task(&chpid);
8154475aeb8SSebastian Ott spin_unlock(&cfg_lock);
8164475aeb8SSebastian Ott
8174475aeb8SSebastian Ott return t == cfg_none;
8184475aeb8SSebastian Ott }
cfg_wait_idle(void)8194475aeb8SSebastian Ott
820e5854a58SPeter Oberparleiter static int cfg_wait_idle(void)
821e5854a58SPeter Oberparleiter {
8224475aeb8SSebastian Ott if (wait_event_interruptible(cfg_wait_queue, cfg_idle()))
823e5854a58SPeter Oberparleiter return -ERESTARTSYS;
824e5854a58SPeter Oberparleiter return 0;
825e5854a58SPeter Oberparleiter }
chp_init(void)826e5854a58SPeter Oberparleiter
827e5854a58SPeter Oberparleiter static int __init chp_init(void)
828e5854a58SPeter Oberparleiter {
829e5854a58SPeter Oberparleiter struct chp_id chpid;
830179a98cbSSebastian Ott int state, ret;
831e5854a58SPeter Oberparleiter
832f5daba1dSHeiko Carstens ret = crw_register_handler(CRW_RSC_CPATH, chp_process_crw);
833c1156189SCornelia Huck if (ret)
834c1156189SCornelia Huck return ret;
835e5854a58SPeter Oberparleiter INIT_WORK(&cfg_work, cfg_func);
836e5854a58SPeter Oberparleiter if (info_update())
837e5854a58SPeter Oberparleiter return 0;
838e5854a58SPeter Oberparleiter /* Register available channel-paths. */
839e5854a58SPeter Oberparleiter chp_id_for_each(&chpid) {
840179a98cbSSebastian Ott state = chp_info_get_status(chpid);
841179a98cbSSebastian Ott if (state == CHP_STATUS_CONFIGURED ||
842179a98cbSSebastian Ott state == CHP_STATUS_STANDBY)
843e5854a58SPeter Oberparleiter chp_new(chpid);
844e5854a58SPeter Oberparleiter }
845e5854a58SPeter Oberparleiter
846e5854a58SPeter Oberparleiter return 0;
847e5854a58SPeter Oberparleiter }
848e5854a58SPeter Oberparleiter
849e5854a58SPeter Oberparleiter subsys_initcall(chp_init);
850