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