18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for s390 eadm subchannels 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2012 68c2ecf20Sopenharmony_ci * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 108c2ecf20Sopenharmony_ci#include <linux/completion.h> 118c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/timer.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/css_chars.h> 208c2ecf20Sopenharmony_ci#include <asm/debug.h> 218c2ecf20Sopenharmony_ci#include <asm/isc.h> 228c2ecf20Sopenharmony_ci#include <asm/cio.h> 238c2ecf20Sopenharmony_ci#include <asm/scsw.h> 248c2ecf20Sopenharmony_ci#include <asm/eadm.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "eadm_sch.h" 278c2ecf20Sopenharmony_ci#include "ioasm.h" 288c2ecf20Sopenharmony_ci#include "cio.h" 298c2ecf20Sopenharmony_ci#include "css.h" 308c2ecf20Sopenharmony_ci#include "orb.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("driver for s390 eadm subchannels"); 338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define EADM_TIMEOUT (7 * HZ) 368c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(list_lock); 378c2ecf20Sopenharmony_cistatic LIST_HEAD(eadm_list); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic debug_info_t *eadm_debug; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define EADM_LOG(imp, txt) do { \ 428c2ecf20Sopenharmony_ci debug_text_event(eadm_debug, imp, txt); \ 438c2ecf20Sopenharmony_ci } while (0) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void EADM_LOG_HEX(int level, void *data, int length) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci debug_event(eadm_debug, level, data, length); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void orb_init(union orb *orb) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci memset(orb, 0, sizeof(union orb)); 538c2ecf20Sopenharmony_ci orb->eadm.compat1 = 1; 548c2ecf20Sopenharmony_ci orb->eadm.compat2 = 1; 558c2ecf20Sopenharmony_ci orb->eadm.fmt = 1; 568c2ecf20Sopenharmony_ci orb->eadm.x = 1; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int eadm_subchannel_start(struct subchannel *sch, struct aob *aob) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci union orb *orb = &get_eadm_private(sch)->orb; 628c2ecf20Sopenharmony_ci int cc; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci orb_init(orb); 658c2ecf20Sopenharmony_ci orb->eadm.aob = (u32)__pa(aob); 668c2ecf20Sopenharmony_ci orb->eadm.intparm = (u32)(addr_t)sch; 678c2ecf20Sopenharmony_ci orb->eadm.key = PAGE_DEFAULT_KEY >> 4; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci EADM_LOG(6, "start"); 708c2ecf20Sopenharmony_ci EADM_LOG_HEX(6, &sch->schid, sizeof(sch->schid)); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci cc = ssch(sch->schid, orb); 738c2ecf20Sopenharmony_ci switch (cc) { 748c2ecf20Sopenharmony_ci case 0: 758c2ecf20Sopenharmony_ci sch->schib.scsw.eadm.actl |= SCSW_ACTL_START_PEND; 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci case 1: /* status pending */ 788c2ecf20Sopenharmony_ci case 2: /* busy */ 798c2ecf20Sopenharmony_ci return -EBUSY; 808c2ecf20Sopenharmony_ci case 3: /* not operational */ 818c2ecf20Sopenharmony_ci return -ENODEV; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int eadm_subchannel_clear(struct subchannel *sch) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int cc; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci cc = csch(sch->schid); 918c2ecf20Sopenharmony_ci if (cc) 928c2ecf20Sopenharmony_ci return -ENODEV; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci sch->schib.scsw.eadm.actl |= SCSW_ACTL_CLEAR_PEND; 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void eadm_subchannel_timeout(struct timer_list *t) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct eadm_private *private = from_timer(private, t, timer); 1018c2ecf20Sopenharmony_ci struct subchannel *sch = private->sch; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci spin_lock_irq(sch->lock); 1048c2ecf20Sopenharmony_ci EADM_LOG(1, "timeout"); 1058c2ecf20Sopenharmony_ci EADM_LOG_HEX(1, &sch->schid, sizeof(sch->schid)); 1068c2ecf20Sopenharmony_ci if (eadm_subchannel_clear(sch)) 1078c2ecf20Sopenharmony_ci EADM_LOG(0, "clear failed"); 1088c2ecf20Sopenharmony_ci spin_unlock_irq(sch->lock); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void eadm_subchannel_set_timeout(struct subchannel *sch, int expires) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct eadm_private *private = get_eadm_private(sch); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (expires == 0) { 1168c2ecf20Sopenharmony_ci del_timer(&private->timer); 1178c2ecf20Sopenharmony_ci return; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci if (timer_pending(&private->timer)) { 1208c2ecf20Sopenharmony_ci if (mod_timer(&private->timer, jiffies + expires)) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci private->timer.expires = jiffies + expires; 1248c2ecf20Sopenharmony_ci add_timer(&private->timer); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void eadm_subchannel_irq(struct subchannel *sch) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct eadm_private *private = get_eadm_private(sch); 1308c2ecf20Sopenharmony_ci struct eadm_scsw *scsw = &sch->schib.scsw.eadm; 1318c2ecf20Sopenharmony_ci struct irb *irb = this_cpu_ptr(&cio_irb); 1328c2ecf20Sopenharmony_ci blk_status_t error = BLK_STS_OK; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci EADM_LOG(6, "irq"); 1358c2ecf20Sopenharmony_ci EADM_LOG_HEX(6, irb, sizeof(*irb)); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci inc_irq_stat(IRQIO_ADM); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if ((scsw->stctl & (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) 1408c2ecf20Sopenharmony_ci && scsw->eswf == 1 && irb->esw.eadm.erw.r) 1418c2ecf20Sopenharmony_ci error = BLK_STS_IOERR; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (scsw->fctl & SCSW_FCTL_CLEAR_FUNC) 1448c2ecf20Sopenharmony_ci error = BLK_STS_TIMEOUT; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci eadm_subchannel_set_timeout(sch, 0); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (private->state != EADM_BUSY) { 1498c2ecf20Sopenharmony_ci EADM_LOG(1, "irq unsol"); 1508c2ecf20Sopenharmony_ci EADM_LOG_HEX(1, irb, sizeof(*irb)); 1518c2ecf20Sopenharmony_ci private->state = EADM_NOT_OPER; 1528c2ecf20Sopenharmony_ci css_sched_sch_todo(sch, SCH_TODO_EVAL); 1538c2ecf20Sopenharmony_ci return; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error); 1568c2ecf20Sopenharmony_ci private->state = EADM_IDLE; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (private->completion) 1598c2ecf20Sopenharmony_ci complete(private->completion); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic struct subchannel *eadm_get_idle_sch(void) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct eadm_private *private; 1658c2ecf20Sopenharmony_ci struct subchannel *sch; 1668c2ecf20Sopenharmony_ci unsigned long flags; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spin_lock_irqsave(&list_lock, flags); 1698c2ecf20Sopenharmony_ci list_for_each_entry(private, &eadm_list, head) { 1708c2ecf20Sopenharmony_ci sch = private->sch; 1718c2ecf20Sopenharmony_ci spin_lock(sch->lock); 1728c2ecf20Sopenharmony_ci if (private->state == EADM_IDLE) { 1738c2ecf20Sopenharmony_ci private->state = EADM_BUSY; 1748c2ecf20Sopenharmony_ci list_move_tail(&private->head, &eadm_list); 1758c2ecf20Sopenharmony_ci spin_unlock(sch->lock); 1768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&list_lock, flags); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return sch; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci spin_unlock(sch->lock); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&list_lock, flags); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return NULL; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciint eadm_start_aob(struct aob *aob) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct eadm_private *private; 1908c2ecf20Sopenharmony_ci struct subchannel *sch; 1918c2ecf20Sopenharmony_ci unsigned long flags; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci sch = eadm_get_idle_sch(); 1958c2ecf20Sopenharmony_ci if (!sch) 1968c2ecf20Sopenharmony_ci return -EBUSY; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci spin_lock_irqsave(sch->lock, flags); 1998c2ecf20Sopenharmony_ci eadm_subchannel_set_timeout(sch, EADM_TIMEOUT); 2008c2ecf20Sopenharmony_ci ret = eadm_subchannel_start(sch, aob); 2018c2ecf20Sopenharmony_ci if (!ret) 2028c2ecf20Sopenharmony_ci goto out_unlock; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Handle start subchannel failure. */ 2058c2ecf20Sopenharmony_ci eadm_subchannel_set_timeout(sch, 0); 2068c2ecf20Sopenharmony_ci private = get_eadm_private(sch); 2078c2ecf20Sopenharmony_ci private->state = EADM_NOT_OPER; 2088c2ecf20Sopenharmony_ci css_sched_sch_todo(sch, SCH_TODO_EVAL); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciout_unlock: 2118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(sch->lock, flags); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return ret; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(eadm_start_aob); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int eadm_subchannel_probe(struct subchannel *sch) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct eadm_private *private; 2208c2ecf20Sopenharmony_ci int ret; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); 2238c2ecf20Sopenharmony_ci if (!private) 2248c2ecf20Sopenharmony_ci return -ENOMEM; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&private->head); 2278c2ecf20Sopenharmony_ci timer_setup(&private->timer, eadm_subchannel_timeout, 0); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci spin_lock_irq(sch->lock); 2308c2ecf20Sopenharmony_ci set_eadm_private(sch, private); 2318c2ecf20Sopenharmony_ci private->state = EADM_IDLE; 2328c2ecf20Sopenharmony_ci private->sch = sch; 2338c2ecf20Sopenharmony_ci sch->isc = EADM_SCH_ISC; 2348c2ecf20Sopenharmony_ci ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch); 2358c2ecf20Sopenharmony_ci if (ret) { 2368c2ecf20Sopenharmony_ci set_eadm_private(sch, NULL); 2378c2ecf20Sopenharmony_ci spin_unlock_irq(sch->lock); 2388c2ecf20Sopenharmony_ci kfree(private); 2398c2ecf20Sopenharmony_ci goto out; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci spin_unlock_irq(sch->lock); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci spin_lock_irq(&list_lock); 2448c2ecf20Sopenharmony_ci list_add(&private->head, &eadm_list); 2458c2ecf20Sopenharmony_ci spin_unlock_irq(&list_lock); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (dev_get_uevent_suppress(&sch->dev)) { 2488c2ecf20Sopenharmony_ci dev_set_uevent_suppress(&sch->dev, 0); 2498c2ecf20Sopenharmony_ci kobject_uevent(&sch->dev.kobj, KOBJ_ADD); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ciout: 2528c2ecf20Sopenharmony_ci return ret; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void eadm_quiesce(struct subchannel *sch) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct eadm_private *private = get_eadm_private(sch); 2588c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(completion); 2598c2ecf20Sopenharmony_ci int ret; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci spin_lock_irq(sch->lock); 2628c2ecf20Sopenharmony_ci if (private->state != EADM_BUSY) 2638c2ecf20Sopenharmony_ci goto disable; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (eadm_subchannel_clear(sch)) 2668c2ecf20Sopenharmony_ci goto disable; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci private->completion = &completion; 2698c2ecf20Sopenharmony_ci spin_unlock_irq(sch->lock); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci wait_for_completion_io(&completion); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci spin_lock_irq(sch->lock); 2748c2ecf20Sopenharmony_ci private->completion = NULL; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cidisable: 2778c2ecf20Sopenharmony_ci eadm_subchannel_set_timeout(sch, 0); 2788c2ecf20Sopenharmony_ci do { 2798c2ecf20Sopenharmony_ci ret = cio_disable_subchannel(sch); 2808c2ecf20Sopenharmony_ci } while (ret == -EBUSY); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci spin_unlock_irq(sch->lock); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int eadm_subchannel_remove(struct subchannel *sch) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct eadm_private *private = get_eadm_private(sch); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci spin_lock_irq(&list_lock); 2908c2ecf20Sopenharmony_ci list_del(&private->head); 2918c2ecf20Sopenharmony_ci spin_unlock_irq(&list_lock); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci eadm_quiesce(sch); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci spin_lock_irq(sch->lock); 2968c2ecf20Sopenharmony_ci set_eadm_private(sch, NULL); 2978c2ecf20Sopenharmony_ci spin_unlock_irq(sch->lock); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci kfree(private); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic void eadm_subchannel_shutdown(struct subchannel *sch) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci eadm_quiesce(sch); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int eadm_subchannel_freeze(struct subchannel *sch) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci return cio_disable_subchannel(sch); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int eadm_subchannel_restore(struct subchannel *sch) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci return cio_enable_subchannel(sch, (u32)(unsigned long)sch); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/** 3208c2ecf20Sopenharmony_ci * eadm_subchannel_sch_event - process subchannel event 3218c2ecf20Sopenharmony_ci * @sch: subchannel 3228c2ecf20Sopenharmony_ci * @process: non-zero if function is called in process context 3238c2ecf20Sopenharmony_ci * 3248c2ecf20Sopenharmony_ci * An unspecified event occurred for this subchannel. Adjust data according 3258c2ecf20Sopenharmony_ci * to the current operational state of the subchannel. Return zero when the 3268c2ecf20Sopenharmony_ci * event has been handled sufficiently or -EAGAIN when this function should 3278c2ecf20Sopenharmony_ci * be called again in process context. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_cistatic int eadm_subchannel_sch_event(struct subchannel *sch, int process) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct eadm_private *private; 3328c2ecf20Sopenharmony_ci unsigned long flags; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci spin_lock_irqsave(sch->lock, flags); 3358c2ecf20Sopenharmony_ci if (!device_is_registered(&sch->dev)) 3368c2ecf20Sopenharmony_ci goto out_unlock; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (work_pending(&sch->todo_work)) 3398c2ecf20Sopenharmony_ci goto out_unlock; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (cio_update_schib(sch)) { 3428c2ecf20Sopenharmony_ci css_sched_sch_todo(sch, SCH_TODO_UNREG); 3438c2ecf20Sopenharmony_ci goto out_unlock; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci private = get_eadm_private(sch); 3468c2ecf20Sopenharmony_ci if (private->state == EADM_NOT_OPER) 3478c2ecf20Sopenharmony_ci private->state = EADM_IDLE; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ciout_unlock: 3508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(sch->lock, flags); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic struct css_device_id eadm_subchannel_ids[] = { 3568c2ecf20Sopenharmony_ci { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_ADM, }, 3578c2ecf20Sopenharmony_ci { /* end of list */ }, 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(css, eadm_subchannel_ids); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic struct css_driver eadm_subchannel_driver = { 3628c2ecf20Sopenharmony_ci .drv = { 3638c2ecf20Sopenharmony_ci .name = "eadm_subchannel", 3648c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3658c2ecf20Sopenharmony_ci }, 3668c2ecf20Sopenharmony_ci .subchannel_type = eadm_subchannel_ids, 3678c2ecf20Sopenharmony_ci .irq = eadm_subchannel_irq, 3688c2ecf20Sopenharmony_ci .probe = eadm_subchannel_probe, 3698c2ecf20Sopenharmony_ci .remove = eadm_subchannel_remove, 3708c2ecf20Sopenharmony_ci .shutdown = eadm_subchannel_shutdown, 3718c2ecf20Sopenharmony_ci .sch_event = eadm_subchannel_sch_event, 3728c2ecf20Sopenharmony_ci .freeze = eadm_subchannel_freeze, 3738c2ecf20Sopenharmony_ci .thaw = eadm_subchannel_restore, 3748c2ecf20Sopenharmony_ci .restore = eadm_subchannel_restore, 3758c2ecf20Sopenharmony_ci}; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int __init eadm_sch_init(void) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci int ret; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!css_general_characteristics.eadm) 3828c2ecf20Sopenharmony_ci return -ENXIO; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci eadm_debug = debug_register("eadm_log", 16, 1, 16); 3858c2ecf20Sopenharmony_ci if (!eadm_debug) 3868c2ecf20Sopenharmony_ci return -ENOMEM; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci debug_register_view(eadm_debug, &debug_hex_ascii_view); 3898c2ecf20Sopenharmony_ci debug_set_level(eadm_debug, 2); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci isc_register(EADM_SCH_ISC); 3928c2ecf20Sopenharmony_ci ret = css_driver_register(&eadm_subchannel_driver); 3938c2ecf20Sopenharmony_ci if (ret) 3948c2ecf20Sopenharmony_ci goto cleanup; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return ret; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cicleanup: 3998c2ecf20Sopenharmony_ci isc_unregister(EADM_SCH_ISC); 4008c2ecf20Sopenharmony_ci debug_unregister(eadm_debug); 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic void __exit eadm_sch_exit(void) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci css_driver_unregister(&eadm_subchannel_driver); 4078c2ecf20Sopenharmony_ci isc_unregister(EADM_SCH_ISC); 4088c2ecf20Sopenharmony_ci debug_unregister(eadm_debug); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_cimodule_init(eadm_sch_init); 4118c2ecf20Sopenharmony_cimodule_exit(eadm_sch_exit); 412