18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Intel IFC VF NIC driver for virtio dataplane offloading
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Intel Corporation.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Zhu Lingshan <lingshan.zhu@intel.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "ifcvf_base.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic inline u8 ifc_ioread8(u8 __iomem *addr)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	return ioread8(addr);
168c2ecf20Sopenharmony_ci}
178c2ecf20Sopenharmony_cistatic inline u16 ifc_ioread16 (__le16 __iomem *addr)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	return ioread16(addr);
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic inline u32 ifc_ioread32(__le32 __iomem *addr)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	return ioread32(addr);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic inline void ifc_iowrite8(u8 value, u8 __iomem *addr)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	iowrite8(value, addr);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic inline void ifc_iowrite16(u16 value, __le16 __iomem *addr)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	iowrite16(value, addr);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic inline void ifc_iowrite32(u32 value, __le32 __iomem *addr)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	iowrite32(value, addr);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void ifc_iowrite64_twopart(u64 val,
438c2ecf20Sopenharmony_ci				  __le32 __iomem *lo, __le32 __iomem *hi)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	ifc_iowrite32((u32)val, lo);
468c2ecf20Sopenharmony_ci	ifc_iowrite32(val >> 32, hi);
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct ifcvf_adapter *vf_to_adapter(struct ifcvf_hw *hw)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return container_of(hw, struct ifcvf_adapter, vf);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void __iomem *get_cap_addr(struct ifcvf_hw *hw,
558c2ecf20Sopenharmony_ci				  struct virtio_pci_cap *cap)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct ifcvf_adapter *ifcvf;
588c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
598c2ecf20Sopenharmony_ci	u32 length, offset;
608c2ecf20Sopenharmony_ci	u8 bar;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	length = le32_to_cpu(cap->length);
638c2ecf20Sopenharmony_ci	offset = le32_to_cpu(cap->offset);
648c2ecf20Sopenharmony_ci	bar = cap->bar;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	ifcvf= vf_to_adapter(hw);
678c2ecf20Sopenharmony_ci	pdev = ifcvf->pdev;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (bar >= IFCVF_PCI_MAX_RESOURCE) {
708c2ecf20Sopenharmony_ci		IFCVF_DBG(pdev,
718c2ecf20Sopenharmony_ci			  "Invalid bar number %u to get capabilities\n", bar);
728c2ecf20Sopenharmony_ci		return NULL;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (offset + length > pci_resource_len(pdev, bar)) {
768c2ecf20Sopenharmony_ci		IFCVF_DBG(pdev,
778c2ecf20Sopenharmony_ci			  "offset(%u) + len(%u) overflows bar%u's capability\n",
788c2ecf20Sopenharmony_ci			  offset, length, bar);
798c2ecf20Sopenharmony_ci		return NULL;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return hw->base[bar] + offset;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int ifcvf_read_config_range(struct pci_dev *dev,
868c2ecf20Sopenharmony_ci				   uint32_t *val, int size, int where)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int ret, i;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	for (i = 0; i < size; i += 4) {
918c2ecf20Sopenharmony_ci		ret = pci_read_config_dword(dev, where + i, val + i / 4);
928c2ecf20Sopenharmony_ci		if (ret < 0)
938c2ecf20Sopenharmony_ci			return ret;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciint ifcvf_init_hw(struct ifcvf_hw *hw, struct pci_dev *pdev)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct virtio_pci_cap cap;
1028c2ecf20Sopenharmony_ci	u16 notify_off;
1038c2ecf20Sopenharmony_ci	int ret;
1048c2ecf20Sopenharmony_ci	u8 pos;
1058c2ecf20Sopenharmony_ci	u32 i;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	ret = pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pos);
1088c2ecf20Sopenharmony_ci	if (ret < 0) {
1098c2ecf20Sopenharmony_ci		IFCVF_ERR(pdev, "Failed to read PCI capability list\n");
1108c2ecf20Sopenharmony_ci		return -EIO;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	while (pos) {
1148c2ecf20Sopenharmony_ci		ret = ifcvf_read_config_range(pdev, (u32 *)&cap,
1158c2ecf20Sopenharmony_ci					      sizeof(cap), pos);
1168c2ecf20Sopenharmony_ci		if (ret < 0) {
1178c2ecf20Sopenharmony_ci			IFCVF_ERR(pdev,
1188c2ecf20Sopenharmony_ci				  "Failed to get PCI capability at %x\n", pos);
1198c2ecf20Sopenharmony_ci			break;
1208c2ecf20Sopenharmony_ci		}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		if (cap.cap_vndr != PCI_CAP_ID_VNDR)
1238c2ecf20Sopenharmony_ci			goto next;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		switch (cap.cfg_type) {
1268c2ecf20Sopenharmony_ci		case VIRTIO_PCI_CAP_COMMON_CFG:
1278c2ecf20Sopenharmony_ci			hw->common_cfg = get_cap_addr(hw, &cap);
1288c2ecf20Sopenharmony_ci			IFCVF_DBG(pdev, "hw->common_cfg = %p\n",
1298c2ecf20Sopenharmony_ci				  hw->common_cfg);
1308c2ecf20Sopenharmony_ci			break;
1318c2ecf20Sopenharmony_ci		case VIRTIO_PCI_CAP_NOTIFY_CFG:
1328c2ecf20Sopenharmony_ci			pci_read_config_dword(pdev, pos + sizeof(cap),
1338c2ecf20Sopenharmony_ci					      &hw->notify_off_multiplier);
1348c2ecf20Sopenharmony_ci			hw->notify_bar = cap.bar;
1358c2ecf20Sopenharmony_ci			hw->notify_base = get_cap_addr(hw, &cap);
1368c2ecf20Sopenharmony_ci			IFCVF_DBG(pdev, "hw->notify_base = %p\n",
1378c2ecf20Sopenharmony_ci				  hw->notify_base);
1388c2ecf20Sopenharmony_ci			break;
1398c2ecf20Sopenharmony_ci		case VIRTIO_PCI_CAP_ISR_CFG:
1408c2ecf20Sopenharmony_ci			hw->isr = get_cap_addr(hw, &cap);
1418c2ecf20Sopenharmony_ci			IFCVF_DBG(pdev, "hw->isr = %p\n", hw->isr);
1428c2ecf20Sopenharmony_ci			break;
1438c2ecf20Sopenharmony_ci		case VIRTIO_PCI_CAP_DEVICE_CFG:
1448c2ecf20Sopenharmony_ci			hw->net_cfg = get_cap_addr(hw, &cap);
1458c2ecf20Sopenharmony_ci			IFCVF_DBG(pdev, "hw->net_cfg = %p\n", hw->net_cfg);
1468c2ecf20Sopenharmony_ci			break;
1478c2ecf20Sopenharmony_ci		}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cinext:
1508c2ecf20Sopenharmony_ci		pos = cap.cap_next;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (hw->common_cfg == NULL || hw->notify_base == NULL ||
1548c2ecf20Sopenharmony_ci	    hw->isr == NULL || hw->net_cfg == NULL) {
1558c2ecf20Sopenharmony_ci		IFCVF_ERR(pdev, "Incomplete PCI capabilities\n");
1568c2ecf20Sopenharmony_ci		return -EIO;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	for (i = 0; i < IFCVF_MAX_QUEUE_PAIRS * 2; i++) {
1608c2ecf20Sopenharmony_ci		ifc_iowrite16(i, &hw->common_cfg->queue_select);
1618c2ecf20Sopenharmony_ci		notify_off = ifc_ioread16(&hw->common_cfg->queue_notify_off);
1628c2ecf20Sopenharmony_ci		hw->vring[i].notify_addr = hw->notify_base +
1638c2ecf20Sopenharmony_ci			notify_off * hw->notify_off_multiplier;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	hw->lm_cfg = hw->base[IFCVF_LM_BAR];
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	IFCVF_DBG(pdev,
1698c2ecf20Sopenharmony_ci		  "PCI capability mapping: common cfg: %p, notify base: %p\n, isr cfg: %p, device cfg: %p, multiplier: %u\n",
1708c2ecf20Sopenharmony_ci		  hw->common_cfg, hw->notify_base, hw->isr,
1718c2ecf20Sopenharmony_ci		  hw->net_cfg, hw->notify_off_multiplier);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ciu8 ifcvf_get_status(struct ifcvf_hw *hw)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	return ifc_ioread8(&hw->common_cfg->device_status);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_civoid ifcvf_set_status(struct ifcvf_hw *hw, u8 status)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	ifc_iowrite8(status, &hw->common_cfg->device_status);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_civoid ifcvf_reset(struct ifcvf_hw *hw)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	hw->config_cb.callback = NULL;
1898c2ecf20Sopenharmony_ci	hw->config_cb.private = NULL;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ifcvf_set_status(hw, 0);
1928c2ecf20Sopenharmony_ci	/* flush set_status, make sure VF is stopped, reset */
1938c2ecf20Sopenharmony_ci	ifcvf_get_status(hw);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic void ifcvf_add_status(struct ifcvf_hw *hw, u8 status)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	if (status != 0)
1998c2ecf20Sopenharmony_ci		status |= ifcvf_get_status(hw);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	ifcvf_set_status(hw, status);
2028c2ecf20Sopenharmony_ci	ifcvf_get_status(hw);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ciu64 ifcvf_get_features(struct ifcvf_hw *hw)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct virtio_pci_common_cfg __iomem *cfg = hw->common_cfg;
2088c2ecf20Sopenharmony_ci	u32 features_lo, features_hi;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	ifc_iowrite32(0, &cfg->device_feature_select);
2118c2ecf20Sopenharmony_ci	features_lo = ifc_ioread32(&cfg->device_feature);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	ifc_iowrite32(1, &cfg->device_feature_select);
2148c2ecf20Sopenharmony_ci	features_hi = ifc_ioread32(&cfg->device_feature);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return ((u64)features_hi << 32) | features_lo;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_civoid ifcvf_read_net_config(struct ifcvf_hw *hw, u64 offset,
2208c2ecf20Sopenharmony_ci			   void *dst, int length)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	u8 old_gen, new_gen, *p;
2238c2ecf20Sopenharmony_ci	int i;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	WARN_ON(offset + length > sizeof(struct virtio_net_config));
2268c2ecf20Sopenharmony_ci	do {
2278c2ecf20Sopenharmony_ci		old_gen = ifc_ioread8(&hw->common_cfg->config_generation);
2288c2ecf20Sopenharmony_ci		p = dst;
2298c2ecf20Sopenharmony_ci		for (i = 0; i < length; i++)
2308c2ecf20Sopenharmony_ci			*p++ = ifc_ioread8(hw->net_cfg + offset + i);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		new_gen = ifc_ioread8(&hw->common_cfg->config_generation);
2338c2ecf20Sopenharmony_ci	} while (old_gen != new_gen);
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_civoid ifcvf_write_net_config(struct ifcvf_hw *hw, u64 offset,
2378c2ecf20Sopenharmony_ci			    const void *src, int length)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	const u8 *p;
2408c2ecf20Sopenharmony_ci	int i;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	p = src;
2438c2ecf20Sopenharmony_ci	WARN_ON(offset + length > sizeof(struct virtio_net_config));
2448c2ecf20Sopenharmony_ci	for (i = 0; i < length; i++)
2458c2ecf20Sopenharmony_ci		ifc_iowrite8(*p++, hw->net_cfg + offset + i);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void ifcvf_set_features(struct ifcvf_hw *hw, u64 features)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct virtio_pci_common_cfg __iomem *cfg = hw->common_cfg;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	ifc_iowrite32(0, &cfg->guest_feature_select);
2538c2ecf20Sopenharmony_ci	ifc_iowrite32((u32)features, &cfg->guest_feature);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	ifc_iowrite32(1, &cfg->guest_feature_select);
2568c2ecf20Sopenharmony_ci	ifc_iowrite32(features >> 32, &cfg->guest_feature);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int ifcvf_config_features(struct ifcvf_hw *hw)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct ifcvf_adapter *ifcvf;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	ifcvf = vf_to_adapter(hw);
2648c2ecf20Sopenharmony_ci	ifcvf_set_features(hw, hw->req_features);
2658c2ecf20Sopenharmony_ci	ifcvf_add_status(hw, VIRTIO_CONFIG_S_FEATURES_OK);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (!(ifcvf_get_status(hw) & VIRTIO_CONFIG_S_FEATURES_OK)) {
2688c2ecf20Sopenharmony_ci		IFCVF_ERR(ifcvf->pdev, "Failed to set FEATURES_OK status\n");
2698c2ecf20Sopenharmony_ci		return -EIO;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ciu16 ifcvf_get_vq_state(struct ifcvf_hw *hw, u16 qid)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct ifcvf_lm_cfg __iomem *ifcvf_lm;
2788c2ecf20Sopenharmony_ci	void __iomem *avail_idx_addr;
2798c2ecf20Sopenharmony_ci	u16 last_avail_idx;
2808c2ecf20Sopenharmony_ci	u32 q_pair_id;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	ifcvf_lm = (struct ifcvf_lm_cfg __iomem *)hw->lm_cfg;
2838c2ecf20Sopenharmony_ci	q_pair_id = qid / (IFCVF_MAX_QUEUE_PAIRS * 2);
2848c2ecf20Sopenharmony_ci	avail_idx_addr = &ifcvf_lm->vring_lm_cfg[q_pair_id].idx_addr[qid % 2];
2858c2ecf20Sopenharmony_ci	last_avail_idx = ifc_ioread16(avail_idx_addr);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return last_avail_idx;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ciint ifcvf_set_vq_state(struct ifcvf_hw *hw, u16 qid, u16 num)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct ifcvf_lm_cfg __iomem *ifcvf_lm;
2938c2ecf20Sopenharmony_ci	void __iomem *avail_idx_addr;
2948c2ecf20Sopenharmony_ci	u32 q_pair_id;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ifcvf_lm = (struct ifcvf_lm_cfg __iomem *)hw->lm_cfg;
2978c2ecf20Sopenharmony_ci	q_pair_id = qid / (IFCVF_MAX_QUEUE_PAIRS * 2);
2988c2ecf20Sopenharmony_ci	avail_idx_addr = &ifcvf_lm->vring_lm_cfg[q_pair_id].idx_addr[qid % 2];
2998c2ecf20Sopenharmony_ci	hw->vring[qid].last_avail_idx = num;
3008c2ecf20Sopenharmony_ci	ifc_iowrite16(num, avail_idx_addr);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	return 0;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic int ifcvf_hw_enable(struct ifcvf_hw *hw)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct virtio_pci_common_cfg __iomem *cfg;
3088c2ecf20Sopenharmony_ci	struct ifcvf_adapter *ifcvf;
3098c2ecf20Sopenharmony_ci	u32 i;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	ifcvf = vf_to_adapter(hw);
3128c2ecf20Sopenharmony_ci	cfg = hw->common_cfg;
3138c2ecf20Sopenharmony_ci	ifc_iowrite16(IFCVF_MSI_CONFIG_OFF, &cfg->msix_config);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (ifc_ioread16(&cfg->msix_config) == VIRTIO_MSI_NO_VECTOR) {
3168c2ecf20Sopenharmony_ci		IFCVF_ERR(ifcvf->pdev, "No msix vector for device config\n");
3178c2ecf20Sopenharmony_ci		return -EINVAL;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	for (i = 0; i < hw->nr_vring; i++) {
3218c2ecf20Sopenharmony_ci		if (!hw->vring[i].ready)
3228c2ecf20Sopenharmony_ci			break;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		ifc_iowrite16(i, &cfg->queue_select);
3258c2ecf20Sopenharmony_ci		ifc_iowrite64_twopart(hw->vring[i].desc, &cfg->queue_desc_lo,
3268c2ecf20Sopenharmony_ci				     &cfg->queue_desc_hi);
3278c2ecf20Sopenharmony_ci		ifc_iowrite64_twopart(hw->vring[i].avail, &cfg->queue_avail_lo,
3288c2ecf20Sopenharmony_ci				      &cfg->queue_avail_hi);
3298c2ecf20Sopenharmony_ci		ifc_iowrite64_twopart(hw->vring[i].used, &cfg->queue_used_lo,
3308c2ecf20Sopenharmony_ci				     &cfg->queue_used_hi);
3318c2ecf20Sopenharmony_ci		ifc_iowrite16(hw->vring[i].size, &cfg->queue_size);
3328c2ecf20Sopenharmony_ci		ifc_iowrite16(i + IFCVF_MSI_QUEUE_OFF, &cfg->queue_msix_vector);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		if (ifc_ioread16(&cfg->queue_msix_vector) ==
3358c2ecf20Sopenharmony_ci		    VIRTIO_MSI_NO_VECTOR) {
3368c2ecf20Sopenharmony_ci			IFCVF_ERR(ifcvf->pdev,
3378c2ecf20Sopenharmony_ci				  "No msix vector for queue %u\n", i);
3388c2ecf20Sopenharmony_ci			return -EINVAL;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		ifcvf_set_vq_state(hw, i, hw->vring[i].last_avail_idx);
3428c2ecf20Sopenharmony_ci		ifc_iowrite16(1, &cfg->queue_enable);
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	return 0;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic void ifcvf_hw_disable(struct ifcvf_hw *hw)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct virtio_pci_common_cfg __iomem *cfg;
3518c2ecf20Sopenharmony_ci	u32 i;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	cfg = hw->common_cfg;
3548c2ecf20Sopenharmony_ci	ifc_iowrite16(VIRTIO_MSI_NO_VECTOR, &cfg->msix_config);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	for (i = 0; i < hw->nr_vring; i++) {
3578c2ecf20Sopenharmony_ci		ifc_iowrite16(i, &cfg->queue_select);
3588c2ecf20Sopenharmony_ci		ifc_iowrite16(VIRTIO_MSI_NO_VECTOR, &cfg->queue_msix_vector);
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	ifc_ioread16(&cfg->queue_msix_vector);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ciint ifcvf_start_hw(struct ifcvf_hw *hw)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	ifcvf_reset(hw);
3678c2ecf20Sopenharmony_ci	ifcvf_add_status(hw, VIRTIO_CONFIG_S_ACKNOWLEDGE);
3688c2ecf20Sopenharmony_ci	ifcvf_add_status(hw, VIRTIO_CONFIG_S_DRIVER);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (ifcvf_config_features(hw) < 0)
3718c2ecf20Sopenharmony_ci		return -EINVAL;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (ifcvf_hw_enable(hw) < 0)
3748c2ecf20Sopenharmony_ci		return -EINVAL;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	ifcvf_add_status(hw, VIRTIO_CONFIG_S_DRIVER_OK);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return 0;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_civoid ifcvf_stop_hw(struct ifcvf_hw *hw)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	ifcvf_hw_disable(hw);
3848c2ecf20Sopenharmony_ci	ifcvf_reset(hw);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_civoid ifcvf_notify_queue(struct ifcvf_hw *hw, u16 qid)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	ifc_iowrite16(qid, hw->vring[qid].notify_addr);
3908c2ecf20Sopenharmony_ci}
391