18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ccw based virtio transport 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2012, 2014 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/memblock.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/virtio.h> 158c2ecf20Sopenharmony_ci#include <linux/virtio_config.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/virtio_ring.h> 198c2ecf20Sopenharmony_ci#include <linux/pfn.h> 208c2ecf20Sopenharmony_ci#include <linux/async.h> 218c2ecf20Sopenharmony_ci#include <linux/wait.h> 228c2ecf20Sopenharmony_ci#include <linux/list.h> 238c2ecf20Sopenharmony_ci#include <linux/bitops.h> 248c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 258c2ecf20Sopenharmony_ci#include <linux/io.h> 268c2ecf20Sopenharmony_ci#include <linux/kvm_para.h> 278c2ecf20Sopenharmony_ci#include <linux/notifier.h> 288c2ecf20Sopenharmony_ci#include <asm/diag.h> 298c2ecf20Sopenharmony_ci#include <asm/setup.h> 308c2ecf20Sopenharmony_ci#include <asm/irq.h> 318c2ecf20Sopenharmony_ci#include <asm/cio.h> 328c2ecf20Sopenharmony_ci#include <asm/ccwdev.h> 338c2ecf20Sopenharmony_ci#include <asm/virtio-ccw.h> 348c2ecf20Sopenharmony_ci#include <asm/isc.h> 358c2ecf20Sopenharmony_ci#include <asm/airq.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * virtio related functions 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct vq_config_block { 428c2ecf20Sopenharmony_ci __u16 index; 438c2ecf20Sopenharmony_ci __u16 num; 448c2ecf20Sopenharmony_ci} __packed; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define VIRTIO_CCW_CONFIG_SIZE 0x100 478c2ecf20Sopenharmony_ci/* same as PCI config space size, should be enough for all drivers */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct vcdev_dma_area { 508c2ecf20Sopenharmony_ci unsigned long indicators; 518c2ecf20Sopenharmony_ci unsigned long indicators2; 528c2ecf20Sopenharmony_ci struct vq_config_block config_block; 538c2ecf20Sopenharmony_ci __u8 status; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct virtio_ccw_device { 578c2ecf20Sopenharmony_ci struct virtio_device vdev; 588c2ecf20Sopenharmony_ci __u8 config[VIRTIO_CCW_CONFIG_SIZE]; 598c2ecf20Sopenharmony_ci struct ccw_device *cdev; 608c2ecf20Sopenharmony_ci __u32 curr_io; 618c2ecf20Sopenharmony_ci int err; 628c2ecf20Sopenharmony_ci unsigned int revision; /* Transport revision */ 638c2ecf20Sopenharmony_ci wait_queue_head_t wait_q; 648c2ecf20Sopenharmony_ci spinlock_t lock; 658c2ecf20Sopenharmony_ci struct mutex io_lock; /* Serializes I/O requests */ 668c2ecf20Sopenharmony_ci struct list_head virtqueues; 678c2ecf20Sopenharmony_ci bool is_thinint; 688c2ecf20Sopenharmony_ci bool going_away; 698c2ecf20Sopenharmony_ci bool device_lost; 708c2ecf20Sopenharmony_ci unsigned int config_ready; 718c2ecf20Sopenharmony_ci void *airq_info; 728c2ecf20Sopenharmony_ci struct vcdev_dma_area *dma_area; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic inline unsigned long *indicators(struct virtio_ccw_device *vcdev) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci return &vcdev->dma_area->indicators; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline unsigned long *indicators2(struct virtio_ccw_device *vcdev) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci return &vcdev->dma_area->indicators2; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct vq_info_block_legacy { 868c2ecf20Sopenharmony_ci __u64 queue; 878c2ecf20Sopenharmony_ci __u32 align; 888c2ecf20Sopenharmony_ci __u16 index; 898c2ecf20Sopenharmony_ci __u16 num; 908c2ecf20Sopenharmony_ci} __packed; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct vq_info_block { 938c2ecf20Sopenharmony_ci __u64 desc; 948c2ecf20Sopenharmony_ci __u32 res0; 958c2ecf20Sopenharmony_ci __u16 index; 968c2ecf20Sopenharmony_ci __u16 num; 978c2ecf20Sopenharmony_ci __u64 avail; 988c2ecf20Sopenharmony_ci __u64 used; 998c2ecf20Sopenharmony_ci} __packed; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistruct virtio_feature_desc { 1028c2ecf20Sopenharmony_ci __le32 features; 1038c2ecf20Sopenharmony_ci __u8 index; 1048c2ecf20Sopenharmony_ci} __packed; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistruct virtio_thinint_area { 1078c2ecf20Sopenharmony_ci unsigned long summary_indicator; 1088c2ecf20Sopenharmony_ci unsigned long indicator; 1098c2ecf20Sopenharmony_ci u64 bit_nr; 1108c2ecf20Sopenharmony_ci u8 isc; 1118c2ecf20Sopenharmony_ci} __packed; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistruct virtio_rev_info { 1148c2ecf20Sopenharmony_ci __u16 revision; 1158c2ecf20Sopenharmony_ci __u16 length; 1168c2ecf20Sopenharmony_ci __u8 data[]; 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* the highest virtio-ccw revision we support */ 1208c2ecf20Sopenharmony_ci#define VIRTIO_CCW_REV_MAX 2 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct virtio_ccw_vq_info { 1238c2ecf20Sopenharmony_ci struct virtqueue *vq; 1248c2ecf20Sopenharmony_ci int num; 1258c2ecf20Sopenharmony_ci union { 1268c2ecf20Sopenharmony_ci struct vq_info_block s; 1278c2ecf20Sopenharmony_ci struct vq_info_block_legacy l; 1288c2ecf20Sopenharmony_ci } *info_block; 1298c2ecf20Sopenharmony_ci int bit_nr; 1308c2ecf20Sopenharmony_ci struct list_head node; 1318c2ecf20Sopenharmony_ci long cookie; 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define VIRTIO_AIRQ_ISC IO_SCH_ISC /* inherit from subchannel */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define VIRTIO_IV_BITS (L1_CACHE_BYTES * 8) 1378c2ecf20Sopenharmony_ci#define MAX_AIRQ_AREAS 20 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int virtio_ccw_use_airq = 1; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct airq_info { 1428c2ecf20Sopenharmony_ci rwlock_t lock; 1438c2ecf20Sopenharmony_ci u8 summary_indicator_idx; 1448c2ecf20Sopenharmony_ci struct airq_struct airq; 1458c2ecf20Sopenharmony_ci struct airq_iv *aiv; 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_cistatic struct airq_info *airq_areas[MAX_AIRQ_AREAS]; 1488c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(airq_areas_lock); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic u8 *summary_indicators; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic inline u8 *get_summary_indicator(struct airq_info *info) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci return summary_indicators + info->summary_indicator_idx; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#define CCW_CMD_SET_VQ 0x13 1588c2ecf20Sopenharmony_ci#define CCW_CMD_VDEV_RESET 0x33 1598c2ecf20Sopenharmony_ci#define CCW_CMD_SET_IND 0x43 1608c2ecf20Sopenharmony_ci#define CCW_CMD_SET_CONF_IND 0x53 1618c2ecf20Sopenharmony_ci#define CCW_CMD_READ_FEAT 0x12 1628c2ecf20Sopenharmony_ci#define CCW_CMD_WRITE_FEAT 0x11 1638c2ecf20Sopenharmony_ci#define CCW_CMD_READ_CONF 0x22 1648c2ecf20Sopenharmony_ci#define CCW_CMD_WRITE_CONF 0x21 1658c2ecf20Sopenharmony_ci#define CCW_CMD_WRITE_STATUS 0x31 1668c2ecf20Sopenharmony_ci#define CCW_CMD_READ_VQ_CONF 0x32 1678c2ecf20Sopenharmony_ci#define CCW_CMD_READ_STATUS 0x72 1688c2ecf20Sopenharmony_ci#define CCW_CMD_SET_IND_ADAPTER 0x73 1698c2ecf20Sopenharmony_ci#define CCW_CMD_SET_VIRTIO_REV 0x83 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_VQ 0x00010000 1728c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_RESET 0x00040000 1738c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_READ_FEAT 0x00080000 1748c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000 1758c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000 1768c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000 1778c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000 1788c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_IND 0x01000000 1798c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 1808c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 1818c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_IND_ADAPTER 0x08000000 1828c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_SET_VIRTIO_REV 0x10000000 1838c2ecf20Sopenharmony_ci#define VIRTIO_CCW_DOING_READ_STATUS 0x20000000 1848c2ecf20Sopenharmony_ci#define VIRTIO_CCW_INTPARM_MASK 0xffff0000 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci return container_of(vdev, struct virtio_ccw_device, vdev); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void drop_airq_indicator(struct virtqueue *vq, struct airq_info *info) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci unsigned long i, flags; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci write_lock_irqsave(&info->lock, flags); 1968c2ecf20Sopenharmony_ci for (i = 0; i < airq_iv_end(info->aiv); i++) { 1978c2ecf20Sopenharmony_ci if (vq == (void *)airq_iv_get_ptr(info->aiv, i)) { 1988c2ecf20Sopenharmony_ci airq_iv_free_bit(info->aiv, i); 1998c2ecf20Sopenharmony_ci airq_iv_set_ptr(info->aiv, i, 0); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci write_unlock_irqrestore(&info->lock, flags); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void virtio_airq_handler(struct airq_struct *airq, bool floating) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct airq_info *info = container_of(airq, struct airq_info, airq); 2098c2ecf20Sopenharmony_ci unsigned long ai; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci inc_irq_stat(IRQIO_VAI); 2128c2ecf20Sopenharmony_ci read_lock(&info->lock); 2138c2ecf20Sopenharmony_ci /* Walk through indicators field, summary indicator active. */ 2148c2ecf20Sopenharmony_ci for (ai = 0;;) { 2158c2ecf20Sopenharmony_ci ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv)); 2168c2ecf20Sopenharmony_ci if (ai == -1UL) 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai)); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci *(get_summary_indicator(info)) = 0; 2218c2ecf20Sopenharmony_ci smp_wmb(); 2228c2ecf20Sopenharmony_ci /* Walk through indicators field, summary indicator not active. */ 2238c2ecf20Sopenharmony_ci for (ai = 0;;) { 2248c2ecf20Sopenharmony_ci ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv)); 2258c2ecf20Sopenharmony_ci if (ai == -1UL) 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai)); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci read_unlock(&info->lock); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic struct airq_info *new_airq_info(int index) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct airq_info *info; 2358c2ecf20Sopenharmony_ci int rc; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 2388c2ecf20Sopenharmony_ci if (!info) 2398c2ecf20Sopenharmony_ci return NULL; 2408c2ecf20Sopenharmony_ci rwlock_init(&info->lock); 2418c2ecf20Sopenharmony_ci info->aiv = airq_iv_create(VIRTIO_IV_BITS, AIRQ_IV_ALLOC | AIRQ_IV_PTR 2428c2ecf20Sopenharmony_ci | AIRQ_IV_CACHELINE); 2438c2ecf20Sopenharmony_ci if (!info->aiv) { 2448c2ecf20Sopenharmony_ci kfree(info); 2458c2ecf20Sopenharmony_ci return NULL; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci info->airq.handler = virtio_airq_handler; 2488c2ecf20Sopenharmony_ci info->summary_indicator_idx = index; 2498c2ecf20Sopenharmony_ci info->airq.lsi_ptr = get_summary_indicator(info); 2508c2ecf20Sopenharmony_ci info->airq.lsi_mask = 0xff; 2518c2ecf20Sopenharmony_ci info->airq.isc = VIRTIO_AIRQ_ISC; 2528c2ecf20Sopenharmony_ci rc = register_adapter_interrupt(&info->airq); 2538c2ecf20Sopenharmony_ci if (rc) { 2548c2ecf20Sopenharmony_ci airq_iv_release(info->aiv); 2558c2ecf20Sopenharmony_ci kfree(info); 2568c2ecf20Sopenharmony_ci return NULL; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci return info; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic unsigned long get_airq_indicator(struct virtqueue *vqs[], int nvqs, 2628c2ecf20Sopenharmony_ci u64 *first, void **airq_info) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci int i, j; 2658c2ecf20Sopenharmony_ci struct airq_info *info; 2668c2ecf20Sopenharmony_ci unsigned long indicator_addr = 0; 2678c2ecf20Sopenharmony_ci unsigned long bit, flags; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci for (i = 0; i < MAX_AIRQ_AREAS && !indicator_addr; i++) { 2708c2ecf20Sopenharmony_ci mutex_lock(&airq_areas_lock); 2718c2ecf20Sopenharmony_ci if (!airq_areas[i]) 2728c2ecf20Sopenharmony_ci airq_areas[i] = new_airq_info(i); 2738c2ecf20Sopenharmony_ci info = airq_areas[i]; 2748c2ecf20Sopenharmony_ci mutex_unlock(&airq_areas_lock); 2758c2ecf20Sopenharmony_ci if (!info) 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci write_lock_irqsave(&info->lock, flags); 2788c2ecf20Sopenharmony_ci bit = airq_iv_alloc(info->aiv, nvqs); 2798c2ecf20Sopenharmony_ci if (bit == -1UL) { 2808c2ecf20Sopenharmony_ci /* Not enough vacancies. */ 2818c2ecf20Sopenharmony_ci write_unlock_irqrestore(&info->lock, flags); 2828c2ecf20Sopenharmony_ci continue; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci *first = bit; 2858c2ecf20Sopenharmony_ci *airq_info = info; 2868c2ecf20Sopenharmony_ci indicator_addr = (unsigned long)info->aiv->vector; 2878c2ecf20Sopenharmony_ci for (j = 0; j < nvqs; j++) { 2888c2ecf20Sopenharmony_ci airq_iv_set_ptr(info->aiv, bit + j, 2898c2ecf20Sopenharmony_ci (unsigned long)vqs[j]); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci write_unlock_irqrestore(&info->lock, flags); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci return indicator_addr; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void virtio_ccw_drop_indicators(struct virtio_ccw_device *vcdev) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct virtio_ccw_vq_info *info; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!vcdev->airq_info) 3018c2ecf20Sopenharmony_ci return; 3028c2ecf20Sopenharmony_ci list_for_each_entry(info, &vcdev->virtqueues, node) 3038c2ecf20Sopenharmony_ci drop_airq_indicator(info->vq, vcdev->airq_info); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci unsigned long flags; 3098c2ecf20Sopenharmony_ci __u32 ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); 3128c2ecf20Sopenharmony_ci if (vcdev->err) 3138c2ecf20Sopenharmony_ci ret = 0; 3148c2ecf20Sopenharmony_ci else 3158c2ecf20Sopenharmony_ci ret = vcdev->curr_io & flag; 3168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); 3178c2ecf20Sopenharmony_ci return ret; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int ccw_io_helper(struct virtio_ccw_device *vcdev, 3218c2ecf20Sopenharmony_ci struct ccw1 *ccw, __u32 intparm) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci int ret; 3248c2ecf20Sopenharmony_ci unsigned long flags; 3258c2ecf20Sopenharmony_ci int flag = intparm & VIRTIO_CCW_INTPARM_MASK; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci mutex_lock(&vcdev->io_lock); 3288c2ecf20Sopenharmony_ci do { 3298c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); 3308c2ecf20Sopenharmony_ci ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); 3318c2ecf20Sopenharmony_ci if (!ret) { 3328c2ecf20Sopenharmony_ci if (!vcdev->curr_io) 3338c2ecf20Sopenharmony_ci vcdev->err = 0; 3348c2ecf20Sopenharmony_ci vcdev->curr_io |= flag; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); 3378c2ecf20Sopenharmony_ci cpu_relax(); 3388c2ecf20Sopenharmony_ci } while (ret == -EBUSY); 3398c2ecf20Sopenharmony_ci wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); 3408c2ecf20Sopenharmony_ci ret = ret ? ret : vcdev->err; 3418c2ecf20Sopenharmony_ci mutex_unlock(&vcdev->io_lock); 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev, 3468c2ecf20Sopenharmony_ci struct ccw1 *ccw) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci int ret; 3498c2ecf20Sopenharmony_ci unsigned long *indicatorp = NULL; 3508c2ecf20Sopenharmony_ci struct virtio_thinint_area *thinint_area = NULL; 3518c2ecf20Sopenharmony_ci struct airq_info *airq_info = vcdev->airq_info; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (vcdev->is_thinint) { 3548c2ecf20Sopenharmony_ci thinint_area = ccw_device_dma_zalloc(vcdev->cdev, 3558c2ecf20Sopenharmony_ci sizeof(*thinint_area)); 3568c2ecf20Sopenharmony_ci if (!thinint_area) 3578c2ecf20Sopenharmony_ci return; 3588c2ecf20Sopenharmony_ci thinint_area->summary_indicator = 3598c2ecf20Sopenharmony_ci (unsigned long) get_summary_indicator(airq_info); 3608c2ecf20Sopenharmony_ci thinint_area->isc = VIRTIO_AIRQ_ISC; 3618c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER; 3628c2ecf20Sopenharmony_ci ccw->count = sizeof(*thinint_area); 3638c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long) thinint_area; 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci /* payload is the address of the indicators */ 3668c2ecf20Sopenharmony_ci indicatorp = ccw_device_dma_zalloc(vcdev->cdev, 3678c2ecf20Sopenharmony_ci sizeof(indicators(vcdev))); 3688c2ecf20Sopenharmony_ci if (!indicatorp) 3698c2ecf20Sopenharmony_ci return; 3708c2ecf20Sopenharmony_ci *indicatorp = 0; 3718c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_IND; 3728c2ecf20Sopenharmony_ci ccw->count = sizeof(indicators(vcdev)); 3738c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long) indicatorp; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci /* Deregister indicators from host. */ 3768c2ecf20Sopenharmony_ci *indicators(vcdev) = 0; 3778c2ecf20Sopenharmony_ci ccw->flags = 0; 3788c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, 3798c2ecf20Sopenharmony_ci vcdev->is_thinint ? 3808c2ecf20Sopenharmony_ci VIRTIO_CCW_DOING_SET_IND_ADAPTER : 3818c2ecf20Sopenharmony_ci VIRTIO_CCW_DOING_SET_IND); 3828c2ecf20Sopenharmony_ci if (ret && (ret != -ENODEV)) 3838c2ecf20Sopenharmony_ci dev_info(&vcdev->cdev->dev, 3848c2ecf20Sopenharmony_ci "Failed to deregister indicators (%d)\n", ret); 3858c2ecf20Sopenharmony_ci else if (vcdev->is_thinint) 3868c2ecf20Sopenharmony_ci virtio_ccw_drop_indicators(vcdev); 3878c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, indicatorp, sizeof(indicators(vcdev))); 3888c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, thinint_area, sizeof(*thinint_area)); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic inline long __do_kvm_notify(struct subchannel_id schid, 3928c2ecf20Sopenharmony_ci unsigned long queue_index, 3938c2ecf20Sopenharmony_ci long cookie) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci register unsigned long __nr asm("1") = KVM_S390_VIRTIO_CCW_NOTIFY; 3968c2ecf20Sopenharmony_ci register struct subchannel_id __schid asm("2") = schid; 3978c2ecf20Sopenharmony_ci register unsigned long __index asm("3") = queue_index; 3988c2ecf20Sopenharmony_ci register long __rc asm("2"); 3998c2ecf20Sopenharmony_ci register long __cookie asm("4") = cookie; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci asm volatile ("diag 2,4,0x500\n" 4028c2ecf20Sopenharmony_ci : "=d" (__rc) : "d" (__nr), "d" (__schid), "d" (__index), 4038c2ecf20Sopenharmony_ci "d"(__cookie) 4048c2ecf20Sopenharmony_ci : "memory", "cc"); 4058c2ecf20Sopenharmony_ci return __rc; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic inline long do_kvm_notify(struct subchannel_id schid, 4098c2ecf20Sopenharmony_ci unsigned long queue_index, 4108c2ecf20Sopenharmony_ci long cookie) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci diag_stat_inc(DIAG_STAT_X500); 4138c2ecf20Sopenharmony_ci return __do_kvm_notify(schid, queue_index, cookie); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic bool virtio_ccw_kvm_notify(struct virtqueue *vq) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct virtio_ccw_vq_info *info = vq->priv; 4198c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev; 4208c2ecf20Sopenharmony_ci struct subchannel_id schid; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci vcdev = to_vc_device(info->vq->vdev); 4238c2ecf20Sopenharmony_ci ccw_device_get_schid(vcdev->cdev, &schid); 4248c2ecf20Sopenharmony_ci info->cookie = do_kvm_notify(schid, vq->index, info->cookie); 4258c2ecf20Sopenharmony_ci if (info->cookie < 0) 4268c2ecf20Sopenharmony_ci return false; 4278c2ecf20Sopenharmony_ci return true; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, 4318c2ecf20Sopenharmony_ci struct ccw1 *ccw, int index) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci int ret; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci vcdev->dma_area->config_block.index = index; 4368c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_VQ_CONF; 4378c2ecf20Sopenharmony_ci ccw->flags = 0; 4388c2ecf20Sopenharmony_ci ccw->count = sizeof(struct vq_config_block); 4398c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)(&vcdev->dma_area->config_block); 4408c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF); 4418c2ecf20Sopenharmony_ci if (ret) 4428c2ecf20Sopenharmony_ci return ret; 4438c2ecf20Sopenharmony_ci return vcdev->dma_area->config_block.num ?: -ENOENT; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); 4498c2ecf20Sopenharmony_ci struct virtio_ccw_vq_info *info = vq->priv; 4508c2ecf20Sopenharmony_ci unsigned long flags; 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci unsigned int index = vq->index; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* Remove from our list. */ 4558c2ecf20Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 4568c2ecf20Sopenharmony_ci list_del(&info->node); 4578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* Release from host. */ 4608c2ecf20Sopenharmony_ci if (vcdev->revision == 0) { 4618c2ecf20Sopenharmony_ci info->info_block->l.queue = 0; 4628c2ecf20Sopenharmony_ci info->info_block->l.align = 0; 4638c2ecf20Sopenharmony_ci info->info_block->l.index = index; 4648c2ecf20Sopenharmony_ci info->info_block->l.num = 0; 4658c2ecf20Sopenharmony_ci ccw->count = sizeof(info->info_block->l); 4668c2ecf20Sopenharmony_ci } else { 4678c2ecf20Sopenharmony_ci info->info_block->s.desc = 0; 4688c2ecf20Sopenharmony_ci info->info_block->s.index = index; 4698c2ecf20Sopenharmony_ci info->info_block->s.num = 0; 4708c2ecf20Sopenharmony_ci info->info_block->s.avail = 0; 4718c2ecf20Sopenharmony_ci info->info_block->s.used = 0; 4728c2ecf20Sopenharmony_ci ccw->count = sizeof(info->info_block->s); 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_VQ; 4758c2ecf20Sopenharmony_ci ccw->flags = 0; 4768c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)(info->info_block); 4778c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, 4788c2ecf20Sopenharmony_ci VIRTIO_CCW_DOING_SET_VQ | index); 4798c2ecf20Sopenharmony_ci /* 4808c2ecf20Sopenharmony_ci * -ENODEV isn't considered an error: The device is gone anyway. 4818c2ecf20Sopenharmony_ci * This may happen on device detach. 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci if (ret && (ret != -ENODEV)) 4848c2ecf20Sopenharmony_ci dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d\n", 4858c2ecf20Sopenharmony_ci ret, index); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci vring_del_virtqueue(vq); 4888c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, info->info_block, 4898c2ecf20Sopenharmony_ci sizeof(*info->info_block)); 4908c2ecf20Sopenharmony_ci kfree(info); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic void virtio_ccw_del_vqs(struct virtio_device *vdev) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct virtqueue *vq, *n; 4968c2ecf20Sopenharmony_ci struct ccw1 *ccw; 4978c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 5008c2ecf20Sopenharmony_ci if (!ccw) 5018c2ecf20Sopenharmony_ci return; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci virtio_ccw_drop_indicator(vcdev, ccw); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci list_for_each_entry_safe(vq, n, &vdev->vqs, list) 5068c2ecf20Sopenharmony_ci virtio_ccw_del_vq(vq, ccw); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, 5128c2ecf20Sopenharmony_ci int i, vq_callback_t *callback, 5138c2ecf20Sopenharmony_ci const char *name, bool ctx, 5148c2ecf20Sopenharmony_ci struct ccw1 *ccw) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 5178c2ecf20Sopenharmony_ci int err; 5188c2ecf20Sopenharmony_ci struct virtqueue *vq = NULL; 5198c2ecf20Sopenharmony_ci struct virtio_ccw_vq_info *info; 5208c2ecf20Sopenharmony_ci u64 queue; 5218c2ecf20Sopenharmony_ci unsigned long flags; 5228c2ecf20Sopenharmony_ci bool may_reduce; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* Allocate queue. */ 5258c2ecf20Sopenharmony_ci info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL); 5268c2ecf20Sopenharmony_ci if (!info) { 5278c2ecf20Sopenharmony_ci dev_warn(&vcdev->cdev->dev, "no info\n"); 5288c2ecf20Sopenharmony_ci err = -ENOMEM; 5298c2ecf20Sopenharmony_ci goto out_err; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci info->info_block = ccw_device_dma_zalloc(vcdev->cdev, 5328c2ecf20Sopenharmony_ci sizeof(*info->info_block)); 5338c2ecf20Sopenharmony_ci if (!info->info_block) { 5348c2ecf20Sopenharmony_ci dev_warn(&vcdev->cdev->dev, "no info block\n"); 5358c2ecf20Sopenharmony_ci err = -ENOMEM; 5368c2ecf20Sopenharmony_ci goto out_err; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i); 5398c2ecf20Sopenharmony_ci if (info->num < 0) { 5408c2ecf20Sopenharmony_ci err = info->num; 5418c2ecf20Sopenharmony_ci goto out_err; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci may_reduce = vcdev->revision > 0; 5448c2ecf20Sopenharmony_ci vq = vring_create_virtqueue(i, info->num, KVM_VIRTIO_CCW_RING_ALIGN, 5458c2ecf20Sopenharmony_ci vdev, true, may_reduce, ctx, 5468c2ecf20Sopenharmony_ci virtio_ccw_kvm_notify, callback, name); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (!vq) { 5498c2ecf20Sopenharmony_ci /* For now, we fail if we can't get the requested size. */ 5508c2ecf20Sopenharmony_ci dev_warn(&vcdev->cdev->dev, "no vq\n"); 5518c2ecf20Sopenharmony_ci err = -ENOMEM; 5528c2ecf20Sopenharmony_ci goto out_err; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci /* it may have been reduced */ 5558c2ecf20Sopenharmony_ci info->num = virtqueue_get_vring_size(vq); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Register it with the host. */ 5588c2ecf20Sopenharmony_ci queue = virtqueue_get_desc_addr(vq); 5598c2ecf20Sopenharmony_ci if (vcdev->revision == 0) { 5608c2ecf20Sopenharmony_ci info->info_block->l.queue = queue; 5618c2ecf20Sopenharmony_ci info->info_block->l.align = KVM_VIRTIO_CCW_RING_ALIGN; 5628c2ecf20Sopenharmony_ci info->info_block->l.index = i; 5638c2ecf20Sopenharmony_ci info->info_block->l.num = info->num; 5648c2ecf20Sopenharmony_ci ccw->count = sizeof(info->info_block->l); 5658c2ecf20Sopenharmony_ci } else { 5668c2ecf20Sopenharmony_ci info->info_block->s.desc = queue; 5678c2ecf20Sopenharmony_ci info->info_block->s.index = i; 5688c2ecf20Sopenharmony_ci info->info_block->s.num = info->num; 5698c2ecf20Sopenharmony_ci info->info_block->s.avail = (__u64)virtqueue_get_avail_addr(vq); 5708c2ecf20Sopenharmony_ci info->info_block->s.used = (__u64)virtqueue_get_used_addr(vq); 5718c2ecf20Sopenharmony_ci ccw->count = sizeof(info->info_block->s); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_VQ; 5748c2ecf20Sopenharmony_ci ccw->flags = 0; 5758c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)(info->info_block); 5768c2ecf20Sopenharmony_ci err = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_VQ | i); 5778c2ecf20Sopenharmony_ci if (err) { 5788c2ecf20Sopenharmony_ci dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); 5798c2ecf20Sopenharmony_ci goto out_err; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci info->vq = vq; 5838c2ecf20Sopenharmony_ci vq->priv = info; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Save it to our list. */ 5868c2ecf20Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 5878c2ecf20Sopenharmony_ci list_add(&info->node, &vcdev->virtqueues); 5888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return vq; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ciout_err: 5938c2ecf20Sopenharmony_ci if (vq) 5948c2ecf20Sopenharmony_ci vring_del_virtqueue(vq); 5958c2ecf20Sopenharmony_ci if (info) { 5968c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, info->info_block, 5978c2ecf20Sopenharmony_ci sizeof(*info->info_block)); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci kfree(info); 6008c2ecf20Sopenharmony_ci return ERR_PTR(err); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int virtio_ccw_register_adapter_ind(struct virtio_ccw_device *vcdev, 6048c2ecf20Sopenharmony_ci struct virtqueue *vqs[], int nvqs, 6058c2ecf20Sopenharmony_ci struct ccw1 *ccw) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci int ret; 6088c2ecf20Sopenharmony_ci struct virtio_thinint_area *thinint_area = NULL; 6098c2ecf20Sopenharmony_ci struct airq_info *info; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci thinint_area = ccw_device_dma_zalloc(vcdev->cdev, 6128c2ecf20Sopenharmony_ci sizeof(*thinint_area)); 6138c2ecf20Sopenharmony_ci if (!thinint_area) { 6148c2ecf20Sopenharmony_ci ret = -ENOMEM; 6158c2ecf20Sopenharmony_ci goto out; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci /* Try to get an indicator. */ 6188c2ecf20Sopenharmony_ci thinint_area->indicator = get_airq_indicator(vqs, nvqs, 6198c2ecf20Sopenharmony_ci &thinint_area->bit_nr, 6208c2ecf20Sopenharmony_ci &vcdev->airq_info); 6218c2ecf20Sopenharmony_ci if (!thinint_area->indicator) { 6228c2ecf20Sopenharmony_ci ret = -ENOSPC; 6238c2ecf20Sopenharmony_ci goto out; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci info = vcdev->airq_info; 6268c2ecf20Sopenharmony_ci thinint_area->summary_indicator = 6278c2ecf20Sopenharmony_ci (unsigned long) get_summary_indicator(info); 6288c2ecf20Sopenharmony_ci thinint_area->isc = VIRTIO_AIRQ_ISC; 6298c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER; 6308c2ecf20Sopenharmony_ci ccw->flags = CCW_FLAG_SLI; 6318c2ecf20Sopenharmony_ci ccw->count = sizeof(*thinint_area); 6328c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)thinint_area; 6338c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND_ADAPTER); 6348c2ecf20Sopenharmony_ci if (ret) { 6358c2ecf20Sopenharmony_ci if (ret == -EOPNOTSUPP) { 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * The host does not support adapter interrupts 6388c2ecf20Sopenharmony_ci * for virtio-ccw, stop trying. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci virtio_ccw_use_airq = 0; 6418c2ecf20Sopenharmony_ci pr_info("Adapter interrupts unsupported on host\n"); 6428c2ecf20Sopenharmony_ci } else 6438c2ecf20Sopenharmony_ci dev_warn(&vcdev->cdev->dev, 6448c2ecf20Sopenharmony_ci "enabling adapter interrupts = %d\n", ret); 6458c2ecf20Sopenharmony_ci virtio_ccw_drop_indicators(vcdev); 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ciout: 6488c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, thinint_area, sizeof(*thinint_area)); 6498c2ecf20Sopenharmony_ci return ret; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, 6538c2ecf20Sopenharmony_ci struct virtqueue *vqs[], 6548c2ecf20Sopenharmony_ci vq_callback_t *callbacks[], 6558c2ecf20Sopenharmony_ci const char * const names[], 6568c2ecf20Sopenharmony_ci const bool *ctx, 6578c2ecf20Sopenharmony_ci struct irq_affinity *desc) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 6608c2ecf20Sopenharmony_ci unsigned long *indicatorp = NULL; 6618c2ecf20Sopenharmony_ci int ret, i, queue_idx = 0; 6628c2ecf20Sopenharmony_ci struct ccw1 *ccw; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 6658c2ecf20Sopenharmony_ci if (!ccw) 6668c2ecf20Sopenharmony_ci return -ENOMEM; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci for (i = 0; i < nvqs; ++i) { 6698c2ecf20Sopenharmony_ci if (!names[i]) { 6708c2ecf20Sopenharmony_ci vqs[i] = NULL; 6718c2ecf20Sopenharmony_ci continue; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci vqs[i] = virtio_ccw_setup_vq(vdev, queue_idx++, callbacks[i], 6758c2ecf20Sopenharmony_ci names[i], ctx ? ctx[i] : false, 6768c2ecf20Sopenharmony_ci ccw); 6778c2ecf20Sopenharmony_ci if (IS_ERR(vqs[i])) { 6788c2ecf20Sopenharmony_ci ret = PTR_ERR(vqs[i]); 6798c2ecf20Sopenharmony_ci vqs[i] = NULL; 6808c2ecf20Sopenharmony_ci goto out; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci ret = -ENOMEM; 6848c2ecf20Sopenharmony_ci /* 6858c2ecf20Sopenharmony_ci * We need a data area under 2G to communicate. Our payload is 6868c2ecf20Sopenharmony_ci * the address of the indicators. 6878c2ecf20Sopenharmony_ci */ 6888c2ecf20Sopenharmony_ci indicatorp = ccw_device_dma_zalloc(vcdev->cdev, 6898c2ecf20Sopenharmony_ci sizeof(indicators(vcdev))); 6908c2ecf20Sopenharmony_ci if (!indicatorp) 6918c2ecf20Sopenharmony_ci goto out; 6928c2ecf20Sopenharmony_ci *indicatorp = (unsigned long) indicators(vcdev); 6938c2ecf20Sopenharmony_ci if (vcdev->is_thinint) { 6948c2ecf20Sopenharmony_ci ret = virtio_ccw_register_adapter_ind(vcdev, vqs, nvqs, ccw); 6958c2ecf20Sopenharmony_ci if (ret) 6968c2ecf20Sopenharmony_ci /* no error, just fall back to legacy interrupts */ 6978c2ecf20Sopenharmony_ci vcdev->is_thinint = false; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci if (!vcdev->is_thinint) { 7008c2ecf20Sopenharmony_ci /* Register queue indicators with host. */ 7018c2ecf20Sopenharmony_ci *indicators(vcdev) = 0; 7028c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_IND; 7038c2ecf20Sopenharmony_ci ccw->flags = 0; 7048c2ecf20Sopenharmony_ci ccw->count = sizeof(indicators(vcdev)); 7058c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long) indicatorp; 7068c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); 7078c2ecf20Sopenharmony_ci if (ret) 7088c2ecf20Sopenharmony_ci goto out; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci /* Register indicators2 with host for config changes */ 7118c2ecf20Sopenharmony_ci *indicatorp = (unsigned long) indicators2(vcdev); 7128c2ecf20Sopenharmony_ci *indicators2(vcdev) = 0; 7138c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_CONF_IND; 7148c2ecf20Sopenharmony_ci ccw->flags = 0; 7158c2ecf20Sopenharmony_ci ccw->count = sizeof(indicators2(vcdev)); 7168c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long) indicatorp; 7178c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_CONF_IND); 7188c2ecf20Sopenharmony_ci if (ret) 7198c2ecf20Sopenharmony_ci goto out; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (indicatorp) 7228c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, indicatorp, 7238c2ecf20Sopenharmony_ci sizeof(indicators(vcdev))); 7248c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 7258c2ecf20Sopenharmony_ci return 0; 7268c2ecf20Sopenharmony_ciout: 7278c2ecf20Sopenharmony_ci if (indicatorp) 7288c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, indicatorp, 7298c2ecf20Sopenharmony_ci sizeof(indicators(vcdev))); 7308c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 7318c2ecf20Sopenharmony_ci virtio_ccw_del_vqs(vdev); 7328c2ecf20Sopenharmony_ci return ret; 7338c2ecf20Sopenharmony_ci} 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic void virtio_ccw_reset(struct virtio_device *vdev) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 7388c2ecf20Sopenharmony_ci struct ccw1 *ccw; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 7418c2ecf20Sopenharmony_ci if (!ccw) 7428c2ecf20Sopenharmony_ci return; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* Zero status bits. */ 7458c2ecf20Sopenharmony_ci vcdev->dma_area->status = 0; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* Send a reset ccw on device. */ 7488c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_VDEV_RESET; 7498c2ecf20Sopenharmony_ci ccw->flags = 0; 7508c2ecf20Sopenharmony_ci ccw->count = 0; 7518c2ecf20Sopenharmony_ci ccw->cda = 0; 7528c2ecf20Sopenharmony_ci ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_RESET); 7538c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic u64 virtio_ccw_get_features(struct virtio_device *vdev) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 7598c2ecf20Sopenharmony_ci struct virtio_feature_desc *features; 7608c2ecf20Sopenharmony_ci int ret; 7618c2ecf20Sopenharmony_ci u64 rc; 7628c2ecf20Sopenharmony_ci struct ccw1 *ccw; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 7658c2ecf20Sopenharmony_ci if (!ccw) 7668c2ecf20Sopenharmony_ci return 0; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci features = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*features)); 7698c2ecf20Sopenharmony_ci if (!features) { 7708c2ecf20Sopenharmony_ci rc = 0; 7718c2ecf20Sopenharmony_ci goto out_free; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci /* Read the feature bits from the host. */ 7748c2ecf20Sopenharmony_ci features->index = 0; 7758c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_FEAT; 7768c2ecf20Sopenharmony_ci ccw->flags = 0; 7778c2ecf20Sopenharmony_ci ccw->count = sizeof(*features); 7788c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)features; 7798c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT); 7808c2ecf20Sopenharmony_ci if (ret) { 7818c2ecf20Sopenharmony_ci rc = 0; 7828c2ecf20Sopenharmony_ci goto out_free; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci rc = le32_to_cpu(features->features); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (vcdev->revision == 0) 7888c2ecf20Sopenharmony_ci goto out_free; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* Read second half of the feature bits from the host. */ 7918c2ecf20Sopenharmony_ci features->index = 1; 7928c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_FEAT; 7938c2ecf20Sopenharmony_ci ccw->flags = 0; 7948c2ecf20Sopenharmony_ci ccw->count = sizeof(*features); 7958c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)features; 7968c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT); 7978c2ecf20Sopenharmony_ci if (ret == 0) 7988c2ecf20Sopenharmony_ci rc |= (u64)le32_to_cpu(features->features) << 32; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ciout_free: 8018c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, features, sizeof(*features)); 8028c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 8038c2ecf20Sopenharmony_ci return rc; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic void ccw_transport_features(struct virtio_device *vdev) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci /* 8098c2ecf20Sopenharmony_ci * Currently nothing to do here. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int virtio_ccw_finalize_features(struct virtio_device *vdev) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 8168c2ecf20Sopenharmony_ci struct virtio_feature_desc *features; 8178c2ecf20Sopenharmony_ci struct ccw1 *ccw; 8188c2ecf20Sopenharmony_ci int ret; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (vcdev->revision >= 1 && 8218c2ecf20Sopenharmony_ci !__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) { 8228c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "virtio: device uses revision 1 " 8238c2ecf20Sopenharmony_ci "but does not have VIRTIO_F_VERSION_1\n"); 8248c2ecf20Sopenharmony_ci return -EINVAL; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 8288c2ecf20Sopenharmony_ci if (!ccw) 8298c2ecf20Sopenharmony_ci return -ENOMEM; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci features = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*features)); 8328c2ecf20Sopenharmony_ci if (!features) { 8338c2ecf20Sopenharmony_ci ret = -ENOMEM; 8348c2ecf20Sopenharmony_ci goto out_free; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci /* Give virtio_ring a chance to accept features. */ 8378c2ecf20Sopenharmony_ci vring_transport_features(vdev); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* Give virtio_ccw a chance to accept features. */ 8408c2ecf20Sopenharmony_ci ccw_transport_features(vdev); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci features->index = 0; 8438c2ecf20Sopenharmony_ci features->features = cpu_to_le32((u32)vdev->features); 8448c2ecf20Sopenharmony_ci /* Write the first half of the feature bits to the host. */ 8458c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_WRITE_FEAT; 8468c2ecf20Sopenharmony_ci ccw->flags = 0; 8478c2ecf20Sopenharmony_ci ccw->count = sizeof(*features); 8488c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)features; 8498c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT); 8508c2ecf20Sopenharmony_ci if (ret) 8518c2ecf20Sopenharmony_ci goto out_free; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (vcdev->revision == 0) 8548c2ecf20Sopenharmony_ci goto out_free; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci features->index = 1; 8578c2ecf20Sopenharmony_ci features->features = cpu_to_le32(vdev->features >> 32); 8588c2ecf20Sopenharmony_ci /* Write the second half of the feature bits to the host. */ 8598c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_WRITE_FEAT; 8608c2ecf20Sopenharmony_ci ccw->flags = 0; 8618c2ecf20Sopenharmony_ci ccw->count = sizeof(*features); 8628c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)features; 8638c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ciout_free: 8668c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, features, sizeof(*features)); 8678c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return ret; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic void virtio_ccw_get_config(struct virtio_device *vdev, 8738c2ecf20Sopenharmony_ci unsigned int offset, void *buf, unsigned len) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 8768c2ecf20Sopenharmony_ci int ret; 8778c2ecf20Sopenharmony_ci struct ccw1 *ccw; 8788c2ecf20Sopenharmony_ci void *config_area; 8798c2ecf20Sopenharmony_ci unsigned long flags; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 8828c2ecf20Sopenharmony_ci if (!ccw) 8838c2ecf20Sopenharmony_ci return; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci config_area = ccw_device_dma_zalloc(vcdev->cdev, 8868c2ecf20Sopenharmony_ci VIRTIO_CCW_CONFIG_SIZE); 8878c2ecf20Sopenharmony_ci if (!config_area) 8888c2ecf20Sopenharmony_ci goto out_free; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* Read the config area from the host. */ 8918c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_CONF; 8928c2ecf20Sopenharmony_ci ccw->flags = 0; 8938c2ecf20Sopenharmony_ci ccw->count = offset + len; 8948c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)config_area; 8958c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_CONFIG); 8968c2ecf20Sopenharmony_ci if (ret) 8978c2ecf20Sopenharmony_ci goto out_free; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 9008c2ecf20Sopenharmony_ci memcpy(vcdev->config, config_area, offset + len); 9018c2ecf20Sopenharmony_ci if (vcdev->config_ready < offset + len) 9028c2ecf20Sopenharmony_ci vcdev->config_ready = offset + len; 9038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 9048c2ecf20Sopenharmony_ci if (buf) 9058c2ecf20Sopenharmony_ci memcpy(buf, config_area + offset, len); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ciout_free: 9088c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, config_area, VIRTIO_CCW_CONFIG_SIZE); 9098c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_cistatic void virtio_ccw_set_config(struct virtio_device *vdev, 9138c2ecf20Sopenharmony_ci unsigned int offset, const void *buf, 9148c2ecf20Sopenharmony_ci unsigned len) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 9178c2ecf20Sopenharmony_ci struct ccw1 *ccw; 9188c2ecf20Sopenharmony_ci void *config_area; 9198c2ecf20Sopenharmony_ci unsigned long flags; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 9228c2ecf20Sopenharmony_ci if (!ccw) 9238c2ecf20Sopenharmony_ci return; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci config_area = ccw_device_dma_zalloc(vcdev->cdev, 9268c2ecf20Sopenharmony_ci VIRTIO_CCW_CONFIG_SIZE); 9278c2ecf20Sopenharmony_ci if (!config_area) 9288c2ecf20Sopenharmony_ci goto out_free; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci /* Make sure we don't overwrite fields. */ 9318c2ecf20Sopenharmony_ci if (vcdev->config_ready < offset) 9328c2ecf20Sopenharmony_ci virtio_ccw_get_config(vdev, 0, NULL, offset); 9338c2ecf20Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 9348c2ecf20Sopenharmony_ci memcpy(&vcdev->config[offset], buf, len); 9358c2ecf20Sopenharmony_ci /* Write the config area to the host. */ 9368c2ecf20Sopenharmony_ci memcpy(config_area, vcdev->config, sizeof(vcdev->config)); 9378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 9388c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_WRITE_CONF; 9398c2ecf20Sopenharmony_ci ccw->flags = 0; 9408c2ecf20Sopenharmony_ci ccw->count = offset + len; 9418c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)config_area; 9428c2ecf20Sopenharmony_ci ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_CONFIG); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ciout_free: 9458c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, config_area, VIRTIO_CCW_CONFIG_SIZE); 9468c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic u8 virtio_ccw_get_status(struct virtio_device *vdev) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 9528c2ecf20Sopenharmony_ci u8 old_status = vcdev->dma_area->status; 9538c2ecf20Sopenharmony_ci struct ccw1 *ccw; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (vcdev->revision < 2) 9568c2ecf20Sopenharmony_ci return vcdev->dma_area->status; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 9598c2ecf20Sopenharmony_ci if (!ccw) 9608c2ecf20Sopenharmony_ci return old_status; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_READ_STATUS; 9638c2ecf20Sopenharmony_ci ccw->flags = 0; 9648c2ecf20Sopenharmony_ci ccw->count = sizeof(vcdev->dma_area->status); 9658c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)&vcdev->dma_area->status; 9668c2ecf20Sopenharmony_ci ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_STATUS); 9678c2ecf20Sopenharmony_ci/* 9688c2ecf20Sopenharmony_ci * If the channel program failed (should only happen if the device 9698c2ecf20Sopenharmony_ci * was hotunplugged, and then we clean up via the machine check 9708c2ecf20Sopenharmony_ci * handler anyway), vcdev->dma_area->status was not overwritten and we just 9718c2ecf20Sopenharmony_ci * return the old status, which is fine. 9728c2ecf20Sopenharmony_ci*/ 9738c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return vcdev->dma_area->status; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 9818c2ecf20Sopenharmony_ci u8 old_status = vcdev->dma_area->status; 9828c2ecf20Sopenharmony_ci struct ccw1 *ccw; 9838c2ecf20Sopenharmony_ci int ret; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 9868c2ecf20Sopenharmony_ci if (!ccw) 9878c2ecf20Sopenharmony_ci return; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* Write the status to the host. */ 9908c2ecf20Sopenharmony_ci vcdev->dma_area->status = status; 9918c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_WRITE_STATUS; 9928c2ecf20Sopenharmony_ci ccw->flags = 0; 9938c2ecf20Sopenharmony_ci ccw->count = sizeof(status); 9948c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)&vcdev->dma_area->status; 9958c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_STATUS); 9968c2ecf20Sopenharmony_ci /* Write failed? We assume status is unchanged. */ 9978c2ecf20Sopenharmony_ci if (ret) 9988c2ecf20Sopenharmony_ci vcdev->dma_area->status = old_status; 9998c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic const char *virtio_ccw_bus_name(struct virtio_device *vdev) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(vdev); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci return dev_name(&vcdev->cdev->dev); 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic const struct virtio_config_ops virtio_ccw_config_ops = { 10108c2ecf20Sopenharmony_ci .get_features = virtio_ccw_get_features, 10118c2ecf20Sopenharmony_ci .finalize_features = virtio_ccw_finalize_features, 10128c2ecf20Sopenharmony_ci .get = virtio_ccw_get_config, 10138c2ecf20Sopenharmony_ci .set = virtio_ccw_set_config, 10148c2ecf20Sopenharmony_ci .get_status = virtio_ccw_get_status, 10158c2ecf20Sopenharmony_ci .set_status = virtio_ccw_set_status, 10168c2ecf20Sopenharmony_ci .reset = virtio_ccw_reset, 10178c2ecf20Sopenharmony_ci .find_vqs = virtio_ccw_find_vqs, 10188c2ecf20Sopenharmony_ci .del_vqs = virtio_ccw_del_vqs, 10198c2ecf20Sopenharmony_ci .bus_name = virtio_ccw_bus_name, 10208c2ecf20Sopenharmony_ci}; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci/* 10248c2ecf20Sopenharmony_ci * ccw bus driver related functions 10258c2ecf20Sopenharmony_ci */ 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic void virtio_ccw_release_dev(struct device *_d) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct virtio_device *dev = dev_to_virtio(_d); 10308c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = to_vc_device(dev); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, vcdev->dma_area, 10338c2ecf20Sopenharmony_ci sizeof(*vcdev->dma_area)); 10348c2ecf20Sopenharmony_ci kfree(vcdev); 10358c2ecf20Sopenharmony_ci} 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_cistatic int irb_is_error(struct irb *irb) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci if (scsw_cstat(&irb->scsw) != 0) 10408c2ecf20Sopenharmony_ci return 1; 10418c2ecf20Sopenharmony_ci if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) 10428c2ecf20Sopenharmony_ci return 1; 10438c2ecf20Sopenharmony_ci if (scsw_cc(&irb->scsw) != 0) 10448c2ecf20Sopenharmony_ci return 1; 10458c2ecf20Sopenharmony_ci return 0; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev, 10498c2ecf20Sopenharmony_ci int index) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci struct virtio_ccw_vq_info *info; 10528c2ecf20Sopenharmony_ci unsigned long flags; 10538c2ecf20Sopenharmony_ci struct virtqueue *vq; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci vq = NULL; 10568c2ecf20Sopenharmony_ci spin_lock_irqsave(&vcdev->lock, flags); 10578c2ecf20Sopenharmony_ci list_for_each_entry(info, &vcdev->virtqueues, node) { 10588c2ecf20Sopenharmony_ci if (info->vq->index == index) { 10598c2ecf20Sopenharmony_ci vq = info->vq; 10608c2ecf20Sopenharmony_ci break; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vcdev->lock, flags); 10648c2ecf20Sopenharmony_ci return vq; 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic void virtio_ccw_check_activity(struct virtio_ccw_device *vcdev, 10688c2ecf20Sopenharmony_ci __u32 activity) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci if (vcdev->curr_io & activity) { 10718c2ecf20Sopenharmony_ci switch (activity) { 10728c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_READ_FEAT: 10738c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_WRITE_FEAT: 10748c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_READ_CONFIG: 10758c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_WRITE_CONFIG: 10768c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_WRITE_STATUS: 10778c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_READ_STATUS: 10788c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_SET_VQ: 10798c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_SET_IND: 10808c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_SET_CONF_IND: 10818c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_RESET: 10828c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_READ_VQ_CONF: 10838c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_SET_IND_ADAPTER: 10848c2ecf20Sopenharmony_ci case VIRTIO_CCW_DOING_SET_VIRTIO_REV: 10858c2ecf20Sopenharmony_ci vcdev->curr_io &= ~activity; 10868c2ecf20Sopenharmony_ci wake_up(&vcdev->wait_q); 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci default: 10898c2ecf20Sopenharmony_ci /* don't know what to do... */ 10908c2ecf20Sopenharmony_ci dev_warn(&vcdev->cdev->dev, 10918c2ecf20Sopenharmony_ci "Suspicious activity '%08x'\n", activity); 10928c2ecf20Sopenharmony_ci WARN_ON(1); 10938c2ecf20Sopenharmony_ci break; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic void virtio_ccw_int_handler(struct ccw_device *cdev, 10998c2ecf20Sopenharmony_ci unsigned long intparm, 11008c2ecf20Sopenharmony_ci struct irb *irb) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci __u32 activity = intparm & VIRTIO_CCW_INTPARM_MASK; 11038c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 11048c2ecf20Sopenharmony_ci int i; 11058c2ecf20Sopenharmony_ci struct virtqueue *vq; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (!vcdev) 11088c2ecf20Sopenharmony_ci return; 11098c2ecf20Sopenharmony_ci if (IS_ERR(irb)) { 11108c2ecf20Sopenharmony_ci vcdev->err = PTR_ERR(irb); 11118c2ecf20Sopenharmony_ci virtio_ccw_check_activity(vcdev, activity); 11128c2ecf20Sopenharmony_ci /* Don't poke around indicators, something's wrong. */ 11138c2ecf20Sopenharmony_ci return; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci /* Check if it's a notification from the host. */ 11168c2ecf20Sopenharmony_ci if ((intparm == 0) && 11178c2ecf20Sopenharmony_ci (scsw_stctl(&irb->scsw) == 11188c2ecf20Sopenharmony_ci (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) { 11198c2ecf20Sopenharmony_ci /* OK */ 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci if (irb_is_error(irb)) { 11228c2ecf20Sopenharmony_ci /* Command reject? */ 11238c2ecf20Sopenharmony_ci if ((scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && 11248c2ecf20Sopenharmony_ci (irb->ecw[0] & SNS0_CMD_REJECT)) 11258c2ecf20Sopenharmony_ci vcdev->err = -EOPNOTSUPP; 11268c2ecf20Sopenharmony_ci else 11278c2ecf20Sopenharmony_ci /* Map everything else to -EIO. */ 11288c2ecf20Sopenharmony_ci vcdev->err = -EIO; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci virtio_ccw_check_activity(vcdev, activity); 11318c2ecf20Sopenharmony_ci for_each_set_bit(i, indicators(vcdev), 11328c2ecf20Sopenharmony_ci sizeof(*indicators(vcdev)) * BITS_PER_BYTE) { 11338c2ecf20Sopenharmony_ci /* The bit clear must happen before the vring kick. */ 11348c2ecf20Sopenharmony_ci clear_bit(i, indicators(vcdev)); 11358c2ecf20Sopenharmony_ci barrier(); 11368c2ecf20Sopenharmony_ci vq = virtio_ccw_vq_by_ind(vcdev, i); 11378c2ecf20Sopenharmony_ci vring_interrupt(0, vq); 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci if (test_bit(0, indicators2(vcdev))) { 11408c2ecf20Sopenharmony_ci virtio_config_changed(&vcdev->vdev); 11418c2ecf20Sopenharmony_ci clear_bit(0, indicators2(vcdev)); 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci/* 11468c2ecf20Sopenharmony_ci * We usually want to autoonline all devices, but give the admin 11478c2ecf20Sopenharmony_ci * a way to exempt devices from this. 11488c2ecf20Sopenharmony_ci */ 11498c2ecf20Sopenharmony_ci#define __DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ 11508c2ecf20Sopenharmony_ci (8*sizeof(long))) 11518c2ecf20Sopenharmony_cistatic unsigned long devs_no_auto[__MAX_SSID + 1][__DEV_WORDS]; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cistatic char *no_auto = ""; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cimodule_param(no_auto, charp, 0444); 11568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(no_auto, "list of ccw bus id ranges not to be auto-onlined"); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic int virtio_ccw_check_autoonline(struct ccw_device *cdev) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci struct ccw_dev_id id; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci ccw_device_get_id(cdev, &id); 11638c2ecf20Sopenharmony_ci if (test_bit(id.devno, devs_no_auto[id.ssid])) 11648c2ecf20Sopenharmony_ci return 0; 11658c2ecf20Sopenharmony_ci return 1; 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic void virtio_ccw_auto_online(void *data, async_cookie_t cookie) 11698c2ecf20Sopenharmony_ci{ 11708c2ecf20Sopenharmony_ci struct ccw_device *cdev = data; 11718c2ecf20Sopenharmony_ci int ret; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci ret = ccw_device_set_online(cdev); 11748c2ecf20Sopenharmony_ci if (ret) 11758c2ecf20Sopenharmony_ci dev_warn(&cdev->dev, "Failed to set online: %d\n", ret); 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic int virtio_ccw_probe(struct ccw_device *cdev) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci cdev->handler = virtio_ccw_int_handler; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if (virtio_ccw_check_autoonline(cdev)) 11838c2ecf20Sopenharmony_ci async_schedule(virtio_ccw_auto_online, cdev); 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cistatic struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) 11888c2ecf20Sopenharmony_ci{ 11898c2ecf20Sopenharmony_ci unsigned long flags; 11908c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 11938c2ecf20Sopenharmony_ci vcdev = dev_get_drvdata(&cdev->dev); 11948c2ecf20Sopenharmony_ci if (!vcdev || vcdev->going_away) { 11958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 11968c2ecf20Sopenharmony_ci return NULL; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci vcdev->going_away = true; 11998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 12008c2ecf20Sopenharmony_ci return vcdev; 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_cistatic void virtio_ccw_remove(struct ccw_device *cdev) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci unsigned long flags; 12068c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (vcdev && cdev->online) { 12098c2ecf20Sopenharmony_ci if (vcdev->device_lost) 12108c2ecf20Sopenharmony_ci virtio_break_device(&vcdev->vdev); 12118c2ecf20Sopenharmony_ci unregister_virtio_device(&vcdev->vdev); 12128c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 12138c2ecf20Sopenharmony_ci dev_set_drvdata(&cdev->dev, NULL); 12148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci cdev->handler = NULL; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic int virtio_ccw_offline(struct ccw_device *cdev) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci unsigned long flags; 12228c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (!vcdev) 12258c2ecf20Sopenharmony_ci return 0; 12268c2ecf20Sopenharmony_ci if (vcdev->device_lost) 12278c2ecf20Sopenharmony_ci virtio_break_device(&vcdev->vdev); 12288c2ecf20Sopenharmony_ci unregister_virtio_device(&vcdev->vdev); 12298c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 12308c2ecf20Sopenharmony_ci dev_set_drvdata(&cdev->dev, NULL); 12318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 12328c2ecf20Sopenharmony_ci return 0; 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic int virtio_ccw_set_transport_rev(struct virtio_ccw_device *vcdev) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci struct virtio_rev_info *rev; 12388c2ecf20Sopenharmony_ci struct ccw1 *ccw; 12398c2ecf20Sopenharmony_ci int ret; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci ccw = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*ccw)); 12428c2ecf20Sopenharmony_ci if (!ccw) 12438c2ecf20Sopenharmony_ci return -ENOMEM; 12448c2ecf20Sopenharmony_ci rev = ccw_device_dma_zalloc(vcdev->cdev, sizeof(*rev)); 12458c2ecf20Sopenharmony_ci if (!rev) { 12468c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 12478c2ecf20Sopenharmony_ci return -ENOMEM; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci /* Set transport revision */ 12518c2ecf20Sopenharmony_ci ccw->cmd_code = CCW_CMD_SET_VIRTIO_REV; 12528c2ecf20Sopenharmony_ci ccw->flags = 0; 12538c2ecf20Sopenharmony_ci ccw->count = sizeof(*rev); 12548c2ecf20Sopenharmony_ci ccw->cda = (__u32)(unsigned long)rev; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci vcdev->revision = VIRTIO_CCW_REV_MAX; 12578c2ecf20Sopenharmony_ci do { 12588c2ecf20Sopenharmony_ci rev->revision = vcdev->revision; 12598c2ecf20Sopenharmony_ci /* none of our supported revisions carry payload */ 12608c2ecf20Sopenharmony_ci rev->length = 0; 12618c2ecf20Sopenharmony_ci ret = ccw_io_helper(vcdev, ccw, 12628c2ecf20Sopenharmony_ci VIRTIO_CCW_DOING_SET_VIRTIO_REV); 12638c2ecf20Sopenharmony_ci if (ret == -EOPNOTSUPP) { 12648c2ecf20Sopenharmony_ci if (vcdev->revision == 0) 12658c2ecf20Sopenharmony_ci /* 12668c2ecf20Sopenharmony_ci * The host device does not support setting 12678c2ecf20Sopenharmony_ci * the revision: let's operate it in legacy 12688c2ecf20Sopenharmony_ci * mode. 12698c2ecf20Sopenharmony_ci */ 12708c2ecf20Sopenharmony_ci ret = 0; 12718c2ecf20Sopenharmony_ci else 12728c2ecf20Sopenharmony_ci vcdev->revision--; 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci } while (ret == -EOPNOTSUPP); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, ccw, sizeof(*ccw)); 12778c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, rev, sizeof(*rev)); 12788c2ecf20Sopenharmony_ci return ret; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic int virtio_ccw_online(struct ccw_device *cdev) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci int ret; 12848c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev; 12858c2ecf20Sopenharmony_ci unsigned long flags; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); 12888c2ecf20Sopenharmony_ci if (!vcdev) { 12898c2ecf20Sopenharmony_ci dev_warn(&cdev->dev, "Could not get memory for virtio\n"); 12908c2ecf20Sopenharmony_ci ret = -ENOMEM; 12918c2ecf20Sopenharmony_ci goto out_free; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci vcdev->vdev.dev.parent = &cdev->dev; 12948c2ecf20Sopenharmony_ci vcdev->cdev = cdev; 12958c2ecf20Sopenharmony_ci vcdev->dma_area = ccw_device_dma_zalloc(vcdev->cdev, 12968c2ecf20Sopenharmony_ci sizeof(*vcdev->dma_area)); 12978c2ecf20Sopenharmony_ci if (!vcdev->dma_area) { 12988c2ecf20Sopenharmony_ci ret = -ENOMEM; 12998c2ecf20Sopenharmony_ci goto out_free; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci vcdev->is_thinint = virtio_ccw_use_airq; /* at least try */ 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci vcdev->vdev.dev.release = virtio_ccw_release_dev; 13058c2ecf20Sopenharmony_ci vcdev->vdev.config = &virtio_ccw_config_ops; 13068c2ecf20Sopenharmony_ci init_waitqueue_head(&vcdev->wait_q); 13078c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vcdev->virtqueues); 13088c2ecf20Sopenharmony_ci spin_lock_init(&vcdev->lock); 13098c2ecf20Sopenharmony_ci mutex_init(&vcdev->io_lock); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 13128c2ecf20Sopenharmony_ci dev_set_drvdata(&cdev->dev, vcdev); 13138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 13148c2ecf20Sopenharmony_ci vcdev->vdev.id.vendor = cdev->id.cu_type; 13158c2ecf20Sopenharmony_ci vcdev->vdev.id.device = cdev->id.cu_model; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci ret = virtio_ccw_set_transport_rev(vcdev); 13188c2ecf20Sopenharmony_ci if (ret) 13198c2ecf20Sopenharmony_ci goto out_free; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci ret = register_virtio_device(&vcdev->vdev); 13228c2ecf20Sopenharmony_ci if (ret) { 13238c2ecf20Sopenharmony_ci dev_warn(&cdev->dev, "Failed to register virtio device: %d\n", 13248c2ecf20Sopenharmony_ci ret); 13258c2ecf20Sopenharmony_ci goto out_put; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci return 0; 13288c2ecf20Sopenharmony_ciout_put: 13298c2ecf20Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(cdev), flags); 13308c2ecf20Sopenharmony_ci dev_set_drvdata(&cdev->dev, NULL); 13318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 13328c2ecf20Sopenharmony_ci put_device(&vcdev->vdev.dev); 13338c2ecf20Sopenharmony_ci return ret; 13348c2ecf20Sopenharmony_ciout_free: 13358c2ecf20Sopenharmony_ci if (vcdev) { 13368c2ecf20Sopenharmony_ci ccw_device_dma_free(vcdev->cdev, vcdev->dma_area, 13378c2ecf20Sopenharmony_ci sizeof(*vcdev->dma_area)); 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci kfree(vcdev); 13408c2ecf20Sopenharmony_ci return ret; 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cistatic int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci int rc; 13468c2ecf20Sopenharmony_ci struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* 13498c2ecf20Sopenharmony_ci * Make sure vcdev is set 13508c2ecf20Sopenharmony_ci * i.e. set_offline/remove callback not already running 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_ci if (!vcdev) 13538c2ecf20Sopenharmony_ci return NOTIFY_DONE; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci switch (event) { 13568c2ecf20Sopenharmony_ci case CIO_GONE: 13578c2ecf20Sopenharmony_ci vcdev->device_lost = true; 13588c2ecf20Sopenharmony_ci rc = NOTIFY_DONE; 13598c2ecf20Sopenharmony_ci break; 13608c2ecf20Sopenharmony_ci case CIO_OPER: 13618c2ecf20Sopenharmony_ci rc = NOTIFY_OK; 13628c2ecf20Sopenharmony_ci break; 13638c2ecf20Sopenharmony_ci default: 13648c2ecf20Sopenharmony_ci rc = NOTIFY_DONE; 13658c2ecf20Sopenharmony_ci break; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci return rc; 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic struct ccw_device_id virtio_ids[] = { 13718c2ecf20Sopenharmony_ci { CCW_DEVICE(0x3832, 0) }, 13728c2ecf20Sopenharmony_ci {}, 13738c2ecf20Sopenharmony_ci}; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_cistatic struct ccw_driver virtio_ccw_driver = { 13768c2ecf20Sopenharmony_ci .driver = { 13778c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 13788c2ecf20Sopenharmony_ci .name = "virtio_ccw", 13798c2ecf20Sopenharmony_ci }, 13808c2ecf20Sopenharmony_ci .ids = virtio_ids, 13818c2ecf20Sopenharmony_ci .probe = virtio_ccw_probe, 13828c2ecf20Sopenharmony_ci .remove = virtio_ccw_remove, 13838c2ecf20Sopenharmony_ci .set_offline = virtio_ccw_offline, 13848c2ecf20Sopenharmony_ci .set_online = virtio_ccw_online, 13858c2ecf20Sopenharmony_ci .notify = virtio_ccw_cio_notify, 13868c2ecf20Sopenharmony_ci .int_class = IRQIO_VIR, 13878c2ecf20Sopenharmony_ci}; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_cistatic int __init pure_hex(char **cp, unsigned int *val, int min_digit, 13908c2ecf20Sopenharmony_ci int max_digit, int max_val) 13918c2ecf20Sopenharmony_ci{ 13928c2ecf20Sopenharmony_ci int diff; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci diff = 0; 13958c2ecf20Sopenharmony_ci *val = 0; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci while (diff <= max_digit) { 13988c2ecf20Sopenharmony_ci int value = hex_to_bin(**cp); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (value < 0) 14018c2ecf20Sopenharmony_ci break; 14028c2ecf20Sopenharmony_ci *val = *val * 16 + value; 14038c2ecf20Sopenharmony_ci (*cp)++; 14048c2ecf20Sopenharmony_ci diff++; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci if ((diff < min_digit) || (diff > max_digit) || (*val > max_val)) 14088c2ecf20Sopenharmony_ci return 1; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci return 0; 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cistatic int __init parse_busid(char *str, unsigned int *cssid, 14148c2ecf20Sopenharmony_ci unsigned int *ssid, unsigned int *devno) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci char *str_work; 14178c2ecf20Sopenharmony_ci int rc, ret; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci rc = 1; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (*str == '\0') 14228c2ecf20Sopenharmony_ci goto out; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci str_work = str; 14258c2ecf20Sopenharmony_ci ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID); 14268c2ecf20Sopenharmony_ci if (ret || (str_work[0] != '.')) 14278c2ecf20Sopenharmony_ci goto out; 14288c2ecf20Sopenharmony_ci str_work++; 14298c2ecf20Sopenharmony_ci ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID); 14308c2ecf20Sopenharmony_ci if (ret || (str_work[0] != '.')) 14318c2ecf20Sopenharmony_ci goto out; 14328c2ecf20Sopenharmony_ci str_work++; 14338c2ecf20Sopenharmony_ci ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL); 14348c2ecf20Sopenharmony_ci if (ret || (str_work[0] != '\0')) 14358c2ecf20Sopenharmony_ci goto out; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci rc = 0; 14388c2ecf20Sopenharmony_ciout: 14398c2ecf20Sopenharmony_ci return rc; 14408c2ecf20Sopenharmony_ci} 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_cistatic void __init no_auto_parse(void) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to; 14458c2ecf20Sopenharmony_ci char *parm, *str; 14468c2ecf20Sopenharmony_ci int rc; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci str = no_auto; 14498c2ecf20Sopenharmony_ci while ((parm = strsep(&str, ","))) { 14508c2ecf20Sopenharmony_ci rc = parse_busid(strsep(&parm, "-"), &from_cssid, 14518c2ecf20Sopenharmony_ci &from_ssid, &from); 14528c2ecf20Sopenharmony_ci if (rc) 14538c2ecf20Sopenharmony_ci continue; 14548c2ecf20Sopenharmony_ci if (parm != NULL) { 14558c2ecf20Sopenharmony_ci rc = parse_busid(parm, &to_cssid, 14568c2ecf20Sopenharmony_ci &to_ssid, &to); 14578c2ecf20Sopenharmony_ci if ((from_ssid > to_ssid) || 14588c2ecf20Sopenharmony_ci ((from_ssid == to_ssid) && (from > to))) 14598c2ecf20Sopenharmony_ci rc = -EINVAL; 14608c2ecf20Sopenharmony_ci } else { 14618c2ecf20Sopenharmony_ci to_cssid = from_cssid; 14628c2ecf20Sopenharmony_ci to_ssid = from_ssid; 14638c2ecf20Sopenharmony_ci to = from; 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci if (rc) 14668c2ecf20Sopenharmony_ci continue; 14678c2ecf20Sopenharmony_ci while ((from_ssid < to_ssid) || 14688c2ecf20Sopenharmony_ci ((from_ssid == to_ssid) && (from <= to))) { 14698c2ecf20Sopenharmony_ci set_bit(from, devs_no_auto[from_ssid]); 14708c2ecf20Sopenharmony_ci from++; 14718c2ecf20Sopenharmony_ci if (from > __MAX_SUBCHANNEL) { 14728c2ecf20Sopenharmony_ci from_ssid++; 14738c2ecf20Sopenharmony_ci from = 0; 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci} 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_cistatic int __init virtio_ccw_init(void) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci int rc; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci /* parse no_auto string before we do anything further */ 14848c2ecf20Sopenharmony_ci no_auto_parse(); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci summary_indicators = cio_dma_zalloc(MAX_AIRQ_AREAS); 14878c2ecf20Sopenharmony_ci if (!summary_indicators) 14888c2ecf20Sopenharmony_ci return -ENOMEM; 14898c2ecf20Sopenharmony_ci rc = ccw_driver_register(&virtio_ccw_driver); 14908c2ecf20Sopenharmony_ci if (rc) 14918c2ecf20Sopenharmony_ci cio_dma_free(summary_indicators, MAX_AIRQ_AREAS); 14928c2ecf20Sopenharmony_ci return rc; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_cidevice_initcall(virtio_ccw_init); 1495