162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci** I/O Sapic Driver - PCI interrupt line support 462306a36Sopenharmony_ci** 562306a36Sopenharmony_ci** (c) Copyright 1999 Grant Grundler 662306a36Sopenharmony_ci** (c) Copyright 1999 Hewlett-Packard Company 762306a36Sopenharmony_ci** 862306a36Sopenharmony_ci** 962306a36Sopenharmony_ci** The I/O sapic driver manages the Interrupt Redirection Table which is 1062306a36Sopenharmony_ci** the control logic to convert PCI line based interrupts into a Message 1162306a36Sopenharmony_ci** Signaled Interrupt (aka Transaction Based Interrupt, TBI). 1262306a36Sopenharmony_ci** 1362306a36Sopenharmony_ci** Acronyms 1462306a36Sopenharmony_ci** -------- 1562306a36Sopenharmony_ci** HPA Hard Physical Address (aka MMIO address) 1662306a36Sopenharmony_ci** IRQ Interrupt ReQuest. Implies Line based interrupt. 1762306a36Sopenharmony_ci** IRT Interrupt Routing Table (provided by PAT firmware) 1862306a36Sopenharmony_ci** IRdT Interrupt Redirection Table. IRQ line to TXN ADDR/DATA 1962306a36Sopenharmony_ci** table which is implemented in I/O SAPIC. 2062306a36Sopenharmony_ci** ISR Interrupt Service Routine. aka Interrupt handler. 2162306a36Sopenharmony_ci** MSI Message Signaled Interrupt. PCI 2.2 functionality. 2262306a36Sopenharmony_ci** aka Transaction Based Interrupt (or TBI). 2362306a36Sopenharmony_ci** PA Precision Architecture. HP's RISC architecture. 2462306a36Sopenharmony_ci** RISC Reduced Instruction Set Computer. 2562306a36Sopenharmony_ci** 2662306a36Sopenharmony_ci** 2762306a36Sopenharmony_ci** What's a Message Signalled Interrupt? 2862306a36Sopenharmony_ci** ------------------------------------- 2962306a36Sopenharmony_ci** MSI is a write transaction which targets a processor and is similar 3062306a36Sopenharmony_ci** to a processor write to memory or MMIO. MSIs can be generated by I/O 3162306a36Sopenharmony_ci** devices as well as processors and require *architecture* to work. 3262306a36Sopenharmony_ci** 3362306a36Sopenharmony_ci** PA only supports MSI. So I/O subsystems must either natively generate 3462306a36Sopenharmony_ci** MSIs (e.g. GSC or HP-PB) or convert line based interrupts into MSIs 3562306a36Sopenharmony_ci** (e.g. PCI and EISA). IA64 supports MSIs via a "local SAPIC" which 3662306a36Sopenharmony_ci** acts on behalf of a processor. 3762306a36Sopenharmony_ci** 3862306a36Sopenharmony_ci** MSI allows any I/O device to interrupt any processor. This makes 3962306a36Sopenharmony_ci** load balancing of the interrupt processing possible on an SMP platform. 4062306a36Sopenharmony_ci** Interrupts are also ordered WRT to DMA data. It's possible on I/O 4162306a36Sopenharmony_ci** coherent systems to completely eliminate PIO reads from the interrupt 4262306a36Sopenharmony_ci** path. The device and driver must be designed and implemented to 4362306a36Sopenharmony_ci** guarantee all DMA has been issued (issues about atomicity here) 4462306a36Sopenharmony_ci** before the MSI is issued. I/O status can then safely be read from 4562306a36Sopenharmony_ci** DMA'd data by the ISR. 4662306a36Sopenharmony_ci** 4762306a36Sopenharmony_ci** 4862306a36Sopenharmony_ci** PA Firmware 4962306a36Sopenharmony_ci** ----------- 5062306a36Sopenharmony_ci** PA-RISC platforms have two fundamentally different types of firmware. 5162306a36Sopenharmony_ci** For PCI devices, "Legacy" PDC initializes the "INTERRUPT_LINE" register 5262306a36Sopenharmony_ci** and BARs similar to a traditional PC BIOS. 5362306a36Sopenharmony_ci** The newer "PAT" firmware supports PDC calls which return tables. 5462306a36Sopenharmony_ci** PAT firmware only initializes the PCI Console and Boot interface. 5562306a36Sopenharmony_ci** With these tables, the OS can program all other PCI devices. 5662306a36Sopenharmony_ci** 5762306a36Sopenharmony_ci** One such PAT PDC call returns the "Interrupt Routing Table" (IRT). 5862306a36Sopenharmony_ci** The IRT maps each PCI slot's INTA-D "output" line to an I/O SAPIC 5962306a36Sopenharmony_ci** input line. If the IRT is not available, this driver assumes 6062306a36Sopenharmony_ci** INTERRUPT_LINE register has been programmed by firmware. The latter 6162306a36Sopenharmony_ci** case also means online addition of PCI cards can NOT be supported 6262306a36Sopenharmony_ci** even if HW support is present. 6362306a36Sopenharmony_ci** 6462306a36Sopenharmony_ci** All platforms with PAT firmware to date (Oct 1999) use one Interrupt 6562306a36Sopenharmony_ci** Routing Table for the entire platform. 6662306a36Sopenharmony_ci** 6762306a36Sopenharmony_ci** Where's the iosapic? 6862306a36Sopenharmony_ci** -------------------- 6962306a36Sopenharmony_ci** I/O sapic is part of the "Core Electronics Complex". And on HP platforms 7062306a36Sopenharmony_ci** it's integrated as part of the PCI bus adapter, "lba". So no bus walk 7162306a36Sopenharmony_ci** will discover I/O Sapic. I/O Sapic driver learns about each device 7262306a36Sopenharmony_ci** when lba driver advertises the presence of the I/O sapic by calling 7362306a36Sopenharmony_ci** iosapic_register(). 7462306a36Sopenharmony_ci** 7562306a36Sopenharmony_ci** 7662306a36Sopenharmony_ci** IRQ handling notes 7762306a36Sopenharmony_ci** ------------------ 7862306a36Sopenharmony_ci** The IO-SAPIC can indicate to the CPU which interrupt was asserted. 7962306a36Sopenharmony_ci** So, unlike the GSC-ASIC and Dino, we allocate one CPU interrupt per 8062306a36Sopenharmony_ci** IO-SAPIC interrupt and call the device driver's handler directly. 8162306a36Sopenharmony_ci** The IO-SAPIC driver hijacks the CPU interrupt handler so it can 8262306a36Sopenharmony_ci** issue the End Of Interrupt command to the IO-SAPIC. 8362306a36Sopenharmony_ci** 8462306a36Sopenharmony_ci** Overview of exported iosapic functions 8562306a36Sopenharmony_ci** -------------------------------------- 8662306a36Sopenharmony_ci** (caveat: code isn't finished yet - this is just the plan) 8762306a36Sopenharmony_ci** 8862306a36Sopenharmony_ci** iosapic_init: 8962306a36Sopenharmony_ci** o initialize globals (lock, etc) 9062306a36Sopenharmony_ci** o try to read IRT. Presence of IRT determines if this is 9162306a36Sopenharmony_ci** a PAT platform or not. 9262306a36Sopenharmony_ci** 9362306a36Sopenharmony_ci** iosapic_register(): 9462306a36Sopenharmony_ci** o create iosapic_info instance data structure 9562306a36Sopenharmony_ci** o allocate vector_info array for this iosapic 9662306a36Sopenharmony_ci** o initialize vector_info - read corresponding IRdT? 9762306a36Sopenharmony_ci** 9862306a36Sopenharmony_ci** iosapic_xlate_pin: (only called by fixup_irq for PAT platform) 9962306a36Sopenharmony_ci** o intr_pin = read cfg (INTERRUPT_PIN); 10062306a36Sopenharmony_ci** o if (device under PCI-PCI bridge) 10162306a36Sopenharmony_ci** translate slot/pin 10262306a36Sopenharmony_ci** 10362306a36Sopenharmony_ci** iosapic_fixup_irq: 10462306a36Sopenharmony_ci** o if PAT platform (IRT present) 10562306a36Sopenharmony_ci** intr_pin = iosapic_xlate_pin(isi,pcidev): 10662306a36Sopenharmony_ci** intr_line = find IRT entry(isi, PCI_SLOT(pcidev), intr_pin) 10762306a36Sopenharmony_ci** save IRT entry into vector_info later 10862306a36Sopenharmony_ci** write cfg INTERRUPT_LINE (with intr_line)? 10962306a36Sopenharmony_ci** else 11062306a36Sopenharmony_ci** intr_line = pcidev->irq 11162306a36Sopenharmony_ci** IRT pointer = NULL 11262306a36Sopenharmony_ci** endif 11362306a36Sopenharmony_ci** o locate vector_info (needs: isi, intr_line) 11462306a36Sopenharmony_ci** o allocate processor "irq" and get txn_addr/data 11562306a36Sopenharmony_ci** o request_irq(processor_irq, iosapic_interrupt, vector_info,...) 11662306a36Sopenharmony_ci** 11762306a36Sopenharmony_ci** iosapic_enable_irq: 11862306a36Sopenharmony_ci** o clear any pending IRQ on that line 11962306a36Sopenharmony_ci** o enable IRdT - call enable_irq(vector[line]->processor_irq) 12062306a36Sopenharmony_ci** o write EOI in case line is already asserted. 12162306a36Sopenharmony_ci** 12262306a36Sopenharmony_ci** iosapic_disable_irq: 12362306a36Sopenharmony_ci** o disable IRdT - call disable_irq(vector[line]->processor_irq) 12462306a36Sopenharmony_ci*/ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#include <linux/pci.h> 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#include <asm/pdc.h> 12962306a36Sopenharmony_ci#include <asm/pdcpat.h> 13062306a36Sopenharmony_ci#ifdef CONFIG_SUPERIO 13162306a36Sopenharmony_ci#include <asm/superio.h> 13262306a36Sopenharmony_ci#endif 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#include <asm/ropes.h> 13562306a36Sopenharmony_ci#include "iosapic_private.h" 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define MODULE_NAME "iosapic" 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* "local" compile flags */ 14062306a36Sopenharmony_ci#undef PCI_BRIDGE_FUNCS 14162306a36Sopenharmony_ci#undef DEBUG_IOSAPIC 14262306a36Sopenharmony_ci#undef DEBUG_IOSAPIC_IRT 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#ifdef DEBUG_IOSAPIC 14662306a36Sopenharmony_ci#define DBG(x...) printk(x) 14762306a36Sopenharmony_ci#else /* DEBUG_IOSAPIC */ 14862306a36Sopenharmony_ci#define DBG(x...) 14962306a36Sopenharmony_ci#endif /* DEBUG_IOSAPIC */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#ifdef DEBUG_IOSAPIC_IRT 15262306a36Sopenharmony_ci#define DBG_IRT(x...) printk(x) 15362306a36Sopenharmony_ci#else 15462306a36Sopenharmony_ci#define DBG_IRT(x...) 15562306a36Sopenharmony_ci#endif 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#ifdef CONFIG_64BIT 15862306a36Sopenharmony_ci#define COMPARE_IRTE_ADDR(irte, hpa) ((irte)->dest_iosapic_addr == (hpa)) 15962306a36Sopenharmony_ci#else 16062306a36Sopenharmony_ci#define COMPARE_IRTE_ADDR(irte, hpa) \ 16162306a36Sopenharmony_ci ((irte)->dest_iosapic_addr == ((hpa) | 0xffffffff00000000ULL)) 16262306a36Sopenharmony_ci#endif 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define IOSAPIC_REG_SELECT 0x00 16562306a36Sopenharmony_ci#define IOSAPIC_REG_WINDOW 0x10 16662306a36Sopenharmony_ci#define IOSAPIC_REG_EOI 0x40 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci#define IOSAPIC_REG_VERSION 0x1 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci#define IOSAPIC_IRDT_ENTRY(idx) (0x10+(idx)*2) 17162306a36Sopenharmony_ci#define IOSAPIC_IRDT_ENTRY_HI(idx) (0x11+(idx)*2) 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic inline unsigned int iosapic_read(void __iomem *iosapic, unsigned int reg) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci writel(reg, iosapic + IOSAPIC_REG_SELECT); 17662306a36Sopenharmony_ci return readl(iosapic + IOSAPIC_REG_WINDOW); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic inline void iosapic_write(void __iomem *iosapic, unsigned int reg, u32 val) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci writel(reg, iosapic + IOSAPIC_REG_SELECT); 18262306a36Sopenharmony_ci writel(val, iosapic + IOSAPIC_REG_WINDOW); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#define IOSAPIC_VERSION_MASK 0x000000ff 18662306a36Sopenharmony_ci#define IOSAPIC_VERSION(ver) ((int) (ver & IOSAPIC_VERSION_MASK)) 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci#define IOSAPIC_MAX_ENTRY_MASK 0x00ff0000 18962306a36Sopenharmony_ci#define IOSAPIC_MAX_ENTRY_SHIFT 0x10 19062306a36Sopenharmony_ci#define IOSAPIC_IRDT_MAX_ENTRY(ver) \ 19162306a36Sopenharmony_ci (int) (((ver) & IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT) 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* bits in the "low" I/O Sapic IRdT entry */ 19462306a36Sopenharmony_ci#define IOSAPIC_IRDT_ENABLE 0x10000 19562306a36Sopenharmony_ci#define IOSAPIC_IRDT_PO_LOW 0x02000 19662306a36Sopenharmony_ci#define IOSAPIC_IRDT_LEVEL_TRIG 0x08000 19762306a36Sopenharmony_ci#define IOSAPIC_IRDT_MODE_LPRI 0x00100 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* bits in the "high" I/O Sapic IRdT entry */ 20062306a36Sopenharmony_ci#define IOSAPIC_IRDT_ID_EID_SHIFT 0x10 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(iosapic_lock); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic inline void iosapic_eoi(__le32 __iomem *addr, __le32 data) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci __raw_writel((__force u32)data, addr); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci** REVISIT: future platforms may have more than one IRT. 21262306a36Sopenharmony_ci** If so, the following three fields form a structure which 21362306a36Sopenharmony_ci** then be linked into a list. Names are chosen to make searching 21462306a36Sopenharmony_ci** for them easy - not necessarily accurate (eg "cell"). 21562306a36Sopenharmony_ci** 21662306a36Sopenharmony_ci** Alternative: iosapic_info could point to the IRT it's in. 21762306a36Sopenharmony_ci** iosapic_register() could search a list of IRT's. 21862306a36Sopenharmony_ci*/ 21962306a36Sopenharmony_cistatic struct irt_entry *irt_cell; 22062306a36Sopenharmony_cistatic size_t irt_num_entry; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic struct irt_entry *iosapic_alloc_irt(int num_entries) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci return kcalloc(num_entries, sizeof(struct irt_entry), GFP_KERNEL); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/** 22862306a36Sopenharmony_ci * iosapic_load_irt - Fill in the interrupt routing table 22962306a36Sopenharmony_ci * @cell_num: The cell number of the CPU we're currently executing on 23062306a36Sopenharmony_ci * @irt: The address to place the new IRT at 23162306a36Sopenharmony_ci * @return The number of entries found 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * The "Get PCI INT Routing Table Size" option returns the number of 23462306a36Sopenharmony_ci * entries in the PCI interrupt routing table for the cell specified 23562306a36Sopenharmony_ci * in the cell_number argument. The cell number must be for a cell 23662306a36Sopenharmony_ci * within the caller's protection domain. 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * The "Get PCI INT Routing Table" option returns, for the cell 23962306a36Sopenharmony_ci * specified in the cell_number argument, the PCI interrupt routing 24062306a36Sopenharmony_ci * table in the caller allocated memory pointed to by mem_addr. 24162306a36Sopenharmony_ci * We assume the IRT only contains entries for I/O SAPIC and 24262306a36Sopenharmony_ci * calculate the size based on the size of I/O sapic entries. 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * The PCI interrupt routing table entry format is derived from the 24562306a36Sopenharmony_ci * IA64 SAL Specification 2.4. The PCI interrupt routing table defines 24662306a36Sopenharmony_ci * the routing of PCI interrupt signals between the PCI device output 24762306a36Sopenharmony_ci * "pins" and the IO SAPICs' input "lines" (including core I/O PCI 24862306a36Sopenharmony_ci * devices). This table does NOT include information for devices/slots 24962306a36Sopenharmony_ci * behind PCI to PCI bridges. See PCI to PCI Bridge Architecture Spec. 25062306a36Sopenharmony_ci * for the architected method of routing of IRQ's behind PPB's. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int __init 25562306a36Sopenharmony_ciiosapic_load_irt(unsigned long cell_num, struct irt_entry **irt) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci long status; /* PDC return value status */ 25862306a36Sopenharmony_ci struct irt_entry *table; /* start of interrupt routing tbl */ 25962306a36Sopenharmony_ci unsigned long num_entries = 0UL; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci BUG_ON(!irt); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (is_pdc_pat()) { 26462306a36Sopenharmony_ci /* Use pat pdc routine to get interrupt routing table size */ 26562306a36Sopenharmony_ci DBG("calling get_irt_size (cell %ld)\n", cell_num); 26662306a36Sopenharmony_ci status = pdc_pat_get_irt_size(&num_entries, cell_num); 26762306a36Sopenharmony_ci DBG("get_irt_size: %ld\n", status); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci BUG_ON(status != PDC_OK); 27062306a36Sopenharmony_ci BUG_ON(num_entries == 0); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci ** allocate memory for interrupt routing table 27462306a36Sopenharmony_ci ** This interface isn't really right. We are assuming 27562306a36Sopenharmony_ci ** the contents of the table are exclusively 27662306a36Sopenharmony_ci ** for I/O sapic devices. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci table = iosapic_alloc_irt(num_entries); 27962306a36Sopenharmony_ci if (table == NULL) { 28062306a36Sopenharmony_ci printk(KERN_WARNING MODULE_NAME ": read_irt : can " 28162306a36Sopenharmony_ci "not alloc mem for IRT\n"); 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* get PCI INT routing table */ 28662306a36Sopenharmony_ci status = pdc_pat_get_irt(table, cell_num); 28762306a36Sopenharmony_ci DBG("pdc_pat_get_irt: %ld\n", status); 28862306a36Sopenharmony_ci WARN_ON(status != PDC_OK); 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci ** C3000/J5000 (and similar) platforms with Sprockets PDC 29262306a36Sopenharmony_ci ** will return exactly one IRT for all iosapics. 29362306a36Sopenharmony_ci ** So if we have one, don't need to get it again. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (irt_cell) 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Should be using the Elroy's HPA, but it's ignored anyway */ 29962306a36Sopenharmony_ci status = pdc_pci_irt_size(&num_entries, 0); 30062306a36Sopenharmony_ci DBG("pdc_pci_irt_size: %ld\n", status); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (status != PDC_OK) { 30362306a36Sopenharmony_ci /* Not a "legacy" system with I/O SAPIC either */ 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci BUG_ON(num_entries == 0); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci table = iosapic_alloc_irt(num_entries); 31062306a36Sopenharmony_ci if (!table) { 31162306a36Sopenharmony_ci printk(KERN_WARNING MODULE_NAME ": read_irt : can " 31262306a36Sopenharmony_ci "not alloc mem for IRT\n"); 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* HPA ignored by this call too. */ 31762306a36Sopenharmony_ci status = pdc_pci_irt(num_entries, 0, table); 31862306a36Sopenharmony_ci BUG_ON(status != PDC_OK); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* return interrupt table address */ 32262306a36Sopenharmony_ci *irt = table; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci#ifdef DEBUG_IOSAPIC_IRT 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct irt_entry *p = table; 32762306a36Sopenharmony_ci int i; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci printk(MODULE_NAME " Interrupt Routing Table (cell %ld)\n", cell_num); 33062306a36Sopenharmony_ci printk(MODULE_NAME " start = 0x%p num_entries %ld entry_size %d\n", 33162306a36Sopenharmony_ci table, 33262306a36Sopenharmony_ci num_entries, 33362306a36Sopenharmony_ci (int) sizeof(struct irt_entry)); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci for (i = 0 ; i < num_entries ; i++, p++) { 33662306a36Sopenharmony_ci printk(MODULE_NAME " %02x %02x %02x %02x %02x %02x %02x %02x %08x%08x\n", 33762306a36Sopenharmony_ci p->entry_type, p->entry_length, p->interrupt_type, 33862306a36Sopenharmony_ci p->polarity_trigger, p->src_bus_irq_devno, p->src_bus_id, 33962306a36Sopenharmony_ci p->src_seg_id, p->dest_iosapic_intin, 34062306a36Sopenharmony_ci ((u32 *) p)[2], 34162306a36Sopenharmony_ci ((u32 *) p)[3] 34262306a36Sopenharmony_ci ); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci#endif /* DEBUG_IOSAPIC_IRT */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return num_entries; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int __init iosapic_init(void) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci unsigned long cell = 0; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci#ifdef __LP64__ 35662306a36Sopenharmony_ci if (is_pdc_pat()) { 35762306a36Sopenharmony_ci int status; 35862306a36Sopenharmony_ci struct pdc_pat_cell_num cell_info; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci status = pdc_pat_cell_get_number(&cell_info); 36162306a36Sopenharmony_ci if (status == PDC_OK) { 36262306a36Sopenharmony_ci cell = cell_info.cell_num; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci#endif 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* get interrupt routing table for this cell */ 36862306a36Sopenharmony_ci irt_num_entry = iosapic_load_irt(cell, &irt_cell); 36962306a36Sopenharmony_ci if (irt_num_entry == 0) 37062306a36Sopenharmony_ci irt_cell = NULL; /* old PDC w/o iosapic */ 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ciarch_initcall(iosapic_init); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* 37862306a36Sopenharmony_ci** Return the IRT entry in case we need to look something else up. 37962306a36Sopenharmony_ci*/ 38062306a36Sopenharmony_cistatic struct irt_entry * 38162306a36Sopenharmony_ciirt_find_irqline(struct iosapic_info *isi, u8 slot, u8 intr_pin) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct irt_entry *i = irt_cell; 38462306a36Sopenharmony_ci int cnt; /* track how many entries we've looked at */ 38562306a36Sopenharmony_ci u8 irq_devno = (slot << IRT_DEV_SHIFT) | (intr_pin-1); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci DBG_IRT("irt_find_irqline() SLOT %d pin %d\n", slot, intr_pin); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci for (cnt=0; cnt < irt_num_entry; cnt++, i++) { 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci ** Validate: entry_type, entry_length, interrupt_type 39362306a36Sopenharmony_ci ** 39462306a36Sopenharmony_ci ** Difference between validate vs compare is the former 39562306a36Sopenharmony_ci ** should print debug info and is not expected to "fail" 39662306a36Sopenharmony_ci ** on current platforms. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci if (i->entry_type != IRT_IOSAPIC_TYPE) { 39962306a36Sopenharmony_ci DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d type %d\n", i, cnt, i->entry_type); 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (i->entry_length != IRT_IOSAPIC_LENGTH) { 40462306a36Sopenharmony_ci DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d length %d\n", i, cnt, i->entry_length); 40562306a36Sopenharmony_ci continue; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (i->interrupt_type != IRT_VECTORED_INTR) { 40962306a36Sopenharmony_ci DBG_IRT(KERN_WARNING MODULE_NAME ":find_irqline(0x%p): skipping entry %d interrupt_type %d\n", i, cnt, i->interrupt_type); 41062306a36Sopenharmony_ci continue; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!COMPARE_IRTE_ADDR(i, isi->isi_hpa)) 41462306a36Sopenharmony_ci continue; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if ((i->src_bus_irq_devno & IRT_IRQ_DEVNO_MASK) != irq_devno) 41762306a36Sopenharmony_ci continue; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci ** Ignore: src_bus_id and rc_seg_id correlate with 42162306a36Sopenharmony_ci ** iosapic_info->isi_hpa on HP platforms. 42262306a36Sopenharmony_ci ** If needed, pass in "PFA" (aka config space addr) 42362306a36Sopenharmony_ci ** instead of slot. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Found it! */ 42762306a36Sopenharmony_ci return i; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci printk(KERN_WARNING MODULE_NAME ": 0x%lx : no IRT entry for slot %d, pin %d\n", 43162306a36Sopenharmony_ci isi->isi_hpa, slot, intr_pin); 43262306a36Sopenharmony_ci return NULL; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/* 43762306a36Sopenharmony_ci** xlate_pin() supports the skewing of IRQ lines done by subsidiary bridges. 43862306a36Sopenharmony_ci** Legacy PDC already does this translation for us and stores it in INTR_LINE. 43962306a36Sopenharmony_ci** 44062306a36Sopenharmony_ci** PAT PDC needs to basically do what legacy PDC does: 44162306a36Sopenharmony_ci** o read PIN 44262306a36Sopenharmony_ci** o adjust PIN in case device is "behind" a PPB 44362306a36Sopenharmony_ci** (eg 4-port 100BT and SCSI/LAN "Combo Card") 44462306a36Sopenharmony_ci** o convert slot/pin to I/O SAPIC input line. 44562306a36Sopenharmony_ci** 44662306a36Sopenharmony_ci** HP platforms only support: 44762306a36Sopenharmony_ci** o one level of skewing for any number of PPBs 44862306a36Sopenharmony_ci** o only support PCI-PCI Bridges. 44962306a36Sopenharmony_ci*/ 45062306a36Sopenharmony_cistatic struct irt_entry * 45162306a36Sopenharmony_ciiosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci u8 intr_pin, intr_slot; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci pci_read_config_byte(pcidev, PCI_INTERRUPT_PIN, &intr_pin); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci DBG_IRT("iosapic_xlate_pin(%s) SLOT %d pin %d\n", 45862306a36Sopenharmony_ci pcidev->slot_name, PCI_SLOT(pcidev->devfn), intr_pin); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (intr_pin == 0) { 46162306a36Sopenharmony_ci /* The device does NOT support/use IRQ lines. */ 46262306a36Sopenharmony_ci return NULL; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Check if pcidev behind a PPB */ 46662306a36Sopenharmony_ci if (pcidev->bus->parent) { 46762306a36Sopenharmony_ci /* Convert pcidev INTR_PIN into something we 46862306a36Sopenharmony_ci ** can lookup in the IRT. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci#ifdef PCI_BRIDGE_FUNCS 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci ** Proposal #1: 47362306a36Sopenharmony_ci ** 47462306a36Sopenharmony_ci ** call implementation specific translation function 47562306a36Sopenharmony_ci ** This is architecturally "cleaner". HP-UX doesn't 47662306a36Sopenharmony_ci ** support other secondary bus types (eg. E/ISA) directly. 47762306a36Sopenharmony_ci ** May be needed for other processor (eg IA64) architectures 47862306a36Sopenharmony_ci ** or by some ambitous soul who wants to watch TV. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci if (pci_bridge_funcs->xlate_intr_line) { 48162306a36Sopenharmony_ci intr_pin = pci_bridge_funcs->xlate_intr_line(pcidev); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci#else /* PCI_BRIDGE_FUNCS */ 48462306a36Sopenharmony_ci struct pci_bus *p = pcidev->bus; 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci ** Proposal #2: 48762306a36Sopenharmony_ci ** The "pin" is skewed ((pin + dev - 1) % 4). 48862306a36Sopenharmony_ci ** 48962306a36Sopenharmony_ci ** This isn't very clean since I/O SAPIC must assume: 49062306a36Sopenharmony_ci ** - all platforms only have PCI busses. 49162306a36Sopenharmony_ci ** - only PCI-PCI bridge (eg not PCI-EISA, PCI-PCMCIA) 49262306a36Sopenharmony_ci ** - IRQ routing is only skewed once regardless of 49362306a36Sopenharmony_ci ** the number of PPB's between iosapic and device. 49462306a36Sopenharmony_ci ** (Bit3 expansion chassis follows this rule) 49562306a36Sopenharmony_ci ** 49662306a36Sopenharmony_ci ** Advantage is it's really easy to implement. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci intr_pin = pci_swizzle_interrupt_pin(pcidev, intr_pin); 49962306a36Sopenharmony_ci#endif /* PCI_BRIDGE_FUNCS */ 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* 50262306a36Sopenharmony_ci * Locate the host slot of the PPB. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci while (p->parent->parent) 50562306a36Sopenharmony_ci p = p->parent; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci intr_slot = PCI_SLOT(p->self->devfn); 50862306a36Sopenharmony_ci } else { 50962306a36Sopenharmony_ci intr_slot = PCI_SLOT(pcidev->devfn); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci DBG_IRT("iosapic_xlate_pin: bus %d slot %d pin %d\n", 51262306a36Sopenharmony_ci pcidev->bus->busn_res.start, intr_slot, intr_pin); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return irt_find_irqline(isi, intr_slot, intr_pin); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void iosapic_rd_irt_entry(struct vector_info *vi , u32 *dp0, u32 *dp1) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct iosapic_info *isp = vi->iosapic; 52062306a36Sopenharmony_ci u8 idx = vi->irqline; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci *dp0 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY(idx)); 52362306a36Sopenharmony_ci *dp1 = iosapic_read(isp->addr, IOSAPIC_IRDT_ENTRY_HI(idx)); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void iosapic_wr_irt_entry(struct vector_info *vi, u32 dp0, u32 dp1) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct iosapic_info *isp = vi->iosapic; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci DBG_IRT("iosapic_wr_irt_entry(): irq %d hpa %lx 0x%x 0x%x\n", 53262306a36Sopenharmony_ci vi->irqline, isp->isi_hpa, dp0, dp1); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY(vi->irqline), dp0); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Read the window register to flush the writes down to HW */ 53762306a36Sopenharmony_ci dp0 = readl(isp->addr+IOSAPIC_REG_WINDOW); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci iosapic_write(isp->addr, IOSAPIC_IRDT_ENTRY_HI(vi->irqline), dp1); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Read the window register to flush the writes down to HW */ 54262306a36Sopenharmony_ci dp1 = readl(isp->addr+IOSAPIC_REG_WINDOW); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci/* 54662306a36Sopenharmony_ci** set_irt prepares the data (dp0, dp1) according to the vector_info 54762306a36Sopenharmony_ci** and target cpu (id_eid). dp0/dp1 are then used to program I/O SAPIC 54862306a36Sopenharmony_ci** IRdT for the given "vector" (aka IRQ line). 54962306a36Sopenharmony_ci*/ 55062306a36Sopenharmony_cistatic void 55162306a36Sopenharmony_ciiosapic_set_irt_data( struct vector_info *vi, u32 *dp0, u32 *dp1) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci u32 mode = 0; 55462306a36Sopenharmony_ci struct irt_entry *p = vi->irte; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if ((p->polarity_trigger & IRT_PO_MASK) == IRT_ACTIVE_LO) 55762306a36Sopenharmony_ci mode |= IOSAPIC_IRDT_PO_LOW; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (((p->polarity_trigger >> IRT_EL_SHIFT) & IRT_EL_MASK) == IRT_LEVEL_TRIG) 56062306a36Sopenharmony_ci mode |= IOSAPIC_IRDT_LEVEL_TRIG; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* 56362306a36Sopenharmony_ci ** IA64 REVISIT 56462306a36Sopenharmony_ci ** PA doesn't support EXTINT or LPRIO bits. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci *dp0 = mode | (u32) vi->txn_data; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci ** Extracting id_eid isn't a real clean way of getting it. 57162306a36Sopenharmony_ci ** But the encoding is the same for both PA and IA64 platforms. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci if (is_pdc_pat()) { 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci ** PAT PDC just hands it to us "right". 57662306a36Sopenharmony_ci ** txn_addr comes from cpu_data[x].txn_addr. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_ci *dp1 = (u32) (vi->txn_addr); 57962306a36Sopenharmony_ci } else { 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci ** eg if base_addr == 0xfffa0000), 58262306a36Sopenharmony_ci ** we want to get 0xa0ff0000. 58362306a36Sopenharmony_ci ** 58462306a36Sopenharmony_ci ** eid 0x0ff00000 -> 0x00ff0000 58562306a36Sopenharmony_ci ** id 0x000ff000 -> 0xff000000 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci *dp1 = (((u32)vi->txn_addr & 0x0ff00000) >> 4) | 58862306a36Sopenharmony_ci (((u32)vi->txn_addr & 0x000ff000) << 12); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci DBG_IRT("iosapic_set_irt_data(): 0x%x 0x%x\n", *dp0, *dp1); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic void iosapic_mask_irq(struct irq_data *d) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci unsigned long flags; 59762306a36Sopenharmony_ci struct vector_info *vi = irq_data_get_irq_chip_data(d); 59862306a36Sopenharmony_ci u32 d0, d1; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci spin_lock_irqsave(&iosapic_lock, flags); 60162306a36Sopenharmony_ci iosapic_rd_irt_entry(vi, &d0, &d1); 60262306a36Sopenharmony_ci d0 |= IOSAPIC_IRDT_ENABLE; 60362306a36Sopenharmony_ci iosapic_wr_irt_entry(vi, d0, d1); 60462306a36Sopenharmony_ci spin_unlock_irqrestore(&iosapic_lock, flags); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic void iosapic_unmask_irq(struct irq_data *d) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct vector_info *vi = irq_data_get_irq_chip_data(d); 61062306a36Sopenharmony_ci u32 d0, d1; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* data is initialized by fixup_irq */ 61362306a36Sopenharmony_ci WARN_ON(vi->txn_irq == 0); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci iosapic_set_irt_data(vi, &d0, &d1); 61662306a36Sopenharmony_ci iosapic_wr_irt_entry(vi, d0, d1); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci#ifdef DEBUG_IOSAPIC_IRT 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci u32 *t = (u32 *) ((ulong) vi->eoi_addr & ~0xffUL); 62162306a36Sopenharmony_ci printk("iosapic_enable_irq(): regs %p", vi->eoi_addr); 62262306a36Sopenharmony_ci for ( ; t < vi->eoi_addr; t++) 62362306a36Sopenharmony_ci printk(" %x", readl(t)); 62462306a36Sopenharmony_ci printk("\n"); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ciprintk("iosapic_enable_irq(): sel "); 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct iosapic_info *isp = vi->iosapic; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci for (d0=0x10; d0<0x1e; d0++) { 63262306a36Sopenharmony_ci d1 = iosapic_read(isp->addr, d0); 63362306a36Sopenharmony_ci printk(" %x", d1); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ciprintk("\n"); 63762306a36Sopenharmony_ci#endif 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* 64062306a36Sopenharmony_ci * Issuing I/O SAPIC an EOI causes an interrupt IFF IRQ line is 64162306a36Sopenharmony_ci * asserted. IRQ generally should not be asserted when a driver 64262306a36Sopenharmony_ci * enables their IRQ. It can lead to "interesting" race conditions 64362306a36Sopenharmony_ci * in the driver initialization sequence. 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ci DBG(KERN_DEBUG "enable_irq(%d): eoi(%p, 0x%x)\n", d->irq, 64662306a36Sopenharmony_ci vi->eoi_addr, vi->eoi_data); 64762306a36Sopenharmony_ci iosapic_eoi(vi->eoi_addr, vi->eoi_data); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void iosapic_eoi_irq(struct irq_data *d) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct vector_info *vi = irq_data_get_irq_chip_data(d); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci iosapic_eoi(vi->eoi_addr, vi->eoi_data); 65562306a36Sopenharmony_ci cpu_eoi_irq(d); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci#ifdef CONFIG_SMP 65962306a36Sopenharmony_cistatic int iosapic_set_affinity_irq(struct irq_data *d, 66062306a36Sopenharmony_ci const struct cpumask *dest, bool force) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct vector_info *vi = irq_data_get_irq_chip_data(d); 66362306a36Sopenharmony_ci u32 d0, d1, dummy_d0; 66462306a36Sopenharmony_ci unsigned long flags; 66562306a36Sopenharmony_ci int dest_cpu; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci dest_cpu = cpu_check_affinity(d, dest); 66862306a36Sopenharmony_ci if (dest_cpu < 0) 66962306a36Sopenharmony_ci return -1; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci irq_data_update_affinity(d, cpumask_of(dest_cpu)); 67262306a36Sopenharmony_ci vi->txn_addr = txn_affinity_addr(d->irq, dest_cpu); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci spin_lock_irqsave(&iosapic_lock, flags); 67562306a36Sopenharmony_ci /* d1 contains the destination CPU, so only want to set that 67662306a36Sopenharmony_ci * entry */ 67762306a36Sopenharmony_ci iosapic_rd_irt_entry(vi, &d0, &d1); 67862306a36Sopenharmony_ci iosapic_set_irt_data(vi, &dummy_d0, &d1); 67962306a36Sopenharmony_ci iosapic_wr_irt_entry(vi, d0, d1); 68062306a36Sopenharmony_ci spin_unlock_irqrestore(&iosapic_lock, flags); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci#endif 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic struct irq_chip iosapic_interrupt_type = { 68762306a36Sopenharmony_ci .name = "IO-SAPIC-level", 68862306a36Sopenharmony_ci .irq_unmask = iosapic_unmask_irq, 68962306a36Sopenharmony_ci .irq_mask = iosapic_mask_irq, 69062306a36Sopenharmony_ci .irq_ack = cpu_ack_irq, 69162306a36Sopenharmony_ci .irq_eoi = iosapic_eoi_irq, 69262306a36Sopenharmony_ci#ifdef CONFIG_SMP 69362306a36Sopenharmony_ci .irq_set_affinity = iosapic_set_affinity_irq, 69462306a36Sopenharmony_ci#endif 69562306a36Sopenharmony_ci}; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ciint iosapic_fixup_irq(void *isi_obj, struct pci_dev *pcidev) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct iosapic_info *isi = isi_obj; 70062306a36Sopenharmony_ci struct irt_entry *irte = NULL; /* only used if PAT PDC */ 70162306a36Sopenharmony_ci struct vector_info *vi; 70262306a36Sopenharmony_ci int isi_line; /* line used by device */ 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (!isi) { 70562306a36Sopenharmony_ci printk(KERN_WARNING MODULE_NAME ": hpa not registered for %s\n", 70662306a36Sopenharmony_ci pci_name(pcidev)); 70762306a36Sopenharmony_ci return -1; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci#ifdef CONFIG_SUPERIO 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * HACK ALERT! (non-compliant PCI device support) 71362306a36Sopenharmony_ci * 71462306a36Sopenharmony_ci * All SuckyIO interrupts are routed through the PIC's on function 1. 71562306a36Sopenharmony_ci * But SuckyIO OHCI USB controller gets an IRT entry anyway because 71662306a36Sopenharmony_ci * it advertises INT D for INT_PIN. Use that IRT entry to get the 71762306a36Sopenharmony_ci * SuckyIO interrupt routing for PICs on function 1 (*BLEECCHH*). 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_ci if (is_superio_device(pcidev)) { 72062306a36Sopenharmony_ci /* We must call superio_fixup_irq() to register the pdev */ 72162306a36Sopenharmony_ci pcidev->irq = superio_fixup_irq(pcidev); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Don't return if need to program the IOSAPIC's IRT... */ 72462306a36Sopenharmony_ci if (PCI_FUNC(pcidev->devfn) != SUPERIO_USB_FN) 72562306a36Sopenharmony_ci return pcidev->irq; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci#endif /* CONFIG_SUPERIO */ 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* lookup IRT entry for isi/slot/pin set */ 73062306a36Sopenharmony_ci irte = iosapic_xlate_pin(isi, pcidev); 73162306a36Sopenharmony_ci if (!irte) { 73262306a36Sopenharmony_ci printk("iosapic: no IRTE for %s (IRQ not connected?)\n", 73362306a36Sopenharmony_ci pci_name(pcidev)); 73462306a36Sopenharmony_ci return -1; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci DBG_IRT("iosapic_fixup_irq(): irte %p %x %x %x %x %x %x %x %x\n", 73762306a36Sopenharmony_ci irte, 73862306a36Sopenharmony_ci irte->entry_type, 73962306a36Sopenharmony_ci irte->entry_length, 74062306a36Sopenharmony_ci irte->polarity_trigger, 74162306a36Sopenharmony_ci irte->src_bus_irq_devno, 74262306a36Sopenharmony_ci irte->src_bus_id, 74362306a36Sopenharmony_ci irte->src_seg_id, 74462306a36Sopenharmony_ci irte->dest_iosapic_intin, 74562306a36Sopenharmony_ci (u32) irte->dest_iosapic_addr); 74662306a36Sopenharmony_ci isi_line = irte->dest_iosapic_intin; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* get vector info for this input line */ 74962306a36Sopenharmony_ci vi = isi->isi_vector + isi_line; 75062306a36Sopenharmony_ci DBG_IRT("iosapic_fixup_irq: line %d vi 0x%p\n", isi_line, vi); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* If this IRQ line has already been setup, skip it */ 75362306a36Sopenharmony_ci if (vi->irte) 75462306a36Sopenharmony_ci goto out; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci vi->irte = irte; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* 75962306a36Sopenharmony_ci * Allocate processor IRQ 76062306a36Sopenharmony_ci * 76162306a36Sopenharmony_ci * XXX/FIXME The txn_alloc_irq() code and related code should be 76262306a36Sopenharmony_ci * moved to enable_irq(). That way we only allocate processor IRQ 76362306a36Sopenharmony_ci * bits for devices that actually have drivers claiming them. 76462306a36Sopenharmony_ci * Right now we assign an IRQ to every PCI device present, 76562306a36Sopenharmony_ci * regardless of whether it's used or not. 76662306a36Sopenharmony_ci */ 76762306a36Sopenharmony_ci vi->txn_irq = txn_alloc_irq(8); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (vi->txn_irq < 0) 77062306a36Sopenharmony_ci panic("I/O sapic: couldn't get TXN IRQ\n"); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* enable_irq() will use txn_* to program IRdT */ 77362306a36Sopenharmony_ci vi->txn_addr = txn_alloc_addr(vi->txn_irq); 77462306a36Sopenharmony_ci vi->txn_data = txn_alloc_data(vi->txn_irq); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI; 77762306a36Sopenharmony_ci vi->eoi_data = cpu_to_le32(vi->txn_data); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci out: 78262306a36Sopenharmony_ci pcidev->irq = vi->txn_irq; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci DBG_IRT("iosapic_fixup_irq() %d:%d %x %x line %d irq %d\n", 78562306a36Sopenharmony_ci PCI_SLOT(pcidev->devfn), PCI_FUNC(pcidev->devfn), 78662306a36Sopenharmony_ci pcidev->vendor, pcidev->device, isi_line, pcidev->irq); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return pcidev->irq; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic struct iosapic_info *iosapic_list; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci#ifdef CONFIG_64BIT 79462306a36Sopenharmony_ciint iosapic_serial_irq(struct parisc_device *dev) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci struct iosapic_info *isi; 79762306a36Sopenharmony_ci struct irt_entry *irte; 79862306a36Sopenharmony_ci struct vector_info *vi; 79962306a36Sopenharmony_ci int cnt; 80062306a36Sopenharmony_ci int intin; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci intin = (dev->mod_info >> 24) & 15; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* lookup IRT entry for isi/slot/pin set */ 80562306a36Sopenharmony_ci for (cnt = 0; cnt < irt_num_entry; cnt++) { 80662306a36Sopenharmony_ci irte = &irt_cell[cnt]; 80762306a36Sopenharmony_ci if (COMPARE_IRTE_ADDR(irte, dev->mod0) && 80862306a36Sopenharmony_ci irte->dest_iosapic_intin == intin) 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci if (cnt >= irt_num_entry) 81262306a36Sopenharmony_ci return 0; /* no irq found, force polling */ 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci DBG_IRT("iosapic_serial_irq(): irte %p %x %x %x %x %x %x %x %x\n", 81562306a36Sopenharmony_ci irte, 81662306a36Sopenharmony_ci irte->entry_type, 81762306a36Sopenharmony_ci irte->entry_length, 81862306a36Sopenharmony_ci irte->polarity_trigger, 81962306a36Sopenharmony_ci irte->src_bus_irq_devno, 82062306a36Sopenharmony_ci irte->src_bus_id, 82162306a36Sopenharmony_ci irte->src_seg_id, 82262306a36Sopenharmony_ci irte->dest_iosapic_intin, 82362306a36Sopenharmony_ci (u32) irte->dest_iosapic_addr); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* search for iosapic */ 82662306a36Sopenharmony_ci for (isi = iosapic_list; isi; isi = isi->isi_next) 82762306a36Sopenharmony_ci if (isi->isi_hpa == dev->mod0) 82862306a36Sopenharmony_ci break; 82962306a36Sopenharmony_ci if (!isi) 83062306a36Sopenharmony_ci return 0; /* no iosapic found, force polling */ 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* get vector info for this input line */ 83362306a36Sopenharmony_ci vi = isi->isi_vector + intin; 83462306a36Sopenharmony_ci DBG_IRT("iosapic_serial_irq: line %d vi 0x%p\n", iosapic_intin, vi); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* If this IRQ line has already been setup, skip it */ 83762306a36Sopenharmony_ci if (vi->irte) 83862306a36Sopenharmony_ci goto out; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci vi->irte = irte; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* 84362306a36Sopenharmony_ci * Allocate processor IRQ 84462306a36Sopenharmony_ci * 84562306a36Sopenharmony_ci * XXX/FIXME The txn_alloc_irq() code and related code should be 84662306a36Sopenharmony_ci * moved to enable_irq(). That way we only allocate processor IRQ 84762306a36Sopenharmony_ci * bits for devices that actually have drivers claiming them. 84862306a36Sopenharmony_ci * Right now we assign an IRQ to every PCI device present, 84962306a36Sopenharmony_ci * regardless of whether it's used or not. 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_ci vi->txn_irq = txn_alloc_irq(8); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (vi->txn_irq < 0) 85462306a36Sopenharmony_ci panic("I/O sapic: couldn't get TXN IRQ\n"); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* enable_irq() will use txn_* to program IRdT */ 85762306a36Sopenharmony_ci vi->txn_addr = txn_alloc_addr(vi->txn_irq); 85862306a36Sopenharmony_ci vi->txn_data = txn_alloc_data(vi->txn_irq); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci vi->eoi_addr = isi->addr + IOSAPIC_REG_EOI; 86162306a36Sopenharmony_ci vi->eoi_data = cpu_to_le32(vi->txn_data); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci cpu_claim_irq(vi->txn_irq, &iosapic_interrupt_type, vi); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci out: 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return vi->txn_irq; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ciEXPORT_SYMBOL(iosapic_serial_irq); 87062306a36Sopenharmony_ci#endif 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/* 87462306a36Sopenharmony_ci** squirrel away the I/O Sapic Version 87562306a36Sopenharmony_ci*/ 87662306a36Sopenharmony_cistatic unsigned int 87762306a36Sopenharmony_ciiosapic_rd_version(struct iosapic_info *isi) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci return iosapic_read(isi->addr, IOSAPIC_REG_VERSION); 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/* 88462306a36Sopenharmony_ci** iosapic_register() is called by "drivers" with an integrated I/O SAPIC. 88562306a36Sopenharmony_ci** Caller must be certain they have an I/O SAPIC and know its MMIO address. 88662306a36Sopenharmony_ci** 88762306a36Sopenharmony_ci** o allocate iosapic_info and add it to the list 88862306a36Sopenharmony_ci** o read iosapic version and squirrel that away 88962306a36Sopenharmony_ci** o read size of IRdT. 89062306a36Sopenharmony_ci** o allocate and initialize isi_vector[] 89162306a36Sopenharmony_ci** o allocate irq region 89262306a36Sopenharmony_ci*/ 89362306a36Sopenharmony_civoid *iosapic_register(unsigned long hpa, void __iomem *vaddr) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci struct iosapic_info *isi = NULL; 89662306a36Sopenharmony_ci struct irt_entry *irte = irt_cell; 89762306a36Sopenharmony_ci struct vector_info *vip; 89862306a36Sopenharmony_ci int cnt; /* track how many entries we've looked at */ 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* 90162306a36Sopenharmony_ci * Astro based platforms can only support PCI OLARD if they implement 90262306a36Sopenharmony_ci * PAT PDC. Legacy PDC omits LBAs with no PCI devices from the IRT. 90362306a36Sopenharmony_ci * Search the IRT and ignore iosapic's which aren't in the IRT. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci for (cnt=0; cnt < irt_num_entry; cnt++, irte++) { 90662306a36Sopenharmony_ci WARN_ON(IRT_IOSAPIC_TYPE != irte->entry_type); 90762306a36Sopenharmony_ci if (COMPARE_IRTE_ADDR(irte, hpa)) 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (cnt >= irt_num_entry) { 91262306a36Sopenharmony_ci DBG("iosapic_register() ignoring 0x%lx (NOT FOUND)\n", hpa); 91362306a36Sopenharmony_ci return NULL; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci isi = kzalloc(sizeof(struct iosapic_info), GFP_KERNEL); 91762306a36Sopenharmony_ci if (!isi) { 91862306a36Sopenharmony_ci BUG(); 91962306a36Sopenharmony_ci return NULL; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci isi->addr = vaddr; 92362306a36Sopenharmony_ci isi->isi_hpa = hpa; 92462306a36Sopenharmony_ci isi->isi_version = iosapic_rd_version(isi); 92562306a36Sopenharmony_ci isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci vip = isi->isi_vector = kcalloc(isi->isi_num_vectors, 92862306a36Sopenharmony_ci sizeof(struct vector_info), GFP_KERNEL); 92962306a36Sopenharmony_ci if (vip == NULL) { 93062306a36Sopenharmony_ci kfree(isi); 93162306a36Sopenharmony_ci return NULL; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci for (cnt=0; cnt < isi->isi_num_vectors; cnt++, vip++) { 93562306a36Sopenharmony_ci vip->irqline = (unsigned char) cnt; 93662306a36Sopenharmony_ci vip->iosapic = isi; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci isi->isi_next = iosapic_list; 93962306a36Sopenharmony_ci iosapic_list = isi; 94062306a36Sopenharmony_ci return isi; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci#ifdef DEBUG_IOSAPIC 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic void 94762306a36Sopenharmony_ciiosapic_prt_irt(void *irt, long num_entry) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci unsigned int i, *irp = (unsigned int *) irt; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": Interrupt Routing Table (%lx entries)\n", num_entry); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci for (i=0; i<num_entry; i++, irp += 4) { 95562306a36Sopenharmony_ci printk(KERN_DEBUG "%p : %2d %.8x %.8x %.8x %.8x\n", 95662306a36Sopenharmony_ci irp, i, irp[0], irp[1], irp[2], irp[3]); 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic void 96262306a36Sopenharmony_ciiosapic_prt_vi(struct vector_info *vi) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": vector_info[%d] is at %p\n", vi->irqline, vi); 96562306a36Sopenharmony_ci printk(KERN_DEBUG "\t\tstatus: %.4x\n", vi->status); 96662306a36Sopenharmony_ci printk(KERN_DEBUG "\t\ttxn_irq: %d\n", vi->txn_irq); 96762306a36Sopenharmony_ci printk(KERN_DEBUG "\t\ttxn_addr: %lx\n", vi->txn_addr); 96862306a36Sopenharmony_ci printk(KERN_DEBUG "\t\ttxn_data: %lx\n", vi->txn_data); 96962306a36Sopenharmony_ci printk(KERN_DEBUG "\t\teoi_addr: %p\n", vi->eoi_addr); 97062306a36Sopenharmony_ci printk(KERN_DEBUG "\t\teoi_data: %x\n", vi->eoi_data); 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic void 97562306a36Sopenharmony_ciiosapic_prt_isi(struct iosapic_info *isi) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci printk(KERN_DEBUG MODULE_NAME ": io_sapic_info at %p\n", isi); 97862306a36Sopenharmony_ci printk(KERN_DEBUG "\t\tisi_hpa: %lx\n", isi->isi_hpa); 97962306a36Sopenharmony_ci printk(KERN_DEBUG "\t\tisi_status: %x\n", isi->isi_status); 98062306a36Sopenharmony_ci printk(KERN_DEBUG "\t\tisi_version: %x\n", isi->isi_version); 98162306a36Sopenharmony_ci printk(KERN_DEBUG "\t\tisi_vector: %p\n", isi->isi_vector); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci#endif /* DEBUG_IOSAPIC */ 984