summaryrefslogtreecommitdiffstats
path: root/meta-agl-devel/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-agl-devel/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch')
-rw-r--r--meta-agl-devel/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch650
1 files changed, 650 insertions, 0 deletions
diff --git a/meta-agl-devel/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch b/meta-agl-devel/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch
new file mode 100644
index 00000000..dc10cd2f
--- /dev/null
+++ b/meta-agl-devel/meta-agl-jailhouse/recipes-kernel/linux/linux/0032-ivshmem-net-Adjust-to-reworked-version-of-ivshmem-in.patch
@@ -0,0 +1,650 @@
+From 9caa6a8cab0d7f46475990aaeb7dcc7721547ef0 Mon Sep 17 00:00:00 2001
+From: Jan Kiszka <jan.kiszka@siemens.com>
+Date: Mon, 5 Dec 2016 15:43:53 +0100
+Subject: [PATCH 32/32] ivshmem-net: Adjust to reworked version of ivshmem in
+ Jailhouse
+
+This contains the changes required to work with the new revision of
+ivshmem in Jailhouse, namely:
+
+- changed PCI vendor and device ID
+- vendor capability to communicate region location
+- new MMIO register layout
+- common interrupt control register
+- state table support, removal of rstate register
+- unidirectional shared memory regions
+- vector value has to be written to doorbell register
+- support for multiple vectors, used to split config from tx-rx
+
+Note: Specification work for the interface is ongoing, so details may
+still change.
+
+Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
+---
+ drivers/net/ivshmem-net.c | 335 ++++++++++++++++++++++++++++++----------------
+ 1 file changed, 223 insertions(+), 112 deletions(-)
+
+diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c
+index 9946cef63c1f..18d5a15dbec2 100644
+--- a/drivers/net/ivshmem-net.c
++++ b/drivers/net/ivshmem-net.c
+@@ -1,5 +1,6 @@
+ /*
+ * Copyright 2016 Mans Rullgard <mans@mansr.com>
++ * Copyright (c) Siemens AG, 2016-2020
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -15,6 +16,7 @@
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
++#include <linux/ivshmem.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
+@@ -28,34 +30,28 @@
+
+ #define DRV_NAME "ivshmem-net"
+
+-#define JAILHOUSE_CFG_SHMEM_PTR 0x40
+-#define JAILHOUSE_CFG_SHMEM_SZ 0x48
++#define IVSHM_NET_STATE_RESET 0
++#define IVSHM_NET_STATE_INIT 1
++#define IVSHM_NET_STATE_READY 2
++#define IVSHM_NET_STATE_RUN 3
+
+-#define IVSHMEM_INTX_ENABLE 0x1
++#define IVSHM_NET_FLAG_RUN 0
+
+-#define IVSHM_NET_STATE_RESET 0
+-#define IVSHM_NET_STATE_INIT 1
+-#define IVSHM_NET_STATE_READY 2
+-#define IVSHM_NET_STATE_RUN 3
+-
+-#define IVSHM_NET_FLAG_RUN 0
+-
+-#define IVSHM_NET_MTU_MIN 256
+-#define IVSHM_NET_MTU_MAX 65535
+-#define IVSHM_NET_MTU_DEF 16384
++#define IVSHM_NET_MTU_MIN 256
++#define IVSHM_NET_MTU_MAX 65535
++#define IVSHM_NET_MTU_DEF 16384
+
+ #define IVSHM_NET_FRAME_SIZE(s) ALIGN(18 + (s), SMP_CACHE_BYTES)
+
+ #define IVSHM_NET_VQ_ALIGN 64
+
+-struct ivshmem_regs {
+- u32 intxctrl;
+- u32 istat;
+- u32 ivpos;
+- u32 doorbell;
+- u32 lstate;
+- u32 rstate;
+-};
++#define IVSHM_NET_SECTION_TX 0
++#define IVSHM_NET_SECTION_RX 1
++
++#define IVSHM_NET_MSIX_STATE 0
++#define IVSHM_NET_MSIX_TX_RX 1
++
++#define IVSHM_NET_NUM_VECTORS 2
+
+ struct ivshm_net_queue {
+ struct vring vr;
+@@ -73,7 +69,7 @@ struct ivshm_net_queue {
+ };
+
+ struct ivshm_net_stats {
+- u32 interrupts;
++ u32 tx_rx_interrupts;
+ u32 tx_packets;
+ u32 tx_notify;
+ u32 tx_pause;
+@@ -97,8 +93,9 @@ struct ivshm_net {
+
+ struct napi_struct napi;
+
+- u32 lstate;
+- u32 rstate;
++ u32 state;
++ u32 last_peer_state;
++ u32 *state_table;
+
+ unsigned long flags;
+
+@@ -107,17 +104,19 @@ struct ivshm_net {
+
+ struct ivshm_net_stats stats;
+
+- struct ivshmem_regs __iomem *ivshm_regs;
+- void *shm;
+- phys_addr_t shmaddr;
++ struct ivshm_regs __iomem *ivshm_regs;
++ void *shm[2];
+ resource_size_t shmlen;
+ u32 peer_id;
+
++ u32 tx_rx_vector;
++
+ struct pci_dev *pdev;
+ };
+
+ static void *ivshm_net_desc_data(struct ivshm_net *in,
+ struct ivshm_net_queue *q,
++ unsigned int region,
+ struct vring_desc *desc,
+ u32 *len)
+ {
+@@ -132,7 +131,7 @@ static void *ivshm_net_desc_data(struct ivshm_net *in,
+ if (offs >= in->shmlen)
+ return NULL;
+
+- data = in->shm + offs;
++ data = in->shm[region] + offs;
+
+ if (data < q->data || data >= q->end)
+ return NULL;
+@@ -160,18 +159,17 @@ static void ivshm_net_init_queue(struct ivshm_net *in,
+ static void ivshm_net_init_queues(struct net_device *ndev)
+ {
+ struct ivshm_net *in = netdev_priv(ndev);
+- int ivpos = readl(&in->ivshm_regs->ivpos);
+ void *tx;
+ void *rx;
+ int i;
+
+- tx = in->shm + ivpos * in->shmlen / 2;
+- rx = in->shm + !ivpos * in->shmlen / 2;
++ tx = in->shm[IVSHM_NET_SECTION_TX];
++ rx = in->shm[IVSHM_NET_SECTION_RX];
+
+- memset(tx, 0, in->shmlen / 2);
++ memset(tx, 0, in->shmlen);
+
+- ivshm_net_init_queue(in, &in->rx, rx, in->qlen);
+ ivshm_net_init_queue(in, &in->tx, tx, in->qlen);
++ ivshm_net_init_queue(in, &in->rx, rx, in->qlen);
+
+ swap(in->rx.vr.used, in->tx.vr.used);
+
+@@ -191,14 +189,14 @@ static int ivshm_net_calc_qsize(struct net_device *ndev)
+ for (qlen = 4096; qlen > 32; qlen >>= 1) {
+ vrsize = vring_size(qlen, IVSHM_NET_VQ_ALIGN);
+ vrsize = ALIGN(vrsize, IVSHM_NET_VQ_ALIGN);
+- if (vrsize < in->shmlen / 16)
++ if (vrsize < in->shmlen / 8)
+ break;
+ }
+
+- if (vrsize > in->shmlen / 2)
++ if (vrsize > in->shmlen)
+ return -EINVAL;
+
+- qsize = in->shmlen / 2 - vrsize;
++ qsize = in->shmlen - vrsize;
+
+ if (qsize < 4 * IVSHM_NET_MTU_MIN)
+ return -EINVAL;
+@@ -221,7 +219,8 @@ static void ivshm_net_notify_tx(struct ivshm_net *in, unsigned int num)
+ new = in->tx.last_avail_idx;
+
+ if (vring_need_event(evt, new, old)) {
+- writel(in->peer_id << 16, &in->ivshm_regs->doorbell);
++ writel(in->tx_rx_vector | (in->peer_id << 16),
++ &in->ivshm_regs->doorbell);
+ in->stats.tx_notify++;
+ }
+ }
+@@ -243,7 +242,8 @@ static void ivshm_net_notify_rx(struct ivshm_net *in, unsigned int num)
+ new = in->rx.last_used_idx;
+
+ if (vring_need_event(evt, new, old)) {
+- writel(in->peer_id << 16, &in->ivshm_regs->doorbell);
++ writel(in->tx_rx_vector | (in->peer_id << 16),
++ &in->ivshm_regs->doorbell);
+ in->stats.rx_notify++;
+ }
+ }
+@@ -320,7 +320,7 @@ static int ivshm_net_tx_frame(struct net_device *ndev, struct sk_buff *skb,
+ buf = tx->data + head;
+ skb_copy_and_csum_dev(skb, buf);
+
+- desc->addr = buf - in->shm;
++ desc->addr = buf - in->shm[IVSHM_NET_SECTION_TX];
+ desc->len = skb->len;
+ desc->flags = 0;
+
+@@ -374,7 +374,8 @@ static void ivshm_net_tx_clean(struct net_device *ndev)
+
+ desc = &vr->desc[used->id];
+
+- data = ivshm_net_desc_data(in, &in->tx, desc, &len);
++ data = ivshm_net_desc_data(in, &in->tx, IVSHM_NET_SECTION_TX,
++ desc, &len);
+ if (!data) {
+ netdev_err(ndev, "bad tx descriptor, data == NULL\n");
+ break;
+@@ -466,7 +467,8 @@ static int ivshm_net_poll(struct napi_struct *napi, int budget)
+ if (!desc)
+ break;
+
+- data = ivshm_net_desc_data(in, &in->rx, desc, &len);
++ data = ivshm_net_desc_data(in, &in->rx, IVSHM_NET_SECTION_RX,
++ desc, &len);
+ if (!data) {
+ netdev_err(ndev, "bad rx descriptor\n");
+ break;
+@@ -535,15 +537,15 @@ static netdev_tx_t ivshm_net_xmit(struct sk_buff *skb, struct net_device *ndev)
+ static void ivshm_net_set_state(struct ivshm_net *in, u32 state)
+ {
+ virt_wmb();
+- WRITE_ONCE(in->lstate, state);
+- writel(state, &in->ivshm_regs->lstate);
++ WRITE_ONCE(in->state, state);
++ writel(state, &in->ivshm_regs->state);
+ }
+
+ static void ivshm_net_run(struct net_device *ndev)
+ {
+ struct ivshm_net *in = netdev_priv(ndev);
+
+- if (in->lstate < IVSHM_NET_STATE_READY)
++ if (in->state < IVSHM_NET_STATE_READY)
+ return;
+
+ if (!netif_running(ndev))
+@@ -575,15 +577,15 @@ static void ivshm_net_state_change(struct work_struct *work)
+ {
+ struct ivshm_net *in = container_of(work, struct ivshm_net, state_work);
+ struct net_device *ndev = in->napi.dev;
+- u32 rstate = readl(&in->ivshm_regs->rstate);
++ u32 peer_state = READ_ONCE(in->state_table[in->peer_id]);
+
+- switch (in->lstate) {
++ switch (in->state) {
+ case IVSHM_NET_STATE_RESET:
+ /*
+ * Wait for the remote to leave READY/RUN before transitioning
+ * to INIT.
+ */
+- if (rstate < IVSHM_NET_STATE_READY)
++ if (peer_state < IVSHM_NET_STATE_READY)
+ ivshm_net_set_state(in, IVSHM_NET_STATE_INIT);
+ break;
+
+@@ -592,7 +594,7 @@ static void ivshm_net_state_change(struct work_struct *work)
+ * Wait for the remote to leave RESET before performing the
+ * initialization and moving to READY.
+ */
+- if (rstate > IVSHM_NET_STATE_RESET) {
++ if (peer_state > IVSHM_NET_STATE_RESET) {
+ ivshm_net_init_queues(ndev);
+ ivshm_net_set_state(in, IVSHM_NET_STATE_READY);
+
+@@ -607,7 +609,7 @@ static void ivshm_net_state_change(struct work_struct *work)
+ * Link is up and we are running once the remote is in READY or
+ * RUN.
+ */
+- if (rstate >= IVSHM_NET_STATE_READY) {
++ if (peer_state >= IVSHM_NET_STATE_READY) {
+ netif_carrier_on(ndev);
+ ivshm_net_run(ndev);
+ break;
+@@ -617,7 +619,7 @@ static void ivshm_net_state_change(struct work_struct *work)
+ /*
+ * If the remote goes to RESET, we need to follow immediately.
+ */
+- if (rstate == IVSHM_NET_STATE_RESET) {
++ if (peer_state == IVSHM_NET_STATE_RESET) {
+ netif_carrier_off(ndev);
+ ivshm_net_do_stop(ndev);
+ }
+@@ -625,31 +627,44 @@ static void ivshm_net_state_change(struct work_struct *work)
+ }
+
+ virt_wmb();
+- WRITE_ONCE(in->rstate, rstate);
++ WRITE_ONCE(in->last_peer_state, peer_state);
+ }
+
+-static void ivshm_net_check_state(struct net_device *ndev)
++static void ivshm_net_check_state(struct ivshm_net *in)
+ {
+- struct ivshm_net *in = netdev_priv(ndev);
+- u32 rstate = readl(&in->ivshm_regs->rstate);
+-
+- if (rstate != in->rstate || !test_bit(IVSHM_NET_FLAG_RUN, &in->flags))
++ if (in->state_table[in->peer_id] != in->last_peer_state ||
++ !test_bit(IVSHM_NET_FLAG_RUN, &in->flags))
+ queue_work(in->state_wq, &in->state_work);
+ }
+
+-static irqreturn_t ivshm_net_int(int irq, void *data)
++static irqreturn_t ivshm_net_int_state(int irq, void *data)
+ {
+- struct net_device *ndev = data;
+- struct ivshm_net *in = netdev_priv(ndev);
++ struct ivshm_net *in = data;
++
++ ivshm_net_check_state(in);
++
++ return IRQ_HANDLED;
++}
+
+- in->stats.interrupts++;
++static irqreturn_t ivshm_net_int_tx_rx(int irq, void *data)
++{
++ struct ivshm_net *in = data;
++
++ in->stats.tx_rx_interrupts++;
+
+- ivshm_net_check_state(ndev);
+ napi_schedule_irqoff(&in->napi);
+
+ return IRQ_HANDLED;
+ }
+
++static irqreturn_t ivshm_net_intx(int irq, void *data)
++{
++ ivshm_net_int_state(irq, data);
++ ivshm_net_int_tx_rx(irq, data);
++
++ return IRQ_HANDLED;
++}
++
+ static int ivshm_net_open(struct net_device *ndev)
+ {
+ netdev_reset_queue(ndev);
+@@ -717,7 +732,7 @@ static const struct net_device_ops ivshm_net_ops = {
+ };
+
+ static const char ivshm_net_stats[][ETH_GSTRING_LEN] = {
+- "interrupts",
++ "tx_rx_interrupts",
+ "tx_packets",
+ "tx_notify",
+ "tx_pause",
+@@ -760,7 +775,7 @@ static void ivshm_net_get_ethtool_stats(struct net_device *ndev,
+ unsigned int n = 0;
+ unsigned int i;
+
+- st[n++] = in->stats.interrupts;
++ st[n++] = in->stats.tx_rx_interrupts;
+ st[n++] = in->stats.tx_packets;
+ st[n++] = in->stats.tx_notify;
+ st[n++] = in->stats.tx_pause;
+@@ -789,8 +804,8 @@ static void ivshm_net_get_regs(struct net_device *ndev,
+ u32 *reg32 = p;
+ u16 *reg16;
+
+- *reg32++ = in->lstate;
+- *reg32++ = in->rstate;
++ *reg32++ = in->state;
++ *reg32++ = in->last_peer_state;
+ *reg32++ = in->qlen;
+
+ reg16 = (u16 *)reg32;
+@@ -812,17 +827,28 @@ static const struct ethtool_ops ivshm_net_ethtool_ops = {
+ .get_regs = ivshm_net_get_regs,
+ };
+
++static u64 get_config_qword(struct pci_dev *pdev, unsigned int pos)
++{
++ u32 lo, hi;
++
++ pci_read_config_dword(pdev, pos, &lo);
++ pci_read_config_dword(pdev, pos + 4, &hi);
++ return lo | ((u64)hi << 32);
++}
++
+ static int ivshm_net_probe(struct pci_dev *pdev,
+- const struct pci_device_id *id)
++ const struct pci_device_id *pci_id)
+ {
++ phys_addr_t output_sections_addr, section_addr;
++ resource_size_t section_sz, output_section_sz;
++ void *state_table, *output_sections;
++ struct ivshm_regs __iomem *regs;
+ struct net_device *ndev;
+ struct ivshm_net *in;
+- struct ivshmem_regs __iomem *regs;
+- resource_size_t shmaddr;
+- resource_size_t shmlen;
++ unsigned int cap_pos;
+ char *device_name;
+- void *shm;
+- u32 ivpos;
++ int vendor_cap;
++ u32 id, dword;
+ int ret;
+
+ ret = pcim_enable_device(pdev);
+@@ -839,40 +865,75 @@ static int ivshm_net_probe(struct pci_dev *pdev,
+
+ regs = pcim_iomap_table(pdev)[0];
+
+- shmlen = pci_resource_len(pdev, 2);
++ id = readl(&regs->id);
++ if (id > 1) {
++ dev_err(&pdev->dev, "invalid ID %d\n", id);
++ return -EINVAL;
++ }
++ if (readl(&regs->max_peers) > 2) {
++ dev_err(&pdev->dev, "only 2 peers supported\n");
++ return -EINVAL;
++ }
++
++ vendor_cap = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
++ if (vendor_cap < 0) {
++ dev_err(&pdev->dev, "missing vendor capability\n");
++ return -EINVAL;
++ }
+
+- if (shmlen) {
+- shmaddr = pci_resource_start(pdev, 2);
++ if (pci_resource_len(pdev, 2) > 0) {
++ section_addr = pci_resource_start(pdev, 2);
+ } else {
+- union { u64 v; u32 hl[2]; } val;
+-
+- pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_PTR,
+- &val.hl[0]);
+- pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_PTR + 4,
+- &val.hl[1]);
+- shmaddr = val.v;
+-
+- pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_SZ,
+- &val.hl[0]);
+- pci_read_config_dword(pdev, JAILHOUSE_CFG_SHMEM_SZ + 4,
+- &val.hl[1]);
+- shmlen = val.v;
++ cap_pos = vendor_cap + IVSHM_CFG_ADDRESS;
++ section_addr = get_config_qword(pdev, cap_pos);
+ }
+
++ cap_pos = vendor_cap + IVSHM_CFG_STATE_TAB_SZ;
++ pci_read_config_dword(pdev, cap_pos, &dword);
++ section_sz = dword;
+
+- if (!devm_request_mem_region(&pdev->dev, shmaddr, shmlen, DRV_NAME))
++ if (!devm_request_mem_region(&pdev->dev, section_addr, section_sz,
++ DRV_NAME))
+ return -EBUSY;
+
+- shm = devm_memremap(&pdev->dev, shmaddr, shmlen, MEMREMAP_WB);
+- if (!shm)
++ state_table = devm_memremap(&pdev->dev, section_addr, section_sz,
++ MEMREMAP_WB);
++ if (!state_table)
+ return -ENOMEM;
+
+- ivpos = readl(&regs->ivpos);
+- if (ivpos > 1) {
+- dev_err(&pdev->dev, "invalid IVPosition %d\n", ivpos);
++ output_sections_addr = section_addr + section_sz;
++
++ cap_pos = vendor_cap + IVSHM_CFG_RW_SECTION_SZ;
++ section_sz = get_config_qword(pdev, cap_pos);
++ if (section_sz > 0) {
++ dev_info(&pdev->dev, "R/W section detected - "
++ "unused by this driver version\n");
++ output_sections_addr += section_sz;
++ }
++
++ cap_pos = vendor_cap + IVSHM_CFG_OUTPUT_SECTION_SZ;
++ output_section_sz = get_config_qword(pdev, cap_pos);
++ if (output_section_sz == 0) {
++ dev_err(&pdev->dev, "Missing input/output sections\n");
+ return -EINVAL;
+ }
+
++ if (!devm_request_mem_region(&pdev->dev, output_sections_addr,
++ output_section_sz * 2, DRV_NAME))
++ return -EBUSY;
++
++ output_sections = devm_memremap(&pdev->dev, output_sections_addr,
++ output_section_sz * 2, MEMREMAP_WB);
++ if (!output_sections)
++ return -ENOMEM;
++
++ section_addr = output_sections_addr + output_section_sz * id;
++ dev_info(&pdev->dev, "TX memory at %pa, size %pa\n",
++ &section_addr, &output_section_sz);
++ section_addr = output_sections_addr + output_section_sz * !id;
++ dev_info(&pdev->dev, "RX memory at %pa, size %pa\n",
++ &section_addr, &output_section_sz);
++
+ device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s[%s]", DRV_NAME,
+ dev_name(&pdev->dev));
+ if (!device_name)
+@@ -887,10 +948,16 @@ static int ivshm_net_probe(struct pci_dev *pdev,
+
+ in = netdev_priv(ndev);
+ in->ivshm_regs = regs;
+- in->shm = shm;
+- in->shmaddr = shmaddr;
+- in->shmlen = shmlen;
+- in->peer_id = !ivpos;
++ in->state_table = state_table;
++
++ in->shm[IVSHM_NET_SECTION_TX] =
++ output_sections + output_section_sz * id;
++ in->shm[IVSHM_NET_SECTION_RX] =
++ output_sections + output_section_sz * !id;
++
++ in->shmlen = output_section_sz;
++
++ in->peer_id = !id;
+ in->pdev = pdev;
+ spin_lock_init(&in->tx_free_lock);
+ spin_lock_init(&in->tx_clean_lock);
+@@ -919,24 +986,64 @@ static int ivshm_net_probe(struct pci_dev *pdev,
+ if (ret)
+ goto err_wq;
+
+- ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY | PCI_IRQ_MSIX);
++ ret = pci_alloc_irq_vectors(pdev, 1, 2, PCI_IRQ_LEGACY | PCI_IRQ_MSIX);
+ if (ret < 0)
+ goto err_alloc_irq;
+
+- ret = request_irq(pci_irq_vector(pdev, 0), ivshm_net_int, 0,
+- device_name, ndev);
+- if (ret)
+- goto err_request_irq;
++ if (pdev->msix_enabled) {
++ if (ret != 2) {
++ ret = -EBUSY;
++ goto err_request_irq;
++ }
++
++ device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
++ "%s-state[%s]", DRV_NAME,
++ dev_name(&pdev->dev));
++ if (!device_name) {
++ ret = -ENOMEM;
++ goto err_request_irq;
++ }
++
++ ret = request_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_STATE),
++ ivshm_net_int_state, 0, device_name, in);
++ if (ret)
++ goto err_request_irq;
++
++ device_name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
++ "%s-tx-rx[%s]", DRV_NAME,
++ dev_name(&pdev->dev));
++ if (!device_name) {
++ ret = -ENOMEM;
++ goto err_request_irq2;
++ }
++
++ ret = request_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_TX_RX),
++ ivshm_net_int_tx_rx, 0, device_name, in);
++ if (ret)
++ goto err_request_irq2;
++
++ in->tx_rx_vector = IVSHM_NET_MSIX_TX_RX;
++ } else {
++ ret = request_irq(pci_irq_vector(pdev, 0), ivshm_net_intx, 0,
++ device_name, in);
++ if (ret)
++ goto err_request_irq;
++
++ in->tx_rx_vector = 0;
++ }
+
+ pci_set_master(pdev);
+- if (!pdev->msix_enabled)
+- writel(IVSHMEM_INTX_ENABLE, &in->ivshm_regs->intxctrl);
+
+- writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate);
+- ivshm_net_check_state(ndev);
++ pci_write_config_byte(pdev, vendor_cap + IVSHM_CFG_PRIV_CNTL, 0);
++ writel(IVSHM_INT_ENABLE, &in->ivshm_regs->int_control);
++
++ writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->state);
++ ivshm_net_check_state(in);
+
+ return 0;
+
++err_request_irq2:
++ free_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_STATE), in);
+ err_request_irq:
+ pci_free_irq_vectors(pdev);
+ err_alloc_irq:
+@@ -954,11 +1061,15 @@ static void ivshm_net_remove(struct pci_dev *pdev)
+ struct net_device *ndev = pci_get_drvdata(pdev);
+ struct ivshm_net *in = netdev_priv(ndev);
+
+- writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->lstate);
++ writel(IVSHM_NET_STATE_RESET, &in->ivshm_regs->state);
++ writel(0, &in->ivshm_regs->int_control);
+
+- if (!pdev->msix_enabled)
+- writel(0, &in->ivshm_regs->intxctrl);
+- free_irq(pci_irq_vector(pdev, 0), ndev);
++ if (pdev->msix_enabled) {
++ free_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_STATE), in);
++ free_irq(pci_irq_vector(pdev, IVSHM_NET_MSIX_TX_RX), in);
++ } else {
++ free_irq(pci_irq_vector(pdev, 0), in);
++ }
+ pci_free_irq_vectors(pdev);
+
+ unregister_netdev(ndev);
+@@ -968,8 +1079,8 @@ static void ivshm_net_remove(struct pci_dev *pdev)
+ }
+
+ static const struct pci_device_id ivshm_net_id_table[] = {
+- { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, 0x1110),
+- (PCI_CLASS_OTHERS << 16) | (0x01 << 8), 0xffff00 },
++ { PCI_DEVICE(PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_IVSHMEM),
++ (PCI_CLASS_OTHERS << 16) | IVSHM_PROTO_NET, 0xffffff },
+ { 0 }
+ };
+ MODULE_DEVICE_TABLE(pci, ivshm_net_id_table);
+--
+2.11.0
+