📄 pcic.c
字号:
static voidpcic_fill_irq(struct linux_pcic *pcic, struct pci_dev *dev, int node){ struct pcic_ca2irq *p; int i, ivec; char namebuf[64]; if (node == 0 || node == -1) { strcpy(namebuf, "???"); } else { prom_getstring(node, "name", namebuf, sizeof(namebuf)); } if ((p = pcic->pcic_imap) == 0) { dev->irq = 0; return; } for (i = 0; i < pcic->pcic_imdim; i++) { if (p->busno == dev->bus->number && p->devfn == dev->devfn) break; p++; } if (i >= pcic->pcic_imdim) { printk("PCIC: device %s devfn %02x:%02x not found in %d\n", namebuf, dev->bus->number, dev->devfn, pcic->pcic_imdim); dev->irq = 0; return; } i = p->pin; if (i >= 0 && i < 4) { ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO); dev->irq = ivec >> (i << 2) & 0xF; } else if (i >= 4 && i < 8) { ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI); dev->irq = ivec >> ((i-4) << 2) & 0xF; } else { /* Corrupted map */ printk("PCIC: BAD PIN %d\n", i); for (;;) {} }/* P3 */ /* printk("PCIC: device %s pin %d ivec 0x%x irq %x\n", namebuf, i, ivec, dev->irq); */ /* * dev->irq=0 means PROM did not bother to program the upper * half of PCIC. This happens on JS-E with PROM 3.11, for instance. */ if (dev->irq == 0 || p->force) { if (p->irq == 0 || p->irq >= 15) { /* Corrupted map */ printk("PCIC: BAD IRQ %d\n", p->irq); for (;;) {} } printk("PCIC: setting irq %d at pin %d for device %02x:%02x\n", p->irq, p->pin, dev->bus->number, dev->devfn); dev->irq = p->irq; i = p->pin; if (i >= 4) { ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI); ivec &= ~(0xF << ((i - 4) << 2)); ivec |= p->irq << ((i - 4) << 2); writew(ivec, pcic->pcic_regs+PCI_INT_SELECT_HI); } else { ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO); ivec &= ~(0xF << (i << 2)); ivec |= p->irq << (i << 2); writew(ivec, pcic->pcic_regs+PCI_INT_SELECT_LO); } } return;}/* * Normally called from {do_}pci_scan_bus... */void __init pcibios_fixup_bus(struct pci_bus *bus){ struct list_head *walk; int i, has_io, has_mem; unsigned short cmd; struct linux_pcic *pcic; /* struct linux_pbm_info* pbm = &pcic->pbm; */ int node; struct pcidev_cookie *pcp; if (!pcic0_up) { printk("pcibios_fixup_bus: no PCIC\n"); return; } pcic = &pcic0; /* * Next crud is an equivalent of pbm = pcic_bus_to_pbm(bus); */ if (bus->number != 0) { printk("pcibios_fixup_bus: nonzero bus 0x%x\n", bus->number); return; } walk = &bus->devices; for (walk = walk->next; walk != &bus->devices; walk = walk->next) { struct pci_dev *dev = pci_dev_b(walk); /* * Comment from i386 branch: * There are buggy BIOSes that forget to enable I/O and memory * access to PCI devices. We try to fix this, but we need to * be sure that the BIOS didn't forget to assign an address * to the device. [mj] * OBP is a case of such BIOS :-) */ has_io = has_mem = 0; for(i=0; i<6; i++) { unsigned long f = dev->resource[i].flags; if (f & IORESOURCE_IO) { has_io = 1; } else if (f & IORESOURCE_MEM) has_mem = 1; } pcic_read_config_word(dev, PCI_COMMAND, &cmd); if (has_io && !(cmd & PCI_COMMAND_IO)) { printk("PCIC: Enabling I/O for device %02x:%02x\n", dev->bus->number, dev->devfn); cmd |= PCI_COMMAND_IO; pcic_write_config_word(dev, PCI_COMMAND, cmd); } if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) { printk("PCIC: Enabling memory for device %02x:%02x\n", dev->bus->number, dev->devfn); cmd |= PCI_COMMAND_MEMORY; pcic_write_config_word(dev, PCI_COMMAND, cmd); } node = pdev_to_pnode(&pcic->pbm, dev); if(node == 0) node = -1; /* cookies */ pcp = pci_devcookie_alloc(); pcp->pbm = &pcic->pbm; pcp->prom_node = node; dev->sysdata = pcp; /* fixing I/O to look like memory */ if ((dev->class>>16) != PCI_BASE_CLASS_BRIDGE) pcic_map_pci_device(pcic, dev, node); pcic_fill_irq(pcic, dev, node); }}/* * pcic_pin_to_irq() is exported to ebus.c. */unsigned intpcic_pin_to_irq(unsigned int pin, char *name){ struct linux_pcic *pcic = &pcic0; unsigned int irq; unsigned int ivec; if (pin < 4) { ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_LO); irq = ivec >> (pin << 2) & 0xF; } else if (pin < 8) { ivec = readw(pcic->pcic_regs+PCI_INT_SELECT_HI); irq = ivec >> ((pin-4) << 2) & 0xF; } else { /* Corrupted map */ printk("PCIC: BAD PIN %d FOR %s\n", pin, name); for (;;) {} /* XXX Cannot panic properly in case of PROLL */ }/* P3 */ /* printk("PCIC: dev %s pin %d ivec 0x%x irq %x\n", name, pin, ivec, irq); */ return irq;}/* Makes compiler happy */static volatile int pcic_timer_dummy;static void pcic_clear_clock_irq(void){ pcic_timer_dummy = readl(pcic0.pcic_regs+PCI_SYS_LIMIT);}static void pcic_timer_handler (int irq, void *h, struct pt_regs *regs){ pcic_clear_clock_irq(); do_timer(regs);}#define USECS_PER_JIFFY 10000 /* We have 100HZ "standard" timer for sparc */#define TICK_TIMER_LIMIT ((100*1000000/4)/100)void __init pci_time_init(void){ struct linux_pcic *pcic = &pcic0; unsigned long v; int timer_irq, irq; do_get_fast_time = pci_do_gettimeofday; /* A hack until do_gettimeofday prototype is moved to arch specific headers and btfixupped. Patch do_gettimeofday with ba pci_do_gettimeofday; nop */ ((unsigned int *)do_gettimeofday)[0] = 0x10800000 | ((((unsigned long)pci_do_gettimeofday - (unsigned long)do_gettimeofday) >> 2) & 0x003fffff); ((unsigned int *)do_gettimeofday)[1] = 0x01000000; BTFIXUPSET_CALL(bus_do_settimeofday, pci_do_settimeofday, BTFIXUPCALL_NORM); btfixup(); writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT); /* PROM should set appropriate irq */ v = readb(pcic->pcic_regs+PCI_COUNTER_IRQ); timer_irq = PCI_COUNTER_IRQ_SYS(v); writel (PCI_COUNTER_IRQ_SET(timer_irq, 0), pcic->pcic_regs+PCI_COUNTER_IRQ); irq = request_irq(timer_irq, pcic_timer_handler, (SA_INTERRUPT | SA_STATIC_ALLOC), "timer", NULL); if (irq) { prom_printf("time_init: unable to attach IRQ%d\n", timer_irq); prom_halt(); } __sti();}static __inline__ unsigned long do_gettimeoffset(void){ struct tasklet_struct *t; unsigned long offset = 0; /* * We devide all to 100 * to have microsecond resolution and to avoid overflow */ unsigned long count = readl(pcic0.pcic_regs+PCI_SYS_COUNTER) & ~PCI_SYS_COUNTER_OVERFLOW; count = ((count/100)*USECS_PER_JIFFY) / (TICK_TIMER_LIMIT/100); t = &bh_task_vec[TIMER_BH]; if (test_bit(TASKLET_STATE_SCHED, &t->state)) offset = 1000000; return offset + count;}extern volatile unsigned long wall_jiffies;static void pci_do_gettimeofday(struct timeval *tv){ unsigned long flags; save_and_cli(flags); *tv = xtime; tv->tv_usec += do_gettimeoffset(); /* * xtime is atomically updated in timer_bh. The difference * between jiffies and wall_jiffies is nonzero if the timer * bottom half hasnt executed yet. */ if ((jiffies - wall_jiffies) != 0) tv->tv_usec += USECS_PER_JIFFY; restore_flags(flags); if (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; tv->tv_sec++; } }static void pci_do_settimeofday(struct timeval *tv){ cli(); tv->tv_usec -= do_gettimeoffset(); if(tv->tv_usec < 0) { tv->tv_usec += 1000000; tv->tv_sec--; } xtime = *tv; time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; sti();}#if 0static void watchdog_reset() { writeb(0, pcic->pcic_regs+PCI_SYS_STATUS);}#endif/* * Other archs parse arguments here. */char * __init pcibios_setup(char *str){ return str;}/* */void pcibios_update_resource(struct pci_dev *pdev, struct resource *res1, struct resource *res2, int index){}void pcibios_align_resource(void *data, struct resource *res, unsigned long size){}int pcibios_enable_device(struct pci_dev *pdev){ return 0;}/* * NMI */void pcic_nmi(unsigned int pend, struct pt_regs *regs){ pend = flip_dword(pend); if (!pcic_speculative || (pend & PCI_SYS_INT_PENDING_PIO) == 0) { /* * XXX On CP-1200 PCI #SERR may happen, we do not know * what to do about it yet. */ printk("Aiee, NMI pend 0x%x pc 0x%x spec %d, hanging\n", pend, (int)regs->pc, pcic_speculative); for (;;) { } } pcic_speculative = 0; pcic_trapped = 1; regs->pc = regs->npc; regs->npc += 4;}/* * XXX Gleb wrote me that he needs this for X server (only). * Since we successfuly use XF86_FBDev, we do not need these anymore. * * Following code added to handle extra PCI-related system calls */asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, unsigned char *buf){ unsigned char ubyte; unsigned short ushort; unsigned int uint; int err = 0; if(!suser()) return -EPERM; switch(len) { case 1: pcibios_read_config_byte(bus, dfn, off, &ubyte); put_user(ubyte, (unsigned char *)buf); break; case 2: pcibios_read_config_word(bus, dfn, off, &ushort); put_user(ushort, (unsigned short *)buf); break; case 4: pcibios_read_config_dword(bus, dfn, off, &uint); put_user(uint, (unsigned int *)buf); break; default: err = -EINVAL; break; }; return err;}asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn, unsigned long off, unsigned long len, unsigned char *buf){ unsigned char ubyte; unsigned short ushort; unsigned int uint; int err = 0; if(!suser()) return -EPERM; switch(len) { case 1: err = get_user(ubyte, (unsigned char *)buf); if(err) break; pcibios_write_config_byte(bus, dfn, off, ubyte); break; case 2: err = get_user(ushort, (unsigned short *)buf); if(err) break; pcibios_write_config_byte(bus, dfn, off, ushort); break; case 4: err = get_user(uint, (unsigned int *)buf); if(err) break; pcibios_write_config_byte(bus, dfn, off, uint); break; default: err = -EINVAL; break; }; return err;} static inline unsigned long get_irqmask(int irq_nr){ return 1 << irq_nr;}static inline char *pcic_irq_itoa(unsigned int irq){ static char buff[16]; sprintf(buff, "%d", irq); return buff;}static void pcic_disable_irq(unsigned int irq_nr){ unsigned long mask, flags; mask = get_irqmask(irq_nr); save_and_cli(flags); writel(mask, pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_SET); restore_flags(flags);}static void pcic_enable_irq(unsigned int irq_nr){ unsigned long mask, flags; mask = get_irqmask(irq_nr); save_and_cli(flags); writel(mask, pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_CLEAR); restore_flags(flags);}static void pcic_clear_profile_irq(int cpu){ printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__);}static void pcic_load_profile_irq(int cpu, unsigned int limit){ printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__);}/* We assume the caller is local cli()'d when these are called, or else * very bizarre behavior will result. */static void pcic_disable_pil_irq(unsigned int pil){ writel(get_irqmask(pil), pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_SET);}static void pcic_enable_pil_irq(unsigned int pil){ writel(get_irqmask(pil), pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_CLEAR);}void __init sun4m_pci_init_IRQ(void){ BTFIXUPSET_CALL(enable_irq, pcic_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_irq, pcic_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(enable_pil_irq, pcic_enable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_pil_irq, pcic_disable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, pcic_clear_clock_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_profile_irq, pcic_clear_profile_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(load_profile_irq, pcic_load_profile_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(__irq_itoa, pcic_irq_itoa, BTFIXUPCALL_NORM);}int pcibios_assign_resource(struct pci_dev *pdev, int resource){ return -ENXIO;}/* * This probably belongs here rather than ioport.c because * we do not want this crud linked into SBus kernels. * Also, think for a moment about likes of floppy.c that * include architecture specific parts. They may want to redefine ins/outs. * * We do not use horroble macroses here because we want to * advance pointer by sizeof(size). */void outsb(unsigned long addr, const void *src, unsigned long count) { while (count) { count -= 1; writeb(*(const char *)src, addr); src += 1; addr += 1; }}void outsw(unsigned long addr, const void *src, unsigned long count) { while (count) { count -= 2; writew(*(const short *)src, addr); src += 2; addr += 2; }}void outsl(unsigned long addr, const void *src, unsigned long count) { while (count) { count -= 4; writel(*(const long *)src, addr); src += 4; addr += 4; }}void insb(unsigned long addr, void *dst, unsigned long count) { while (count) { count -= 1; *(unsigned char *)dst = readb(addr); dst += 1; addr += 1; }}void insw(unsigned long addr, void *dst, unsigned long count) { while (count) { count -= 2; *(unsigned short *)dst = readw(addr); dst += 2; addr += 2; }}void insl(unsigned long addr, void *dst, unsigned long count) { while (count) { count -= 4; /* * XXX I am sure we are in for an unaligned trap here. */ *(unsigned long *)dst = readl(addr); dst += 4; addr += 4; }}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -