x86/PCI: Add support for the SiS85C497 PIRQ router
authorMaciej W. Rozycki <macro@orcam.me.uk>
Thu, 31 Mar 2022 07:10:46 +0000 (08:10 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Sun, 10 Apr 2022 10:48:14 +0000 (12:48 +0200)
The SiS 85C496/497 486 Green PC VESA/ISA/PCI Chipset has support for PCI
steering and the ELCR register implemented.  These features are handled
by the SiS85C497 AT Bus Controller & Megacell (ATM) ISA bridge, however
the device is wired as a peer bridge directly to the host bus and has
its PCI configuration registers decoded at addresses 0x80-0xff by the
accompanying SiS85C496 PCI & CPU Memory Controller (PCM) host bridge[1].
Therefore we need to match on the host bridge's vendor and device ID.

Like with the SiS85C503 PIRQ router handle link value ranges of 1-4 and
0xc0-0xc3, corresponding respectively to PIRQ line numbers counted from
1 and link register PCI configuration space addresses.

References:

[1]  "486 Green PC VESA/ISA/PCI Chipset, SiS 85C496/497", Rev 3.0,
     Silicon Integrated Systems Corp., July 1995, Part IV, Section 3.
     "PCI Configuration Space Registers (00h ~ FFh)", p. 114

Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nikolai Zhubr <zhubr.2@gmail.com>
Link: https://lore.kernel.org/r/alpine.DEB.2.21.2203301610490.22465@angie.orcam.me.uk
arch/x86/pci/irq.c

index e5bc9f7a17c30a44681c064b281b2ca3365878f6..4b0e008aeb3099bfaa17b990154881b18a02bb72 100644 (file)
@@ -580,6 +580,81 @@ static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
        return 1;
 }
 
+
+/*
+ *     PIRQ routing for the SiS85C497 AT Bus Controller & Megacell (ATM)
+ *     ISA bridge used with the SiS 85C496/497 486 Green PC VESA/ISA/PCI
+ *     Chipset.
+ *
+ *     There are four PCI INTx#-to-IRQ Link registers provided in the
+ *     SiS85C497 part of the peculiar combined 85C496/497 configuration
+ *     space decoded by the SiS85C496 PCI & CPU Memory Controller (PCM)
+ *     host bridge, at 0xc0/0xc1/0xc2/0xc3 respectively for the PCI INT
+ *     A/B/C/D lines.  Bit 7 enables the respective link if set and bits
+ *     3:0 select the 8259A IRQ line as follows:
+ *
+ *     0000 : Reserved
+ *     0001 : Reserved
+ *     0010 : Reserved
+ *     0011 : IRQ3
+ *     0100 : IRQ4
+ *     0101 : IRQ5
+ *     0110 : IRQ6
+ *     0111 : IRQ7
+ *     1000 : Reserved
+ *     1001 : IRQ9
+ *     1010 : IRQ10
+ *     1011 : IRQ11
+ *     1100 : IRQ12
+ *     1101 : Reserved
+ *     1110 : IRQ14
+ *     1111 : IRQ15
+ *
+ *     We avoid using a reserved value for disabled links, hence the
+ *     choice of IRQ15 for that case.
+ *
+ *     References:
+ *
+ *     "486 Green PC VESA/ISA/PCI Chipset, SiS 85C496/497", Rev 3.0,
+ *     Silicon Integrated Systems Corp., July 1995
+ */
+
+#define PCI_SIS497_INTA_TO_IRQ_LINK    0xc0u
+
+#define PIRQ_SIS497_IRQ_MASK           0x0fu
+#define PIRQ_SIS497_IRQ_ENABLE         0x80u
+
+static int pirq_sis497_get(struct pci_dev *router, struct pci_dev *dev,
+                          int pirq)
+{
+       int reg;
+       u8 x;
+
+       reg = pirq;
+       if (reg >= 1 && reg <= 4)
+               reg += PCI_SIS497_INTA_TO_IRQ_LINK - 1;
+
+       pci_read_config_byte(router, reg, &x);
+       return (x & PIRQ_SIS497_IRQ_ENABLE) ? (x & PIRQ_SIS497_IRQ_MASK) : 0;
+}
+
+static int pirq_sis497_set(struct pci_dev *router, struct pci_dev *dev,
+                          int pirq, int irq)
+{
+       int reg;
+       u8 x;
+
+       reg = pirq;
+       if (reg >= 1 && reg <= 4)
+               reg += PCI_SIS497_INTA_TO_IRQ_LINK - 1;
+
+       pci_read_config_byte(router, reg, &x);
+       x &= ~(PIRQ_SIS497_IRQ_MASK | PIRQ_SIS497_IRQ_ENABLE);
+       x |= irq ? (PIRQ_SIS497_IRQ_ENABLE | irq) : PIRQ_SIS497_IRQ_MASK;
+       pci_write_config_byte(router, reg, x);
+       return 1;
+}
+
 /*
  *     PIRQ routing for SiS 85C503 router used in several SiS chipsets.
  *     We have to deal with the following issues here:
@@ -962,6 +1037,11 @@ static __init int serverworks_router_probe(struct irq_router *r,
 static __init int sis_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
 {
        switch (device) {
+       case PCI_DEVICE_ID_SI_496:
+               r->name = "SiS85C497";
+               r->get = pirq_sis497_get;
+               r->set = pirq_sis497_set;
+               return 1;
        case PCI_DEVICE_ID_SI_503:
                r->name = "SiS85C503";
                r->get = pirq_sis503_get;