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