11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** I/O Sapic Driver - PCI interrupt line support
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**      (c) Copyright 1999 Grant Grundler
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**      (c) Copyright 1999 Hewlett-Packard Company
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**      This program is free software; you can redistribute it and/or modify
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**      it under the terms of the GNU General Public License as published by
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**      the Free Software Foundation; either version 2 of the License, or
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**      (at your option) any later version.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** The I/O sapic driver manages the Interrupt Redirection Table which is
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** the control logic to convert PCI line based interrupts into a Message
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Signaled Interrupt (aka Transaction Based Interrupt, TBI).
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Acronyms
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** --------
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** HPA  Hard Physical Address (aka MMIO address)
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** IRQ  Interrupt ReQuest. Implies Line based interrupt.
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** IRT	Interrupt Routing Table (provided by PAT firmware)
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** IRdT Interrupt Redirection Table. IRQ line to TXN ADDR/DATA
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**      table which is implemented in I/O SAPIC.
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** ISR  Interrupt Service Routine. aka Interrupt handler.
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** MSI	Message Signaled Interrupt. PCI 2.2 functionality.
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**      aka Transaction Based Interrupt (or TBI).
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** PA   Precision Architecture. HP's RISC architecture.
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** RISC Reduced Instruction Set Computer.
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** What's a Message Signalled Interrupt?
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** -------------------------------------
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** MSI is a write transaction which targets a processor and is similar
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** to a processor write to memory or MMIO. MSIs can be generated by I/O
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** devices as well as processors and require *architecture* to work.
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** PA only supports MSI. So I/O subsystems must either natively generate
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** MSIs (e.g. GSC or HP-PB) or convert line based interrupts into MSIs
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** (e.g. PCI and EISA).  IA64 supports MSIs via a "local SAPIC" which
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** acts on behalf of a processor.
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** MSI allows any I/O device to interrupt any processor. This makes
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** load balancing of the interrupt processing possible on an SMP platform.
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Interrupts are also ordered WRT to DMA data.  It's possible on I/O
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** coherent systems to completely eliminate PIO reads from the interrupt
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** path. The device and driver must be designed and implemented to
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** guarantee all DMA has been issued (issues about atomicity here)
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** before the MSI is issued. I/O status can then safely be read from
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** DMA'd data by the ISR.
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** PA Firmware
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** -----------
530779bf2d2ecc4d9b1e9437ae659f50e6776a7666Matt LaPlante** PA-RISC platforms have two fundamentally different types of firmware.
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** For PCI devices, "Legacy" PDC initializes the "INTERRUPT_LINE" register
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** and BARs similar to a traditional PC BIOS.
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** The newer "PAT" firmware supports PDC calls which return tables.
570779bf2d2ecc4d9b1e9437ae659f50e6776a7666Matt LaPlante** PAT firmware only initializes the PCI Console and Boot interface.
580779bf2d2ecc4d9b1e9437ae659f50e6776a7666Matt LaPlante** With these tables, the OS can program all other PCI devices.
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** One such PAT PDC call returns the "Interrupt Routing Table" (IRT).
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** The IRT maps each PCI slot's INTA-D "output" line to an I/O SAPIC
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** input line.  If the IRT is not available, this driver assumes
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** INTERRUPT_LINE register has been programmed by firmware. The latter
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** case also means online addition of PCI cards can NOT be supported
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** even if HW support is present.
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** All platforms with PAT firmware to date (Oct 1999) use one Interrupt
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Routing Table for the entire platform.
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Where's the iosapic?
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** --------------------
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** I/O sapic is part of the "Core Electronics Complex". And on HP platforms
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** it's integrated as part of the PCI bus adapter, "lba".  So no bus walk
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** will discover I/O Sapic. I/O Sapic driver learns about each device
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** when lba driver advertises the presence of the I/O sapic by calling
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_register().
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** IRQ handling notes
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** ------------------
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** The IO-SAPIC can indicate to the CPU which interrupt was asserted.
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** So, unlike the GSC-ASIC and Dino, we allocate one CPU interrupt per
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** IO-SAPIC interrupt and call the device driver's handler directly.
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** The IO-SAPIC driver hijacks the CPU interrupt handler so it can
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** issue the End Of Interrupt command to the IO-SAPIC.
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Overview of exported iosapic functions
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** --------------------------------------
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** (caveat: code isn't finished yet - this is just the plan)
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_init:
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o initialize globals (lock, etc)
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o try to read IRT. Presence of IRT determines if this is
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**     a PAT platform or not.
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_register():
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o create iosapic_info instance data structure
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o allocate vector_info array for this iosapic
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o initialize vector_info - read corresponding IRdT?
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_xlate_pin: (only called by fixup_irq for PAT platform)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o intr_pin = read cfg (INTERRUPT_PIN);
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o if (device under PCI-PCI bridge)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**               translate slot/pin
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_fixup_irq:
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o if PAT platform (IRT present)
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**	   intr_pin = iosapic_xlate_pin(isi,pcidev):
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**         intr_line = find IRT entry(isi, PCI_SLOT(pcidev), intr_pin)
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**         save IRT entry into vector_info later
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**         write cfg INTERRUPT_LINE (with intr_line)?
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**     else
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**         intr_line = pcidev->irq
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**         IRT pointer = NULL
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**     endif
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o locate vector_info (needs: isi, intr_line)
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o allocate processor "irq" and get txn_addr/data
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o request_irq(processor_irq,  iosapic_interrupt, vector_info,...)
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_enable_irq:
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o clear any pending IRQ on that line
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o enable IRdT - call enable_irq(vector[line]->processor_irq)
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o write EOI in case line is already asserted.
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_disable_irq:
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**   o disable IRdT - call disable_irq(vector[line]->processor_irq)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* FIXME: determine which include files are really needed */
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>	/* get in-line asm for swab */
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/pdc.h>
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/pdcpat.h>
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/page.h>
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>		/* read/write functions */
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_SUPERIO
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/superio.h>
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1481790cf9111f61d360d861901b97eba4de3b5414cKyle McMartin#include <asm/ropes.h>
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "./iosapic_private.h"
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MODULE_NAME "iosapic"
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* "local" compile flags */
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef PCI_BRIDGE_FUNCS
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG_IOSAPIC
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef DEBUG_IOSAPIC_IRT
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_IOSAPIC
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DBG(x...) printk(x)
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else /* DEBUG_IOSAPIC */
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DBG(x...)
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* DEBUG_IOSAPIC */
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_IOSAPIC_IRT
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DBG_IRT(x...) printk(x)
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DBG_IRT(x...)
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_64BIT
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define COMPARE_IRTE_ADDR(irte, hpa)	((irte)->dest_iosapic_addr == (hpa))
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define COMPARE_IRTE_ADDR(irte, hpa)	\
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		((irte)->dest_iosapic_addr == ((hpa) | 0xffffffff00000000ULL))
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_REG_SELECT              0x00
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_REG_WINDOW              0x10
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_REG_EOI                 0x40
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_REG_VERSION		0x1
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_IRDT_ENTRY(idx)		(0x10+(idx)*2)
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_IRDT_ENTRY_HI(idx)	(0x11+(idx)*2)
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline unsigned int iosapic_read(void __iomem *iosapic, unsigned int reg)
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writel(reg, iosapic + IOSAPIC_REG_SELECT);
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return readl(iosapic + IOSAPIC_REG_WINDOW);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void iosapic_write(void __iomem *iosapic, unsigned int reg, u32 val)
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writel(reg, iosapic + IOSAPIC_REG_SELECT);
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writel(val, iosapic + IOSAPIC_REG_WINDOW);
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_VERSION_MASK	0x000000ff
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	IOSAPIC_VERSION(ver)	((int) (ver & IOSAPIC_VERSION_MASK))
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_MAX_ENTRY_MASK          0x00ff0000
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_MAX_ENTRY_SHIFT         0x10
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define	IOSAPIC_IRDT_MAX_ENTRY(ver)	\
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(int) (((ver) & IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT)
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* bits in the "low" I/O Sapic IRdT entry */
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_IRDT_ENABLE       0x10000
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_IRDT_PO_LOW       0x02000
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_IRDT_LEVEL_TRIG   0x08000
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_IRDT_MODE_LPRI    0x00100
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* bits in the "high" I/O Sapic IRdT entry */
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IOSAPIC_IRDT_ID_EID_SHIFT              0x10
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
217a9f6a0dd54efea2a5d57a27e6c232f9197c25154Ingo Molnarstatic DEFINE_SPINLOCK(iosapic_lock);
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void iosapic_eoi(void __iomem *addr, unsigned int data)
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__raw_writel(data, addr);
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** REVISIT: future platforms may have more than one IRT.
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** If so, the following three fields form a structure which
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** then be linked into a list. Names are chosen to make searching
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** for them easy - not necessarily accurate (eg "cell").
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Alternative: iosapic_info could point to the IRT it's in.
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_register() could search a list of IRT's.
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct irt_entry *irt_cell;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic size_t irt_num_entry;
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct irt_entry *iosapic_alloc_irt(int num_entries)
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long a;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The IRT needs to be 8-byte aligned for the PDC call.
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Normally kmalloc would guarantee larger alignment, but
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * if CONFIG_DEBUG_SLAB is enabled, then we can get only
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * 4-byte alignment on 32-bit kernels
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	a = (unsigned long)kmalloc(sizeof(struct irt_entry) * num_entries + 8, GFP_KERNEL);
2463aa0862ce7c120e035bc2aa25997fd000d964d6eGrant Grundler	a = (a + 7UL) & ~7UL;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (struct irt_entry *)a;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * iosapic_load_irt - Fill in the interrupt routing table
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @cell_num: The cell number of the CPU we're currently executing on
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @irt: The address to place the new IRT at
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return The number of entries found
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The "Get PCI INT Routing Table Size" option returns the number of
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * entries in the PCI interrupt routing table for the cell specified
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in the cell_number argument.  The cell number must be for a cell
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * within the caller's protection domain.
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The "Get PCI INT Routing Table" option returns, for the cell
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * specified in the cell_number argument, the PCI interrupt routing
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * table in the caller allocated memory pointed to by mem_addr.
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We assume the IRT only contains entries for I/O SAPIC and
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * calculate the size based on the size of I/O sapic entries.
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The PCI interrupt routing table entry format is derived from the
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * IA64 SAL Specification 2.4.   The PCI interrupt routing table defines
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the routing of PCI interrupt signals between the PCI device output
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "pins" and the IO SAPICs' input "lines" (including core I/O PCI
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * devices).  This table does NOT include information for devices/slots
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * behind PCI to PCI bridges. See PCI to PCI Bridge Architecture Spec.
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for the architected method of routing of IRQ's behind PPB's.
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsiosapic_load_irt(unsigned long cell_num, struct irt_entry **irt)
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	long status;              /* PDC return value status */
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct irt_entry *table;  /* start of interrupt routing tbl */
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long num_entries = 0UL;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	BUG_ON(!irt);
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (is_pdc_pat()) {
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Use pat pdc routine to get interrupt routing table size */
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		DBG("calling get_irt_size (cell %ld)\n", cell_num);
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = pdc_pat_get_irt_size(&num_entries, cell_num);
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		DBG("get_irt_size: %ld\n", status);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		BUG_ON(status != PDC_OK);
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		BUG_ON(num_entries == 0);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** allocate memory for interrupt routing table
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** This interface isn't really right. We are assuming
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** the contents of the table are exclusively
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** for I/O sapic devices.
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		table = iosapic_alloc_irt(num_entries);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (table == NULL) {
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING MODULE_NAME ": read_irt : can "
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					"not alloc mem for IRT\n");
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* get PCI INT routing table */
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = pdc_pat_get_irt(table, cell_num);
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		DBG("pdc_pat_get_irt: %ld\n", status);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		WARN_ON(status != PDC_OK);
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** C3000/J5000 (and similar) platforms with Sprockets PDC
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** will return exactly one IRT for all iosapics.
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** So if we have one, don't need to get it again.
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (irt_cell)
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Should be using the Elroy's HPA, but it's ignored anyway */
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = pdc_pci_irt_size(&num_entries, 0);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		DBG("pdc_pci_irt_size: %ld\n", status);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (status != PDC_OK) {
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Not a "legacy" system with I/O SAPIC either */
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		BUG_ON(num_entries == 0);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		table = iosapic_alloc_irt(num_entries);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!table) {
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_WARNING MODULE_NAME ": read_irt : can "
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					"not alloc mem for IRT\n");
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* HPA ignored by this call too. */
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = pdc_pci_irt(num_entries, 0, table);
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		BUG_ON(status != PDC_OK);
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* return interrupt table address */
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*irt = table;
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_IOSAPIC_IRT
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct irt_entry *p = table;
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num);
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(MODULE_NAME " start = 0x%p num_entries %ld entry_size %d\n",
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		table,
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		num_entries,
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(int) sizeof(struct irt_entry));
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0 ; i < num_entries ; i++, p++) {
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n",
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p->entry_type, p->entry_length, p->interrupt_type,
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p->polarity_trigger, p->src_bus_irq_devno, p->src_bus_id,
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		p->src_seg_id, p->dest_iosapic_intin,
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		((u32 *) p)[2],
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		((u32 *) p)[3]
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* DEBUG_IOSAPIC_IRT */
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return num_entries;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid __init iosapic_init(void)
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long cell = 0;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG("iosapic_init()\n");
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef __LP64__
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (is_pdc_pat()) {
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int status;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct pdc_pat_cell_num cell_info;
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = pdc_pat_cell_get_number(&cell_info);
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (status == PDC_OK) {
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cell = cell_info.cell_num;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* get interrupt routing table for this cell */
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irt_num_entry = iosapic_load_irt(cell, &irt_cell);
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (irt_num_entry == 0)
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irt_cell = NULL;	/* old PDC w/o iosapic */
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Return the IRT entry in case we need to look something else up.
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct irt_entry *
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsirt_find_irqline(struct iosapic_info *isi, u8 slot, u8 intr_pin)
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct irt_entry *i = irt_cell;
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int cnt;	/* track how many entries we've looked at */
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 irq_devno = (slot << IRT_DEV_SHIFT) | (intr_pin-1);
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG_IRT("irt_find_irqline() SLOT %d pin %d\n", slot, intr_pin);
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (cnt=0; cnt < irt_num_entry; cnt++, i++) {
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** Validate: entry_type, entry_length, interrupt_type
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** Difference between validate vs compare is the former
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** should print debug info and is not expected to "fail"
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** on current platforms.
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (i->entry_type != IRT_IOSAPIC_TYPE) {
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d type %d\n", i, cnt, i->entry_type);
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (i->entry_length != IRT_IOSAPIC_LENGTH) {
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d  length %d\n", i, cnt, i->entry_length);
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (i->interrupt_type != IRT_VECTORED_INTR) {
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry  %d interrupt_type %d\n", i, cnt, i->interrupt_type);
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!COMPARE_IRTE_ADDR(i, isi->isi_hpa))
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((i->src_bus_irq_devno & IRT_IRQ_DEVNO_MASK) != irq_devno)
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** Ignore: src_bus_id and rc_seg_id correlate with
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**         iosapic_info->isi_hpa on HP platforms.
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**         If needed, pass in "PFA" (aka config space addr)
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**         instead of slot.
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Found it! */
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return i;
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_WARNING MODULE_NAME ": 0x%lx : no IRT entry for slot %d, pin %d\n",
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			isi->isi_hpa, slot, intr_pin);
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** xlate_pin() supports the skewing of IRQ lines done by subsidiary bridges.
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Legacy PDC already does this translation for us and stores it in INTR_LINE.
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** PAT PDC needs to basically do what legacy PDC does:
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** o read PIN
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** o adjust PIN in case device is "behind" a PPB
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**     (eg 4-port 100BT and SCSI/LAN "Combo Card")
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** o convert slot/pin to I/O SAPIC input line.
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** HP platforms only support:
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** o one level of skewing for any number of PPBs
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** o only support PCI-PCI Bridges.
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct irt_entry *
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsiosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev)
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 intr_pin, intr_slot;
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_read_config_byte(pcidev, PCI_INTERRUPT_PIN, &intr_pin);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG_IRT("iosapic_xlate_pin(%s) SLOT %d pin %d\n",
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pcidev->slot_name, PCI_SLOT(pcidev->devfn), intr_pin);
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (intr_pin == 0) {
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* The device does NOT support/use IRQ lines.  */
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check if pcidev behind a PPB */
4899785d646c10b0707412516ffe56b71b9eb18861fGrant Grundler	if (pcidev->bus->parent) {
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Convert pcidev INTR_PIN into something we
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** can lookup in the IRT.
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef PCI_BRIDGE_FUNCS
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** Proposal #1:
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** call implementation specific translation function
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** This is architecturally "cleaner". HP-UX doesn't
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** support other secondary bus types (eg. E/ISA) directly.
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** May be needed for other processor (eg IA64) architectures
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** or by some ambitous soul who wants to watch TV.
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (pci_bridge_funcs->xlate_intr_line) {
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			intr_pin = pci_bridge_funcs->xlate_intr_line(pcidev);
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else	/* PCI_BRIDGE_FUNCS */
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct pci_bus *p = pcidev->bus;
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** Proposal #2:
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** The "pin" is skewed ((pin + dev - 1) % 4).
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** This isn't very clean since I/O SAPIC must assume:
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**   - all platforms only have PCI busses.
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**   - only PCI-PCI bridge (eg not PCI-EISA, PCI-PCMCIA)
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**   - IRQ routing is only skewed once regardless of
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**     the number of PPB's between iosapic and device.
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**     (Bit3 expansion chassis follows this rule)
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** Advantage is it's really easy to implement.
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
521f0e88af855cbc5012f2e796f42686969b82d79d4Bjorn Helgaas		intr_pin = pci_swizzle_interrupt_pin(pcidev, intr_pin);
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* PCI_BRIDGE_FUNCS */
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
5259785d646c10b0707412516ffe56b71b9eb18861fGrant Grundler		 * Locate the host slot of the PPB.
5269785d646c10b0707412516ffe56b71b9eb18861fGrant Grundler		 */
5279785d646c10b0707412516ffe56b71b9eb18861fGrant Grundler		while (p->parent->parent)
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			p = p->parent;
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intr_slot = PCI_SLOT(p->self->devfn);
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		intr_slot = PCI_SLOT(pcidev->devfn);
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG_IRT("iosapic_xlate_pin:  bus %d slot %d pin %d\n",
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pcidev->bus->secondary, intr_slot, intr_pin);
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return irt_find_irqline(isi, intr_slot, intr_pin);
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void iosapic_rd_irt_entry(struct vector_info *vi , u32 *dp0, u32 *dp1)
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct iosapic_info *isp = vi->iosapic;
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 idx = vi->irqline;
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*dp0 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY(idx));
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*dp1 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY_HI(idx));
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void iosapic_wr_irt_entry(struct vector_info *vi, u32 dp0, u32 dp1)
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct iosapic_info *isp = vi->iosapic;
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %lx 0x%x 0x%x\n",
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vi->irqline, isp->isi_hpa, dp0, dp1);
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY(vi->irqline), dp0);
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read the window register to flush the writes down to HW  */
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dp0 = readl(isp->addr+IOSAPIC_REG_WINDOW);
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY_HI(vi->irqline), dp1);
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read the window register to flush the writes down to HW  */
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dp1 = readl(isp->addr+IOSAPIC_REG_WINDOW);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** set_irt prepares the data (dp0, dp1) according to the vector_info
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** and target cpu (id_eid).  dp0/dp1 are then used to program I/O SAPIC
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** IRdT for the given "vector" (aka IRQ line).
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsiosapic_set_irt_data( struct vector_info *vi, u32 *dp0, u32 *dp1)
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 mode = 0;
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct irt_entry *p = vi->irte;
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((p->polarity_trigger & IRT_PO_MASK) == IRT_ACTIVE_LO)
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mode |= IOSAPIC_IRDT_PO_LOW;
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (((p->polarity_trigger >> IRT_EL_SHIFT) & IRT_EL_MASK) == IRT_LEVEL_TRIG)
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mode |= IOSAPIC_IRDT_LEVEL_TRIG;
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	** IA64 REVISIT
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	** PA doesn't support EXTINT or LPRIO bits.
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*dp0 = mode | (u32) vi->txn_data;
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	** Extracting id_eid isn't a real clean way of getting it.
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	** But the encoding is the same for both PA and IA64 platforms.
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (is_pdc_pat()) {
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** PAT PDC just hands it to us "right".
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** txn_addr comes from cpu_data[x].txn_addr.
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*dp1 = (u32) (vi->txn_addr);
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** eg if base_addr == 0xfffa0000),
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**    we want to get 0xa0ff0000.
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		**
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** eid	0x0ff00000 -> 0x00ff0000
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		** id	0x000ff000 -> 0xff000000
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*/
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*dp1 = (((u32)vi->txn_addr & 0x0ff00000) >> 4) |
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			(((u32)vi->txn_addr & 0x000ff000) << 12);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG_IRT("iosapic_set_irt_data(): 0x%x 0x%x\n", *dp0, *dp1);
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6174c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixnerstatic void iosapic_mask_irq(struct irq_data *d)
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
6204c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	struct vector_info *vi = irq_data_get_irq_chip_data(d);
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 d0, d1;
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&iosapic_lock, flags);
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iosapic_rd_irt_entry(vi, &d0, &d1);
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	d0 |= IOSAPIC_IRDT_ENABLE;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iosapic_wr_irt_entry(vi, d0, d1);
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&iosapic_lock, flags);
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6304c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixnerstatic void iosapic_unmask_irq(struct irq_data *d)
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6324c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	struct vector_info *vi = irq_data_get_irq_chip_data(d);
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 d0, d1;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* data is initialized by fixup_irq */
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	WARN_ON(vi->txn_irq  == 0);
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iosapic_set_irt_data(vi, &d0, &d1);
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iosapic_wr_irt_entry(vi, d0, d1);
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_IOSAPIC_IRT
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 *t = (u32 *) ((ulong) vi->eoi_addr & ~0xffUL);
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("iosapic_enable_irq(): regs %p", vi->eoi_addr);
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for ( ; t < vi->eoi_addr; t++)
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(" %x", readl(t));
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("\n");
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsprintk("iosapic_enable_irq(): sel ");
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct iosapic_info *isp = vi->iosapic;
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (d0=0x10; d0<0x1e; d0++) {
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		d1 = iosapic_read(isp->addr, d0);
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(" %x", d1);
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsprintk("\n");
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Issuing I/O SAPIC an EOI causes an interrupt IFF IRQ line is
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * asserted.  IRQ generally should not be asserted when a driver
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * enables their IRQ. It can lead to "interesting" race conditions
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * in the driver initialization sequence.
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
6684c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	DBG(KERN_DEBUG "enable_irq(%d): eoi(%p, 0x%x)\n", d->irq,
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			vi->eoi_addr, vi->eoi_data);
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iosapic_eoi(vi->eoi_addr, vi->eoi_data);
67151890613f2bfa70453a5cc22c91c63946dd311cdJames Bottomley}
67251890613f2bfa70453a5cc22c91c63946dd311cdJames Bottomley
6734c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixnerstatic void iosapic_eoi_irq(struct irq_data *d)
67451890613f2bfa70453a5cc22c91c63946dd311cdJames Bottomley{
6754c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	struct vector_info *vi = irq_data_get_irq_chip_data(d);
67651890613f2bfa70453a5cc22c91c63946dd311cdJames Bottomley
67751890613f2bfa70453a5cc22c91c63946dd311cdJames Bottomley	iosapic_eoi(vi->eoi_addr, vi->eoi_data);
6784c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	cpu_eoi_irq(d);
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley#ifdef CONFIG_SMP
6824c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixnerstatic int iosapic_set_affinity_irq(struct irq_data *d,
6834c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner				    const struct cpumask *dest, bool force)
684c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley{
6854c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	struct vector_info *vi = irq_data_get_irq_chip_data(d);
686c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	u32 d0, d1, dummy_d0;
687c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	unsigned long flags;
6888b6649c575e0d8312f62fe643ae43558892da2e1Kyle McMartin	int dest_cpu;
689c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley
6904c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	dest_cpu = cpu_check_affinity(d, dest);
6918b6649c575e0d8312f62fe643ae43558892da2e1Kyle McMartin	if (dest_cpu < 0)
692d5dedd4507d307eb3f35f21b6e16f336fdc0d82aYinghai Lu		return -1;
693c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley
6944c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	cpumask_copy(d->affinity, cpumask_of(dest_cpu));
6954c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	vi->txn_addr = txn_affinity_addr(d->irq, dest_cpu);
696c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley
697c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	spin_lock_irqsave(&iosapic_lock, flags);
698c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	/* d1 contains the destination CPU, so only want to set that
699c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	 * entry */
700c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	iosapic_rd_irt_entry(vi, &d0, &d1);
701c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	iosapic_set_irt_data(vi, &dummy_d0, &d1);
702c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	iosapic_wr_irt_entry(vi, d0, d1);
703c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley	spin_unlock_irqrestore(&iosapic_lock, flags);
704d5dedd4507d307eb3f35f21b6e16f336fdc0d82aYinghai Lu
705d5dedd4507d307eb3f35f21b6e16f336fdc0d82aYinghai Lu	return 0;
706c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley}
707c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley#endif
708c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley
709dfe07565021959f0f646e9e775810c1bfbe0f6d6Thomas Gleixnerstatic struct irq_chip iosapic_interrupt_type = {
7104c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	.name		=	"IO-SAPIC-level",
7114c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	.irq_unmask	=	iosapic_unmask_irq,
7124c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	.irq_mask	=	iosapic_mask_irq,
7134c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	.irq_ack	=	cpu_ack_irq,
7144c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	.irq_eoi	=	iosapic_eoi_irq,
715c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley#ifdef CONFIG_SMP
7164c4231ea2f794d73bbb50b8d84e00c66a012a607Thomas Gleixner	.irq_set_affinity =	iosapic_set_affinity_irq,
717c2ab64d09815cc4d48347ee3679658f197455a2aJames Bottomley#endif
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint iosapic_fixup_irq(void *isi_obj, struct pci_dev *pcidev)
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct iosapic_info *isi = isi_obj;
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct irt_entry *irte = NULL;  /* only used if PAT PDC */
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct vector_info *vi;
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int isi_line;	/* line used by device */
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!isi) {
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING MODULE_NAME ": hpa not registered for %s\n",
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pci_name(pcidev));
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_SUPERIO
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * HACK ALERT! (non-compliant PCI device support)
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * All SuckyIO interrupts are routed through the PIC's on function 1.
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * But SuckyIO OHCI USB controller gets an IRT entry anyway because
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * it advertises INT D for INT_PIN.  Use that IRT entry to get the
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * SuckyIO interrupt routing for PICs on function 1 (*BLEECCHH*).
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (is_superio_device(pcidev)) {
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* We must call superio_fixup_irq() to register the pdev */
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pcidev->irq = superio_fixup_irq(pcidev);
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Don't return if need to program the IOSAPIC's IRT... */
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (PCI_FUNC(pcidev->devfn) != SUPERIO_USB_FN)
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return pcidev->irq;
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* CONFIG_SUPERIO */
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* lookup IRT entry for isi/slot/pin set */
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irte = iosapic_xlate_pin(isi, pcidev);
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!irte) {
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("iosapic: no IRTE for %s (IRQ not connected?)\n",
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pci_name(pcidev));
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG_IRT("iosapic_fixup_irq(): irte %p %x %x %x %x %x %x %x %x\n",
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irte,
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irte->entry_type,
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irte->entry_length,
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irte->polarity_trigger,
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irte->src_bus_irq_devno,
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irte->src_bus_id,
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irte->src_seg_id,
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irte->dest_iosapic_intin,
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(u32) irte->dest_iosapic_addr);
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	isi_line = irte->dest_iosapic_intin;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* get vector info for this input line */
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vi = isi->isi_vector + isi_line;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG_IRT("iosapic_fixup_irq:  line %d vi 0x%p\n", isi_line, vi);
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* If this IRQ line has already been setup, skip it */
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vi->irte)
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vi->irte = irte;
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Allocate processor IRQ
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * XXX/FIXME The txn_alloc_irq() code and related code should be
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * moved to enable_irq(). That way we only allocate processor IRQ
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * bits for devices that actually have drivers claiming them.
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Right now we assign an IRQ to every PCI device present,
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * regardless of whether it's used or not.
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vi->txn_irq = txn_alloc_irq(8);
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vi->txn_irq < 0)
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		panic("I/O sapic: couldn't get TXN IRQ\n");
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* enable_irq() will use txn_* to program IRdT */
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vi->txn_addr = txn_alloc_addr(vi->txn_irq);
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vi->txn_data = txn_alloc_data(vi->txn_irq);
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI;
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vi->eoi_data = cpu_to_le32(vi->txn_data);
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi);
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out:
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pcidev->irq = vi->txn_irq;
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DBG_IRT("iosapic_fixup_irq() %d:%d %x %x line %d irq %d\n",
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PCI_SLOT(pcidev->devfn), PCI_FUNC(pcidev->devfn),
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pcidev->vendor, pcidev->device, isi_line, pcidev->irq);
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return pcidev->irq;
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** squirrel away the I/O Sapic Version
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsiosapic_rd_version(struct iosapic_info *isi)
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return iosapic_read(isi->addr, IOSAPIC_REG_VERSION);
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** iosapic_register() is called by "drivers" with an integrated I/O SAPIC.
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds** Caller must be certain they have an I/O SAPIC and know its MMIO address.
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**	o allocate iosapic_info and add it to the list
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**	o read iosapic version and squirrel that away
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**	o read size of IRdT.
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**	o allocate and initialize isi_vector[]
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds**	o allocate irq region
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid *iosapic_register(unsigned long hpa)
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct iosapic_info *isi = NULL;
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct irt_entry *irte = irt_cell;
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct vector_info *vip;
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int cnt;	/* track how many entries we've looked at */
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Astro based platforms can only support PCI OLARD if they implement
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * PAT PDC.  Legacy PDC omits LBAs with no PCI devices from the IRT.
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Search the IRT and ignore iosapic's which aren't in the IRT.
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (cnt=0; cnt < irt_num_entry; cnt++, irte++) {
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		WARN_ON(IRT_IOSAPIC_TYPE != irte->entry_type);
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (COMPARE_IRTE_ADDR(irte, hpa))
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cnt >= irt_num_entry) {
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		DBG("iosapic_register() ignoring 0x%lx (NOT FOUND)\n", hpa);
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8585cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day	isi = kzalloc(sizeof(struct iosapic_info), GFP_KERNEL);
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!isi) {
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		BUG();
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8645076c15862644edb91d2e3436b2fa3e07b28385dHelge Deller	isi->addr = ioremap_nocache(hpa, 4096);
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	isi->isi_hpa = hpa;
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	isi->isi_version = iosapic_rd_version(isi);
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1;
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
869f8301041d7bdca3197d718518242eeba0c75352fJoe Perches	vip = isi->isi_vector = kcalloc(isi->isi_num_vectors,
870f8301041d7bdca3197d718518242eeba0c75352fJoe Perches					sizeof(struct vector_info), GFP_KERNEL);
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vip == NULL) {
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(isi);
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (cnt=0; cnt < isi->isi_num_vectors; cnt++, vip++) {
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vip->irqline = (unsigned char) cnt;
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vip->iosapic = isi;
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return isi;
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_IOSAPIC
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsiosapic_prt_irt(void *irt, long num_entry)
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int i, *irp = (unsigned int *) irt;
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG MODULE_NAME ": Interrupt Routing Table (%lx entries)\n", num_entry);
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i<num_entry; i++, irp += 4) {
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_DEBUG "%p : %2d %.8x %.8x %.8x %.8x\n",
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					irp, i, irp[0], irp[1], irp[2], irp[3]);
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsiosapic_prt_vi(struct vector_info *vi)
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG MODULE_NAME ": vector_info[%d] is at %p\n", vi->irqline, vi);
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\tstatus:	 %.4x\n", vi->status);
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\ttxn_irq:  %d\n",  vi->txn_irq);
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\ttxn_addr: %lx\n", vi->txn_addr);
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\ttxn_data: %lx\n", vi->txn_data);
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\teoi_addr: %p\n",  vi->eoi_addr);
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\teoi_data: %x\n",  vi->eoi_data);
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsiosapic_prt_isi(struct iosapic_info *isi)
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG MODULE_NAME ": io_sapic_info at %p\n", isi);
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\tisi_hpa:       %lx\n", isi->isi_hpa);
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\tisi_status:    %x\n", isi->isi_status);
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\tisi_version:   %x\n", isi->isi_version);
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_DEBUG "\t\tisi_vector:    %p\n", isi->isi_vector);
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif /* DEBUG_IOSAPIC */
924