18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 1999, 2010
48c2ecf20Sopenharmony_ci *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
58c2ecf20Sopenharmony_ci *		 Arnd Bergmann (arndb@de.ibm.com)
68c2ecf20Sopenharmony_ci *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/bug.h>
108c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
118c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
128c2ecf20Sopenharmony_ci#include <linux/export.h>
138c2ecf20Sopenharmony_ci#include <linux/sched.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
168c2ecf20Sopenharmony_ci#include <linux/wait.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <asm/chpid.h>
218c2ecf20Sopenharmony_ci#include <asm/sclp.h>
228c2ecf20Sopenharmony_ci#include <asm/crw.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "cio.h"
258c2ecf20Sopenharmony_ci#include "css.h"
268c2ecf20Sopenharmony_ci#include "ioasm.h"
278c2ecf20Sopenharmony_ci#include "cio_debug.h"
288c2ecf20Sopenharmony_ci#include "chp.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define to_channelpath(device) container_of(device, struct channel_path, dev)
318c2ecf20Sopenharmony_ci#define CHP_INFO_UPDATE_INTERVAL	1*HZ
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cienum cfg_task_t {
348c2ecf20Sopenharmony_ci	cfg_none,
358c2ecf20Sopenharmony_ci	cfg_configure,
368c2ecf20Sopenharmony_ci	cfg_deconfigure
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* Map for pending configure tasks. */
408c2ecf20Sopenharmony_cistatic enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1];
418c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cfg_lock);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Map for channel-path status. */
448c2ecf20Sopenharmony_cistatic struct sclp_chp_info chp_info;
458c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(info_lock);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Time after which channel-path status may be outdated. */
488c2ecf20Sopenharmony_cistatic unsigned long chp_info_expires;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct work_struct cfg_work;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* Wait queue for configure completion events. */
538c2ecf20Sopenharmony_cistatic wait_queue_head_t cfg_wait_queue;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* Set vary state for given chpid. */
568c2ecf20Sopenharmony_cistatic void set_chp_logically_online(struct chp_id chpid, int onoff)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	chpid_to_chp(chpid)->state = onoff;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* On success return 0 if channel-path is varied offline, 1 if it is varied
628c2ecf20Sopenharmony_ci * online. Return -ENODEV if channel-path is not registered. */
638c2ecf20Sopenharmony_ciint chp_get_status(struct chp_id chpid)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/**
698c2ecf20Sopenharmony_ci * chp_get_sch_opm - return opm for subchannel
708c2ecf20Sopenharmony_ci * @sch: subchannel
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci * Calculate and return the operational path mask (opm) based on the chpids
738c2ecf20Sopenharmony_ci * used by the subchannel and the status of the associated channel-paths.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_ciu8 chp_get_sch_opm(struct subchannel *sch)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct chp_id chpid;
788c2ecf20Sopenharmony_ci	int opm;
798c2ecf20Sopenharmony_ci	int i;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	opm = 0;
828c2ecf20Sopenharmony_ci	chp_id_init(&chpid);
838c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
848c2ecf20Sopenharmony_ci		opm <<= 1;
858c2ecf20Sopenharmony_ci		chpid.id = sch->schib.pmcw.chpid[i];
868c2ecf20Sopenharmony_ci		if (chp_get_status(chpid) != 0)
878c2ecf20Sopenharmony_ci			opm |= 1;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	return opm;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chp_get_sch_opm);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/**
948c2ecf20Sopenharmony_ci * chp_is_registered - check if a channel-path is registered
958c2ecf20Sopenharmony_ci * @chpid: channel-path ID
968c2ecf20Sopenharmony_ci *
978c2ecf20Sopenharmony_ci * Return non-zero if a channel-path with the given chpid is registered,
988c2ecf20Sopenharmony_ci * zero otherwise.
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_ciint chp_is_registered(struct chp_id chpid)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	return chpid_to_chp(chpid) != NULL;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/*
1068c2ecf20Sopenharmony_ci * Function: s390_vary_chpid
1078c2ecf20Sopenharmony_ci * Varies the specified chpid online or offline
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_cistatic int s390_vary_chpid(struct chp_id chpid, int on)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	char dbf_text[15];
1128c2ecf20Sopenharmony_ci	int status;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid,
1158c2ecf20Sopenharmony_ci		chpid.id);
1168c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(2, dbf_text);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	status = chp_get_status(chpid);
1198c2ecf20Sopenharmony_ci	if (!on && !status)
1208c2ecf20Sopenharmony_ci		return 0;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	set_chp_logically_online(chpid, on);
1238c2ecf20Sopenharmony_ci	chsc_chp_vary(chpid, on);
1248c2ecf20Sopenharmony_ci	return 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci/*
1288c2ecf20Sopenharmony_ci * Channel measurement related functions
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_cistatic ssize_t chp_measurement_chars_read(struct file *filp,
1318c2ecf20Sopenharmony_ci					  struct kobject *kobj,
1328c2ecf20Sopenharmony_ci					  struct bin_attribute *bin_attr,
1338c2ecf20Sopenharmony_ci					  char *buf, loff_t off, size_t count)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct channel_path *chp;
1368c2ecf20Sopenharmony_ci	struct device *device;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	device = kobj_to_dev(kobj);
1398c2ecf20Sopenharmony_ci	chp = to_channelpath(device);
1408c2ecf20Sopenharmony_ci	if (chp->cmg == -1)
1418c2ecf20Sopenharmony_ci		return 0;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	return memory_read_from_buffer(buf, count, &off, &chp->cmg_chars,
1448c2ecf20Sopenharmony_ci				       sizeof(chp->cmg_chars));
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic const struct bin_attribute chp_measurement_chars_attr = {
1488c2ecf20Sopenharmony_ci	.attr = {
1498c2ecf20Sopenharmony_ci		.name = "measurement_chars",
1508c2ecf20Sopenharmony_ci		.mode = S_IRUSR,
1518c2ecf20Sopenharmony_ci	},
1528c2ecf20Sopenharmony_ci	.size = sizeof(struct cmg_chars),
1538c2ecf20Sopenharmony_ci	.read = chp_measurement_chars_read,
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic void chp_measurement_copy_block(struct cmg_entry *buf,
1578c2ecf20Sopenharmony_ci				       struct channel_subsystem *css,
1588c2ecf20Sopenharmony_ci				       struct chp_id chpid)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	void *area;
1618c2ecf20Sopenharmony_ci	struct cmg_entry *entry, reference_buf;
1628c2ecf20Sopenharmony_ci	int idx;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (chpid.id < 128) {
1658c2ecf20Sopenharmony_ci		area = css->cub_addr1;
1668c2ecf20Sopenharmony_ci		idx = chpid.id;
1678c2ecf20Sopenharmony_ci	} else {
1688c2ecf20Sopenharmony_ci		area = css->cub_addr2;
1698c2ecf20Sopenharmony_ci		idx = chpid.id - 128;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	entry = area + (idx * sizeof(struct cmg_entry));
1728c2ecf20Sopenharmony_ci	do {
1738c2ecf20Sopenharmony_ci		memcpy(buf, entry, sizeof(*entry));
1748c2ecf20Sopenharmony_ci		memcpy(&reference_buf, entry, sizeof(*entry));
1758c2ecf20Sopenharmony_ci	} while (reference_buf.values[0] != buf->values[0]);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic ssize_t chp_measurement_read(struct file *filp, struct kobject *kobj,
1798c2ecf20Sopenharmony_ci				    struct bin_attribute *bin_attr,
1808c2ecf20Sopenharmony_ci				    char *buf, loff_t off, size_t count)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct channel_path *chp;
1838c2ecf20Sopenharmony_ci	struct channel_subsystem *css;
1848c2ecf20Sopenharmony_ci	struct device *device;
1858c2ecf20Sopenharmony_ci	unsigned int size;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	device = kobj_to_dev(kobj);
1888c2ecf20Sopenharmony_ci	chp = to_channelpath(device);
1898c2ecf20Sopenharmony_ci	css = to_css(chp->dev.parent);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	size = sizeof(struct cmg_entry);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* Only allow single reads. */
1948c2ecf20Sopenharmony_ci	if (off || count < size)
1958c2ecf20Sopenharmony_ci		return 0;
1968c2ecf20Sopenharmony_ci	chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid);
1978c2ecf20Sopenharmony_ci	count = size;
1988c2ecf20Sopenharmony_ci	return count;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic const struct bin_attribute chp_measurement_attr = {
2028c2ecf20Sopenharmony_ci	.attr = {
2038c2ecf20Sopenharmony_ci		.name = "measurement",
2048c2ecf20Sopenharmony_ci		.mode = S_IRUSR,
2058c2ecf20Sopenharmony_ci	},
2068c2ecf20Sopenharmony_ci	.size = sizeof(struct cmg_entry),
2078c2ecf20Sopenharmony_ci	.read = chp_measurement_read,
2088c2ecf20Sopenharmony_ci};
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_civoid chp_remove_cmg_attr(struct channel_path *chp)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
2138c2ecf20Sopenharmony_ci	device_remove_bin_file(&chp->dev, &chp_measurement_attr);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ciint chp_add_cmg_attr(struct channel_path *chp)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	int ret;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
2218c2ecf20Sopenharmony_ci	if (ret)
2228c2ecf20Sopenharmony_ci		return ret;
2238c2ecf20Sopenharmony_ci	ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
2248c2ecf20Sopenharmony_ci	if (ret)
2258c2ecf20Sopenharmony_ci		device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
2268c2ecf20Sopenharmony_ci	return ret;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/*
2308c2ecf20Sopenharmony_ci * Files for the channel path entries.
2318c2ecf20Sopenharmony_ci */
2328c2ecf20Sopenharmony_cistatic ssize_t chp_status_show(struct device *dev,
2338c2ecf20Sopenharmony_ci			       struct device_attribute *attr, char *buf)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	struct channel_path *chp = to_channelpath(dev);
2368c2ecf20Sopenharmony_ci	int status;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	mutex_lock(&chp->lock);
2398c2ecf20Sopenharmony_ci	status = chp->state;
2408c2ecf20Sopenharmony_ci	mutex_unlock(&chp->lock);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return status ? sprintf(buf, "online\n") : sprintf(buf, "offline\n");
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic ssize_t chp_status_write(struct device *dev,
2468c2ecf20Sopenharmony_ci				struct device_attribute *attr,
2478c2ecf20Sopenharmony_ci				const char *buf, size_t count)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct channel_path *cp = to_channelpath(dev);
2508c2ecf20Sopenharmony_ci	char cmd[10];
2518c2ecf20Sopenharmony_ci	int num_args;
2528c2ecf20Sopenharmony_ci	int error;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	num_args = sscanf(buf, "%5s", cmd);
2558c2ecf20Sopenharmony_ci	if (!num_args)
2568c2ecf20Sopenharmony_ci		return count;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* Wait until previous actions have settled. */
2598c2ecf20Sopenharmony_ci	css_wait_for_slow_path();
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (!strncasecmp(cmd, "on", 2) || !strcmp(cmd, "1")) {
2628c2ecf20Sopenharmony_ci		mutex_lock(&cp->lock);
2638c2ecf20Sopenharmony_ci		error = s390_vary_chpid(cp->chpid, 1);
2648c2ecf20Sopenharmony_ci		mutex_unlock(&cp->lock);
2658c2ecf20Sopenharmony_ci	} else if (!strncasecmp(cmd, "off", 3) || !strcmp(cmd, "0")) {
2668c2ecf20Sopenharmony_ci		mutex_lock(&cp->lock);
2678c2ecf20Sopenharmony_ci		error = s390_vary_chpid(cp->chpid, 0);
2688c2ecf20Sopenharmony_ci		mutex_unlock(&cp->lock);
2698c2ecf20Sopenharmony_ci	} else
2708c2ecf20Sopenharmony_ci		error = -EINVAL;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return error < 0 ? error : count;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic ssize_t chp_configure_show(struct device *dev,
2788c2ecf20Sopenharmony_ci				  struct device_attribute *attr, char *buf)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct channel_path *cp;
2818c2ecf20Sopenharmony_ci	int status;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	cp = to_channelpath(dev);
2848c2ecf20Sopenharmony_ci	status = chp_info_get_status(cp->chpid);
2858c2ecf20Sopenharmony_ci	if (status < 0)
2868c2ecf20Sopenharmony_ci		return status;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", status);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int cfg_wait_idle(void);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic ssize_t chp_configure_write(struct device *dev,
2948c2ecf20Sopenharmony_ci				   struct device_attribute *attr,
2958c2ecf20Sopenharmony_ci				   const char *buf, size_t count)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct channel_path *cp;
2988c2ecf20Sopenharmony_ci	int val;
2998c2ecf20Sopenharmony_ci	char delim;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (sscanf(buf, "%d %c", &val, &delim) != 1)
3028c2ecf20Sopenharmony_ci		return -EINVAL;
3038c2ecf20Sopenharmony_ci	if (val != 0 && val != 1)
3048c2ecf20Sopenharmony_ci		return -EINVAL;
3058c2ecf20Sopenharmony_ci	cp = to_channelpath(dev);
3068c2ecf20Sopenharmony_ci	chp_cfg_schedule(cp->chpid, val);
3078c2ecf20Sopenharmony_ci	cfg_wait_idle();
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return count;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic ssize_t chp_type_show(struct device *dev, struct device_attribute *attr,
3158c2ecf20Sopenharmony_ci			     char *buf)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct channel_path *chp = to_channelpath(dev);
3188c2ecf20Sopenharmony_ci	u8 type;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	mutex_lock(&chp->lock);
3218c2ecf20Sopenharmony_ci	type = chp->desc.desc;
3228c2ecf20Sopenharmony_ci	mutex_unlock(&chp->lock);
3238c2ecf20Sopenharmony_ci	return sprintf(buf, "%x\n", type);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic DEVICE_ATTR(type, 0444, chp_type_show, NULL);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr,
3298c2ecf20Sopenharmony_ci			    char *buf)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct channel_path *chp = to_channelpath(dev);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (!chp)
3348c2ecf20Sopenharmony_ci		return 0;
3358c2ecf20Sopenharmony_ci	if (chp->cmg == -1) /* channel measurements not available */
3368c2ecf20Sopenharmony_ci		return sprintf(buf, "unknown\n");
3378c2ecf20Sopenharmony_ci	return sprintf(buf, "%x\n", chp->cmg);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic ssize_t chp_shared_show(struct device *dev,
3438c2ecf20Sopenharmony_ci			       struct device_attribute *attr, char *buf)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct channel_path *chp = to_channelpath(dev);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!chp)
3488c2ecf20Sopenharmony_ci		return 0;
3498c2ecf20Sopenharmony_ci	if (chp->shared == -1) /* channel measurements not available */
3508c2ecf20Sopenharmony_ci		return sprintf(buf, "unknown\n");
3518c2ecf20Sopenharmony_ci	return sprintf(buf, "%x\n", chp->shared);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic DEVICE_ATTR(shared, 0444, chp_shared_show, NULL);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic ssize_t chp_chid_show(struct device *dev, struct device_attribute *attr,
3578c2ecf20Sopenharmony_ci			     char *buf)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct channel_path *chp = to_channelpath(dev);
3608c2ecf20Sopenharmony_ci	ssize_t rc;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	mutex_lock(&chp->lock);
3638c2ecf20Sopenharmony_ci	if (chp->desc_fmt1.flags & 0x10)
3648c2ecf20Sopenharmony_ci		rc = sprintf(buf, "%04x\n", chp->desc_fmt1.chid);
3658c2ecf20Sopenharmony_ci	else
3668c2ecf20Sopenharmony_ci		rc = 0;
3678c2ecf20Sopenharmony_ci	mutex_unlock(&chp->lock);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return rc;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_cistatic DEVICE_ATTR(chid, 0444, chp_chid_show, NULL);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic ssize_t chp_chid_external_show(struct device *dev,
3748c2ecf20Sopenharmony_ci				      struct device_attribute *attr, char *buf)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct channel_path *chp = to_channelpath(dev);
3778c2ecf20Sopenharmony_ci	ssize_t rc;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	mutex_lock(&chp->lock);
3808c2ecf20Sopenharmony_ci	if (chp->desc_fmt1.flags & 0x10)
3818c2ecf20Sopenharmony_ci		rc = sprintf(buf, "%x\n", chp->desc_fmt1.flags & 0x8 ? 1 : 0);
3828c2ecf20Sopenharmony_ci	else
3838c2ecf20Sopenharmony_ci		rc = 0;
3848c2ecf20Sopenharmony_ci	mutex_unlock(&chp->lock);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return rc;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_cistatic DEVICE_ATTR(chid_external, 0444, chp_chid_external_show, NULL);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic ssize_t util_string_read(struct file *filp, struct kobject *kobj,
3918c2ecf20Sopenharmony_ci				struct bin_attribute *attr, char *buf,
3928c2ecf20Sopenharmony_ci				loff_t off, size_t count)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
3958c2ecf20Sopenharmony_ci	ssize_t rc;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	mutex_lock(&chp->lock);
3988c2ecf20Sopenharmony_ci	rc = memory_read_from_buffer(buf, count, &off, chp->desc_fmt3.util_str,
3998c2ecf20Sopenharmony_ci				     sizeof(chp->desc_fmt3.util_str));
4008c2ecf20Sopenharmony_ci	mutex_unlock(&chp->lock);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return rc;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_cistatic BIN_ATTR_RO(util_string,
4058c2ecf20Sopenharmony_ci		   sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic struct bin_attribute *chp_bin_attrs[] = {
4088c2ecf20Sopenharmony_ci	&bin_attr_util_string,
4098c2ecf20Sopenharmony_ci	NULL,
4108c2ecf20Sopenharmony_ci};
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic struct attribute *chp_attrs[] = {
4138c2ecf20Sopenharmony_ci	&dev_attr_status.attr,
4148c2ecf20Sopenharmony_ci	&dev_attr_configure.attr,
4158c2ecf20Sopenharmony_ci	&dev_attr_type.attr,
4168c2ecf20Sopenharmony_ci	&dev_attr_cmg.attr,
4178c2ecf20Sopenharmony_ci	&dev_attr_shared.attr,
4188c2ecf20Sopenharmony_ci	&dev_attr_chid.attr,
4198c2ecf20Sopenharmony_ci	&dev_attr_chid_external.attr,
4208c2ecf20Sopenharmony_ci	NULL,
4218c2ecf20Sopenharmony_ci};
4228c2ecf20Sopenharmony_cistatic struct attribute_group chp_attr_group = {
4238c2ecf20Sopenharmony_ci	.attrs = chp_attrs,
4248c2ecf20Sopenharmony_ci	.bin_attrs = chp_bin_attrs,
4258c2ecf20Sopenharmony_ci};
4268c2ecf20Sopenharmony_cistatic const struct attribute_group *chp_attr_groups[] = {
4278c2ecf20Sopenharmony_ci	&chp_attr_group,
4288c2ecf20Sopenharmony_ci	NULL,
4298c2ecf20Sopenharmony_ci};
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic void chp_release(struct device *dev)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct channel_path *cp;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	cp = to_channelpath(dev);
4368c2ecf20Sopenharmony_ci	kfree(cp);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/**
4408c2ecf20Sopenharmony_ci * chp_update_desc - update channel-path description
4418c2ecf20Sopenharmony_ci * @chp: channel-path
4428c2ecf20Sopenharmony_ci *
4438c2ecf20Sopenharmony_ci * Update the channel-path description of the specified channel-path
4448c2ecf20Sopenharmony_ci * including channel measurement related information.
4458c2ecf20Sopenharmony_ci * Return zero on success, non-zero otherwise.
4468c2ecf20Sopenharmony_ci */
4478c2ecf20Sopenharmony_ciint chp_update_desc(struct channel_path *chp)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	int rc;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	rc = chsc_determine_fmt0_channel_path_desc(chp->chpid, &chp->desc);
4528c2ecf20Sopenharmony_ci	if (rc)
4538c2ecf20Sopenharmony_ci		return rc;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/*
4568c2ecf20Sopenharmony_ci	 * Fetching the following data is optional. Not all machines or
4578c2ecf20Sopenharmony_ci	 * hypervisors implement the required chsc commands.
4588c2ecf20Sopenharmony_ci	 */
4598c2ecf20Sopenharmony_ci	chsc_determine_fmt1_channel_path_desc(chp->chpid, &chp->desc_fmt1);
4608c2ecf20Sopenharmony_ci	chsc_determine_fmt3_channel_path_desc(chp->chpid, &chp->desc_fmt3);
4618c2ecf20Sopenharmony_ci	chsc_get_channel_measurement_chars(chp);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	return 0;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci/**
4678c2ecf20Sopenharmony_ci * chp_new - register a new channel-path
4688c2ecf20Sopenharmony_ci * @chpid: channel-path ID
4698c2ecf20Sopenharmony_ci *
4708c2ecf20Sopenharmony_ci * Create and register data structure representing new channel-path. Return
4718c2ecf20Sopenharmony_ci * zero on success, non-zero otherwise.
4728c2ecf20Sopenharmony_ci */
4738c2ecf20Sopenharmony_ciint chp_new(struct chp_id chpid)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct channel_subsystem *css = css_by_id(chpid.cssid);
4768c2ecf20Sopenharmony_ci	struct channel_path *chp;
4778c2ecf20Sopenharmony_ci	int ret = 0;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	mutex_lock(&css->mutex);
4808c2ecf20Sopenharmony_ci	if (chp_is_registered(chpid))
4818c2ecf20Sopenharmony_ci		goto out;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL);
4848c2ecf20Sopenharmony_ci	if (!chp) {
4858c2ecf20Sopenharmony_ci		ret = -ENOMEM;
4868c2ecf20Sopenharmony_ci		goto out;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci	/* fill in status, etc. */
4898c2ecf20Sopenharmony_ci	chp->chpid = chpid;
4908c2ecf20Sopenharmony_ci	chp->state = 1;
4918c2ecf20Sopenharmony_ci	chp->dev.parent = &css->device;
4928c2ecf20Sopenharmony_ci	chp->dev.groups = chp_attr_groups;
4938c2ecf20Sopenharmony_ci	chp->dev.release = chp_release;
4948c2ecf20Sopenharmony_ci	mutex_init(&chp->lock);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* Obtain channel path description and fill it in. */
4978c2ecf20Sopenharmony_ci	ret = chp_update_desc(chp);
4988c2ecf20Sopenharmony_ci	if (ret)
4998c2ecf20Sopenharmony_ci		goto out_free;
5008c2ecf20Sopenharmony_ci	if ((chp->desc.flags & 0x80) == 0) {
5018c2ecf20Sopenharmony_ci		ret = -ENODEV;
5028c2ecf20Sopenharmony_ci		goto out_free;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci	dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	/* make it known to the system */
5078c2ecf20Sopenharmony_ci	ret = device_register(&chp->dev);
5088c2ecf20Sopenharmony_ci	if (ret) {
5098c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(0, "Could not register chp%x.%02x: %d\n",
5108c2ecf20Sopenharmony_ci			      chpid.cssid, chpid.id, ret);
5118c2ecf20Sopenharmony_ci		put_device(&chp->dev);
5128c2ecf20Sopenharmony_ci		goto out;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (css->cm_enabled) {
5168c2ecf20Sopenharmony_ci		ret = chp_add_cmg_attr(chp);
5178c2ecf20Sopenharmony_ci		if (ret) {
5188c2ecf20Sopenharmony_ci			device_unregister(&chp->dev);
5198c2ecf20Sopenharmony_ci			goto out;
5208c2ecf20Sopenharmony_ci		}
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	css->chps[chpid.id] = chp;
5238c2ecf20Sopenharmony_ci	goto out;
5248c2ecf20Sopenharmony_ciout_free:
5258c2ecf20Sopenharmony_ci	kfree(chp);
5268c2ecf20Sopenharmony_ciout:
5278c2ecf20Sopenharmony_ci	mutex_unlock(&css->mutex);
5288c2ecf20Sopenharmony_ci	return ret;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/**
5328c2ecf20Sopenharmony_ci * chp_get_chp_desc - return newly allocated channel-path description
5338c2ecf20Sopenharmony_ci * @chpid: channel-path ID
5348c2ecf20Sopenharmony_ci *
5358c2ecf20Sopenharmony_ci * On success return a newly allocated copy of the channel-path description
5368c2ecf20Sopenharmony_ci * data associated with the given channel-path ID. Return %NULL on error.
5378c2ecf20Sopenharmony_ci */
5388c2ecf20Sopenharmony_cistruct channel_path_desc_fmt0 *chp_get_chp_desc(struct chp_id chpid)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct channel_path *chp;
5418c2ecf20Sopenharmony_ci	struct channel_path_desc_fmt0 *desc;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	chp = chpid_to_chp(chpid);
5448c2ecf20Sopenharmony_ci	if (!chp)
5458c2ecf20Sopenharmony_ci		return NULL;
5468c2ecf20Sopenharmony_ci	desc = kmalloc(sizeof(*desc), GFP_KERNEL);
5478c2ecf20Sopenharmony_ci	if (!desc)
5488c2ecf20Sopenharmony_ci		return NULL;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	mutex_lock(&chp->lock);
5518c2ecf20Sopenharmony_ci	memcpy(desc, &chp->desc, sizeof(*desc));
5528c2ecf20Sopenharmony_ci	mutex_unlock(&chp->lock);
5538c2ecf20Sopenharmony_ci	return desc;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/**
5578c2ecf20Sopenharmony_ci * chp_process_crw - process channel-path status change
5588c2ecf20Sopenharmony_ci * @crw0: channel report-word to handler
5598c2ecf20Sopenharmony_ci * @crw1: second channel-report word (always NULL)
5608c2ecf20Sopenharmony_ci * @overflow: crw overflow indication
5618c2ecf20Sopenharmony_ci *
5628c2ecf20Sopenharmony_ci * Handle channel-report-words indicating that the status of a channel-path
5638c2ecf20Sopenharmony_ci * has changed.
5648c2ecf20Sopenharmony_ci */
5658c2ecf20Sopenharmony_cistatic void chp_process_crw(struct crw *crw0, struct crw *crw1,
5668c2ecf20Sopenharmony_ci			    int overflow)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct chp_id chpid;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (overflow) {
5718c2ecf20Sopenharmony_ci		css_schedule_eval_all();
5728c2ecf20Sopenharmony_ci		return;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci	CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
5758c2ecf20Sopenharmony_ci		      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
5768c2ecf20Sopenharmony_ci		      crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
5778c2ecf20Sopenharmony_ci		      crw0->erc, crw0->rsid);
5788c2ecf20Sopenharmony_ci	/*
5798c2ecf20Sopenharmony_ci	 * Check for solicited machine checks. These are
5808c2ecf20Sopenharmony_ci	 * created by reset channel path and need not be
5818c2ecf20Sopenharmony_ci	 * handled here.
5828c2ecf20Sopenharmony_ci	 */
5838c2ecf20Sopenharmony_ci	if (crw0->slct) {
5848c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "solicited machine check for "
5858c2ecf20Sopenharmony_ci			      "channel path %02X\n", crw0->rsid);
5868c2ecf20Sopenharmony_ci		return;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci	chp_id_init(&chpid);
5898c2ecf20Sopenharmony_ci	chpid.id = crw0->rsid;
5908c2ecf20Sopenharmony_ci	switch (crw0->erc) {
5918c2ecf20Sopenharmony_ci	case CRW_ERC_IPARM: /* Path has come. */
5928c2ecf20Sopenharmony_ci	case CRW_ERC_INIT:
5938c2ecf20Sopenharmony_ci		chp_new(chpid);
5948c2ecf20Sopenharmony_ci		chsc_chp_online(chpid);
5958c2ecf20Sopenharmony_ci		break;
5968c2ecf20Sopenharmony_ci	case CRW_ERC_PERRI: /* Path has gone. */
5978c2ecf20Sopenharmony_ci	case CRW_ERC_PERRN:
5988c2ecf20Sopenharmony_ci		chsc_chp_offline(chpid);
5998c2ecf20Sopenharmony_ci		break;
6008c2ecf20Sopenharmony_ci	default:
6018c2ecf20Sopenharmony_ci		CIO_CRW_EVENT(2, "Don't know how to handle erc=%x\n",
6028c2ecf20Sopenharmony_ci			      crw0->erc);
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ciint chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct chp_link *link)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	int i;
6098c2ecf20Sopenharmony_ci	int mask;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
6128c2ecf20Sopenharmony_ci		mask = 0x80 >> i;
6138c2ecf20Sopenharmony_ci		if (!(ssd->path_mask & mask))
6148c2ecf20Sopenharmony_ci			continue;
6158c2ecf20Sopenharmony_ci		if (!chp_id_is_equal(&ssd->chpid[i], &link->chpid))
6168c2ecf20Sopenharmony_ci			continue;
6178c2ecf20Sopenharmony_ci		if ((ssd->fla_valid_mask & mask) &&
6188c2ecf20Sopenharmony_ci		    ((ssd->fla[i] & link->fla_mask) != link->fla))
6198c2ecf20Sopenharmony_ci			continue;
6208c2ecf20Sopenharmony_ci		return mask;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci	return 0;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(chp_ssd_get_mask);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic inline int info_bit_num(struct chp_id id)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	return id.id + id.cssid * (__MAX_CHPID + 1);
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci/* Force chp_info refresh on next call to info_validate(). */
6328c2ecf20Sopenharmony_cistatic void info_expire(void)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	mutex_lock(&info_lock);
6358c2ecf20Sopenharmony_ci	chp_info_expires = jiffies - 1;
6368c2ecf20Sopenharmony_ci	mutex_unlock(&info_lock);
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci/* Ensure that chp_info is up-to-date. */
6408c2ecf20Sopenharmony_cistatic int info_update(void)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	int rc;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	mutex_lock(&info_lock);
6458c2ecf20Sopenharmony_ci	rc = 0;
6468c2ecf20Sopenharmony_ci	if (time_after(jiffies, chp_info_expires)) {
6478c2ecf20Sopenharmony_ci		/* Data is too old, update. */
6488c2ecf20Sopenharmony_ci		rc = sclp_chp_read_info(&chp_info);
6498c2ecf20Sopenharmony_ci		chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ;
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci	mutex_unlock(&info_lock);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	return rc;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci/**
6578c2ecf20Sopenharmony_ci * chp_info_get_status - retrieve configure status of a channel-path
6588c2ecf20Sopenharmony_ci * @chpid: channel-path ID
6598c2ecf20Sopenharmony_ci *
6608c2ecf20Sopenharmony_ci * On success, return 0 for standby, 1 for configured, 2 for reserved,
6618c2ecf20Sopenharmony_ci * 3 for not recognized. Return negative error code on error.
6628c2ecf20Sopenharmony_ci */
6638c2ecf20Sopenharmony_ciint chp_info_get_status(struct chp_id chpid)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	int rc;
6668c2ecf20Sopenharmony_ci	int bit;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	rc = info_update();
6698c2ecf20Sopenharmony_ci	if (rc)
6708c2ecf20Sopenharmony_ci		return rc;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	bit = info_bit_num(chpid);
6738c2ecf20Sopenharmony_ci	mutex_lock(&info_lock);
6748c2ecf20Sopenharmony_ci	if (!chp_test_bit(chp_info.recognized, bit))
6758c2ecf20Sopenharmony_ci		rc = CHP_STATUS_NOT_RECOGNIZED;
6768c2ecf20Sopenharmony_ci	else if (chp_test_bit(chp_info.configured, bit))
6778c2ecf20Sopenharmony_ci		rc = CHP_STATUS_CONFIGURED;
6788c2ecf20Sopenharmony_ci	else if (chp_test_bit(chp_info.standby, bit))
6798c2ecf20Sopenharmony_ci		rc = CHP_STATUS_STANDBY;
6808c2ecf20Sopenharmony_ci	else
6818c2ecf20Sopenharmony_ci		rc = CHP_STATUS_RESERVED;
6828c2ecf20Sopenharmony_ci	mutex_unlock(&info_lock);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	return rc;
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci/* Return configure task for chpid. */
6888c2ecf20Sopenharmony_cistatic enum cfg_task_t cfg_get_task(struct chp_id chpid)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	return chp_cfg_task[chpid.cssid][chpid.id];
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci/* Set configure task for chpid. */
6948c2ecf20Sopenharmony_cistatic void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	chp_cfg_task[chpid.cssid][chpid.id] = cfg;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci/* Fetch the first configure task. Set chpid accordingly. */
7008c2ecf20Sopenharmony_cistatic enum cfg_task_t chp_cfg_fetch_task(struct chp_id *chpid)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	enum cfg_task_t t = cfg_none;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	chp_id_for_each(chpid) {
7058c2ecf20Sopenharmony_ci		t = cfg_get_task(*chpid);
7068c2ecf20Sopenharmony_ci		if (t != cfg_none)
7078c2ecf20Sopenharmony_ci			break;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return t;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci/* Perform one configure/deconfigure request. Reschedule work function until
7148c2ecf20Sopenharmony_ci * last request. */
7158c2ecf20Sopenharmony_cistatic void cfg_func(struct work_struct *work)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct chp_id chpid;
7188c2ecf20Sopenharmony_ci	enum cfg_task_t t;
7198c2ecf20Sopenharmony_ci	int rc;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	spin_lock(&cfg_lock);
7228c2ecf20Sopenharmony_ci	t = chp_cfg_fetch_task(&chpid);
7238c2ecf20Sopenharmony_ci	spin_unlock(&cfg_lock);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	switch (t) {
7268c2ecf20Sopenharmony_ci	case cfg_configure:
7278c2ecf20Sopenharmony_ci		rc = sclp_chp_configure(chpid);
7288c2ecf20Sopenharmony_ci		if (rc)
7298c2ecf20Sopenharmony_ci			CIO_MSG_EVENT(2, "chp: sclp_chp_configure(%x.%02x)="
7308c2ecf20Sopenharmony_ci				      "%d\n", chpid.cssid, chpid.id, rc);
7318c2ecf20Sopenharmony_ci		else {
7328c2ecf20Sopenharmony_ci			info_expire();
7338c2ecf20Sopenharmony_ci			chsc_chp_online(chpid);
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci		break;
7368c2ecf20Sopenharmony_ci	case cfg_deconfigure:
7378c2ecf20Sopenharmony_ci		rc = sclp_chp_deconfigure(chpid);
7388c2ecf20Sopenharmony_ci		if (rc)
7398c2ecf20Sopenharmony_ci			CIO_MSG_EVENT(2, "chp: sclp_chp_deconfigure(%x.%02x)="
7408c2ecf20Sopenharmony_ci				      "%d\n", chpid.cssid, chpid.id, rc);
7418c2ecf20Sopenharmony_ci		else {
7428c2ecf20Sopenharmony_ci			info_expire();
7438c2ecf20Sopenharmony_ci			chsc_chp_offline(chpid);
7448c2ecf20Sopenharmony_ci		}
7458c2ecf20Sopenharmony_ci		break;
7468c2ecf20Sopenharmony_ci	case cfg_none:
7478c2ecf20Sopenharmony_ci		/* Get updated information after last change. */
7488c2ecf20Sopenharmony_ci		info_update();
7498c2ecf20Sopenharmony_ci		wake_up_interruptible(&cfg_wait_queue);
7508c2ecf20Sopenharmony_ci		return;
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci	spin_lock(&cfg_lock);
7538c2ecf20Sopenharmony_ci	if (t == cfg_get_task(chpid))
7548c2ecf20Sopenharmony_ci		cfg_set_task(chpid, cfg_none);
7558c2ecf20Sopenharmony_ci	spin_unlock(&cfg_lock);
7568c2ecf20Sopenharmony_ci	schedule_work(&cfg_work);
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci/**
7608c2ecf20Sopenharmony_ci * chp_cfg_schedule - schedule chpid configuration request
7618c2ecf20Sopenharmony_ci * @chpid: channel-path ID
7628c2ecf20Sopenharmony_ci * @configure: Non-zero for configure, zero for deconfigure
7638c2ecf20Sopenharmony_ci *
7648c2ecf20Sopenharmony_ci * Schedule a channel-path configuration/deconfiguration request.
7658c2ecf20Sopenharmony_ci */
7668c2ecf20Sopenharmony_civoid chp_cfg_schedule(struct chp_id chpid, int configure)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id,
7698c2ecf20Sopenharmony_ci		      configure);
7708c2ecf20Sopenharmony_ci	spin_lock(&cfg_lock);
7718c2ecf20Sopenharmony_ci	cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure);
7728c2ecf20Sopenharmony_ci	spin_unlock(&cfg_lock);
7738c2ecf20Sopenharmony_ci	schedule_work(&cfg_work);
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci/**
7778c2ecf20Sopenharmony_ci * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request
7788c2ecf20Sopenharmony_ci * @chpid: channel-path ID
7798c2ecf20Sopenharmony_ci *
7808c2ecf20Sopenharmony_ci * Cancel an active channel-path deconfiguration request if it has not yet
7818c2ecf20Sopenharmony_ci * been performed.
7828c2ecf20Sopenharmony_ci */
7838c2ecf20Sopenharmony_civoid chp_cfg_cancel_deconfigure(struct chp_id chpid)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id);
7868c2ecf20Sopenharmony_ci	spin_lock(&cfg_lock);
7878c2ecf20Sopenharmony_ci	if (cfg_get_task(chpid) == cfg_deconfigure)
7888c2ecf20Sopenharmony_ci		cfg_set_task(chpid, cfg_none);
7898c2ecf20Sopenharmony_ci	spin_unlock(&cfg_lock);
7908c2ecf20Sopenharmony_ci}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_cistatic bool cfg_idle(void)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct chp_id chpid;
7958c2ecf20Sopenharmony_ci	enum cfg_task_t t;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	spin_lock(&cfg_lock);
7988c2ecf20Sopenharmony_ci	t = chp_cfg_fetch_task(&chpid);
7998c2ecf20Sopenharmony_ci	spin_unlock(&cfg_lock);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	return t == cfg_none;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic int cfg_wait_idle(void)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	if (wait_event_interruptible(cfg_wait_queue, cfg_idle()))
8078c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
8088c2ecf20Sopenharmony_ci	return 0;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cistatic int __init chp_init(void)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	struct chp_id chpid;
8148c2ecf20Sopenharmony_ci	int state, ret;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	ret = crw_register_handler(CRW_RSC_CPATH, chp_process_crw);
8178c2ecf20Sopenharmony_ci	if (ret)
8188c2ecf20Sopenharmony_ci		return ret;
8198c2ecf20Sopenharmony_ci	INIT_WORK(&cfg_work, cfg_func);
8208c2ecf20Sopenharmony_ci	init_waitqueue_head(&cfg_wait_queue);
8218c2ecf20Sopenharmony_ci	if (info_update())
8228c2ecf20Sopenharmony_ci		return 0;
8238c2ecf20Sopenharmony_ci	/* Register available channel-paths. */
8248c2ecf20Sopenharmony_ci	chp_id_for_each(&chpid) {
8258c2ecf20Sopenharmony_ci		state = chp_info_get_status(chpid);
8268c2ecf20Sopenharmony_ci		if (state == CHP_STATUS_CONFIGURED ||
8278c2ecf20Sopenharmony_ci		    state == CHP_STATUS_STANDBY)
8288c2ecf20Sopenharmony_ci			chp_new(chpid);
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	return 0;
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_cisubsys_initcall(chp_init);
835