162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 Intel Corporation
462306a36Sopenharmony_ci * Author: Johannes Berg <johannes@sipsolutions.net>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/pci.h>
862306a36Sopenharmony_ci#include <linux/virtio.h>
962306a36Sopenharmony_ci#include <linux/virtio_config.h>
1062306a36Sopenharmony_ci#include <linux/logic_iomem.h>
1162306a36Sopenharmony_ci#include <linux/of_platform.h>
1262306a36Sopenharmony_ci#include <linux/irqdomain.h>
1362306a36Sopenharmony_ci#include <linux/virtio_pcidev.h>
1462306a36Sopenharmony_ci#include <linux/virtio-uml.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/msi.h>
1762306a36Sopenharmony_ci#include <asm/unaligned.h>
1862306a36Sopenharmony_ci#include <irq_kern.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define MAX_DEVICES 8
2162306a36Sopenharmony_ci#define MAX_MSI_VECTORS 32
2262306a36Sopenharmony_ci#define CFG_SPACE_SIZE 4096
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* for MSI-X we have a 32-bit payload */
2562306a36Sopenharmony_ci#define MAX_IRQ_MSG_SIZE (sizeof(struct virtio_pcidev_msg) + sizeof(u32))
2662306a36Sopenharmony_ci#define NUM_IRQ_MSGS	10
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define HANDLE_NO_FREE(ptr) ((void *)((unsigned long)(ptr) | 1))
2962306a36Sopenharmony_ci#define HANDLE_IS_NO_FREE(ptr) ((unsigned long)(ptr) & 1)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct um_pci_device {
3262306a36Sopenharmony_ci	struct virtio_device *vdev;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* for now just standard BARs */
3562306a36Sopenharmony_ci	u8 resptr[PCI_STD_NUM_BARS];
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	struct virtqueue *cmd_vq, *irq_vq;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define UM_PCI_STAT_WAITING	0
4062306a36Sopenharmony_ci	unsigned long status;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	int irq;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	bool platform;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct um_pci_device_reg {
4862306a36Sopenharmony_ci	struct um_pci_device *dev;
4962306a36Sopenharmony_ci	void __iomem *iomem;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct pci_host_bridge *bridge;
5362306a36Sopenharmony_cistatic DEFINE_MUTEX(um_pci_mtx);
5462306a36Sopenharmony_cistatic struct um_pci_device *um_pci_platform_device;
5562306a36Sopenharmony_cistatic struct um_pci_device_reg um_pci_devices[MAX_DEVICES];
5662306a36Sopenharmony_cistatic struct fwnode_handle *um_pci_fwnode;
5762306a36Sopenharmony_cistatic struct irq_domain *um_pci_inner_domain;
5862306a36Sopenharmony_cistatic struct irq_domain *um_pci_msi_domain;
5962306a36Sopenharmony_cistatic unsigned long um_pci_msi_used[BITS_TO_LONGS(MAX_MSI_VECTORS)];
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic unsigned int um_pci_max_delay_us = 40000;
6262306a36Sopenharmony_cimodule_param_named(max_delay_us, um_pci_max_delay_us, uint, 0644);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct um_pci_message_buffer {
6562306a36Sopenharmony_ci	struct virtio_pcidev_msg hdr;
6662306a36Sopenharmony_ci	u8 data[8];
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic struct um_pci_message_buffer __percpu *um_pci_msg_bufs;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int um_pci_send_cmd(struct um_pci_device *dev,
7262306a36Sopenharmony_ci			   struct virtio_pcidev_msg *cmd,
7362306a36Sopenharmony_ci			   unsigned int cmd_size,
7462306a36Sopenharmony_ci			   const void *extra, unsigned int extra_size,
7562306a36Sopenharmony_ci			   void *out, unsigned int out_size)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct scatterlist out_sg, extra_sg, in_sg;
7862306a36Sopenharmony_ci	struct scatterlist *sgs_list[] = {
7962306a36Sopenharmony_ci		[0] = &out_sg,
8062306a36Sopenharmony_ci		[1] = extra ? &extra_sg : &in_sg,
8162306a36Sopenharmony_ci		[2] = extra ? &in_sg : NULL,
8262306a36Sopenharmony_ci	};
8362306a36Sopenharmony_ci	struct um_pci_message_buffer *buf;
8462306a36Sopenharmony_ci	int delay_count = 0;
8562306a36Sopenharmony_ci	int ret, len;
8662306a36Sopenharmony_ci	bool posted;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (WARN_ON(cmd_size < sizeof(*cmd) || cmd_size > sizeof(*buf)))
8962306a36Sopenharmony_ci		return -EINVAL;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	switch (cmd->op) {
9262306a36Sopenharmony_ci	case VIRTIO_PCIDEV_OP_CFG_WRITE:
9362306a36Sopenharmony_ci	case VIRTIO_PCIDEV_OP_MMIO_WRITE:
9462306a36Sopenharmony_ci	case VIRTIO_PCIDEV_OP_MMIO_MEMSET:
9562306a36Sopenharmony_ci		/* in PCI, writes are posted, so don't wait */
9662306a36Sopenharmony_ci		posted = !out;
9762306a36Sopenharmony_ci		WARN_ON(!posted);
9862306a36Sopenharmony_ci		break;
9962306a36Sopenharmony_ci	default:
10062306a36Sopenharmony_ci		posted = false;
10162306a36Sopenharmony_ci		break;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	buf = get_cpu_var(um_pci_msg_bufs);
10562306a36Sopenharmony_ci	if (buf)
10662306a36Sopenharmony_ci		memcpy(buf, cmd, cmd_size);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (posted) {
10962306a36Sopenharmony_ci		u8 *ncmd = kmalloc(cmd_size + extra_size, GFP_ATOMIC);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		if (ncmd) {
11262306a36Sopenharmony_ci			memcpy(ncmd, cmd, cmd_size);
11362306a36Sopenharmony_ci			if (extra)
11462306a36Sopenharmony_ci				memcpy(ncmd + cmd_size, extra, extra_size);
11562306a36Sopenharmony_ci			cmd = (void *)ncmd;
11662306a36Sopenharmony_ci			cmd_size += extra_size;
11762306a36Sopenharmony_ci			extra = NULL;
11862306a36Sopenharmony_ci			extra_size = 0;
11962306a36Sopenharmony_ci		} else {
12062306a36Sopenharmony_ci			/* try without allocating memory */
12162306a36Sopenharmony_ci			posted = false;
12262306a36Sopenharmony_ci			cmd = (void *)buf;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	} else {
12562306a36Sopenharmony_ci		cmd = (void *)buf;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	sg_init_one(&out_sg, cmd, cmd_size);
12962306a36Sopenharmony_ci	if (extra)
13062306a36Sopenharmony_ci		sg_init_one(&extra_sg, extra, extra_size);
13162306a36Sopenharmony_ci	if (out)
13262306a36Sopenharmony_ci		sg_init_one(&in_sg, out, out_size);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* add to internal virtio queue */
13562306a36Sopenharmony_ci	ret = virtqueue_add_sgs(dev->cmd_vq, sgs_list,
13662306a36Sopenharmony_ci				extra ? 2 : 1,
13762306a36Sopenharmony_ci				out ? 1 : 0,
13862306a36Sopenharmony_ci				posted ? cmd : HANDLE_NO_FREE(cmd),
13962306a36Sopenharmony_ci				GFP_ATOMIC);
14062306a36Sopenharmony_ci	if (ret) {
14162306a36Sopenharmony_ci		if (posted)
14262306a36Sopenharmony_ci			kfree(cmd);
14362306a36Sopenharmony_ci		goto out;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (posted) {
14762306a36Sopenharmony_ci		virtqueue_kick(dev->cmd_vq);
14862306a36Sopenharmony_ci		ret = 0;
14962306a36Sopenharmony_ci		goto out;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* kick and poll for getting a response on the queue */
15362306a36Sopenharmony_ci	set_bit(UM_PCI_STAT_WAITING, &dev->status);
15462306a36Sopenharmony_ci	virtqueue_kick(dev->cmd_vq);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	while (1) {
15762306a36Sopenharmony_ci		void *completed = virtqueue_get_buf(dev->cmd_vq, &len);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		if (completed == HANDLE_NO_FREE(cmd))
16062306a36Sopenharmony_ci			break;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		if (completed && !HANDLE_IS_NO_FREE(completed))
16362306a36Sopenharmony_ci			kfree(completed);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		if (WARN_ONCE(virtqueue_is_broken(dev->cmd_vq) ||
16662306a36Sopenharmony_ci			      ++delay_count > um_pci_max_delay_us,
16762306a36Sopenharmony_ci			      "um virt-pci delay: %d", delay_count)) {
16862306a36Sopenharmony_ci			ret = -EIO;
16962306a36Sopenharmony_ci			break;
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci		udelay(1);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci	clear_bit(UM_PCI_STAT_WAITING, &dev->status);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciout:
17662306a36Sopenharmony_ci	put_cpu_var(um_pci_msg_bufs);
17762306a36Sopenharmony_ci	return ret;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic unsigned long um_pci_cfgspace_read(void *priv, unsigned int offset,
18162306a36Sopenharmony_ci					  int size)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct um_pci_device_reg *reg = priv;
18462306a36Sopenharmony_ci	struct um_pci_device *dev = reg->dev;
18562306a36Sopenharmony_ci	struct virtio_pcidev_msg hdr = {
18662306a36Sopenharmony_ci		.op = VIRTIO_PCIDEV_OP_CFG_READ,
18762306a36Sopenharmony_ci		.size = size,
18862306a36Sopenharmony_ci		.addr = offset,
18962306a36Sopenharmony_ci	};
19062306a36Sopenharmony_ci	/* buf->data is maximum size - we may only use parts of it */
19162306a36Sopenharmony_ci	struct um_pci_message_buffer *buf;
19262306a36Sopenharmony_ci	u8 *data;
19362306a36Sopenharmony_ci	unsigned long ret = ULONG_MAX;
19462306a36Sopenharmony_ci	size_t bytes = sizeof(buf->data);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (!dev)
19762306a36Sopenharmony_ci		return ULONG_MAX;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	buf = get_cpu_var(um_pci_msg_bufs);
20062306a36Sopenharmony_ci	data = buf->data;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (buf)
20362306a36Sopenharmony_ci		memset(data, 0xff, bytes);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	switch (size) {
20662306a36Sopenharmony_ci	case 1:
20762306a36Sopenharmony_ci	case 2:
20862306a36Sopenharmony_ci	case 4:
20962306a36Sopenharmony_ci#ifdef CONFIG_64BIT
21062306a36Sopenharmony_ci	case 8:
21162306a36Sopenharmony_ci#endif
21262306a36Sopenharmony_ci		break;
21362306a36Sopenharmony_ci	default:
21462306a36Sopenharmony_ci		WARN(1, "invalid config space read size %d\n", size);
21562306a36Sopenharmony_ci		goto out;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, data, bytes))
21962306a36Sopenharmony_ci		goto out;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	switch (size) {
22262306a36Sopenharmony_ci	case 1:
22362306a36Sopenharmony_ci		ret = data[0];
22462306a36Sopenharmony_ci		break;
22562306a36Sopenharmony_ci	case 2:
22662306a36Sopenharmony_ci		ret = le16_to_cpup((void *)data);
22762306a36Sopenharmony_ci		break;
22862306a36Sopenharmony_ci	case 4:
22962306a36Sopenharmony_ci		ret = le32_to_cpup((void *)data);
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci#ifdef CONFIG_64BIT
23262306a36Sopenharmony_ci	case 8:
23362306a36Sopenharmony_ci		ret = le64_to_cpup((void *)data);
23462306a36Sopenharmony_ci		break;
23562306a36Sopenharmony_ci#endif
23662306a36Sopenharmony_ci	default:
23762306a36Sopenharmony_ci		break;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ciout:
24162306a36Sopenharmony_ci	put_cpu_var(um_pci_msg_bufs);
24262306a36Sopenharmony_ci	return ret;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void um_pci_cfgspace_write(void *priv, unsigned int offset, int size,
24662306a36Sopenharmony_ci				  unsigned long val)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct um_pci_device_reg *reg = priv;
24962306a36Sopenharmony_ci	struct um_pci_device *dev = reg->dev;
25062306a36Sopenharmony_ci	struct {
25162306a36Sopenharmony_ci		struct virtio_pcidev_msg hdr;
25262306a36Sopenharmony_ci		/* maximum size - we may only use parts of it */
25362306a36Sopenharmony_ci		u8 data[8];
25462306a36Sopenharmony_ci	} msg = {
25562306a36Sopenharmony_ci		.hdr = {
25662306a36Sopenharmony_ci			.op = VIRTIO_PCIDEV_OP_CFG_WRITE,
25762306a36Sopenharmony_ci			.size = size,
25862306a36Sopenharmony_ci			.addr = offset,
25962306a36Sopenharmony_ci		},
26062306a36Sopenharmony_ci	};
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (!dev)
26362306a36Sopenharmony_ci		return;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	switch (size) {
26662306a36Sopenharmony_ci	case 1:
26762306a36Sopenharmony_ci		msg.data[0] = (u8)val;
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci	case 2:
27062306a36Sopenharmony_ci		put_unaligned_le16(val, (void *)msg.data);
27162306a36Sopenharmony_ci		break;
27262306a36Sopenharmony_ci	case 4:
27362306a36Sopenharmony_ci		put_unaligned_le32(val, (void *)msg.data);
27462306a36Sopenharmony_ci		break;
27562306a36Sopenharmony_ci#ifdef CONFIG_64BIT
27662306a36Sopenharmony_ci	case 8:
27762306a36Sopenharmony_ci		put_unaligned_le64(val, (void *)msg.data);
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci#endif
28062306a36Sopenharmony_ci	default:
28162306a36Sopenharmony_ci		WARN(1, "invalid config space write size %d\n", size);
28262306a36Sopenharmony_ci		return;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	WARN_ON(um_pci_send_cmd(dev, &msg.hdr, sizeof(msg), NULL, 0, NULL, 0));
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct logic_iomem_ops um_pci_device_cfgspace_ops = {
28962306a36Sopenharmony_ci	.read = um_pci_cfgspace_read,
29062306a36Sopenharmony_ci	.write = um_pci_cfgspace_write,
29162306a36Sopenharmony_ci};
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void um_pci_bar_copy_from(void *priv, void *buffer,
29462306a36Sopenharmony_ci				 unsigned int offset, int size)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	u8 *resptr = priv;
29762306a36Sopenharmony_ci	struct um_pci_device *dev = container_of(resptr - *resptr,
29862306a36Sopenharmony_ci						 struct um_pci_device,
29962306a36Sopenharmony_ci						 resptr[0]);
30062306a36Sopenharmony_ci	struct virtio_pcidev_msg hdr = {
30162306a36Sopenharmony_ci		.op = VIRTIO_PCIDEV_OP_MMIO_READ,
30262306a36Sopenharmony_ci		.bar = *resptr,
30362306a36Sopenharmony_ci		.size = size,
30462306a36Sopenharmony_ci		.addr = offset,
30562306a36Sopenharmony_ci	};
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	memset(buffer, 0xff, size);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	um_pci_send_cmd(dev, &hdr, sizeof(hdr), NULL, 0, buffer, size);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic unsigned long um_pci_bar_read(void *priv, unsigned int offset,
31362306a36Sopenharmony_ci				     int size)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	/* buf->data is maximum size - we may only use parts of it */
31662306a36Sopenharmony_ci	struct um_pci_message_buffer *buf;
31762306a36Sopenharmony_ci	u8 *data;
31862306a36Sopenharmony_ci	unsigned long ret = ULONG_MAX;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	buf = get_cpu_var(um_pci_msg_bufs);
32162306a36Sopenharmony_ci	data = buf->data;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	switch (size) {
32462306a36Sopenharmony_ci	case 1:
32562306a36Sopenharmony_ci	case 2:
32662306a36Sopenharmony_ci	case 4:
32762306a36Sopenharmony_ci#ifdef CONFIG_64BIT
32862306a36Sopenharmony_ci	case 8:
32962306a36Sopenharmony_ci#endif
33062306a36Sopenharmony_ci		break;
33162306a36Sopenharmony_ci	default:
33262306a36Sopenharmony_ci		WARN(1, "invalid config space read size %d\n", size);
33362306a36Sopenharmony_ci		goto out;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	um_pci_bar_copy_from(priv, data, offset, size);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	switch (size) {
33962306a36Sopenharmony_ci	case 1:
34062306a36Sopenharmony_ci		ret = data[0];
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	case 2:
34362306a36Sopenharmony_ci		ret = le16_to_cpup((void *)data);
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	case 4:
34662306a36Sopenharmony_ci		ret = le32_to_cpup((void *)data);
34762306a36Sopenharmony_ci		break;
34862306a36Sopenharmony_ci#ifdef CONFIG_64BIT
34962306a36Sopenharmony_ci	case 8:
35062306a36Sopenharmony_ci		ret = le64_to_cpup((void *)data);
35162306a36Sopenharmony_ci		break;
35262306a36Sopenharmony_ci#endif
35362306a36Sopenharmony_ci	default:
35462306a36Sopenharmony_ci		break;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ciout:
35862306a36Sopenharmony_ci	put_cpu_var(um_pci_msg_bufs);
35962306a36Sopenharmony_ci	return ret;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void um_pci_bar_copy_to(void *priv, unsigned int offset,
36362306a36Sopenharmony_ci			       const void *buffer, int size)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	u8 *resptr = priv;
36662306a36Sopenharmony_ci	struct um_pci_device *dev = container_of(resptr - *resptr,
36762306a36Sopenharmony_ci						 struct um_pci_device,
36862306a36Sopenharmony_ci						 resptr[0]);
36962306a36Sopenharmony_ci	struct virtio_pcidev_msg hdr = {
37062306a36Sopenharmony_ci		.op = VIRTIO_PCIDEV_OP_MMIO_WRITE,
37162306a36Sopenharmony_ci		.bar = *resptr,
37262306a36Sopenharmony_ci		.size = size,
37362306a36Sopenharmony_ci		.addr = offset,
37462306a36Sopenharmony_ci	};
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	um_pci_send_cmd(dev, &hdr, sizeof(hdr), buffer, size, NULL, 0);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void um_pci_bar_write(void *priv, unsigned int offset, int size,
38062306a36Sopenharmony_ci			     unsigned long val)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	/* maximum size - we may only use parts of it */
38362306a36Sopenharmony_ci	u8 data[8];
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	switch (size) {
38662306a36Sopenharmony_ci	case 1:
38762306a36Sopenharmony_ci		data[0] = (u8)val;
38862306a36Sopenharmony_ci		break;
38962306a36Sopenharmony_ci	case 2:
39062306a36Sopenharmony_ci		put_unaligned_le16(val, (void *)data);
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	case 4:
39362306a36Sopenharmony_ci		put_unaligned_le32(val, (void *)data);
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci#ifdef CONFIG_64BIT
39662306a36Sopenharmony_ci	case 8:
39762306a36Sopenharmony_ci		put_unaligned_le64(val, (void *)data);
39862306a36Sopenharmony_ci		break;
39962306a36Sopenharmony_ci#endif
40062306a36Sopenharmony_ci	default:
40162306a36Sopenharmony_ci		WARN(1, "invalid config space write size %d\n", size);
40262306a36Sopenharmony_ci		return;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	um_pci_bar_copy_to(priv, offset, data, size);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic void um_pci_bar_set(void *priv, unsigned int offset, u8 value, int size)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	u8 *resptr = priv;
41162306a36Sopenharmony_ci	struct um_pci_device *dev = container_of(resptr - *resptr,
41262306a36Sopenharmony_ci						 struct um_pci_device,
41362306a36Sopenharmony_ci						 resptr[0]);
41462306a36Sopenharmony_ci	struct {
41562306a36Sopenharmony_ci		struct virtio_pcidev_msg hdr;
41662306a36Sopenharmony_ci		u8 data;
41762306a36Sopenharmony_ci	} msg = {
41862306a36Sopenharmony_ci		.hdr = {
41962306a36Sopenharmony_ci			.op = VIRTIO_PCIDEV_OP_CFG_WRITE,
42062306a36Sopenharmony_ci			.bar = *resptr,
42162306a36Sopenharmony_ci			.size = size,
42262306a36Sopenharmony_ci			.addr = offset,
42362306a36Sopenharmony_ci		},
42462306a36Sopenharmony_ci		.data = value,
42562306a36Sopenharmony_ci	};
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	um_pci_send_cmd(dev, &msg.hdr, sizeof(msg), NULL, 0, NULL, 0);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic const struct logic_iomem_ops um_pci_device_bar_ops = {
43162306a36Sopenharmony_ci	.read = um_pci_bar_read,
43262306a36Sopenharmony_ci	.write = um_pci_bar_write,
43362306a36Sopenharmony_ci	.set = um_pci_bar_set,
43462306a36Sopenharmony_ci	.copy_from = um_pci_bar_copy_from,
43562306a36Sopenharmony_ci	.copy_to = um_pci_bar_copy_to,
43662306a36Sopenharmony_ci};
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic void __iomem *um_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
43962306a36Sopenharmony_ci				    int where)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct um_pci_device_reg *dev;
44262306a36Sopenharmony_ci	unsigned int busn = bus->number;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (busn > 0)
44562306a36Sopenharmony_ci		return NULL;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* not allowing functions for now ... */
44862306a36Sopenharmony_ci	if (devfn % 8)
44962306a36Sopenharmony_ci		return NULL;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (devfn / 8 >= ARRAY_SIZE(um_pci_devices))
45262306a36Sopenharmony_ci		return NULL;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	dev = &um_pci_devices[devfn / 8];
45562306a36Sopenharmony_ci	if (!dev)
45662306a36Sopenharmony_ci		return NULL;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return (void __iomem *)((unsigned long)dev->iomem + where);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic struct pci_ops um_pci_ops = {
46262306a36Sopenharmony_ci	.map_bus = um_pci_map_bus,
46362306a36Sopenharmony_ci	.read = pci_generic_config_read,
46462306a36Sopenharmony_ci	.write = pci_generic_config_write,
46562306a36Sopenharmony_ci};
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic void um_pci_rescan(void)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	pci_lock_rescan_remove();
47062306a36Sopenharmony_ci	pci_rescan_bus(bridge->bus);
47162306a36Sopenharmony_ci	pci_unlock_rescan_remove();
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic void um_pci_irq_vq_addbuf(struct virtqueue *vq, void *buf, bool kick)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	struct scatterlist sg[1];
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	sg_init_one(sg, buf, MAX_IRQ_MSG_SIZE);
47962306a36Sopenharmony_ci	if (virtqueue_add_inbuf(vq, sg, 1, buf, GFP_ATOMIC))
48062306a36Sopenharmony_ci		kfree(buf);
48162306a36Sopenharmony_ci	else if (kick)
48262306a36Sopenharmony_ci		virtqueue_kick(vq);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic void um_pci_handle_irq_message(struct virtqueue *vq,
48662306a36Sopenharmony_ci				      struct virtio_pcidev_msg *msg)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct virtio_device *vdev = vq->vdev;
48962306a36Sopenharmony_ci	struct um_pci_device *dev = vdev->priv;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (!dev->irq)
49262306a36Sopenharmony_ci		return;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	/* we should properly chain interrupts, but on ARCH=um we don't care */
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	switch (msg->op) {
49762306a36Sopenharmony_ci	case VIRTIO_PCIDEV_OP_INT:
49862306a36Sopenharmony_ci		generic_handle_irq(dev->irq);
49962306a36Sopenharmony_ci		break;
50062306a36Sopenharmony_ci	case VIRTIO_PCIDEV_OP_MSI:
50162306a36Sopenharmony_ci		/* our MSI message is just the interrupt number */
50262306a36Sopenharmony_ci		if (msg->size == sizeof(u32))
50362306a36Sopenharmony_ci			generic_handle_irq(le32_to_cpup((void *)msg->data));
50462306a36Sopenharmony_ci		else
50562306a36Sopenharmony_ci			generic_handle_irq(le16_to_cpup((void *)msg->data));
50662306a36Sopenharmony_ci		break;
50762306a36Sopenharmony_ci	case VIRTIO_PCIDEV_OP_PME:
50862306a36Sopenharmony_ci		/* nothing to do - we already woke up due to the message */
50962306a36Sopenharmony_ci		break;
51062306a36Sopenharmony_ci	default:
51162306a36Sopenharmony_ci		dev_err(&vdev->dev, "unexpected virt-pci message %d\n", msg->op);
51262306a36Sopenharmony_ci		break;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic void um_pci_cmd_vq_cb(struct virtqueue *vq)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct virtio_device *vdev = vq->vdev;
51962306a36Sopenharmony_ci	struct um_pci_device *dev = vdev->priv;
52062306a36Sopenharmony_ci	void *cmd;
52162306a36Sopenharmony_ci	int len;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (test_bit(UM_PCI_STAT_WAITING, &dev->status))
52462306a36Sopenharmony_ci		return;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	while ((cmd = virtqueue_get_buf(vq, &len))) {
52762306a36Sopenharmony_ci		if (WARN_ON(HANDLE_IS_NO_FREE(cmd)))
52862306a36Sopenharmony_ci			continue;
52962306a36Sopenharmony_ci		kfree(cmd);
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic void um_pci_irq_vq_cb(struct virtqueue *vq)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct virtio_pcidev_msg *msg;
53662306a36Sopenharmony_ci	int len;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	while ((msg = virtqueue_get_buf(vq, &len))) {
53962306a36Sopenharmony_ci		if (len >= sizeof(*msg))
54062306a36Sopenharmony_ci			um_pci_handle_irq_message(vq, msg);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		/* recycle the message buffer */
54362306a36Sopenharmony_ci		um_pci_irq_vq_addbuf(vq, msg, true);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci#ifdef CONFIG_OF
54862306a36Sopenharmony_ci/* Copied from arch/x86/kernel/devicetree.c */
54962306a36Sopenharmony_cistruct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct device_node *np;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	for_each_node_by_type(np, "pci") {
55462306a36Sopenharmony_ci		const void *prop;
55562306a36Sopenharmony_ci		unsigned int bus_min;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		prop = of_get_property(np, "bus-range", NULL);
55862306a36Sopenharmony_ci		if (!prop)
55962306a36Sopenharmony_ci			continue;
56062306a36Sopenharmony_ci		bus_min = be32_to_cpup(prop);
56162306a36Sopenharmony_ci		if (bus->number == bus_min)
56262306a36Sopenharmony_ci			return np;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci	return NULL;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci#endif
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int um_pci_init_vqs(struct um_pci_device *dev)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct virtqueue *vqs[2];
57162306a36Sopenharmony_ci	static const char *const names[2] = { "cmd", "irq" };
57262306a36Sopenharmony_ci	vq_callback_t *cbs[2] = { um_pci_cmd_vq_cb, um_pci_irq_vq_cb };
57362306a36Sopenharmony_ci	int err, i;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	err = virtio_find_vqs(dev->vdev, 2, vqs, cbs, names, NULL);
57662306a36Sopenharmony_ci	if (err)
57762306a36Sopenharmony_ci		return err;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	dev->cmd_vq = vqs[0];
58062306a36Sopenharmony_ci	dev->irq_vq = vqs[1];
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	virtio_device_ready(dev->vdev);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	for (i = 0; i < NUM_IRQ_MSGS; i++) {
58562306a36Sopenharmony_ci		void *msg = kzalloc(MAX_IRQ_MSG_SIZE, GFP_KERNEL);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		if (msg)
58862306a36Sopenharmony_ci			um_pci_irq_vq_addbuf(dev->irq_vq, msg, false);
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	virtqueue_kick(dev->irq_vq);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	return 0;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic void __um_pci_virtio_platform_remove(struct virtio_device *vdev,
59762306a36Sopenharmony_ci					    struct um_pci_device *dev)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	virtio_reset_device(vdev);
60062306a36Sopenharmony_ci	vdev->config->del_vqs(vdev);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	mutex_lock(&um_pci_mtx);
60362306a36Sopenharmony_ci	um_pci_platform_device = NULL;
60462306a36Sopenharmony_ci	mutex_unlock(&um_pci_mtx);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	kfree(dev);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int um_pci_virtio_platform_probe(struct virtio_device *vdev,
61062306a36Sopenharmony_ci					struct um_pci_device *dev)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	int ret;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	dev->platform = true;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	mutex_lock(&um_pci_mtx);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (um_pci_platform_device) {
61962306a36Sopenharmony_ci		mutex_unlock(&um_pci_mtx);
62062306a36Sopenharmony_ci		ret = -EBUSY;
62162306a36Sopenharmony_ci		goto out_free;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	ret = um_pci_init_vqs(dev);
62562306a36Sopenharmony_ci	if (ret) {
62662306a36Sopenharmony_ci		mutex_unlock(&um_pci_mtx);
62762306a36Sopenharmony_ci		goto out_free;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	um_pci_platform_device = dev;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	mutex_unlock(&um_pci_mtx);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	ret = of_platform_default_populate(vdev->dev.of_node, NULL, &vdev->dev);
63562306a36Sopenharmony_ci	if (ret)
63662306a36Sopenharmony_ci		__um_pci_virtio_platform_remove(vdev, dev);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	return ret;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ciout_free:
64162306a36Sopenharmony_ci	kfree(dev);
64262306a36Sopenharmony_ci	return ret;
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic int um_pci_virtio_probe(struct virtio_device *vdev)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct um_pci_device *dev;
64862306a36Sopenharmony_ci	int i, free = -1;
64962306a36Sopenharmony_ci	int err = -ENOSPC;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
65262306a36Sopenharmony_ci	if (!dev)
65362306a36Sopenharmony_ci		return -ENOMEM;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	dev->vdev = vdev;
65662306a36Sopenharmony_ci	vdev->priv = dev;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (of_device_is_compatible(vdev->dev.of_node, "simple-bus"))
65962306a36Sopenharmony_ci		return um_pci_virtio_platform_probe(vdev, dev);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	mutex_lock(&um_pci_mtx);
66262306a36Sopenharmony_ci	for (i = 0; i < MAX_DEVICES; i++) {
66362306a36Sopenharmony_ci		if (um_pci_devices[i].dev)
66462306a36Sopenharmony_ci			continue;
66562306a36Sopenharmony_ci		free = i;
66662306a36Sopenharmony_ci		break;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (free < 0)
67062306a36Sopenharmony_ci		goto error;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	err = um_pci_init_vqs(dev);
67362306a36Sopenharmony_ci	if (err)
67462306a36Sopenharmony_ci		goto error;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	dev->irq = irq_alloc_desc(numa_node_id());
67762306a36Sopenharmony_ci	if (dev->irq < 0) {
67862306a36Sopenharmony_ci		err = dev->irq;
67962306a36Sopenharmony_ci		goto err_reset;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci	um_pci_devices[free].dev = dev;
68262306a36Sopenharmony_ci	vdev->priv = dev;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	mutex_unlock(&um_pci_mtx);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	device_set_wakeup_enable(&vdev->dev, true);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/*
68962306a36Sopenharmony_ci	 * In order to do suspend-resume properly, don't allow VQs
69062306a36Sopenharmony_ci	 * to be suspended.
69162306a36Sopenharmony_ci	 */
69262306a36Sopenharmony_ci	virtio_uml_set_no_vq_suspend(vdev, true);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	um_pci_rescan();
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_cierr_reset:
69762306a36Sopenharmony_ci	virtio_reset_device(vdev);
69862306a36Sopenharmony_ci	vdev->config->del_vqs(vdev);
69962306a36Sopenharmony_cierror:
70062306a36Sopenharmony_ci	mutex_unlock(&um_pci_mtx);
70162306a36Sopenharmony_ci	kfree(dev);
70262306a36Sopenharmony_ci	return err;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic void um_pci_virtio_remove(struct virtio_device *vdev)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	struct um_pci_device *dev = vdev->priv;
70862306a36Sopenharmony_ci	int i;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (dev->platform) {
71162306a36Sopenharmony_ci		of_platform_depopulate(&vdev->dev);
71262306a36Sopenharmony_ci		__um_pci_virtio_platform_remove(vdev, dev);
71362306a36Sopenharmony_ci		return;
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	device_set_wakeup_enable(&vdev->dev, false);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	mutex_lock(&um_pci_mtx);
71962306a36Sopenharmony_ci	for (i = 0; i < MAX_DEVICES; i++) {
72062306a36Sopenharmony_ci		if (um_pci_devices[i].dev != dev)
72162306a36Sopenharmony_ci			continue;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		um_pci_devices[i].dev = NULL;
72462306a36Sopenharmony_ci		irq_free_desc(dev->irq);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		break;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci	mutex_unlock(&um_pci_mtx);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (i < MAX_DEVICES) {
73162306a36Sopenharmony_ci		struct pci_dev *pci_dev;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		pci_dev = pci_get_slot(bridge->bus, i);
73462306a36Sopenharmony_ci		if (pci_dev)
73562306a36Sopenharmony_ci			pci_stop_and_remove_bus_device_locked(pci_dev);
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	/* Stop all virtqueues */
73962306a36Sopenharmony_ci	virtio_reset_device(vdev);
74062306a36Sopenharmony_ci	dev->cmd_vq = NULL;
74162306a36Sopenharmony_ci	dev->irq_vq = NULL;
74262306a36Sopenharmony_ci	vdev->config->del_vqs(vdev);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	kfree(dev);
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic struct virtio_device_id id_table[] = {
74862306a36Sopenharmony_ci	{ CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID, VIRTIO_DEV_ANY_ID },
74962306a36Sopenharmony_ci	{ 0 },
75062306a36Sopenharmony_ci};
75162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic struct virtio_driver um_pci_virtio_driver = {
75462306a36Sopenharmony_ci	.driver.name = "virtio-pci",
75562306a36Sopenharmony_ci	.driver.owner = THIS_MODULE,
75662306a36Sopenharmony_ci	.id_table = id_table,
75762306a36Sopenharmony_ci	.probe = um_pci_virtio_probe,
75862306a36Sopenharmony_ci	.remove = um_pci_virtio_remove,
75962306a36Sopenharmony_ci};
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic struct resource virt_cfgspace_resource = {
76262306a36Sopenharmony_ci	.name = "PCI config space",
76362306a36Sopenharmony_ci	.start = 0xf0000000 - MAX_DEVICES * CFG_SPACE_SIZE,
76462306a36Sopenharmony_ci	.end = 0xf0000000 - 1,
76562306a36Sopenharmony_ci	.flags = IORESOURCE_MEM,
76662306a36Sopenharmony_ci};
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic long um_pci_map_cfgspace(unsigned long offset, size_t size,
76962306a36Sopenharmony_ci				const struct logic_iomem_ops **ops,
77062306a36Sopenharmony_ci				void **priv)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	if (WARN_ON(size > CFG_SPACE_SIZE || offset % CFG_SPACE_SIZE))
77362306a36Sopenharmony_ci		return -EINVAL;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	if (offset / CFG_SPACE_SIZE < MAX_DEVICES) {
77662306a36Sopenharmony_ci		*ops = &um_pci_device_cfgspace_ops;
77762306a36Sopenharmony_ci		*priv = &um_pci_devices[offset / CFG_SPACE_SIZE];
77862306a36Sopenharmony_ci		return 0;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	WARN(1, "cannot map offset 0x%lx/0x%zx\n", offset, size);
78262306a36Sopenharmony_ci	return -ENOENT;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic const struct logic_iomem_region_ops um_pci_cfgspace_ops = {
78662306a36Sopenharmony_ci	.map = um_pci_map_cfgspace,
78762306a36Sopenharmony_ci};
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic struct resource virt_iomem_resource = {
79062306a36Sopenharmony_ci	.name = "PCI iomem",
79162306a36Sopenharmony_ci	.start = 0xf0000000,
79262306a36Sopenharmony_ci	.end = 0xffffffff,
79362306a36Sopenharmony_ci	.flags = IORESOURCE_MEM,
79462306a36Sopenharmony_ci};
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistruct um_pci_map_iomem_data {
79762306a36Sopenharmony_ci	unsigned long offset;
79862306a36Sopenharmony_ci	size_t size;
79962306a36Sopenharmony_ci	const struct logic_iomem_ops **ops;
80062306a36Sopenharmony_ci	void **priv;
80162306a36Sopenharmony_ci	long ret;
80262306a36Sopenharmony_ci};
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int um_pci_map_iomem_walk(struct pci_dev *pdev, void *_data)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct um_pci_map_iomem_data *data = _data;
80762306a36Sopenharmony_ci	struct um_pci_device_reg *reg = &um_pci_devices[pdev->devfn / 8];
80862306a36Sopenharmony_ci	struct um_pci_device *dev;
80962306a36Sopenharmony_ci	int i;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (!reg->dev)
81262306a36Sopenharmony_ci		return 0;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(dev->resptr); i++) {
81562306a36Sopenharmony_ci		struct resource *r = &pdev->resource[i];
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		if ((r->flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM)
81862306a36Sopenharmony_ci			continue;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		/*
82162306a36Sopenharmony_ci		 * must be the whole or part of the resource,
82262306a36Sopenharmony_ci		 * not allowed to only overlap
82362306a36Sopenharmony_ci		 */
82462306a36Sopenharmony_ci		if (data->offset < r->start || data->offset > r->end)
82562306a36Sopenharmony_ci			continue;
82662306a36Sopenharmony_ci		if (data->offset + data->size - 1 > r->end)
82762306a36Sopenharmony_ci			continue;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		dev = reg->dev;
83062306a36Sopenharmony_ci		*data->ops = &um_pci_device_bar_ops;
83162306a36Sopenharmony_ci		dev->resptr[i] = i;
83262306a36Sopenharmony_ci		*data->priv = &dev->resptr[i];
83362306a36Sopenharmony_ci		data->ret = data->offset - r->start;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		/* no need to continue */
83662306a36Sopenharmony_ci		return 1;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return 0;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic long um_pci_map_iomem(unsigned long offset, size_t size,
84362306a36Sopenharmony_ci			     const struct logic_iomem_ops **ops,
84462306a36Sopenharmony_ci			     void **priv)
84562306a36Sopenharmony_ci{
84662306a36Sopenharmony_ci	struct um_pci_map_iomem_data data = {
84762306a36Sopenharmony_ci		/* we want the full address here */
84862306a36Sopenharmony_ci		.offset = offset + virt_iomem_resource.start,
84962306a36Sopenharmony_ci		.size = size,
85062306a36Sopenharmony_ci		.ops = ops,
85162306a36Sopenharmony_ci		.priv = priv,
85262306a36Sopenharmony_ci		.ret = -ENOENT,
85362306a36Sopenharmony_ci	};
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	pci_walk_bus(bridge->bus, um_pci_map_iomem_walk, &data);
85662306a36Sopenharmony_ci	return data.ret;
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic const struct logic_iomem_region_ops um_pci_iomem_ops = {
86062306a36Sopenharmony_ci	.map = um_pci_map_iomem,
86162306a36Sopenharmony_ci};
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic void um_pci_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	/*
86662306a36Sopenharmony_ci	 * This is a very low address and not actually valid 'physical' memory
86762306a36Sopenharmony_ci	 * in UML, so we can simply map MSI(-X) vectors to there, it cannot be
86862306a36Sopenharmony_ci	 * legitimately written to by the device in any other way.
86962306a36Sopenharmony_ci	 * We use the (virtual) IRQ number here as the message to simplify the
87062306a36Sopenharmony_ci	 * code that receives the message, where for now we simply trust the
87162306a36Sopenharmony_ci	 * device to send the correct message.
87262306a36Sopenharmony_ci	 */
87362306a36Sopenharmony_ci	msg->address_hi = 0;
87462306a36Sopenharmony_ci	msg->address_lo = 0xa0000;
87562306a36Sopenharmony_ci	msg->data = data->irq;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic struct irq_chip um_pci_msi_bottom_irq_chip = {
87962306a36Sopenharmony_ci	.name = "UM virtio MSI",
88062306a36Sopenharmony_ci	.irq_compose_msi_msg = um_pci_compose_msi_msg,
88162306a36Sopenharmony_ci};
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic int um_pci_inner_domain_alloc(struct irq_domain *domain,
88462306a36Sopenharmony_ci				     unsigned int virq, unsigned int nr_irqs,
88562306a36Sopenharmony_ci				     void *args)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	unsigned long bit;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	WARN_ON(nr_irqs != 1);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	mutex_lock(&um_pci_mtx);
89262306a36Sopenharmony_ci	bit = find_first_zero_bit(um_pci_msi_used, MAX_MSI_VECTORS);
89362306a36Sopenharmony_ci	if (bit >= MAX_MSI_VECTORS) {
89462306a36Sopenharmony_ci		mutex_unlock(&um_pci_mtx);
89562306a36Sopenharmony_ci		return -ENOSPC;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	set_bit(bit, um_pci_msi_used);
89962306a36Sopenharmony_ci	mutex_unlock(&um_pci_mtx);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	irq_domain_set_info(domain, virq, bit, &um_pci_msi_bottom_irq_chip,
90262306a36Sopenharmony_ci			    domain->host_data, handle_simple_irq,
90362306a36Sopenharmony_ci			    NULL, NULL);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	return 0;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic void um_pci_inner_domain_free(struct irq_domain *domain,
90962306a36Sopenharmony_ci				     unsigned int virq, unsigned int nr_irqs)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	mutex_lock(&um_pci_mtx);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (!test_bit(d->hwirq, um_pci_msi_used))
91662306a36Sopenharmony_ci		pr_err("trying to free unused MSI#%lu\n", d->hwirq);
91762306a36Sopenharmony_ci	else
91862306a36Sopenharmony_ci		__clear_bit(d->hwirq, um_pci_msi_used);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	mutex_unlock(&um_pci_mtx);
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic const struct irq_domain_ops um_pci_inner_domain_ops = {
92462306a36Sopenharmony_ci	.alloc = um_pci_inner_domain_alloc,
92562306a36Sopenharmony_ci	.free = um_pci_inner_domain_free,
92662306a36Sopenharmony_ci};
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cistatic struct irq_chip um_pci_msi_irq_chip = {
92962306a36Sopenharmony_ci	.name = "UM virtio PCIe MSI",
93062306a36Sopenharmony_ci	.irq_mask = pci_msi_mask_irq,
93162306a36Sopenharmony_ci	.irq_unmask = pci_msi_unmask_irq,
93262306a36Sopenharmony_ci};
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic struct msi_domain_info um_pci_msi_domain_info = {
93562306a36Sopenharmony_ci	.flags	= MSI_FLAG_USE_DEF_DOM_OPS |
93662306a36Sopenharmony_ci		  MSI_FLAG_USE_DEF_CHIP_OPS |
93762306a36Sopenharmony_ci		  MSI_FLAG_PCI_MSIX,
93862306a36Sopenharmony_ci	.chip	= &um_pci_msi_irq_chip,
93962306a36Sopenharmony_ci};
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic struct resource busn_resource = {
94262306a36Sopenharmony_ci	.name	= "PCI busn",
94362306a36Sopenharmony_ci	.start	= 0,
94462306a36Sopenharmony_ci	.end	= 0,
94562306a36Sopenharmony_ci	.flags	= IORESOURCE_BUS,
94662306a36Sopenharmony_ci};
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic int um_pci_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	struct um_pci_device_reg *reg = &um_pci_devices[pdev->devfn / 8];
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	if (WARN_ON(!reg->dev))
95362306a36Sopenharmony_ci		return -EINVAL;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* Yes, we map all pins to the same IRQ ... doesn't matter for now. */
95662306a36Sopenharmony_ci	return reg->dev->irq;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_civoid *pci_root_bus_fwnode(struct pci_bus *bus)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	return um_pci_fwnode;
96262306a36Sopenharmony_ci}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_cistatic long um_pci_map_platform(unsigned long offset, size_t size,
96562306a36Sopenharmony_ci				const struct logic_iomem_ops **ops,
96662306a36Sopenharmony_ci				void **priv)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	if (!um_pci_platform_device)
96962306a36Sopenharmony_ci		return -ENOENT;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	*ops = &um_pci_device_bar_ops;
97262306a36Sopenharmony_ci	*priv = &um_pci_platform_device->resptr[0];
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	return offset;
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cistatic const struct logic_iomem_region_ops um_pci_platform_ops = {
97862306a36Sopenharmony_ci	.map = um_pci_map_platform,
97962306a36Sopenharmony_ci};
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistatic struct resource virt_platform_resource = {
98262306a36Sopenharmony_ci	.name = "platform",
98362306a36Sopenharmony_ci	.start = 0x10000000,
98462306a36Sopenharmony_ci	.end = 0x1fffffff,
98562306a36Sopenharmony_ci	.flags = IORESOURCE_MEM,
98662306a36Sopenharmony_ci};
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int __init um_pci_init(void)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	int err, i;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	WARN_ON(logic_iomem_add_region(&virt_cfgspace_resource,
99362306a36Sopenharmony_ci				       &um_pci_cfgspace_ops));
99462306a36Sopenharmony_ci	WARN_ON(logic_iomem_add_region(&virt_iomem_resource,
99562306a36Sopenharmony_ci				       &um_pci_iomem_ops));
99662306a36Sopenharmony_ci	WARN_ON(logic_iomem_add_region(&virt_platform_resource,
99762306a36Sopenharmony_ci				       &um_pci_platform_ops));
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (WARN(CONFIG_UML_PCI_OVER_VIRTIO_DEVICE_ID < 0,
100062306a36Sopenharmony_ci		 "No virtio device ID configured for PCI - no PCI support\n"))
100162306a36Sopenharmony_ci		return 0;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	um_pci_msg_bufs = alloc_percpu(struct um_pci_message_buffer);
100462306a36Sopenharmony_ci	if (!um_pci_msg_bufs)
100562306a36Sopenharmony_ci		return -ENOMEM;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	bridge = pci_alloc_host_bridge(0);
100862306a36Sopenharmony_ci	if (!bridge) {
100962306a36Sopenharmony_ci		err = -ENOMEM;
101062306a36Sopenharmony_ci		goto free;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	um_pci_fwnode = irq_domain_alloc_named_fwnode("um-pci");
101462306a36Sopenharmony_ci	if (!um_pci_fwnode) {
101562306a36Sopenharmony_ci		err = -ENOMEM;
101662306a36Sopenharmony_ci		goto free;
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	um_pci_inner_domain = __irq_domain_add(um_pci_fwnode, MAX_MSI_VECTORS,
102062306a36Sopenharmony_ci					       MAX_MSI_VECTORS, 0,
102162306a36Sopenharmony_ci					       &um_pci_inner_domain_ops, NULL);
102262306a36Sopenharmony_ci	if (!um_pci_inner_domain) {
102362306a36Sopenharmony_ci		err = -ENOMEM;
102462306a36Sopenharmony_ci		goto free;
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	um_pci_msi_domain = pci_msi_create_irq_domain(um_pci_fwnode,
102862306a36Sopenharmony_ci						      &um_pci_msi_domain_info,
102962306a36Sopenharmony_ci						      um_pci_inner_domain);
103062306a36Sopenharmony_ci	if (!um_pci_msi_domain) {
103162306a36Sopenharmony_ci		err = -ENOMEM;
103262306a36Sopenharmony_ci		goto free;
103362306a36Sopenharmony_ci	}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	pci_add_resource(&bridge->windows, &virt_iomem_resource);
103662306a36Sopenharmony_ci	pci_add_resource(&bridge->windows, &busn_resource);
103762306a36Sopenharmony_ci	bridge->ops = &um_pci_ops;
103862306a36Sopenharmony_ci	bridge->map_irq = um_pci_map_irq;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	for (i = 0; i < MAX_DEVICES; i++) {
104162306a36Sopenharmony_ci		resource_size_t start;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci		start = virt_cfgspace_resource.start + i * CFG_SPACE_SIZE;
104462306a36Sopenharmony_ci		um_pci_devices[i].iomem = ioremap(start, CFG_SPACE_SIZE);
104562306a36Sopenharmony_ci		if (WARN(!um_pci_devices[i].iomem, "failed to map %d\n", i)) {
104662306a36Sopenharmony_ci			err = -ENOMEM;
104762306a36Sopenharmony_ci			goto free;
104862306a36Sopenharmony_ci		}
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	err = pci_host_probe(bridge);
105262306a36Sopenharmony_ci	if (err)
105362306a36Sopenharmony_ci		goto free;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	err = register_virtio_driver(&um_pci_virtio_driver);
105662306a36Sopenharmony_ci	if (err)
105762306a36Sopenharmony_ci		goto free;
105862306a36Sopenharmony_ci	return 0;
105962306a36Sopenharmony_cifree:
106062306a36Sopenharmony_ci	if (um_pci_inner_domain)
106162306a36Sopenharmony_ci		irq_domain_remove(um_pci_inner_domain);
106262306a36Sopenharmony_ci	if (um_pci_fwnode)
106362306a36Sopenharmony_ci		irq_domain_free_fwnode(um_pci_fwnode);
106462306a36Sopenharmony_ci	if (bridge) {
106562306a36Sopenharmony_ci		pci_free_resource_list(&bridge->windows);
106662306a36Sopenharmony_ci		pci_free_host_bridge(bridge);
106762306a36Sopenharmony_ci	}
106862306a36Sopenharmony_ci	free_percpu(um_pci_msg_bufs);
106962306a36Sopenharmony_ci	return err;
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_cimodule_init(um_pci_init);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic void __exit um_pci_exit(void)
107462306a36Sopenharmony_ci{
107562306a36Sopenharmony_ci	unregister_virtio_driver(&um_pci_virtio_driver);
107662306a36Sopenharmony_ci	irq_domain_remove(um_pci_msi_domain);
107762306a36Sopenharmony_ci	irq_domain_remove(um_pci_inner_domain);
107862306a36Sopenharmony_ci	pci_free_resource_list(&bridge->windows);
107962306a36Sopenharmony_ci	pci_free_host_bridge(bridge);
108062306a36Sopenharmony_ci	free_percpu(um_pci_msg_bufs);
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_cimodule_exit(um_pci_exit);
1083