162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  bus driver for ccw devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *    Copyright IBM Corp. 2002, 2008
662306a36Sopenharmony_ci *    Author(s): Arnd Bergmann (arndb@de.ibm.com)
762306a36Sopenharmony_ci *		 Cornelia Huck (cornelia.huck@de.ibm.com)
862306a36Sopenharmony_ci *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define KMSG_COMPONENT "cio"
1262306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/export.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/spinlock.h>
1762306a36Sopenharmony_ci#include <linux/errno.h>
1862306a36Sopenharmony_ci#include <linux/err.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/list.h>
2162306a36Sopenharmony_ci#include <linux/device.h>
2262306a36Sopenharmony_ci#include <linux/workqueue.h>
2362306a36Sopenharmony_ci#include <linux/delay.h>
2462306a36Sopenharmony_ci#include <linux/timer.h>
2562306a36Sopenharmony_ci#include <linux/kernel_stat.h>
2662306a36Sopenharmony_ci#include <linux/sched/signal.h>
2762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <asm/ccwdev.h>
3062306a36Sopenharmony_ci#include <asm/cio.h>
3162306a36Sopenharmony_ci#include <asm/param.h>		/* HZ */
3262306a36Sopenharmony_ci#include <asm/cmb.h>
3362306a36Sopenharmony_ci#include <asm/isc.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "chp.h"
3662306a36Sopenharmony_ci#include "cio.h"
3762306a36Sopenharmony_ci#include "cio_debug.h"
3862306a36Sopenharmony_ci#include "css.h"
3962306a36Sopenharmony_ci#include "device.h"
4062306a36Sopenharmony_ci#include "ioasm.h"
4162306a36Sopenharmony_ci#include "io_sch.h"
4262306a36Sopenharmony_ci#include "blacklist.h"
4362306a36Sopenharmony_ci#include "chsc.h"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic struct timer_list recovery_timer;
4662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(recovery_lock);
4762306a36Sopenharmony_cistatic int recovery_phase;
4862306a36Sopenharmony_cistatic const unsigned long recovery_delay[] = { 3, 30, 300 };
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic atomic_t ccw_device_init_count = ATOMIC_INIT(0);
5162306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(ccw_device_init_wq);
5262306a36Sopenharmony_cistatic struct bus_type ccw_bus_type;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/******************* bus type handling ***********************/
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* The Linux driver model distinguishes between a bus type and
5762306a36Sopenharmony_ci * the bus itself. Of course we only have one channel
5862306a36Sopenharmony_ci * subsystem driver and one channel system per machine, but
5962306a36Sopenharmony_ci * we still use the abstraction. T.R. says it's a good idea. */
6062306a36Sopenharmony_cistatic int
6162306a36Sopenharmony_ciccw_bus_match (struct device * dev, struct device_driver * drv)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
6462306a36Sopenharmony_ci	struct ccw_driver *cdrv = to_ccwdrv(drv);
6562306a36Sopenharmony_ci	const struct ccw_device_id *ids = cdrv->ids, *found;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (!ids)
6862306a36Sopenharmony_ci		return 0;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	found = ccw_device_id_match(ids, &cdev->id);
7162306a36Sopenharmony_ci	if (!found)
7262306a36Sopenharmony_ci		return 0;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	cdev->id.driver_info = found->driver_info;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return 1;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* Store modalias string delimited by prefix/suffix string into buffer with
8062306a36Sopenharmony_ci * specified size. Return length of resulting string (excluding trailing '\0')
8162306a36Sopenharmony_ci * even if string doesn't fit buffer (snprintf semantics). */
8262306a36Sopenharmony_cistatic int snprint_alias(char *buf, size_t size,
8362306a36Sopenharmony_ci			 const struct ccw_device_id *id, const char *suffix)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int len;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model);
8862306a36Sopenharmony_ci	if (len > size)
8962306a36Sopenharmony_ci		return len;
9062306a36Sopenharmony_ci	buf += len;
9162306a36Sopenharmony_ci	size -= len;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (id->dev_type != 0)
9462306a36Sopenharmony_ci		len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type,
9562306a36Sopenharmony_ci				id->dev_model, suffix);
9662306a36Sopenharmony_ci	else
9762306a36Sopenharmony_ci		len += snprintf(buf, size, "dtdm%s", suffix);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return len;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* Set up environment variables for ccw device uevent. Return 0 on success,
10362306a36Sopenharmony_ci * non-zero otherwise. */
10462306a36Sopenharmony_cistatic int ccw_uevent(const struct device *dev, struct kobj_uevent_env *env)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	const struct ccw_device *cdev = to_ccwdev(dev);
10762306a36Sopenharmony_ci	const struct ccw_device_id *id = &(cdev->id);
10862306a36Sopenharmony_ci	int ret;
10962306a36Sopenharmony_ci	char modalias_buf[30];
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* CU_TYPE= */
11262306a36Sopenharmony_ci	ret = add_uevent_var(env, "CU_TYPE=%04X", id->cu_type);
11362306a36Sopenharmony_ci	if (ret)
11462306a36Sopenharmony_ci		return ret;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/* CU_MODEL= */
11762306a36Sopenharmony_ci	ret = add_uevent_var(env, "CU_MODEL=%02X", id->cu_model);
11862306a36Sopenharmony_ci	if (ret)
11962306a36Sopenharmony_ci		return ret;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* The next two can be zero, that's ok for us */
12262306a36Sopenharmony_ci	/* DEV_TYPE= */
12362306a36Sopenharmony_ci	ret = add_uevent_var(env, "DEV_TYPE=%04X", id->dev_type);
12462306a36Sopenharmony_ci	if (ret)
12562306a36Sopenharmony_ci		return ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* DEV_MODEL= */
12862306a36Sopenharmony_ci	ret = add_uevent_var(env, "DEV_MODEL=%02X", id->dev_model);
12962306a36Sopenharmony_ci	if (ret)
13062306a36Sopenharmony_ci		return ret;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* MODALIAS=  */
13362306a36Sopenharmony_ci	snprint_alias(modalias_buf, sizeof(modalias_buf), id, "");
13462306a36Sopenharmony_ci	ret = add_uevent_var(env, "MODALIAS=%s", modalias_buf);
13562306a36Sopenharmony_ci	return ret;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void io_subchannel_irq(struct subchannel *);
13962306a36Sopenharmony_cistatic int io_subchannel_probe(struct subchannel *);
14062306a36Sopenharmony_cistatic void io_subchannel_remove(struct subchannel *);
14162306a36Sopenharmony_cistatic void io_subchannel_shutdown(struct subchannel *);
14262306a36Sopenharmony_cistatic int io_subchannel_sch_event(struct subchannel *, int);
14362306a36Sopenharmony_cistatic int io_subchannel_chp_event(struct subchannel *, struct chp_link *,
14462306a36Sopenharmony_ci				   int);
14562306a36Sopenharmony_cistatic void recovery_func(struct timer_list *unused);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic struct css_device_id io_subchannel_ids[] = {
14862306a36Sopenharmony_ci	{ .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, },
14962306a36Sopenharmony_ci	{ /* end of list */ },
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int io_subchannel_settle(void)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = wait_event_interruptible(ccw_device_init_wq,
15762306a36Sopenharmony_ci				atomic_read(&ccw_device_init_count) == 0);
15862306a36Sopenharmony_ci	if (ret)
15962306a36Sopenharmony_ci		return -EINTR;
16062306a36Sopenharmony_ci	flush_workqueue(cio_work_q);
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic struct css_driver io_subchannel_driver = {
16562306a36Sopenharmony_ci	.drv = {
16662306a36Sopenharmony_ci		.owner = THIS_MODULE,
16762306a36Sopenharmony_ci		.name = "io_subchannel",
16862306a36Sopenharmony_ci	},
16962306a36Sopenharmony_ci	.subchannel_type = io_subchannel_ids,
17062306a36Sopenharmony_ci	.irq = io_subchannel_irq,
17162306a36Sopenharmony_ci	.sch_event = io_subchannel_sch_event,
17262306a36Sopenharmony_ci	.chp_event = io_subchannel_chp_event,
17362306a36Sopenharmony_ci	.probe = io_subchannel_probe,
17462306a36Sopenharmony_ci	.remove = io_subchannel_remove,
17562306a36Sopenharmony_ci	.shutdown = io_subchannel_shutdown,
17662306a36Sopenharmony_ci	.settle = io_subchannel_settle,
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciint __init io_subchannel_init(void)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	timer_setup(&recovery_timer, recovery_func, 0);
18462306a36Sopenharmony_ci	ret = bus_register(&ccw_bus_type);
18562306a36Sopenharmony_ci	if (ret)
18662306a36Sopenharmony_ci		return ret;
18762306a36Sopenharmony_ci	ret = css_driver_register(&io_subchannel_driver);
18862306a36Sopenharmony_ci	if (ret)
18962306a36Sopenharmony_ci		bus_unregister(&ccw_bus_type);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return ret;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/************************ device handling **************************/
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic ssize_t
19862306a36Sopenharmony_cidevtype_show (struct device *dev, struct device_attribute *attr, char *buf)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
20162306a36Sopenharmony_ci	struct ccw_device_id *id = &(cdev->id);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (id->dev_type != 0)
20462306a36Sopenharmony_ci		return sprintf(buf, "%04x/%02x\n",
20562306a36Sopenharmony_ci				id->dev_type, id->dev_model);
20662306a36Sopenharmony_ci	else
20762306a36Sopenharmony_ci		return sprintf(buf, "n/a\n");
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic ssize_t
21162306a36Sopenharmony_cicutype_show (struct device *dev, struct device_attribute *attr, char *buf)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
21462306a36Sopenharmony_ci	struct ccw_device_id *id = &(cdev->id);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return sprintf(buf, "%04x/%02x\n",
21762306a36Sopenharmony_ci		       id->cu_type, id->cu_model);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic ssize_t
22162306a36Sopenharmony_cimodalias_show (struct device *dev, struct device_attribute *attr, char *buf)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
22462306a36Sopenharmony_ci	struct ccw_device_id *id = &(cdev->id);
22562306a36Sopenharmony_ci	int len;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	len = snprint_alias(buf, PAGE_SIZE, id, "\n");
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return len > PAGE_SIZE ? PAGE_SIZE : len;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic ssize_t
23362306a36Sopenharmony_cionline_show (struct device *dev, struct device_attribute *attr, char *buf)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return sprintf(buf, cdev->online ? "1\n" : "0\n");
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ciint ccw_device_is_orphan(struct ccw_device *cdev)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void ccw_device_unregister(struct ccw_device *cdev)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	mutex_lock(&cdev->reg_mutex);
24862306a36Sopenharmony_ci	if (device_is_registered(&cdev->dev)) {
24962306a36Sopenharmony_ci		/* Undo device_add(). */
25062306a36Sopenharmony_ci		device_del(&cdev->dev);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	mutex_unlock(&cdev->reg_mutex);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (cdev->private->flags.initialized) {
25562306a36Sopenharmony_ci		cdev->private->flags.initialized = 0;
25662306a36Sopenharmony_ci		/* Release reference from device_initialize(). */
25762306a36Sopenharmony_ci		put_device(&cdev->dev);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic void io_subchannel_quiesce(struct subchannel *);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci/**
26462306a36Sopenharmony_ci * ccw_device_set_offline() - disable a ccw device for I/O
26562306a36Sopenharmony_ci * @cdev: target ccw device
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * This function calls the driver's set_offline() function for @cdev, if
26862306a36Sopenharmony_ci * given, and then disables @cdev.
26962306a36Sopenharmony_ci * Returns:
27062306a36Sopenharmony_ci *   %0 on success and a negative error value on failure.
27162306a36Sopenharmony_ci * Context:
27262306a36Sopenharmony_ci *  enabled, ccw device lock not held
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_ciint ccw_device_set_offline(struct ccw_device *cdev)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct subchannel *sch;
27762306a36Sopenharmony_ci	int ret, state;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (!cdev)
28062306a36Sopenharmony_ci		return -ENODEV;
28162306a36Sopenharmony_ci	if (!cdev->online || !cdev->drv)
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (cdev->drv->set_offline) {
28562306a36Sopenharmony_ci		ret = cdev->drv->set_offline(cdev);
28662306a36Sopenharmony_ci		if (ret != 0)
28762306a36Sopenharmony_ci			return ret;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
29062306a36Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
29162306a36Sopenharmony_ci	cdev->online = 0;
29262306a36Sopenharmony_ci	/* Wait until a final state or DISCONNECTED is reached */
29362306a36Sopenharmony_ci	while (!dev_fsm_final_state(cdev) &&
29462306a36Sopenharmony_ci	       cdev->private->state != DEV_STATE_DISCONNECTED) {
29562306a36Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
29662306a36Sopenharmony_ci		wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
29762306a36Sopenharmony_ci			   cdev->private->state == DEV_STATE_DISCONNECTED));
29862306a36Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci	do {
30162306a36Sopenharmony_ci		ret = ccw_device_offline(cdev);
30262306a36Sopenharmony_ci		if (!ret)
30362306a36Sopenharmony_ci			break;
30462306a36Sopenharmony_ci		CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device "
30562306a36Sopenharmony_ci			      "0.%x.%04x\n", ret, cdev->private->dev_id.ssid,
30662306a36Sopenharmony_ci			      cdev->private->dev_id.devno);
30762306a36Sopenharmony_ci		if (ret != -EBUSY)
30862306a36Sopenharmony_ci			goto error;
30962306a36Sopenharmony_ci		state = cdev->private->state;
31062306a36Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
31162306a36Sopenharmony_ci		io_subchannel_quiesce(sch);
31262306a36Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
31362306a36Sopenharmony_ci		cdev->private->state = state;
31462306a36Sopenharmony_ci	} while (ret == -EBUSY);
31562306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
31662306a36Sopenharmony_ci	wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
31762306a36Sopenharmony_ci		   cdev->private->state == DEV_STATE_DISCONNECTED));
31862306a36Sopenharmony_ci	/* Inform the user if set offline failed. */
31962306a36Sopenharmony_ci	if (cdev->private->state == DEV_STATE_BOXED) {
32062306a36Sopenharmony_ci		pr_warn("%s: The device entered boxed state while being set offline\n",
32162306a36Sopenharmony_ci			dev_name(&cdev->dev));
32262306a36Sopenharmony_ci	} else if (cdev->private->state == DEV_STATE_NOT_OPER) {
32362306a36Sopenharmony_ci		pr_warn("%s: The device stopped operating while being set offline\n",
32462306a36Sopenharmony_ci			dev_name(&cdev->dev));
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci	/* Give up reference from ccw_device_set_online(). */
32762306a36Sopenharmony_ci	put_device(&cdev->dev);
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cierror:
33162306a36Sopenharmony_ci	cdev->private->state = DEV_STATE_OFFLINE;
33262306a36Sopenharmony_ci	dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
33362306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
33462306a36Sopenharmony_ci	/* Give up reference from ccw_device_set_online(). */
33562306a36Sopenharmony_ci	put_device(&cdev->dev);
33662306a36Sopenharmony_ci	return -ENODEV;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/**
34062306a36Sopenharmony_ci * ccw_device_set_online() - enable a ccw device for I/O
34162306a36Sopenharmony_ci * @cdev: target ccw device
34262306a36Sopenharmony_ci *
34362306a36Sopenharmony_ci * This function first enables @cdev and then calls the driver's set_online()
34462306a36Sopenharmony_ci * function for @cdev, if given. If set_online() returns an error, @cdev is
34562306a36Sopenharmony_ci * disabled again.
34662306a36Sopenharmony_ci * Returns:
34762306a36Sopenharmony_ci *   %0 on success and a negative error value on failure.
34862306a36Sopenharmony_ci * Context:
34962306a36Sopenharmony_ci *  enabled, ccw device lock not held
35062306a36Sopenharmony_ci */
35162306a36Sopenharmony_ciint ccw_device_set_online(struct ccw_device *cdev)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	int ret;
35462306a36Sopenharmony_ci	int ret2;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (!cdev)
35762306a36Sopenharmony_ci		return -ENODEV;
35862306a36Sopenharmony_ci	if (cdev->online || !cdev->drv)
35962306a36Sopenharmony_ci		return -EINVAL;
36062306a36Sopenharmony_ci	/* Hold on to an extra reference while device is online. */
36162306a36Sopenharmony_ci	if (!get_device(&cdev->dev))
36262306a36Sopenharmony_ci		return -ENODEV;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
36562306a36Sopenharmony_ci	ret = ccw_device_online(cdev);
36662306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
36762306a36Sopenharmony_ci	if (ret == 0)
36862306a36Sopenharmony_ci		wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
36962306a36Sopenharmony_ci	else {
37062306a36Sopenharmony_ci		CIO_MSG_EVENT(0, "ccw_device_online returned %d, "
37162306a36Sopenharmony_ci			      "device 0.%x.%04x\n",
37262306a36Sopenharmony_ci			      ret, cdev->private->dev_id.ssid,
37362306a36Sopenharmony_ci			      cdev->private->dev_id.devno);
37462306a36Sopenharmony_ci		/* Give up online reference since onlining failed. */
37562306a36Sopenharmony_ci		put_device(&cdev->dev);
37662306a36Sopenharmony_ci		return ret;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
37962306a36Sopenharmony_ci	/* Check if online processing was successful */
38062306a36Sopenharmony_ci	if ((cdev->private->state != DEV_STATE_ONLINE) &&
38162306a36Sopenharmony_ci	    (cdev->private->state != DEV_STATE_W4SENSE)) {
38262306a36Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
38362306a36Sopenharmony_ci		/* Inform the user that set online failed. */
38462306a36Sopenharmony_ci		if (cdev->private->state == DEV_STATE_BOXED) {
38562306a36Sopenharmony_ci			pr_warn("%s: Setting the device online failed because it is boxed\n",
38662306a36Sopenharmony_ci				dev_name(&cdev->dev));
38762306a36Sopenharmony_ci		} else if (cdev->private->state == DEV_STATE_NOT_OPER) {
38862306a36Sopenharmony_ci			pr_warn("%s: Setting the device online failed because it is not operational\n",
38962306a36Sopenharmony_ci				dev_name(&cdev->dev));
39062306a36Sopenharmony_ci		}
39162306a36Sopenharmony_ci		/* Give up online reference since onlining failed. */
39262306a36Sopenharmony_ci		put_device(&cdev->dev);
39362306a36Sopenharmony_ci		return -ENODEV;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
39662306a36Sopenharmony_ci	if (cdev->drv->set_online)
39762306a36Sopenharmony_ci		ret = cdev->drv->set_online(cdev);
39862306a36Sopenharmony_ci	if (ret)
39962306a36Sopenharmony_ci		goto rollback;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
40262306a36Sopenharmony_ci	cdev->online = 1;
40362306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
40462306a36Sopenharmony_ci	return 0;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cirollback:
40762306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
40862306a36Sopenharmony_ci	/* Wait until a final state or DISCONNECTED is reached */
40962306a36Sopenharmony_ci	while (!dev_fsm_final_state(cdev) &&
41062306a36Sopenharmony_ci	       cdev->private->state != DEV_STATE_DISCONNECTED) {
41162306a36Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
41262306a36Sopenharmony_ci		wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
41362306a36Sopenharmony_ci			   cdev->private->state == DEV_STATE_DISCONNECTED));
41462306a36Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	ret2 = ccw_device_offline(cdev);
41762306a36Sopenharmony_ci	if (ret2)
41862306a36Sopenharmony_ci		goto error;
41962306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
42062306a36Sopenharmony_ci	wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
42162306a36Sopenharmony_ci		   cdev->private->state == DEV_STATE_DISCONNECTED));
42262306a36Sopenharmony_ci	/* Give up online reference since onlining failed. */
42362306a36Sopenharmony_ci	put_device(&cdev->dev);
42462306a36Sopenharmony_ci	return ret;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cierror:
42762306a36Sopenharmony_ci	CIO_MSG_EVENT(0, "rollback ccw_device_offline returned %d, "
42862306a36Sopenharmony_ci		      "device 0.%x.%04x\n",
42962306a36Sopenharmony_ci		      ret2, cdev->private->dev_id.ssid,
43062306a36Sopenharmony_ci		      cdev->private->dev_id.devno);
43162306a36Sopenharmony_ci	cdev->private->state = DEV_STATE_OFFLINE;
43262306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
43362306a36Sopenharmony_ci	/* Give up online reference since onlining failed. */
43462306a36Sopenharmony_ci	put_device(&cdev->dev);
43562306a36Sopenharmony_ci	return ret;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic int online_store_handle_offline(struct ccw_device *cdev)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	if (cdev->private->state == DEV_STATE_DISCONNECTED) {
44162306a36Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
44262306a36Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
44362306a36Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
44462306a36Sopenharmony_ci		return 0;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci	if (cdev->drv && cdev->drv->set_offline)
44762306a36Sopenharmony_ci		return ccw_device_set_offline(cdev);
44862306a36Sopenharmony_ci	return -EINVAL;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int online_store_recog_and_online(struct ccw_device *cdev)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	/* Do device recognition, if needed. */
45462306a36Sopenharmony_ci	if (cdev->private->state == DEV_STATE_BOXED) {
45562306a36Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
45662306a36Sopenharmony_ci		ccw_device_recognition(cdev);
45762306a36Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
45862306a36Sopenharmony_ci		wait_event(cdev->private->wait_q,
45962306a36Sopenharmony_ci			   cdev->private->flags.recog_done);
46062306a36Sopenharmony_ci		if (cdev->private->state != DEV_STATE_OFFLINE)
46162306a36Sopenharmony_ci			/* recognition failed */
46262306a36Sopenharmony_ci			return -EAGAIN;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci	if (cdev->drv && cdev->drv->set_online)
46562306a36Sopenharmony_ci		return ccw_device_set_online(cdev);
46662306a36Sopenharmony_ci	return -EINVAL;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int online_store_handle_online(struct ccw_device *cdev, int force)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	int ret;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	ret = online_store_recog_and_online(cdev);
47462306a36Sopenharmony_ci	if (ret && !force)
47562306a36Sopenharmony_ci		return ret;
47662306a36Sopenharmony_ci	if (force && cdev->private->state == DEV_STATE_BOXED) {
47762306a36Sopenharmony_ci		ret = ccw_device_stlck(cdev);
47862306a36Sopenharmony_ci		if (ret)
47962306a36Sopenharmony_ci			return ret;
48062306a36Sopenharmony_ci		if (cdev->id.cu_type == 0)
48162306a36Sopenharmony_ci			cdev->private->state = DEV_STATE_NOT_OPER;
48262306a36Sopenharmony_ci		ret = online_store_recog_and_online(cdev);
48362306a36Sopenharmony_ci		if (ret)
48462306a36Sopenharmony_ci			return ret;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic ssize_t online_store (struct device *dev, struct device_attribute *attr,
49062306a36Sopenharmony_ci			     const char *buf, size_t count)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
49362306a36Sopenharmony_ci	int force, ret;
49462306a36Sopenharmony_ci	unsigned long i;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* Prevent conflict between multiple on-/offline processing requests. */
49762306a36Sopenharmony_ci	if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
49862306a36Sopenharmony_ci		return -EAGAIN;
49962306a36Sopenharmony_ci	/* Prevent conflict between internal I/Os and on-/offline processing. */
50062306a36Sopenharmony_ci	if (!dev_fsm_final_state(cdev) &&
50162306a36Sopenharmony_ci	    cdev->private->state != DEV_STATE_DISCONNECTED) {
50262306a36Sopenharmony_ci		ret = -EAGAIN;
50362306a36Sopenharmony_ci		goto out;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci	/* Prevent conflict between pending work and on-/offline processing.*/
50662306a36Sopenharmony_ci	if (work_pending(&cdev->private->todo_work)) {
50762306a36Sopenharmony_ci		ret = -EAGAIN;
50862306a36Sopenharmony_ci		goto out;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci	if (!strncmp(buf, "force\n", count)) {
51162306a36Sopenharmony_ci		force = 1;
51262306a36Sopenharmony_ci		i = 1;
51362306a36Sopenharmony_ci		ret = 0;
51462306a36Sopenharmony_ci	} else {
51562306a36Sopenharmony_ci		force = 0;
51662306a36Sopenharmony_ci		ret = kstrtoul(buf, 16, &i);
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci	if (ret)
51962306a36Sopenharmony_ci		goto out;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	device_lock(dev);
52262306a36Sopenharmony_ci	switch (i) {
52362306a36Sopenharmony_ci	case 0:
52462306a36Sopenharmony_ci		ret = online_store_handle_offline(cdev);
52562306a36Sopenharmony_ci		break;
52662306a36Sopenharmony_ci	case 1:
52762306a36Sopenharmony_ci		ret = online_store_handle_online(cdev, force);
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci	default:
53062306a36Sopenharmony_ci		ret = -EINVAL;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci	device_unlock(dev);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ciout:
53562306a36Sopenharmony_ci	atomic_set(&cdev->private->onoff, 0);
53662306a36Sopenharmony_ci	return (ret < 0) ? ret : count;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic ssize_t
54062306a36Sopenharmony_ciavailable_show (struct device *dev, struct device_attribute *attr, char *buf)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
54362306a36Sopenharmony_ci	struct subchannel *sch;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (ccw_device_is_orphan(cdev))
54662306a36Sopenharmony_ci		return sprintf(buf, "no device\n");
54762306a36Sopenharmony_ci	switch (cdev->private->state) {
54862306a36Sopenharmony_ci	case DEV_STATE_BOXED:
54962306a36Sopenharmony_ci		return sprintf(buf, "boxed\n");
55062306a36Sopenharmony_ci	case DEV_STATE_DISCONNECTED:
55162306a36Sopenharmony_ci	case DEV_STATE_DISCONNECTED_SENSE_ID:
55262306a36Sopenharmony_ci	case DEV_STATE_NOT_OPER:
55362306a36Sopenharmony_ci		sch = to_subchannel(dev->parent);
55462306a36Sopenharmony_ci		if (!sch->lpm)
55562306a36Sopenharmony_ci			return sprintf(buf, "no path\n");
55662306a36Sopenharmony_ci		else
55762306a36Sopenharmony_ci			return sprintf(buf, "no device\n");
55862306a36Sopenharmony_ci	default:
55962306a36Sopenharmony_ci		/* All other states considered fine. */
56062306a36Sopenharmony_ci		return sprintf(buf, "good\n");
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic ssize_t
56562306a36Sopenharmony_ciinitiate_logging(struct device *dev, struct device_attribute *attr,
56662306a36Sopenharmony_ci		 const char *buf, size_t count)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(dev);
56962306a36Sopenharmony_ci	int rc;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	rc = chsc_siosl(sch->schid);
57262306a36Sopenharmony_ci	if (rc < 0) {
57362306a36Sopenharmony_ci		pr_warn("Logging for subchannel 0.%x.%04x failed with errno=%d\n",
57462306a36Sopenharmony_ci			sch->schid.ssid, sch->schid.sch_no, rc);
57562306a36Sopenharmony_ci		return rc;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci	pr_notice("Logging for subchannel 0.%x.%04x was triggered\n",
57862306a36Sopenharmony_ci		  sch->schid.ssid, sch->schid.sch_no);
57962306a36Sopenharmony_ci	return count;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic ssize_t vpm_show(struct device *dev, struct device_attribute *attr,
58362306a36Sopenharmony_ci			char *buf)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(dev);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return sprintf(buf, "%02x\n", sch->vpm);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(devtype);
59162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cutype);
59262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
59362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(online);
59462306a36Sopenharmony_cistatic DEVICE_ATTR(availability, 0444, available_show, NULL);
59562306a36Sopenharmony_cistatic DEVICE_ATTR(logging, 0200, NULL, initiate_logging);
59662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(vpm);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic struct attribute *io_subchannel_attrs[] = {
59962306a36Sopenharmony_ci	&dev_attr_logging.attr,
60062306a36Sopenharmony_ci	&dev_attr_vpm.attr,
60162306a36Sopenharmony_ci	NULL,
60262306a36Sopenharmony_ci};
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic const struct attribute_group io_subchannel_attr_group = {
60562306a36Sopenharmony_ci	.attrs = io_subchannel_attrs,
60662306a36Sopenharmony_ci};
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic struct attribute * ccwdev_attrs[] = {
60962306a36Sopenharmony_ci	&dev_attr_devtype.attr,
61062306a36Sopenharmony_ci	&dev_attr_cutype.attr,
61162306a36Sopenharmony_ci	&dev_attr_modalias.attr,
61262306a36Sopenharmony_ci	&dev_attr_online.attr,
61362306a36Sopenharmony_ci	&dev_attr_cmb_enable.attr,
61462306a36Sopenharmony_ci	&dev_attr_availability.attr,
61562306a36Sopenharmony_ci	NULL,
61662306a36Sopenharmony_ci};
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic const struct attribute_group ccwdev_attr_group = {
61962306a36Sopenharmony_ci	.attrs = ccwdev_attrs,
62062306a36Sopenharmony_ci};
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic const struct attribute_group *ccwdev_attr_groups[] = {
62362306a36Sopenharmony_ci	&ccwdev_attr_group,
62462306a36Sopenharmony_ci	NULL,
62562306a36Sopenharmony_ci};
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic int match_dev_id(struct device *dev, const void *data)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
63062306a36Sopenharmony_ci	struct ccw_dev_id *dev_id = (void *)data;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/**
63662306a36Sopenharmony_ci * get_ccwdev_by_dev_id() - obtain device from a ccw device id
63762306a36Sopenharmony_ci * @dev_id: id of the device to be searched
63862306a36Sopenharmony_ci *
63962306a36Sopenharmony_ci * This function searches all devices attached to the ccw bus for a device
64062306a36Sopenharmony_ci * matching @dev_id.
64162306a36Sopenharmony_ci * Returns:
64262306a36Sopenharmony_ci *  If a device is found its reference count is increased and returned;
64362306a36Sopenharmony_ci *  else %NULL is returned.
64462306a36Sopenharmony_ci */
64562306a36Sopenharmony_cistruct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct device *dev;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	dev = bus_find_device(&ccw_bus_type, NULL, dev_id, match_dev_id);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return dev ? to_ccwdev(dev) : NULL;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(get_ccwdev_by_dev_id);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic void ccw_device_do_unbind_bind(struct ccw_device *cdev)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	int ret;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	mutex_lock(&cdev->reg_mutex);
66062306a36Sopenharmony_ci	if (device_is_registered(&cdev->dev)) {
66162306a36Sopenharmony_ci		device_release_driver(&cdev->dev);
66262306a36Sopenharmony_ci		ret = device_attach(&cdev->dev);
66362306a36Sopenharmony_ci		WARN_ON(ret == -ENODEV);
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci	mutex_unlock(&cdev->reg_mutex);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic void
66962306a36Sopenharmony_ciccw_device_release(struct device *dev)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct ccw_device *cdev;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	cdev = to_ccwdev(dev);
67462306a36Sopenharmony_ci	cio_gp_dma_free(cdev->private->dma_pool, cdev->private->dma_area,
67562306a36Sopenharmony_ci			sizeof(*cdev->private->dma_area));
67662306a36Sopenharmony_ci	cio_gp_dma_destroy(cdev->private->dma_pool, &cdev->dev);
67762306a36Sopenharmony_ci	/* Release reference of parent subchannel. */
67862306a36Sopenharmony_ci	put_device(cdev->dev.parent);
67962306a36Sopenharmony_ci	kfree(cdev->private);
68062306a36Sopenharmony_ci	kfree(cdev);
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct ccw_device *cdev;
68662306a36Sopenharmony_ci	struct gen_pool *dma_pool;
68762306a36Sopenharmony_ci	int ret;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	cdev  = kzalloc(sizeof(*cdev), GFP_KERNEL);
69062306a36Sopenharmony_ci	if (!cdev) {
69162306a36Sopenharmony_ci		ret = -ENOMEM;
69262306a36Sopenharmony_ci		goto err_cdev;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci	cdev->private = kzalloc(sizeof(struct ccw_device_private),
69562306a36Sopenharmony_ci				GFP_KERNEL | GFP_DMA);
69662306a36Sopenharmony_ci	if (!cdev->private) {
69762306a36Sopenharmony_ci		ret = -ENOMEM;
69862306a36Sopenharmony_ci		goto err_priv;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	cdev->dev.dma_mask = sch->dev.dma_mask;
70262306a36Sopenharmony_ci	ret = dma_set_coherent_mask(&cdev->dev, sch->dev.coherent_dma_mask);
70362306a36Sopenharmony_ci	if (ret)
70462306a36Sopenharmony_ci		goto err_coherent_mask;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	dma_pool = cio_gp_dma_create(&cdev->dev, 1);
70762306a36Sopenharmony_ci	if (!dma_pool) {
70862306a36Sopenharmony_ci		ret = -ENOMEM;
70962306a36Sopenharmony_ci		goto err_dma_pool;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci	cdev->private->dma_pool = dma_pool;
71262306a36Sopenharmony_ci	cdev->private->dma_area = cio_gp_dma_zalloc(dma_pool, &cdev->dev,
71362306a36Sopenharmony_ci					sizeof(*cdev->private->dma_area));
71462306a36Sopenharmony_ci	if (!cdev->private->dma_area) {
71562306a36Sopenharmony_ci		ret = -ENOMEM;
71662306a36Sopenharmony_ci		goto err_dma_area;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci	return cdev;
71962306a36Sopenharmony_cierr_dma_area:
72062306a36Sopenharmony_ci	cio_gp_dma_destroy(dma_pool, &cdev->dev);
72162306a36Sopenharmony_cierr_dma_pool:
72262306a36Sopenharmony_cierr_coherent_mask:
72362306a36Sopenharmony_ci	kfree(cdev->private);
72462306a36Sopenharmony_cierr_priv:
72562306a36Sopenharmony_ci	kfree(cdev);
72662306a36Sopenharmony_cierr_cdev:
72762306a36Sopenharmony_ci	return ERR_PTR(ret);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic void ccw_device_todo(struct work_struct *work);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic int io_subchannel_initialize_dev(struct subchannel *sch,
73362306a36Sopenharmony_ci					struct ccw_device *cdev)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	struct ccw_device_private *priv = cdev->private;
73662306a36Sopenharmony_ci	int ret;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	priv->cdev = cdev;
73962306a36Sopenharmony_ci	priv->int_class = IRQIO_CIO;
74062306a36Sopenharmony_ci	priv->state = DEV_STATE_NOT_OPER;
74162306a36Sopenharmony_ci	priv->dev_id.devno = sch->schib.pmcw.dev;
74262306a36Sopenharmony_ci	priv->dev_id.ssid = sch->schid.ssid;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	INIT_WORK(&priv->todo_work, ccw_device_todo);
74562306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->cmb_list);
74662306a36Sopenharmony_ci	init_waitqueue_head(&priv->wait_q);
74762306a36Sopenharmony_ci	timer_setup(&priv->timer, ccw_device_timeout, 0);
74862306a36Sopenharmony_ci	mutex_init(&cdev->reg_mutex);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	atomic_set(&priv->onoff, 0);
75162306a36Sopenharmony_ci	cdev->ccwlock = sch->lock;
75262306a36Sopenharmony_ci	cdev->dev.parent = &sch->dev;
75362306a36Sopenharmony_ci	cdev->dev.release = ccw_device_release;
75462306a36Sopenharmony_ci	cdev->dev.bus = &ccw_bus_type;
75562306a36Sopenharmony_ci	cdev->dev.groups = ccwdev_attr_groups;
75662306a36Sopenharmony_ci	/* Do first half of device_register. */
75762306a36Sopenharmony_ci	device_initialize(&cdev->dev);
75862306a36Sopenharmony_ci	ret = dev_set_name(&cdev->dev, "0.%x.%04x", cdev->private->dev_id.ssid,
75962306a36Sopenharmony_ci			   cdev->private->dev_id.devno);
76062306a36Sopenharmony_ci	if (ret)
76162306a36Sopenharmony_ci		goto out_put;
76262306a36Sopenharmony_ci	if (!get_device(&sch->dev)) {
76362306a36Sopenharmony_ci		ret = -ENODEV;
76462306a36Sopenharmony_ci		goto out_put;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	priv->flags.initialized = 1;
76762306a36Sopenharmony_ci	spin_lock_irq(sch->lock);
76862306a36Sopenharmony_ci	sch_set_cdev(sch, cdev);
76962306a36Sopenharmony_ci	spin_unlock_irq(sch->lock);
77062306a36Sopenharmony_ci	return 0;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ciout_put:
77362306a36Sopenharmony_ci	/* Release reference from device_initialize(). */
77462306a36Sopenharmony_ci	put_device(&cdev->dev);
77562306a36Sopenharmony_ci	return ret;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct ccw_device *cdev;
78162306a36Sopenharmony_ci	int ret;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	cdev = io_subchannel_allocate_dev(sch);
78462306a36Sopenharmony_ci	if (!IS_ERR(cdev)) {
78562306a36Sopenharmony_ci		ret = io_subchannel_initialize_dev(sch, cdev);
78662306a36Sopenharmony_ci		if (ret)
78762306a36Sopenharmony_ci			cdev = ERR_PTR(ret);
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	return cdev;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic void io_subchannel_recog(struct ccw_device *, struct subchannel *);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic void sch_create_and_recog_new_device(struct subchannel *sch)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct ccw_device *cdev;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/* Need to allocate a new ccw device. */
79962306a36Sopenharmony_ci	cdev = io_subchannel_create_ccwdev(sch);
80062306a36Sopenharmony_ci	if (IS_ERR(cdev)) {
80162306a36Sopenharmony_ci		/* OK, we did everything we could... */
80262306a36Sopenharmony_ci		css_sch_device_unregister(sch);
80362306a36Sopenharmony_ci		return;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci	/* Start recognition for the new ccw device. */
80662306a36Sopenharmony_ci	io_subchannel_recog(cdev, sch);
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci/*
81062306a36Sopenharmony_ci * Register recognized device.
81162306a36Sopenharmony_ci */
81262306a36Sopenharmony_cistatic void io_subchannel_register(struct ccw_device *cdev)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct subchannel *sch;
81562306a36Sopenharmony_ci	int ret, adjust_init_count = 1;
81662306a36Sopenharmony_ci	unsigned long flags;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
81962306a36Sopenharmony_ci	/*
82062306a36Sopenharmony_ci	 * Check if subchannel is still registered. It may have become
82162306a36Sopenharmony_ci	 * unregistered if a machine check hit us after finishing
82262306a36Sopenharmony_ci	 * device recognition but before the register work could be
82362306a36Sopenharmony_ci	 * queued.
82462306a36Sopenharmony_ci	 */
82562306a36Sopenharmony_ci	if (!device_is_registered(&sch->dev))
82662306a36Sopenharmony_ci		goto out_err;
82762306a36Sopenharmony_ci	css_update_ssd_info(sch);
82862306a36Sopenharmony_ci	/*
82962306a36Sopenharmony_ci	 * io_subchannel_register() will also be called after device
83062306a36Sopenharmony_ci	 * recognition has been done for a boxed device (which will already
83162306a36Sopenharmony_ci	 * be registered). We need to reprobe since we may now have sense id
83262306a36Sopenharmony_ci	 * information.
83362306a36Sopenharmony_ci	 */
83462306a36Sopenharmony_ci	mutex_lock(&cdev->reg_mutex);
83562306a36Sopenharmony_ci	if (device_is_registered(&cdev->dev)) {
83662306a36Sopenharmony_ci		if (!cdev->drv) {
83762306a36Sopenharmony_ci			ret = device_reprobe(&cdev->dev);
83862306a36Sopenharmony_ci			if (ret)
83962306a36Sopenharmony_ci				/* We can't do much here. */
84062306a36Sopenharmony_ci				CIO_MSG_EVENT(0, "device_reprobe() returned"
84162306a36Sopenharmony_ci					      " %d for 0.%x.%04x\n", ret,
84262306a36Sopenharmony_ci					      cdev->private->dev_id.ssid,
84362306a36Sopenharmony_ci					      cdev->private->dev_id.devno);
84462306a36Sopenharmony_ci		}
84562306a36Sopenharmony_ci		adjust_init_count = 0;
84662306a36Sopenharmony_ci		goto out;
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci	/* make it known to the system */
84962306a36Sopenharmony_ci	ret = device_add(&cdev->dev);
85062306a36Sopenharmony_ci	if (ret) {
85162306a36Sopenharmony_ci		CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",
85262306a36Sopenharmony_ci			      cdev->private->dev_id.ssid,
85362306a36Sopenharmony_ci			      cdev->private->dev_id.devno, ret);
85462306a36Sopenharmony_ci		spin_lock_irqsave(sch->lock, flags);
85562306a36Sopenharmony_ci		sch_set_cdev(sch, NULL);
85662306a36Sopenharmony_ci		spin_unlock_irqrestore(sch->lock, flags);
85762306a36Sopenharmony_ci		mutex_unlock(&cdev->reg_mutex);
85862306a36Sopenharmony_ci		/* Release initial device reference. */
85962306a36Sopenharmony_ci		put_device(&cdev->dev);
86062306a36Sopenharmony_ci		goto out_err;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ciout:
86362306a36Sopenharmony_ci	cdev->private->flags.recog_done = 1;
86462306a36Sopenharmony_ci	mutex_unlock(&cdev->reg_mutex);
86562306a36Sopenharmony_ci	wake_up(&cdev->private->wait_q);
86662306a36Sopenharmony_ciout_err:
86762306a36Sopenharmony_ci	if (adjust_init_count && atomic_dec_and_test(&ccw_device_init_count))
86862306a36Sopenharmony_ci		wake_up(&ccw_device_init_wq);
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci/*
87262306a36Sopenharmony_ci * subchannel recognition done. Called from the state machine.
87362306a36Sopenharmony_ci */
87462306a36Sopenharmony_civoid
87562306a36Sopenharmony_ciio_subchannel_recog_done(struct ccw_device *cdev)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	if (css_init_done == 0) {
87862306a36Sopenharmony_ci		cdev->private->flags.recog_done = 1;
87962306a36Sopenharmony_ci		return;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci	switch (cdev->private->state) {
88262306a36Sopenharmony_ci	case DEV_STATE_BOXED:
88362306a36Sopenharmony_ci		/* Device did not respond in time. */
88462306a36Sopenharmony_ci	case DEV_STATE_NOT_OPER:
88562306a36Sopenharmony_ci		cdev->private->flags.recog_done = 1;
88662306a36Sopenharmony_ci		/* Remove device found not operational. */
88762306a36Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
88862306a36Sopenharmony_ci		if (atomic_dec_and_test(&ccw_device_init_count))
88962306a36Sopenharmony_ci			wake_up(&ccw_device_init_wq);
89062306a36Sopenharmony_ci		break;
89162306a36Sopenharmony_ci	case DEV_STATE_OFFLINE:
89262306a36Sopenharmony_ci		/*
89362306a36Sopenharmony_ci		 * We can't register the device in interrupt context so
89462306a36Sopenharmony_ci		 * we schedule a work item.
89562306a36Sopenharmony_ci		 */
89662306a36Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_REGISTER);
89762306a36Sopenharmony_ci		break;
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic void io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	/* Increase counter of devices currently in recognition. */
90462306a36Sopenharmony_ci	atomic_inc(&ccw_device_init_count);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* Start async. device sensing. */
90762306a36Sopenharmony_ci	spin_lock_irq(sch->lock);
90862306a36Sopenharmony_ci	ccw_device_recognition(cdev);
90962306a36Sopenharmony_ci	spin_unlock_irq(sch->lock);
91062306a36Sopenharmony_ci}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic int ccw_device_move_to_sch(struct ccw_device *cdev,
91362306a36Sopenharmony_ci				  struct subchannel *sch)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct subchannel *old_sch;
91662306a36Sopenharmony_ci	int rc, old_enabled = 0;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	old_sch = to_subchannel(cdev->dev.parent);
91962306a36Sopenharmony_ci	/* Obtain child reference for new parent. */
92062306a36Sopenharmony_ci	if (!get_device(&sch->dev))
92162306a36Sopenharmony_ci		return -ENODEV;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (!sch_is_pseudo_sch(old_sch)) {
92462306a36Sopenharmony_ci		spin_lock_irq(old_sch->lock);
92562306a36Sopenharmony_ci		old_enabled = old_sch->schib.pmcw.ena;
92662306a36Sopenharmony_ci		rc = 0;
92762306a36Sopenharmony_ci		if (old_enabled)
92862306a36Sopenharmony_ci			rc = cio_disable_subchannel(old_sch);
92962306a36Sopenharmony_ci		spin_unlock_irq(old_sch->lock);
93062306a36Sopenharmony_ci		if (rc == -EBUSY) {
93162306a36Sopenharmony_ci			/* Release child reference for new parent. */
93262306a36Sopenharmony_ci			put_device(&sch->dev);
93362306a36Sopenharmony_ci			return rc;
93462306a36Sopenharmony_ci		}
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	mutex_lock(&sch->reg_mutex);
93862306a36Sopenharmony_ci	rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
93962306a36Sopenharmony_ci	mutex_unlock(&sch->reg_mutex);
94062306a36Sopenharmony_ci	if (rc) {
94162306a36Sopenharmony_ci		CIO_MSG_EVENT(0, "device_move(0.%x.%04x,0.%x.%04x)=%d\n",
94262306a36Sopenharmony_ci			      cdev->private->dev_id.ssid,
94362306a36Sopenharmony_ci			      cdev->private->dev_id.devno, sch->schid.ssid,
94462306a36Sopenharmony_ci			      sch->schib.pmcw.dev, rc);
94562306a36Sopenharmony_ci		if (old_enabled) {
94662306a36Sopenharmony_ci			/* Try to re-enable the old subchannel. */
94762306a36Sopenharmony_ci			spin_lock_irq(old_sch->lock);
94862306a36Sopenharmony_ci			cio_enable_subchannel(old_sch, (u32)virt_to_phys(old_sch));
94962306a36Sopenharmony_ci			spin_unlock_irq(old_sch->lock);
95062306a36Sopenharmony_ci		}
95162306a36Sopenharmony_ci		/* Release child reference for new parent. */
95262306a36Sopenharmony_ci		put_device(&sch->dev);
95362306a36Sopenharmony_ci		return rc;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci	/* Clean up old subchannel. */
95662306a36Sopenharmony_ci	if (!sch_is_pseudo_sch(old_sch)) {
95762306a36Sopenharmony_ci		spin_lock_irq(old_sch->lock);
95862306a36Sopenharmony_ci		sch_set_cdev(old_sch, NULL);
95962306a36Sopenharmony_ci		spin_unlock_irq(old_sch->lock);
96062306a36Sopenharmony_ci		css_schedule_eval(old_sch->schid);
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci	/* Release child reference for old parent. */
96362306a36Sopenharmony_ci	put_device(&old_sch->dev);
96462306a36Sopenharmony_ci	/* Initialize new subchannel. */
96562306a36Sopenharmony_ci	spin_lock_irq(sch->lock);
96662306a36Sopenharmony_ci	cdev->ccwlock = sch->lock;
96762306a36Sopenharmony_ci	if (!sch_is_pseudo_sch(sch))
96862306a36Sopenharmony_ci		sch_set_cdev(sch, cdev);
96962306a36Sopenharmony_ci	spin_unlock_irq(sch->lock);
97062306a36Sopenharmony_ci	if (!sch_is_pseudo_sch(sch))
97162306a36Sopenharmony_ci		css_update_ssd_info(sch);
97262306a36Sopenharmony_ci	return 0;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic int ccw_device_move_to_orph(struct ccw_device *cdev)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
97862306a36Sopenharmony_ci	struct channel_subsystem *css = to_css(sch->dev.parent);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	return ccw_device_move_to_sch(cdev, css->pseudo_subchannel);
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic void io_subchannel_irq(struct subchannel *sch)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	struct ccw_device *cdev;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	cdev = sch_get_cdev(sch);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	CIO_TRACE_EVENT(6, "IRQ");
99062306a36Sopenharmony_ci	CIO_TRACE_EVENT(6, dev_name(&sch->dev));
99162306a36Sopenharmony_ci	if (cdev)
99262306a36Sopenharmony_ci		dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
99362306a36Sopenharmony_ci	else
99462306a36Sopenharmony_ci		inc_irq_stat(IRQIO_CIO);
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_civoid io_subchannel_init_config(struct subchannel *sch)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	memset(&sch->config, 0, sizeof(sch->config));
100062306a36Sopenharmony_ci	sch->config.csense = 1;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic void io_subchannel_init_fields(struct subchannel *sch)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	if (cio_is_console(sch->schid))
100662306a36Sopenharmony_ci		sch->opm = 0xff;
100762306a36Sopenharmony_ci	else
100862306a36Sopenharmony_ci		sch->opm = chp_get_sch_opm(sch);
100962306a36Sopenharmony_ci	sch->lpm = sch->schib.pmcw.pam & sch->opm;
101062306a36Sopenharmony_ci	sch->isc = cio_is_console(sch->schid) ? CONSOLE_ISC : IO_SCH_ISC;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X"
101362306a36Sopenharmony_ci		      " - PIM = %02X, PAM = %02X, POM = %02X\n",
101462306a36Sopenharmony_ci		      sch->schib.pmcw.dev, sch->schid.ssid,
101562306a36Sopenharmony_ci		      sch->schid.sch_no, sch->schib.pmcw.pim,
101662306a36Sopenharmony_ci		      sch->schib.pmcw.pam, sch->schib.pmcw.pom);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	io_subchannel_init_config(sch);
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci/*
102262306a36Sopenharmony_ci * Note: We always return 0 so that we bind to the device even on error.
102362306a36Sopenharmony_ci * This is needed so that our remove function is called on unregister.
102462306a36Sopenharmony_ci */
102562306a36Sopenharmony_cistatic int io_subchannel_probe(struct subchannel *sch)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	struct io_subchannel_private *io_priv;
102862306a36Sopenharmony_ci	struct ccw_device *cdev;
102962306a36Sopenharmony_ci	int rc;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	if (cio_is_console(sch->schid)) {
103262306a36Sopenharmony_ci		rc = sysfs_create_group(&sch->dev.kobj,
103362306a36Sopenharmony_ci					&io_subchannel_attr_group);
103462306a36Sopenharmony_ci		if (rc)
103562306a36Sopenharmony_ci			CIO_MSG_EVENT(0, "Failed to create io subchannel "
103662306a36Sopenharmony_ci				      "attributes for subchannel "
103762306a36Sopenharmony_ci				      "0.%x.%04x (rc=%d)\n",
103862306a36Sopenharmony_ci				      sch->schid.ssid, sch->schid.sch_no, rc);
103962306a36Sopenharmony_ci		/*
104062306a36Sopenharmony_ci		* The console subchannel already has an associated ccw_device.
104162306a36Sopenharmony_ci		* Register it and exit.
104262306a36Sopenharmony_ci		*/
104362306a36Sopenharmony_ci		cdev = sch_get_cdev(sch);
104462306a36Sopenharmony_ci		rc = device_add(&cdev->dev);
104562306a36Sopenharmony_ci		if (rc) {
104662306a36Sopenharmony_ci			/* Release online reference. */
104762306a36Sopenharmony_ci			put_device(&cdev->dev);
104862306a36Sopenharmony_ci			goto out_schedule;
104962306a36Sopenharmony_ci		}
105062306a36Sopenharmony_ci		if (atomic_dec_and_test(&ccw_device_init_count))
105162306a36Sopenharmony_ci			wake_up(&ccw_device_init_wq);
105262306a36Sopenharmony_ci		return 0;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci	io_subchannel_init_fields(sch);
105562306a36Sopenharmony_ci	rc = cio_commit_config(sch);
105662306a36Sopenharmony_ci	if (rc)
105762306a36Sopenharmony_ci		goto out_schedule;
105862306a36Sopenharmony_ci	rc = sysfs_create_group(&sch->dev.kobj,
105962306a36Sopenharmony_ci				&io_subchannel_attr_group);
106062306a36Sopenharmony_ci	if (rc)
106162306a36Sopenharmony_ci		goto out_schedule;
106262306a36Sopenharmony_ci	/* Allocate I/O subchannel private data. */
106362306a36Sopenharmony_ci	io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA);
106462306a36Sopenharmony_ci	if (!io_priv)
106562306a36Sopenharmony_ci		goto out_schedule;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	io_priv->dma_area = dma_alloc_coherent(&sch->dev,
106862306a36Sopenharmony_ci				sizeof(*io_priv->dma_area),
106962306a36Sopenharmony_ci				&io_priv->dma_area_dma, GFP_KERNEL);
107062306a36Sopenharmony_ci	if (!io_priv->dma_area) {
107162306a36Sopenharmony_ci		kfree(io_priv);
107262306a36Sopenharmony_ci		goto out_schedule;
107362306a36Sopenharmony_ci	}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	set_io_private(sch, io_priv);
107662306a36Sopenharmony_ci	css_schedule_eval(sch->schid);
107762306a36Sopenharmony_ci	return 0;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ciout_schedule:
108062306a36Sopenharmony_ci	spin_lock_irq(sch->lock);
108162306a36Sopenharmony_ci	css_sched_sch_todo(sch, SCH_TODO_UNREG);
108262306a36Sopenharmony_ci	spin_unlock_irq(sch->lock);
108362306a36Sopenharmony_ci	return 0;
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic void io_subchannel_remove(struct subchannel *sch)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	struct io_subchannel_private *io_priv = to_io_private(sch);
108962306a36Sopenharmony_ci	struct ccw_device *cdev;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	cdev = sch_get_cdev(sch);
109262306a36Sopenharmony_ci	if (!cdev)
109362306a36Sopenharmony_ci		goto out_free;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	ccw_device_unregister(cdev);
109662306a36Sopenharmony_ci	spin_lock_irq(sch->lock);
109762306a36Sopenharmony_ci	sch_set_cdev(sch, NULL);
109862306a36Sopenharmony_ci	set_io_private(sch, NULL);
109962306a36Sopenharmony_ci	spin_unlock_irq(sch->lock);
110062306a36Sopenharmony_ciout_free:
110162306a36Sopenharmony_ci	dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
110262306a36Sopenharmony_ci			  io_priv->dma_area, io_priv->dma_area_dma);
110362306a36Sopenharmony_ci	kfree(io_priv);
110462306a36Sopenharmony_ci	sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic void io_subchannel_verify(struct subchannel *sch)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct ccw_device *cdev;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	cdev = sch_get_cdev(sch);
111262306a36Sopenharmony_ci	if (cdev)
111362306a36Sopenharmony_ci		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
111462306a36Sopenharmony_ci	else
111562306a36Sopenharmony_ci		css_schedule_eval(sch->schid);
111662306a36Sopenharmony_ci}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistatic void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
111962306a36Sopenharmony_ci{
112062306a36Sopenharmony_ci	struct ccw_device *cdev;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	cdev = sch_get_cdev(sch);
112362306a36Sopenharmony_ci	if (!cdev)
112462306a36Sopenharmony_ci		return;
112562306a36Sopenharmony_ci	if (cio_update_schib(sch))
112662306a36Sopenharmony_ci		goto err;
112762306a36Sopenharmony_ci	/* Check for I/O on path. */
112862306a36Sopenharmony_ci	if (scsw_actl(&sch->schib.scsw) == 0 || sch->schib.pmcw.lpum != mask)
112962306a36Sopenharmony_ci		goto out;
113062306a36Sopenharmony_ci	if (cdev->private->state == DEV_STATE_ONLINE) {
113162306a36Sopenharmony_ci		ccw_device_kill_io(cdev);
113262306a36Sopenharmony_ci		goto out;
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci	if (cio_clear(sch))
113562306a36Sopenharmony_ci		goto err;
113662306a36Sopenharmony_ciout:
113762306a36Sopenharmony_ci	/* Trigger path verification. */
113862306a36Sopenharmony_ci	dev_fsm_event(cdev, DEV_EVENT_VERIFY);
113962306a36Sopenharmony_ci	return;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cierr:
114262306a36Sopenharmony_ci	dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_cistatic int io_subchannel_chp_event(struct subchannel *sch,
114662306a36Sopenharmony_ci				   struct chp_link *link, int event)
114762306a36Sopenharmony_ci{
114862306a36Sopenharmony_ci	struct ccw_device *cdev = sch_get_cdev(sch);
114962306a36Sopenharmony_ci	int mask, chpid, valid_bit;
115062306a36Sopenharmony_ci	int path_event[8];
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	mask = chp_ssd_get_mask(&sch->ssd_info, link);
115362306a36Sopenharmony_ci	if (!mask)
115462306a36Sopenharmony_ci		return 0;
115562306a36Sopenharmony_ci	switch (event) {
115662306a36Sopenharmony_ci	case CHP_VARY_OFF:
115762306a36Sopenharmony_ci		sch->opm &= ~mask;
115862306a36Sopenharmony_ci		sch->lpm &= ~mask;
115962306a36Sopenharmony_ci		if (cdev)
116062306a36Sopenharmony_ci			cdev->private->path_gone_mask |= mask;
116162306a36Sopenharmony_ci		io_subchannel_terminate_path(sch, mask);
116262306a36Sopenharmony_ci		break;
116362306a36Sopenharmony_ci	case CHP_VARY_ON:
116462306a36Sopenharmony_ci		sch->opm |= mask;
116562306a36Sopenharmony_ci		sch->lpm |= mask;
116662306a36Sopenharmony_ci		if (cdev)
116762306a36Sopenharmony_ci			cdev->private->path_new_mask |= mask;
116862306a36Sopenharmony_ci		io_subchannel_verify(sch);
116962306a36Sopenharmony_ci		break;
117062306a36Sopenharmony_ci	case CHP_OFFLINE:
117162306a36Sopenharmony_ci		if (cio_update_schib(sch))
117262306a36Sopenharmony_ci			return -ENODEV;
117362306a36Sopenharmony_ci		if (cdev)
117462306a36Sopenharmony_ci			cdev->private->path_gone_mask |= mask;
117562306a36Sopenharmony_ci		io_subchannel_terminate_path(sch, mask);
117662306a36Sopenharmony_ci		break;
117762306a36Sopenharmony_ci	case CHP_ONLINE:
117862306a36Sopenharmony_ci		if (cio_update_schib(sch))
117962306a36Sopenharmony_ci			return -ENODEV;
118062306a36Sopenharmony_ci		sch->lpm |= mask & sch->opm;
118162306a36Sopenharmony_ci		if (cdev)
118262306a36Sopenharmony_ci			cdev->private->path_new_mask |= mask;
118362306a36Sopenharmony_ci		io_subchannel_verify(sch);
118462306a36Sopenharmony_ci		break;
118562306a36Sopenharmony_ci	case CHP_FCES_EVENT:
118662306a36Sopenharmony_ci		/* Forward Endpoint Security event */
118762306a36Sopenharmony_ci		for (chpid = 0, valid_bit = 0x80; chpid < 8; chpid++,
118862306a36Sopenharmony_ci				valid_bit >>= 1) {
118962306a36Sopenharmony_ci			if (mask & valid_bit)
119062306a36Sopenharmony_ci				path_event[chpid] = PE_PATH_FCES_EVENT;
119162306a36Sopenharmony_ci			else
119262306a36Sopenharmony_ci				path_event[chpid] = PE_NONE;
119362306a36Sopenharmony_ci		}
119462306a36Sopenharmony_ci		if (cdev && cdev->drv && cdev->drv->path_event)
119562306a36Sopenharmony_ci			cdev->drv->path_event(cdev, path_event);
119662306a36Sopenharmony_ci		break;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci	return 0;
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic void io_subchannel_quiesce(struct subchannel *sch)
120262306a36Sopenharmony_ci{
120362306a36Sopenharmony_ci	struct ccw_device *cdev;
120462306a36Sopenharmony_ci	int ret;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	spin_lock_irq(sch->lock);
120762306a36Sopenharmony_ci	cdev = sch_get_cdev(sch);
120862306a36Sopenharmony_ci	if (cio_is_console(sch->schid))
120962306a36Sopenharmony_ci		goto out_unlock;
121062306a36Sopenharmony_ci	if (!sch->schib.pmcw.ena)
121162306a36Sopenharmony_ci		goto out_unlock;
121262306a36Sopenharmony_ci	ret = cio_disable_subchannel(sch);
121362306a36Sopenharmony_ci	if (ret != -EBUSY)
121462306a36Sopenharmony_ci		goto out_unlock;
121562306a36Sopenharmony_ci	if (cdev->handler)
121662306a36Sopenharmony_ci		cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
121762306a36Sopenharmony_ci	while (ret == -EBUSY) {
121862306a36Sopenharmony_ci		cdev->private->state = DEV_STATE_QUIESCE;
121962306a36Sopenharmony_ci		cdev->private->iretry = 255;
122062306a36Sopenharmony_ci		ret = ccw_device_cancel_halt_clear(cdev);
122162306a36Sopenharmony_ci		if (ret == -EBUSY) {
122262306a36Sopenharmony_ci			ccw_device_set_timeout(cdev, HZ/10);
122362306a36Sopenharmony_ci			spin_unlock_irq(sch->lock);
122462306a36Sopenharmony_ci			wait_event(cdev->private->wait_q,
122562306a36Sopenharmony_ci				   cdev->private->state != DEV_STATE_QUIESCE);
122662306a36Sopenharmony_ci			spin_lock_irq(sch->lock);
122762306a36Sopenharmony_ci		}
122862306a36Sopenharmony_ci		ret = cio_disable_subchannel(sch);
122962306a36Sopenharmony_ci	}
123062306a36Sopenharmony_ciout_unlock:
123162306a36Sopenharmony_ci	spin_unlock_irq(sch->lock);
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic void io_subchannel_shutdown(struct subchannel *sch)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	io_subchannel_quiesce(sch);
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic int device_is_disconnected(struct ccw_device *cdev)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	if (!cdev)
124262306a36Sopenharmony_ci		return 0;
124362306a36Sopenharmony_ci	return (cdev->private->state == DEV_STATE_DISCONNECTED ||
124462306a36Sopenharmony_ci		cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
124562306a36Sopenharmony_ci}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cistatic int recovery_check(struct device *dev, void *data)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
125062306a36Sopenharmony_ci	struct subchannel *sch;
125162306a36Sopenharmony_ci	int *redo = data;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
125462306a36Sopenharmony_ci	switch (cdev->private->state) {
125562306a36Sopenharmony_ci	case DEV_STATE_ONLINE:
125662306a36Sopenharmony_ci		sch = to_subchannel(cdev->dev.parent);
125762306a36Sopenharmony_ci		if ((sch->schib.pmcw.pam & sch->opm) == sch->vpm)
125862306a36Sopenharmony_ci			break;
125962306a36Sopenharmony_ci		fallthrough;
126062306a36Sopenharmony_ci	case DEV_STATE_DISCONNECTED:
126162306a36Sopenharmony_ci		CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n",
126262306a36Sopenharmony_ci			      cdev->private->dev_id.ssid,
126362306a36Sopenharmony_ci			      cdev->private->dev_id.devno);
126462306a36Sopenharmony_ci		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
126562306a36Sopenharmony_ci		*redo = 1;
126662306a36Sopenharmony_ci		break;
126762306a36Sopenharmony_ci	case DEV_STATE_DISCONNECTED_SENSE_ID:
126862306a36Sopenharmony_ci		*redo = 1;
126962306a36Sopenharmony_ci		break;
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	return 0;
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic void recovery_work_func(struct work_struct *unused)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	int redo = 0;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
128162306a36Sopenharmony_ci	if (redo) {
128262306a36Sopenharmony_ci		spin_lock_irq(&recovery_lock);
128362306a36Sopenharmony_ci		if (!timer_pending(&recovery_timer)) {
128462306a36Sopenharmony_ci			if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
128562306a36Sopenharmony_ci				recovery_phase++;
128662306a36Sopenharmony_ci			mod_timer(&recovery_timer, jiffies +
128762306a36Sopenharmony_ci				  recovery_delay[recovery_phase] * HZ);
128862306a36Sopenharmony_ci		}
128962306a36Sopenharmony_ci		spin_unlock_irq(&recovery_lock);
129062306a36Sopenharmony_ci	} else
129162306a36Sopenharmony_ci		CIO_MSG_EVENT(3, "recovery: end\n");
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_cistatic DECLARE_WORK(recovery_work, recovery_work_func);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cistatic void recovery_func(struct timer_list *unused)
129762306a36Sopenharmony_ci{
129862306a36Sopenharmony_ci	/*
129962306a36Sopenharmony_ci	 * We can't do our recovery in softirq context and it's not
130062306a36Sopenharmony_ci	 * performance critical, so we schedule it.
130162306a36Sopenharmony_ci	 */
130262306a36Sopenharmony_ci	schedule_work(&recovery_work);
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_civoid ccw_device_schedule_recovery(void)
130662306a36Sopenharmony_ci{
130762306a36Sopenharmony_ci	unsigned long flags;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	CIO_MSG_EVENT(3, "recovery: schedule\n");
131062306a36Sopenharmony_ci	spin_lock_irqsave(&recovery_lock, flags);
131162306a36Sopenharmony_ci	if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
131262306a36Sopenharmony_ci		recovery_phase = 0;
131362306a36Sopenharmony_ci		mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci	spin_unlock_irqrestore(&recovery_lock, flags);
131662306a36Sopenharmony_ci}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_cistatic int purge_fn(struct device *dev, void *data)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
132162306a36Sopenharmony_ci	struct ccw_dev_id *id = &cdev->private->dev_id;
132262306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
132562306a36Sopenharmony_ci	if (is_blacklisted(id->ssid, id->devno) &&
132662306a36Sopenharmony_ci	    (cdev->private->state == DEV_STATE_OFFLINE) &&
132762306a36Sopenharmony_ci	    (atomic_cmpxchg(&cdev->private->onoff, 0, 1) == 0)) {
132862306a36Sopenharmony_ci		CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid,
132962306a36Sopenharmony_ci			      id->devno);
133062306a36Sopenharmony_ci		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
133162306a36Sopenharmony_ci		css_sched_sch_todo(sch, SCH_TODO_UNREG);
133262306a36Sopenharmony_ci		atomic_set(&cdev->private->onoff, 0);
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
133562306a36Sopenharmony_ci	/* Abort loop in case of pending signal. */
133662306a36Sopenharmony_ci	if (signal_pending(current))
133762306a36Sopenharmony_ci		return -EINTR;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	return 0;
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci/**
134362306a36Sopenharmony_ci * ccw_purge_blacklisted - purge unused, blacklisted devices
134462306a36Sopenharmony_ci *
134562306a36Sopenharmony_ci * Unregister all ccw devices that are offline and on the blacklist.
134662306a36Sopenharmony_ci */
134762306a36Sopenharmony_ciint ccw_purge_blacklisted(void)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	CIO_MSG_EVENT(2, "ccw: purging blacklisted devices\n");
135062306a36Sopenharmony_ci	bus_for_each_dev(&ccw_bus_type, NULL, NULL, purge_fn);
135162306a36Sopenharmony_ci	return 0;
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_civoid ccw_device_set_disconnected(struct ccw_device *cdev)
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	if (!cdev)
135762306a36Sopenharmony_ci		return;
135862306a36Sopenharmony_ci	ccw_device_set_timeout(cdev, 0);
135962306a36Sopenharmony_ci	cdev->private->flags.fake_irb = 0;
136062306a36Sopenharmony_ci	cdev->private->state = DEV_STATE_DISCONNECTED;
136162306a36Sopenharmony_ci	if (cdev->online)
136262306a36Sopenharmony_ci		ccw_device_schedule_recovery();
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_civoid ccw_device_set_notoper(struct ccw_device *cdev)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, "notoper");
137062306a36Sopenharmony_ci	CIO_TRACE_EVENT(2, dev_name(&sch->dev));
137162306a36Sopenharmony_ci	ccw_device_set_timeout(cdev, 0);
137262306a36Sopenharmony_ci	cio_disable_subchannel(sch);
137362306a36Sopenharmony_ci	cdev->private->state = DEV_STATE_NOT_OPER;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cienum io_sch_action {
137762306a36Sopenharmony_ci	IO_SCH_UNREG,
137862306a36Sopenharmony_ci	IO_SCH_ORPH_UNREG,
137962306a36Sopenharmony_ci	IO_SCH_UNREG_CDEV,
138062306a36Sopenharmony_ci	IO_SCH_ATTACH,
138162306a36Sopenharmony_ci	IO_SCH_UNREG_ATTACH,
138262306a36Sopenharmony_ci	IO_SCH_ORPH_ATTACH,
138362306a36Sopenharmony_ci	IO_SCH_REPROBE,
138462306a36Sopenharmony_ci	IO_SCH_VERIFY,
138562306a36Sopenharmony_ci	IO_SCH_DISC,
138662306a36Sopenharmony_ci	IO_SCH_NOP,
138762306a36Sopenharmony_ci};
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_cistatic enum io_sch_action sch_get_action(struct subchannel *sch)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	struct ccw_device *cdev;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	cdev = sch_get_cdev(sch);
139462306a36Sopenharmony_ci	if (cio_update_schib(sch)) {
139562306a36Sopenharmony_ci		/* Not operational. */
139662306a36Sopenharmony_ci		if (!cdev)
139762306a36Sopenharmony_ci			return IO_SCH_UNREG;
139862306a36Sopenharmony_ci		if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
139962306a36Sopenharmony_ci			return IO_SCH_UNREG;
140062306a36Sopenharmony_ci		return IO_SCH_ORPH_UNREG;
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci	/* Operational. */
140362306a36Sopenharmony_ci	if (!cdev)
140462306a36Sopenharmony_ci		return IO_SCH_ATTACH;
140562306a36Sopenharmony_ci	if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
140662306a36Sopenharmony_ci		if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
140762306a36Sopenharmony_ci			return IO_SCH_UNREG_ATTACH;
140862306a36Sopenharmony_ci		return IO_SCH_ORPH_ATTACH;
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci	if ((sch->schib.pmcw.pam & sch->opm) == 0) {
141162306a36Sopenharmony_ci		if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK)
141262306a36Sopenharmony_ci			return IO_SCH_UNREG_CDEV;
141362306a36Sopenharmony_ci		return IO_SCH_DISC;
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci	if (device_is_disconnected(cdev))
141662306a36Sopenharmony_ci		return IO_SCH_REPROBE;
141762306a36Sopenharmony_ci	if (cdev->online)
141862306a36Sopenharmony_ci		return IO_SCH_VERIFY;
141962306a36Sopenharmony_ci	if (cdev->private->state == DEV_STATE_NOT_OPER)
142062306a36Sopenharmony_ci		return IO_SCH_UNREG_ATTACH;
142162306a36Sopenharmony_ci	return IO_SCH_NOP;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci/**
142562306a36Sopenharmony_ci * io_subchannel_sch_event - process subchannel event
142662306a36Sopenharmony_ci * @sch: subchannel
142762306a36Sopenharmony_ci * @process: non-zero if function is called in process context
142862306a36Sopenharmony_ci *
142962306a36Sopenharmony_ci * An unspecified event occurred for this subchannel. Adjust data according
143062306a36Sopenharmony_ci * to the current operational state of the subchannel and device. Return
143162306a36Sopenharmony_ci * zero when the event has been handled sufficiently or -EAGAIN when this
143262306a36Sopenharmony_ci * function should be called again in process context.
143362306a36Sopenharmony_ci */
143462306a36Sopenharmony_cistatic int io_subchannel_sch_event(struct subchannel *sch, int process)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	unsigned long flags;
143762306a36Sopenharmony_ci	struct ccw_device *cdev;
143862306a36Sopenharmony_ci	struct ccw_dev_id dev_id;
143962306a36Sopenharmony_ci	enum io_sch_action action;
144062306a36Sopenharmony_ci	int rc = -EAGAIN;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	spin_lock_irqsave(sch->lock, flags);
144362306a36Sopenharmony_ci	if (!device_is_registered(&sch->dev))
144462306a36Sopenharmony_ci		goto out_unlock;
144562306a36Sopenharmony_ci	if (work_pending(&sch->todo_work))
144662306a36Sopenharmony_ci		goto out_unlock;
144762306a36Sopenharmony_ci	cdev = sch_get_cdev(sch);
144862306a36Sopenharmony_ci	if (cdev && work_pending(&cdev->private->todo_work))
144962306a36Sopenharmony_ci		goto out_unlock;
145062306a36Sopenharmony_ci	action = sch_get_action(sch);
145162306a36Sopenharmony_ci	CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n",
145262306a36Sopenharmony_ci		      sch->schid.ssid, sch->schid.sch_no, process,
145362306a36Sopenharmony_ci		      action);
145462306a36Sopenharmony_ci	/* Perform immediate actions while holding the lock. */
145562306a36Sopenharmony_ci	switch (action) {
145662306a36Sopenharmony_ci	case IO_SCH_REPROBE:
145762306a36Sopenharmony_ci		/* Trigger device recognition. */
145862306a36Sopenharmony_ci		ccw_device_trigger_reprobe(cdev);
145962306a36Sopenharmony_ci		rc = 0;
146062306a36Sopenharmony_ci		goto out_unlock;
146162306a36Sopenharmony_ci	case IO_SCH_VERIFY:
146262306a36Sopenharmony_ci		/* Trigger path verification. */
146362306a36Sopenharmony_ci		io_subchannel_verify(sch);
146462306a36Sopenharmony_ci		rc = 0;
146562306a36Sopenharmony_ci		goto out_unlock;
146662306a36Sopenharmony_ci	case IO_SCH_DISC:
146762306a36Sopenharmony_ci		ccw_device_set_disconnected(cdev);
146862306a36Sopenharmony_ci		rc = 0;
146962306a36Sopenharmony_ci		goto out_unlock;
147062306a36Sopenharmony_ci	case IO_SCH_ORPH_UNREG:
147162306a36Sopenharmony_ci	case IO_SCH_ORPH_ATTACH:
147262306a36Sopenharmony_ci		ccw_device_set_disconnected(cdev);
147362306a36Sopenharmony_ci		break;
147462306a36Sopenharmony_ci	case IO_SCH_UNREG_CDEV:
147562306a36Sopenharmony_ci	case IO_SCH_UNREG_ATTACH:
147662306a36Sopenharmony_ci	case IO_SCH_UNREG:
147762306a36Sopenharmony_ci		if (!cdev)
147862306a36Sopenharmony_ci			break;
147962306a36Sopenharmony_ci		if (cdev->private->state == DEV_STATE_SENSE_ID) {
148062306a36Sopenharmony_ci			/*
148162306a36Sopenharmony_ci			 * Note: delayed work triggered by this event
148262306a36Sopenharmony_ci			 * and repeated calls to sch_event are synchronized
148362306a36Sopenharmony_ci			 * by the above check for work_pending(cdev).
148462306a36Sopenharmony_ci			 */
148562306a36Sopenharmony_ci			dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
148662306a36Sopenharmony_ci		} else
148762306a36Sopenharmony_ci			ccw_device_set_notoper(cdev);
148862306a36Sopenharmony_ci		break;
148962306a36Sopenharmony_ci	case IO_SCH_NOP:
149062306a36Sopenharmony_ci		rc = 0;
149162306a36Sopenharmony_ci		goto out_unlock;
149262306a36Sopenharmony_ci	default:
149362306a36Sopenharmony_ci		break;
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci	spin_unlock_irqrestore(sch->lock, flags);
149662306a36Sopenharmony_ci	/* All other actions require process context. */
149762306a36Sopenharmony_ci	if (!process)
149862306a36Sopenharmony_ci		goto out;
149962306a36Sopenharmony_ci	/* Handle attached ccw device. */
150062306a36Sopenharmony_ci	switch (action) {
150162306a36Sopenharmony_ci	case IO_SCH_ORPH_UNREG:
150262306a36Sopenharmony_ci	case IO_SCH_ORPH_ATTACH:
150362306a36Sopenharmony_ci		/* Move ccw device to orphanage. */
150462306a36Sopenharmony_ci		rc = ccw_device_move_to_orph(cdev);
150562306a36Sopenharmony_ci		if (rc)
150662306a36Sopenharmony_ci			goto out;
150762306a36Sopenharmony_ci		break;
150862306a36Sopenharmony_ci	case IO_SCH_UNREG_CDEV:
150962306a36Sopenharmony_ci	case IO_SCH_UNREG_ATTACH:
151062306a36Sopenharmony_ci		spin_lock_irqsave(sch->lock, flags);
151162306a36Sopenharmony_ci		sch_set_cdev(sch, NULL);
151262306a36Sopenharmony_ci		spin_unlock_irqrestore(sch->lock, flags);
151362306a36Sopenharmony_ci		/* Unregister ccw device. */
151462306a36Sopenharmony_ci		ccw_device_unregister(cdev);
151562306a36Sopenharmony_ci		break;
151662306a36Sopenharmony_ci	default:
151762306a36Sopenharmony_ci		break;
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci	/* Handle subchannel. */
152062306a36Sopenharmony_ci	switch (action) {
152162306a36Sopenharmony_ci	case IO_SCH_ORPH_UNREG:
152262306a36Sopenharmony_ci	case IO_SCH_UNREG:
152362306a36Sopenharmony_ci		css_sch_device_unregister(sch);
152462306a36Sopenharmony_ci		break;
152562306a36Sopenharmony_ci	case IO_SCH_ORPH_ATTACH:
152662306a36Sopenharmony_ci	case IO_SCH_UNREG_ATTACH:
152762306a36Sopenharmony_ci	case IO_SCH_ATTACH:
152862306a36Sopenharmony_ci		dev_id.ssid = sch->schid.ssid;
152962306a36Sopenharmony_ci		dev_id.devno = sch->schib.pmcw.dev;
153062306a36Sopenharmony_ci		cdev = get_ccwdev_by_dev_id(&dev_id);
153162306a36Sopenharmony_ci		if (!cdev) {
153262306a36Sopenharmony_ci			sch_create_and_recog_new_device(sch);
153362306a36Sopenharmony_ci			break;
153462306a36Sopenharmony_ci		}
153562306a36Sopenharmony_ci		rc = ccw_device_move_to_sch(cdev, sch);
153662306a36Sopenharmony_ci		if (rc) {
153762306a36Sopenharmony_ci			/* Release reference from get_ccwdev_by_dev_id() */
153862306a36Sopenharmony_ci			put_device(&cdev->dev);
153962306a36Sopenharmony_ci			goto out;
154062306a36Sopenharmony_ci		}
154162306a36Sopenharmony_ci		spin_lock_irqsave(sch->lock, flags);
154262306a36Sopenharmony_ci		ccw_device_trigger_reprobe(cdev);
154362306a36Sopenharmony_ci		spin_unlock_irqrestore(sch->lock, flags);
154462306a36Sopenharmony_ci		/* Release reference from get_ccwdev_by_dev_id() */
154562306a36Sopenharmony_ci		put_device(&cdev->dev);
154662306a36Sopenharmony_ci		break;
154762306a36Sopenharmony_ci	default:
154862306a36Sopenharmony_ci		break;
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci	return 0;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ciout_unlock:
155362306a36Sopenharmony_ci	spin_unlock_irqrestore(sch->lock, flags);
155462306a36Sopenharmony_ciout:
155562306a36Sopenharmony_ci	return rc;
155662306a36Sopenharmony_ci}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_cistatic void ccw_device_set_int_class(struct ccw_device *cdev)
155962306a36Sopenharmony_ci{
156062306a36Sopenharmony_ci	struct ccw_driver *cdrv = cdev->drv;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	/* Note: we interpret class 0 in this context as an uninitialized
156362306a36Sopenharmony_ci	 * field since it translates to a non-I/O interrupt class. */
156462306a36Sopenharmony_ci	if (cdrv->int_class != 0)
156562306a36Sopenharmony_ci		cdev->private->int_class = cdrv->int_class;
156662306a36Sopenharmony_ci	else
156762306a36Sopenharmony_ci		cdev->private->int_class = IRQIO_CIO;
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci#ifdef CONFIG_CCW_CONSOLE
157162306a36Sopenharmony_ciint __init ccw_device_enable_console(struct ccw_device *cdev)
157262306a36Sopenharmony_ci{
157362306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
157462306a36Sopenharmony_ci	int rc;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	if (!cdev->drv || !cdev->handler)
157762306a36Sopenharmony_ci		return -EINVAL;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	io_subchannel_init_fields(sch);
158062306a36Sopenharmony_ci	rc = cio_commit_config(sch);
158162306a36Sopenharmony_ci	if (rc)
158262306a36Sopenharmony_ci		return rc;
158362306a36Sopenharmony_ci	sch->driver = &io_subchannel_driver;
158462306a36Sopenharmony_ci	io_subchannel_recog(cdev, sch);
158562306a36Sopenharmony_ci	/* Now wait for the async. recognition to come to an end. */
158662306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
158762306a36Sopenharmony_ci	while (!dev_fsm_final_state(cdev))
158862306a36Sopenharmony_ci		ccw_device_wait_idle(cdev);
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	/* Hold on to an extra reference while device is online. */
159162306a36Sopenharmony_ci	get_device(&cdev->dev);
159262306a36Sopenharmony_ci	rc = ccw_device_online(cdev);
159362306a36Sopenharmony_ci	if (rc)
159462306a36Sopenharmony_ci		goto out_unlock;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	while (!dev_fsm_final_state(cdev))
159762306a36Sopenharmony_ci		ccw_device_wait_idle(cdev);
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	if (cdev->private->state == DEV_STATE_ONLINE)
160062306a36Sopenharmony_ci		cdev->online = 1;
160162306a36Sopenharmony_ci	else
160262306a36Sopenharmony_ci		rc = -EIO;
160362306a36Sopenharmony_ciout_unlock:
160462306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
160562306a36Sopenharmony_ci	if (rc) /* Give up online reference since onlining failed. */
160662306a36Sopenharmony_ci		put_device(&cdev->dev);
160762306a36Sopenharmony_ci	return rc;
160862306a36Sopenharmony_ci}
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_cistruct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv)
161162306a36Sopenharmony_ci{
161262306a36Sopenharmony_ci	struct io_subchannel_private *io_priv;
161362306a36Sopenharmony_ci	struct ccw_device *cdev;
161462306a36Sopenharmony_ci	struct subchannel *sch;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	sch = cio_probe_console();
161762306a36Sopenharmony_ci	if (IS_ERR(sch))
161862306a36Sopenharmony_ci		return ERR_CAST(sch);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	io_priv = kzalloc(sizeof(*io_priv), GFP_KERNEL | GFP_DMA);
162162306a36Sopenharmony_ci	if (!io_priv)
162262306a36Sopenharmony_ci		goto err_priv;
162362306a36Sopenharmony_ci	io_priv->dma_area = dma_alloc_coherent(&sch->dev,
162462306a36Sopenharmony_ci				sizeof(*io_priv->dma_area),
162562306a36Sopenharmony_ci				&io_priv->dma_area_dma, GFP_KERNEL);
162662306a36Sopenharmony_ci	if (!io_priv->dma_area)
162762306a36Sopenharmony_ci		goto err_dma_area;
162862306a36Sopenharmony_ci	set_io_private(sch, io_priv);
162962306a36Sopenharmony_ci	cdev = io_subchannel_create_ccwdev(sch);
163062306a36Sopenharmony_ci	if (IS_ERR(cdev)) {
163162306a36Sopenharmony_ci		dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
163262306a36Sopenharmony_ci				  io_priv->dma_area, io_priv->dma_area_dma);
163362306a36Sopenharmony_ci		set_io_private(sch, NULL);
163462306a36Sopenharmony_ci		put_device(&sch->dev);
163562306a36Sopenharmony_ci		kfree(io_priv);
163662306a36Sopenharmony_ci		return cdev;
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci	cdev->drv = drv;
163962306a36Sopenharmony_ci	ccw_device_set_int_class(cdev);
164062306a36Sopenharmony_ci	return cdev;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_cierr_dma_area:
164362306a36Sopenharmony_ci	kfree(io_priv);
164462306a36Sopenharmony_cierr_priv:
164562306a36Sopenharmony_ci	put_device(&sch->dev);
164662306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
164762306a36Sopenharmony_ci}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_civoid __init ccw_device_destroy_console(struct ccw_device *cdev)
165062306a36Sopenharmony_ci{
165162306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
165262306a36Sopenharmony_ci	struct io_subchannel_private *io_priv = to_io_private(sch);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	set_io_private(sch, NULL);
165562306a36Sopenharmony_ci	dma_free_coherent(&sch->dev, sizeof(*io_priv->dma_area),
165662306a36Sopenharmony_ci			  io_priv->dma_area, io_priv->dma_area_dma);
165762306a36Sopenharmony_ci	put_device(&sch->dev);
165862306a36Sopenharmony_ci	put_device(&cdev->dev);
165962306a36Sopenharmony_ci	kfree(io_priv);
166062306a36Sopenharmony_ci}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci/**
166362306a36Sopenharmony_ci * ccw_device_wait_idle() - busy wait for device to become idle
166462306a36Sopenharmony_ci * @cdev: ccw device
166562306a36Sopenharmony_ci *
166662306a36Sopenharmony_ci * Poll until activity control is zero, that is, no function or data
166762306a36Sopenharmony_ci * transfer is pending/active.
166862306a36Sopenharmony_ci * Called with device lock being held.
166962306a36Sopenharmony_ci */
167062306a36Sopenharmony_civoid ccw_device_wait_idle(struct ccw_device *cdev)
167162306a36Sopenharmony_ci{
167262306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	while (1) {
167562306a36Sopenharmony_ci		cio_tsch(sch);
167662306a36Sopenharmony_ci		if (sch->schib.scsw.cmd.actl == 0)
167762306a36Sopenharmony_ci			break;
167862306a36Sopenharmony_ci		udelay(100);
167962306a36Sopenharmony_ci	}
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ci#endif
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci/**
168462306a36Sopenharmony_ci * get_ccwdev_by_busid() - obtain device from a bus id
168562306a36Sopenharmony_ci * @cdrv: driver the device is owned by
168662306a36Sopenharmony_ci * @bus_id: bus id of the device to be searched
168762306a36Sopenharmony_ci *
168862306a36Sopenharmony_ci * This function searches all devices owned by @cdrv for a device with a bus
168962306a36Sopenharmony_ci * id matching @bus_id.
169062306a36Sopenharmony_ci * Returns:
169162306a36Sopenharmony_ci *  If a match is found, its reference count of the found device is increased
169262306a36Sopenharmony_ci *  and it is returned; else %NULL is returned.
169362306a36Sopenharmony_ci */
169462306a36Sopenharmony_cistruct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv,
169562306a36Sopenharmony_ci				       const char *bus_id)
169662306a36Sopenharmony_ci{
169762306a36Sopenharmony_ci	struct device *dev;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	dev = driver_find_device_by_name(&cdrv->driver, bus_id);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	return dev ? to_ccwdev(dev) : NULL;
170262306a36Sopenharmony_ci}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci/************************** device driver handling ************************/
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci/* This is the implementation of the ccw_driver class. The probe, remove
170762306a36Sopenharmony_ci * and release methods are initially very similar to the device_driver
170862306a36Sopenharmony_ci * implementations, with the difference that they have ccw_device
170962306a36Sopenharmony_ci * arguments.
171062306a36Sopenharmony_ci *
171162306a36Sopenharmony_ci * A ccw driver also contains the information that is needed for
171262306a36Sopenharmony_ci * device matching.
171362306a36Sopenharmony_ci */
171462306a36Sopenharmony_cistatic int
171562306a36Sopenharmony_ciccw_device_probe (struct device *dev)
171662306a36Sopenharmony_ci{
171762306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
171862306a36Sopenharmony_ci	struct ccw_driver *cdrv = to_ccwdrv(dev->driver);
171962306a36Sopenharmony_ci	int ret;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	cdev->drv = cdrv; /* to let the driver call _set_online */
172262306a36Sopenharmony_ci	ccw_device_set_int_class(cdev);
172362306a36Sopenharmony_ci	ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV;
172462306a36Sopenharmony_ci	if (ret) {
172562306a36Sopenharmony_ci		cdev->drv = NULL;
172662306a36Sopenharmony_ci		cdev->private->int_class = IRQIO_CIO;
172762306a36Sopenharmony_ci		return ret;
172862306a36Sopenharmony_ci	}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	return 0;
173162306a36Sopenharmony_ci}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_cistatic void ccw_device_remove(struct device *dev)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
173662306a36Sopenharmony_ci	struct ccw_driver *cdrv = cdev->drv;
173762306a36Sopenharmony_ci	struct subchannel *sch;
173862306a36Sopenharmony_ci	int ret;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	if (cdrv->remove)
174162306a36Sopenharmony_ci		cdrv->remove(cdev);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
174462306a36Sopenharmony_ci	if (cdev->online) {
174562306a36Sopenharmony_ci		cdev->online = 0;
174662306a36Sopenharmony_ci		ret = ccw_device_offline(cdev);
174762306a36Sopenharmony_ci		spin_unlock_irq(cdev->ccwlock);
174862306a36Sopenharmony_ci		if (ret == 0)
174962306a36Sopenharmony_ci			wait_event(cdev->private->wait_q,
175062306a36Sopenharmony_ci				   dev_fsm_final_state(cdev));
175162306a36Sopenharmony_ci		else
175262306a36Sopenharmony_ci			CIO_MSG_EVENT(0, "ccw_device_offline returned %d, "
175362306a36Sopenharmony_ci				      "device 0.%x.%04x\n",
175462306a36Sopenharmony_ci				      ret, cdev->private->dev_id.ssid,
175562306a36Sopenharmony_ci				      cdev->private->dev_id.devno);
175662306a36Sopenharmony_ci		/* Give up reference obtained in ccw_device_set_online(). */
175762306a36Sopenharmony_ci		put_device(&cdev->dev);
175862306a36Sopenharmony_ci		spin_lock_irq(cdev->ccwlock);
175962306a36Sopenharmony_ci	}
176062306a36Sopenharmony_ci	ccw_device_set_timeout(cdev, 0);
176162306a36Sopenharmony_ci	cdev->drv = NULL;
176262306a36Sopenharmony_ci	cdev->private->int_class = IRQIO_CIO;
176362306a36Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
176462306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
176562306a36Sopenharmony_ci	io_subchannel_quiesce(sch);
176662306a36Sopenharmony_ci	__disable_cmf(cdev);
176762306a36Sopenharmony_ci}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_cistatic void ccw_device_shutdown(struct device *dev)
177062306a36Sopenharmony_ci{
177162306a36Sopenharmony_ci	struct ccw_device *cdev;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	cdev = to_ccwdev(dev);
177462306a36Sopenharmony_ci	if (cdev->drv && cdev->drv->shutdown)
177562306a36Sopenharmony_ci		cdev->drv->shutdown(cdev);
177662306a36Sopenharmony_ci	__disable_cmf(cdev);
177762306a36Sopenharmony_ci}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cistatic struct bus_type ccw_bus_type = {
178062306a36Sopenharmony_ci	.name   = "ccw",
178162306a36Sopenharmony_ci	.match  = ccw_bus_match,
178262306a36Sopenharmony_ci	.uevent = ccw_uevent,
178362306a36Sopenharmony_ci	.probe  = ccw_device_probe,
178462306a36Sopenharmony_ci	.remove = ccw_device_remove,
178562306a36Sopenharmony_ci	.shutdown = ccw_device_shutdown,
178662306a36Sopenharmony_ci};
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci/**
178962306a36Sopenharmony_ci * ccw_driver_register() - register a ccw driver
179062306a36Sopenharmony_ci * @cdriver: driver to be registered
179162306a36Sopenharmony_ci *
179262306a36Sopenharmony_ci * This function is mainly a wrapper around driver_register().
179362306a36Sopenharmony_ci * Returns:
179462306a36Sopenharmony_ci *   %0 on success and a negative error value on failure.
179562306a36Sopenharmony_ci */
179662306a36Sopenharmony_ciint ccw_driver_register(struct ccw_driver *cdriver)
179762306a36Sopenharmony_ci{
179862306a36Sopenharmony_ci	struct device_driver *drv = &cdriver->driver;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	drv->bus = &ccw_bus_type;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	return driver_register(drv);
180362306a36Sopenharmony_ci}
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci/**
180662306a36Sopenharmony_ci * ccw_driver_unregister() - deregister a ccw driver
180762306a36Sopenharmony_ci * @cdriver: driver to be deregistered
180862306a36Sopenharmony_ci *
180962306a36Sopenharmony_ci * This function is mainly a wrapper around driver_unregister().
181062306a36Sopenharmony_ci */
181162306a36Sopenharmony_civoid ccw_driver_unregister(struct ccw_driver *cdriver)
181262306a36Sopenharmony_ci{
181362306a36Sopenharmony_ci	driver_unregister(&cdriver->driver);
181462306a36Sopenharmony_ci}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_cistatic void ccw_device_todo(struct work_struct *work)
181762306a36Sopenharmony_ci{
181862306a36Sopenharmony_ci	struct ccw_device_private *priv;
181962306a36Sopenharmony_ci	struct ccw_device *cdev;
182062306a36Sopenharmony_ci	struct subchannel *sch;
182162306a36Sopenharmony_ci	enum cdev_todo todo;
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	priv = container_of(work, struct ccw_device_private, todo_work);
182462306a36Sopenharmony_ci	cdev = priv->cdev;
182562306a36Sopenharmony_ci	sch = to_subchannel(cdev->dev.parent);
182662306a36Sopenharmony_ci	/* Find out todo. */
182762306a36Sopenharmony_ci	spin_lock_irq(cdev->ccwlock);
182862306a36Sopenharmony_ci	todo = priv->todo;
182962306a36Sopenharmony_ci	priv->todo = CDEV_TODO_NOTHING;
183062306a36Sopenharmony_ci	CIO_MSG_EVENT(4, "cdev_todo: cdev=0.%x.%04x todo=%d\n",
183162306a36Sopenharmony_ci		      priv->dev_id.ssid, priv->dev_id.devno, todo);
183262306a36Sopenharmony_ci	spin_unlock_irq(cdev->ccwlock);
183362306a36Sopenharmony_ci	/* Perform todo. */
183462306a36Sopenharmony_ci	switch (todo) {
183562306a36Sopenharmony_ci	case CDEV_TODO_ENABLE_CMF:
183662306a36Sopenharmony_ci		cmf_reenable(cdev);
183762306a36Sopenharmony_ci		break;
183862306a36Sopenharmony_ci	case CDEV_TODO_REBIND:
183962306a36Sopenharmony_ci		ccw_device_do_unbind_bind(cdev);
184062306a36Sopenharmony_ci		break;
184162306a36Sopenharmony_ci	case CDEV_TODO_REGISTER:
184262306a36Sopenharmony_ci		io_subchannel_register(cdev);
184362306a36Sopenharmony_ci		break;
184462306a36Sopenharmony_ci	case CDEV_TODO_UNREG_EVAL:
184562306a36Sopenharmony_ci		if (!sch_is_pseudo_sch(sch))
184662306a36Sopenharmony_ci			css_schedule_eval(sch->schid);
184762306a36Sopenharmony_ci		fallthrough;
184862306a36Sopenharmony_ci	case CDEV_TODO_UNREG:
184962306a36Sopenharmony_ci		spin_lock_irq(sch->lock);
185062306a36Sopenharmony_ci		sch_set_cdev(sch, NULL);
185162306a36Sopenharmony_ci		spin_unlock_irq(sch->lock);
185262306a36Sopenharmony_ci		ccw_device_unregister(cdev);
185362306a36Sopenharmony_ci		break;
185462306a36Sopenharmony_ci	default:
185562306a36Sopenharmony_ci		break;
185662306a36Sopenharmony_ci	}
185762306a36Sopenharmony_ci	/* Release workqueue ref. */
185862306a36Sopenharmony_ci	put_device(&cdev->dev);
185962306a36Sopenharmony_ci}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci/**
186262306a36Sopenharmony_ci * ccw_device_sched_todo - schedule ccw device operation
186362306a36Sopenharmony_ci * @cdev: ccw device
186462306a36Sopenharmony_ci * @todo: todo
186562306a36Sopenharmony_ci *
186662306a36Sopenharmony_ci * Schedule the operation identified by @todo to be performed on the slow path
186762306a36Sopenharmony_ci * workqueue. Do nothing if another operation with higher priority is already
186862306a36Sopenharmony_ci * scheduled. Needs to be called with ccwdev lock held.
186962306a36Sopenharmony_ci */
187062306a36Sopenharmony_civoid ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo)
187162306a36Sopenharmony_ci{
187262306a36Sopenharmony_ci	CIO_MSG_EVENT(4, "cdev_todo: sched cdev=0.%x.%04x todo=%d\n",
187362306a36Sopenharmony_ci		      cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
187462306a36Sopenharmony_ci		      todo);
187562306a36Sopenharmony_ci	if (cdev->private->todo >= todo)
187662306a36Sopenharmony_ci		return;
187762306a36Sopenharmony_ci	cdev->private->todo = todo;
187862306a36Sopenharmony_ci	/* Get workqueue ref. */
187962306a36Sopenharmony_ci	if (!get_device(&cdev->dev))
188062306a36Sopenharmony_ci		return;
188162306a36Sopenharmony_ci	if (!queue_work(cio_work_q, &cdev->private->todo_work)) {
188262306a36Sopenharmony_ci		/* Already queued, release workqueue ref. */
188362306a36Sopenharmony_ci		put_device(&cdev->dev);
188462306a36Sopenharmony_ci	}
188562306a36Sopenharmony_ci}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci/**
188862306a36Sopenharmony_ci * ccw_device_siosl() - initiate logging
188962306a36Sopenharmony_ci * @cdev: ccw device
189062306a36Sopenharmony_ci *
189162306a36Sopenharmony_ci * This function is used to invoke model-dependent logging within the channel
189262306a36Sopenharmony_ci * subsystem.
189362306a36Sopenharmony_ci */
189462306a36Sopenharmony_ciint ccw_device_siosl(struct ccw_device *cdev)
189562306a36Sopenharmony_ci{
189662306a36Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	return chsc_siosl(sch->schid);
189962306a36Sopenharmony_ci}
190062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ccw_device_siosl);
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_set_online);
190362306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_device_set_offline);
190462306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_driver_register);
190562306a36Sopenharmony_ciEXPORT_SYMBOL(ccw_driver_unregister);
190662306a36Sopenharmony_ciEXPORT_SYMBOL(get_ccwdev_by_busid);
1907