162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Finite state machine for vfio-ccw device handling 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2017 662306a36Sopenharmony_ci * Copyright Red Hat, Inc. 2019 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> 962306a36Sopenharmony_ci * Cornelia Huck <cohuck@redhat.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/vfio.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/isc.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "ioasm.h" 1762306a36Sopenharmony_ci#include "vfio_ccw_private.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int fsm_io_helper(struct vfio_ccw_private *private) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 2262306a36Sopenharmony_ci union orb *orb; 2362306a36Sopenharmony_ci int ccode; 2462306a36Sopenharmony_ci __u8 lpm; 2562306a36Sopenharmony_ci unsigned long flags; 2662306a36Sopenharmony_ci int ret; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci spin_lock_irqsave(sch->lock, flags); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci orb = cp_get_orb(&private->cp, sch); 3162306a36Sopenharmony_ci if (!orb) { 3262306a36Sopenharmony_ci ret = -EIO; 3362306a36Sopenharmony_ci goto out; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci VFIO_CCW_TRACE_EVENT(5, "stIO"); 3762306a36Sopenharmony_ci VFIO_CCW_TRACE_EVENT(5, dev_name(&sch->dev)); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Issue "Start Subchannel" */ 4062306a36Sopenharmony_ci ccode = ssch(sch->schid, orb); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci VFIO_CCW_HEX_EVENT(5, &ccode, sizeof(ccode)); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci switch (ccode) { 4562306a36Sopenharmony_ci case 0: 4662306a36Sopenharmony_ci /* 4762306a36Sopenharmony_ci * Initialize device status information 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; 5062306a36Sopenharmony_ci ret = 0; 5162306a36Sopenharmony_ci private->state = VFIO_CCW_STATE_CP_PENDING; 5262306a36Sopenharmony_ci break; 5362306a36Sopenharmony_ci case 1: /* Status pending */ 5462306a36Sopenharmony_ci case 2: /* Busy */ 5562306a36Sopenharmony_ci ret = -EBUSY; 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci case 3: /* Device/path not operational */ 5862306a36Sopenharmony_ci { 5962306a36Sopenharmony_ci lpm = orb->cmd.lpm; 6062306a36Sopenharmony_ci if (lpm != 0) 6162306a36Sopenharmony_ci sch->lpm &= ~lpm; 6262306a36Sopenharmony_ci else 6362306a36Sopenharmony_ci sch->lpm = 0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (cio_update_schib(sch)) 6662306a36Sopenharmony_ci ret = -ENODEV; 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci ret = sch->lpm ? -EACCES : -ENODEV; 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci default: 7262306a36Sopenharmony_ci ret = ccode; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ciout: 7562306a36Sopenharmony_ci spin_unlock_irqrestore(sch->lock, flags); 7662306a36Sopenharmony_ci return ret; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int fsm_do_halt(struct vfio_ccw_private *private) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 8262306a36Sopenharmony_ci unsigned long flags; 8362306a36Sopenharmony_ci int ccode; 8462306a36Sopenharmony_ci int ret; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci spin_lock_irqsave(sch->lock, flags); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, "haltIO"); 8962306a36Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev)); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Issue "Halt Subchannel" */ 9262306a36Sopenharmony_ci ccode = hsch(sch->schid); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci VFIO_CCW_HEX_EVENT(2, &ccode, sizeof(ccode)); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci switch (ccode) { 9762306a36Sopenharmony_ci case 0: 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * Initialize device status information 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND; 10262306a36Sopenharmony_ci ret = 0; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case 1: /* Status pending */ 10562306a36Sopenharmony_ci case 2: /* Busy */ 10662306a36Sopenharmony_ci ret = -EBUSY; 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci case 3: /* Device not operational */ 10962306a36Sopenharmony_ci ret = -ENODEV; 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci default: 11262306a36Sopenharmony_ci ret = ccode; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci spin_unlock_irqrestore(sch->lock, flags); 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int fsm_do_clear(struct vfio_ccw_private *private) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 12162306a36Sopenharmony_ci unsigned long flags; 12262306a36Sopenharmony_ci int ccode; 12362306a36Sopenharmony_ci int ret; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci spin_lock_irqsave(sch->lock, flags); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, "clearIO"); 12862306a36Sopenharmony_ci VFIO_CCW_TRACE_EVENT(2, dev_name(&sch->dev)); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Issue "Clear Subchannel" */ 13162306a36Sopenharmony_ci ccode = csch(sch->schid); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci VFIO_CCW_HEX_EVENT(2, &ccode, sizeof(ccode)); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci switch (ccode) { 13662306a36Sopenharmony_ci case 0: 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Initialize device status information 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci sch->schib.scsw.cmd.actl = SCSW_ACTL_CLEAR_PEND; 14162306a36Sopenharmony_ci /* TODO: check what else we might need to clear */ 14262306a36Sopenharmony_ci ret = 0; 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci case 3: /* Device not operational */ 14562306a36Sopenharmony_ci ret = -ENODEV; 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci default: 14862306a36Sopenharmony_ci ret = ccode; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci spin_unlock_irqrestore(sch->lock, flags); 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void fsm_notoper(struct vfio_ccw_private *private, 15562306a36Sopenharmony_ci enum vfio_ccw_event event) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: notoper event %x state %x\n", 16062306a36Sopenharmony_ci sch->schid.cssid, 16162306a36Sopenharmony_ci sch->schid.ssid, 16262306a36Sopenharmony_ci sch->schid.sch_no, 16362306a36Sopenharmony_ci event, 16462306a36Sopenharmony_ci private->state); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci * TODO: 16862306a36Sopenharmony_ci * Probably we should send the machine check to the guest. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci css_sched_sch_todo(sch, SCH_TODO_UNREG); 17162306a36Sopenharmony_ci private->state = VFIO_CCW_STATE_NOT_OPER; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* This is usually handled during CLOSE event */ 17462306a36Sopenharmony_ci cp_free(&private->cp); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * No operation action. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistatic void fsm_nop(struct vfio_ccw_private *private, 18162306a36Sopenharmony_ci enum vfio_ccw_event event) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void fsm_io_error(struct vfio_ccw_private *private, 18662306a36Sopenharmony_ci enum vfio_ccw_event event) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci pr_err("vfio-ccw: FSM: I/O request from state:%d\n", private->state); 18962306a36Sopenharmony_ci private->io_region->ret_code = -EIO; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void fsm_io_busy(struct vfio_ccw_private *private, 19362306a36Sopenharmony_ci enum vfio_ccw_event event) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci private->io_region->ret_code = -EBUSY; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic void fsm_io_retry(struct vfio_ccw_private *private, 19962306a36Sopenharmony_ci enum vfio_ccw_event event) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci private->io_region->ret_code = -EAGAIN; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void fsm_async_error(struct vfio_ccw_private *private, 20562306a36Sopenharmony_ci enum vfio_ccw_event event) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct ccw_cmd_region *cmd_region = private->cmd_region; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci pr_err("vfio-ccw: FSM: %s request from state:%d\n", 21062306a36Sopenharmony_ci cmd_region->command == VFIO_CCW_ASYNC_CMD_HSCH ? "halt" : 21162306a36Sopenharmony_ci cmd_region->command == VFIO_CCW_ASYNC_CMD_CSCH ? "clear" : 21262306a36Sopenharmony_ci "<unknown>", private->state); 21362306a36Sopenharmony_ci cmd_region->ret_code = -EIO; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void fsm_async_retry(struct vfio_ccw_private *private, 21762306a36Sopenharmony_ci enum vfio_ccw_event event) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci private->cmd_region->ret_code = -EAGAIN; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void fsm_disabled_irq(struct vfio_ccw_private *private, 22362306a36Sopenharmony_ci enum vfio_ccw_event event) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* 22862306a36Sopenharmony_ci * An interrupt in a disabled state means a previous disable was not 22962306a36Sopenharmony_ci * successful - should not happen, but we try to disable again. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci cio_disable_subchannel(sch); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ciinline struct subchannel_id get_schid(struct vfio_ccw_private *p) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(p->vdev.dev->parent); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return sch->schid; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* 24162306a36Sopenharmony_ci * Deal with the ccw command request from the userspace. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cistatic void fsm_io_request(struct vfio_ccw_private *private, 24462306a36Sopenharmony_ci enum vfio_ccw_event event) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci union orb *orb; 24762306a36Sopenharmony_ci union scsw *scsw = &private->scsw; 24862306a36Sopenharmony_ci struct ccw_io_region *io_region = private->io_region; 24962306a36Sopenharmony_ci char *errstr = "request"; 25062306a36Sopenharmony_ci struct subchannel_id schid = get_schid(private); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci private->state = VFIO_CCW_STATE_CP_PROCESSING; 25362306a36Sopenharmony_ci memcpy(scsw, io_region->scsw_area, sizeof(*scsw)); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) { 25662306a36Sopenharmony_ci orb = (union orb *)io_region->orb_area; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Don't try to build a cp if transport mode is specified. */ 25962306a36Sopenharmony_ci if (orb->tm.b) { 26062306a36Sopenharmony_ci io_region->ret_code = -EOPNOTSUPP; 26162306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 26262306a36Sopenharmony_ci "sch %x.%x.%04x: transport mode\n", 26362306a36Sopenharmony_ci schid.cssid, 26462306a36Sopenharmony_ci schid.ssid, schid.sch_no); 26562306a36Sopenharmony_ci errstr = "transport mode"; 26662306a36Sopenharmony_ci goto err_out; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci io_region->ret_code = cp_init(&private->cp, orb); 26962306a36Sopenharmony_ci if (io_region->ret_code) { 27062306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 27162306a36Sopenharmony_ci "sch %x.%x.%04x: cp_init=%d\n", 27262306a36Sopenharmony_ci schid.cssid, 27362306a36Sopenharmony_ci schid.ssid, schid.sch_no, 27462306a36Sopenharmony_ci io_region->ret_code); 27562306a36Sopenharmony_ci errstr = "cp init"; 27662306a36Sopenharmony_ci goto err_out; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci io_region->ret_code = cp_prefetch(&private->cp); 28062306a36Sopenharmony_ci if (io_region->ret_code) { 28162306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 28262306a36Sopenharmony_ci "sch %x.%x.%04x: cp_prefetch=%d\n", 28362306a36Sopenharmony_ci schid.cssid, 28462306a36Sopenharmony_ci schid.ssid, schid.sch_no, 28562306a36Sopenharmony_ci io_region->ret_code); 28662306a36Sopenharmony_ci errstr = "cp prefetch"; 28762306a36Sopenharmony_ci cp_free(&private->cp); 28862306a36Sopenharmony_ci goto err_out; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Start channel program and wait for I/O interrupt. */ 29262306a36Sopenharmony_ci io_region->ret_code = fsm_io_helper(private); 29362306a36Sopenharmony_ci if (io_region->ret_code) { 29462306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 29562306a36Sopenharmony_ci "sch %x.%x.%04x: fsm_io_helper=%d\n", 29662306a36Sopenharmony_ci schid.cssid, 29762306a36Sopenharmony_ci schid.ssid, schid.sch_no, 29862306a36Sopenharmony_ci io_region->ret_code); 29962306a36Sopenharmony_ci errstr = "cp fsm_io_helper"; 30062306a36Sopenharmony_ci cp_free(&private->cp); 30162306a36Sopenharmony_ci goto err_out; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) { 30562306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 30662306a36Sopenharmony_ci "sch %x.%x.%04x: halt on io_region\n", 30762306a36Sopenharmony_ci schid.cssid, 30862306a36Sopenharmony_ci schid.ssid, schid.sch_no); 30962306a36Sopenharmony_ci /* halt is handled via the async cmd region */ 31062306a36Sopenharmony_ci io_region->ret_code = -EOPNOTSUPP; 31162306a36Sopenharmony_ci goto err_out; 31262306a36Sopenharmony_ci } else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { 31362306a36Sopenharmony_ci VFIO_CCW_MSG_EVENT(2, 31462306a36Sopenharmony_ci "sch %x.%x.%04x: clear on io_region\n", 31562306a36Sopenharmony_ci schid.cssid, 31662306a36Sopenharmony_ci schid.ssid, schid.sch_no); 31762306a36Sopenharmony_ci /* clear is handled via the async cmd region */ 31862306a36Sopenharmony_ci io_region->ret_code = -EOPNOTSUPP; 31962306a36Sopenharmony_ci goto err_out; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cierr_out: 32362306a36Sopenharmony_ci private->state = VFIO_CCW_STATE_IDLE; 32462306a36Sopenharmony_ci trace_vfio_ccw_fsm_io_request(scsw->cmd.fctl, schid, 32562306a36Sopenharmony_ci io_region->ret_code, errstr); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* 32962306a36Sopenharmony_ci * Deal with an async request from userspace. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_cistatic void fsm_async_request(struct vfio_ccw_private *private, 33262306a36Sopenharmony_ci enum vfio_ccw_event event) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct ccw_cmd_region *cmd_region = private->cmd_region; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci switch (cmd_region->command) { 33762306a36Sopenharmony_ci case VFIO_CCW_ASYNC_CMD_HSCH: 33862306a36Sopenharmony_ci cmd_region->ret_code = fsm_do_halt(private); 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case VFIO_CCW_ASYNC_CMD_CSCH: 34162306a36Sopenharmony_ci cmd_region->ret_code = fsm_do_clear(private); 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci default: 34462306a36Sopenharmony_ci /* should not happen? */ 34562306a36Sopenharmony_ci cmd_region->ret_code = -EINVAL; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci trace_vfio_ccw_fsm_async_request(get_schid(private), 34962306a36Sopenharmony_ci cmd_region->command, 35062306a36Sopenharmony_ci cmd_region->ret_code); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/* 35462306a36Sopenharmony_ci * Got an interrupt for a normal io (state busy). 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_cistatic void fsm_irq(struct vfio_ccw_private *private, 35762306a36Sopenharmony_ci enum vfio_ccw_event event) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 36062306a36Sopenharmony_ci struct irb *irb = this_cpu_ptr(&cio_irb); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci VFIO_CCW_TRACE_EVENT(6, "IRQ"); 36362306a36Sopenharmony_ci VFIO_CCW_TRACE_EVENT(6, dev_name(&sch->dev)); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci memcpy(&private->irb, irb, sizeof(*irb)); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci queue_work(vfio_ccw_work_q, &private->io_work); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (private->completion) 37062306a36Sopenharmony_ci complete(private->completion); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void fsm_open(struct vfio_ccw_private *private, 37462306a36Sopenharmony_ci enum vfio_ccw_event event) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 37762306a36Sopenharmony_ci int ret; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci spin_lock_irq(sch->lock); 38062306a36Sopenharmony_ci sch->isc = VFIO_CCW_ISC; 38162306a36Sopenharmony_ci ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch); 38262306a36Sopenharmony_ci if (ret) 38362306a36Sopenharmony_ci goto err_unlock; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci private->state = VFIO_CCW_STATE_IDLE; 38662306a36Sopenharmony_ci spin_unlock_irq(sch->lock); 38762306a36Sopenharmony_ci return; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cierr_unlock: 39062306a36Sopenharmony_ci spin_unlock_irq(sch->lock); 39162306a36Sopenharmony_ci vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void fsm_close(struct vfio_ccw_private *private, 39562306a36Sopenharmony_ci enum vfio_ccw_event event) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(private->vdev.dev->parent); 39862306a36Sopenharmony_ci int ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci spin_lock_irq(sch->lock); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!sch->schib.pmcw.ena) 40362306a36Sopenharmony_ci goto err_unlock; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ret = cio_disable_subchannel(sch); 40662306a36Sopenharmony_ci if (ret == -EBUSY) 40762306a36Sopenharmony_ci ret = vfio_ccw_sch_quiesce(sch); 40862306a36Sopenharmony_ci if (ret) 40962306a36Sopenharmony_ci goto err_unlock; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci private->state = VFIO_CCW_STATE_STANDBY; 41262306a36Sopenharmony_ci spin_unlock_irq(sch->lock); 41362306a36Sopenharmony_ci cp_free(&private->cp); 41462306a36Sopenharmony_ci return; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cierr_unlock: 41762306a36Sopenharmony_ci spin_unlock_irq(sch->lock); 41862306a36Sopenharmony_ci vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* 42262306a36Sopenharmony_ci * Device statemachine 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cifsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = { 42562306a36Sopenharmony_ci [VFIO_CCW_STATE_NOT_OPER] = { 42662306a36Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_nop, 42762306a36Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 42862306a36Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_error, 42962306a36Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_disabled_irq, 43062306a36Sopenharmony_ci [VFIO_CCW_EVENT_OPEN] = fsm_nop, 43162306a36Sopenharmony_ci [VFIO_CCW_EVENT_CLOSE] = fsm_nop, 43262306a36Sopenharmony_ci }, 43362306a36Sopenharmony_ci [VFIO_CCW_STATE_STANDBY] = { 43462306a36Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 43562306a36Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_error, 43662306a36Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_error, 43762306a36Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_disabled_irq, 43862306a36Sopenharmony_ci [VFIO_CCW_EVENT_OPEN] = fsm_open, 43962306a36Sopenharmony_ci [VFIO_CCW_EVENT_CLOSE] = fsm_notoper, 44062306a36Sopenharmony_ci }, 44162306a36Sopenharmony_ci [VFIO_CCW_STATE_IDLE] = { 44262306a36Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 44362306a36Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_request, 44462306a36Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_request, 44562306a36Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 44662306a36Sopenharmony_ci [VFIO_CCW_EVENT_OPEN] = fsm_notoper, 44762306a36Sopenharmony_ci [VFIO_CCW_EVENT_CLOSE] = fsm_close, 44862306a36Sopenharmony_ci }, 44962306a36Sopenharmony_ci [VFIO_CCW_STATE_CP_PROCESSING] = { 45062306a36Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 45162306a36Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_retry, 45262306a36Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_retry, 45362306a36Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 45462306a36Sopenharmony_ci [VFIO_CCW_EVENT_OPEN] = fsm_notoper, 45562306a36Sopenharmony_ci [VFIO_CCW_EVENT_CLOSE] = fsm_close, 45662306a36Sopenharmony_ci }, 45762306a36Sopenharmony_ci [VFIO_CCW_STATE_CP_PENDING] = { 45862306a36Sopenharmony_ci [VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper, 45962306a36Sopenharmony_ci [VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy, 46062306a36Sopenharmony_ci [VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_request, 46162306a36Sopenharmony_ci [VFIO_CCW_EVENT_INTERRUPT] = fsm_irq, 46262306a36Sopenharmony_ci [VFIO_CCW_EVENT_OPEN] = fsm_notoper, 46362306a36Sopenharmony_ci [VFIO_CCW_EVENT_CLOSE] = fsm_close, 46462306a36Sopenharmony_ci }, 46562306a36Sopenharmony_ci}; 466