18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  bus driver for ccw devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2002, 2008
68c2ecf20Sopenharmony_ci *    Author(s): Arnd Bergmann (arndb@de.ibm.com)
78c2ecf20Sopenharmony_ci *		 Cornelia Huck (cornelia.huck@de.ibm.com)
88c2ecf20Sopenharmony_ci *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "cio"
128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/export.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/err.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/list.h>
218c2ecf20Sopenharmony_ci#include <linux/device.h>
228c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/timer.h>
258c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h>
268c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
278c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
308c2ecf20Sopenharmony_ci#include <asm/cio.h>
318c2ecf20Sopenharmony_ci#include <asm/param.h>		/* HZ */
328c2ecf20Sopenharmony_ci#include <asm/cmb.h>
338c2ecf20Sopenharmony_ci#include <asm/isc.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "chp.h"
368c2ecf20Sopenharmony_ci#include "cio.h"
378c2ecf20Sopenharmony_ci#include "cio_debug.h"
388c2ecf20Sopenharmony_ci#include "css.h"
398c2ecf20Sopenharmony_ci#include "device.h"
408c2ecf20Sopenharmony_ci#include "ioasm.h"
418c2ecf20Sopenharmony_ci#include "io_sch.h"
428c2ecf20Sopenharmony_ci#include "blacklist.h"
438c2ecf20Sopenharmony_ci#include "chsc.h"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic struct timer_list recovery_timer;
468c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(recovery_lock);
478c2ecf20Sopenharmony_cistatic int recovery_phase;
488c2ecf20Sopenharmony_cistatic const unsigned long recovery_delay[] = { 3, 30, 300 };
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic atomic_t ccw_device_init_count = ATOMIC_INIT(0);
518c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(ccw_device_init_wq);
528c2ecf20Sopenharmony_cistatic struct bus_type ccw_bus_type;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/******************* bus type handling ***********************/
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* The Linux driver model distinguishes between a bus type and
578c2ecf20Sopenharmony_ci * the bus itself. Of course we only have one channel
588c2ecf20Sopenharmony_ci * subsystem driver and one channel system per machine, but
598c2ecf20Sopenharmony_ci * we still use the abstraction. T.R. says it's a good idea. */
608c2ecf20Sopenharmony_cistatic int
618c2ecf20Sopenharmony_ciccw_bus_match (struct device * dev, struct device_driver * drv)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
648c2ecf20Sopenharmony_ci	struct ccw_driver *cdrv = to_ccwdrv(drv);
658c2ecf20Sopenharmony_ci	const struct ccw_device_id *ids = cdrv->ids, *found;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (!ids)
688c2ecf20Sopenharmony_ci		return 0;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	found = ccw_device_id_match(ids, &cdev->id);
718c2ecf20Sopenharmony_ci	if (!found)
728c2ecf20Sopenharmony_ci		return 0;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	cdev->id.driver_info = found->driver_info;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 1;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* Store modalias string delimited by prefix/suffix string into buffer with
808c2ecf20Sopenharmony_ci * specified size. Return length of resulting string (excluding trailing '\0')
818c2ecf20Sopenharmony_ci * even if string doesn't fit buffer (snprintf semantics). */
828c2ecf20Sopenharmony_cistatic int snprint_alias(char *buf, size_t size,
838c2ecf20Sopenharmony_ci			 struct ccw_device_id *id, const char *suffix)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int len;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model);
888c2ecf20Sopenharmony_ci	if (len > size)
898c2ecf20Sopenharmony_ci		return len;
908c2ecf20Sopenharmony_ci	buf += len;
918c2ecf20Sopenharmony_ci	size -= len;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (id->dev_type != 0)
948c2ecf20Sopenharmony_ci		len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type,
958c2ecf20Sopenharmony_ci				id->dev_model, suffix);
968c2ecf20Sopenharmony_ci	else
978c2ecf20Sopenharmony_ci		len += snprintf(buf, size, "dtdm%s", suffix);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return len;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/* Set up environment variables for ccw device uevent. Return 0 on success,
1038c2ecf20Sopenharmony_ci * non-zero otherwise. */
1048c2ecf20Sopenharmony_cistatic int ccw_uevent(struct device *dev, struct kobj_uevent_env *env)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
1078c2ecf20Sopenharmony_ci	struct ccw_device_id *id = &(cdev->id);
1088c2ecf20Sopenharmony_ci	int ret;
1098c2ecf20Sopenharmony_ci	char modalias_buf[30];
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* CU_TYPE= */
1128c2ecf20Sopenharmony_ci	ret = add_uevent_var(env, "CU_TYPE=%04X", id->cu_type);
1138c2ecf20Sopenharmony_ci	if (ret)
1148c2ecf20Sopenharmony_ci		return ret;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* CU_MODEL= */
1178c2ecf20Sopenharmony_ci	ret = add_uevent_var(env, "CU_MODEL=%02X", id->cu_model);
1188c2ecf20Sopenharmony_ci	if (ret)
1198c2ecf20Sopenharmony_ci		return ret;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* The next two can be zero, that's ok for us */
1228c2ecf20Sopenharmony_ci	/* DEV_TYPE= */
1238c2ecf20Sopenharmony_ci	ret = add_uevent_var(env, "DEV_TYPE=%04X", id->dev_type);
1248c2ecf20Sopenharmony_ci	if (ret)
1258c2ecf20Sopenharmony_ci		return ret;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* DEV_MODEL= */
1288c2ecf20Sopenharmony_ci	ret = add_uevent_var(env, "DEV_MODEL=%02X", id->dev_model);
1298c2ecf20Sopenharmony_ci	if (ret)
1308c2ecf20Sopenharmony_ci		return ret;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* MODALIAS=  */
1338c2ecf20Sopenharmony_ci	snprint_alias(modalias_buf, sizeof(modalias_buf), id, "");
1348c2ecf20Sopenharmony_ci	ret = add_uevent_var(env, "MODALIAS=%s", modalias_buf);
1358c2ecf20Sopenharmony_ci	return ret;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void io_subchannel_irq(struct subchannel *);
1398c2ecf20Sopenharmony_cistatic int io_subchannel_probe(struct subchannel *);
1408c2ecf20Sopenharmony_cistatic int io_subchannel_remove(struct subchannel *);
1418c2ecf20Sopenharmony_cistatic void io_subchannel_shutdown(struct subchannel *);
1428c2ecf20Sopenharmony_cistatic int io_subchannel_sch_event(struct subchannel *, int);
1438c2ecf20Sopenharmony_cistatic int io_subchannel_chp_event(struct subchannel *, struct chp_link *,
1448c2ecf20Sopenharmony_ci				   int);
1458c2ecf20Sopenharmony_cistatic void recovery_func(struct timer_list *unused);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic struct css_device_id io_subchannel_ids[] = {
1488c2ecf20Sopenharmony_ci	{ .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
1498c2ecf20Sopenharmony_ci	{ /* end of list */ },
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int io_subchannel_prepare(struct subchannel *sch)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
1558c2ecf20Sopenharmony_ci	/*
1568c2ecf20Sopenharmony_ci	 * Don't allow suspend while a ccw device registration
1578c2ecf20Sopenharmony_ci	 * is still outstanding.
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	cdev = sch_get_cdev(sch);
1608c2ecf20Sopenharmony_ci	if (cdev && !device_is_registered(&cdev->dev))
1618c2ecf20Sopenharmony_ci		return -EAGAIN;
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int io_subchannel_settle(void)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	int ret;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	ret = wait_event_interruptible(ccw_device_init_wq,
1708c2ecf20Sopenharmony_ci				atomic_read(&ccw_device_init_count) == 0);
1718c2ecf20Sopenharmony_ci	if (ret)
1728c2ecf20Sopenharmony_ci		return -EINTR;
1738c2ecf20Sopenharmony_ci	flush_workqueue(cio_work_q);
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic struct css_driver io_subchannel_driver = {
1788c2ecf20Sopenharmony_ci	.drv = {
1798c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
1808c2ecf20Sopenharmony_ci		.name = "io_subchannel",
1818c2ecf20Sopenharmony_ci	},
1828c2ecf20Sopenharmony_ci	.subchannel_type = io_subchannel_ids,
1838c2ecf20Sopenharmony_ci	.irq = io_subchannel_irq,
1848c2ecf20Sopenharmony_ci	.sch_event = io_subchannel_sch_event,
1858c2ecf20Sopenharmony_ci	.chp_event = io_subchannel_chp_event,
1868c2ecf20Sopenharmony_ci	.probe = io_subchannel_probe,
1878c2ecf20Sopenharmony_ci	.remove = io_subchannel_remove,
1888c2ecf20Sopenharmony_ci	.shutdown = io_subchannel_shutdown,
1898c2ecf20Sopenharmony_ci	.prepare = io_subchannel_prepare,
1908c2ecf20Sopenharmony_ci	.settle = io_subchannel_settle,
1918c2ecf20Sopenharmony_ci};
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciint __init io_subchannel_init(void)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	timer_setup(&recovery_timer, recovery_func, 0);
1988c2ecf20Sopenharmony_ci	ret = bus_register(&ccw_bus_type);
1998c2ecf20Sopenharmony_ci	if (ret)
2008c2ecf20Sopenharmony_ci		return ret;
2018c2ecf20Sopenharmony_ci	ret = css_driver_register(&io_subchannel_driver);
2028c2ecf20Sopenharmony_ci	if (ret)
2038c2ecf20Sopenharmony_ci		bus_unregister(&ccw_bus_type);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return ret;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/************************ device handling **************************/
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic ssize_t
2128c2ecf20Sopenharmony_cidevtype_show (struct device *dev, struct device_attribute *attr, char *buf)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
2158c2ecf20Sopenharmony_ci	struct ccw_device_id *id = &(cdev->id);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (id->dev_type != 0)
2188c2ecf20Sopenharmony_ci		return sprintf(buf, "%04x/%02x\n",
2198c2ecf20Sopenharmony_ci				id->dev_type, id->dev_model);
2208c2ecf20Sopenharmony_ci	else
2218c2ecf20Sopenharmony_ci		return sprintf(buf, "n/a\n");
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic ssize_t
2258c2ecf20Sopenharmony_cicutype_show (struct device *dev, struct device_attribute *attr, char *buf)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
2288c2ecf20Sopenharmony_ci	struct ccw_device_id *id = &(cdev->id);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	return sprintf(buf, "%04x/%02x\n",
2318c2ecf20Sopenharmony_ci		       id->cu_type, id->cu_model);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic ssize_t
2358c2ecf20Sopenharmony_cimodalias_show (struct device *dev, struct device_attribute *attr, char *buf)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
2388c2ecf20Sopenharmony_ci	struct ccw_device_id *id = &(cdev->id);
2398c2ecf20Sopenharmony_ci	int len;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	len = snprint_alias(buf, PAGE_SIZE, id, "\n");
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return len > PAGE_SIZE ? PAGE_SIZE : len;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic ssize_t
2478c2ecf20Sopenharmony_cionline_show (struct device *dev, struct device_attribute *attr, char *buf)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return sprintf(buf, cdev->online ? "1\n" : "0\n");
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ciint ccw_device_is_orphan(struct ccw_device *cdev)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void ccw_device_unregister(struct ccw_device *cdev)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	if (device_is_registered(&cdev->dev)) {
2628c2ecf20Sopenharmony_ci		/* Undo device_add(). */
2638c2ecf20Sopenharmony_ci		device_del(&cdev->dev);
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci	if (cdev->private->flags.initialized) {
2668c2ecf20Sopenharmony_ci		cdev->private->flags.initialized = 0;
2678c2ecf20Sopenharmony_ci		/* Release reference from device_initialize(). */
2688c2ecf20Sopenharmony_ci		put_device(&cdev->dev);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic void io_subchannel_quiesce(struct subchannel *);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/**
2758c2ecf20Sopenharmony_ci * ccw_device_set_offline() - disable a ccw device for I/O
2768c2ecf20Sopenharmony_ci * @cdev: target ccw device
2778c2ecf20Sopenharmony_ci *
2788c2ecf20Sopenharmony_ci * This function calls the driver's set_offline() function for @cdev, if
2798c2ecf20Sopenharmony_ci * given, and then disables @cdev.
2808c2ecf20Sopenharmony_ci * Returns:
2818c2ecf20Sopenharmony_ci *   %0 on success and a negative error value on failure.
2828c2ecf20Sopenharmony_ci * Context:
2838c2ecf20Sopenharmony_ci *  enabled, ccw device lock not held
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_ciint ccw_device_set_offline(struct ccw_device *cdev)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct subchannel *sch;
2888c2ecf20Sopenharmony_ci	int ret, state;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (!cdev)
2918c2ecf20Sopenharmony_ci		return -ENODEV;
2928c2ecf20Sopenharmony_ci	if (!cdev->online || !cdev->drv)
2938c2ecf20Sopenharmony_ci		return -EINVAL;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (cdev->drv->set_offline) {
2968c2ecf20Sopenharmony_ci		ret = cdev->drv->set_offline(cdev);
2978c2ecf20Sopenharmony_ci		if (ret != 0)
2988c2ecf20Sopenharmony_ci			return ret;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
3018c2ecf20Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
3028c2ecf20Sopenharmony_ci	cdev->online = 0;
3038c2ecf20Sopenharmony_ci	/* Wait until a final state or DISCONNECTED is reached */
3048c2ecf20Sopenharmony_ci	while (!dev_fsm_final_state(cdev) &&
3058c2ecf20Sopenharmony_ci	       cdev->private->state != DEV_STATE_DISCONNECTED) {
3068c2ecf20Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
3078c2ecf20Sopenharmony_ci		wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
3088c2ecf20Sopenharmony_ci			   cdev->private->state == DEV_STATE_DISCONNECTED));
3098c2ecf20Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	do {
3128c2ecf20Sopenharmony_ci		ret = ccw_device_offline(cdev);
3138c2ecf20Sopenharmony_ci		if (!ret)
3148c2ecf20Sopenharmony_ci			break;
3158c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device "
3168c2ecf20Sopenharmony_ci			      "0.%x.%04x\n", ret, cdev->private->dev_id.ssid,
3178c2ecf20Sopenharmony_ci			      cdev->private->dev_id.devno);
3188c2ecf20Sopenharmony_ci		if (ret != -EBUSY)
3198c2ecf20Sopenharmony_ci			goto error;
3208c2ecf20Sopenharmony_ci		state = cdev->private->state;
3218c2ecf20Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
3228c2ecf20Sopenharmony_ci		io_subchannel_quiesce(sch);
3238c2ecf20Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
3248c2ecf20Sopenharmony_ci		cdev->private->state = state;
3258c2ecf20Sopenharmony_ci	} while (ret == -EBUSY);
3268c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
3278c2ecf20Sopenharmony_ci	wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
3288c2ecf20Sopenharmony_ci		   cdev->private->state == DEV_STATE_DISCONNECTED));
3298c2ecf20Sopenharmony_ci	/* Inform the user if set offline failed. */
3308c2ecf20Sopenharmony_ci	if (cdev->private->state == DEV_STATE_BOXED) {
3318c2ecf20Sopenharmony_ci		pr_warn("%s: The device entered boxed state while being set offline\n",
3328c2ecf20Sopenharmony_ci			dev_name(&cdev->dev));
3338c2ecf20Sopenharmony_ci	} else if (cdev->private->state == DEV_STATE_NOT_OPER) {
3348c2ecf20Sopenharmony_ci		pr_warn("%s: The device stopped operating while being set offline\n",
3358c2ecf20Sopenharmony_ci			dev_name(&cdev->dev));
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	/* Give up reference from ccw_device_set_online(). */
3388c2ecf20Sopenharmony_ci	put_device(&cdev->dev);
3398c2ecf20Sopenharmony_ci	return 0;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cierror:
3428c2ecf20Sopenharmony_ci	cdev->private->state = DEV_STATE_OFFLINE;
3438c2ecf20Sopenharmony_ci	dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
3448c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
3458c2ecf20Sopenharmony_ci	/* Give up reference from ccw_device_set_online(). */
3468c2ecf20Sopenharmony_ci	put_device(&cdev->dev);
3478c2ecf20Sopenharmony_ci	return -ENODEV;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/**
3518c2ecf20Sopenharmony_ci * ccw_device_set_online() - enable a ccw device for I/O
3528c2ecf20Sopenharmony_ci * @cdev: target ccw device
3538c2ecf20Sopenharmony_ci *
3548c2ecf20Sopenharmony_ci * This function first enables @cdev and then calls the driver's set_online()
3558c2ecf20Sopenharmony_ci * function for @cdev, if given. If set_online() returns an error, @cdev is
3568c2ecf20Sopenharmony_ci * disabled again.
3578c2ecf20Sopenharmony_ci * Returns:
3588c2ecf20Sopenharmony_ci *   %0 on success and a negative error value on failure.
3598c2ecf20Sopenharmony_ci * Context:
3608c2ecf20Sopenharmony_ci *  enabled, ccw device lock not held
3618c2ecf20Sopenharmony_ci */
3628c2ecf20Sopenharmony_ciint ccw_device_set_online(struct ccw_device *cdev)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	int ret;
3658c2ecf20Sopenharmony_ci	int ret2;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (!cdev)
3688c2ecf20Sopenharmony_ci		return -ENODEV;
3698c2ecf20Sopenharmony_ci	if (cdev->online || !cdev->drv)
3708c2ecf20Sopenharmony_ci		return -EINVAL;
3718c2ecf20Sopenharmony_ci	/* Hold on to an extra reference while device is online. */
3728c2ecf20Sopenharmony_ci	if (!get_device(&cdev->dev))
3738c2ecf20Sopenharmony_ci		return -ENODEV;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
3768c2ecf20Sopenharmony_ci	ret = ccw_device_online(cdev);
3778c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
3788c2ecf20Sopenharmony_ci	if (ret == 0)
3798c2ecf20Sopenharmony_ci		wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
3808c2ecf20Sopenharmony_ci	else {
3818c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(0, "ccw_device_online returned %d, "
3828c2ecf20Sopenharmony_ci			      "device 0.%x.%04x\n",
3838c2ecf20Sopenharmony_ci			      ret, cdev->private->dev_id.ssid,
3848c2ecf20Sopenharmony_ci			      cdev->private->dev_id.devno);
3858c2ecf20Sopenharmony_ci		/* Give up online reference since onlining failed. */
3868c2ecf20Sopenharmony_ci		put_device(&cdev->dev);
3878c2ecf20Sopenharmony_ci		return ret;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
3908c2ecf20Sopenharmony_ci	/* Check if online processing was successful */
3918c2ecf20Sopenharmony_ci	if ((cdev->private->state != DEV_STATE_ONLINE) &&
3928c2ecf20Sopenharmony_ci	    (cdev->private->state != DEV_STATE_W4SENSE)) {
3938c2ecf20Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
3948c2ecf20Sopenharmony_ci		/* Inform the user that set online failed. */
3958c2ecf20Sopenharmony_ci		if (cdev->private->state == DEV_STATE_BOXED) {
3968c2ecf20Sopenharmony_ci			pr_warn("%s: Setting the device online failed because it is boxed\n",
3978c2ecf20Sopenharmony_ci				dev_name(&cdev->dev));
3988c2ecf20Sopenharmony_ci		} else if (cdev->private->state == DEV_STATE_NOT_OPER) {
3998c2ecf20Sopenharmony_ci			pr_warn("%s: Setting the device online failed because it is not operational\n",
4008c2ecf20Sopenharmony_ci				dev_name(&cdev->dev));
4018c2ecf20Sopenharmony_ci		}
4028c2ecf20Sopenharmony_ci		/* Give up online reference since onlining failed. */
4038c2ecf20Sopenharmony_ci		put_device(&cdev->dev);
4048c2ecf20Sopenharmony_ci		return -ENODEV;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
4078c2ecf20Sopenharmony_ci	if (cdev->drv->set_online)
4088c2ecf20Sopenharmony_ci		ret = cdev->drv->set_online(cdev);
4098c2ecf20Sopenharmony_ci	if (ret)
4108c2ecf20Sopenharmony_ci		goto rollback;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
4138c2ecf20Sopenharmony_ci	cdev->online = 1;
4148c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
4158c2ecf20Sopenharmony_ci	return 0;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cirollback:
4188c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
4198c2ecf20Sopenharmony_ci	/* Wait until a final state or DISCONNECTED is reached */
4208c2ecf20Sopenharmony_ci	while (!dev_fsm_final_state(cdev) &&
4218c2ecf20Sopenharmony_ci	       cdev->private->state != DEV_STATE_DISCONNECTED) {
4228c2ecf20Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
4238c2ecf20Sopenharmony_ci		wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
4248c2ecf20Sopenharmony_ci			   cdev->private->state == DEV_STATE_DISCONNECTED));
4258c2ecf20Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	ret2 = ccw_device_offline(cdev);
4288c2ecf20Sopenharmony_ci	if (ret2)
4298c2ecf20Sopenharmony_ci		goto error;
4308c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
4318c2ecf20Sopenharmony_ci	wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
4328c2ecf20Sopenharmony_ci		   cdev->private->state == DEV_STATE_DISCONNECTED));
4338c2ecf20Sopenharmony_ci	/* Give up online reference since onlining failed. */
4348c2ecf20Sopenharmony_ci	put_device(&cdev->dev);
4358c2ecf20Sopenharmony_ci	return ret;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cierror:
4388c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(0, "rollback ccw_device_offline returned %d, "
4398c2ecf20Sopenharmony_ci		      "device 0.%x.%04x\n",
4408c2ecf20Sopenharmony_ci		      ret2, cdev->private->dev_id.ssid,
4418c2ecf20Sopenharmony_ci		      cdev->private->dev_id.devno);
4428c2ecf20Sopenharmony_ci	cdev->private->state = DEV_STATE_OFFLINE;
4438c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
4448c2ecf20Sopenharmony_ci	/* Give up online reference since onlining failed. */
4458c2ecf20Sopenharmony_ci	put_device(&cdev->dev);
4468c2ecf20Sopenharmony_ci	return ret;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic int online_store_handle_offline(struct ccw_device *cdev)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	if (cdev->private->state == DEV_STATE_DISCONNECTED) {
4528c2ecf20Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
4538c2ecf20Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
4548c2ecf20Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
4558c2ecf20Sopenharmony_ci		return 0;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	if (cdev->drv && cdev->drv->set_offline)
4588c2ecf20Sopenharmony_ci		return ccw_device_set_offline(cdev);
4598c2ecf20Sopenharmony_ci	return -EINVAL;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic int online_store_recog_and_online(struct ccw_device *cdev)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	/* Do device recognition, if needed. */
4658c2ecf20Sopenharmony_ci	if (cdev->private->state == DEV_STATE_BOXED) {
4668c2ecf20Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
4678c2ecf20Sopenharmony_ci		ccw_device_recognition(cdev);
4688c2ecf20Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
4698c2ecf20Sopenharmony_ci		wait_event(cdev->private->wait_q,
4708c2ecf20Sopenharmony_ci			   cdev->private->flags.recog_done);
4718c2ecf20Sopenharmony_ci		if (cdev->private->state != DEV_STATE_OFFLINE)
4728c2ecf20Sopenharmony_ci			/* recognition failed */
4738c2ecf20Sopenharmony_ci			return -EAGAIN;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci	if (cdev->drv && cdev->drv->set_online)
4768c2ecf20Sopenharmony_ci		return ccw_device_set_online(cdev);
4778c2ecf20Sopenharmony_ci	return -EINVAL;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic int online_store_handle_online(struct ccw_device *cdev, int force)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	int ret;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	ret = online_store_recog_and_online(cdev);
4858c2ecf20Sopenharmony_ci	if (ret && !force)
4868c2ecf20Sopenharmony_ci		return ret;
4878c2ecf20Sopenharmony_ci	if (force && cdev->private->state == DEV_STATE_BOXED) {
4888c2ecf20Sopenharmony_ci		ret = ccw_device_stlck(cdev);
4898c2ecf20Sopenharmony_ci		if (ret)
4908c2ecf20Sopenharmony_ci			return ret;
4918c2ecf20Sopenharmony_ci		if (cdev->id.cu_type == 0)
4928c2ecf20Sopenharmony_ci			cdev->private->state = DEV_STATE_NOT_OPER;
4938c2ecf20Sopenharmony_ci		ret = online_store_recog_and_online(cdev);
4948c2ecf20Sopenharmony_ci		if (ret)
4958c2ecf20Sopenharmony_ci			return ret;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci	return 0;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic ssize_t online_store (struct device *dev, struct device_attribute *attr,
5018c2ecf20Sopenharmony_ci			     const char *buf, size_t count)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
5048c2ecf20Sopenharmony_ci	int force, ret;
5058c2ecf20Sopenharmony_ci	unsigned long i;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/* Prevent conflict between multiple on-/offline processing requests. */
5088c2ecf20Sopenharmony_ci	if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
5098c2ecf20Sopenharmony_ci		return -EAGAIN;
5108c2ecf20Sopenharmony_ci	/* Prevent conflict between internal I/Os and on-/offline processing. */
5118c2ecf20Sopenharmony_ci	if (!dev_fsm_final_state(cdev) &&
5128c2ecf20Sopenharmony_ci	    cdev->private->state != DEV_STATE_DISCONNECTED) {
5138c2ecf20Sopenharmony_ci		ret = -EAGAIN;
5148c2ecf20Sopenharmony_ci		goto out;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	/* Prevent conflict between pending work and on-/offline processing.*/
5178c2ecf20Sopenharmony_ci	if (work_pending(&cdev->private->todo_work)) {
5188c2ecf20Sopenharmony_ci		ret = -EAGAIN;
5198c2ecf20Sopenharmony_ci		goto out;
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci	if (!strncmp(buf, "force\n", count)) {
5228c2ecf20Sopenharmony_ci		force = 1;
5238c2ecf20Sopenharmony_ci		i = 1;
5248c2ecf20Sopenharmony_ci		ret = 0;
5258c2ecf20Sopenharmony_ci	} else {
5268c2ecf20Sopenharmony_ci		force = 0;
5278c2ecf20Sopenharmony_ci		ret = kstrtoul(buf, 16, &i);
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci	if (ret)
5308c2ecf20Sopenharmony_ci		goto out;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	device_lock(dev);
5338c2ecf20Sopenharmony_ci	switch (i) {
5348c2ecf20Sopenharmony_ci	case 0:
5358c2ecf20Sopenharmony_ci		ret = online_store_handle_offline(cdev);
5368c2ecf20Sopenharmony_ci		break;
5378c2ecf20Sopenharmony_ci	case 1:
5388c2ecf20Sopenharmony_ci		ret = online_store_handle_online(cdev, force);
5398c2ecf20Sopenharmony_ci		break;
5408c2ecf20Sopenharmony_ci	default:
5418c2ecf20Sopenharmony_ci		ret = -EINVAL;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci	device_unlock(dev);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ciout:
5468c2ecf20Sopenharmony_ci	atomic_set(&cdev->private->onoff, 0);
5478c2ecf20Sopenharmony_ci	return (ret < 0) ? ret : count;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic ssize_t
5518c2ecf20Sopenharmony_ciavailable_show (struct device *dev, struct device_attribute *attr, char *buf)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
5548c2ecf20Sopenharmony_ci	struct subchannel *sch;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (ccw_device_is_orphan(cdev))
5578c2ecf20Sopenharmony_ci		return sprintf(buf, "no device\n");
5588c2ecf20Sopenharmony_ci	switch (cdev->private->state) {
5598c2ecf20Sopenharmony_ci	case DEV_STATE_BOXED:
5608c2ecf20Sopenharmony_ci		return sprintf(buf, "boxed\n");
5618c2ecf20Sopenharmony_ci	case DEV_STATE_DISCONNECTED:
5628c2ecf20Sopenharmony_ci	case DEV_STATE_DISCONNECTED_SENSE_ID:
5638c2ecf20Sopenharmony_ci	case DEV_STATE_NOT_OPER:
5648c2ecf20Sopenharmony_ci		sch = to_subchannel(dev->parent);
5658c2ecf20Sopenharmony_ci		if (!sch->lpm)
5668c2ecf20Sopenharmony_ci			return sprintf(buf, "no path\n");
5678c2ecf20Sopenharmony_ci		else
5688c2ecf20Sopenharmony_ci			return sprintf(buf, "no device\n");
5698c2ecf20Sopenharmony_ci	default:
5708c2ecf20Sopenharmony_ci		/* All other states considered fine. */
5718c2ecf20Sopenharmony_ci		return sprintf(buf, "good\n");
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic ssize_t
5768c2ecf20Sopenharmony_ciinitiate_logging(struct device *dev, struct device_attribute *attr,
5778c2ecf20Sopenharmony_ci		 const char *buf, size_t count)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(dev);
5808c2ecf20Sopenharmony_ci	int rc;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	rc = chsc_siosl(sch->schid);
5838c2ecf20Sopenharmony_ci	if (rc < 0) {
5848c2ecf20Sopenharmony_ci		pr_warn("Logging for subchannel 0.%x.%04x failed with errno=%d\n",
5858c2ecf20Sopenharmony_ci			sch->schid.ssid, sch->schid.sch_no, rc);
5868c2ecf20Sopenharmony_ci		return rc;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci	pr_notice("Logging for subchannel 0.%x.%04x was triggered\n",
5898c2ecf20Sopenharmony_ci		  sch->schid.ssid, sch->schid.sch_no);
5908c2ecf20Sopenharmony_ci	return count;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic ssize_t vpm_show(struct device *dev, struct device_attribute *attr,
5948c2ecf20Sopenharmony_ci			char *buf)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(dev);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	return sprintf(buf, "%02x\n", sch->vpm);
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(devtype);
6028c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cutype);
6038c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
6048c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(online);
6058c2ecf20Sopenharmony_cistatic DEVICE_ATTR(availability, 0444, available_show, NULL);
6068c2ecf20Sopenharmony_cistatic DEVICE_ATTR(logging, 0200, NULL, initiate_logging);
6078c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(vpm);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic struct attribute *io_subchannel_attrs[] = {
6108c2ecf20Sopenharmony_ci	&dev_attr_logging.attr,
6118c2ecf20Sopenharmony_ci	&dev_attr_vpm.attr,
6128c2ecf20Sopenharmony_ci	NULL,
6138c2ecf20Sopenharmony_ci};
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic const struct attribute_group io_subchannel_attr_group = {
6168c2ecf20Sopenharmony_ci	.attrs = io_subchannel_attrs,
6178c2ecf20Sopenharmony_ci};
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic struct attribute * ccwdev_attrs[] = {
6208c2ecf20Sopenharmony_ci	&dev_attr_devtype.attr,
6218c2ecf20Sopenharmony_ci	&dev_attr_cutype.attr,
6228c2ecf20Sopenharmony_ci	&dev_attr_modalias.attr,
6238c2ecf20Sopenharmony_ci	&dev_attr_online.attr,
6248c2ecf20Sopenharmony_ci	&dev_attr_cmb_enable.attr,
6258c2ecf20Sopenharmony_ci	&dev_attr_availability.attr,
6268c2ecf20Sopenharmony_ci	NULL,
6278c2ecf20Sopenharmony_ci};
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistatic const struct attribute_group ccwdev_attr_group = {
6308c2ecf20Sopenharmony_ci	.attrs = ccwdev_attrs,
6318c2ecf20Sopenharmony_ci};
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_cistatic const struct attribute_group *ccwdev_attr_groups[] = {
6348c2ecf20Sopenharmony_ci	&ccwdev_attr_group,
6358c2ecf20Sopenharmony_ci	NULL,
6368c2ecf20Sopenharmony_ci};
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic int ccw_device_add(struct ccw_device *cdev)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct device *dev = &cdev->dev;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	dev->bus = &ccw_bus_type;
6438c2ecf20Sopenharmony_ci	return device_add(dev);
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic int match_dev_id(struct device *dev, const void *data)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
6498c2ecf20Sopenharmony_ci	struct ccw_dev_id *dev_id = (void *)data;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id);
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci/**
6558c2ecf20Sopenharmony_ci * get_ccwdev_by_dev_id() - obtain device from a ccw device id
6568c2ecf20Sopenharmony_ci * @dev_id: id of the device to be searched
6578c2ecf20Sopenharmony_ci *
6588c2ecf20Sopenharmony_ci * This function searches all devices attached to the ccw bus for a device
6598c2ecf20Sopenharmony_ci * matching @dev_id.
6608c2ecf20Sopenharmony_ci * Returns:
6618c2ecf20Sopenharmony_ci *  If a device is found its reference count is increased and returned;
6628c2ecf20Sopenharmony_ci *  else %NULL is returned.
6638c2ecf20Sopenharmony_ci */
6648c2ecf20Sopenharmony_cistruct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct device *dev;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	dev = bus_find_device(&ccw_bus_type, NULL, dev_id, match_dev_id);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	return dev ? to_ccwdev(dev) : NULL;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(get_ccwdev_by_dev_id);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic void ccw_device_do_unbind_bind(struct ccw_device *cdev)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	int ret;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (device_is_registered(&cdev->dev)) {
6798c2ecf20Sopenharmony_ci		device_release_driver(&cdev->dev);
6808c2ecf20Sopenharmony_ci		ret = device_attach(&cdev->dev);
6818c2ecf20Sopenharmony_ci		WARN_ON(ret == -ENODEV);
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic void
6868c2ecf20Sopenharmony_ciccw_device_release(struct device *dev)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	cdev = to_ccwdev(dev);
6918c2ecf20Sopenharmony_ci	cio_gp_dma_free(cdev->private->dma_pool, cdev->private->dma_area,
6928c2ecf20Sopenharmony_ci			sizeof(*cdev->private->dma_area));
6938c2ecf20Sopenharmony_ci	cio_gp_dma_destroy(cdev->private->dma_pool, &cdev->dev);
6948c2ecf20Sopenharmony_ci	/* Release reference of parent subchannel. */
6958c2ecf20Sopenharmony_ci	put_device(cdev->dev.parent);
6968c2ecf20Sopenharmony_ci	kfree(cdev->private);
6978c2ecf20Sopenharmony_ci	kfree(cdev);
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
7038c2ecf20Sopenharmony_ci	struct gen_pool *dma_pool;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	cdev  = kzalloc(sizeof(*cdev), GFP_KERNEL);
7068c2ecf20Sopenharmony_ci	if (!cdev)
7078c2ecf20Sopenharmony_ci		goto err_cdev;
7088c2ecf20Sopenharmony_ci	cdev->private = kzalloc(sizeof(struct ccw_device_private),
7098c2ecf20Sopenharmony_ci				GFP_KERNEL | GFP_DMA);
7108c2ecf20Sopenharmony_ci	if (!cdev->private)
7118c2ecf20Sopenharmony_ci		goto err_priv;
7128c2ecf20Sopenharmony_ci	cdev->dev.coherent_dma_mask = sch->dev.coherent_dma_mask;
7138c2ecf20Sopenharmony_ci	cdev->dev.dma_mask = sch->dev.dma_mask;
7148c2ecf20Sopenharmony_ci	dma_pool = cio_gp_dma_create(&cdev->dev, 1);
7158c2ecf20Sopenharmony_ci	if (!dma_pool)
7168c2ecf20Sopenharmony_ci		goto err_dma_pool;
7178c2ecf20Sopenharmony_ci	cdev->private->dma_pool = dma_pool;
7188c2ecf20Sopenharmony_ci	cdev->private->dma_area = cio_gp_dma_zalloc(dma_pool, &cdev->dev,
7198c2ecf20Sopenharmony_ci					sizeof(*cdev->private->dma_area));
7208c2ecf20Sopenharmony_ci	if (!cdev->private->dma_area)
7218c2ecf20Sopenharmony_ci		goto err_dma_area;
7228c2ecf20Sopenharmony_ci	return cdev;
7238c2ecf20Sopenharmony_cierr_dma_area:
7248c2ecf20Sopenharmony_ci	cio_gp_dma_destroy(dma_pool, &cdev->dev);
7258c2ecf20Sopenharmony_cierr_dma_pool:
7268c2ecf20Sopenharmony_ci	kfree(cdev->private);
7278c2ecf20Sopenharmony_cierr_priv:
7288c2ecf20Sopenharmony_ci	kfree(cdev);
7298c2ecf20Sopenharmony_cierr_cdev:
7308c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic void ccw_device_todo(struct work_struct *work);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cistatic int io_subchannel_initialize_dev(struct subchannel *sch,
7368c2ecf20Sopenharmony_ci					struct ccw_device *cdev)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	struct ccw_device_private *priv = cdev->private;
7398c2ecf20Sopenharmony_ci	int ret;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	priv->cdev = cdev;
7428c2ecf20Sopenharmony_ci	priv->int_class = IRQIO_CIO;
7438c2ecf20Sopenharmony_ci	priv->state = DEV_STATE_NOT_OPER;
7448c2ecf20Sopenharmony_ci	priv->dev_id.devno = sch->schib.pmcw.dev;
7458c2ecf20Sopenharmony_ci	priv->dev_id.ssid = sch->schid.ssid;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	INIT_WORK(&priv->todo_work, ccw_device_todo);
7488c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&priv->cmb_list);
7498c2ecf20Sopenharmony_ci	init_waitqueue_head(&priv->wait_q);
7508c2ecf20Sopenharmony_ci	timer_setup(&priv->timer, ccw_device_timeout, 0);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	atomic_set(&priv->onoff, 0);
7538c2ecf20Sopenharmony_ci	cdev->ccwlock = sch->lock;
7548c2ecf20Sopenharmony_ci	cdev->dev.parent = &sch->dev;
7558c2ecf20Sopenharmony_ci	cdev->dev.release = ccw_device_release;
7568c2ecf20Sopenharmony_ci	cdev->dev.groups = ccwdev_attr_groups;
7578c2ecf20Sopenharmony_ci	/* Do first half of device_register. */
7588c2ecf20Sopenharmony_ci	device_initialize(&cdev->dev);
7598c2ecf20Sopenharmony_ci	ret = dev_set_name(&cdev->dev, "0.%x.%04x", cdev->private->dev_id.ssid,
7608c2ecf20Sopenharmony_ci			   cdev->private->dev_id.devno);
7618c2ecf20Sopenharmony_ci	if (ret)
7628c2ecf20Sopenharmony_ci		goto out_put;
7638c2ecf20Sopenharmony_ci	if (!get_device(&sch->dev)) {
7648c2ecf20Sopenharmony_ci		ret = -ENODEV;
7658c2ecf20Sopenharmony_ci		goto out_put;
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci	priv->flags.initialized = 1;
7688c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
7698c2ecf20Sopenharmony_ci	sch_set_cdev(sch, cdev);
7708c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
7718c2ecf20Sopenharmony_ci	return 0;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ciout_put:
7748c2ecf20Sopenharmony_ci	/* Release reference from device_initialize(). */
7758c2ecf20Sopenharmony_ci	put_device(&cdev->dev);
7768c2ecf20Sopenharmony_ci	return ret;
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
7828c2ecf20Sopenharmony_ci	int ret;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	cdev = io_subchannel_allocate_dev(sch);
7858c2ecf20Sopenharmony_ci	if (!IS_ERR(cdev)) {
7868c2ecf20Sopenharmony_ci		ret = io_subchannel_initialize_dev(sch, cdev);
7878c2ecf20Sopenharmony_ci		if (ret)
7888c2ecf20Sopenharmony_ci			cdev = ERR_PTR(ret);
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci	return cdev;
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_cistatic void io_subchannel_recog(struct ccw_device *, struct subchannel *);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistatic void sch_create_and_recog_new_device(struct subchannel *sch)
7968c2ecf20Sopenharmony_ci{
7978c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	/* Need to allocate a new ccw device. */
8008c2ecf20Sopenharmony_ci	cdev = io_subchannel_create_ccwdev(sch);
8018c2ecf20Sopenharmony_ci	if (IS_ERR(cdev)) {
8028c2ecf20Sopenharmony_ci		/* OK, we did everything we could... */
8038c2ecf20Sopenharmony_ci		css_sch_device_unregister(sch);
8048c2ecf20Sopenharmony_ci		return;
8058c2ecf20Sopenharmony_ci	}
8068c2ecf20Sopenharmony_ci	/* Start recognition for the new ccw device. */
8078c2ecf20Sopenharmony_ci	io_subchannel_recog(cdev, sch);
8088c2ecf20Sopenharmony_ci}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci/*
8118c2ecf20Sopenharmony_ci * Register recognized device.
8128c2ecf20Sopenharmony_ci */
8138c2ecf20Sopenharmony_cistatic void io_subchannel_register(struct ccw_device *cdev)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	struct subchannel *sch;
8168c2ecf20Sopenharmony_ci	int ret, adjust_init_count = 1;
8178c2ecf20Sopenharmony_ci	unsigned long flags;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
8208c2ecf20Sopenharmony_ci	/*
8218c2ecf20Sopenharmony_ci	 * Check if subchannel is still registered. It may have become
8228c2ecf20Sopenharmony_ci	 * unregistered if a machine check hit us after finishing
8238c2ecf20Sopenharmony_ci	 * device recognition but before the register work could be
8248c2ecf20Sopenharmony_ci	 * queued.
8258c2ecf20Sopenharmony_ci	 */
8268c2ecf20Sopenharmony_ci	if (!device_is_registered(&sch->dev))
8278c2ecf20Sopenharmony_ci		goto out_err;
8288c2ecf20Sopenharmony_ci	css_update_ssd_info(sch);
8298c2ecf20Sopenharmony_ci	/*
8308c2ecf20Sopenharmony_ci	 * io_subchannel_register() will also be called after device
8318c2ecf20Sopenharmony_ci	 * recognition has been done for a boxed device (which will already
8328c2ecf20Sopenharmony_ci	 * be registered). We need to reprobe since we may now have sense id
8338c2ecf20Sopenharmony_ci	 * information.
8348c2ecf20Sopenharmony_ci	 */
8358c2ecf20Sopenharmony_ci	if (device_is_registered(&cdev->dev)) {
8368c2ecf20Sopenharmony_ci		if (!cdev->drv) {
8378c2ecf20Sopenharmony_ci			ret = device_reprobe(&cdev->dev);
8388c2ecf20Sopenharmony_ci			if (ret)
8398c2ecf20Sopenharmony_ci				/* We can't do much here. */
8408c2ecf20Sopenharmony_ci				CIO_MSG_EVENT(0, "device_reprobe() returned"
8418c2ecf20Sopenharmony_ci					      " %d for 0.%x.%04x\n", ret,
8428c2ecf20Sopenharmony_ci					      cdev->private->dev_id.ssid,
8438c2ecf20Sopenharmony_ci					      cdev->private->dev_id.devno);
8448c2ecf20Sopenharmony_ci		}
8458c2ecf20Sopenharmony_ci		adjust_init_count = 0;
8468c2ecf20Sopenharmony_ci		goto out;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci	/*
8498c2ecf20Sopenharmony_ci	 * Now we know this subchannel will stay, we can throw
8508c2ecf20Sopenharmony_ci	 * our delayed uevent.
8518c2ecf20Sopenharmony_ci	 */
8528c2ecf20Sopenharmony_ci	if (dev_get_uevent_suppress(&sch->dev)) {
8538c2ecf20Sopenharmony_ci		dev_set_uevent_suppress(&sch->dev, 0);
8548c2ecf20Sopenharmony_ci		kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci	/* make it known to the system */
8578c2ecf20Sopenharmony_ci	ret = ccw_device_add(cdev);
8588c2ecf20Sopenharmony_ci	if (ret) {
8598c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",
8608c2ecf20Sopenharmony_ci			      cdev->private->dev_id.ssid,
8618c2ecf20Sopenharmony_ci			      cdev->private->dev_id.devno, ret);
8628c2ecf20Sopenharmony_ci		spin_lock_irqsave(sch->lock, flags);
8638c2ecf20Sopenharmony_ci		sch_set_cdev(sch, NULL);
8648c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(sch->lock, flags);
8658c2ecf20Sopenharmony_ci		/* Release initial device reference. */
8668c2ecf20Sopenharmony_ci		put_device(&cdev->dev);
8678c2ecf20Sopenharmony_ci		goto out_err;
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ciout:
8708c2ecf20Sopenharmony_ci	cdev->private->flags.recog_done = 1;
8718c2ecf20Sopenharmony_ci	wake_up(&cdev->private->wait_q);
8728c2ecf20Sopenharmony_ciout_err:
8738c2ecf20Sopenharmony_ci	if (adjust_init_count && atomic_dec_and_test(&ccw_device_init_count))
8748c2ecf20Sopenharmony_ci		wake_up(&ccw_device_init_wq);
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_cistatic void ccw_device_call_sch_unregister(struct ccw_device *cdev)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	struct subchannel *sch;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/* Get subchannel reference for local processing. */
8828c2ecf20Sopenharmony_ci	if (!get_device(cdev->dev.parent))
8838c2ecf20Sopenharmony_ci		return;
8848c2ecf20Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
8858c2ecf20Sopenharmony_ci	css_sch_device_unregister(sch);
8868c2ecf20Sopenharmony_ci	/* Release subchannel reference for local processing. */
8878c2ecf20Sopenharmony_ci	put_device(&sch->dev);
8888c2ecf20Sopenharmony_ci}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci/*
8918c2ecf20Sopenharmony_ci * subchannel recognition done. Called from the state machine.
8928c2ecf20Sopenharmony_ci */
8938c2ecf20Sopenharmony_civoid
8948c2ecf20Sopenharmony_ciio_subchannel_recog_done(struct ccw_device *cdev)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	if (css_init_done == 0) {
8978c2ecf20Sopenharmony_ci		cdev->private->flags.recog_done = 1;
8988c2ecf20Sopenharmony_ci		return;
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci	switch (cdev->private->state) {
9018c2ecf20Sopenharmony_ci	case DEV_STATE_BOXED:
9028c2ecf20Sopenharmony_ci		/* Device did not respond in time. */
9038c2ecf20Sopenharmony_ci	case DEV_STATE_NOT_OPER:
9048c2ecf20Sopenharmony_ci		cdev->private->flags.recog_done = 1;
9058c2ecf20Sopenharmony_ci		/* Remove device found not operational. */
9068c2ecf20Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
9078c2ecf20Sopenharmony_ci		if (atomic_dec_and_test(&ccw_device_init_count))
9088c2ecf20Sopenharmony_ci			wake_up(&ccw_device_init_wq);
9098c2ecf20Sopenharmony_ci		break;
9108c2ecf20Sopenharmony_ci	case DEV_STATE_OFFLINE:
9118c2ecf20Sopenharmony_ci		/*
9128c2ecf20Sopenharmony_ci		 * We can't register the device in interrupt context so
9138c2ecf20Sopenharmony_ci		 * we schedule a work item.
9148c2ecf20Sopenharmony_ci		 */
9158c2ecf20Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_REGISTER);
9168c2ecf20Sopenharmony_ci		break;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cistatic void io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
9218c2ecf20Sopenharmony_ci{
9228c2ecf20Sopenharmony_ci	/* Increase counter of devices currently in recognition. */
9238c2ecf20Sopenharmony_ci	atomic_inc(&ccw_device_init_count);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	/* Start async. device sensing. */
9268c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
9278c2ecf20Sopenharmony_ci	ccw_device_recognition(cdev);
9288c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
9298c2ecf20Sopenharmony_ci}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_cistatic int ccw_device_move_to_sch(struct ccw_device *cdev,
9328c2ecf20Sopenharmony_ci				  struct subchannel *sch)
9338c2ecf20Sopenharmony_ci{
9348c2ecf20Sopenharmony_ci	struct subchannel *old_sch;
9358c2ecf20Sopenharmony_ci	int rc, old_enabled = 0;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	old_sch = to_subchannel(cdev->dev.parent);
9388c2ecf20Sopenharmony_ci	/* Obtain child reference for new parent. */
9398c2ecf20Sopenharmony_ci	if (!get_device(&sch->dev))
9408c2ecf20Sopenharmony_ci		return -ENODEV;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	if (!sch_is_pseudo_sch(old_sch)) {
9438c2ecf20Sopenharmony_ci		spin_lock_irq(old_sch->lock);
9448c2ecf20Sopenharmony_ci		old_enabled = old_sch->schib.pmcw.ena;
9458c2ecf20Sopenharmony_ci		rc = 0;
9468c2ecf20Sopenharmony_ci		if (old_enabled)
9478c2ecf20Sopenharmony_ci			rc = cio_disable_subchannel(old_sch);
9488c2ecf20Sopenharmony_ci		spin_unlock_irq(old_sch->lock);
9498c2ecf20Sopenharmony_ci		if (rc == -EBUSY) {
9508c2ecf20Sopenharmony_ci			/* Release child reference for new parent. */
9518c2ecf20Sopenharmony_ci			put_device(&sch->dev);
9528c2ecf20Sopenharmony_ci			return rc;
9538c2ecf20Sopenharmony_ci		}
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	mutex_lock(&sch->reg_mutex);
9578c2ecf20Sopenharmony_ci	rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
9588c2ecf20Sopenharmony_ci	mutex_unlock(&sch->reg_mutex);
9598c2ecf20Sopenharmony_ci	if (rc) {
9608c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(0, "device_move(0.%x.%04x,0.%x.%04x)=%d\n",
9618c2ecf20Sopenharmony_ci			      cdev->private->dev_id.ssid,
9628c2ecf20Sopenharmony_ci			      cdev->private->dev_id.devno, sch->schid.ssid,
9638c2ecf20Sopenharmony_ci			      sch->schib.pmcw.dev, rc);
9648c2ecf20Sopenharmony_ci		if (old_enabled) {
9658c2ecf20Sopenharmony_ci			/* Try to reenable the old subchannel. */
9668c2ecf20Sopenharmony_ci			spin_lock_irq(old_sch->lock);
9678c2ecf20Sopenharmony_ci			cio_enable_subchannel(old_sch, (u32)(addr_t)old_sch);
9688c2ecf20Sopenharmony_ci			spin_unlock_irq(old_sch->lock);
9698c2ecf20Sopenharmony_ci		}
9708c2ecf20Sopenharmony_ci		/* Release child reference for new parent. */
9718c2ecf20Sopenharmony_ci		put_device(&sch->dev);
9728c2ecf20Sopenharmony_ci		return rc;
9738c2ecf20Sopenharmony_ci	}
9748c2ecf20Sopenharmony_ci	/* Clean up old subchannel. */
9758c2ecf20Sopenharmony_ci	if (!sch_is_pseudo_sch(old_sch)) {
9768c2ecf20Sopenharmony_ci		spin_lock_irq(old_sch->lock);
9778c2ecf20Sopenharmony_ci		sch_set_cdev(old_sch, NULL);
9788c2ecf20Sopenharmony_ci		spin_unlock_irq(old_sch->lock);
9798c2ecf20Sopenharmony_ci		css_schedule_eval(old_sch->schid);
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci	/* Release child reference for old parent. */
9828c2ecf20Sopenharmony_ci	put_device(&old_sch->dev);
9838c2ecf20Sopenharmony_ci	/* Initialize new subchannel. */
9848c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
9858c2ecf20Sopenharmony_ci	cdev->ccwlock = sch->lock;
9868c2ecf20Sopenharmony_ci	if (!sch_is_pseudo_sch(sch))
9878c2ecf20Sopenharmony_ci		sch_set_cdev(sch, cdev);
9888c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
9898c2ecf20Sopenharmony_ci	if (!sch_is_pseudo_sch(sch))
9908c2ecf20Sopenharmony_ci		css_update_ssd_info(sch);
9918c2ecf20Sopenharmony_ci	return 0;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic int ccw_device_move_to_orph(struct ccw_device *cdev)
9958c2ecf20Sopenharmony_ci{
9968c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
9978c2ecf20Sopenharmony_ci	struct channel_subsystem *css = to_css(sch->dev.parent);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return ccw_device_move_to_sch(cdev, css->pseudo_subchannel);
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic void io_subchannel_irq(struct subchannel *sch)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	cdev = sch_get_cdev(sch);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(6, "IRQ");
10098c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(6, dev_name(&sch->dev));
10108c2ecf20Sopenharmony_ci	if (cdev)
10118c2ecf20Sopenharmony_ci		dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
10128c2ecf20Sopenharmony_ci	else
10138c2ecf20Sopenharmony_ci		inc_irq_stat(IRQIO_CIO);
10148c2ecf20Sopenharmony_ci}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_civoid io_subchannel_init_config(struct subchannel *sch)
10178c2ecf20Sopenharmony_ci{
10188c2ecf20Sopenharmony_ci	memset(&sch->config, 0, sizeof(sch->config));
10198c2ecf20Sopenharmony_ci	sch->config.csense = 1;
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic void io_subchannel_init_fields(struct subchannel *sch)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	if (cio_is_console(sch->schid))
10258c2ecf20Sopenharmony_ci		sch->opm = 0xff;
10268c2ecf20Sopenharmony_ci	else
10278c2ecf20Sopenharmony_ci		sch->opm = chp_get_sch_opm(sch);
10288c2ecf20Sopenharmony_ci	sch->lpm = sch->schib.pmcw.pam & sch->opm;
10298c2ecf20Sopenharmony_ci	sch->isc = cio_is_console(sch->schid) ? CONSOLE_ISC : IO_SCH_ISC;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X"
10328c2ecf20Sopenharmony_ci		      " - PIM = %02X, PAM = %02X, POM = %02X\n",
10338c2ecf20Sopenharmony_ci		      sch->schib.pmcw.dev, sch->schid.ssid,
10348c2ecf20Sopenharmony_ci		      sch->schid.sch_no, sch->schib.pmcw.pim,
10358c2ecf20Sopenharmony_ci		      sch->schib.pmcw.pam, sch->schib.pmcw.pom);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	io_subchannel_init_config(sch);
10388c2ecf20Sopenharmony_ci}
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci/*
10418c2ecf20Sopenharmony_ci * Note: We always return 0 so that we bind to the device even on error.
10428c2ecf20Sopenharmony_ci * This is needed so that our remove function is called on unregister.
10438c2ecf20Sopenharmony_ci */
10448c2ecf20Sopenharmony_cistatic int io_subchannel_probe(struct subchannel *sch)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	struct io_subchannel_private *io_priv;
10478c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
10488c2ecf20Sopenharmony_ci	int rc;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	if (cio_is_console(sch->schid)) {
10518c2ecf20Sopenharmony_ci		rc = sysfs_create_group(&sch->dev.kobj,
10528c2ecf20Sopenharmony_ci					&io_subchannel_attr_group);
10538c2ecf20Sopenharmony_ci		if (rc)
10548c2ecf20Sopenharmony_ci			CIO_MSG_EVENT(0, "Failed to create io subchannel "
10558c2ecf20Sopenharmony_ci				      "attributes for subchannel "
10568c2ecf20Sopenharmony_ci				      "0.%x.%04x (rc=%d)\n",
10578c2ecf20Sopenharmony_ci				      sch->schid.ssid, sch->schid.sch_no, rc);
10588c2ecf20Sopenharmony_ci		/*
10598c2ecf20Sopenharmony_ci		 * The console subchannel already has an associated ccw_device.
10608c2ecf20Sopenharmony_ci		 * Throw the delayed uevent for the subchannel, register
10618c2ecf20Sopenharmony_ci		 * the ccw_device and exit.
10628c2ecf20Sopenharmony_ci		 */
10638c2ecf20Sopenharmony_ci		if (dev_get_uevent_suppress(&sch->dev)) {
10648c2ecf20Sopenharmony_ci			/* should always be the case for the console */
10658c2ecf20Sopenharmony_ci			dev_set_uevent_suppress(&sch->dev, 0);
10668c2ecf20Sopenharmony_ci			kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
10678c2ecf20Sopenharmony_ci		}
10688c2ecf20Sopenharmony_ci		cdev = sch_get_cdev(sch);
10698c2ecf20Sopenharmony_ci		rc = ccw_device_add(cdev);
10708c2ecf20Sopenharmony_ci		if (rc) {
10718c2ecf20Sopenharmony_ci			/* Release online reference. */
10728c2ecf20Sopenharmony_ci			put_device(&cdev->dev);
10738c2ecf20Sopenharmony_ci			goto out_schedule;
10748c2ecf20Sopenharmony_ci		}
10758c2ecf20Sopenharmony_ci		if (atomic_dec_and_test(&ccw_device_init_count))
10768c2ecf20Sopenharmony_ci			wake_up(&ccw_device_init_wq);
10778c2ecf20Sopenharmony_ci		return 0;
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci	io_subchannel_init_fields(sch);
10808c2ecf20Sopenharmony_ci	rc = cio_commit_config(sch);
10818c2ecf20Sopenharmony_ci	if (rc)
10828c2ecf20Sopenharmony_ci		goto out_schedule;
10838c2ecf20Sopenharmony_ci	rc = sysfs_create_group(&sch->dev.kobj,
10848c2ecf20Sopenharmony_ci				&io_subchannel_attr_group);
10858c2ecf20Sopenharmony_ci	if (rc)
10868c2ecf20Sopenharmony_ci		goto out_schedule;
10878c2ecf20Sopenharmony_ci	/* Allocate I/O subchannel private data. */
10888c2ecf20Sopenharmony_ci	io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA);
10898c2ecf20Sopenharmony_ci	if (!io_priv)
10908c2ecf20Sopenharmony_ci		goto out_schedule;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	io_priv->dma_area = dma_alloc_coherent(&sch->dev,
10938c2ecf20Sopenharmony_ci				sizeof(*io_priv->dma_area),
10948c2ecf20Sopenharmony_ci				&io_priv->dma_area_dma, GFP_KERNEL);
10958c2ecf20Sopenharmony_ci	if (!io_priv->dma_area) {
10968c2ecf20Sopenharmony_ci		kfree(io_priv);
10978c2ecf20Sopenharmony_ci		goto out_schedule;
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	set_io_private(sch, io_priv);
11018c2ecf20Sopenharmony_ci	css_schedule_eval(sch->schid);
11028c2ecf20Sopenharmony_ci	return 0;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ciout_schedule:
11058c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
11068c2ecf20Sopenharmony_ci	css_sched_sch_todo(sch, SCH_TODO_UNREG);
11078c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
11088c2ecf20Sopenharmony_ci	return 0;
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_cistatic int io_subchannel_remove(struct subchannel *sch)
11128c2ecf20Sopenharmony_ci{
11138c2ecf20Sopenharmony_ci	struct io_subchannel_private *io_priv = to_io_private(sch);
11148c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	cdev = sch_get_cdev(sch);
11178c2ecf20Sopenharmony_ci	if (!cdev)
11188c2ecf20Sopenharmony_ci		goto out_free;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	ccw_device_unregister(cdev);
11218c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
11228c2ecf20Sopenharmony_ci	sch_set_cdev(sch, NULL);
11238c2ecf20Sopenharmony_ci	set_io_private(sch, NULL);
11248c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
11258c2ecf20Sopenharmony_ciout_free:
11268c2ecf20Sopenharmony_ci	dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
11278c2ecf20Sopenharmony_ci			  io_priv->dma_area, io_priv->dma_area_dma);
11288c2ecf20Sopenharmony_ci	kfree(io_priv);
11298c2ecf20Sopenharmony_ci	sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
11308c2ecf20Sopenharmony_ci	return 0;
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic void io_subchannel_verify(struct subchannel *sch)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	cdev = sch_get_cdev(sch);
11388c2ecf20Sopenharmony_ci	if (cdev)
11398c2ecf20Sopenharmony_ci		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
11408c2ecf20Sopenharmony_ci}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_cistatic void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	cdev = sch_get_cdev(sch);
11478c2ecf20Sopenharmony_ci	if (!cdev)
11488c2ecf20Sopenharmony_ci		return;
11498c2ecf20Sopenharmony_ci	if (cio_update_schib(sch))
11508c2ecf20Sopenharmony_ci		goto err;
11518c2ecf20Sopenharmony_ci	/* Check for I/O on path. */
11528c2ecf20Sopenharmony_ci	if (scsw_actl(&sch->schib.scsw) == 0 || sch->schib.pmcw.lpum != mask)
11538c2ecf20Sopenharmony_ci		goto out;
11548c2ecf20Sopenharmony_ci	if (cdev->private->state == DEV_STATE_ONLINE) {
11558c2ecf20Sopenharmony_ci		ccw_device_kill_io(cdev);
11568c2ecf20Sopenharmony_ci		goto out;
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci	if (cio_clear(sch))
11598c2ecf20Sopenharmony_ci		goto err;
11608c2ecf20Sopenharmony_ciout:
11618c2ecf20Sopenharmony_ci	/* Trigger path verification. */
11628c2ecf20Sopenharmony_ci	dev_fsm_event(cdev, DEV_EVENT_VERIFY);
11638c2ecf20Sopenharmony_ci	return;
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_cierr:
11668c2ecf20Sopenharmony_ci	dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
11678c2ecf20Sopenharmony_ci}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_cistatic int io_subchannel_chp_event(struct subchannel *sch,
11708c2ecf20Sopenharmony_ci				   struct chp_link *link, int event)
11718c2ecf20Sopenharmony_ci{
11728c2ecf20Sopenharmony_ci	struct ccw_device *cdev = sch_get_cdev(sch);
11738c2ecf20Sopenharmony_ci	int mask;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	mask = chp_ssd_get_mask(&sch->ssd_info, link);
11768c2ecf20Sopenharmony_ci	if (!mask)
11778c2ecf20Sopenharmony_ci		return 0;
11788c2ecf20Sopenharmony_ci	switch (event) {
11798c2ecf20Sopenharmony_ci	case CHP_VARY_OFF:
11808c2ecf20Sopenharmony_ci		sch->opm &= ~mask;
11818c2ecf20Sopenharmony_ci		sch->lpm &= ~mask;
11828c2ecf20Sopenharmony_ci		if (cdev)
11838c2ecf20Sopenharmony_ci			cdev->private->path_gone_mask |= mask;
11848c2ecf20Sopenharmony_ci		io_subchannel_terminate_path(sch, mask);
11858c2ecf20Sopenharmony_ci		break;
11868c2ecf20Sopenharmony_ci	case CHP_VARY_ON:
11878c2ecf20Sopenharmony_ci		sch->opm |= mask;
11888c2ecf20Sopenharmony_ci		sch->lpm |= mask;
11898c2ecf20Sopenharmony_ci		if (cdev)
11908c2ecf20Sopenharmony_ci			cdev->private->path_new_mask |= mask;
11918c2ecf20Sopenharmony_ci		io_subchannel_verify(sch);
11928c2ecf20Sopenharmony_ci		break;
11938c2ecf20Sopenharmony_ci	case CHP_OFFLINE:
11948c2ecf20Sopenharmony_ci		if (cio_update_schib(sch))
11958c2ecf20Sopenharmony_ci			return -ENODEV;
11968c2ecf20Sopenharmony_ci		if (cdev)
11978c2ecf20Sopenharmony_ci			cdev->private->path_gone_mask |= mask;
11988c2ecf20Sopenharmony_ci		io_subchannel_terminate_path(sch, mask);
11998c2ecf20Sopenharmony_ci		break;
12008c2ecf20Sopenharmony_ci	case CHP_ONLINE:
12018c2ecf20Sopenharmony_ci		if (cio_update_schib(sch))
12028c2ecf20Sopenharmony_ci			return -ENODEV;
12038c2ecf20Sopenharmony_ci		sch->lpm |= mask & sch->opm;
12048c2ecf20Sopenharmony_ci		if (cdev)
12058c2ecf20Sopenharmony_ci			cdev->private->path_new_mask |= mask;
12068c2ecf20Sopenharmony_ci		io_subchannel_verify(sch);
12078c2ecf20Sopenharmony_ci		break;
12088c2ecf20Sopenharmony_ci	}
12098c2ecf20Sopenharmony_ci	return 0;
12108c2ecf20Sopenharmony_ci}
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_cistatic void io_subchannel_quiesce(struct subchannel *sch)
12138c2ecf20Sopenharmony_ci{
12148c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
12158c2ecf20Sopenharmony_ci	int ret;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
12188c2ecf20Sopenharmony_ci	cdev = sch_get_cdev(sch);
12198c2ecf20Sopenharmony_ci	if (cio_is_console(sch->schid))
12208c2ecf20Sopenharmony_ci		goto out_unlock;
12218c2ecf20Sopenharmony_ci	if (!sch->schib.pmcw.ena)
12228c2ecf20Sopenharmony_ci		goto out_unlock;
12238c2ecf20Sopenharmony_ci	ret = cio_disable_subchannel(sch);
12248c2ecf20Sopenharmony_ci	if (ret != -EBUSY)
12258c2ecf20Sopenharmony_ci		goto out_unlock;
12268c2ecf20Sopenharmony_ci	if (cdev->handler)
12278c2ecf20Sopenharmony_ci		cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
12288c2ecf20Sopenharmony_ci	while (ret == -EBUSY) {
12298c2ecf20Sopenharmony_ci		cdev->private->state = DEV_STATE_QUIESCE;
12308c2ecf20Sopenharmony_ci		cdev->private->iretry = 255;
12318c2ecf20Sopenharmony_ci		ret = ccw_device_cancel_halt_clear(cdev);
12328c2ecf20Sopenharmony_ci		if (ret == -EBUSY) {
12338c2ecf20Sopenharmony_ci			ccw_device_set_timeout(cdev, HZ/10);
12348c2ecf20Sopenharmony_ci			spin_unlock_irq(sch->lock);
12358c2ecf20Sopenharmony_ci			wait_event(cdev->private->wait_q,
12368c2ecf20Sopenharmony_ci				   cdev->private->state != DEV_STATE_QUIESCE);
12378c2ecf20Sopenharmony_ci			spin_lock_irq(sch->lock);
12388c2ecf20Sopenharmony_ci		}
12398c2ecf20Sopenharmony_ci		ret = cio_disable_subchannel(sch);
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ciout_unlock:
12428c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic void io_subchannel_shutdown(struct subchannel *sch)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	io_subchannel_quiesce(sch);
12488c2ecf20Sopenharmony_ci}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic int device_is_disconnected(struct ccw_device *cdev)
12518c2ecf20Sopenharmony_ci{
12528c2ecf20Sopenharmony_ci	if (!cdev)
12538c2ecf20Sopenharmony_ci		return 0;
12548c2ecf20Sopenharmony_ci	return (cdev->private->state == DEV_STATE_DISCONNECTED ||
12558c2ecf20Sopenharmony_ci		cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_cistatic int recovery_check(struct device *dev, void *data)
12598c2ecf20Sopenharmony_ci{
12608c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
12618c2ecf20Sopenharmony_ci	struct subchannel *sch;
12628c2ecf20Sopenharmony_ci	int *redo = data;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
12658c2ecf20Sopenharmony_ci	switch (cdev->private->state) {
12668c2ecf20Sopenharmony_ci	case DEV_STATE_ONLINE:
12678c2ecf20Sopenharmony_ci		sch = to_subchannel(cdev->dev.parent);
12688c2ecf20Sopenharmony_ci		if ((sch->schib.pmcw.pam & sch->opm) == sch->vpm)
12698c2ecf20Sopenharmony_ci			break;
12708c2ecf20Sopenharmony_ci		fallthrough;
12718c2ecf20Sopenharmony_ci	case DEV_STATE_DISCONNECTED:
12728c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n",
12738c2ecf20Sopenharmony_ci			      cdev->private->dev_id.ssid,
12748c2ecf20Sopenharmony_ci			      cdev->private->dev_id.devno);
12758c2ecf20Sopenharmony_ci		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
12768c2ecf20Sopenharmony_ci		*redo = 1;
12778c2ecf20Sopenharmony_ci		break;
12788c2ecf20Sopenharmony_ci	case DEV_STATE_DISCONNECTED_SENSE_ID:
12798c2ecf20Sopenharmony_ci		*redo = 1;
12808c2ecf20Sopenharmony_ci		break;
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	return 0;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic void recovery_work_func(struct work_struct *unused)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	int redo = 0;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
12928c2ecf20Sopenharmony_ci	if (redo) {
12938c2ecf20Sopenharmony_ci		spin_lock_irq(&recovery_lock);
12948c2ecf20Sopenharmony_ci		if (!timer_pending(&recovery_timer)) {
12958c2ecf20Sopenharmony_ci			if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
12968c2ecf20Sopenharmony_ci				recovery_phase++;
12978c2ecf20Sopenharmony_ci			mod_timer(&recovery_timer, jiffies +
12988c2ecf20Sopenharmony_ci				  recovery_delay[recovery_phase] * HZ);
12998c2ecf20Sopenharmony_ci		}
13008c2ecf20Sopenharmony_ci		spin_unlock_irq(&recovery_lock);
13018c2ecf20Sopenharmony_ci	} else
13028c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(3, "recovery: end\n");
13038c2ecf20Sopenharmony_ci}
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_cistatic DECLARE_WORK(recovery_work, recovery_work_func);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_cistatic void recovery_func(struct timer_list *unused)
13088c2ecf20Sopenharmony_ci{
13098c2ecf20Sopenharmony_ci	/*
13108c2ecf20Sopenharmony_ci	 * We can't do our recovery in softirq context and it's not
13118c2ecf20Sopenharmony_ci	 * performance critical, so we schedule it.
13128c2ecf20Sopenharmony_ci	 */
13138c2ecf20Sopenharmony_ci	schedule_work(&recovery_work);
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_civoid ccw_device_schedule_recovery(void)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	unsigned long flags;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(3, "recovery: schedule\n");
13218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&recovery_lock, flags);
13228c2ecf20Sopenharmony_ci	if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
13238c2ecf20Sopenharmony_ci		recovery_phase = 0;
13248c2ecf20Sopenharmony_ci		mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
13258c2ecf20Sopenharmony_ci	}
13268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&recovery_lock, flags);
13278c2ecf20Sopenharmony_ci}
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_cistatic int purge_fn(struct device *dev, void *data)
13308c2ecf20Sopenharmony_ci{
13318c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
13328c2ecf20Sopenharmony_ci	struct ccw_dev_id *id = &cdev->private->dev_id;
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
13358c2ecf20Sopenharmony_ci	if (is_blacklisted(id->ssid, id->devno) &&
13368c2ecf20Sopenharmony_ci	    (cdev->private->state == DEV_STATE_OFFLINE) &&
13378c2ecf20Sopenharmony_ci	    (atomic_cmpxchg(&cdev->private->onoff, 0, 1) == 0)) {
13388c2ecf20Sopenharmony_ci		CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid,
13398c2ecf20Sopenharmony_ci			      id->devno);
13408c2ecf20Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
13418c2ecf20Sopenharmony_ci		atomic_set(&cdev->private->onoff, 0);
13428c2ecf20Sopenharmony_ci	}
13438c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
13448c2ecf20Sopenharmony_ci	/* Abort loop in case of pending signal. */
13458c2ecf20Sopenharmony_ci	if (signal_pending(current))
13468c2ecf20Sopenharmony_ci		return -EINTR;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	return 0;
13498c2ecf20Sopenharmony_ci}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci/**
13528c2ecf20Sopenharmony_ci * ccw_purge_blacklisted - purge unused, blacklisted devices
13538c2ecf20Sopenharmony_ci *
13548c2ecf20Sopenharmony_ci * Unregister all ccw devices that are offline and on the blacklist.
13558c2ecf20Sopenharmony_ci */
13568c2ecf20Sopenharmony_ciint ccw_purge_blacklisted(void)
13578c2ecf20Sopenharmony_ci{
13588c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(2, "ccw: purging blacklisted devices\n");
13598c2ecf20Sopenharmony_ci	bus_for_each_dev(&ccw_bus_type, NULL, NULL, purge_fn);
13608c2ecf20Sopenharmony_ci	return 0;
13618c2ecf20Sopenharmony_ci}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_civoid ccw_device_set_disconnected(struct ccw_device *cdev)
13648c2ecf20Sopenharmony_ci{
13658c2ecf20Sopenharmony_ci	if (!cdev)
13668c2ecf20Sopenharmony_ci		return;
13678c2ecf20Sopenharmony_ci	ccw_device_set_timeout(cdev, 0);
13688c2ecf20Sopenharmony_ci	cdev->private->flags.fake_irb = 0;
13698c2ecf20Sopenharmony_ci	cdev->private->state = DEV_STATE_DISCONNECTED;
13708c2ecf20Sopenharmony_ci	if (cdev->online)
13718c2ecf20Sopenharmony_ci		ccw_device_schedule_recovery();
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_civoid ccw_device_set_notoper(struct ccw_device *cdev)
13758c2ecf20Sopenharmony_ci{
13768c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(2, "notoper");
13798c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(2, dev_name(&sch->dev));
13808c2ecf20Sopenharmony_ci	ccw_device_set_timeout(cdev, 0);
13818c2ecf20Sopenharmony_ci	cio_disable_subchannel(sch);
13828c2ecf20Sopenharmony_ci	cdev->private->state = DEV_STATE_NOT_OPER;
13838c2ecf20Sopenharmony_ci}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_cienum io_sch_action {
13868c2ecf20Sopenharmony_ci	IO_SCH_UNREG,
13878c2ecf20Sopenharmony_ci	IO_SCH_ORPH_UNREG,
13888c2ecf20Sopenharmony_ci	IO_SCH_UNREG_CDEV,
13898c2ecf20Sopenharmony_ci	IO_SCH_ATTACH,
13908c2ecf20Sopenharmony_ci	IO_SCH_UNREG_ATTACH,
13918c2ecf20Sopenharmony_ci	IO_SCH_ORPH_ATTACH,
13928c2ecf20Sopenharmony_ci	IO_SCH_REPROBE,
13938c2ecf20Sopenharmony_ci	IO_SCH_VERIFY,
13948c2ecf20Sopenharmony_ci	IO_SCH_DISC,
13958c2ecf20Sopenharmony_ci	IO_SCH_NOP,
13968c2ecf20Sopenharmony_ci};
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_cistatic enum io_sch_action sch_get_action(struct subchannel *sch)
13998c2ecf20Sopenharmony_ci{
14008c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	cdev = sch_get_cdev(sch);
14038c2ecf20Sopenharmony_ci	if (cio_update_schib(sch)) {
14048c2ecf20Sopenharmony_ci		/* Not operational. */
14058c2ecf20Sopenharmony_ci		if (!cdev)
14068c2ecf20Sopenharmony_ci			return IO_SCH_UNREG;
14078c2ecf20Sopenharmony_ci		if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
14088c2ecf20Sopenharmony_ci			return IO_SCH_UNREG;
14098c2ecf20Sopenharmony_ci		return IO_SCH_ORPH_UNREG;
14108c2ecf20Sopenharmony_ci	}
14118c2ecf20Sopenharmony_ci	/* Operational. */
14128c2ecf20Sopenharmony_ci	if (!cdev)
14138c2ecf20Sopenharmony_ci		return IO_SCH_ATTACH;
14148c2ecf20Sopenharmony_ci	if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
14158c2ecf20Sopenharmony_ci		if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
14168c2ecf20Sopenharmony_ci			return IO_SCH_UNREG_ATTACH;
14178c2ecf20Sopenharmony_ci		return IO_SCH_ORPH_ATTACH;
14188c2ecf20Sopenharmony_ci	}
14198c2ecf20Sopenharmony_ci	if ((sch->schib.pmcw.pam & sch->opm) == 0) {
14208c2ecf20Sopenharmony_ci		if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK)
14218c2ecf20Sopenharmony_ci			return IO_SCH_UNREG_CDEV;
14228c2ecf20Sopenharmony_ci		return IO_SCH_DISC;
14238c2ecf20Sopenharmony_ci	}
14248c2ecf20Sopenharmony_ci	if (device_is_disconnected(cdev))
14258c2ecf20Sopenharmony_ci		return IO_SCH_REPROBE;
14268c2ecf20Sopenharmony_ci	if (cdev->online && !cdev->private->flags.resuming)
14278c2ecf20Sopenharmony_ci		return IO_SCH_VERIFY;
14288c2ecf20Sopenharmony_ci	if (cdev->private->state == DEV_STATE_NOT_OPER)
14298c2ecf20Sopenharmony_ci		return IO_SCH_UNREG_ATTACH;
14308c2ecf20Sopenharmony_ci	return IO_SCH_NOP;
14318c2ecf20Sopenharmony_ci}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci/**
14348c2ecf20Sopenharmony_ci * io_subchannel_sch_event - process subchannel event
14358c2ecf20Sopenharmony_ci * @sch: subchannel
14368c2ecf20Sopenharmony_ci * @process: non-zero if function is called in process context
14378c2ecf20Sopenharmony_ci *
14388c2ecf20Sopenharmony_ci * An unspecified event occurred for this subchannel. Adjust data according
14398c2ecf20Sopenharmony_ci * to the current operational state of the subchannel and device. Return
14408c2ecf20Sopenharmony_ci * zero when the event has been handled sufficiently or -EAGAIN when this
14418c2ecf20Sopenharmony_ci * function should be called again in process context.
14428c2ecf20Sopenharmony_ci */
14438c2ecf20Sopenharmony_cistatic int io_subchannel_sch_event(struct subchannel *sch, int process)
14448c2ecf20Sopenharmony_ci{
14458c2ecf20Sopenharmony_ci	unsigned long flags;
14468c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
14478c2ecf20Sopenharmony_ci	struct ccw_dev_id dev_id;
14488c2ecf20Sopenharmony_ci	enum io_sch_action action;
14498c2ecf20Sopenharmony_ci	int rc = -EAGAIN;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	spin_lock_irqsave(sch->lock, flags);
14528c2ecf20Sopenharmony_ci	if (!device_is_registered(&sch->dev))
14538c2ecf20Sopenharmony_ci		goto out_unlock;
14548c2ecf20Sopenharmony_ci	if (work_pending(&sch->todo_work))
14558c2ecf20Sopenharmony_ci		goto out_unlock;
14568c2ecf20Sopenharmony_ci	cdev = sch_get_cdev(sch);
14578c2ecf20Sopenharmony_ci	if (cdev && work_pending(&cdev->private->todo_work))
14588c2ecf20Sopenharmony_ci		goto out_unlock;
14598c2ecf20Sopenharmony_ci	action = sch_get_action(sch);
14608c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n",
14618c2ecf20Sopenharmony_ci		      sch->schid.ssid, sch->schid.sch_no, process,
14628c2ecf20Sopenharmony_ci		      action);
14638c2ecf20Sopenharmony_ci	/* Perform immediate actions while holding the lock. */
14648c2ecf20Sopenharmony_ci	switch (action) {
14658c2ecf20Sopenharmony_ci	case IO_SCH_REPROBE:
14668c2ecf20Sopenharmony_ci		/* Trigger device recognition. */
14678c2ecf20Sopenharmony_ci		ccw_device_trigger_reprobe(cdev);
14688c2ecf20Sopenharmony_ci		rc = 0;
14698c2ecf20Sopenharmony_ci		goto out_unlock;
14708c2ecf20Sopenharmony_ci	case IO_SCH_VERIFY:
14718c2ecf20Sopenharmony_ci		/* Trigger path verification. */
14728c2ecf20Sopenharmony_ci		io_subchannel_verify(sch);
14738c2ecf20Sopenharmony_ci		rc = 0;
14748c2ecf20Sopenharmony_ci		goto out_unlock;
14758c2ecf20Sopenharmony_ci	case IO_SCH_DISC:
14768c2ecf20Sopenharmony_ci		ccw_device_set_disconnected(cdev);
14778c2ecf20Sopenharmony_ci		rc = 0;
14788c2ecf20Sopenharmony_ci		goto out_unlock;
14798c2ecf20Sopenharmony_ci	case IO_SCH_ORPH_UNREG:
14808c2ecf20Sopenharmony_ci	case IO_SCH_ORPH_ATTACH:
14818c2ecf20Sopenharmony_ci		ccw_device_set_disconnected(cdev);
14828c2ecf20Sopenharmony_ci		break;
14838c2ecf20Sopenharmony_ci	case IO_SCH_UNREG_CDEV:
14848c2ecf20Sopenharmony_ci	case IO_SCH_UNREG_ATTACH:
14858c2ecf20Sopenharmony_ci	case IO_SCH_UNREG:
14868c2ecf20Sopenharmony_ci		if (!cdev)
14878c2ecf20Sopenharmony_ci			break;
14888c2ecf20Sopenharmony_ci		if (cdev->private->state == DEV_STATE_SENSE_ID) {
14898c2ecf20Sopenharmony_ci			/*
14908c2ecf20Sopenharmony_ci			 * Note: delayed work triggered by this event
14918c2ecf20Sopenharmony_ci			 * and repeated calls to sch_event are synchronized
14928c2ecf20Sopenharmony_ci			 * by the above check for work_pending(cdev).
14938c2ecf20Sopenharmony_ci			 */
14948c2ecf20Sopenharmony_ci			dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
14958c2ecf20Sopenharmony_ci		} else
14968c2ecf20Sopenharmony_ci			ccw_device_set_notoper(cdev);
14978c2ecf20Sopenharmony_ci		break;
14988c2ecf20Sopenharmony_ci	case IO_SCH_NOP:
14998c2ecf20Sopenharmony_ci		rc = 0;
15008c2ecf20Sopenharmony_ci		goto out_unlock;
15018c2ecf20Sopenharmony_ci	default:
15028c2ecf20Sopenharmony_ci		break;
15038c2ecf20Sopenharmony_ci	}
15048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(sch->lock, flags);
15058c2ecf20Sopenharmony_ci	/* All other actions require process context. */
15068c2ecf20Sopenharmony_ci	if (!process)
15078c2ecf20Sopenharmony_ci		goto out;
15088c2ecf20Sopenharmony_ci	/* Handle attached ccw device. */
15098c2ecf20Sopenharmony_ci	switch (action) {
15108c2ecf20Sopenharmony_ci	case IO_SCH_ORPH_UNREG:
15118c2ecf20Sopenharmony_ci	case IO_SCH_ORPH_ATTACH:
15128c2ecf20Sopenharmony_ci		/* Move ccw device to orphanage. */
15138c2ecf20Sopenharmony_ci		rc = ccw_device_move_to_orph(cdev);
15148c2ecf20Sopenharmony_ci		if (rc)
15158c2ecf20Sopenharmony_ci			goto out;
15168c2ecf20Sopenharmony_ci		break;
15178c2ecf20Sopenharmony_ci	case IO_SCH_UNREG_CDEV:
15188c2ecf20Sopenharmony_ci	case IO_SCH_UNREG_ATTACH:
15198c2ecf20Sopenharmony_ci		spin_lock_irqsave(sch->lock, flags);
15208c2ecf20Sopenharmony_ci		if (cdev->private->flags.resuming) {
15218c2ecf20Sopenharmony_ci			/* Device will be handled later. */
15228c2ecf20Sopenharmony_ci			rc = 0;
15238c2ecf20Sopenharmony_ci			goto out_unlock;
15248c2ecf20Sopenharmony_ci		}
15258c2ecf20Sopenharmony_ci		sch_set_cdev(sch, NULL);
15268c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(sch->lock, flags);
15278c2ecf20Sopenharmony_ci		/* Unregister ccw device. */
15288c2ecf20Sopenharmony_ci		ccw_device_unregister(cdev);
15298c2ecf20Sopenharmony_ci		break;
15308c2ecf20Sopenharmony_ci	default:
15318c2ecf20Sopenharmony_ci		break;
15328c2ecf20Sopenharmony_ci	}
15338c2ecf20Sopenharmony_ci	/* Handle subchannel. */
15348c2ecf20Sopenharmony_ci	switch (action) {
15358c2ecf20Sopenharmony_ci	case IO_SCH_ORPH_UNREG:
15368c2ecf20Sopenharmony_ci	case IO_SCH_UNREG:
15378c2ecf20Sopenharmony_ci		if (!cdev || !cdev->private->flags.resuming)
15388c2ecf20Sopenharmony_ci			css_sch_device_unregister(sch);
15398c2ecf20Sopenharmony_ci		break;
15408c2ecf20Sopenharmony_ci	case IO_SCH_ORPH_ATTACH:
15418c2ecf20Sopenharmony_ci	case IO_SCH_UNREG_ATTACH:
15428c2ecf20Sopenharmony_ci	case IO_SCH_ATTACH:
15438c2ecf20Sopenharmony_ci		dev_id.ssid = sch->schid.ssid;
15448c2ecf20Sopenharmony_ci		dev_id.devno = sch->schib.pmcw.dev;
15458c2ecf20Sopenharmony_ci		cdev = get_ccwdev_by_dev_id(&dev_id);
15468c2ecf20Sopenharmony_ci		if (!cdev) {
15478c2ecf20Sopenharmony_ci			sch_create_and_recog_new_device(sch);
15488c2ecf20Sopenharmony_ci			break;
15498c2ecf20Sopenharmony_ci		}
15508c2ecf20Sopenharmony_ci		rc = ccw_device_move_to_sch(cdev, sch);
15518c2ecf20Sopenharmony_ci		if (rc) {
15528c2ecf20Sopenharmony_ci			/* Release reference from get_ccwdev_by_dev_id() */
15538c2ecf20Sopenharmony_ci			put_device(&cdev->dev);
15548c2ecf20Sopenharmony_ci			goto out;
15558c2ecf20Sopenharmony_ci		}
15568c2ecf20Sopenharmony_ci		spin_lock_irqsave(sch->lock, flags);
15578c2ecf20Sopenharmony_ci		ccw_device_trigger_reprobe(cdev);
15588c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(sch->lock, flags);
15598c2ecf20Sopenharmony_ci		/* Release reference from get_ccwdev_by_dev_id() */
15608c2ecf20Sopenharmony_ci		put_device(&cdev->dev);
15618c2ecf20Sopenharmony_ci		break;
15628c2ecf20Sopenharmony_ci	default:
15638c2ecf20Sopenharmony_ci		break;
15648c2ecf20Sopenharmony_ci	}
15658c2ecf20Sopenharmony_ci	return 0;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ciout_unlock:
15688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(sch->lock, flags);
15698c2ecf20Sopenharmony_ciout:
15708c2ecf20Sopenharmony_ci	return rc;
15718c2ecf20Sopenharmony_ci}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_cistatic void ccw_device_set_int_class(struct ccw_device *cdev)
15748c2ecf20Sopenharmony_ci{
15758c2ecf20Sopenharmony_ci	struct ccw_driver *cdrv = cdev->drv;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	/* Note: we interpret class 0 in this context as an uninitialized
15788c2ecf20Sopenharmony_ci	 * field since it translates to a non-I/O interrupt class. */
15798c2ecf20Sopenharmony_ci	if (cdrv->int_class != 0)
15808c2ecf20Sopenharmony_ci		cdev->private->int_class = cdrv->int_class;
15818c2ecf20Sopenharmony_ci	else
15828c2ecf20Sopenharmony_ci		cdev->private->int_class = IRQIO_CIO;
15838c2ecf20Sopenharmony_ci}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci#ifdef CONFIG_CCW_CONSOLE
15868c2ecf20Sopenharmony_ciint __init ccw_device_enable_console(struct ccw_device *cdev)
15878c2ecf20Sopenharmony_ci{
15888c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
15898c2ecf20Sopenharmony_ci	int rc;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	if (!cdev->drv || !cdev->handler)
15928c2ecf20Sopenharmony_ci		return -EINVAL;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	io_subchannel_init_fields(sch);
15958c2ecf20Sopenharmony_ci	rc = cio_commit_config(sch);
15968c2ecf20Sopenharmony_ci	if (rc)
15978c2ecf20Sopenharmony_ci		return rc;
15988c2ecf20Sopenharmony_ci	sch->driver = &io_subchannel_driver;
15998c2ecf20Sopenharmony_ci	io_subchannel_recog(cdev, sch);
16008c2ecf20Sopenharmony_ci	/* Now wait for the async. recognition to come to an end. */
16018c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
16028c2ecf20Sopenharmony_ci	while (!dev_fsm_final_state(cdev))
16038c2ecf20Sopenharmony_ci		ccw_device_wait_idle(cdev);
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	/* Hold on to an extra reference while device is online. */
16068c2ecf20Sopenharmony_ci	get_device(&cdev->dev);
16078c2ecf20Sopenharmony_ci	rc = ccw_device_online(cdev);
16088c2ecf20Sopenharmony_ci	if (rc)
16098c2ecf20Sopenharmony_ci		goto out_unlock;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	while (!dev_fsm_final_state(cdev))
16128c2ecf20Sopenharmony_ci		ccw_device_wait_idle(cdev);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	if (cdev->private->state == DEV_STATE_ONLINE)
16158c2ecf20Sopenharmony_ci		cdev->online = 1;
16168c2ecf20Sopenharmony_ci	else
16178c2ecf20Sopenharmony_ci		rc = -EIO;
16188c2ecf20Sopenharmony_ciout_unlock:
16198c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
16208c2ecf20Sopenharmony_ci	if (rc) /* Give up online reference since onlining failed. */
16218c2ecf20Sopenharmony_ci		put_device(&cdev->dev);
16228c2ecf20Sopenharmony_ci	return rc;
16238c2ecf20Sopenharmony_ci}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_cistruct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv)
16268c2ecf20Sopenharmony_ci{
16278c2ecf20Sopenharmony_ci	struct io_subchannel_private *io_priv;
16288c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
16298c2ecf20Sopenharmony_ci	struct subchannel *sch;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	sch = cio_probe_console();
16328c2ecf20Sopenharmony_ci	if (IS_ERR(sch))
16338c2ecf20Sopenharmony_ci		return ERR_CAST(sch);
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA);
16368c2ecf20Sopenharmony_ci	if (!io_priv)
16378c2ecf20Sopenharmony_ci		goto err_priv;
16388c2ecf20Sopenharmony_ci	io_priv->dma_area = dma_alloc_coherent(&sch->dev,
16398c2ecf20Sopenharmony_ci				sizeof(*io_priv->dma_area),
16408c2ecf20Sopenharmony_ci				&io_priv->dma_area_dma, GFP_KERNEL);
16418c2ecf20Sopenharmony_ci	if (!io_priv->dma_area)
16428c2ecf20Sopenharmony_ci		goto err_dma_area;
16438c2ecf20Sopenharmony_ci	set_io_private(sch, io_priv);
16448c2ecf20Sopenharmony_ci	cdev = io_subchannel_create_ccwdev(sch);
16458c2ecf20Sopenharmony_ci	if (IS_ERR(cdev)) {
16468c2ecf20Sopenharmony_ci		dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
16478c2ecf20Sopenharmony_ci				  io_priv->dma_area, io_priv->dma_area_dma);
16488c2ecf20Sopenharmony_ci		set_io_private(sch, NULL);
16498c2ecf20Sopenharmony_ci		put_device(&sch->dev);
16508c2ecf20Sopenharmony_ci		kfree(io_priv);
16518c2ecf20Sopenharmony_ci		return cdev;
16528c2ecf20Sopenharmony_ci	}
16538c2ecf20Sopenharmony_ci	cdev->drv = drv;
16548c2ecf20Sopenharmony_ci	ccw_device_set_int_class(cdev);
16558c2ecf20Sopenharmony_ci	return cdev;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_cierr_dma_area:
16588c2ecf20Sopenharmony_ci	kfree(io_priv);
16598c2ecf20Sopenharmony_cierr_priv:
16608c2ecf20Sopenharmony_ci	put_device(&sch->dev);
16618c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
16628c2ecf20Sopenharmony_ci}
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_civoid __init ccw_device_destroy_console(struct ccw_device *cdev)
16658c2ecf20Sopenharmony_ci{
16668c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
16678c2ecf20Sopenharmony_ci	struct io_subchannel_private *io_priv = to_io_private(sch);
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	set_io_private(sch, NULL);
16708c2ecf20Sopenharmony_ci	dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
16718c2ecf20Sopenharmony_ci			  io_priv->dma_area, io_priv->dma_area_dma);
16728c2ecf20Sopenharmony_ci	put_device(&sch->dev);
16738c2ecf20Sopenharmony_ci	put_device(&cdev->dev);
16748c2ecf20Sopenharmony_ci	kfree(io_priv);
16758c2ecf20Sopenharmony_ci}
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci/**
16788c2ecf20Sopenharmony_ci * ccw_device_wait_idle() - busy wait for device to become idle
16798c2ecf20Sopenharmony_ci * @cdev: ccw device
16808c2ecf20Sopenharmony_ci *
16818c2ecf20Sopenharmony_ci * Poll until activity control is zero, that is, no function or data
16828c2ecf20Sopenharmony_ci * transfer is pending/active.
16838c2ecf20Sopenharmony_ci * Called with device lock being held.
16848c2ecf20Sopenharmony_ci */
16858c2ecf20Sopenharmony_civoid ccw_device_wait_idle(struct ccw_device *cdev)
16868c2ecf20Sopenharmony_ci{
16878c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	while (1) {
16908c2ecf20Sopenharmony_ci		cio_tsch(sch);
16918c2ecf20Sopenharmony_ci		if (sch->schib.scsw.cmd.actl == 0)
16928c2ecf20Sopenharmony_ci			break;
16938c2ecf20Sopenharmony_ci		udelay_simple(100);
16948c2ecf20Sopenharmony_ci	}
16958c2ecf20Sopenharmony_ci}
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_cistatic int ccw_device_pm_restore(struct device *dev);
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ciint ccw_device_force_console(struct ccw_device *cdev)
17008c2ecf20Sopenharmony_ci{
17018c2ecf20Sopenharmony_ci	return ccw_device_pm_restore(&cdev->dev);
17028c2ecf20Sopenharmony_ci}
17038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_force_console);
17048c2ecf20Sopenharmony_ci#endif
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci/**
17078c2ecf20Sopenharmony_ci * get_ccwdev_by_busid() - obtain device from a bus id
17088c2ecf20Sopenharmony_ci * @cdrv: driver the device is owned by
17098c2ecf20Sopenharmony_ci * @bus_id: bus id of the device to be searched
17108c2ecf20Sopenharmony_ci *
17118c2ecf20Sopenharmony_ci * This function searches all devices owned by @cdrv for a device with a bus
17128c2ecf20Sopenharmony_ci * id matching @bus_id.
17138c2ecf20Sopenharmony_ci * Returns:
17148c2ecf20Sopenharmony_ci *  If a match is found, its reference count of the found device is increased
17158c2ecf20Sopenharmony_ci *  and it is returned; else %NULL is returned.
17168c2ecf20Sopenharmony_ci */
17178c2ecf20Sopenharmony_cistruct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv,
17188c2ecf20Sopenharmony_ci				       const char *bus_id)
17198c2ecf20Sopenharmony_ci{
17208c2ecf20Sopenharmony_ci	struct device *dev;
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	dev = driver_find_device_by_name(&cdrv->driver, bus_id);
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	return dev ? to_ccwdev(dev) : NULL;
17258c2ecf20Sopenharmony_ci}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci/************************** device driver handling ************************/
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci/* This is the implementation of the ccw_driver class. The probe, remove
17308c2ecf20Sopenharmony_ci * and release methods are initially very similar to the device_driver
17318c2ecf20Sopenharmony_ci * implementations, with the difference that they have ccw_device
17328c2ecf20Sopenharmony_ci * arguments.
17338c2ecf20Sopenharmony_ci *
17348c2ecf20Sopenharmony_ci * A ccw driver also contains the information that is needed for
17358c2ecf20Sopenharmony_ci * device matching.
17368c2ecf20Sopenharmony_ci */
17378c2ecf20Sopenharmony_cistatic int
17388c2ecf20Sopenharmony_ciccw_device_probe (struct device *dev)
17398c2ecf20Sopenharmony_ci{
17408c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
17418c2ecf20Sopenharmony_ci	struct ccw_driver *cdrv = to_ccwdrv(dev->driver);
17428c2ecf20Sopenharmony_ci	int ret;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	cdev->drv = cdrv; /* to let the driver call _set_online */
17458c2ecf20Sopenharmony_ci	ccw_device_set_int_class(cdev);
17468c2ecf20Sopenharmony_ci	ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV;
17478c2ecf20Sopenharmony_ci	if (ret) {
17488c2ecf20Sopenharmony_ci		cdev->drv = NULL;
17498c2ecf20Sopenharmony_ci		cdev->private->int_class = IRQIO_CIO;
17508c2ecf20Sopenharmony_ci		return ret;
17518c2ecf20Sopenharmony_ci	}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	return 0;
17548c2ecf20Sopenharmony_ci}
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_cistatic int ccw_device_remove(struct device *dev)
17578c2ecf20Sopenharmony_ci{
17588c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
17598c2ecf20Sopenharmony_ci	struct ccw_driver *cdrv = cdev->drv;
17608c2ecf20Sopenharmony_ci	struct subchannel *sch;
17618c2ecf20Sopenharmony_ci	int ret;
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	if (cdrv->remove)
17648c2ecf20Sopenharmony_ci		cdrv->remove(cdev);
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
17678c2ecf20Sopenharmony_ci	if (cdev->online) {
17688c2ecf20Sopenharmony_ci		cdev->online = 0;
17698c2ecf20Sopenharmony_ci		ret = ccw_device_offline(cdev);
17708c2ecf20Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
17718c2ecf20Sopenharmony_ci		if (ret == 0)
17728c2ecf20Sopenharmony_ci			wait_event(cdev->private->wait_q,
17738c2ecf20Sopenharmony_ci				   dev_fsm_final_state(cdev));
17748c2ecf20Sopenharmony_ci		else
17758c2ecf20Sopenharmony_ci			CIO_MSG_EVENT(0, "ccw_device_offline returned %d, "
17768c2ecf20Sopenharmony_ci				      "device 0.%x.%04x\n",
17778c2ecf20Sopenharmony_ci				      ret, cdev->private->dev_id.ssid,
17788c2ecf20Sopenharmony_ci				      cdev->private->dev_id.devno);
17798c2ecf20Sopenharmony_ci		/* Give up reference obtained in ccw_device_set_online(). */
17808c2ecf20Sopenharmony_ci		put_device(&cdev->dev);
17818c2ecf20Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
17828c2ecf20Sopenharmony_ci	}
17838c2ecf20Sopenharmony_ci	ccw_device_set_timeout(cdev, 0);
17848c2ecf20Sopenharmony_ci	cdev->drv = NULL;
17858c2ecf20Sopenharmony_ci	cdev->private->int_class = IRQIO_CIO;
17868c2ecf20Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
17878c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
17888c2ecf20Sopenharmony_ci	io_subchannel_quiesce(sch);
17898c2ecf20Sopenharmony_ci	__disable_cmf(cdev);
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	return 0;
17928c2ecf20Sopenharmony_ci}
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_cistatic void ccw_device_shutdown(struct device *dev)
17958c2ecf20Sopenharmony_ci{
17968c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	cdev = to_ccwdev(dev);
17998c2ecf20Sopenharmony_ci	if (cdev->drv && cdev->drv->shutdown)
18008c2ecf20Sopenharmony_ci		cdev->drv->shutdown(cdev);
18018c2ecf20Sopenharmony_ci	__disable_cmf(cdev);
18028c2ecf20Sopenharmony_ci}
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_cistatic int ccw_device_pm_prepare(struct device *dev)
18058c2ecf20Sopenharmony_ci{
18068c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	if (work_pending(&cdev->private->todo_work))
18098c2ecf20Sopenharmony_ci		return -EAGAIN;
18108c2ecf20Sopenharmony_ci	/* Fail while device is being set online/offline. */
18118c2ecf20Sopenharmony_ci	if (atomic_read(&cdev->private->onoff))
18128c2ecf20Sopenharmony_ci		return -EAGAIN;
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	if (cdev->online && cdev->drv && cdev->drv->prepare)
18158c2ecf20Sopenharmony_ci		return cdev->drv->prepare(cdev);
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	return 0;
18188c2ecf20Sopenharmony_ci}
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_cistatic void ccw_device_pm_complete(struct device *dev)
18218c2ecf20Sopenharmony_ci{
18228c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	if (cdev->online && cdev->drv && cdev->drv->complete)
18258c2ecf20Sopenharmony_ci		cdev->drv->complete(cdev);
18268c2ecf20Sopenharmony_ci}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_cistatic int ccw_device_pm_freeze(struct device *dev)
18298c2ecf20Sopenharmony_ci{
18308c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
18318c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
18328c2ecf20Sopenharmony_ci	int ret, cm_enabled;
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci	/* Fail suspend while device is in transistional state. */
18358c2ecf20Sopenharmony_ci	if (!dev_fsm_final_state(cdev))
18368c2ecf20Sopenharmony_ci		return -EAGAIN;
18378c2ecf20Sopenharmony_ci	if (!cdev->online)
18388c2ecf20Sopenharmony_ci		return 0;
18398c2ecf20Sopenharmony_ci	if (cdev->drv && cdev->drv->freeze) {
18408c2ecf20Sopenharmony_ci		ret = cdev->drv->freeze(cdev);
18418c2ecf20Sopenharmony_ci		if (ret)
18428c2ecf20Sopenharmony_ci			return ret;
18438c2ecf20Sopenharmony_ci	}
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
18468c2ecf20Sopenharmony_ci	cm_enabled = cdev->private->cmb != NULL;
18478c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
18488c2ecf20Sopenharmony_ci	if (cm_enabled) {
18498c2ecf20Sopenharmony_ci		/* Don't have the css write on memory. */
18508c2ecf20Sopenharmony_ci		ret = ccw_set_cmf(cdev, 0);
18518c2ecf20Sopenharmony_ci		if (ret)
18528c2ecf20Sopenharmony_ci			return ret;
18538c2ecf20Sopenharmony_ci	}
18548c2ecf20Sopenharmony_ci	/* From here on, disallow device driver I/O. */
18558c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
18568c2ecf20Sopenharmony_ci	ret = cio_disable_subchannel(sch);
18578c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci	return ret;
18608c2ecf20Sopenharmony_ci}
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_cistatic int ccw_device_pm_thaw(struct device *dev)
18638c2ecf20Sopenharmony_ci{
18648c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
18658c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
18668c2ecf20Sopenharmony_ci	int ret, cm_enabled;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	if (!cdev->online)
18698c2ecf20Sopenharmony_ci		return 0;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
18728c2ecf20Sopenharmony_ci	/* Allow device driver I/O again. */
18738c2ecf20Sopenharmony_ci	ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
18748c2ecf20Sopenharmony_ci	cm_enabled = cdev->private->cmb != NULL;
18758c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
18768c2ecf20Sopenharmony_ci	if (ret)
18778c2ecf20Sopenharmony_ci		return ret;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	if (cm_enabled) {
18808c2ecf20Sopenharmony_ci		ret = ccw_set_cmf(cdev, 1);
18818c2ecf20Sopenharmony_ci		if (ret)
18828c2ecf20Sopenharmony_ci			return ret;
18838c2ecf20Sopenharmony_ci	}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	if (cdev->drv && cdev->drv->thaw)
18868c2ecf20Sopenharmony_ci		ret = cdev->drv->thaw(cdev);
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci	return ret;
18898c2ecf20Sopenharmony_ci}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_cistatic void __ccw_device_pm_restore(struct ccw_device *cdev)
18928c2ecf20Sopenharmony_ci{
18938c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
18968c2ecf20Sopenharmony_ci	if (cio_is_console(sch->schid)) {
18978c2ecf20Sopenharmony_ci		cio_enable_subchannel(sch, (u32)(addr_t)sch);
18988c2ecf20Sopenharmony_ci		goto out_unlock;
18998c2ecf20Sopenharmony_ci	}
19008c2ecf20Sopenharmony_ci	/*
19018c2ecf20Sopenharmony_ci	 * While we were sleeping, devices may have gone or become
19028c2ecf20Sopenharmony_ci	 * available again. Kick re-detection.
19038c2ecf20Sopenharmony_ci	 */
19048c2ecf20Sopenharmony_ci	cdev->private->flags.resuming = 1;
19058c2ecf20Sopenharmony_ci	cdev->private->path_new_mask = LPM_ANYPATH;
19068c2ecf20Sopenharmony_ci	css_sched_sch_todo(sch, SCH_TODO_EVAL);
19078c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
19088c2ecf20Sopenharmony_ci	css_wait_for_slow_path();
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	/* cdev may have been moved to a different subchannel. */
19118c2ecf20Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
19128c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
19138c2ecf20Sopenharmony_ci	if (cdev->private->state != DEV_STATE_ONLINE &&
19148c2ecf20Sopenharmony_ci	    cdev->private->state != DEV_STATE_OFFLINE)
19158c2ecf20Sopenharmony_ci		goto out_unlock;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	ccw_device_recognition(cdev);
19188c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
19198c2ecf20Sopenharmony_ci	wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) ||
19208c2ecf20Sopenharmony_ci		   cdev->private->state == DEV_STATE_DISCONNECTED);
19218c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ciout_unlock:
19248c2ecf20Sopenharmony_ci	cdev->private->flags.resuming = 0;
19258c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
19268c2ecf20Sopenharmony_ci}
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_cistatic int resume_handle_boxed(struct ccw_device *cdev)
19298c2ecf20Sopenharmony_ci{
19308c2ecf20Sopenharmony_ci	cdev->private->state = DEV_STATE_BOXED;
19318c2ecf20Sopenharmony_ci	if (ccw_device_notify(cdev, CIO_BOXED) == NOTIFY_OK)
19328c2ecf20Sopenharmony_ci		return 0;
19338c2ecf20Sopenharmony_ci	ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
19348c2ecf20Sopenharmony_ci	return -ENODEV;
19358c2ecf20Sopenharmony_ci}
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_cistatic int resume_handle_disc(struct ccw_device *cdev)
19388c2ecf20Sopenharmony_ci{
19398c2ecf20Sopenharmony_ci	cdev->private->state = DEV_STATE_DISCONNECTED;
19408c2ecf20Sopenharmony_ci	if (ccw_device_notify(cdev, CIO_GONE) == NOTIFY_OK)
19418c2ecf20Sopenharmony_ci		return 0;
19428c2ecf20Sopenharmony_ci	ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
19438c2ecf20Sopenharmony_ci	return -ENODEV;
19448c2ecf20Sopenharmony_ci}
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_cistatic int ccw_device_pm_restore(struct device *dev)
19478c2ecf20Sopenharmony_ci{
19488c2ecf20Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
19498c2ecf20Sopenharmony_ci	struct subchannel *sch;
19508c2ecf20Sopenharmony_ci	int ret = 0;
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	__ccw_device_pm_restore(cdev);
19538c2ecf20Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
19548c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
19558c2ecf20Sopenharmony_ci	if (cio_is_console(sch->schid))
19568c2ecf20Sopenharmony_ci		goto out_restore;
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	/* check recognition results */
19598c2ecf20Sopenharmony_ci	switch (cdev->private->state) {
19608c2ecf20Sopenharmony_ci	case DEV_STATE_OFFLINE:
19618c2ecf20Sopenharmony_ci	case DEV_STATE_ONLINE:
19628c2ecf20Sopenharmony_ci		cdev->private->flags.donotify = 0;
19638c2ecf20Sopenharmony_ci		break;
19648c2ecf20Sopenharmony_ci	case DEV_STATE_BOXED:
19658c2ecf20Sopenharmony_ci		ret = resume_handle_boxed(cdev);
19668c2ecf20Sopenharmony_ci		if (ret)
19678c2ecf20Sopenharmony_ci			goto out_unlock;
19688c2ecf20Sopenharmony_ci		goto out_restore;
19698c2ecf20Sopenharmony_ci	default:
19708c2ecf20Sopenharmony_ci		ret = resume_handle_disc(cdev);
19718c2ecf20Sopenharmony_ci		if (ret)
19728c2ecf20Sopenharmony_ci			goto out_unlock;
19738c2ecf20Sopenharmony_ci		goto out_restore;
19748c2ecf20Sopenharmony_ci	}
19758c2ecf20Sopenharmony_ci	/* check if the device type has changed */
19768c2ecf20Sopenharmony_ci	if (!ccw_device_test_sense_data(cdev)) {
19778c2ecf20Sopenharmony_ci		ccw_device_update_sense_data(cdev);
19788c2ecf20Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
19798c2ecf20Sopenharmony_ci		ret = -ENODEV;
19808c2ecf20Sopenharmony_ci		goto out_unlock;
19818c2ecf20Sopenharmony_ci	}
19828c2ecf20Sopenharmony_ci	if (!cdev->online)
19838c2ecf20Sopenharmony_ci		goto out_unlock;
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	if (ccw_device_online(cdev)) {
19868c2ecf20Sopenharmony_ci		ret = resume_handle_disc(cdev);
19878c2ecf20Sopenharmony_ci		if (ret)
19888c2ecf20Sopenharmony_ci			goto out_unlock;
19898c2ecf20Sopenharmony_ci		goto out_restore;
19908c2ecf20Sopenharmony_ci	}
19918c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
19928c2ecf20Sopenharmony_ci	wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
19938c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_BAD) {
19968c2ecf20Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
19978c2ecf20Sopenharmony_ci		ret = -ENODEV;
19988c2ecf20Sopenharmony_ci		goto out_unlock;
19998c2ecf20Sopenharmony_ci	}
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci	/* reenable cmf, if needed */
20028c2ecf20Sopenharmony_ci	if (cdev->private->cmb) {
20038c2ecf20Sopenharmony_ci		spin_unlock_irq(sch->lock);
20048c2ecf20Sopenharmony_ci		ret = ccw_set_cmf(cdev, 1);
20058c2ecf20Sopenharmony_ci		spin_lock_irq(sch->lock);
20068c2ecf20Sopenharmony_ci		if (ret) {
20078c2ecf20Sopenharmony_ci			CIO_MSG_EVENT(2, "resume: cdev 0.%x.%04x: cmf failed "
20088c2ecf20Sopenharmony_ci				      "(rc=%d)\n", cdev->private->dev_id.ssid,
20098c2ecf20Sopenharmony_ci				      cdev->private->dev_id.devno, ret);
20108c2ecf20Sopenharmony_ci			ret = 0;
20118c2ecf20Sopenharmony_ci		}
20128c2ecf20Sopenharmony_ci	}
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ciout_restore:
20158c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
20168c2ecf20Sopenharmony_ci	if (cdev->online && cdev->drv && cdev->drv->restore)
20178c2ecf20Sopenharmony_ci		ret = cdev->drv->restore(cdev);
20188c2ecf20Sopenharmony_ci	return ret;
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ciout_unlock:
20218c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
20228c2ecf20Sopenharmony_ci	return ret;
20238c2ecf20Sopenharmony_ci}
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_cistatic const struct dev_pm_ops ccw_pm_ops = {
20268c2ecf20Sopenharmony_ci	.prepare = ccw_device_pm_prepare,
20278c2ecf20Sopenharmony_ci	.complete = ccw_device_pm_complete,
20288c2ecf20Sopenharmony_ci	.freeze = ccw_device_pm_freeze,
20298c2ecf20Sopenharmony_ci	.thaw = ccw_device_pm_thaw,
20308c2ecf20Sopenharmony_ci	.restore = ccw_device_pm_restore,
20318c2ecf20Sopenharmony_ci};
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_cistatic struct bus_type ccw_bus_type = {
20348c2ecf20Sopenharmony_ci	.name   = "ccw",
20358c2ecf20Sopenharmony_ci	.match  = ccw_bus_match,
20368c2ecf20Sopenharmony_ci	.uevent = ccw_uevent,
20378c2ecf20Sopenharmony_ci	.probe  = ccw_device_probe,
20388c2ecf20Sopenharmony_ci	.remove = ccw_device_remove,
20398c2ecf20Sopenharmony_ci	.shutdown = ccw_device_shutdown,
20408c2ecf20Sopenharmony_ci	.pm = &ccw_pm_ops,
20418c2ecf20Sopenharmony_ci};
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci/**
20448c2ecf20Sopenharmony_ci * ccw_driver_register() - register a ccw driver
20458c2ecf20Sopenharmony_ci * @cdriver: driver to be registered
20468c2ecf20Sopenharmony_ci *
20478c2ecf20Sopenharmony_ci * This function is mainly a wrapper around driver_register().
20488c2ecf20Sopenharmony_ci * Returns:
20498c2ecf20Sopenharmony_ci *   %0 on success and a negative error value on failure.
20508c2ecf20Sopenharmony_ci */
20518c2ecf20Sopenharmony_ciint ccw_driver_register(struct ccw_driver *cdriver)
20528c2ecf20Sopenharmony_ci{
20538c2ecf20Sopenharmony_ci	struct device_driver *drv = &cdriver->driver;
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	drv->bus = &ccw_bus_type;
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	return driver_register(drv);
20588c2ecf20Sopenharmony_ci}
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci/**
20618c2ecf20Sopenharmony_ci * ccw_driver_unregister() - deregister a ccw driver
20628c2ecf20Sopenharmony_ci * @cdriver: driver to be deregistered
20638c2ecf20Sopenharmony_ci *
20648c2ecf20Sopenharmony_ci * This function is mainly a wrapper around driver_unregister().
20658c2ecf20Sopenharmony_ci */
20668c2ecf20Sopenharmony_civoid ccw_driver_unregister(struct ccw_driver *cdriver)
20678c2ecf20Sopenharmony_ci{
20688c2ecf20Sopenharmony_ci	driver_unregister(&cdriver->driver);
20698c2ecf20Sopenharmony_ci}
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_cistatic void ccw_device_todo(struct work_struct *work)
20728c2ecf20Sopenharmony_ci{
20738c2ecf20Sopenharmony_ci	struct ccw_device_private *priv;
20748c2ecf20Sopenharmony_ci	struct ccw_device *cdev;
20758c2ecf20Sopenharmony_ci	struct subchannel *sch;
20768c2ecf20Sopenharmony_ci	enum cdev_todo todo;
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	priv = container_of(work, struct ccw_device_private, todo_work);
20798c2ecf20Sopenharmony_ci	cdev = priv->cdev;
20808c2ecf20Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
20818c2ecf20Sopenharmony_ci	/* Find out todo. */
20828c2ecf20Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
20838c2ecf20Sopenharmony_ci	todo = priv->todo;
20848c2ecf20Sopenharmony_ci	priv->todo = CDEV_TODO_NOTHING;
20858c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(4, "cdev_todo: cdev=0.%x.%04x todo=%d\n",
20868c2ecf20Sopenharmony_ci		      priv->dev_id.ssid, priv->dev_id.devno, todo);
20878c2ecf20Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
20888c2ecf20Sopenharmony_ci	/* Perform todo. */
20898c2ecf20Sopenharmony_ci	switch (todo) {
20908c2ecf20Sopenharmony_ci	case CDEV_TODO_ENABLE_CMF:
20918c2ecf20Sopenharmony_ci		cmf_reenable(cdev);
20928c2ecf20Sopenharmony_ci		break;
20938c2ecf20Sopenharmony_ci	case CDEV_TODO_REBIND:
20948c2ecf20Sopenharmony_ci		ccw_device_do_unbind_bind(cdev);
20958c2ecf20Sopenharmony_ci		break;
20968c2ecf20Sopenharmony_ci	case CDEV_TODO_REGISTER:
20978c2ecf20Sopenharmony_ci		io_subchannel_register(cdev);
20988c2ecf20Sopenharmony_ci		break;
20998c2ecf20Sopenharmony_ci	case CDEV_TODO_UNREG_EVAL:
21008c2ecf20Sopenharmony_ci		if (!sch_is_pseudo_sch(sch))
21018c2ecf20Sopenharmony_ci			css_schedule_eval(sch->schid);
21028c2ecf20Sopenharmony_ci		fallthrough;
21038c2ecf20Sopenharmony_ci	case CDEV_TODO_UNREG:
21048c2ecf20Sopenharmony_ci		if (sch_is_pseudo_sch(sch))
21058c2ecf20Sopenharmony_ci			ccw_device_unregister(cdev);
21068c2ecf20Sopenharmony_ci		else
21078c2ecf20Sopenharmony_ci			ccw_device_call_sch_unregister(cdev);
21088c2ecf20Sopenharmony_ci		break;
21098c2ecf20Sopenharmony_ci	default:
21108c2ecf20Sopenharmony_ci		break;
21118c2ecf20Sopenharmony_ci	}
21128c2ecf20Sopenharmony_ci	/* Release workqueue ref. */
21138c2ecf20Sopenharmony_ci	put_device(&cdev->dev);
21148c2ecf20Sopenharmony_ci}
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci/**
21178c2ecf20Sopenharmony_ci * ccw_device_sched_todo - schedule ccw device operation
21188c2ecf20Sopenharmony_ci * @cdev: ccw device
21198c2ecf20Sopenharmony_ci * @todo: todo
21208c2ecf20Sopenharmony_ci *
21218c2ecf20Sopenharmony_ci * Schedule the operation identified by @todo to be performed on the slow path
21228c2ecf20Sopenharmony_ci * workqueue. Do nothing if another operation with higher priority is already
21238c2ecf20Sopenharmony_ci * scheduled. Needs to be called with ccwdev lock held.
21248c2ecf20Sopenharmony_ci */
21258c2ecf20Sopenharmony_civoid ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo)
21268c2ecf20Sopenharmony_ci{
21278c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(4, "cdev_todo: sched cdev=0.%x.%04x todo=%d\n",
21288c2ecf20Sopenharmony_ci		      cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
21298c2ecf20Sopenharmony_ci		      todo);
21308c2ecf20Sopenharmony_ci	if (cdev->private->todo >= todo)
21318c2ecf20Sopenharmony_ci		return;
21328c2ecf20Sopenharmony_ci	cdev->private->todo = todo;
21338c2ecf20Sopenharmony_ci	/* Get workqueue ref. */
21348c2ecf20Sopenharmony_ci	if (!get_device(&cdev->dev))
21358c2ecf20Sopenharmony_ci		return;
21368c2ecf20Sopenharmony_ci	if (!queue_work(cio_work_q, &cdev->private->todo_work)) {
21378c2ecf20Sopenharmony_ci		/* Already queued, release workqueue ref. */
21388c2ecf20Sopenharmony_ci		put_device(&cdev->dev);
21398c2ecf20Sopenharmony_ci	}
21408c2ecf20Sopenharmony_ci}
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci/**
21438c2ecf20Sopenharmony_ci * ccw_device_siosl() - initiate logging
21448c2ecf20Sopenharmony_ci * @cdev: ccw device
21458c2ecf20Sopenharmony_ci *
21468c2ecf20Sopenharmony_ci * This function is used to invoke model-dependent logging within the channel
21478c2ecf20Sopenharmony_ci * subsystem.
21488c2ecf20Sopenharmony_ci */
21498c2ecf20Sopenharmony_ciint ccw_device_siosl(struct ccw_device *cdev)
21508c2ecf20Sopenharmony_ci{
21518c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	return chsc_siosl(sch->schid);
21548c2ecf20Sopenharmony_ci}
21558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_siosl);
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ccw_device_set_online);
21588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ccw_device_set_offline);
21598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ccw_driver_register);
21608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ccw_driver_unregister);
21618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_ccwdev_by_busid);
2162