⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 iosapic.c

📁 ARM 嵌入式 系统 设计与实例开发 实验教材 二源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/*** I/O Sapic Driver - PCI interrupt line support****      (c) Copyright 1999 Grant Grundler**      (c) Copyright 1999 Hewlett-Packard Company****      This program is free software; you can redistribute it and/or modify**      it under the terms of the GNU General Public License as published by**      the Free Software Foundation; either version 2 of the License, or**      (at your option) any later version.**** The I/O sapic driver manages the Interrupt Redirection Table which is** the control logic to convert PCI line based interrupts into a Message** Signaled Interrupt (aka Transaction Based Interrupt, TBI).**** Acronyms** --------** HPA  Hard Physical Address (aka MMIO address)** IRQ  Interrupt ReQuest. Implies Line based interrupt.** IRT	Interrupt Routing Table (provided by PAT firmware)** IRdT Interrupt Redirection Table. IRQ line to TXN ADDR/DATA**      table which is implemented in I/O SAPIC.** ISR  Interrupt Service Routine. aka Interrupt handler.** MSI	Message Signaled Interrupt. PCI 2.2 functionality.**      aka Transaction Based Interrupt (or TBI).** PA   Precision Architecture. HP's RISC architecture.** RISC Reduced Instruction Set Computer.****** What's a Message Signalled Interrupt?** -------------------------------------** MSI is a write transaction which targets a processor and is similar** to a processor write to memory or MMIO. MSIs can be generated by I/O** devices as well as processors and require *architecture* to work.**** PA only supports MSI. So I/O subsystems must either natively generate** MSIs (e.g. GSC or HP-PB) or convert line based interrupts into MSIs** (e.g. PCI and EISA).  IA64 supports MSIs via a "local SAPIC" which** acts on behalf of a processor.**** MSI allows any I/O device to interrupt any processor. This makes** load balancing of the interrupt processing possible on an SMP platform.** Interrupts are also ordered WRT to DMA data.  It's possible on I/O** coherent systems to completely eliminate PIO reads from the interrupt** path. The device and driver must be designed and implemented to** guarantee all DMA has been issued (issues about atomicity here)** before the MSI is issued. I/O status can then safely be read from** DMA'd data by the ISR.****** PA Firmware** -----------** PA-RISC platforms have two fundementally different types of firmware.** For PCI devices, "Legacy" PDC initializes the "INTERRUPT_LINE" register** and BARs similar to a traditional PC BIOS.** The newer "PAT" firmware supports PDC calls which return tables.** PAT firmware only initializes PCI Console and Boot interface.** With these tables, the OS can progam all other PCI devices.**** One such PAT PDC call returns the "Interrupt Routing Table" (IRT).** The IRT maps each PCI slot's INTA-D "output" line to an I/O SAPIC** input line.  If the IRT is not available, this driver assumes** INTERRUPT_LINE register has been programmed by firmware. The latter** case also means online addition of PCI cards can NOT be supported** even if HW support is present.**** All platforms with PAT firmware to date (Oct 1999) use one Interrupt** Routing Table for the entire platform.**** Where's the iosapic?** --------------------** I/O sapic is part of the "Core Electronics Complex". And on HP platforms** it's integrated as part of the PCI bus adapter, "lba".  So no bus walk** will discover I/O Sapic. I/O Sapic driver learns about each device** when lba driver advertises the presence of the I/O sapic by calling** iosapic_register().****** IRQ region notes** ----------------** The data passed to iosapic_interrupt() is per IRQ line.** Each IRQ line will get one txn_addr/data pair. Thus each IRQ region,** will have several txn_addr/data pairs (up to 7 for current I/O SAPIC** implementations).  The IRQ region "sysdata" will NOT be directly passed** to the interrupt handler like GSCtoPCI (dino.c).**** iosapic interrupt handler will NOT call do_irq_mask().** It doesn't need to read a bit mask to determine which IRQ line was pulled** since it already knows based on vector_info passed to iosapic_interrupt().**** One IRQ number represents both an IRQ line and a driver ISR.** The I/O sapic driver can't manage shared IRQ lines because** additional data besides the IRQ number must be passed via** irq_region_ops. do_irq() and request_irq() must manage** a sharing a bit in the mask.**** iosapic_interrupt() replaces do_irq_mask() and calls do_irq().** Which IRQ line was asserted is already known since each** line has unique data associated with it. We could omit** iosapic_interrupt() from the calling path if it did NOT need** to write EOI. For unshared lines, it really doesn't.**** Unfortunately, can't optimize out EOI if IRQ line isn't "shared".** N-class console "device" and some sort of heartbeat actually share** one line though only one driver is registered...<sigh>...this was** true for HP-UX at least. May not be true for parisc-linux.****** Overview of exported iosapic functions** --------------------------------------** (caveat: code isn't finished yet - this is just the plan)**** iosapic_init:**   o initialize globals (lock, etc)**   o try to read IRT. Presence of IRT determines if this is**     a PAT platform or not.**** iosapic_register():**   o create iosapic_info instance data structure**   o allocate vector_info array for this iosapic**   o initialize vector_info - read corresponding IRdT?**** iosapic_xlate_pin: (only called by fixup_irq for PAT platform)**   o intr_pin = read cfg (INTERRUPT_PIN);**   o if (device under PCI-PCI bridge)**               translate slot/pin**** iosapic_fixup_irq:**   o if PAT platform (IRT present)**	   intr_pin = iosapic_xlate_pin(isi,pcidev):**         intr_line = find IRT entry(isi, PCI_SLOT(pcidev), intr_pin)**         save IRT entry into vector_info later**         write cfg INTERRUPT_LINE (with intr_line)?**     else**         intr_line = pcidev->irq**         IRT pointer = NULL**     endif**   o locate vector_info (needs: isi, intr_line)**   o allocate processor "irq" and get txn_addr/data**   o request_irq(processor_irq,  iosapic_interrupt, vector_info,...)**   o pcidev->irq = isi->isi_region...base + intr_line;**** iosapic_interrupt:**   o call do_irq(vector->isi->irq_region, vector->irq_line, regs)**   o assume level triggered and write EOI**** iosapic_enable_irq:**   o clear any pending IRQ on that line**   o enable IRdT - call enable_irq(vector[line]->processor_irq)**   o write EOI in case line is already asserted.**** iosapic_disable_irq:**   o disable IRdT - call disable_irq(vector[line]->processor_irq)**** FIXME: mask/unmask*//* FIXME: determine which include files are really needed */#include <linux/types.h>#include <linux/kernel.h>#include <linux/spinlock.h>#include <linux/pci.h>		/* pci cfg accessor functions  */#include <linux/init.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/interrupt.h>	/* irqaction */#include <linux/irq.h>		/* irq_region support */#include <asm/byteorder.h>	/* get in-line asm for swab */#include <asm/pdc.h>#include <asm/pdcpat.h>#include <asm/page.h>#include <asm/segment.h>#include <asm/system.h>#include <asm/gsc.h>		/* gsc_read/write functions */#include <asm/iosapic.h>#include "./iosapic_private.h"#define MODULE_NAME "iosapic"/* "local" compile flags */#undef IOSAPIC_CALLBACK#undef PCI_BRIDGE_FUNCS#undef DEBUG_IOSAPIC#undef DEBUG_IOSAPIC_IRT#ifdef DEBUG_IOSAPICstatic char assert_buf[128];static intassert_failed (char *a, char *f, int l){        sprintf(assert_buf,			"ASSERT(%s) failed!\nline %d in %s\n",			a,      /* assertion text */			l,      /* line number */			f);     /* file name */        panic(assert_buf);	return 0;}#undef ASSERT#define ASSERT(EX) { if (!(EX)) assert_failed(# EX, __FILE__, __LINE__); }#define DBG(x...) printk(x)#else /* DEBUG_IOSAPIC */#define DBG(x...)#define ASSERT(EX)#endif /* DEBUG_IOSAPIC */#ifdef DEBUG_IOSAPIC_IRT#define DBG_IRT(x...) printk(x)#else#define DBG_IRT(x...)#endif#define READ_U8(addr)  gsc_readb(addr)#define READ_U16(addr) le16_to_cpu(gsc_readw((u16 *) (addr)))#define READ_U32(addr) le32_to_cpu(gsc_readl((u32 *) (addr)))#define READ_REG16(addr) gsc_readw((u16 *) (addr))#define READ_REG32(addr) gsc_readl((u32 *) (addr))#define WRITE_U8(value, addr) gsc_writeb(value, addr)#define WRITE_U16(value, addr) gsc_writew(cpu_to_le16(value), (u16 *) (addr))#define WRITE_U32(value, addr) gsc_writel(cpu_to_le32(value), (u32 *) (addr))#define WRITE_REG16(value, addr) gsc_writew(value, (u16 *) (addr))#define WRITE_REG32(value, addr) gsc_writel(value, (u32 *) (addr))#define IOSAPIC_REG_SELECT              0#define IOSAPIC_REG_WINDOW              0x10#define IOSAPIC_REG_EOI                 0x40#define IOSAPIC_REG_VERSION		0x1#define IOSAPIC_IRDT_ENTRY(idx)		(0x10+(idx)*2)#define IOSAPIC_IRDT_ENTRY_HI(idx)	(0x11+(idx)*2)/*** FIXME: revisit which GFP flags we should really be using.**     GFP_KERNEL includes __GFP_WAIT flag and that may not**     be acceptable. Since this is boot time, we shouldn't have**     to wait ever and this code should (will?) never get called**     from the interrrupt context.*/#define	IOSAPIC_KALLOC(a_type, cnt) \			(a_type *) kmalloc(sizeof(a_type)*(cnt), GFP_KERNEL)#define IOSAPIC_FREE(addr, f_type, cnt) kfree((void *)addr)#define	IOSAPIC_LOCK(lck)	spin_lock_irqsave(lck, irqflags)#define	IOSAPIC_UNLOCK(lck)	spin_unlock_irqrestore(lck, irqflags)#define IOSAPIC_VERSION_MASK            0x000000ff#define IOSAPIC_VERSION_SHIFT           0x0#define	IOSAPIC_VERSION(ver)				\		(int) ((ver & IOSAPIC_VERSION_MASK) >> IOSAPIC_VERSION_SHIFT)#define IOSAPIC_MAX_ENTRY_MASK          0x00ff0000#define IOSAPIC_MAX_ENTRY_SHIFT         0x10#define	IOSAPIC_IRDT_MAX_ENTRY(ver)			\		(int) ((ver&IOSAPIC_MAX_ENTRY_MASK) >> IOSAPIC_MAX_ENTRY_SHIFT)/* bits in the "low" I/O Sapic IRdT entry */#define IOSAPIC_IRDT_ENABLE       0x10000#define IOSAPIC_IRDT_PO_LOW       0x02000#define IOSAPIC_IRDT_LEVEL_TRIG   0x08000#define IOSAPIC_IRDT_MODE_LPRI    0x00100/* bits in the "high" I/O Sapic IRdT entry */#define IOSAPIC_IRDT_ID_EID_SHIFT              0x10#define	IOSAPIC_EOI(eoi_addr, eoi_data) gsc_writel(eoi_data, eoi_addr)#if IOSAPIC_CALLBACK/*** Shouldn't use callback since SAPIC doesn't have an officially assigned** H or S version numbers. Slight long term risk the number chosen would** collide with something else.** But benefit is cleaner lba/sapic interface.** Might be worth it but for just use direct calls for now.**** Entry below is copied from lba driver.** Only thing different is hw_type.*/static struct pa_iodc_driver iosapic_driver_for[] = {	{HPHW_OTHER, 0x782, 0, 0x0000A, 0, 0x00,	DRIVER_CHECK_HWTYPE + DRIVER_CHECK_HVERSION + DRIVER_CHECK_SVERSION,	"I/O Sapic", "",(void *) iosapic_callback},     	{0,0,0,0,0,0,	0,	(char *) NULL,(char *) NULL,(void *) NULL}                     };#endif /* IOSAPIO_CALLBACK */static struct iosapic_info *iosapic_list;static spinlock_t iosapic_lock;static int iosapic_count;/*** REVISIT: future platforms may have more than one IRT.** If so, the following three fields form a structure which** then be linked into a list. Names are chosen to make searching** for them easy - not necessarily accurate (eg "cell").**** Alternative: iosapic_info could point to the IRT it's in.** iosapic_register() could search a list of IRT's.*/static struct irt_entry *irt_cell;static size_t irt_num_entry;/*** iosapic_load_irt**** The "Get PCI INT Routing Table Size" option returns the number of ** entries in the PCI interrupt routing table for the cell specified ** in the cell_number argument.  The cell number must be for a cell ** within the caller's protection domain.**** The "Get PCI INT Routing Table" option returns, for the cell ** specified in the cell_number argument, the PCI interrupt routing ** table in the caller allocated memory pointed to by mem_addr.** We assume the IRT only contains entries for I/O SAPIC and** calculate the size based on the size of I/O sapic entries.**** The PCI interrupt routing table entry format is derived from the** IA64 SAL Specification 2.4.   The PCI interrupt routing table defines** the routing of PCI interrupt signals between the PCI device output** "pins" and the IO SAPICs' input "lines" (including core I/O PCI** devices).  This table does NOT include information for devices/slots** behind PCI to PCI bridges. See PCI to PCI Bridge Architecture Spec.** for the architected method of routing of IRQ's behind PPB's.*/static int __init /* return number of entries as success/fail flag */iosapic_load_irt(unsigned long cell_num, struct irt_entry **irt){	struct pdc_pat_io_num pdc_io_num; /* PAT PDC return block */	long status;              /* PDC return value status */	struct irt_entry *table = NULL;  /* start of interrupt routing tbl */	unsigned long num_entries = 0UL;	ASSERT(NULL != irt);	/* FIXME ASSERT(((&pdc_io_num) & (0x3f)) == 0);  enforce 32-byte alignment */	/* Try PAT_PDC to get interrupt routing table size */	DBG(KERN_DEBUG "calling get_irt_size\n");	status = pdc_pat_get_irt_size( &pdc_io_num, cell_num);	DBG(KERN_DEBUG "get_irt_size: %ld\n", status);	switch(status) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -