xref: /openbmc/linux/drivers/s390/cio/chp.c (revision 36db6e8484ed455bbb320d89a119378897ae991c)
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