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