18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci** I/O Sapic Driver - PCI interrupt line support
48c2ecf20Sopenharmony_ci**
58c2ecf20Sopenharmony_ci**      (c) Copyright 1999 Grant Grundler
68c2ecf20Sopenharmony_ci**      (c) Copyright 1999 Hewlett-Packard Company
78c2ecf20Sopenharmony_ci**
88c2ecf20Sopenharmony_ci**
98c2ecf20Sopenharmony_ci** The I/O sapic driver manages the Interrupt Redirection Table which is
108c2ecf20Sopenharmony_ci** the control logic to convert PCI line based interrupts into a Message
118c2ecf20Sopenharmony_ci** Signaled Interrupt (aka Transaction Based Interrupt, TBI).
128c2ecf20Sopenharmony_ci**
138c2ecf20Sopenharmony_ci** Acronyms
148c2ecf20Sopenharmony_ci** --------
158c2ecf20Sopenharmony_ci** HPA  Hard Physical Address (aka MMIO address)
168c2ecf20Sopenharmony_ci** IRQ  Interrupt ReQuest. Implies Line based interrupt.
178c2ecf20Sopenharmony_ci** IRT	Interrupt Routing Table (provided by PAT firmware)
188c2ecf20Sopenharmony_ci** IRdT Interrupt Redirection Table. IRQ line to TXN ADDR/DATA
198c2ecf20Sopenharmony_ci**      table which is implemented in I/O SAPIC.
208c2ecf20Sopenharmony_ci** ISR  Interrupt Service Routine. aka Interrupt handler.
218c2ecf20Sopenharmony_ci** MSI	Message Signaled Interrupt. PCI 2.2 functionality.
228c2ecf20Sopenharmony_ci**      aka Transaction Based Interrupt (or TBI).
238c2ecf20Sopenharmony_ci** PA   Precision Architecture. HP's RISC architecture.
248c2ecf20Sopenharmony_ci** RISC Reduced Instruction Set Computer.
258c2ecf20Sopenharmony_ci**
268c2ecf20Sopenharmony_ci**
278c2ecf20Sopenharmony_ci** What's a Message Signalled Interrupt?
288c2ecf20Sopenharmony_ci** -------------------------------------
298c2ecf20Sopenharmony_ci** MSI is a write transaction which targets a processor and is similar
308c2ecf20Sopenharmony_ci** to a processor write to memory or MMIO. MSIs can be generated by I/O
318c2ecf20Sopenharmony_ci** devices as well as processors and require *architecture* to work.
328c2ecf20Sopenharmony_ci**
338c2ecf20Sopenharmony_ci** PA only supports MSI. So I/O subsystems must either natively generate
348c2ecf20Sopenharmony_ci** MSIs (e.g. GSC or HP-PB) or convert line based interrupts into MSIs
358c2ecf20Sopenharmony_ci** (e.g. PCI and EISA).  IA64 supports MSIs via a "local SAPIC" which
368c2ecf20Sopenharmony_ci** acts on behalf of a processor.
378c2ecf20Sopenharmony_ci**
388c2ecf20Sopenharmony_ci** MSI allows any I/O device to interrupt any processor. This makes
398c2ecf20Sopenharmony_ci** load balancing of the interrupt processing possible on an SMP platform.
408c2ecf20Sopenharmony_ci** Interrupts are also ordered WRT to DMA data.  It's possible on I/O
418c2ecf20Sopenharmony_ci** coherent systems to completely eliminate PIO reads from the interrupt
428c2ecf20Sopenharmony_ci** path. The device and driver must be designed and implemented to
438c2ecf20Sopenharmony_ci** guarantee all DMA has been issued (issues about atomicity here)
448c2ecf20Sopenharmony_ci** before the MSI is issued. I/O status can then safely be read from
458c2ecf20Sopenharmony_ci** DMA'd data by the ISR.
468c2ecf20Sopenharmony_ci**
478c2ecf20Sopenharmony_ci**
488c2ecf20Sopenharmony_ci** PA Firmware
498c2ecf20Sopenharmony_ci** -----------
508c2ecf20Sopenharmony_ci** PA-RISC platforms have two fundamentally different types of firmware.
518c2ecf20Sopenharmony_ci** For PCI devices, "Legacy" PDC initializes the "INTERRUPT_LINE" register
528c2ecf20Sopenharmony_ci** and BARs similar to a traditional PC BIOS.
538c2ecf20Sopenharmony_ci** The newer "PAT" firmware supports PDC calls which return tables.
548c2ecf20Sopenharmony_ci** PAT firmware only initializes the PCI Console and Boot interface.
558c2ecf20Sopenharmony_ci** With these tables, the OS can program all other PCI devices.
568c2ecf20Sopenharmony_ci**
578c2ecf20Sopenharmony_ci** One such PAT PDC call returns the "Interrupt Routing Table" (IRT).
588c2ecf20Sopenharmony_ci** The IRT maps each PCI slot's INTA-D "output" line to an I/O SAPIC
598c2ecf20Sopenharmony_ci** input line.  If the IRT is not available, this driver assumes
608c2ecf20Sopenharmony_ci** INTERRUPT_LINE register has been programmed by firmware. The latter
618c2ecf20Sopenharmony_ci** case also means online addition of PCI cards can NOT be supported
628c2ecf20Sopenharmony_ci** even if HW support is present.
638c2ecf20Sopenharmony_ci**
648c2ecf20Sopenharmony_ci** All platforms with PAT firmware to date (Oct 1999) use one Interrupt
658c2ecf20Sopenharmony_ci** Routing Table for the entire platform.
668c2ecf20Sopenharmony_ci**
678c2ecf20Sopenharmony_ci** Where's the iosapic?
688c2ecf20Sopenharmony_ci** --------------------
698c2ecf20Sopenharmony_ci** I/O sapic is part of the "Core Electronics Complex". And on HP platforms
708c2ecf20Sopenharmony_ci** it's integrated as part of the PCI bus adapter, "lba".  So no bus walk
718c2ecf20Sopenharmony_ci** will discover I/O Sapic. I/O Sapic driver learns about each device
728c2ecf20Sopenharmony_ci** when lba driver advertises the presence of the I/O sapic by calling
738c2ecf20Sopenharmony_ci** iosapic_register().
748c2ecf20Sopenharmony_ci**
758c2ecf20Sopenharmony_ci**
768c2ecf20Sopenharmony_ci** IRQ handling notes
778c2ecf20Sopenharmony_ci** ------------------
788c2ecf20Sopenharmony_ci** The IO-SAPIC can indicate to the CPU which interrupt was asserted.
798c2ecf20Sopenharmony_ci** So, unlike the GSC-ASIC and Dino, we allocate one CPU interrupt per
808c2ecf20Sopenharmony_ci** IO-SAPIC interrupt and call the device driver's handler directly.
818c2ecf20Sopenharmony_ci** The IO-SAPIC driver hijacks the CPU interrupt handler so it can
828c2ecf20Sopenharmony_ci** issue the End Of Interrupt command to the IO-SAPIC.
838c2ecf20Sopenharmony_ci**
848c2ecf20Sopenharmony_ci** Overview of exported iosapic functions
858c2ecf20Sopenharmony_ci** --------------------------------------
868c2ecf20Sopenharmony_ci** (caveat: code isn't finished yet - this is just the plan)
878c2ecf20Sopenharmony_ci**
888c2ecf20Sopenharmony_ci** iosapic_init:
898c2ecf20Sopenharmony_ci**   o initialize globals (lock, etc)
908c2ecf20Sopenharmony_ci**   o try to read IRT. Presence of IRT determines if this is
918c2ecf20Sopenharmony_ci**     a PAT platform or not.
928c2ecf20Sopenharmony_ci**
938c2ecf20Sopenharmony_ci** iosapic_register():
948c2ecf20Sopenharmony_ci**   o create iosapic_info instance data structure
958c2ecf20Sopenharmony_ci**   o allocate vector_info array for this iosapic
968c2ecf20Sopenharmony_ci**   o initialize vector_info - read corresponding IRdT?
978c2ecf20Sopenharmony_ci**
988c2ecf20Sopenharmony_ci** iosapic_xlate_pin: (only called by fixup_irq for PAT platform)
998c2ecf20Sopenharmony_ci**   o intr_pin = read cfg (INTERRUPT_PIN);
1008c2ecf20Sopenharmony_ci**   o if (device under PCI-PCI bridge)
1018c2ecf20Sopenharmony_ci**               translate slot/pin
1028c2ecf20Sopenharmony_ci**
1038c2ecf20Sopenharmony_ci** iosapic_fixup_irq:
1048c2ecf20Sopenharmony_ci**   o if PAT platform (IRT present)
1058c2ecf20Sopenharmony_ci**	   intr_pin = iosapic_xlate_pin(isi,pcidev):
1068c2ecf20Sopenharmony_ci**         intr_line = find IRT entry(isi, PCI_SLOT(pcidev), intr_pin)
1078c2ecf20Sopenharmony_ci**         save IRT entry into vector_info later
1088c2ecf20Sopenharmony_ci**         write cfg INTERRUPT_LINE (with intr_line)?
1098c2ecf20Sopenharmony_ci**     else
1108c2ecf20Sopenharmony_ci**         intr_line = pcidev->irq
1118c2ecf20Sopenharmony_ci**         IRT pointer = NULL
1128c2ecf20Sopenharmony_ci**     endif
1138c2ecf20Sopenharmony_ci**   o locate vector_info (needs: isi, intr_line)
1148c2ecf20Sopenharmony_ci**   o allocate processor "irq" and get txn_addr/data
1158c2ecf20Sopenharmony_ci**   o request_irq(processor_irq,  iosapic_interrupt, vector_info,...)
1168c2ecf20Sopenharmony_ci**
1178c2ecf20Sopenharmony_ci** iosapic_enable_irq:
1188c2ecf20Sopenharmony_ci**   o clear any pending IRQ on that line
1198c2ecf20Sopenharmony_ci**   o enable IRdT - call enable_irq(vector[line]->processor_irq)
1208c2ecf20Sopenharmony_ci**   o write EOI in case line is already asserted.
1218c2ecf20Sopenharmony_ci**
1228c2ecf20Sopenharmony_ci** iosapic_disable_irq:
1238c2ecf20Sopenharmony_ci**   o disable IRdT - call disable_irq(vector[line]->processor_irq)
1248c2ecf20Sopenharmony_ci*/
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci#include <linux/pci.h>
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci#include <asm/pdc.h>
1298c2ecf20Sopenharmony_ci#include <asm/pdcpat.h>
1308c2ecf20Sopenharmony_ci#ifdef CONFIG_SUPERIO
1318c2ecf20Sopenharmony_ci#include <asm/superio.h>
1328c2ecf20Sopenharmony_ci#endif
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci#include <asm/ropes.h>
1358c2ecf20Sopenharmony_ci#include "iosapic_private.h"
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci#define MODULE_NAME "iosapic"
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* "local" compile flags */
1408c2ecf20Sopenharmony_ci#undef PCI_BRIDGE_FUNCS
1418c2ecf20Sopenharmony_ci#undef DEBUG_IOSAPIC
1428c2ecf20Sopenharmony_ci#undef DEBUG_IOSAPIC_IRT
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci#ifdef DEBUG_IOSAPIC
1468c2ecf20Sopenharmony_ci#define DBG(x...) printk(x)
1478c2ecf20Sopenharmony_ci#else /* DEBUG_IOSAPIC */
1488c2ecf20Sopenharmony_ci#define DBG(x...)
1498c2ecf20Sopenharmony_ci#endif /* DEBUG_IOSAPIC */
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci#ifdef DEBUG_IOSAPIC_IRT
1528c2ecf20Sopenharmony_ci#define DBG_IRT(x...) printk(x)
1538c2ecf20Sopenharmony_ci#else
1548c2ecf20Sopenharmony_ci#define DBG_IRT(x...)
1558c2ecf20Sopenharmony_ci#endif
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
1588c2ecf20Sopenharmony_ci#define COMPARE_IRTE_ADDR(irte, hpa)	((irte)->dest_iosapic_addr == (hpa))
1598c2ecf20Sopenharmony_ci#else
1608c2ecf20Sopenharmony_ci#define COMPARE_IRTE_ADDR(irte, hpa)	\
1618c2ecf20Sopenharmony_ci		((irte)->dest_iosapic_addr == ((hpa) | 0xffffffff00000000ULL))
1628c2ecf20Sopenharmony_ci#endif
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci#define IOSAPIC_REG_SELECT              0x00
1658c2ecf20Sopenharmony_ci#define IOSAPIC_REG_WINDOW              0x10
1668c2ecf20Sopenharmony_ci#define IOSAPIC_REG_EOI                 0x40
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci#define IOSAPIC_REG_VERSION		0x1
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci#define IOSAPIC_IRDT_ENTRY(idx)		(0x10+(idx)*2)
1718c2ecf20Sopenharmony_ci#define IOSAPIC_IRDT_ENTRY_HI(idx)	(0x11+(idx)*2)
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic inline unsigned int iosapic_read(void __iomem *iosapic, unsigned int reg)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	writel(reg, iosapic + IOSAPIC_REG_SELECT);
1768c2ecf20Sopenharmony_ci	return readl(iosapic + IOSAPIC_REG_WINDOW);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic inline void iosapic_write(void __iomem *iosapic, unsigned int reg, u32 val)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	writel(reg, iosapic + IOSAPIC_REG_SELECT);
1828c2ecf20Sopenharmony_ci	writel(val, iosapic + IOSAPIC_REG_WINDOW);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci#define IOSAPIC_VERSION_MASK	0x000000ff
1868c2ecf20Sopenharmony_ci#define	IOSAPIC_VERSION(ver)	((int) (ver & IOSAPIC_VERSION_MASK))
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci#define IOSAPIC_MAX_ENTRY_MASK          0x00ff0000
1898c2ecf20Sopenharmony_ci#define IOSAPIC_MAX_ENTRY_SHIFT         0x10
1908c2ecf20Sopenharmony_ci#define	IOSAPIC_IRDT_MAX_ENTRY(ver)	\
1918c2ecf20Sopenharmony_ci	(int) (((ver) & IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT)
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci/* bits in the "low" I/O Sapic IRdT entry */
1948c2ecf20Sopenharmony_ci#define IOSAPIC_IRDT_ENABLE       0x10000
1958c2ecf20Sopenharmony_ci#define IOSAPIC_IRDT_PO_LOW       0x02000
1968c2ecf20Sopenharmony_ci#define IOSAPIC_IRDT_LEVEL_TRIG   0x08000
1978c2ecf20Sopenharmony_ci#define IOSAPIC_IRDT_MODE_LPRI    0x00100
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/* bits in the "high" I/O Sapic IRdT entry */
2008c2ecf20Sopenharmony_ci#define IOSAPIC_IRDT_ID_EID_SHIFT              0x10
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(iosapic_lock);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic inline void iosapic_eoi(__le32 __iomem *addr, __le32 data)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	__raw_writel((__force u32)data, addr);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci/*
2118c2ecf20Sopenharmony_ci** REVISIT: future platforms may have more than one IRT.
2128c2ecf20Sopenharmony_ci** If so, the following three fields form a structure which
2138c2ecf20Sopenharmony_ci** then be linked into a list. Names are chosen to make searching
2148c2ecf20Sopenharmony_ci** for them easy - not necessarily accurate (eg "cell").
2158c2ecf20Sopenharmony_ci**
2168c2ecf20Sopenharmony_ci** Alternative: iosapic_info could point to the IRT it's in.
2178c2ecf20Sopenharmony_ci** iosapic_register() could search a list of IRT's.
2188c2ecf20Sopenharmony_ci*/
2198c2ecf20Sopenharmony_cistatic struct irt_entry *irt_cell;
2208c2ecf20Sopenharmony_cistatic size_t irt_num_entry;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic struct irt_entry *iosapic_alloc_irt(int num_entries)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	unsigned long a;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* The IRT needs to be 8-byte aligned for the PDC call.
2278c2ecf20Sopenharmony_ci	 * Normally kmalloc would guarantee larger alignment, but
2288c2ecf20Sopenharmony_ci	 * if CONFIG_DEBUG_SLAB is enabled, then we can get only
2298c2ecf20Sopenharmony_ci	 * 4-byte alignment on 32-bit kernels
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	a = (unsigned long)kmalloc(sizeof(struct irt_entry) * num_entries + 8, GFP_KERNEL);
2328c2ecf20Sopenharmony_ci	a = (a + 7UL) & ~7UL;
2338c2ecf20Sopenharmony_ci	return (struct irt_entry *)a;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/**
2378c2ecf20Sopenharmony_ci * iosapic_load_irt - Fill in the interrupt routing table
2388c2ecf20Sopenharmony_ci * @cell_num: The cell number of the CPU we're currently executing on
2398c2ecf20Sopenharmony_ci * @irt: The address to place the new IRT at
2408c2ecf20Sopenharmony_ci * @return The number of entries found
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * The "Get PCI INT Routing Table Size" option returns the number of
2438c2ecf20Sopenharmony_ci * entries in the PCI interrupt routing table for the cell specified
2448c2ecf20Sopenharmony_ci * in the cell_number argument.  The cell number must be for a cell
2458c2ecf20Sopenharmony_ci * within the caller's protection domain.
2468c2ecf20Sopenharmony_ci *
2478c2ecf20Sopenharmony_ci * The "Get PCI INT Routing Table" option returns, for the cell
2488c2ecf20Sopenharmony_ci * specified in the cell_number argument, the PCI interrupt routing
2498c2ecf20Sopenharmony_ci * table in the caller allocated memory pointed to by mem_addr.
2508c2ecf20Sopenharmony_ci * We assume the IRT only contains entries for I/O SAPIC and
2518c2ecf20Sopenharmony_ci * calculate the size based on the size of I/O sapic entries.
2528c2ecf20Sopenharmony_ci *
2538c2ecf20Sopenharmony_ci * The PCI interrupt routing table entry format is derived from the
2548c2ecf20Sopenharmony_ci * IA64 SAL Specification 2.4.   The PCI interrupt routing table defines
2558c2ecf20Sopenharmony_ci * the routing of PCI interrupt signals between the PCI device output
2568c2ecf20Sopenharmony_ci * "pins" and the IO SAPICs' input "lines" (including core I/O PCI
2578c2ecf20Sopenharmony_ci * devices).  This table does NOT include information for devices/slots
2588c2ecf20Sopenharmony_ci * behind PCI to PCI bridges. See PCI to PCI Bridge Architecture Spec.
2598c2ecf20Sopenharmony_ci * for the architected method of routing of IRQ's behind PPB's.
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int __init
2648c2ecf20Sopenharmony_ciiosapic_load_irt(unsigned long cell_num, struct irt_entry **irt)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	long status;              /* PDC return value status */
2678c2ecf20Sopenharmony_ci	struct irt_entry *table;  /* start of interrupt routing tbl */
2688c2ecf20Sopenharmony_ci	unsigned long num_entries = 0UL;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	BUG_ON(!irt);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (is_pdc_pat()) {
2738c2ecf20Sopenharmony_ci		/* Use pat pdc routine to get interrupt routing table size */
2748c2ecf20Sopenharmony_ci		DBG("calling get_irt_size (cell %ld)\n", cell_num);
2758c2ecf20Sopenharmony_ci		status = pdc_pat_get_irt_size(&num_entries, cell_num);
2768c2ecf20Sopenharmony_ci		DBG("get_irt_size: %ld\n", status);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		BUG_ON(status != PDC_OK);
2798c2ecf20Sopenharmony_ci		BUG_ON(num_entries == 0);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		/*
2828c2ecf20Sopenharmony_ci		** allocate memory for interrupt routing table
2838c2ecf20Sopenharmony_ci		** This interface isn't really right. We are assuming
2848c2ecf20Sopenharmony_ci		** the contents of the table are exclusively
2858c2ecf20Sopenharmony_ci		** for I/O sapic devices.
2868c2ecf20Sopenharmony_ci		*/
2878c2ecf20Sopenharmony_ci		table = iosapic_alloc_irt(num_entries);
2888c2ecf20Sopenharmony_ci		if (table == NULL) {
2898c2ecf20Sopenharmony_ci			printk(KERN_WARNING MODULE_NAME ": read_irt : can "
2908c2ecf20Sopenharmony_ci					"not alloc mem for IRT\n");
2918c2ecf20Sopenharmony_ci			return 0;
2928c2ecf20Sopenharmony_ci		}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		/* get PCI INT routing table */
2958c2ecf20Sopenharmony_ci		status = pdc_pat_get_irt(table, cell_num);
2968c2ecf20Sopenharmony_ci		DBG("pdc_pat_get_irt: %ld\n", status);
2978c2ecf20Sopenharmony_ci		WARN_ON(status != PDC_OK);
2988c2ecf20Sopenharmony_ci	} else {
2998c2ecf20Sopenharmony_ci		/*
3008c2ecf20Sopenharmony_ci		** C3000/J5000 (and similar) platforms with Sprockets PDC
3018c2ecf20Sopenharmony_ci		** will return exactly one IRT for all iosapics.
3028c2ecf20Sopenharmony_ci		** So if we have one, don't need to get it again.
3038c2ecf20Sopenharmony_ci		*/
3048c2ecf20Sopenharmony_ci		if (irt_cell)
3058c2ecf20Sopenharmony_ci			return 0;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		/* Should be using the Elroy's HPA, but it's ignored anyway */
3088c2ecf20Sopenharmony_ci		status = pdc_pci_irt_size(&num_entries, 0);
3098c2ecf20Sopenharmony_ci		DBG("pdc_pci_irt_size: %ld\n", status);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		if (status != PDC_OK) {
3128c2ecf20Sopenharmony_ci			/* Not a "legacy" system with I/O SAPIC either */
3138c2ecf20Sopenharmony_ci			return 0;
3148c2ecf20Sopenharmony_ci		}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		BUG_ON(num_entries == 0);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		table = iosapic_alloc_irt(num_entries);
3198c2ecf20Sopenharmony_ci		if (!table) {
3208c2ecf20Sopenharmony_ci			printk(KERN_WARNING MODULE_NAME ": read_irt : can "
3218c2ecf20Sopenharmony_ci					"not alloc mem for IRT\n");
3228c2ecf20Sopenharmony_ci			return 0;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		/* HPA ignored by this call too. */
3268c2ecf20Sopenharmony_ci		status = pdc_pci_irt(num_entries, 0, table);
3278c2ecf20Sopenharmony_ci		BUG_ON(status != PDC_OK);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* return interrupt table address */
3318c2ecf20Sopenharmony_ci	*irt = table;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci#ifdef DEBUG_IOSAPIC_IRT
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct irt_entry *p = table;
3368c2ecf20Sopenharmony_ci	int i;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	printk(MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num);
3398c2ecf20Sopenharmony_ci	printk(MODULE_NAME " start = 0x%p num_entries %ld entry_size %d\n",
3408c2ecf20Sopenharmony_ci		table,
3418c2ecf20Sopenharmony_ci		num_entries,
3428c2ecf20Sopenharmony_ci		(int) sizeof(struct irt_entry));
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	for (i = 0 ; i < num_entries ; i++, p++) {
3458c2ecf20Sopenharmony_ci		printk(MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n",
3468c2ecf20Sopenharmony_ci		p->entry_type, p->entry_length, p->interrupt_type,
3478c2ecf20Sopenharmony_ci		p->polarity_trigger, p->src_bus_irq_devno, p->src_bus_id,
3488c2ecf20Sopenharmony_ci		p->src_seg_id, p->dest_iosapic_intin,
3498c2ecf20Sopenharmony_ci		((u32 *) p)[2],
3508c2ecf20Sopenharmony_ci		((u32 *) p)[3]
3518c2ecf20Sopenharmony_ci		);
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci#endif /* DEBUG_IOSAPIC_IRT */
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return num_entries;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_civoid __init iosapic_init(void)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	unsigned long cell = 0;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	DBG("iosapic_init()\n");
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci#ifdef __LP64__
3688c2ecf20Sopenharmony_ci	if (is_pdc_pat()) {
3698c2ecf20Sopenharmony_ci		int status;
3708c2ecf20Sopenharmony_ci		struct pdc_pat_cell_num cell_info;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		status = pdc_pat_cell_get_number(&cell_info);
3738c2ecf20Sopenharmony_ci		if (status == PDC_OK) {
3748c2ecf20Sopenharmony_ci			cell = cell_info.cell_num;
3758c2ecf20Sopenharmony_ci		}
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci#endif
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* get interrupt routing table for this cell */
3808c2ecf20Sopenharmony_ci	irt_num_entry = iosapic_load_irt(cell, &irt_cell);
3818c2ecf20Sopenharmony_ci	if (irt_num_entry == 0)
3828c2ecf20Sopenharmony_ci		irt_cell = NULL;	/* old PDC w/o iosapic */
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/*
3878c2ecf20Sopenharmony_ci** Return the IRT entry in case we need to look something else up.
3888c2ecf20Sopenharmony_ci*/
3898c2ecf20Sopenharmony_cistatic struct irt_entry *
3908c2ecf20Sopenharmony_ciirt_find_irqline(struct iosapic_info *isi, u8 slot, u8 intr_pin)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	struct irt_entry *i = irt_cell;
3938c2ecf20Sopenharmony_ci	int cnt;	/* track how many entries we've looked at */
3948c2ecf20Sopenharmony_ci	u8 irq_devno = (slot << IRT_DEV_SHIFT) | (intr_pin-1);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	DBG_IRT("irt_find_irqline() SLOT %d pin %d\n", slot, intr_pin);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	for (cnt=0; cnt < irt_num_entry; cnt++, i++) {
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		/*
4018c2ecf20Sopenharmony_ci		** Validate: entry_type, entry_length, interrupt_type
4028c2ecf20Sopenharmony_ci		**
4038c2ecf20Sopenharmony_ci		** Difference between validate vs compare is the former
4048c2ecf20Sopenharmony_ci		** should print debug info and is not expected to "fail"
4058c2ecf20Sopenharmony_ci		** on current platforms.
4068c2ecf20Sopenharmony_ci		*/
4078c2ecf20Sopenharmony_ci		if (i->entry_type != IRT_IOSAPIC_TYPE) {
4088c2ecf20Sopenharmony_ci			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d type %d\n", i, cnt, i->entry_type);
4098c2ecf20Sopenharmony_ci			continue;
4108c2ecf20Sopenharmony_ci		}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		if (i->entry_length != IRT_IOSAPIC_LENGTH) {
4138c2ecf20Sopenharmony_ci			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d  length %d\n", i, cnt, i->entry_length);
4148c2ecf20Sopenharmony_ci			continue;
4158c2ecf20Sopenharmony_ci		}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci		if (i->interrupt_type != IRT_VECTORED_INTR) {
4188c2ecf20Sopenharmony_ci			DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry  %d interrupt_type %d\n", i, cnt, i->interrupt_type);
4198c2ecf20Sopenharmony_ci			continue;
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		if (!COMPARE_IRTE_ADDR(i, isi->isi_hpa))
4238c2ecf20Sopenharmony_ci			continue;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		if ((i->src_bus_irq_devno & IRT_IRQ_DEVNO_MASK) != irq_devno)
4268c2ecf20Sopenharmony_ci			continue;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		/*
4298c2ecf20Sopenharmony_ci		** Ignore: src_bus_id and rc_seg_id correlate with
4308c2ecf20Sopenharmony_ci		**         iosapic_info->isi_hpa on HP platforms.
4318c2ecf20Sopenharmony_ci		**         If needed, pass in "PFA" (aka config space addr)
4328c2ecf20Sopenharmony_ci		**         instead of slot.
4338c2ecf20Sopenharmony_ci		*/
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		/* Found it! */
4368c2ecf20Sopenharmony_ci		return i;
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	printk(KERN_WARNING MODULE_NAME ": 0x%lx : no IRT entry for slot %d, pin %d\n",
4408c2ecf20Sopenharmony_ci			isi->isi_hpa, slot, intr_pin);
4418c2ecf20Sopenharmony_ci	return NULL;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/*
4468c2ecf20Sopenharmony_ci** xlate_pin() supports the skewing of IRQ lines done by subsidiary bridges.
4478c2ecf20Sopenharmony_ci** Legacy PDC already does this translation for us and stores it in INTR_LINE.
4488c2ecf20Sopenharmony_ci**
4498c2ecf20Sopenharmony_ci** PAT PDC needs to basically do what legacy PDC does:
4508c2ecf20Sopenharmony_ci** o read PIN
4518c2ecf20Sopenharmony_ci** o adjust PIN in case device is "behind" a PPB
4528c2ecf20Sopenharmony_ci**     (eg 4-port 100BT and SCSI/LAN "Combo Card")
4538c2ecf20Sopenharmony_ci** o convert slot/pin to I/O SAPIC input line.
4548c2ecf20Sopenharmony_ci**
4558c2ecf20Sopenharmony_ci** HP platforms only support:
4568c2ecf20Sopenharmony_ci** o one level of skewing for any number of PPBs
4578c2ecf20Sopenharmony_ci** o only support PCI-PCI Bridges.
4588c2ecf20Sopenharmony_ci*/
4598c2ecf20Sopenharmony_cistatic struct irt_entry *
4608c2ecf20Sopenharmony_ciiosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	u8 intr_pin, intr_slot;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	pci_read_config_byte(pcidev, PCI_INTERRUPT_PIN, &intr_pin);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_xlate_pin(%s) SLOT %d pin %d\n",
4678c2ecf20Sopenharmony_ci		pcidev->slot_name, PCI_SLOT(pcidev->devfn), intr_pin);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (intr_pin == 0) {
4708c2ecf20Sopenharmony_ci		/* The device does NOT support/use IRQ lines.  */
4718c2ecf20Sopenharmony_ci		return NULL;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Check if pcidev behind a PPB */
4758c2ecf20Sopenharmony_ci	if (pcidev->bus->parent) {
4768c2ecf20Sopenharmony_ci		/* Convert pcidev INTR_PIN into something we
4778c2ecf20Sopenharmony_ci		** can lookup in the IRT.
4788c2ecf20Sopenharmony_ci		*/
4798c2ecf20Sopenharmony_ci#ifdef PCI_BRIDGE_FUNCS
4808c2ecf20Sopenharmony_ci		/*
4818c2ecf20Sopenharmony_ci		** Proposal #1:
4828c2ecf20Sopenharmony_ci		**
4838c2ecf20Sopenharmony_ci		** call implementation specific translation function
4848c2ecf20Sopenharmony_ci		** This is architecturally "cleaner". HP-UX doesn't
4858c2ecf20Sopenharmony_ci		** support other secondary bus types (eg. E/ISA) directly.
4868c2ecf20Sopenharmony_ci		** May be needed for other processor (eg IA64) architectures
4878c2ecf20Sopenharmony_ci		** or by some ambitous soul who wants to watch TV.
4888c2ecf20Sopenharmony_ci		*/
4898c2ecf20Sopenharmony_ci		if (pci_bridge_funcs->xlate_intr_line) {
4908c2ecf20Sopenharmony_ci			intr_pin = pci_bridge_funcs->xlate_intr_line(pcidev);
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci#else	/* PCI_BRIDGE_FUNCS */
4938c2ecf20Sopenharmony_ci		struct pci_bus *p = pcidev->bus;
4948c2ecf20Sopenharmony_ci		/*
4958c2ecf20Sopenharmony_ci		** Proposal #2:
4968c2ecf20Sopenharmony_ci		** The "pin" is skewed ((pin + dev - 1) % 4).
4978c2ecf20Sopenharmony_ci		**
4988c2ecf20Sopenharmony_ci		** This isn't very clean since I/O SAPIC must assume:
4998c2ecf20Sopenharmony_ci		**   - all platforms only have PCI busses.
5008c2ecf20Sopenharmony_ci		**   - only PCI-PCI bridge (eg not PCI-EISA, PCI-PCMCIA)
5018c2ecf20Sopenharmony_ci		**   - IRQ routing is only skewed once regardless of
5028c2ecf20Sopenharmony_ci		**     the number of PPB's between iosapic and device.
5038c2ecf20Sopenharmony_ci		**     (Bit3 expansion chassis follows this rule)
5048c2ecf20Sopenharmony_ci		**
5058c2ecf20Sopenharmony_ci		** Advantage is it's really easy to implement.
5068c2ecf20Sopenharmony_ci		*/
5078c2ecf20Sopenharmony_ci		intr_pin = pci_swizzle_interrupt_pin(pcidev, intr_pin);
5088c2ecf20Sopenharmony_ci#endif /* PCI_BRIDGE_FUNCS */
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		/*
5118c2ecf20Sopenharmony_ci		 * Locate the host slot of the PPB.
5128c2ecf20Sopenharmony_ci		 */
5138c2ecf20Sopenharmony_ci		while (p->parent->parent)
5148c2ecf20Sopenharmony_ci			p = p->parent;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		intr_slot = PCI_SLOT(p->self->devfn);
5178c2ecf20Sopenharmony_ci	} else {
5188c2ecf20Sopenharmony_ci		intr_slot = PCI_SLOT(pcidev->devfn);
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_xlate_pin:  bus %d slot %d pin %d\n",
5218c2ecf20Sopenharmony_ci			pcidev->bus->busn_res.start, intr_slot, intr_pin);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	return irt_find_irqline(isi, intr_slot, intr_pin);
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic void iosapic_rd_irt_entry(struct vector_info *vi , u32 *dp0, u32 *dp1)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct iosapic_info *isp = vi->iosapic;
5298c2ecf20Sopenharmony_ci	u8 idx = vi->irqline;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	*dp0 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY(idx));
5328c2ecf20Sopenharmony_ci	*dp1 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY_HI(idx));
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void iosapic_wr_irt_entry(struct vector_info *vi, u32 dp0, u32 dp1)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct iosapic_info *isp = vi->iosapic;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %lx 0x%x 0x%x\n",
5418c2ecf20Sopenharmony_ci		vi->irqline, isp->isi_hpa, dp0, dp1);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY(vi->irqline), dp0);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* Read the window register to flush the writes down to HW  */
5468c2ecf20Sopenharmony_ci	dp0 = readl(isp->addr+IOSAPIC_REG_WINDOW);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY_HI(vi->irqline), dp1);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	/* Read the window register to flush the writes down to HW  */
5518c2ecf20Sopenharmony_ci	dp1 = readl(isp->addr+IOSAPIC_REG_WINDOW);
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/*
5558c2ecf20Sopenharmony_ci** set_irt prepares the data (dp0, dp1) according to the vector_info
5568c2ecf20Sopenharmony_ci** and target cpu (id_eid).  dp0/dp1 are then used to program I/O SAPIC
5578c2ecf20Sopenharmony_ci** IRdT for the given "vector" (aka IRQ line).
5588c2ecf20Sopenharmony_ci*/
5598c2ecf20Sopenharmony_cistatic void
5608c2ecf20Sopenharmony_ciiosapic_set_irt_data( struct vector_info *vi, u32 *dp0, u32 *dp1)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	u32 mode = 0;
5638c2ecf20Sopenharmony_ci	struct irt_entry *p = vi->irte;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if ((p->polarity_trigger & IRT_PO_MASK) == IRT_ACTIVE_LO)
5668c2ecf20Sopenharmony_ci		mode |= IOSAPIC_IRDT_PO_LOW;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (((p->polarity_trigger >> IRT_EL_SHIFT) & IRT_EL_MASK) == IRT_LEVEL_TRIG)
5698c2ecf20Sopenharmony_ci		mode |= IOSAPIC_IRDT_LEVEL_TRIG;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	/*
5728c2ecf20Sopenharmony_ci	** IA64 REVISIT
5738c2ecf20Sopenharmony_ci	** PA doesn't support EXTINT or LPRIO bits.
5748c2ecf20Sopenharmony_ci	*/
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	*dp0 = mode | (u32) vi->txn_data;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/*
5798c2ecf20Sopenharmony_ci	** Extracting id_eid isn't a real clean way of getting it.
5808c2ecf20Sopenharmony_ci	** But the encoding is the same for both PA and IA64 platforms.
5818c2ecf20Sopenharmony_ci	*/
5828c2ecf20Sopenharmony_ci	if (is_pdc_pat()) {
5838c2ecf20Sopenharmony_ci		/*
5848c2ecf20Sopenharmony_ci		** PAT PDC just hands it to us "right".
5858c2ecf20Sopenharmony_ci		** txn_addr comes from cpu_data[x].txn_addr.
5868c2ecf20Sopenharmony_ci		*/
5878c2ecf20Sopenharmony_ci		*dp1 = (u32) (vi->txn_addr);
5888c2ecf20Sopenharmony_ci	} else {
5898c2ecf20Sopenharmony_ci		/*
5908c2ecf20Sopenharmony_ci		** eg if base_addr == 0xfffa0000),
5918c2ecf20Sopenharmony_ci		**    we want to get 0xa0ff0000.
5928c2ecf20Sopenharmony_ci		**
5938c2ecf20Sopenharmony_ci		** eid	0x0ff00000 -> 0x00ff0000
5948c2ecf20Sopenharmony_ci		** id	0x000ff000 -> 0xff000000
5958c2ecf20Sopenharmony_ci		*/
5968c2ecf20Sopenharmony_ci		*dp1 = (((u32)vi->txn_addr & 0x0ff00000) >> 4) |
5978c2ecf20Sopenharmony_ci			(((u32)vi->txn_addr & 0x000ff000) << 12);
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_set_irt_data(): 0x%x 0x%x\n", *dp0, *dp1);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic void iosapic_mask_irq(struct irq_data *d)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	unsigned long flags;
6068c2ecf20Sopenharmony_ci	struct vector_info *vi = irq_data_get_irq_chip_data(d);
6078c2ecf20Sopenharmony_ci	u32 d0, d1;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&iosapic_lock, flags);
6108c2ecf20Sopenharmony_ci	iosapic_rd_irt_entry(vi, &d0, &d1);
6118c2ecf20Sopenharmony_ci	d0 |= IOSAPIC_IRDT_ENABLE;
6128c2ecf20Sopenharmony_ci	iosapic_wr_irt_entry(vi, d0, d1);
6138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&iosapic_lock, flags);
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic void iosapic_unmask_irq(struct irq_data *d)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	struct vector_info *vi = irq_data_get_irq_chip_data(d);
6198c2ecf20Sopenharmony_ci	u32 d0, d1;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	/* data is initialized by fixup_irq */
6228c2ecf20Sopenharmony_ci	WARN_ON(vi->txn_irq  == 0);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	iosapic_set_irt_data(vi, &d0, &d1);
6258c2ecf20Sopenharmony_ci	iosapic_wr_irt_entry(vi, d0, d1);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci#ifdef DEBUG_IOSAPIC_IRT
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	u32 *t = (u32 *) ((ulong) vi->eoi_addr & ~0xffUL);
6308c2ecf20Sopenharmony_ci	printk("iosapic_enable_irq(): regs %p", vi->eoi_addr);
6318c2ecf20Sopenharmony_ci	for ( ; t < vi->eoi_addr; t++)
6328c2ecf20Sopenharmony_ci		printk(" %x", readl(t));
6338c2ecf20Sopenharmony_ci	printk("\n");
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ciprintk("iosapic_enable_irq(): sel ");
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct iosapic_info *isp = vi->iosapic;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	for (d0=0x10; d0<0x1e; d0++) {
6418c2ecf20Sopenharmony_ci		d1 = iosapic_read(isp->addr, d0);
6428c2ecf20Sopenharmony_ci		printk(" %x", d1);
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ciprintk("\n");
6468c2ecf20Sopenharmony_ci#endif
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	/*
6498c2ecf20Sopenharmony_ci	 * Issuing I/O SAPIC an EOI causes an interrupt IFF IRQ line is
6508c2ecf20Sopenharmony_ci	 * asserted.  IRQ generally should not be asserted when a driver
6518c2ecf20Sopenharmony_ci	 * enables their IRQ. It can lead to "interesting" race conditions
6528c2ecf20Sopenharmony_ci	 * in the driver initialization sequence.
6538c2ecf20Sopenharmony_ci	 */
6548c2ecf20Sopenharmony_ci	DBG(KERN_DEBUG "enable_irq(%d): eoi(%p, 0x%x)\n", d->irq,
6558c2ecf20Sopenharmony_ci			vi->eoi_addr, vi->eoi_data);
6568c2ecf20Sopenharmony_ci	iosapic_eoi(vi->eoi_addr, vi->eoi_data);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic void iosapic_eoi_irq(struct irq_data *d)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct vector_info *vi = irq_data_get_irq_chip_data(d);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	iosapic_eoi(vi->eoi_addr, vi->eoi_data);
6648c2ecf20Sopenharmony_ci	cpu_eoi_irq(d);
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
6688c2ecf20Sopenharmony_cistatic int iosapic_set_affinity_irq(struct irq_data *d,
6698c2ecf20Sopenharmony_ci				    const struct cpumask *dest, bool force)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct vector_info *vi = irq_data_get_irq_chip_data(d);
6728c2ecf20Sopenharmony_ci	u32 d0, d1, dummy_d0;
6738c2ecf20Sopenharmony_ci	unsigned long flags;
6748c2ecf20Sopenharmony_ci	int dest_cpu;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	dest_cpu = cpu_check_affinity(d, dest);
6778c2ecf20Sopenharmony_ci	if (dest_cpu < 0)
6788c2ecf20Sopenharmony_ci		return -1;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	cpumask_copy(irq_data_get_affinity_mask(d), cpumask_of(dest_cpu));
6818c2ecf20Sopenharmony_ci	vi->txn_addr = txn_affinity_addr(d->irq, dest_cpu);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	spin_lock_irqsave(&iosapic_lock, flags);
6848c2ecf20Sopenharmony_ci	/* d1 contains the destination CPU, so only want to set that
6858c2ecf20Sopenharmony_ci	 * entry */
6868c2ecf20Sopenharmony_ci	iosapic_rd_irt_entry(vi, &d0, &d1);
6878c2ecf20Sopenharmony_ci	iosapic_set_irt_data(vi, &dummy_d0, &d1);
6888c2ecf20Sopenharmony_ci	iosapic_wr_irt_entry(vi, d0, d1);
6898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&iosapic_lock, flags);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	return 0;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci#endif
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic struct irq_chip iosapic_interrupt_type = {
6968c2ecf20Sopenharmony_ci	.name		=	"IO-SAPIC-level",
6978c2ecf20Sopenharmony_ci	.irq_unmask	=	iosapic_unmask_irq,
6988c2ecf20Sopenharmony_ci	.irq_mask	=	iosapic_mask_irq,
6998c2ecf20Sopenharmony_ci	.irq_ack	=	cpu_ack_irq,
7008c2ecf20Sopenharmony_ci	.irq_eoi	=	iosapic_eoi_irq,
7018c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
7028c2ecf20Sopenharmony_ci	.irq_set_affinity =	iosapic_set_affinity_irq,
7038c2ecf20Sopenharmony_ci#endif
7048c2ecf20Sopenharmony_ci};
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ciint iosapic_fixup_irq(void *isi_obj, struct pci_dev *pcidev)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct iosapic_info *isi = isi_obj;
7098c2ecf20Sopenharmony_ci	struct irt_entry *irte = NULL;  /* only used if PAT PDC */
7108c2ecf20Sopenharmony_ci	struct vector_info *vi;
7118c2ecf20Sopenharmony_ci	int isi_line;	/* line used by device */
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (!isi) {
7148c2ecf20Sopenharmony_ci		printk(KERN_WARNING MODULE_NAME ": hpa not registered for %s\n",
7158c2ecf20Sopenharmony_ci			pci_name(pcidev));
7168c2ecf20Sopenharmony_ci		return -1;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci#ifdef CONFIG_SUPERIO
7208c2ecf20Sopenharmony_ci	/*
7218c2ecf20Sopenharmony_ci	 * HACK ALERT! (non-compliant PCI device support)
7228c2ecf20Sopenharmony_ci	 *
7238c2ecf20Sopenharmony_ci	 * All SuckyIO interrupts are routed through the PIC's on function 1.
7248c2ecf20Sopenharmony_ci	 * But SuckyIO OHCI USB controller gets an IRT entry anyway because
7258c2ecf20Sopenharmony_ci	 * it advertises INT D for INT_PIN.  Use that IRT entry to get the
7268c2ecf20Sopenharmony_ci	 * SuckyIO interrupt routing for PICs on function 1 (*BLEECCHH*).
7278c2ecf20Sopenharmony_ci	 */
7288c2ecf20Sopenharmony_ci	if (is_superio_device(pcidev)) {
7298c2ecf20Sopenharmony_ci		/* We must call superio_fixup_irq() to register the pdev */
7308c2ecf20Sopenharmony_ci		pcidev->irq = superio_fixup_irq(pcidev);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci		/* Don't return if need to program the IOSAPIC's IRT... */
7338c2ecf20Sopenharmony_ci		if (PCI_FUNC(pcidev->devfn) != SUPERIO_USB_FN)
7348c2ecf20Sopenharmony_ci			return pcidev->irq;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci#endif /* CONFIG_SUPERIO */
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* lookup IRT entry for isi/slot/pin set */
7398c2ecf20Sopenharmony_ci	irte = iosapic_xlate_pin(isi, pcidev);
7408c2ecf20Sopenharmony_ci	if (!irte) {
7418c2ecf20Sopenharmony_ci		printk("iosapic: no IRTE for %s (IRQ not connected?)\n",
7428c2ecf20Sopenharmony_ci				pci_name(pcidev));
7438c2ecf20Sopenharmony_ci		return -1;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_fixup_irq(): irte %p %x %x %x %x %x %x %x %x\n",
7468c2ecf20Sopenharmony_ci		irte,
7478c2ecf20Sopenharmony_ci		irte->entry_type,
7488c2ecf20Sopenharmony_ci		irte->entry_length,
7498c2ecf20Sopenharmony_ci		irte->polarity_trigger,
7508c2ecf20Sopenharmony_ci		irte->src_bus_irq_devno,
7518c2ecf20Sopenharmony_ci		irte->src_bus_id,
7528c2ecf20Sopenharmony_ci		irte->src_seg_id,
7538c2ecf20Sopenharmony_ci		irte->dest_iosapic_intin,
7548c2ecf20Sopenharmony_ci		(u32) irte->dest_iosapic_addr);
7558c2ecf20Sopenharmony_ci	isi_line = irte->dest_iosapic_intin;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/* get vector info for this input line */
7588c2ecf20Sopenharmony_ci	vi = isi->isi_vector + isi_line;
7598c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_fixup_irq:  line %d vi 0x%p\n", isi_line, vi);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	/* If this IRQ line has already been setup, skip it */
7628c2ecf20Sopenharmony_ci	if (vi->irte)
7638c2ecf20Sopenharmony_ci		goto out;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	vi->irte = irte;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	/*
7688c2ecf20Sopenharmony_ci	 * Allocate processor IRQ
7698c2ecf20Sopenharmony_ci	 *
7708c2ecf20Sopenharmony_ci	 * XXX/FIXME The txn_alloc_irq() code and related code should be
7718c2ecf20Sopenharmony_ci	 * moved to enable_irq(). That way we only allocate processor IRQ
7728c2ecf20Sopenharmony_ci	 * bits for devices that actually have drivers claiming them.
7738c2ecf20Sopenharmony_ci	 * Right now we assign an IRQ to every PCI device present,
7748c2ecf20Sopenharmony_ci	 * regardless of whether it's used or not.
7758c2ecf20Sopenharmony_ci	 */
7768c2ecf20Sopenharmony_ci	vi->txn_irq = txn_alloc_irq(8);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	if (vi->txn_irq < 0)
7798c2ecf20Sopenharmony_ci		panic("I/O sapic: couldn't get TXN IRQ\n");
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	/* enable_irq() will use txn_* to program IRdT */
7828c2ecf20Sopenharmony_ci	vi->txn_addr = txn_alloc_addr(vi->txn_irq);
7838c2ecf20Sopenharmony_ci	vi->txn_data = txn_alloc_data(vi->txn_irq);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI;
7868c2ecf20Sopenharmony_ci	vi->eoi_data = cpu_to_le32(vi->txn_data);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci out:
7918c2ecf20Sopenharmony_ci	pcidev->irq = vi->txn_irq;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_fixup_irq() %d:%d %x %x line %d irq %d\n",
7948c2ecf20Sopenharmony_ci		PCI_SLOT(pcidev->devfn), PCI_FUNC(pcidev->devfn),
7958c2ecf20Sopenharmony_ci		pcidev->vendor, pcidev->device, isi_line, pcidev->irq);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return pcidev->irq;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_cistatic struct iosapic_info *iosapic_list;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
8038c2ecf20Sopenharmony_ciint iosapic_serial_irq(struct parisc_device *dev)
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci	struct iosapic_info *isi;
8068c2ecf20Sopenharmony_ci	struct irt_entry *irte;
8078c2ecf20Sopenharmony_ci	struct vector_info *vi;
8088c2ecf20Sopenharmony_ci	int cnt;
8098c2ecf20Sopenharmony_ci	int intin;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	intin = (dev->mod_info >> 24) & 15;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* lookup IRT entry for isi/slot/pin set */
8148c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < irt_num_entry; cnt++) {
8158c2ecf20Sopenharmony_ci		irte = &irt_cell[cnt];
8168c2ecf20Sopenharmony_ci		if (COMPARE_IRTE_ADDR(irte, dev->mod0) &&
8178c2ecf20Sopenharmony_ci		    irte->dest_iosapic_intin == intin)
8188c2ecf20Sopenharmony_ci			break;
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci	if (cnt >= irt_num_entry)
8218c2ecf20Sopenharmony_ci		return 0; /* no irq found, force polling */
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_serial_irq(): irte %p %x %x %x %x %x %x %x %x\n",
8248c2ecf20Sopenharmony_ci		irte,
8258c2ecf20Sopenharmony_ci		irte->entry_type,
8268c2ecf20Sopenharmony_ci		irte->entry_length,
8278c2ecf20Sopenharmony_ci		irte->polarity_trigger,
8288c2ecf20Sopenharmony_ci		irte->src_bus_irq_devno,
8298c2ecf20Sopenharmony_ci		irte->src_bus_id,
8308c2ecf20Sopenharmony_ci		irte->src_seg_id,
8318c2ecf20Sopenharmony_ci		irte->dest_iosapic_intin,
8328c2ecf20Sopenharmony_ci		(u32) irte->dest_iosapic_addr);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* search for iosapic */
8358c2ecf20Sopenharmony_ci	for (isi = iosapic_list; isi; isi = isi->isi_next)
8368c2ecf20Sopenharmony_ci		if (isi->isi_hpa == dev->mod0)
8378c2ecf20Sopenharmony_ci			break;
8388c2ecf20Sopenharmony_ci	if (!isi)
8398c2ecf20Sopenharmony_ci		return 0; /* no iosapic found, force polling */
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* get vector info for this input line */
8428c2ecf20Sopenharmony_ci	vi = isi->isi_vector + intin;
8438c2ecf20Sopenharmony_ci	DBG_IRT("iosapic_serial_irq:  line %d vi 0x%p\n", iosapic_intin, vi);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/* If this IRQ line has already been setup, skip it */
8468c2ecf20Sopenharmony_ci	if (vi->irte)
8478c2ecf20Sopenharmony_ci		goto out;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	vi->irte = irte;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/*
8528c2ecf20Sopenharmony_ci	 * Allocate processor IRQ
8538c2ecf20Sopenharmony_ci	 *
8548c2ecf20Sopenharmony_ci	 * XXX/FIXME The txn_alloc_irq() code and related code should be
8558c2ecf20Sopenharmony_ci	 * moved to enable_irq(). That way we only allocate processor IRQ
8568c2ecf20Sopenharmony_ci	 * bits for devices that actually have drivers claiming them.
8578c2ecf20Sopenharmony_ci	 * Right now we assign an IRQ to every PCI device present,
8588c2ecf20Sopenharmony_ci	 * regardless of whether it's used or not.
8598c2ecf20Sopenharmony_ci	 */
8608c2ecf20Sopenharmony_ci	vi->txn_irq = txn_alloc_irq(8);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (vi->txn_irq < 0)
8638c2ecf20Sopenharmony_ci		panic("I/O sapic: couldn't get TXN IRQ\n");
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	/* enable_irq() will use txn_* to program IRdT */
8668c2ecf20Sopenharmony_ci	vi->txn_addr = txn_alloc_addr(vi->txn_irq);
8678c2ecf20Sopenharmony_ci	vi->txn_data = txn_alloc_data(vi->txn_irq);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI;
8708c2ecf20Sopenharmony_ci	vi->eoi_data = cpu_to_le32(vi->txn_data);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci out:
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	return vi->txn_irq;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iosapic_serial_irq);
8798c2ecf20Sopenharmony_ci#endif
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci/*
8838c2ecf20Sopenharmony_ci** squirrel away the I/O Sapic Version
8848c2ecf20Sopenharmony_ci*/
8858c2ecf20Sopenharmony_cistatic unsigned int
8868c2ecf20Sopenharmony_ciiosapic_rd_version(struct iosapic_info *isi)
8878c2ecf20Sopenharmony_ci{
8888c2ecf20Sopenharmony_ci	return iosapic_read(isi->addr, IOSAPIC_REG_VERSION);
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci/*
8938c2ecf20Sopenharmony_ci** iosapic_register() is called by "drivers" with an integrated I/O SAPIC.
8948c2ecf20Sopenharmony_ci** Caller must be certain they have an I/O SAPIC and know its MMIO address.
8958c2ecf20Sopenharmony_ci**
8968c2ecf20Sopenharmony_ci**	o allocate iosapic_info and add it to the list
8978c2ecf20Sopenharmony_ci**	o read iosapic version and squirrel that away
8988c2ecf20Sopenharmony_ci**	o read size of IRdT.
8998c2ecf20Sopenharmony_ci**	o allocate and initialize isi_vector[]
9008c2ecf20Sopenharmony_ci**	o allocate irq region
9018c2ecf20Sopenharmony_ci*/
9028c2ecf20Sopenharmony_civoid *iosapic_register(unsigned long hpa)
9038c2ecf20Sopenharmony_ci{
9048c2ecf20Sopenharmony_ci	struct iosapic_info *isi = NULL;
9058c2ecf20Sopenharmony_ci	struct irt_entry *irte = irt_cell;
9068c2ecf20Sopenharmony_ci	struct vector_info *vip;
9078c2ecf20Sopenharmony_ci	int cnt;	/* track how many entries we've looked at */
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	/*
9108c2ecf20Sopenharmony_ci	 * Astro based platforms can only support PCI OLARD if they implement
9118c2ecf20Sopenharmony_ci	 * PAT PDC.  Legacy PDC omits LBAs with no PCI devices from the IRT.
9128c2ecf20Sopenharmony_ci	 * Search the IRT and ignore iosapic's which aren't in the IRT.
9138c2ecf20Sopenharmony_ci	 */
9148c2ecf20Sopenharmony_ci	for (cnt=0; cnt < irt_num_entry; cnt++, irte++) {
9158c2ecf20Sopenharmony_ci		WARN_ON(IRT_IOSAPIC_TYPE != irte->entry_type);
9168c2ecf20Sopenharmony_ci		if (COMPARE_IRTE_ADDR(irte, hpa))
9178c2ecf20Sopenharmony_ci			break;
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	if (cnt >= irt_num_entry) {
9218c2ecf20Sopenharmony_ci		DBG("iosapic_register() ignoring 0x%lx (NOT FOUND)\n", hpa);
9228c2ecf20Sopenharmony_ci		return NULL;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	isi = kzalloc(sizeof(struct iosapic_info), GFP_KERNEL);
9268c2ecf20Sopenharmony_ci	if (!isi) {
9278c2ecf20Sopenharmony_ci		BUG();
9288c2ecf20Sopenharmony_ci		return NULL;
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	isi->addr = ioremap(hpa, 4096);
9328c2ecf20Sopenharmony_ci	isi->isi_hpa = hpa;
9338c2ecf20Sopenharmony_ci	isi->isi_version = iosapic_rd_version(isi);
9348c2ecf20Sopenharmony_ci	isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1;
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	vip = isi->isi_vector = kcalloc(isi->isi_num_vectors,
9378c2ecf20Sopenharmony_ci					sizeof(struct vector_info), GFP_KERNEL);
9388c2ecf20Sopenharmony_ci	if (vip == NULL) {
9398c2ecf20Sopenharmony_ci		kfree(isi);
9408c2ecf20Sopenharmony_ci		return NULL;
9418c2ecf20Sopenharmony_ci	}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	for (cnt=0; cnt < isi->isi_num_vectors; cnt++, vip++) {
9448c2ecf20Sopenharmony_ci		vip->irqline = (unsigned char) cnt;
9458c2ecf20Sopenharmony_ci		vip->iosapic = isi;
9468c2ecf20Sopenharmony_ci	}
9478c2ecf20Sopenharmony_ci	isi->isi_next = iosapic_list;
9488c2ecf20Sopenharmony_ci	iosapic_list = isi;
9498c2ecf20Sopenharmony_ci	return isi;
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci#ifdef DEBUG_IOSAPIC
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_cistatic void
9568c2ecf20Sopenharmony_ciiosapic_prt_irt(void *irt, long num_entry)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	unsigned int i, *irp = (unsigned int *) irt;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	printk(KERN_DEBUG MODULE_NAME ": Interrupt Routing Table (%lx entries)\n", num_entry);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	for (i=0; i<num_entry; i++, irp += 4) {
9648c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%p : %2d %.8x %.8x %.8x %.8x\n",
9658c2ecf20Sopenharmony_ci					irp, i, irp[0], irp[1], irp[2], irp[3]);
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistatic void
9718c2ecf20Sopenharmony_ciiosapic_prt_vi(struct vector_info *vi)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	printk(KERN_DEBUG MODULE_NAME ": vector_info[%d] is at %p\n", vi->irqline, vi);
9748c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\tstatus:	 %.4x\n", vi->status);
9758c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\ttxn_irq:  %d\n",  vi->txn_irq);
9768c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\ttxn_addr: %lx\n", vi->txn_addr);
9778c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\ttxn_data: %lx\n", vi->txn_data);
9788c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\teoi_addr: %p\n",  vi->eoi_addr);
9798c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\teoi_data: %x\n",  vi->eoi_data);
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cistatic void
9848c2ecf20Sopenharmony_ciiosapic_prt_isi(struct iosapic_info *isi)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	printk(KERN_DEBUG MODULE_NAME ": io_sapic_info at %p\n", isi);
9878c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\tisi_hpa:       %lx\n", isi->isi_hpa);
9888c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\tisi_status:    %x\n", isi->isi_status);
9898c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\tisi_version:   %x\n", isi->isi_version);
9908c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\t\tisi_vector:    %p\n", isi->isi_vector);
9918c2ecf20Sopenharmony_ci}
9928c2ecf20Sopenharmony_ci#endif /* DEBUG_IOSAPIC */
993