|
13 | 13 | #include <linux/dmi.h>
|
14 | 14 | #include <linux/io.h>
|
15 | 15 | #include <linux/smp.h>
|
| 16 | +#include <linux/spinlock.h> |
16 | 17 | #include <asm/io_apic.h>
|
17 | 18 | #include <linux/irq.h>
|
18 | 19 | #include <linux/acpi.h>
|
| 20 | + |
| 21 | +#include <asm/pc-conf-reg.h> |
19 | 22 | #include <asm/pci_x86.h>
|
20 | 23 |
|
21 | 24 | #define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24))
|
@@ -47,6 +50,8 @@ struct irq_router {
|
47 | 50 | int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq);
|
48 | 51 | int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq,
|
49 | 52 | int new);
|
| 53 | + int (*lvl)(struct pci_dev *router, struct pci_dev *dev, int pirq, |
| 54 | + int irq); |
50 | 55 | };
|
51 | 56 |
|
52 | 57 | struct irq_router_handler {
|
@@ -169,6 +174,139 @@ void elcr_set_level_irq(unsigned int irq)
|
169 | 174 | }
|
170 | 175 | }
|
171 | 176 |
|
| 177 | +/* |
| 178 | + * PIRQ routing for the M1487 ISA Bus Controller (IBC) ASIC used |
| 179 | + * with the ALi FinALi 486 chipset. The IBC is not decoded in the |
| 180 | + * PCI configuration space, so we identify it by the accompanying |
| 181 | + * M1489 Cache-Memory PCI Controller (CMP) ASIC. |
| 182 | + * |
| 183 | + * There are four 4-bit mappings provided, spread across two PCI |
| 184 | + * INTx Routing Table Mapping Registers, available in the port I/O |
| 185 | + * space accessible indirectly via the index/data register pair at |
| 186 | + * 0x22/0x23, located at indices 0x42 and 0x43 for the INT1/INT2 |
| 187 | + * and INT3/INT4 lines respectively. The INT1/INT3 and INT2/INT4 |
| 188 | + * lines are mapped in the low and the high 4-bit nibble of the |
| 189 | + * corresponding register as follows: |
| 190 | + * |
| 191 | + * 0000 : Disabled |
| 192 | + * 0001 : IRQ9 |
| 193 | + * 0010 : IRQ3 |
| 194 | + * 0011 : IRQ10 |
| 195 | + * 0100 : IRQ4 |
| 196 | + * 0101 : IRQ5 |
| 197 | + * 0110 : IRQ7 |
| 198 | + * 0111 : IRQ6 |
| 199 | + * 1000 : Reserved |
| 200 | + * 1001 : IRQ11 |
| 201 | + * 1010 : Reserved |
| 202 | + * 1011 : IRQ12 |
| 203 | + * 1100 : Reserved |
| 204 | + * 1101 : IRQ14 |
| 205 | + * 1110 : Reserved |
| 206 | + * 1111 : IRQ15 |
| 207 | + * |
| 208 | + * In addition to the usual ELCR register pair there is a separate |
| 209 | + * PCI INTx Sensitivity Register at index 0x44 in the same port I/O |
| 210 | + * space, whose bits 3:0 select the trigger mode for INT[4:1] lines |
| 211 | + * respectively. Any bit set to 1 causes interrupts coming on the |
| 212 | + * corresponding line to be passed to ISA as edge-triggered and |
| 213 | + * otherwise they are passed as level-triggered. Manufacturer's |
| 214 | + * documentation says this register has to be set consistently with |
| 215 | + * the relevant ELCR register. |
| 216 | + * |
| 217 | + * Accesses to the port I/O space concerned here need to be unlocked |
| 218 | + * by writing the value of 0xc5 to the Lock Register at index 0x03 |
| 219 | + * beforehand. Any other value written to said register prevents |
| 220 | + * further accesses from reaching the register file, except for the |
| 221 | + * Lock Register being written with 0xc5 again. |
| 222 | + * |
| 223 | + * References: |
| 224 | + * |
| 225 | + * "M1489/M1487: 486 PCI Chip Set", Version 1.2, Acer Laboratories |
| 226 | + * Inc., July 1997 |
| 227 | + */ |
| 228 | + |
| 229 | +#define PC_CONF_FINALI_LOCK 0x03u |
| 230 | +#define PC_CONF_FINALI_PCI_INTX_RT1 0x42u |
| 231 | +#define PC_CONF_FINALI_PCI_INTX_RT2 0x43u |
| 232 | +#define PC_CONF_FINALI_PCI_INTX_SENS 0x44u |
| 233 | + |
| 234 | +#define PC_CONF_FINALI_LOCK_KEY 0xc5u |
| 235 | + |
| 236 | +static u8 read_pc_conf_nybble(u8 base, u8 index) |
| 237 | +{ |
| 238 | + u8 reg = base + (index >> 1); |
| 239 | + u8 x; |
| 240 | + |
| 241 | + x = pc_conf_get(reg); |
| 242 | + return index & 1 ? x >> 4 : x & 0xf; |
| 243 | +} |
| 244 | + |
| 245 | +static void write_pc_conf_nybble(u8 base, u8 index, u8 val) |
| 246 | +{ |
| 247 | + u8 reg = base + (index >> 1); |
| 248 | + u8 x; |
| 249 | + |
| 250 | + x = pc_conf_get(reg); |
| 251 | + x = index & 1 ? (x & 0x0f) | (val << 4) : (x & 0xf0) | val; |
| 252 | + pc_conf_set(reg, x); |
| 253 | +} |
| 254 | + |
| 255 | +static int pirq_finali_get(struct pci_dev *router, struct pci_dev *dev, |
| 256 | + int pirq) |
| 257 | +{ |
| 258 | + static const u8 irqmap[16] = { |
| 259 | + 0, 9, 3, 10, 4, 5, 7, 6, 0, 11, 0, 12, 0, 14, 0, 15 |
| 260 | + }; |
| 261 | + unsigned long flags; |
| 262 | + u8 x; |
| 263 | + |
| 264 | + raw_spin_lock_irqsave(&pc_conf_lock, flags); |
| 265 | + pc_conf_set(PC_CONF_FINALI_LOCK, PC_CONF_FINALI_LOCK_KEY); |
| 266 | + x = irqmap[read_pc_conf_nybble(PC_CONF_FINALI_PCI_INTX_RT1, pirq - 1)]; |
| 267 | + pc_conf_set(PC_CONF_FINALI_LOCK, 0); |
| 268 | + raw_spin_unlock_irqrestore(&pc_conf_lock, flags); |
| 269 | + return x; |
| 270 | +} |
| 271 | + |
| 272 | +static int pirq_finali_set(struct pci_dev *router, struct pci_dev *dev, |
| 273 | + int pirq, int irq) |
| 274 | +{ |
| 275 | + static const u8 irqmap[16] = { |
| 276 | + 0, 0, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 |
| 277 | + }; |
| 278 | + u8 val = irqmap[irq]; |
| 279 | + unsigned long flags; |
| 280 | + |
| 281 | + if (!val) |
| 282 | + return 0; |
| 283 | + |
| 284 | + raw_spin_lock_irqsave(&pc_conf_lock, flags); |
| 285 | + pc_conf_set(PC_CONF_FINALI_LOCK, PC_CONF_FINALI_LOCK_KEY); |
| 286 | + write_pc_conf_nybble(PC_CONF_FINALI_PCI_INTX_RT1, pirq - 1, val); |
| 287 | + pc_conf_set(PC_CONF_FINALI_LOCK, 0); |
| 288 | + raw_spin_unlock_irqrestore(&pc_conf_lock, flags); |
| 289 | + return 1; |
| 290 | +} |
| 291 | + |
| 292 | +static int pirq_finali_lvl(struct pci_dev *router, struct pci_dev *dev, |
| 293 | + int pirq, int irq) |
| 294 | +{ |
| 295 | + u8 mask = ~(1u << (pirq - 1)); |
| 296 | + unsigned long flags; |
| 297 | + u8 trig; |
| 298 | + |
| 299 | + elcr_set_level_irq(irq); |
| 300 | + raw_spin_lock_irqsave(&pc_conf_lock, flags); |
| 301 | + pc_conf_set(PC_CONF_FINALI_LOCK, PC_CONF_FINALI_LOCK_KEY); |
| 302 | + trig = pc_conf_get(PC_CONF_FINALI_PCI_INTX_SENS); |
| 303 | + trig &= mask; |
| 304 | + pc_conf_set(PC_CONF_FINALI_PCI_INTX_SENS, trig); |
| 305 | + pc_conf_set(PC_CONF_FINALI_LOCK, 0); |
| 306 | + raw_spin_unlock_irqrestore(&pc_conf_lock, flags); |
| 307 | + return 1; |
| 308 | +} |
| 309 | + |
172 | 310 | /*
|
173 | 311 | * Common IRQ routing practice: nibbles in config space,
|
174 | 312 | * offset by some magic constant.
|
@@ -745,6 +883,12 @@ static __init int ite_router_probe(struct irq_router *r, struct pci_dev *router,
|
745 | 883 | static __init int ali_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
|
746 | 884 | {
|
747 | 885 | switch (device) {
|
| 886 | + case PCI_DEVICE_ID_AL_M1489: |
| 887 | + r->name = "FinALi"; |
| 888 | + r->get = pirq_finali_get; |
| 889 | + r->set = pirq_finali_set; |
| 890 | + r->lvl = pirq_finali_lvl; |
| 891 | + return 1; |
748 | 892 | case PCI_DEVICE_ID_AL_M1533:
|
749 | 893 | case PCI_DEVICE_ID_AL_M1563:
|
750 | 894 | r->name = "ALI";
|
@@ -968,11 +1112,17 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
|
968 | 1112 | } else if (r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
|
969 | 1113 | ((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask))) {
|
970 | 1114 | msg = "found";
|
971 |
| - elcr_set_level_irq(irq); |
| 1115 | + if (r->lvl) |
| 1116 | + r->lvl(pirq_router_dev, dev, pirq, irq); |
| 1117 | + else |
| 1118 | + elcr_set_level_irq(irq); |
972 | 1119 | } else if (newirq && r->set &&
|
973 | 1120 | (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
|
974 | 1121 | if (r->set(pirq_router_dev, dev, pirq, newirq)) {
|
975 |
| - elcr_set_level_irq(newirq); |
| 1122 | + if (r->lvl) |
| 1123 | + r->lvl(pirq_router_dev, dev, pirq, newirq); |
| 1124 | + else |
| 1125 | + elcr_set_level_irq(newirq); |
976 | 1126 | msg = "assigned";
|
977 | 1127 | irq = newirq;
|
978 | 1128 | }
|
|
0 commit comments