1eadb86abSSebastian Ott /* 2eadb86abSSebastian Ott * Driver for s390 eadm subchannels 3eadb86abSSebastian Ott * 4eadb86abSSebastian Ott * Copyright IBM Corp. 2012 5eadb86abSSebastian Ott * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com> 6eadb86abSSebastian Ott */ 7eadb86abSSebastian Ott 82e73c2cfSSebastian Ott #include <linux/kernel_stat.h> 96aa2677aSSebastian Ott #include <linux/completion.h> 10eadb86abSSebastian Ott #include <linux/workqueue.h> 11eadb86abSSebastian Ott #include <linux/spinlock.h> 12eadb86abSSebastian Ott #include <linux/device.h> 13eadb86abSSebastian Ott #include <linux/module.h> 14eadb86abSSebastian Ott #include <linux/timer.h> 15eadb86abSSebastian Ott #include <linux/slab.h> 16eadb86abSSebastian Ott #include <linux/list.h> 17eadb86abSSebastian Ott 18eadb86abSSebastian Ott #include <asm/css_chars.h> 19eadb86abSSebastian Ott #include <asm/debug.h> 20eadb86abSSebastian Ott #include <asm/isc.h> 21eadb86abSSebastian Ott #include <asm/cio.h> 22eadb86abSSebastian Ott #include <asm/scsw.h> 23eadb86abSSebastian Ott #include <asm/eadm.h> 24eadb86abSSebastian Ott 25eadb86abSSebastian Ott #include "eadm_sch.h" 26eadb86abSSebastian Ott #include "ioasm.h" 27eadb86abSSebastian Ott #include "cio.h" 28eadb86abSSebastian Ott #include "css.h" 29eadb86abSSebastian Ott #include "orb.h" 30eadb86abSSebastian Ott 31eadb86abSSebastian Ott MODULE_DESCRIPTION("driver for s390 eadm subchannels"); 32eadb86abSSebastian Ott MODULE_LICENSE("GPL"); 33eadb86abSSebastian Ott 34eadb86abSSebastian Ott #define EADM_TIMEOUT (5 * HZ) 35eadb86abSSebastian Ott static DEFINE_SPINLOCK(list_lock); 36eadb86abSSebastian Ott static LIST_HEAD(eadm_list); 37eadb86abSSebastian Ott 38eadb86abSSebastian Ott static debug_info_t *eadm_debug; 39eadb86abSSebastian Ott 40eadb86abSSebastian Ott #define EADM_LOG(imp, txt) do { \ 41eadb86abSSebastian Ott debug_text_event(eadm_debug, imp, txt); \ 42eadb86abSSebastian Ott } while (0) 43eadb86abSSebastian Ott 44eadb86abSSebastian Ott static void EADM_LOG_HEX(int level, void *data, int length) 45eadb86abSSebastian Ott { 468e6a8285SHendrik Brueckner if (!debug_level_enabled(eadm_debug, level)) 47eadb86abSSebastian Ott return; 48eadb86abSSebastian Ott while (length > 0) { 49eadb86abSSebastian Ott debug_event(eadm_debug, level, data, length); 50eadb86abSSebastian Ott length -= eadm_debug->buf_size; 51eadb86abSSebastian Ott data += eadm_debug->buf_size; 52eadb86abSSebastian Ott } 53eadb86abSSebastian Ott } 54eadb86abSSebastian Ott 55eadb86abSSebastian Ott static void orb_init(union orb *orb) 56eadb86abSSebastian Ott { 57eadb86abSSebastian Ott memset(orb, 0, sizeof(union orb)); 58eadb86abSSebastian Ott orb->eadm.compat1 = 1; 59eadb86abSSebastian Ott orb->eadm.compat2 = 1; 60eadb86abSSebastian Ott orb->eadm.fmt = 1; 61eadb86abSSebastian Ott orb->eadm.x = 1; 62eadb86abSSebastian Ott } 63eadb86abSSebastian Ott 64eadb86abSSebastian Ott static int eadm_subchannel_start(struct subchannel *sch, struct aob *aob) 65eadb86abSSebastian Ott { 66eadb86abSSebastian Ott union orb *orb = &get_eadm_private(sch)->orb; 67eadb86abSSebastian Ott int cc; 68eadb86abSSebastian Ott 69eadb86abSSebastian Ott orb_init(orb); 70eadb86abSSebastian Ott orb->eadm.aob = (u32)__pa(aob); 71eadb86abSSebastian Ott orb->eadm.intparm = (u32)(addr_t)sch; 72eadb86abSSebastian Ott orb->eadm.key = PAGE_DEFAULT_KEY >> 4; 73eadb86abSSebastian Ott 74eadb86abSSebastian Ott EADM_LOG(6, "start"); 75eadb86abSSebastian Ott EADM_LOG_HEX(6, &sch->schid, sizeof(sch->schid)); 76eadb86abSSebastian Ott 77eadb86abSSebastian Ott cc = ssch(sch->schid, orb); 78eadb86abSSebastian Ott switch (cc) { 79eadb86abSSebastian Ott case 0: 80eadb86abSSebastian Ott sch->schib.scsw.eadm.actl |= SCSW_ACTL_START_PEND; 81eadb86abSSebastian Ott break; 82eadb86abSSebastian Ott case 1: /* status pending */ 83eadb86abSSebastian Ott case 2: /* busy */ 84eadb86abSSebastian Ott return -EBUSY; 85eadb86abSSebastian Ott case 3: /* not operational */ 86eadb86abSSebastian Ott return -ENODEV; 87eadb86abSSebastian Ott } 88eadb86abSSebastian Ott return 0; 89eadb86abSSebastian Ott } 90eadb86abSSebastian Ott 91eadb86abSSebastian Ott static int eadm_subchannel_clear(struct subchannel *sch) 92eadb86abSSebastian Ott { 93eadb86abSSebastian Ott int cc; 94eadb86abSSebastian Ott 95eadb86abSSebastian Ott cc = csch(sch->schid); 96eadb86abSSebastian Ott if (cc) 97eadb86abSSebastian Ott return -ENODEV; 98eadb86abSSebastian Ott 99eadb86abSSebastian Ott sch->schib.scsw.eadm.actl |= SCSW_ACTL_CLEAR_PEND; 100eadb86abSSebastian Ott return 0; 101eadb86abSSebastian Ott } 102eadb86abSSebastian Ott 103eadb86abSSebastian Ott static void eadm_subchannel_timeout(unsigned long data) 104eadb86abSSebastian Ott { 105eadb86abSSebastian Ott struct subchannel *sch = (struct subchannel *) data; 106eadb86abSSebastian Ott 107eadb86abSSebastian Ott spin_lock_irq(sch->lock); 108eadb86abSSebastian Ott EADM_LOG(1, "timeout"); 109eadb86abSSebastian Ott EADM_LOG_HEX(1, &sch->schid, sizeof(sch->schid)); 110eadb86abSSebastian Ott if (eadm_subchannel_clear(sch)) 111eadb86abSSebastian Ott EADM_LOG(0, "clear failed"); 112eadb86abSSebastian Ott spin_unlock_irq(sch->lock); 113eadb86abSSebastian Ott } 114eadb86abSSebastian Ott 115eadb86abSSebastian Ott static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires) 116eadb86abSSebastian Ott { 117eadb86abSSebastian Ott struct eadm_private *private = get_eadm_private(sch); 118eadb86abSSebastian Ott 119eadb86abSSebastian Ott if (expires == 0) { 120eadb86abSSebastian Ott del_timer(&private->timer); 121eadb86abSSebastian Ott return; 122eadb86abSSebastian Ott } 123eadb86abSSebastian Ott if (timer_pending(&private->timer)) { 124eadb86abSSebastian Ott if (mod_timer(&private->timer, jiffies + expires)) 125eadb86abSSebastian Ott return; 126eadb86abSSebastian Ott } 127eadb86abSSebastian Ott private->timer.function = eadm_subchannel_timeout; 128eadb86abSSebastian Ott private->timer.data = (unsigned long) sch; 129eadb86abSSebastian Ott private->timer.expires = jiffies + expires; 130eadb86abSSebastian Ott add_timer(&private->timer); 131eadb86abSSebastian Ott } 132eadb86abSSebastian Ott 133eadb86abSSebastian Ott static void eadm_subchannel_irq(struct subchannel *sch) 134eadb86abSSebastian Ott { 135eadb86abSSebastian Ott struct eadm_private *private = get_eadm_private(sch); 136eadb86abSSebastian Ott struct eadm_scsw *scsw = &sch->schib.scsw.eadm; 137*0bf7fcf1SChristoph Lameter struct irb *irb = this_cpu_ptr(&cio_irb); 138eadb86abSSebastian Ott int error = 0; 139eadb86abSSebastian Ott 140eadb86abSSebastian Ott EADM_LOG(6, "irq"); 141eadb86abSSebastian Ott EADM_LOG_HEX(6, irb, sizeof(*irb)); 142eadb86abSSebastian Ott 143420f42ecSHeiko Carstens inc_irq_stat(IRQIO_ADM); 1442e73c2cfSSebastian Ott 145eadb86abSSebastian Ott if ((scsw->stctl & (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) 146eadb86abSSebastian Ott && scsw->eswf == 1 && irb->esw.eadm.erw.r) 147eadb86abSSebastian Ott error = -EIO; 148eadb86abSSebastian Ott 149eadb86abSSebastian Ott if (scsw->fctl & SCSW_FCTL_CLEAR_FUNC) 150eadb86abSSebastian Ott error = -ETIMEDOUT; 151eadb86abSSebastian Ott 152eadb86abSSebastian Ott eadm_subchannel_set_timeout(sch, 0); 153eadb86abSSebastian Ott 154eadb86abSSebastian Ott if (private->state != EADM_BUSY) { 155eadb86abSSebastian Ott EADM_LOG(1, "irq unsol"); 156eadb86abSSebastian Ott EADM_LOG_HEX(1, irb, sizeof(*irb)); 157eadb86abSSebastian Ott private->state = EADM_NOT_OPER; 158eadb86abSSebastian Ott css_sched_sch_todo(sch, SCH_TODO_EVAL); 159eadb86abSSebastian Ott return; 160eadb86abSSebastian Ott } 161eadb86abSSebastian Ott scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error); 162eadb86abSSebastian Ott private->state = EADM_IDLE; 1636aa2677aSSebastian Ott 1646aa2677aSSebastian Ott if (private->completion) 1656aa2677aSSebastian Ott complete(private->completion); 166eadb86abSSebastian Ott } 167eadb86abSSebastian Ott 168eadb86abSSebastian Ott static struct subchannel *eadm_get_idle_sch(void) 169eadb86abSSebastian Ott { 170eadb86abSSebastian Ott struct eadm_private *private; 171eadb86abSSebastian Ott struct subchannel *sch; 172eadb86abSSebastian Ott unsigned long flags; 173eadb86abSSebastian Ott 174eadb86abSSebastian Ott spin_lock_irqsave(&list_lock, flags); 175eadb86abSSebastian Ott list_for_each_entry(private, &eadm_list, head) { 176eadb86abSSebastian Ott sch = private->sch; 177eadb86abSSebastian Ott spin_lock(sch->lock); 178eadb86abSSebastian Ott if (private->state == EADM_IDLE) { 179eadb86abSSebastian Ott private->state = EADM_BUSY; 180eadb86abSSebastian Ott list_move_tail(&private->head, &eadm_list); 181eadb86abSSebastian Ott spin_unlock(sch->lock); 182eadb86abSSebastian Ott spin_unlock_irqrestore(&list_lock, flags); 183eadb86abSSebastian Ott 184eadb86abSSebastian Ott return sch; 185eadb86abSSebastian Ott } 186eadb86abSSebastian Ott spin_unlock(sch->lock); 187eadb86abSSebastian Ott } 188eadb86abSSebastian Ott spin_unlock_irqrestore(&list_lock, flags); 189eadb86abSSebastian Ott 190eadb86abSSebastian Ott return NULL; 191eadb86abSSebastian Ott } 192eadb86abSSebastian Ott 193605c3698SSebastian Ott int eadm_start_aob(struct aob *aob) 194eadb86abSSebastian Ott { 195eadb86abSSebastian Ott struct eadm_private *private; 196eadb86abSSebastian Ott struct subchannel *sch; 197eadb86abSSebastian Ott unsigned long flags; 198eadb86abSSebastian Ott int ret; 199eadb86abSSebastian Ott 200eadb86abSSebastian Ott sch = eadm_get_idle_sch(); 201eadb86abSSebastian Ott if (!sch) 202eadb86abSSebastian Ott return -EBUSY; 203eadb86abSSebastian Ott 204eadb86abSSebastian Ott spin_lock_irqsave(sch->lock, flags); 205eadb86abSSebastian Ott eadm_subchannel_set_timeout(sch, EADM_TIMEOUT); 206eadb86abSSebastian Ott ret = eadm_subchannel_start(sch, aob); 207eadb86abSSebastian Ott if (!ret) 208eadb86abSSebastian Ott goto out_unlock; 209eadb86abSSebastian Ott 210eadb86abSSebastian Ott /* Handle start subchannel failure. */ 211eadb86abSSebastian Ott eadm_subchannel_set_timeout(sch, 0); 212eadb86abSSebastian Ott private = get_eadm_private(sch); 213eadb86abSSebastian Ott private->state = EADM_NOT_OPER; 214eadb86abSSebastian Ott css_sched_sch_todo(sch, SCH_TODO_EVAL); 215eadb86abSSebastian Ott 216eadb86abSSebastian Ott out_unlock: 217eadb86abSSebastian Ott spin_unlock_irqrestore(sch->lock, flags); 218eadb86abSSebastian Ott 219eadb86abSSebastian Ott return ret; 220eadb86abSSebastian Ott } 221605c3698SSebastian Ott EXPORT_SYMBOL_GPL(eadm_start_aob); 222eadb86abSSebastian Ott 223eadb86abSSebastian Ott static int eadm_subchannel_probe(struct subchannel *sch) 224eadb86abSSebastian Ott { 225eadb86abSSebastian Ott struct eadm_private *private; 226eadb86abSSebastian Ott int ret; 227eadb86abSSebastian Ott 228eadb86abSSebastian Ott private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA); 229eadb86abSSebastian Ott if (!private) 230eadb86abSSebastian Ott return -ENOMEM; 231eadb86abSSebastian Ott 232eadb86abSSebastian Ott INIT_LIST_HEAD(&private->head); 233eadb86abSSebastian Ott init_timer(&private->timer); 234eadb86abSSebastian Ott 235eadb86abSSebastian Ott spin_lock_irq(sch->lock); 236eadb86abSSebastian Ott set_eadm_private(sch, private); 237eadb86abSSebastian Ott private->state = EADM_IDLE; 238eadb86abSSebastian Ott private->sch = sch; 239eadb86abSSebastian Ott sch->isc = EADM_SCH_ISC; 240eadb86abSSebastian Ott ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch); 241eadb86abSSebastian Ott if (ret) { 242eadb86abSSebastian Ott set_eadm_private(sch, NULL); 243eadb86abSSebastian Ott spin_unlock_irq(sch->lock); 244eadb86abSSebastian Ott kfree(private); 245eadb86abSSebastian Ott goto out; 246eadb86abSSebastian Ott } 247eadb86abSSebastian Ott spin_unlock_irq(sch->lock); 248eadb86abSSebastian Ott 249eadb86abSSebastian Ott spin_lock_irq(&list_lock); 250eadb86abSSebastian Ott list_add(&private->head, &eadm_list); 251eadb86abSSebastian Ott spin_unlock_irq(&list_lock); 252eadb86abSSebastian Ott 253eadb86abSSebastian Ott if (dev_get_uevent_suppress(&sch->dev)) { 254eadb86abSSebastian Ott dev_set_uevent_suppress(&sch->dev, 0); 255eadb86abSSebastian Ott kobject_uevent(&sch->dev.kobj, KOBJ_ADD); 256eadb86abSSebastian Ott } 257eadb86abSSebastian Ott out: 258eadb86abSSebastian Ott return ret; 259eadb86abSSebastian Ott } 260eadb86abSSebastian Ott 261eadb86abSSebastian Ott static void eadm_quiesce(struct subchannel *sch) 262eadb86abSSebastian Ott { 2636aa2677aSSebastian Ott struct eadm_private *private = get_eadm_private(sch); 2646aa2677aSSebastian Ott DECLARE_COMPLETION_ONSTACK(completion); 265eadb86abSSebastian Ott int ret; 266eadb86abSSebastian Ott 267eadb86abSSebastian Ott spin_lock_irq(sch->lock); 2686aa2677aSSebastian Ott if (private->state != EADM_BUSY) 2696aa2677aSSebastian Ott goto disable; 2706aa2677aSSebastian Ott 2716aa2677aSSebastian Ott if (eadm_subchannel_clear(sch)) 2726aa2677aSSebastian Ott goto disable; 2736aa2677aSSebastian Ott 2746aa2677aSSebastian Ott private->completion = &completion; 275eadb86abSSebastian Ott spin_unlock_irq(sch->lock); 2766aa2677aSSebastian Ott 2776aa2677aSSebastian Ott wait_for_completion_io(&completion); 2786aa2677aSSebastian Ott 2796aa2677aSSebastian Ott spin_lock_irq(sch->lock); 2806aa2677aSSebastian Ott private->completion = NULL; 2816aa2677aSSebastian Ott 2826aa2677aSSebastian Ott disable: 2836aa2677aSSebastian Ott eadm_subchannel_set_timeout(sch, 0); 2846aa2677aSSebastian Ott do { 2856aa2677aSSebastian Ott ret = cio_disable_subchannel(sch); 286eadb86abSSebastian Ott } while (ret == -EBUSY); 2876aa2677aSSebastian Ott 2886aa2677aSSebastian Ott spin_unlock_irq(sch->lock); 289eadb86abSSebastian Ott } 290eadb86abSSebastian Ott 291eadb86abSSebastian Ott static int eadm_subchannel_remove(struct subchannel *sch) 292eadb86abSSebastian Ott { 293eadb86abSSebastian Ott struct eadm_private *private = get_eadm_private(sch); 294eadb86abSSebastian Ott 295eadb86abSSebastian Ott spin_lock_irq(&list_lock); 296eadb86abSSebastian Ott list_del(&private->head); 297eadb86abSSebastian Ott spin_unlock_irq(&list_lock); 298eadb86abSSebastian Ott 299eadb86abSSebastian Ott eadm_quiesce(sch); 300eadb86abSSebastian Ott 301eadb86abSSebastian Ott spin_lock_irq(sch->lock); 302eadb86abSSebastian Ott set_eadm_private(sch, NULL); 303eadb86abSSebastian Ott spin_unlock_irq(sch->lock); 304eadb86abSSebastian Ott 305eadb86abSSebastian Ott kfree(private); 306eadb86abSSebastian Ott 307eadb86abSSebastian Ott return 0; 308eadb86abSSebastian Ott } 309eadb86abSSebastian Ott 310eadb86abSSebastian Ott static void eadm_subchannel_shutdown(struct subchannel *sch) 311eadb86abSSebastian Ott { 312eadb86abSSebastian Ott eadm_quiesce(sch); 313eadb86abSSebastian Ott } 314eadb86abSSebastian Ott 315eadb86abSSebastian Ott static int eadm_subchannel_freeze(struct subchannel *sch) 316eadb86abSSebastian Ott { 317eadb86abSSebastian Ott return cio_disable_subchannel(sch); 318eadb86abSSebastian Ott } 319eadb86abSSebastian Ott 320eadb86abSSebastian Ott static int eadm_subchannel_restore(struct subchannel *sch) 321eadb86abSSebastian Ott { 322eadb86abSSebastian Ott return cio_enable_subchannel(sch, (u32)(unsigned long)sch); 323eadb86abSSebastian Ott } 324eadb86abSSebastian Ott 325eadb86abSSebastian Ott /** 326eadb86abSSebastian Ott * eadm_subchannel_sch_event - process subchannel event 327eadb86abSSebastian Ott * @sch: subchannel 328eadb86abSSebastian Ott * @process: non-zero if function is called in process context 329eadb86abSSebastian Ott * 330eadb86abSSebastian Ott * An unspecified event occurred for this subchannel. Adjust data according 331eadb86abSSebastian Ott * to the current operational state of the subchannel. Return zero when the 332eadb86abSSebastian Ott * event has been handled sufficiently or -EAGAIN when this function should 333eadb86abSSebastian Ott * be called again in process context. 334eadb86abSSebastian Ott */ 335eadb86abSSebastian Ott static int eadm_subchannel_sch_event(struct subchannel *sch, int process) 336eadb86abSSebastian Ott { 337eadb86abSSebastian Ott struct eadm_private *private; 338eadb86abSSebastian Ott unsigned long flags; 339eadb86abSSebastian Ott int ret = 0; 340eadb86abSSebastian Ott 341eadb86abSSebastian Ott spin_lock_irqsave(sch->lock, flags); 342eadb86abSSebastian Ott if (!device_is_registered(&sch->dev)) 343eadb86abSSebastian Ott goto out_unlock; 344eadb86abSSebastian Ott 345eadb86abSSebastian Ott if (work_pending(&sch->todo_work)) 346eadb86abSSebastian Ott goto out_unlock; 347eadb86abSSebastian Ott 348eadb86abSSebastian Ott if (cio_update_schib(sch)) { 349eadb86abSSebastian Ott css_sched_sch_todo(sch, SCH_TODO_UNREG); 350eadb86abSSebastian Ott goto out_unlock; 351eadb86abSSebastian Ott } 352eadb86abSSebastian Ott private = get_eadm_private(sch); 353eadb86abSSebastian Ott if (private->state == EADM_NOT_OPER) 354eadb86abSSebastian Ott private->state = EADM_IDLE; 355eadb86abSSebastian Ott 356eadb86abSSebastian Ott out_unlock: 357eadb86abSSebastian Ott spin_unlock_irqrestore(sch->lock, flags); 358eadb86abSSebastian Ott 359eadb86abSSebastian Ott return ret; 360eadb86abSSebastian Ott } 361eadb86abSSebastian Ott 362eadb86abSSebastian Ott static struct css_device_id eadm_subchannel_ids[] = { 363eadb86abSSebastian Ott { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_ADM, }, 364eadb86abSSebastian Ott { /* end of list */ }, 365eadb86abSSebastian Ott }; 366eadb86abSSebastian Ott MODULE_DEVICE_TABLE(css, eadm_subchannel_ids); 367eadb86abSSebastian Ott 368eadb86abSSebastian Ott static struct css_driver eadm_subchannel_driver = { 369eadb86abSSebastian Ott .drv = { 370eadb86abSSebastian Ott .name = "eadm_subchannel", 371eadb86abSSebastian Ott .owner = THIS_MODULE, 372eadb86abSSebastian Ott }, 373eadb86abSSebastian Ott .subchannel_type = eadm_subchannel_ids, 374eadb86abSSebastian Ott .irq = eadm_subchannel_irq, 375eadb86abSSebastian Ott .probe = eadm_subchannel_probe, 376eadb86abSSebastian Ott .remove = eadm_subchannel_remove, 377eadb86abSSebastian Ott .shutdown = eadm_subchannel_shutdown, 378eadb86abSSebastian Ott .sch_event = eadm_subchannel_sch_event, 379eadb86abSSebastian Ott .freeze = eadm_subchannel_freeze, 380eadb86abSSebastian Ott .thaw = eadm_subchannel_restore, 381eadb86abSSebastian Ott .restore = eadm_subchannel_restore, 382eadb86abSSebastian Ott }; 383eadb86abSSebastian Ott 384eadb86abSSebastian Ott static int __init eadm_sch_init(void) 385eadb86abSSebastian Ott { 386eadb86abSSebastian Ott int ret; 387eadb86abSSebastian Ott 388eadb86abSSebastian Ott if (!css_general_characteristics.eadm) 389eadb86abSSebastian Ott return -ENXIO; 390eadb86abSSebastian Ott 391eadb86abSSebastian Ott eadm_debug = debug_register("eadm_log", 16, 1, 16); 392eadb86abSSebastian Ott if (!eadm_debug) 393eadb86abSSebastian Ott return -ENOMEM; 394eadb86abSSebastian Ott 395eadb86abSSebastian Ott debug_register_view(eadm_debug, &debug_hex_ascii_view); 396eadb86abSSebastian Ott debug_set_level(eadm_debug, 2); 397eadb86abSSebastian Ott 398eadb86abSSebastian Ott isc_register(EADM_SCH_ISC); 399eadb86abSSebastian Ott ret = css_driver_register(&eadm_subchannel_driver); 400eadb86abSSebastian Ott if (ret) 401eadb86abSSebastian Ott goto cleanup; 402eadb86abSSebastian Ott 403eadb86abSSebastian Ott return ret; 404eadb86abSSebastian Ott 405eadb86abSSebastian Ott cleanup: 406eadb86abSSebastian Ott isc_unregister(EADM_SCH_ISC); 407eadb86abSSebastian Ott debug_unregister(eadm_debug); 408eadb86abSSebastian Ott return ret; 409eadb86abSSebastian Ott } 410eadb86abSSebastian Ott 411eadb86abSSebastian Ott static void __exit eadm_sch_exit(void) 412eadb86abSSebastian Ott { 413eadb86abSSebastian Ott css_driver_unregister(&eadm_subchannel_driver); 414eadb86abSSebastian Ott isc_unregister(EADM_SCH_ISC); 415eadb86abSSebastian Ott debug_unregister(eadm_debug); 416eadb86abSSebastian Ott } 417eadb86abSSebastian Ott module_init(eadm_sch_init); 418eadb86abSSebastian Ott module_exit(eadm_sch_exit); 419