162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ccw based virtio transport 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2012, 2014 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/memblock.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/virtio.h> 1562306a36Sopenharmony_ci#include <linux/virtio_config.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/virtio_ring.h> 1962306a36Sopenharmony_ci#include <linux/pfn.h> 2062306a36Sopenharmony_ci#include <linux/async.h> 2162306a36Sopenharmony_ci#include <linux/wait.h> 2262306a36Sopenharmony_ci#include <linux/list.h> 2362306a36Sopenharmony_ci#include <linux/bitops.h> 2462306a36Sopenharmony_ci#include <linux/moduleparam.h> 2562306a36Sopenharmony_ci#include <linux/io.h> 2662306a36Sopenharmony_ci#include <linux/kvm_para.h> 2762306a36Sopenharmony_ci#include <linux/notifier.h> 2862306a36Sopenharmony_ci#include <asm/diag.h> 2962306a36Sopenharmony_ci#include <asm/setup.h> 3062306a36Sopenharmony_ci#include <asm/irq.h> 3162306a36Sopenharmony_ci#include <asm/cio.h> 3262306a36Sopenharmony_ci#include <asm/ccwdev.h> 3362306a36Sopenharmony_ci#include <asm/virtio-ccw.h> 3462306a36Sopenharmony_ci#include <asm/isc.h> 3562306a36Sopenharmony_ci#include <asm/airq.h> 3662306a36Sopenharmony_ci#include <asm/tpi.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * virtio related functions 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct vq_config_block { 4362306a36Sopenharmony_ci __u16 index; 4462306a36Sopenharmony_ci __u16 num; 4562306a36Sopenharmony_ci} __packed; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define VIRTIO_CCW_CONFIG_SIZE 0x100 4862306a36Sopenharmony_ci/* same as PCI config space size, should be enough for all drivers */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct vcdev_dma_area { 5162306a36Sopenharmony_ci unsigned long indicators; 5262306a36Sopenharmony_ci unsigned long indicators2; 5362306a36Sopenharmony_ci struct vq_config_block config_block; 5462306a36Sopenharmony_ci __u8 status; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct virtio_ccw_device { 5862306a36Sopenharmony_ci struct virtio_device vdev; 5962306a36Sopenharmony_ci __u8 config[VIRTIO_CCW_CONFIG_SIZE]; 6062306a36Sopenharmony_ci struct ccw_device *cdev; 6162306a36Sopenharmony_ci __u32 curr_io; 6262306a36Sopenharmony_ci int err; 6362306a36Sopenharmony_ci unsigned int revision; /* Transport revision */ 6462306a36Sopenharmony_ci wait_queue_head_t wait_q; 6562306a36Sopenharmony_ci spinlock_t lock; 6662306a36Sopenharmony_ci rwlock_t irq_lock; 6762306a36Sopenharmony_ci struct mutex io_lock; /* Serializes I/O requests */ 6862306a36Sopenharmony_ci struct list_head virtqueues; 6962306a36Sopenharmony_ci bool is_thinint; 7062306a36Sopenharmony_ci bool going_away; 7162306a36Sopenharmony_ci bool device_lost; 7262306a36Sopenharmony_ci unsigned int config_ready; 7362306a36Sopenharmony_ci void *airq_info; 7462306a36Sopenharmony_ci struct vcdev_dma_area *dma_area; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic inline unsigned long *indicators(struct virtio_ccw_device *vcdev) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci return &vcdev->dma_area->indicators; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic inline unsigned long *indicators2(struct virtio_ccw_device *vcdev) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci return &vcdev->dma_area->indicators2; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistruct vq_info_block_legacy { 8862306a36Sopenharmony_ci __u64 queue; 8962306a36Sopenharmony_ci __u32 align; 9062306a36Sopenharmony_ci __u16 index; 9162306a36Sopenharmony_ci __u16 num; 9262306a36Sopenharmony_ci} __packed; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistruct vq_info_block { 9562306a36Sopenharmony_ci __u64 desc; 9662306a36Sopenharmony_ci __u32 res0; 9762306a36Sopenharmony_ci __u16 index; 9862306a36Sopenharmony_ci __u16 num; 9962306a36Sopenharmony_ci __u64 avail; 10062306a36Sopenharmony_ci __u64 used; 10162306a36Sopenharmony_ci} __packed; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistruct virtio_feature_desc { 10462306a36Sopenharmony_ci __le32 features; 10562306a36Sopenharmony_ci __u8 index; 10662306a36Sopenharmony_ci} __packed; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistruct virtio_thinint_area { 10962306a36Sopenharmony_ci unsigned long summary_indicator; 11062306a36Sopenharmony_ci unsigned long indicator; 11162306a36Sopenharmony_ci u64 bit_nr; 11262306a36Sopenharmony_ci u8 isc; 11362306a36Sopenharmony_ci} __packed; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct virtio_rev_info { 11662306a36Sopenharmony_ci __u16 revision; 11762306a36Sopenharmony_ci __u16 length; 11862306a36Sopenharmony_ci __u8 data[]; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* the highest virtio-ccw revision we support */ 12262306a36Sopenharmony_ci#define VIRTIO_CCW_REV_MAX 2 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct virtio_ccw_vq_info { 12562306a36Sopenharmony_ci struct virtqueue *vq; 12662306a36Sopenharmony_ci int num; 12762306a36Sopenharmony_ci union { 12862306a36Sopenharmony_ci struct vq_info_block s; 12962306a36Sopenharmony_ci struct vq_info_block_legacy l; 13062306a36Sopenharmony_ci } *info_block; 13162306a36Sopenharmony_ci int bit_nr; 13262306a36Sopenharmony_ci struct list_head node; 13362306a36Sopenharmony_ci long cookie; 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define VIRTIO_AIRQ_ISC IO_SCH_ISC /* inherit from subchannel */ 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define VIRTIO_IV_BITS (L1_CACHE_BYTES * 8) 13962306a36Sopenharmony_ci#define MAX_AIRQ_AREAS 20 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int virtio_ccw_use_airq = 1; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistruct airq_info { 14462306a36Sopenharmony_ci rwlock_t lock; 14562306a36Sopenharmony_ci u8 summary_indicator_idx; 14662306a36Sopenharmony_ci struct airq_struct airq; 14762306a36Sopenharmony_ci struct airq_iv *aiv; 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_cistatic struct airq_info *airq_areas[MAX_AIRQ_AREAS]; 15062306a36Sopenharmony_cistatic DEFINE_MUTEX(airq_areas_lock); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic u8 *summary_indicators; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic inline u8 *get_summary_indicator(struct airq_info *info) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return summary_indicators + info->summary_indicator_idx; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define CCW_CMD_SET_VQ 0x13 16062306a36Sopenharmony_ci#define CCW_CMD_VDEV_RESET 0x33 16162306a36Sopenharmony_ci#define CCW_CMD_SET_IND 0x43 16262306a36Sopenharmony_ci#define CCW_CMD_SET_CONF_IND 0x53 16362306a36Sopenharmony_ci#define CCW_CMD_READ_FEAT 0x12 16462306a36Sopenharmony_ci#define CCW_CMD_WRITE_FEAT 0x11 16562306a36Sopenharmony_ci#define CCW_CMD_READ_CONF 0x22 16662306a36Sopenharmony_ci#define CCW_CMD_WRITE_CONF 0x21 16762306a36Sopenharmony_ci#define CCW_CMD_WRITE_STATUS 0x31 16862306a36Sopenharmony_ci#define CCW_CMD_READ_VQ_CONF 0x32 16962306a36Sopenharmony_ci#define CCW_CMD_READ_STATUS 0x72 17062306a36Sopenharmony_ci#define CCW_CMD_SET_IND_ADAPTER 0x73 17162306a36Sopenharmony_ci#define CCW_CMD_SET_VIRTIO_REV 0x83 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_VQ 0x00010000 17462306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_RESET 0x00040000 17562306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_READ_FEAT 0x00080000 17662306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000 17762306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000 17862306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000 17962306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000 18062306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_IND 0x01000000 18162306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 18262306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 18362306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_IND_ADAPTER 0x08000000 18462306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_VIRTIO_REV 0x10000000 18562306a36Sopenharmony_ci#define VIRTIO_CCW_DOING_READ_STATUS 0x20000000 18662306a36Sopenharmony_ci#define VIRTIO_CCW_INTPARM_MASK 0xffff0000 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci return container_of(vdev, struct virtio_ccw_device, vdev); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void drop_airq_indicator(struct virtqueue *vq, struct airq_info *info) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci unsigned long i, flags; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci write_lock_irqsave(&info->lock, flags); 19862306a36Sopenharmony_ci for (i = 0; i < airq_iv_end(info->aiv); i++) { 19962306a36Sopenharmony_ci if (vq == (void *)airq_iv_get_ptr(info->aiv, i)) { 20062306a36Sopenharmony_ci airq_iv_free_bit(info->aiv, i); 20162306a36Sopenharmony_ci airq_iv_set_ptr(info->aiv, i, 0); 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci write_unlock_irqrestore(&info->lock, flags); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void virtio_airq_handler(struct airq_struct *airq, 20962306a36Sopenharmony_ci struct tpi_info *tpi_info) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct airq_info *info = container_of(airq, struct airq_info, airq); 21262306a36Sopenharmony_ci unsigned long ai; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci inc_irq_stat(IRQIO_VAI); 21562306a36Sopenharmony_ci read_lock(&info->lock); 21662306a36Sopenharmony_ci /* Walk through indicators field, summary indicator active. */ 21762306a36Sopenharmony_ci for (ai = 0;;) { 21862306a36Sopenharmony_ci ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv)); 21962306a36Sopenharmony_ci if (ai == -1UL) 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai)); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci *(get_summary_indicator(info)) = 0; 22462306a36Sopenharmony_ci smp_wmb(); 22562306a36Sopenharmony_ci /* Walk through indicators field, summary indicator not active. */ 22662306a36Sopenharmony_ci for (ai = 0;;) { 22762306a36Sopenharmony_ci ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv)); 22862306a36Sopenharmony_ci if (ai == -1UL) 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai)); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci read_unlock(&info->lock); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic struct airq_info *new_airq_info(int index) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct airq_info *info; 23862306a36Sopenharmony_ci int rc; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 24162306a36Sopenharmony_ci if (!info) 24262306a36Sopenharmony_ci return NULL; 24362306a36Sopenharmony_ci rwlock_init(&info->lock); 24462306a36Sopenharmony_ci info->aiv = airq_iv_create(VIRTIO_IV_BITS, AIRQ_IV_ALLOC | AIRQ_IV_PTR 24562306a36Sopenharmony_ci | AIRQ_IV_CACHELINE, NULL); 24662306a36Sopenharmony_ci if (!info->aiv) { 24762306a36Sopenharmony_ci kfree(info); 24862306a36Sopenharmony_ci return NULL; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci info->airq.handler = virtio_airq_handler; 25162306a36Sopenharmony_ci info->summary_indicator_idx = index; 25262306a36Sopenharmony_ci info->airq.lsi_ptr = get_summary_indicator(info); 25362306a36Sopenharmony_ci info->airq.isc = VIRTIO_AIRQ_ISC; 25462306a36Sopenharmony_ci rc = register_adapter_interrupt(&info->airq); 25562306a36Sopenharmony_ci if (rc) { 25662306a36Sopenharmony_ci airq_iv_release(info->aiv); 25762306a36Sopenharmony_ci kfree(info); 25862306a36Sopenharmony_ci return NULL; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci return info; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic unsigned long get_airq_indicator(struct virtqueue *vqs[], int nvqs, 26462306a36Sopenharmony_ci u64 *first, void **airq_info) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci int i, j; 26762306a36Sopenharmony_ci struct airq_info *info; 26862306a36Sopenharmony_ci unsigned long indicator_addr = 0; 26962306a36Sopenharmony_ci unsigned long bit, flags; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci for (i = 0; i < MAX_AIRQ_AREAS && !indicator_addr; i++) { 27262306a36Sopenharmony_ci mutex_lock(&airq_areas_lock); 27362306a36Sopenharmony_ci if (!airq_areas[i]) 27462306a36Sopenharmony_ci airq_areas[i] = new_airq_info(i); 27562306a36Sopenharmony_ci info = airq_areas[i]; 27662306a36Sopenharmony_ci mutex_unlock(&airq_areas_lock); 27762306a36Sopenharmony_ci if (!info) 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci write_lock_irqsave(&info->lock, flags); 28062306a36Sopenharmony_ci bit = airq_iv_alloc(info->aiv, nvqs); 28162306a36Sopenharmony_ci if (bit == -1UL) { 28262306a36Sopenharmony_ci /* Not enough vacancies. */ 28362306a36Sopenharmony_ci write_unlock_irqrestore(&info->lock, flags); 28462306a36Sopenharmony_ci continue; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci *first = bit; 28762306a36Sopenharmony_ci *airq_info = info; 28862306a36Sopenharmony_ci indicator_addr = (unsigned long)info->aiv->vector; 28962306a36Sopenharmony_ci for (j = 0; j < nvqs; j++) { 29062306a36Sopenharmony_ci airq_iv_set_ptr(info->aiv, bit + j, 29162306a36Sopenharmony_ci (unsigned long)vqs[j]); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci write_unlock_irqrestore(&info->lock, flags); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci return indicator_addr; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void virtio_ccw_drop_indicators(struct virtio_ccw_device *vcdev) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct virtio_ccw_vq_info *info; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (!vcdev->airq_info) 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci list_for_each_entry(info, &vcdev->virtqueues, node) 30562306a36Sopenharmony_ci drop_airq_indicator(info->vq, vcdev->airq_info); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci unsigned long flags; 31162306a36Sopenharmony_ci __u32 ret; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); 31462306a36Sopenharmony_ci if (vcdev->err) 31562306a36Sopenharmony_ci ret = 0; 31662306a36Sopenharmony_ci else 31762306a36Sopenharmony_ci ret = vcdev->curr_io & flag; 31862306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int ccw_io_helper(struct virtio_ccw_device *vcdev, 32362306a36Sopenharmony_ci struct ccw1 *ccw, __u32 intparm) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci int ret; 32662306a36Sopenharmony_ci unsigned long flags; 32762306a36Sopenharmony_ci int flag = intparm & VIRTIO_CCW_INTPARM_MASK; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci mutex_lock(&vcdev->io_lock); 33062306a36Sopenharmony_ci do { 33162306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); 33262306a36Sopenharmony_ci ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); 33362306a36Sopenharmony_ci if (!ret) { 33462306a36Sopenharmony_ci if (!vcdev->curr_io) 33562306a36Sopenharmony_ci vcdev->err = 0; 33662306a36Sopenharmony_ci vcdev->curr_io |= flag; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); 33962306a36Sopenharmony_ci cpu_relax(); 34062306a36Sopenharmony_ci } while (ret == -EBUSY); 34162306a36Sopenharmony_ci wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); 34262306a36Sopenharmony_ci ret = ret ? ret : vcdev->err; 34362306a36Sopenharmony_ci mutex_unlock(&vcdev->io_lock); 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev, 34862306a36Sopenharmony_ci struct ccw1 *ccw) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci int ret; 35162306a36Sopenharmony_ci unsigned long *indicatorp = NULL; 35262306a36Sopenharmony_ci struct virtio_thinint_area *thinint_area = NULL; 35362306a36Sopenharmony_ci struct airq_info *airq_info = vcdev->airq_info; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (vcdev->is_thinint) { 35662306a36Sopenharmony_ci thinint_area = ccw_device_dma_zalloc(vcdev->cdev, 35762306a36Sopenharmony_ci sizeof(*thinint_area)); 35862306a36Sopenharmony_ci if (!thinint_area) 35962306a36Sopenharmony_ci return; 36062306a36Sopenharmony_ci thinint_area->summary_indicator = 36162306a36Sopenharmony_ci (unsigned long) get_summary_indicator(airq_info); 36262306a36Sopenharmony_ci thinint_area->isc = VIRTIO_AIRQ_ISC; 36362306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER; 36462306a36Sopenharmony_ci ccw->count = sizeof(*thinint_area); 36562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(thinint_area); 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci /* payload is the address of the indicators */ 36862306a36Sopenharmony_ci indicatorp = ccw_device_dma_zalloc(vcdev->cdev, 36962306a36Sopenharmony_ci sizeof(indicators(vcdev))); 37062306a36Sopenharmony_ci if (!indicatorp) 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci *indicatorp = 0; 37362306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_IND; 37462306a36Sopenharmony_ci ccw->count = sizeof(indicators(vcdev)); 37562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(indicatorp); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci /* Deregister indicators from host. */ 37862306a36Sopenharmony_ci *indicators(vcdev) = 0; 37962306a36Sopenharmony_ci ccw->flags = 0; 38062306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, 38162306a36Sopenharmony_ci vcdev->is_thinint ? 38262306a36Sopenharmony_ci VIRTIO_CCW_DOING_SET_IND_ADAPTER : 38362306a36Sopenharmony_ci VIRTIO_CCW_DOING_SET_IND); 38462306a36Sopenharmony_ci if (ret && (ret != -ENODEV)) 38562306a36Sopenharmony_ci dev_info(&vcdev->cdev->dev, 38662306a36Sopenharmony_ci "Failed to deregister indicators (%d)\n", ret); 38762306a36Sopenharmony_ci else if (vcdev->is_thinint) 38862306a36Sopenharmony_ci virtio_ccw_drop_indicators(vcdev); 38962306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, indicatorp, sizeof(indicators(vcdev))); 39062306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, thinint_area, sizeof(*thinint_area)); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic inline bool virtio_ccw_do_kvm_notify(struct virtqueue *vq, u32 data) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct virtio_ccw_vq_info *info = vq->priv; 39662306a36Sopenharmony_ci struct virtio_ccw_device *vcdev; 39762306a36Sopenharmony_ci struct subchannel_id schid; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci vcdev = to_vc_device(info->vq->vdev); 40062306a36Sopenharmony_ci ccw_device_get_schid(vcdev->cdev, &schid); 40162306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct subchannel_id) != sizeof(unsigned int)); 40262306a36Sopenharmony_ci info->cookie = kvm_hypercall3(KVM_S390_VIRTIO_CCW_NOTIFY, 40362306a36Sopenharmony_ci *((unsigned int *)&schid), 40462306a36Sopenharmony_ci data, info->cookie); 40562306a36Sopenharmony_ci if (info->cookie < 0) 40662306a36Sopenharmony_ci return false; 40762306a36Sopenharmony_ci return true; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic bool virtio_ccw_kvm_notify(struct virtqueue *vq) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci return virtio_ccw_do_kvm_notify(vq, vq->index); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic bool virtio_ccw_kvm_notify_with_data(struct virtqueue *vq) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci return virtio_ccw_do_kvm_notify(vq, vring_notification_data(vq)); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, 42162306a36Sopenharmony_ci struct ccw1 *ccw, int index) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci int ret; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci vcdev->dma_area->config_block.index = index; 42662306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_VQ_CONF; 42762306a36Sopenharmony_ci ccw->flags = 0; 42862306a36Sopenharmony_ci ccw->count = sizeof(struct vq_config_block); 42962306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(&vcdev->dma_area->config_block); 43062306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF); 43162306a36Sopenharmony_ci if (ret) 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci return vcdev->dma_area->config_block.num ?: -ENOENT; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); 43962306a36Sopenharmony_ci struct virtio_ccw_vq_info *info = vq->priv; 44062306a36Sopenharmony_ci unsigned long flags; 44162306a36Sopenharmony_ci int ret; 44262306a36Sopenharmony_ci unsigned int index = vq->index; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* Remove from our list. */ 44562306a36Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 44662306a36Sopenharmony_ci list_del(&info->node); 44762306a36Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Release from host. */ 45062306a36Sopenharmony_ci if (vcdev->revision == 0) { 45162306a36Sopenharmony_ci info->info_block->l.queue = 0; 45262306a36Sopenharmony_ci info->info_block->l.align = 0; 45362306a36Sopenharmony_ci info->info_block->l.index = index; 45462306a36Sopenharmony_ci info->info_block->l.num = 0; 45562306a36Sopenharmony_ci ccw->count = sizeof(info->info_block->l); 45662306a36Sopenharmony_ci } else { 45762306a36Sopenharmony_ci info->info_block->s.desc = 0; 45862306a36Sopenharmony_ci info->info_block->s.index = index; 45962306a36Sopenharmony_ci info->info_block->s.num = 0; 46062306a36Sopenharmony_ci info->info_block->s.avail = 0; 46162306a36Sopenharmony_ci info->info_block->s.used = 0; 46262306a36Sopenharmony_ci ccw->count = sizeof(info->info_block->s); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_VQ; 46562306a36Sopenharmony_ci ccw->flags = 0; 46662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(info->info_block); 46762306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, 46862306a36Sopenharmony_ci VIRTIO_CCW_DOING_SET_VQ | index); 46962306a36Sopenharmony_ci /* 47062306a36Sopenharmony_ci * -ENODEV isn't considered an error: The device is gone anyway. 47162306a36Sopenharmony_ci * This may happen on device detach. 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_ci if (ret && (ret != -ENODEV)) 47462306a36Sopenharmony_ci dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d\n", 47562306a36Sopenharmony_ci ret, index); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci vring_del_virtqueue(vq); 47862306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, info->info_block, 47962306a36Sopenharmony_ci sizeof(*info->info_block)); 48062306a36Sopenharmony_ci kfree(info); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void virtio_ccw_del_vqs(struct virtio_device *vdev) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct virtqueue *vq, *n; 48662306a36Sopenharmony_ci struct ccw1 *ccw; 48762306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 49062306a36Sopenharmony_ci if (!ccw) 49162306a36Sopenharmony_ci return; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci virtio_ccw_drop_indicator(vcdev, ccw); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci list_for_each_entry_safe(vq, n, &vdev->vqs, list) 49662306a36Sopenharmony_ci virtio_ccw_del_vq(vq, ccw); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, 50262306a36Sopenharmony_ci int i, vq_callback_t *callback, 50362306a36Sopenharmony_ci const char *name, bool ctx, 50462306a36Sopenharmony_ci struct ccw1 *ccw) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 50762306a36Sopenharmony_ci bool (*notify)(struct virtqueue *vq); 50862306a36Sopenharmony_ci int err; 50962306a36Sopenharmony_ci struct virtqueue *vq = NULL; 51062306a36Sopenharmony_ci struct virtio_ccw_vq_info *info; 51162306a36Sopenharmony_ci u64 queue; 51262306a36Sopenharmony_ci unsigned long flags; 51362306a36Sopenharmony_ci bool may_reduce; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (__virtio_test_bit(vdev, VIRTIO_F_NOTIFICATION_DATA)) 51662306a36Sopenharmony_ci notify = virtio_ccw_kvm_notify_with_data; 51762306a36Sopenharmony_ci else 51862306a36Sopenharmony_ci notify = virtio_ccw_kvm_notify; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Allocate queue. */ 52162306a36Sopenharmony_ci info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL); 52262306a36Sopenharmony_ci if (!info) { 52362306a36Sopenharmony_ci dev_warn(&vcdev->cdev->dev, "no info\n"); 52462306a36Sopenharmony_ci err = -ENOMEM; 52562306a36Sopenharmony_ci goto out_err; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci info->info_block = ccw_device_dma_zalloc(vcdev->cdev, 52862306a36Sopenharmony_ci sizeof(*info->info_block)); 52962306a36Sopenharmony_ci if (!info->info_block) { 53062306a36Sopenharmony_ci dev_warn(&vcdev->cdev->dev, "no info block\n"); 53162306a36Sopenharmony_ci err = -ENOMEM; 53262306a36Sopenharmony_ci goto out_err; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i); 53562306a36Sopenharmony_ci if (info->num < 0) { 53662306a36Sopenharmony_ci err = info->num; 53762306a36Sopenharmony_ci goto out_err; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci may_reduce = vcdev->revision > 0; 54062306a36Sopenharmony_ci vq = vring_create_virtqueue(i, info->num, KVM_VIRTIO_CCW_RING_ALIGN, 54162306a36Sopenharmony_ci vdev, true, may_reduce, ctx, 54262306a36Sopenharmony_ci notify, callback, name); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (!vq) { 54562306a36Sopenharmony_ci /* For now, we fail if we can't get the requested size. */ 54662306a36Sopenharmony_ci dev_warn(&vcdev->cdev->dev, "no vq\n"); 54762306a36Sopenharmony_ci err = -ENOMEM; 54862306a36Sopenharmony_ci goto out_err; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci vq->num_max = info->num; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* it may have been reduced */ 55462306a36Sopenharmony_ci info->num = virtqueue_get_vring_size(vq); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* Register it with the host. */ 55762306a36Sopenharmony_ci queue = virtqueue_get_desc_addr(vq); 55862306a36Sopenharmony_ci if (vcdev->revision == 0) { 55962306a36Sopenharmony_ci info->info_block->l.queue = queue; 56062306a36Sopenharmony_ci info->info_block->l.align = KVM_VIRTIO_CCW_RING_ALIGN; 56162306a36Sopenharmony_ci info->info_block->l.index = i; 56262306a36Sopenharmony_ci info->info_block->l.num = info->num; 56362306a36Sopenharmony_ci ccw->count = sizeof(info->info_block->l); 56462306a36Sopenharmony_ci } else { 56562306a36Sopenharmony_ci info->info_block->s.desc = queue; 56662306a36Sopenharmony_ci info->info_block->s.index = i; 56762306a36Sopenharmony_ci info->info_block->s.num = info->num; 56862306a36Sopenharmony_ci info->info_block->s.avail = (__u64)virtqueue_get_avail_addr(vq); 56962306a36Sopenharmony_ci info->info_block->s.used = (__u64)virtqueue_get_used_addr(vq); 57062306a36Sopenharmony_ci ccw->count = sizeof(info->info_block->s); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_VQ; 57362306a36Sopenharmony_ci ccw->flags = 0; 57462306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(info->info_block); 57562306a36Sopenharmony_ci err = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_VQ | i); 57662306a36Sopenharmony_ci if (err) { 57762306a36Sopenharmony_ci dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); 57862306a36Sopenharmony_ci goto out_err; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci info->vq = vq; 58262306a36Sopenharmony_ci vq->priv = info; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Save it to our list. */ 58562306a36Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 58662306a36Sopenharmony_ci list_add(&info->node, &vcdev->virtqueues); 58762306a36Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return vq; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ciout_err: 59262306a36Sopenharmony_ci if (vq) 59362306a36Sopenharmony_ci vring_del_virtqueue(vq); 59462306a36Sopenharmony_ci if (info) { 59562306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, info->info_block, 59662306a36Sopenharmony_ci sizeof(*info->info_block)); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci kfree(info); 59962306a36Sopenharmony_ci return ERR_PTR(err); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int virtio_ccw_register_adapter_ind(struct virtio_ccw_device *vcdev, 60362306a36Sopenharmony_ci struct virtqueue *vqs[], int nvqs, 60462306a36Sopenharmony_ci struct ccw1 *ccw) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci int ret; 60762306a36Sopenharmony_ci struct virtio_thinint_area *thinint_area = NULL; 60862306a36Sopenharmony_ci unsigned long indicator_addr; 60962306a36Sopenharmony_ci struct airq_info *info; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci thinint_area = ccw_device_dma_zalloc(vcdev->cdev, 61262306a36Sopenharmony_ci sizeof(*thinint_area)); 61362306a36Sopenharmony_ci if (!thinint_area) { 61462306a36Sopenharmony_ci ret = -ENOMEM; 61562306a36Sopenharmony_ci goto out; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci /* Try to get an indicator. */ 61862306a36Sopenharmony_ci indicator_addr = get_airq_indicator(vqs, nvqs, 61962306a36Sopenharmony_ci &thinint_area->bit_nr, 62062306a36Sopenharmony_ci &vcdev->airq_info); 62162306a36Sopenharmony_ci if (!indicator_addr) { 62262306a36Sopenharmony_ci ret = -ENOSPC; 62362306a36Sopenharmony_ci goto out; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci thinint_area->indicator = virt_to_phys((void *)indicator_addr); 62662306a36Sopenharmony_ci info = vcdev->airq_info; 62762306a36Sopenharmony_ci thinint_area->summary_indicator = 62862306a36Sopenharmony_ci virt_to_phys(get_summary_indicator(info)); 62962306a36Sopenharmony_ci thinint_area->isc = VIRTIO_AIRQ_ISC; 63062306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER; 63162306a36Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 63262306a36Sopenharmony_ci ccw->count = sizeof(*thinint_area); 63362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(thinint_area); 63462306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND_ADAPTER); 63562306a36Sopenharmony_ci if (ret) { 63662306a36Sopenharmony_ci if (ret == -EOPNOTSUPP) { 63762306a36Sopenharmony_ci /* 63862306a36Sopenharmony_ci * The host does not support adapter interrupts 63962306a36Sopenharmony_ci * for virtio-ccw, stop trying. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci virtio_ccw_use_airq = 0; 64262306a36Sopenharmony_ci pr_info("Adapter interrupts unsupported on host\n"); 64362306a36Sopenharmony_ci } else 64462306a36Sopenharmony_ci dev_warn(&vcdev->cdev->dev, 64562306a36Sopenharmony_ci "enabling adapter interrupts = %d\n", ret); 64662306a36Sopenharmony_ci virtio_ccw_drop_indicators(vcdev); 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ciout: 64962306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, thinint_area, sizeof(*thinint_area)); 65062306a36Sopenharmony_ci return ret; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, 65462306a36Sopenharmony_ci struct virtqueue *vqs[], 65562306a36Sopenharmony_ci vq_callback_t *callbacks[], 65662306a36Sopenharmony_ci const char * const names[], 65762306a36Sopenharmony_ci const bool *ctx, 65862306a36Sopenharmony_ci struct irq_affinity *desc) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 66162306a36Sopenharmony_ci unsigned long *indicatorp = NULL; 66262306a36Sopenharmony_ci int ret, i, queue_idx = 0; 66362306a36Sopenharmony_ci struct ccw1 *ccw; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 66662306a36Sopenharmony_ci if (!ccw) 66762306a36Sopenharmony_ci return -ENOMEM; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci for (i = 0; i < nvqs; ++i) { 67062306a36Sopenharmony_ci if (!names[i]) { 67162306a36Sopenharmony_ci vqs[i] = NULL; 67262306a36Sopenharmony_ci continue; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci vqs[i] = virtio_ccw_setup_vq(vdev, queue_idx++, callbacks[i], 67662306a36Sopenharmony_ci names[i], ctx ? ctx[i] : false, 67762306a36Sopenharmony_ci ccw); 67862306a36Sopenharmony_ci if (IS_ERR(vqs[i])) { 67962306a36Sopenharmony_ci ret = PTR_ERR(vqs[i]); 68062306a36Sopenharmony_ci vqs[i] = NULL; 68162306a36Sopenharmony_ci goto out; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci ret = -ENOMEM; 68562306a36Sopenharmony_ci /* 68662306a36Sopenharmony_ci * We need a data area under 2G to communicate. Our payload is 68762306a36Sopenharmony_ci * the address of the indicators. 68862306a36Sopenharmony_ci */ 68962306a36Sopenharmony_ci indicatorp = ccw_device_dma_zalloc(vcdev->cdev, 69062306a36Sopenharmony_ci sizeof(indicators(vcdev))); 69162306a36Sopenharmony_ci if (!indicatorp) 69262306a36Sopenharmony_ci goto out; 69362306a36Sopenharmony_ci *indicatorp = (unsigned long) indicators(vcdev); 69462306a36Sopenharmony_ci if (vcdev->is_thinint) { 69562306a36Sopenharmony_ci ret = virtio_ccw_register_adapter_ind(vcdev, vqs, nvqs, ccw); 69662306a36Sopenharmony_ci if (ret) 69762306a36Sopenharmony_ci /* no error, just fall back to legacy interrupts */ 69862306a36Sopenharmony_ci vcdev->is_thinint = false; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci if (!vcdev->is_thinint) { 70162306a36Sopenharmony_ci /* Register queue indicators with host. */ 70262306a36Sopenharmony_ci *indicators(vcdev) = 0; 70362306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_IND; 70462306a36Sopenharmony_ci ccw->flags = 0; 70562306a36Sopenharmony_ci ccw->count = sizeof(indicators(vcdev)); 70662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(indicatorp); 70762306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); 70862306a36Sopenharmony_ci if (ret) 70962306a36Sopenharmony_ci goto out; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci /* Register indicators2 with host for config changes */ 71262306a36Sopenharmony_ci *indicatorp = (unsigned long) indicators2(vcdev); 71362306a36Sopenharmony_ci *indicators2(vcdev) = 0; 71462306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_CONF_IND; 71562306a36Sopenharmony_ci ccw->flags = 0; 71662306a36Sopenharmony_ci ccw->count = sizeof(indicators2(vcdev)); 71762306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(indicatorp); 71862306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_CONF_IND); 71962306a36Sopenharmony_ci if (ret) 72062306a36Sopenharmony_ci goto out; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (indicatorp) 72362306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, indicatorp, 72462306a36Sopenharmony_ci sizeof(indicators(vcdev))); 72562306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ciout: 72862306a36Sopenharmony_ci if (indicatorp) 72962306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, indicatorp, 73062306a36Sopenharmony_ci sizeof(indicators(vcdev))); 73162306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 73262306a36Sopenharmony_ci virtio_ccw_del_vqs(vdev); 73362306a36Sopenharmony_ci return ret; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic void virtio_ccw_reset(struct virtio_device *vdev) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 73962306a36Sopenharmony_ci struct ccw1 *ccw; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 74262306a36Sopenharmony_ci if (!ccw) 74362306a36Sopenharmony_ci return; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* Zero status bits. */ 74662306a36Sopenharmony_ci vcdev->dma_area->status = 0; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* Send a reset ccw on device. */ 74962306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_VDEV_RESET; 75062306a36Sopenharmony_ci ccw->flags = 0; 75162306a36Sopenharmony_ci ccw->count = 0; 75262306a36Sopenharmony_ci ccw->cda = 0; 75362306a36Sopenharmony_ci ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_RESET); 75462306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic u64 virtio_ccw_get_features(struct virtio_device *vdev) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 76062306a36Sopenharmony_ci struct virtio_feature_desc *features; 76162306a36Sopenharmony_ci int ret; 76262306a36Sopenharmony_ci u64 rc; 76362306a36Sopenharmony_ci struct ccw1 *ccw; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 76662306a36Sopenharmony_ci if (!ccw) 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci features = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*features)); 77062306a36Sopenharmony_ci if (!features) { 77162306a36Sopenharmony_ci rc = 0; 77262306a36Sopenharmony_ci goto out_free; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci /* Read the feature bits from the host. */ 77562306a36Sopenharmony_ci features->index = 0; 77662306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_FEAT; 77762306a36Sopenharmony_ci ccw->flags = 0; 77862306a36Sopenharmony_ci ccw->count = sizeof(*features); 77962306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(features); 78062306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT); 78162306a36Sopenharmony_ci if (ret) { 78262306a36Sopenharmony_ci rc = 0; 78362306a36Sopenharmony_ci goto out_free; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci rc = le32_to_cpu(features->features); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (vcdev->revision == 0) 78962306a36Sopenharmony_ci goto out_free; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Read second half of the feature bits from the host. */ 79262306a36Sopenharmony_ci features->index = 1; 79362306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_FEAT; 79462306a36Sopenharmony_ci ccw->flags = 0; 79562306a36Sopenharmony_ci ccw->count = sizeof(*features); 79662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(features); 79762306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT); 79862306a36Sopenharmony_ci if (ret == 0) 79962306a36Sopenharmony_ci rc |= (u64)le32_to_cpu(features->features) << 32; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ciout_free: 80262306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, features, sizeof(*features)); 80362306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 80462306a36Sopenharmony_ci return rc; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic void ccw_transport_features(struct virtio_device *vdev) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci /* 81062306a36Sopenharmony_ci * Currently nothing to do here. 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int virtio_ccw_finalize_features(struct virtio_device *vdev) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 81762306a36Sopenharmony_ci struct virtio_feature_desc *features; 81862306a36Sopenharmony_ci struct ccw1 *ccw; 81962306a36Sopenharmony_ci int ret; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (vcdev->revision >= 1 && 82262306a36Sopenharmony_ci !__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) { 82362306a36Sopenharmony_ci dev_err(&vdev->dev, "virtio: device uses revision 1 " 82462306a36Sopenharmony_ci "but does not have VIRTIO_F_VERSION_1\n"); 82562306a36Sopenharmony_ci return -EINVAL; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 82962306a36Sopenharmony_ci if (!ccw) 83062306a36Sopenharmony_ci return -ENOMEM; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci features = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*features)); 83362306a36Sopenharmony_ci if (!features) { 83462306a36Sopenharmony_ci ret = -ENOMEM; 83562306a36Sopenharmony_ci goto out_free; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci /* Give virtio_ring a chance to accept features. */ 83862306a36Sopenharmony_ci vring_transport_features(vdev); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* Give virtio_ccw a chance to accept features. */ 84162306a36Sopenharmony_ci ccw_transport_features(vdev); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci features->index = 0; 84462306a36Sopenharmony_ci features->features = cpu_to_le32((u32)vdev->features); 84562306a36Sopenharmony_ci /* Write the first half of the feature bits to the host. */ 84662306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_WRITE_FEAT; 84762306a36Sopenharmony_ci ccw->flags = 0; 84862306a36Sopenharmony_ci ccw->count = sizeof(*features); 84962306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(features); 85062306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT); 85162306a36Sopenharmony_ci if (ret) 85262306a36Sopenharmony_ci goto out_free; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (vcdev->revision == 0) 85562306a36Sopenharmony_ci goto out_free; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci features->index = 1; 85862306a36Sopenharmony_ci features->features = cpu_to_le32(vdev->features >> 32); 85962306a36Sopenharmony_ci /* Write the second half of the feature bits to the host. */ 86062306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_WRITE_FEAT; 86162306a36Sopenharmony_ci ccw->flags = 0; 86262306a36Sopenharmony_ci ccw->count = sizeof(*features); 86362306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(features); 86462306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ciout_free: 86762306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, features, sizeof(*features)); 86862306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return ret; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic void virtio_ccw_get_config(struct virtio_device *vdev, 87462306a36Sopenharmony_ci unsigned int offset, void *buf, unsigned len) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 87762306a36Sopenharmony_ci int ret; 87862306a36Sopenharmony_ci struct ccw1 *ccw; 87962306a36Sopenharmony_ci void *config_area; 88062306a36Sopenharmony_ci unsigned long flags; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 88362306a36Sopenharmony_ci if (!ccw) 88462306a36Sopenharmony_ci return; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci config_area = ccw_device_dma_zalloc(vcdev->cdev, 88762306a36Sopenharmony_ci VIRTIO_CCW_CONFIG_SIZE); 88862306a36Sopenharmony_ci if (!config_area) 88962306a36Sopenharmony_ci goto out_free; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* Read the config area from the host. */ 89262306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_CONF; 89362306a36Sopenharmony_ci ccw->flags = 0; 89462306a36Sopenharmony_ci ccw->count = offset + len; 89562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(config_area); 89662306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_CONFIG); 89762306a36Sopenharmony_ci if (ret) 89862306a36Sopenharmony_ci goto out_free; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 90162306a36Sopenharmony_ci memcpy(vcdev->config, config_area, offset + len); 90262306a36Sopenharmony_ci if (vcdev->config_ready < offset + len) 90362306a36Sopenharmony_ci vcdev->config_ready = offset + len; 90462306a36Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 90562306a36Sopenharmony_ci if (buf) 90662306a36Sopenharmony_ci memcpy(buf, config_area + offset, len); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ciout_free: 90962306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, config_area, VIRTIO_CCW_CONFIG_SIZE); 91062306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic void virtio_ccw_set_config(struct virtio_device *vdev, 91462306a36Sopenharmony_ci unsigned int offset, const void *buf, 91562306a36Sopenharmony_ci unsigned len) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 91862306a36Sopenharmony_ci struct ccw1 *ccw; 91962306a36Sopenharmony_ci void *config_area; 92062306a36Sopenharmony_ci unsigned long flags; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 92362306a36Sopenharmony_ci if (!ccw) 92462306a36Sopenharmony_ci return; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci config_area = ccw_device_dma_zalloc(vcdev->cdev, 92762306a36Sopenharmony_ci VIRTIO_CCW_CONFIG_SIZE); 92862306a36Sopenharmony_ci if (!config_area) 92962306a36Sopenharmony_ci goto out_free; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* Make sure we don't overwrite fields. */ 93262306a36Sopenharmony_ci if (vcdev->config_ready < offset) 93362306a36Sopenharmony_ci virtio_ccw_get_config(vdev, 0, NULL, offset); 93462306a36Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 93562306a36Sopenharmony_ci memcpy(&vcdev->config[offset], buf, len); 93662306a36Sopenharmony_ci /* Write the config area to the host. */ 93762306a36Sopenharmony_ci memcpy(config_area, vcdev->config, sizeof(vcdev->config)); 93862306a36Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 93962306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_WRITE_CONF; 94062306a36Sopenharmony_ci ccw->flags = 0; 94162306a36Sopenharmony_ci ccw->count = offset + len; 94262306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(config_area); 94362306a36Sopenharmony_ci ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_CONFIG); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ciout_free: 94662306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, config_area, VIRTIO_CCW_CONFIG_SIZE); 94762306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic u8 virtio_ccw_get_status(struct virtio_device *vdev) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 95362306a36Sopenharmony_ci u8 old_status = vcdev->dma_area->status; 95462306a36Sopenharmony_ci struct ccw1 *ccw; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (vcdev->revision < 2) 95762306a36Sopenharmony_ci return vcdev->dma_area->status; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 96062306a36Sopenharmony_ci if (!ccw) 96162306a36Sopenharmony_ci return old_status; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_STATUS; 96462306a36Sopenharmony_ci ccw->flags = 0; 96562306a36Sopenharmony_ci ccw->count = sizeof(vcdev->dma_area->status); 96662306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(&vcdev->dma_area->status); 96762306a36Sopenharmony_ci ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_STATUS); 96862306a36Sopenharmony_ci/* 96962306a36Sopenharmony_ci * If the channel program failed (should only happen if the device 97062306a36Sopenharmony_ci * was hotunplugged, and then we clean up via the machine check 97162306a36Sopenharmony_ci * handler anyway), vcdev->dma_area->status was not overwritten and we just 97262306a36Sopenharmony_ci * return the old status, which is fine. 97362306a36Sopenharmony_ci*/ 97462306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci return vcdev->dma_area->status; 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistatic void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 98262306a36Sopenharmony_ci u8 old_status = vcdev->dma_area->status; 98362306a36Sopenharmony_ci struct ccw1 *ccw; 98462306a36Sopenharmony_ci int ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 98762306a36Sopenharmony_ci if (!ccw) 98862306a36Sopenharmony_ci return; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* Write the status to the host. */ 99162306a36Sopenharmony_ci vcdev->dma_area->status = status; 99262306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_WRITE_STATUS; 99362306a36Sopenharmony_ci ccw->flags = 0; 99462306a36Sopenharmony_ci ccw->count = sizeof(status); 99562306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(&vcdev->dma_area->status); 99662306a36Sopenharmony_ci /* We use ssch for setting the status which is a serializing 99762306a36Sopenharmony_ci * instruction that guarantees the memory writes have 99862306a36Sopenharmony_ci * completed before ssch. 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_STATUS); 100162306a36Sopenharmony_ci /* Write failed? We assume status is unchanged. */ 100262306a36Sopenharmony_ci if (ret) 100362306a36Sopenharmony_ci vcdev->dma_area->status = old_status; 100462306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic const char *virtio_ccw_bus_name(struct virtio_device *vdev) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci return dev_name(&vcdev->cdev->dev); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic void virtio_ccw_synchronize_cbs(struct virtio_device *vdev) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 101762306a36Sopenharmony_ci struct airq_info *info = vcdev->airq_info; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (info) { 102062306a36Sopenharmony_ci /* 102162306a36Sopenharmony_ci * This device uses adapter interrupts: synchronize with 102262306a36Sopenharmony_ci * vring_interrupt() called by virtio_airq_handler() 102362306a36Sopenharmony_ci * via the indicator area lock. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_ci write_lock_irq(&info->lock); 102662306a36Sopenharmony_ci write_unlock_irq(&info->lock); 102762306a36Sopenharmony_ci } else { 102862306a36Sopenharmony_ci /* This device uses classic interrupts: synchronize 102962306a36Sopenharmony_ci * with vring_interrupt() called by 103062306a36Sopenharmony_ci * virtio_ccw_int_handler() via the per-device 103162306a36Sopenharmony_ci * irq_lock 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_ci write_lock_irq(&vcdev->irq_lock); 103462306a36Sopenharmony_ci write_unlock_irq(&vcdev->irq_lock); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic const struct virtio_config_ops virtio_ccw_config_ops = { 103962306a36Sopenharmony_ci .get_features = virtio_ccw_get_features, 104062306a36Sopenharmony_ci .finalize_features = virtio_ccw_finalize_features, 104162306a36Sopenharmony_ci .get = virtio_ccw_get_config, 104262306a36Sopenharmony_ci .set = virtio_ccw_set_config, 104362306a36Sopenharmony_ci .get_status = virtio_ccw_get_status, 104462306a36Sopenharmony_ci .set_status = virtio_ccw_set_status, 104562306a36Sopenharmony_ci .reset = virtio_ccw_reset, 104662306a36Sopenharmony_ci .find_vqs = virtio_ccw_find_vqs, 104762306a36Sopenharmony_ci .del_vqs = virtio_ccw_del_vqs, 104862306a36Sopenharmony_ci .bus_name = virtio_ccw_bus_name, 104962306a36Sopenharmony_ci .synchronize_cbs = virtio_ccw_synchronize_cbs, 105062306a36Sopenharmony_ci}; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci/* 105462306a36Sopenharmony_ci * ccw bus driver related functions 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic void virtio_ccw_release_dev(struct device *_d) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct virtio_device *dev = dev_to_virtio(_d); 106062306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(dev); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, vcdev->dma_area, 106362306a36Sopenharmony_ci sizeof(*vcdev->dma_area)); 106462306a36Sopenharmony_ci kfree(vcdev); 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic int irb_is_error(struct irb *irb) 106862306a36Sopenharmony_ci{ 106962306a36Sopenharmony_ci if (scsw_cstat(&irb->scsw) != 0) 107062306a36Sopenharmony_ci return 1; 107162306a36Sopenharmony_ci if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) 107262306a36Sopenharmony_ci return 1; 107362306a36Sopenharmony_ci if (scsw_cc(&irb->scsw) != 0) 107462306a36Sopenharmony_ci return 1; 107562306a36Sopenharmony_ci return 0; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev, 107962306a36Sopenharmony_ci int index) 108062306a36Sopenharmony_ci{ 108162306a36Sopenharmony_ci struct virtio_ccw_vq_info *info; 108262306a36Sopenharmony_ci unsigned long flags; 108362306a36Sopenharmony_ci struct virtqueue *vq; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci vq = NULL; 108662306a36Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 108762306a36Sopenharmony_ci list_for_each_entry(info, &vcdev->virtqueues, node) { 108862306a36Sopenharmony_ci if (info->vq->index == index) { 108962306a36Sopenharmony_ci vq = info->vq; 109062306a36Sopenharmony_ci break; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 109462306a36Sopenharmony_ci return vq; 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic void virtio_ccw_check_activity(struct virtio_ccw_device *vcdev, 109862306a36Sopenharmony_ci __u32 activity) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci if (vcdev->curr_io & activity) { 110162306a36Sopenharmony_ci switch (activity) { 110262306a36Sopenharmony_ci case VIRTIO_CCW_DOING_READ_FEAT: 110362306a36Sopenharmony_ci case VIRTIO_CCW_DOING_WRITE_FEAT: 110462306a36Sopenharmony_ci case VIRTIO_CCW_DOING_READ_CONFIG: 110562306a36Sopenharmony_ci case VIRTIO_CCW_DOING_WRITE_CONFIG: 110662306a36Sopenharmony_ci case VIRTIO_CCW_DOING_WRITE_STATUS: 110762306a36Sopenharmony_ci case VIRTIO_CCW_DOING_READ_STATUS: 110862306a36Sopenharmony_ci case VIRTIO_CCW_DOING_SET_VQ: 110962306a36Sopenharmony_ci case VIRTIO_CCW_DOING_SET_IND: 111062306a36Sopenharmony_ci case VIRTIO_CCW_DOING_SET_CONF_IND: 111162306a36Sopenharmony_ci case VIRTIO_CCW_DOING_RESET: 111262306a36Sopenharmony_ci case VIRTIO_CCW_DOING_READ_VQ_CONF: 111362306a36Sopenharmony_ci case VIRTIO_CCW_DOING_SET_IND_ADAPTER: 111462306a36Sopenharmony_ci case VIRTIO_CCW_DOING_SET_VIRTIO_REV: 111562306a36Sopenharmony_ci vcdev->curr_io &= ~activity; 111662306a36Sopenharmony_ci wake_up(&vcdev->wait_q); 111762306a36Sopenharmony_ci break; 111862306a36Sopenharmony_ci default: 111962306a36Sopenharmony_ci /* don't know what to do... */ 112062306a36Sopenharmony_ci dev_warn(&vcdev->cdev->dev, 112162306a36Sopenharmony_ci "Suspicious activity '%08x'\n", activity); 112262306a36Sopenharmony_ci WARN_ON(1); 112362306a36Sopenharmony_ci break; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic void virtio_ccw_int_handler(struct ccw_device *cdev, 112962306a36Sopenharmony_ci unsigned long intparm, 113062306a36Sopenharmony_ci struct irb *irb) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci __u32 activity = intparm & VIRTIO_CCW_INTPARM_MASK; 113362306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 113462306a36Sopenharmony_ci int i; 113562306a36Sopenharmony_ci struct virtqueue *vq; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (!vcdev) 113862306a36Sopenharmony_ci return; 113962306a36Sopenharmony_ci if (IS_ERR(irb)) { 114062306a36Sopenharmony_ci vcdev->err = PTR_ERR(irb); 114162306a36Sopenharmony_ci virtio_ccw_check_activity(vcdev, activity); 114262306a36Sopenharmony_ci /* Don't poke around indicators, something's wrong. */ 114362306a36Sopenharmony_ci return; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci /* Check if it's a notification from the host. */ 114662306a36Sopenharmony_ci if ((intparm == 0) && 114762306a36Sopenharmony_ci (scsw_stctl(&irb->scsw) == 114862306a36Sopenharmony_ci (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) { 114962306a36Sopenharmony_ci /* OK */ 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci if (irb_is_error(irb)) { 115262306a36Sopenharmony_ci /* Command reject? */ 115362306a36Sopenharmony_ci if ((scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && 115462306a36Sopenharmony_ci (irb->ecw[0] & SNS0_CMD_REJECT)) 115562306a36Sopenharmony_ci vcdev->err = -EOPNOTSUPP; 115662306a36Sopenharmony_ci else 115762306a36Sopenharmony_ci /* Map everything else to -EIO. */ 115862306a36Sopenharmony_ci vcdev->err = -EIO; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci virtio_ccw_check_activity(vcdev, activity); 116162306a36Sopenharmony_ci#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION 116262306a36Sopenharmony_ci /* 116362306a36Sopenharmony_ci * Paired with virtio_ccw_synchronize_cbs() and interrupts are 116462306a36Sopenharmony_ci * disabled here. 116562306a36Sopenharmony_ci */ 116662306a36Sopenharmony_ci read_lock(&vcdev->irq_lock); 116762306a36Sopenharmony_ci#endif 116862306a36Sopenharmony_ci for_each_set_bit(i, indicators(vcdev), 116962306a36Sopenharmony_ci sizeof(*indicators(vcdev)) * BITS_PER_BYTE) { 117062306a36Sopenharmony_ci /* The bit clear must happen before the vring kick. */ 117162306a36Sopenharmony_ci clear_bit(i, indicators(vcdev)); 117262306a36Sopenharmony_ci barrier(); 117362306a36Sopenharmony_ci vq = virtio_ccw_vq_by_ind(vcdev, i); 117462306a36Sopenharmony_ci vring_interrupt(0, vq); 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci#ifdef CONFIG_VIRTIO_HARDEN_NOTIFICATION 117762306a36Sopenharmony_ci read_unlock(&vcdev->irq_lock); 117862306a36Sopenharmony_ci#endif 117962306a36Sopenharmony_ci if (test_bit(0, indicators2(vcdev))) { 118062306a36Sopenharmony_ci virtio_config_changed(&vcdev->vdev); 118162306a36Sopenharmony_ci clear_bit(0, indicators2(vcdev)); 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/* 118662306a36Sopenharmony_ci * We usually want to autoonline all devices, but give the admin 118762306a36Sopenharmony_ci * a way to exempt devices from this. 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ci#define __DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ 119062306a36Sopenharmony_ci (8*sizeof(long))) 119162306a36Sopenharmony_cistatic unsigned long devs_no_auto[__MAX_SSID + 1][__DEV_WORDS]; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic char *no_auto = ""; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cimodule_param(no_auto, charp, 0444); 119662306a36Sopenharmony_ciMODULE_PARM_DESC(no_auto, "list of ccw bus id ranges not to be auto-onlined"); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic int virtio_ccw_check_autoonline(struct ccw_device *cdev) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci struct ccw_dev_id id; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci ccw_device_get_id(cdev, &id); 120362306a36Sopenharmony_ci if (test_bit(id.devno, devs_no_auto[id.ssid])) 120462306a36Sopenharmony_ci return 0; 120562306a36Sopenharmony_ci return 1; 120662306a36Sopenharmony_ci} 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cistatic void virtio_ccw_auto_online(void *data, async_cookie_t cookie) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct ccw_device *cdev = data; 121162306a36Sopenharmony_ci int ret; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci ret = ccw_device_set_online(cdev); 121462306a36Sopenharmony_ci if (ret) 121562306a36Sopenharmony_ci dev_warn(&cdev->dev, "Failed to set online: %d\n", ret); 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cistatic int virtio_ccw_probe(struct ccw_device *cdev) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci cdev->handler = virtio_ccw_int_handler; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (virtio_ccw_check_autoonline(cdev)) 122362306a36Sopenharmony_ci async_schedule(virtio_ccw_auto_online, cdev); 122462306a36Sopenharmony_ci return 0; 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci unsigned long flags; 123062306a36Sopenharmony_ci struct virtio_ccw_device *vcdev; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 123362306a36Sopenharmony_ci vcdev = dev_get_drvdata(&cdev->dev); 123462306a36Sopenharmony_ci if (!vcdev || vcdev->going_away) { 123562306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 123662306a36Sopenharmony_ci return NULL; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci vcdev->going_away = true; 123962306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 124062306a36Sopenharmony_ci return vcdev; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic void virtio_ccw_remove(struct ccw_device *cdev) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci unsigned long flags; 124662306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (vcdev && cdev->online) { 124962306a36Sopenharmony_ci if (vcdev->device_lost) 125062306a36Sopenharmony_ci virtio_break_device(&vcdev->vdev); 125162306a36Sopenharmony_ci unregister_virtio_device(&vcdev->vdev); 125262306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 125362306a36Sopenharmony_ci dev_set_drvdata(&cdev->dev, NULL); 125462306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci cdev->handler = NULL; 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic int virtio_ccw_offline(struct ccw_device *cdev) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci unsigned long flags; 126262306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (!vcdev) 126562306a36Sopenharmony_ci return 0; 126662306a36Sopenharmony_ci if (vcdev->device_lost) 126762306a36Sopenharmony_ci virtio_break_device(&vcdev->vdev); 126862306a36Sopenharmony_ci unregister_virtio_device(&vcdev->vdev); 126962306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 127062306a36Sopenharmony_ci dev_set_drvdata(&cdev->dev, NULL); 127162306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 127262306a36Sopenharmony_ci return 0; 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic int virtio_ccw_set_transport_rev(struct virtio_ccw_device *vcdev) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci struct virtio_rev_info *rev; 127862306a36Sopenharmony_ci struct ccw1 *ccw; 127962306a36Sopenharmony_ci int ret; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 128262306a36Sopenharmony_ci if (!ccw) 128362306a36Sopenharmony_ci return -ENOMEM; 128462306a36Sopenharmony_ci rev = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*rev)); 128562306a36Sopenharmony_ci if (!rev) { 128662306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 128762306a36Sopenharmony_ci return -ENOMEM; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* Set transport revision */ 129162306a36Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_VIRTIO_REV; 129262306a36Sopenharmony_ci ccw->flags = 0; 129362306a36Sopenharmony_ci ccw->count = sizeof(*rev); 129462306a36Sopenharmony_ci ccw->cda = (__u32)virt_to_phys(rev); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci vcdev->revision = VIRTIO_CCW_REV_MAX; 129762306a36Sopenharmony_ci do { 129862306a36Sopenharmony_ci rev->revision = vcdev->revision; 129962306a36Sopenharmony_ci /* none of our supported revisions carry payload */ 130062306a36Sopenharmony_ci rev->length = 0; 130162306a36Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, 130262306a36Sopenharmony_ci VIRTIO_CCW_DOING_SET_VIRTIO_REV); 130362306a36Sopenharmony_ci if (ret == -EOPNOTSUPP) { 130462306a36Sopenharmony_ci if (vcdev->revision == 0) 130562306a36Sopenharmony_ci /* 130662306a36Sopenharmony_ci * The host device does not support setting 130762306a36Sopenharmony_ci * the revision: let's operate it in legacy 130862306a36Sopenharmony_ci * mode. 130962306a36Sopenharmony_ci */ 131062306a36Sopenharmony_ci ret = 0; 131162306a36Sopenharmony_ci else 131262306a36Sopenharmony_ci vcdev->revision--; 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci } while (ret == -EOPNOTSUPP); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 131762306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, rev, sizeof(*rev)); 131862306a36Sopenharmony_ci return ret; 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cistatic int virtio_ccw_online(struct ccw_device *cdev) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci int ret; 132462306a36Sopenharmony_ci struct virtio_ccw_device *vcdev; 132562306a36Sopenharmony_ci unsigned long flags; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); 132862306a36Sopenharmony_ci if (!vcdev) { 132962306a36Sopenharmony_ci dev_warn(&cdev->dev, "Could not get memory for virtio\n"); 133062306a36Sopenharmony_ci ret = -ENOMEM; 133162306a36Sopenharmony_ci goto out_free; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci vcdev->vdev.dev.parent = &cdev->dev; 133462306a36Sopenharmony_ci vcdev->cdev = cdev; 133562306a36Sopenharmony_ci vcdev->dma_area = ccw_device_dma_zalloc(vcdev->cdev, 133662306a36Sopenharmony_ci sizeof(*vcdev->dma_area)); 133762306a36Sopenharmony_ci if (!vcdev->dma_area) { 133862306a36Sopenharmony_ci ret = -ENOMEM; 133962306a36Sopenharmony_ci goto out_free; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci vcdev->is_thinint = virtio_ccw_use_airq; /* at least try */ 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci vcdev->vdev.dev.release = virtio_ccw_release_dev; 134562306a36Sopenharmony_ci vcdev->vdev.config = &virtio_ccw_config_ops; 134662306a36Sopenharmony_ci init_waitqueue_head(&vcdev->wait_q); 134762306a36Sopenharmony_ci INIT_LIST_HEAD(&vcdev->virtqueues); 134862306a36Sopenharmony_ci spin_lock_init(&vcdev->lock); 134962306a36Sopenharmony_ci rwlock_init(&vcdev->irq_lock); 135062306a36Sopenharmony_ci mutex_init(&vcdev->io_lock); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 135362306a36Sopenharmony_ci dev_set_drvdata(&cdev->dev, vcdev); 135462306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 135562306a36Sopenharmony_ci vcdev->vdev.id.vendor = cdev->id.cu_type; 135662306a36Sopenharmony_ci vcdev->vdev.id.device = cdev->id.cu_model; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci ret = virtio_ccw_set_transport_rev(vcdev); 135962306a36Sopenharmony_ci if (ret) 136062306a36Sopenharmony_ci goto out_free; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci ret = register_virtio_device(&vcdev->vdev); 136362306a36Sopenharmony_ci if (ret) { 136462306a36Sopenharmony_ci dev_warn(&cdev->dev, "Failed to register virtio device: %d\n", 136562306a36Sopenharmony_ci ret); 136662306a36Sopenharmony_ci goto out_put; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci return 0; 136962306a36Sopenharmony_ciout_put: 137062306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 137162306a36Sopenharmony_ci dev_set_drvdata(&cdev->dev, NULL); 137262306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 137362306a36Sopenharmony_ci put_device(&vcdev->vdev.dev); 137462306a36Sopenharmony_ci return ret; 137562306a36Sopenharmony_ciout_free: 137662306a36Sopenharmony_ci if (vcdev) { 137762306a36Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, vcdev->dma_area, 137862306a36Sopenharmony_ci sizeof(*vcdev->dma_area)); 137962306a36Sopenharmony_ci } 138062306a36Sopenharmony_ci kfree(vcdev); 138162306a36Sopenharmony_ci return ret; 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci int rc; 138762306a36Sopenharmony_ci struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci /* 139062306a36Sopenharmony_ci * Make sure vcdev is set 139162306a36Sopenharmony_ci * i.e. set_offline/remove callback not already running 139262306a36Sopenharmony_ci */ 139362306a36Sopenharmony_ci if (!vcdev) 139462306a36Sopenharmony_ci return NOTIFY_DONE; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci switch (event) { 139762306a36Sopenharmony_ci case CIO_GONE: 139862306a36Sopenharmony_ci vcdev->device_lost = true; 139962306a36Sopenharmony_ci rc = NOTIFY_DONE; 140062306a36Sopenharmony_ci break; 140162306a36Sopenharmony_ci case CIO_OPER: 140262306a36Sopenharmony_ci rc = NOTIFY_OK; 140362306a36Sopenharmony_ci break; 140462306a36Sopenharmony_ci default: 140562306a36Sopenharmony_ci rc = NOTIFY_DONE; 140662306a36Sopenharmony_ci break; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci return rc; 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_cistatic struct ccw_device_id virtio_ids[] = { 141262306a36Sopenharmony_ci { CCW_DEVICE(0x3832, 0) }, 141362306a36Sopenharmony_ci {}, 141462306a36Sopenharmony_ci}; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic struct ccw_driver virtio_ccw_driver = { 141762306a36Sopenharmony_ci .driver = { 141862306a36Sopenharmony_ci .owner = THIS_MODULE, 141962306a36Sopenharmony_ci .name = "virtio_ccw", 142062306a36Sopenharmony_ci }, 142162306a36Sopenharmony_ci .ids = virtio_ids, 142262306a36Sopenharmony_ci .probe = virtio_ccw_probe, 142362306a36Sopenharmony_ci .remove = virtio_ccw_remove, 142462306a36Sopenharmony_ci .set_offline = virtio_ccw_offline, 142562306a36Sopenharmony_ci .set_online = virtio_ccw_online, 142662306a36Sopenharmony_ci .notify = virtio_ccw_cio_notify, 142762306a36Sopenharmony_ci .int_class = IRQIO_VIR, 142862306a36Sopenharmony_ci}; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_cistatic int __init pure_hex(char **cp, unsigned int *val, int min_digit, 143162306a36Sopenharmony_ci int max_digit, int max_val) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci int diff; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci diff = 0; 143662306a36Sopenharmony_ci *val = 0; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci while (diff <= max_digit) { 143962306a36Sopenharmony_ci int value = hex_to_bin(**cp); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci if (value < 0) 144262306a36Sopenharmony_ci break; 144362306a36Sopenharmony_ci *val = *val * 16 + value; 144462306a36Sopenharmony_ci (*cp)++; 144562306a36Sopenharmony_ci diff++; 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if ((diff < min_digit) || (diff > max_digit) || (*val > max_val)) 144962306a36Sopenharmony_ci return 1; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return 0; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic int __init parse_busid(char *str, unsigned int *cssid, 145562306a36Sopenharmony_ci unsigned int *ssid, unsigned int *devno) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci char *str_work; 145862306a36Sopenharmony_ci int rc, ret; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci rc = 1; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (*str == '\0') 146362306a36Sopenharmony_ci goto out; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci str_work = str; 146662306a36Sopenharmony_ci ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID); 146762306a36Sopenharmony_ci if (ret || (str_work[0] != '.')) 146862306a36Sopenharmony_ci goto out; 146962306a36Sopenharmony_ci str_work++; 147062306a36Sopenharmony_ci ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID); 147162306a36Sopenharmony_ci if (ret || (str_work[0] != '.')) 147262306a36Sopenharmony_ci goto out; 147362306a36Sopenharmony_ci str_work++; 147462306a36Sopenharmony_ci ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL); 147562306a36Sopenharmony_ci if (ret || (str_work[0] != '\0')) 147662306a36Sopenharmony_ci goto out; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci rc = 0; 147962306a36Sopenharmony_ciout: 148062306a36Sopenharmony_ci return rc; 148162306a36Sopenharmony_ci} 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_cistatic void __init no_auto_parse(void) 148462306a36Sopenharmony_ci{ 148562306a36Sopenharmony_ci unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to; 148662306a36Sopenharmony_ci char *parm, *str; 148762306a36Sopenharmony_ci int rc; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci str = no_auto; 149062306a36Sopenharmony_ci while ((parm = strsep(&str, ","))) { 149162306a36Sopenharmony_ci rc = parse_busid(strsep(&parm, "-"), &from_cssid, 149262306a36Sopenharmony_ci &from_ssid, &from); 149362306a36Sopenharmony_ci if (rc) 149462306a36Sopenharmony_ci continue; 149562306a36Sopenharmony_ci if (parm != NULL) { 149662306a36Sopenharmony_ci rc = parse_busid(parm, &to_cssid, 149762306a36Sopenharmony_ci &to_ssid, &to); 149862306a36Sopenharmony_ci if ((from_ssid > to_ssid) || 149962306a36Sopenharmony_ci ((from_ssid == to_ssid) && (from > to))) 150062306a36Sopenharmony_ci rc = -EINVAL; 150162306a36Sopenharmony_ci } else { 150262306a36Sopenharmony_ci to_cssid = from_cssid; 150362306a36Sopenharmony_ci to_ssid = from_ssid; 150462306a36Sopenharmony_ci to = from; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci if (rc) 150762306a36Sopenharmony_ci continue; 150862306a36Sopenharmony_ci while ((from_ssid < to_ssid) || 150962306a36Sopenharmony_ci ((from_ssid == to_ssid) && (from <= to))) { 151062306a36Sopenharmony_ci set_bit(from, devs_no_auto[from_ssid]); 151162306a36Sopenharmony_ci from++; 151262306a36Sopenharmony_ci if (from > __MAX_SUBCHANNEL) { 151362306a36Sopenharmony_ci from_ssid++; 151462306a36Sopenharmony_ci from = 0; 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci} 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_cistatic int __init virtio_ccw_init(void) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci int rc; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* parse no_auto string before we do anything further */ 152562306a36Sopenharmony_ci no_auto_parse(); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci summary_indicators = cio_dma_zalloc(MAX_AIRQ_AREAS); 152862306a36Sopenharmony_ci if (!summary_indicators) 152962306a36Sopenharmony_ci return -ENOMEM; 153062306a36Sopenharmony_ci rc = ccw_driver_register(&virtio_ccw_driver); 153162306a36Sopenharmony_ci if (rc) 153262306a36Sopenharmony_ci cio_dma_free(summary_indicators, MAX_AIRQ_AREAS); 153362306a36Sopenharmony_ci return rc; 153462306a36Sopenharmony_ci} 153562306a36Sopenharmony_cidevice_initcall(virtio_ccw_init); 1536