xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
From: "Jan Beulich" <JBeulich@suse.com>
To: xen-devel <xen-devel@lists.xenproject.org>
Cc: Ian Campbell <Ian.Campbell@eu.citrix.com>,
	Andrew Cooper <andrew.cooper3@citrix.com>,
	Keir Fraser <keir@xen.org>,
	Ian Jackson <Ian.Jackson@eu.citrix.com>, Tim Deegan <tim@xen.org>
Subject: [PATCH RFC 4/4] ns16550: enable use of PCI MSI
Date: Tue, 23 Feb 2016 04:30:41 -0700	[thread overview]
Message-ID: <56CC50F102000078000D52DB@prv-mh.provo.novell.com> (raw)
In-Reply-To: <56CC4F0B02000078000D5290@prv-mh.provo.novell.com>

[-- Attachment #1: Type: text/plain, Size: 6900 bytes --]

Which, on x86, requires fiddling with the INTx bit in PCI config space,
since for internally used MSI we can't delegate this to Dom0.

ns16550_init_postirq() also needs (benign) re-ordering of its
operations.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
RFC reason: No interrupts occur for an unknown reason (presumably
            broken hardware/firmware on the device I have).

--- a/xen/arch/x86/msi.c
+++ b/xen/arch/x86/msi.c
@@ -738,6 +738,16 @@ static int msi_capability_init(struct pc
 
     *desc = entry;
     /* Restore the original MSI enabled bits  */
+    if ( !hardware_domain )
+    {
+        /*
+         * Except for internal requests (before Dom0 starts), in which case
+         * we rather need to behave "normally", i.e. not follow the split
+         * brain model where Dom0 actually enables MSI (and disables INTx).
+         */
+        pci_intx(dev, 0);
+        control |= PCI_MSI_FLAGS_ENABLE;
+    }
     pci_conf_write16(seg, bus, slot, func, msi_control_reg(pos), control);
 
     return 0;
@@ -1071,6 +1081,8 @@ static void __pci_disable_msi(struct msi
 
     dev = entry->dev;
     msi_set_enable(dev, 0);
+    if ( entry->irq > 0 && !(irq_to_desc(entry->irq)->status & IRQ_GUEST) )
+        pci_intx(dev, 1);
 
     BUG_ON(list_empty(&dev->msi_list));
 }
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -74,6 +74,7 @@ static struct ns16550 {
     u32 bar64;
     u16 cr;
     u8 bar_idx;
+    bool_t msi;
     const struct ns16550_config_param *param; /* Points into .init.*! */
 #endif
 } ns16550_com[2] = { { 0 } };
@@ -644,6 +645,16 @@ static void __init ns16550_init_preirq(s
         uart->fifo_size = 16;
 }
 
+static void __init ns16550_init_irq(struct serial_port *port)
+{
+#ifdef CONFIG_HAS_PCI
+    struct ns16550 *uart = port->uart;
+
+    if ( uart->msi )
+        uart->irq = create_irq(0);
+#endif
+}
+
 static void ns16550_setup_postirq(struct ns16550 *uart)
 {
     if ( uart->irq > 0 )
@@ -678,17 +689,6 @@ static void __init ns16550_init_postirq(
     uart->timeout_ms = max_t(
         unsigned int, 1, (bits * uart->fifo_size * 1000) / uart->baud);
 
-    if ( uart->irq > 0 )
-    {
-        uart->irqaction.handler = ns16550_interrupt;
-        uart->irqaction.name    = "ns16550";
-        uart->irqaction.dev_id  = port;
-        if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 )
-            printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq);
-    }
-
-    ns16550_setup_postirq(uart);
-
 #ifdef CONFIG_HAS_PCI
     if ( uart->bar || uart->ps_bdf_enable )
     {
@@ -709,8 +709,65 @@ static void __init ns16550_init_postirq(
                                     uart->ps_bdf[0], uart->ps_bdf[1],
                                     uart->ps_bdf[2]);
         }
+
+        if ( uart->msi )
+        {
+            struct msi_info msi = {
+                .bus = uart->ps_bdf[0],
+                .devfn = PCI_DEVFN(uart->ps_bdf[1], uart->ps_bdf[2]),
+                .irq = rc = uart->irq,
+                .entry_nr = 1
+            };
+
+            if ( rc > 0 )
+            {
+                struct msi_desc *msi_desc = NULL;
+
+                spin_lock(&pcidevs_lock);
+
+                rc = pci_enable_msi(&msi, &msi_desc);
+                if ( !rc )
+                {
+                    struct irq_desc *desc = irq_to_desc(msi.irq);
+                    unsigned long flags;
+
+                    spin_lock_irqsave(&desc->lock, flags);
+                    rc = setup_msi_irq(desc, msi_desc);
+                    spin_unlock_irqrestore(&desc->lock, flags);
+                    if ( rc )
+                        pci_disable_msi(msi_desc);
+                }
+
+                spin_unlock(&pcidevs_lock);
+
+                if ( rc )
+                {
+                    uart->irq = 0;
+                    if ( msi_desc )
+                        msi_free_irq(msi_desc);
+                    else
+                        destroy_irq(msi.irq);
+                }
+            }
+
+            if ( rc )
+                printk(XENLOG_WARNING
+                       "MSI setup failed (%d) for %02x:%02x.%o\n",
+                       rc, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2]);
+        }
     }
 #endif
+
+    if ( uart->irq > 0 )
+    {
+        uart->irqaction.handler = ns16550_interrupt;
+        uart->irqaction.name    = "ns16550";
+        uart->irqaction.dev_id  = port;
+        if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 )
+            printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq);
+    }
+
+    ns16550_setup_postirq(uart);
 }
 
 static void ns16550_suspend(struct serial_port *port)
@@ -820,6 +877,7 @@ static const struct vuart_info *ns16550_
 
 static struct uart_driver __read_mostly ns16550_driver = {
     .init_preirq  = ns16550_init_preirq,
+    .init_irq     = ns16550_init_irq,
     .init_postirq = ns16550_init_postirq,
     .endboot      = ns16550_endboot,
     .suspend      = ns16550_suspend,
@@ -1123,7 +1181,18 @@ static void __init ns16550_parse_port_co
     }
 
     if ( *conf == ',' && *++conf != ',' )
-        uart->irq = simple_strtol(conf, &conf, 10);
+    {
+#ifdef CONFIG_HAS_PCI
+        if ( strncmp(conf, "msi", 3) == 0 )
+        {
+            conf += 3;
+            uart->msi = 1;
+            uart->irq = 0;
+        }
+        else
+#endif
+            uart->irq = simple_strtol(conf, &conf, 10);
+    }
 
 #ifdef CONFIG_HAS_PCI
     if ( *conf == ',' && *++conf != ',' )
--- a/xen/drivers/pci/pci.c
+++ b/xen/drivers/pci/pci.c
@@ -115,6 +115,21 @@ int pci_find_next_ext_capability(int seg
     return 0;
 }
 
+void pci_intx(const struct pci_dev *pdev, bool_t enable)
+{
+    uint16_t seg = pdev->seg;
+    uint8_t bus = pdev->bus;
+    uint8_t slot = PCI_SLOT(pdev->devfn);
+    uint8_t func = PCI_FUNC(pdev->devfn);
+    uint16_t cmd = pci_conf_read16(seg, bus, slot, func, PCI_COMMAND);
+
+    if ( enable )
+        cmd &= ~PCI_COMMAND_INTX_DISABLE;
+    else
+        cmd |= PCI_COMMAND_INTX_DISABLE;
+    pci_conf_write16(seg, bus, slot, func, PCI_COMMAND, cmd);
+}
+
 const char *__init parse_pci(const char *s, unsigned int *seg_p,
                              unsigned int *bus_p, unsigned int *dev_p,
                              unsigned int *func_p)
--- a/xen/include/xen/pci.h
+++ b/xen/include/xen/pci.h
@@ -152,6 +152,7 @@ int pci_find_next_ext_capability(int seg
 const char *parse_pci(const char *, unsigned int *seg, unsigned int *bus,
                       unsigned int *dev, unsigned int *func);
 
+void pci_intx(const struct pci_dev *, bool_t enable);
 bool_t pcie_aer_get_firmware_first(const struct pci_dev *);
 
 struct pirq;



[-- Attachment #2: ns16550-MSI.patch --]
[-- Type: text/plain, Size: 6930 bytes --]

ns16550: enable use of PCI MSI

Which, on x86, requires fiddling with the INTx bit in PCI config space,
since for internally used MSI we can't delegate this to Dom0.

ns16550_init_postirq() also needs (benign) re-ordering of its
operations.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
---
RFC reason: No interrupts occur for an unknown reason (presumably
            broken hardware/firmware on the device I have).

--- a/xen/arch/x86/msi.c
+++ b/xen/arch/x86/msi.c
@@ -738,6 +738,16 @@ static int msi_capability_init(struct pc
 
     *desc = entry;
     /* Restore the original MSI enabled bits  */
+    if ( !hardware_domain )
+    {
+        /*
+         * Except for internal requests (before Dom0 starts), in which case
+         * we rather need to behave "normally", i.e. not follow the split
+         * brain model where Dom0 actually enables MSI (and disables INTx).
+         */
+        pci_intx(dev, 0);
+        control |= PCI_MSI_FLAGS_ENABLE;
+    }
     pci_conf_write16(seg, bus, slot, func, msi_control_reg(pos), control);
 
     return 0;
@@ -1071,6 +1081,8 @@ static void __pci_disable_msi(struct msi
 
     dev = entry->dev;
     msi_set_enable(dev, 0);
+    if ( entry->irq > 0 && !(irq_to_desc(entry->irq)->status & IRQ_GUEST) )
+        pci_intx(dev, 1);
 
     BUG_ON(list_empty(&dev->msi_list));
 }
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -74,6 +74,7 @@ static struct ns16550 {
     u32 bar64;
     u16 cr;
     u8 bar_idx;
+    bool_t msi;
     const struct ns16550_config_param *param; /* Points into .init.*! */
 #endif
 } ns16550_com[2] = { { 0 } };
@@ -644,6 +645,16 @@ static void __init ns16550_init_preirq(s
         uart->fifo_size = 16;
 }
 
+static void __init ns16550_init_irq(struct serial_port *port)
+{
+#ifdef CONFIG_HAS_PCI
+    struct ns16550 *uart = port->uart;
+
+    if ( uart->msi )
+        uart->irq = create_irq(0);
+#endif
+}
+
 static void ns16550_setup_postirq(struct ns16550 *uart)
 {
     if ( uart->irq > 0 )
@@ -678,17 +689,6 @@ static void __init ns16550_init_postirq(
     uart->timeout_ms = max_t(
         unsigned int, 1, (bits * uart->fifo_size * 1000) / uart->baud);
 
-    if ( uart->irq > 0 )
-    {
-        uart->irqaction.handler = ns16550_interrupt;
-        uart->irqaction.name    = "ns16550";
-        uart->irqaction.dev_id  = port;
-        if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 )
-            printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq);
-    }
-
-    ns16550_setup_postirq(uart);
-
 #ifdef CONFIG_HAS_PCI
     if ( uart->bar || uart->ps_bdf_enable )
     {
@@ -709,8 +709,65 @@ static void __init ns16550_init_postirq(
                                     uart->ps_bdf[0], uart->ps_bdf[1],
                                     uart->ps_bdf[2]);
         }
+
+        if ( uart->msi )
+        {
+            struct msi_info msi = {
+                .bus = uart->ps_bdf[0],
+                .devfn = PCI_DEVFN(uart->ps_bdf[1], uart->ps_bdf[2]),
+                .irq = rc = uart->irq,
+                .entry_nr = 1
+            };
+
+            if ( rc > 0 )
+            {
+                struct msi_desc *msi_desc = NULL;
+
+                spin_lock(&pcidevs_lock);
+
+                rc = pci_enable_msi(&msi, &msi_desc);
+                if ( !rc )
+                {
+                    struct irq_desc *desc = irq_to_desc(msi.irq);
+                    unsigned long flags;
+
+                    spin_lock_irqsave(&desc->lock, flags);
+                    rc = setup_msi_irq(desc, msi_desc);
+                    spin_unlock_irqrestore(&desc->lock, flags);
+                    if ( rc )
+                        pci_disable_msi(msi_desc);
+                }
+
+                spin_unlock(&pcidevs_lock);
+
+                if ( rc )
+                {
+                    uart->irq = 0;
+                    if ( msi_desc )
+                        msi_free_irq(msi_desc);
+                    else
+                        destroy_irq(msi.irq);
+                }
+            }
+
+            if ( rc )
+                printk(XENLOG_WARNING
+                       "MSI setup failed (%d) for %02x:%02x.%o\n",
+                       rc, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2]);
+        }
     }
 #endif
+
+    if ( uart->irq > 0 )
+    {
+        uart->irqaction.handler = ns16550_interrupt;
+        uart->irqaction.name    = "ns16550";
+        uart->irqaction.dev_id  = port;
+        if ( (rc = setup_irq(uart->irq, 0, &uart->irqaction)) != 0 )
+            printk("ERROR: Failed to allocate ns16550 IRQ %d\n", uart->irq);
+    }
+
+    ns16550_setup_postirq(uart);
 }
 
 static void ns16550_suspend(struct serial_port *port)
@@ -820,6 +877,7 @@ static const struct vuart_info *ns16550_
 
 static struct uart_driver __read_mostly ns16550_driver = {
     .init_preirq  = ns16550_init_preirq,
+    .init_irq     = ns16550_init_irq,
     .init_postirq = ns16550_init_postirq,
     .endboot      = ns16550_endboot,
     .suspend      = ns16550_suspend,
@@ -1123,7 +1181,18 @@ static void __init ns16550_parse_port_co
     }
 
     if ( *conf == ',' && *++conf != ',' )
-        uart->irq = simple_strtol(conf, &conf, 10);
+    {
+#ifdef CONFIG_HAS_PCI
+        if ( strncmp(conf, "msi", 3) == 0 )
+        {
+            conf += 3;
+            uart->msi = 1;
+            uart->irq = 0;
+        }
+        else
+#endif
+            uart->irq = simple_strtol(conf, &conf, 10);
+    }
 
 #ifdef CONFIG_HAS_PCI
     if ( *conf == ',' && *++conf != ',' )
--- a/xen/drivers/pci/pci.c
+++ b/xen/drivers/pci/pci.c
@@ -115,6 +115,21 @@ int pci_find_next_ext_capability(int seg
     return 0;
 }
 
+void pci_intx(const struct pci_dev *pdev, bool_t enable)
+{
+    uint16_t seg = pdev->seg;
+    uint8_t bus = pdev->bus;
+    uint8_t slot = PCI_SLOT(pdev->devfn);
+    uint8_t func = PCI_FUNC(pdev->devfn);
+    uint16_t cmd = pci_conf_read16(seg, bus, slot, func, PCI_COMMAND);
+
+    if ( enable )
+        cmd &= ~PCI_COMMAND_INTX_DISABLE;
+    else
+        cmd |= PCI_COMMAND_INTX_DISABLE;
+    pci_conf_write16(seg, bus, slot, func, PCI_COMMAND, cmd);
+}
+
 const char *__init parse_pci(const char *s, unsigned int *seg_p,
                              unsigned int *bus_p, unsigned int *dev_p,
                              unsigned int *func_p)
--- a/xen/include/xen/pci.h
+++ b/xen/include/xen/pci.h
@@ -152,6 +152,7 @@ int pci_find_next_ext_capability(int seg
 const char *parse_pci(const char *, unsigned int *seg, unsigned int *bus,
                       unsigned int *dev, unsigned int *func);
 
+void pci_intx(const struct pci_dev *, bool_t enable);
 bool_t pcie_aer_get_firmware_first(const struct pci_dev *);
 
 struct pirq;

[-- Attachment #3: Type: text/plain, Size: 126 bytes --]

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel

  parent reply	other threads:[~2016-02-23 11:30 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-02-23 11:22 [PATCH 0/4] ns16550: enable support for Pericom controllers Jan Beulich
2016-02-23 11:28 ` [PATCH 1/4] ns16550: store pointer to config parameters for PCI Jan Beulich
2016-03-07 21:06   ` Konrad Rzeszutek Wilk
2016-02-23 11:28 ` [PATCH 2/4] ns16550: enable Pericom controller support Jan Beulich
2016-03-07 22:04   ` Konrad Rzeszutek Wilk
2016-03-08  8:48     ` Jan Beulich
2016-03-09 16:52       ` Konrad Rzeszutek Wilk
2016-03-09 17:01         ` Jan Beulich
2016-03-11  2:31           ` Konrad Rzeszutek Wilk
2016-03-11 11:02             ` Jan Beulich
2016-03-22 13:19   ` [PATCH v2 " Jan Beulich
2016-03-28 14:46     ` Konrad Rzeszutek Wilk
2016-02-23 11:30 ` [PATCH 3/4] console: adjust IRQ initialization Jan Beulich
2016-03-07 22:10   ` Konrad Rzeszutek Wilk
2016-02-23 11:30 ` Jan Beulich [this message]
2016-02-29 16:56 ` [PATCH 0/4] ns16550: enable support for Pericom controllers Konrad Rzeszutek Wilk
2016-02-29 17:03   ` Jan Beulich
2016-03-04 13:19 ` Ping: " Jan Beulich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=56CC50F102000078000D52DB@prv-mh.provo.novell.com \
    --to=jbeulich@suse.com \
    --cc=Ian.Campbell@eu.citrix.com \
    --cc=Ian.Jackson@eu.citrix.com \
    --cc=andrew.cooper3@citrix.com \
    --cc=keir@xen.org \
    --cc=tim@xen.org \
    --cc=xen-devel@lists.xenproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).