📄 pci_psycho.c
字号:
* all PCI-to-PCI bridges under each PBM. The generic bus * probing will fix them up. */ pbm_pci_bridge_renumber(&p->pbm_B, p->pbm_B.pci_first_busno); pbm_pci_bridge_renumber(&p->pbm_A, p->pbm_A.pci_first_busno); /* Move PBM A out of the way. */ pbm = &p->pbm_A; addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno, 0, PBM_BRIDGE_BUS); pci_config_write8(addr, 0xff); addr = psycho_pci_config_mkaddr(pbm, 0xff, 0, PBM_BRIDGE_SUBORDINATE); pci_config_write8(addr, 0xff); /* Now we can safely renumber both PBMs. */ pbm_renumber(&p->pbm_B, p->pbm_B.pci_first_busno); pbm_renumber(&p->pbm_A, 0xff);}static void __init pbm_config_busmastering(struct pci_pbm_info *pbm){ u8 *addr; /* Set cache-line size to 64 bytes, this is actually * a nop but I do it for completeness. */ addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno, 0, PCI_CACHE_LINE_SIZE); pci_config_write8(addr, 64 / sizeof(u32)); /* Set PBM latency timer to 64 PCI clocks. */ addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno, 0, PCI_LATENCY_TIMER); pci_config_write8(addr, 64);}static void __init pbm_scan_bus(struct pci_controller_info *p, struct pci_pbm_info *pbm){ pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, p->pci_ops, pbm); pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node); pci_record_assignments(pbm, pbm->pci_bus); pci_assign_unassigned(pbm, pbm->pci_bus); pci_fixup_irq(pbm, pbm->pci_bus); pci_determine_66mhz_disposition(pbm, pbm->pci_bus); pci_setup_busmastering(pbm, pbm->pci_bus);}static void __init psycho_scan_bus(struct pci_controller_info *p){ pbm_bridge_reconfigure(p); pbm_config_busmastering(&p->pbm_B); p->pbm_B.is_66mhz_capable = 0; pbm_config_busmastering(&p->pbm_A); p->pbm_A.is_66mhz_capable = 1; pbm_scan_bus(p, &p->pbm_B); pbm_scan_bus(p, &p->pbm_A); /* After the PCI bus scan is complete, we can register * the error interrupt handlers. */ psycho_register_error_handlers(p);}static void __init psycho_iommu_init(struct pci_controller_info *p){ unsigned long tsbbase, i; u64 control; /* Setup initial software IOMMU state. */ spin_lock_init(&p->iommu.lock); p->iommu.iommu_cur_ctx = 0; /* Register addresses. */ p->iommu.iommu_control = p->controller_regs + PSYCHO_IOMMU_CONTROL; p->iommu.iommu_tsbbase = p->controller_regs + PSYCHO_IOMMU_TSBBASE; p->iommu.iommu_flush = p->controller_regs + PSYCHO_IOMMU_FLUSH; /* PSYCHO's IOMMU lacks ctx flushing. */ p->iommu.iommu_ctxflush = 0; /* We use the main control register of PSYCHO as the write * completion register. */ p->iommu.write_complete_reg = p->controller_regs + PSYCHO_CONTROL; /* * Invalidate TLB Entries. */ control = psycho_read(p->controller_regs + PSYCHO_IOMMU_CONTROL); control |= PSYCHO_IOMMU_CTRL_DENAB; psycho_write(p->controller_regs + PSYCHO_IOMMU_CONTROL, control); for(i = 0; i < 16; i++) { psycho_write(p->controller_regs + PSYCHO_IOMMU_TAG + (i * 8UL), 0); psycho_write(p->controller_regs + PSYCHO_IOMMU_DATA + (i * 8UL), 0); } /* Leave diag mode enabled for full-flushing done * in pci_iommu.c */ /* Using assumed page size 8K with 128K entries we need 1MB iommu page * table (128K ioptes * 8 bytes per iopte). This is * page order 7 on UltraSparc. */ tsbbase = __get_free_pages(GFP_KERNEL, 7); if (!tsbbase) { prom_printf("PSYCHO_IOMMU: Error, gfp(tsb) failed.\n"); prom_halt(); } p->iommu.page_table = (iopte_t *)tsbbase; p->iommu.page_table_sz_bits = 17; p->iommu.page_table_map_base = 0xc0000000; p->iommu.dma_addr_mask = 0xffffffff; memset((char *)tsbbase, 0, PAGE_SIZE << 7); /* We start with no consistent mappings. */ p->iommu.lowest_consistent_map = 1 << (p->iommu.page_table_sz_bits - PBM_LOGCLUSTERS); for (i = 0; i < PBM_NCLUSTERS; i++) { p->iommu.alloc_info[i].flush = 0; p->iommu.alloc_info[i].next = 0; } psycho_write(p->controller_regs + PSYCHO_IOMMU_TSBBASE, __pa(tsbbase)); control = psycho_read(p->controller_regs + PSYCHO_IOMMU_CONTROL); control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ); control |= (PSYCHO_IOMMU_TSBSZ_128K | PSYCHO_IOMMU_CTRL_ENAB); psycho_write(p->controller_regs + PSYCHO_IOMMU_CONTROL, control); /* If necessary, hook us up for starfire IRQ translations. */ if(this_is_starfire) p->starfire_cookie = starfire_hookup(p->portid); else p->starfire_cookie = NULL;}#define PSYCHO_IRQ_RETRY 0x1a00UL#define PSYCHO_PCIA_DIAG 0x2020UL#define PSYCHO_PCIB_DIAG 0x4020UL#define PSYCHO_PCIDIAG_RESV 0xffffffffffffff80 /* Reserved */#define PSYCHO_PCIDIAG_DRETRY 0x0000000000000040 /* Disable retry limit */#define PSYCHO_PCIDIAG_DISYNC 0x0000000000000020 /* Disable DMA wr / irq sync */#define PSYCHO_PCIDIAG_DDWSYNC 0x0000000000000010 /* Disable DMA wr / PIO rd sync */#define PSYCHO_PCIDIAG_IDDPAR 0x0000000000000008 /* Invert DMA data parity */#define PSYCHO_PCIDIAG_IPDPAR 0x0000000000000004 /* Invert PIO data parity */#define PSYCHO_PCIDIAG_IPAPAR 0x0000000000000002 /* Invert PIO address parity */#define PSYCHO_PCIDIAG_LPBACK 0x0000000000000001 /* Enable loopback mode */static void psycho_controller_hwinit(struct pci_controller_info *p){ u64 tmp; /* PROM sets the IRQ retry value too low, increase it. */ psycho_write(p->controller_regs + PSYCHO_IRQ_RETRY, 0xff); /* Enable arbiter for all PCI slots. */ tmp = psycho_read(p->controller_regs + PSYCHO_PCIA_CTRL); tmp |= PSYCHO_PCICTRL_AEN; psycho_write(p->controller_regs + PSYCHO_PCIA_CTRL, tmp); tmp = psycho_read(p->controller_regs + PSYCHO_PCIB_CTRL); tmp |= PSYCHO_PCICTRL_AEN; psycho_write(p->controller_regs + PSYCHO_PCIB_CTRL, tmp); /* Disable DMA write / PIO read synchronization on * both PCI bus segments. * [ U2P Erratum 1243770, STP2223BGA data sheet ] */ tmp = psycho_read(p->controller_regs + PSYCHO_PCIA_DIAG); tmp |= PSYCHO_PCIDIAG_DDWSYNC; psycho_write(p->controller_regs + PSYCHO_PCIA_DIAG, tmp); tmp = psycho_read(p->controller_regs + PSYCHO_PCIB_DIAG); tmp |= PSYCHO_PCIDIAG_DDWSYNC; psycho_write(p->controller_regs + PSYCHO_PCIB_DIAG, tmp);}static void __init pbm_register_toplevel_resources(struct pci_controller_info *p, struct pci_pbm_info *pbm){ char *name = pbm->name; sprintf(name, "PSYCHO%d PBM%c", p->index, (pbm == &p->pbm_A ? 'A' : 'B')); pbm->io_space.name = pbm->mem_space.name = name; request_resource(&ioport_resource, &pbm->io_space); request_resource(&iomem_resource, &pbm->mem_space);}static void psycho_pbm_strbuf_init(struct pci_controller_info *p, struct pci_pbm_info *pbm, int is_pbm_a){ unsigned long base = p->controller_regs; u64 control; if (is_pbm_a) { pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_A; pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_A; pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_A; } else { pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_B; pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_B; pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_B; } /* PSYCHO's streaming buffer lacks ctx flushing. */ pbm->stc.strbuf_ctxflush = 0; pbm->stc.strbuf_ctxmatch_base = 0; pbm->stc.strbuf_flushflag = (volatile unsigned long *) ((((unsigned long)&pbm->stc.__flushflag_buf[0]) + 63UL) & ~63UL); pbm->stc.strbuf_flushflag_pa = (unsigned long) __pa(pbm->stc.strbuf_flushflag); /* Enable the streaming buffer. We have to be careful * just in case OBP left it with LRU locking enabled. * * It is possible to control if PBM will be rerun on * line misses. Currently I just retain whatever setting * OBP left us with. All checks so far show it having * a value of zero. */#undef PSYCHO_STRBUF_RERUN_ENABLE#undef PSYCHO_STRBUF_RERUN_DISABLE control = psycho_read(pbm->stc.strbuf_control); control |= PSYCHO_STRBUF_CTRL_ENAB; control &= ~(PSYCHO_STRBUF_CTRL_LENAB | PSYCHO_STRBUF_CTRL_LPTR);#ifdef PSYCHO_STRBUF_RERUN_ENABLE control &= ~(PSYCHO_STRBUF_CTRL_RRDIS);#else#ifdef PSYCHO_STRBUF_RERUN_DISABLE control |= PSYCHO_STRBUF_CTRL_RRDIS;#endif#endif psycho_write(pbm->stc.strbuf_control, control); pbm->stc.strbuf_enabled = 1;}#define PSYCHO_IOSPACE_A 0x002000000UL#define PSYCHO_IOSPACE_B 0x002010000UL#define PSYCHO_IOSPACE_SIZE 0x00000ffffUL#define PSYCHO_MEMSPACE_A 0x100000000UL#define PSYCHO_MEMSPACE_B 0x180000000UL#define PSYCHO_MEMSPACE_SIZE 0x07fffffffULstatic void psycho_pbm_init(struct pci_controller_info *p, int prom_node, int is_pbm_a){ unsigned int busrange[2]; struct pci_pbm_info *pbm; int err; if (is_pbm_a) { pbm = &p->pbm_A; pbm->io_space.start = p->controller_regs + PSYCHO_IOSPACE_A; pbm->mem_space.start = p->controller_regs + PSYCHO_MEMSPACE_A; } else { pbm = &p->pbm_B; pbm->io_space.start = p->controller_regs + PSYCHO_IOSPACE_B; pbm->mem_space.start = p->controller_regs + PSYCHO_MEMSPACE_B; } pbm->io_space.end = pbm->io_space.start + PSYCHO_IOSPACE_SIZE; pbm->io_space.flags = IORESOURCE_IO; pbm->mem_space.end = pbm->mem_space.start + PSYCHO_MEMSPACE_SIZE; pbm->mem_space.flags = IORESOURCE_MEM; pbm_register_toplevel_resources(p, pbm); pbm->parent = p; pbm->prom_node = prom_node; prom_getstring(prom_node, "name", pbm->prom_name, sizeof(pbm->prom_name)); err = prom_getproperty(prom_node, "ranges", (char *)pbm->pbm_ranges, sizeof(pbm->pbm_ranges)); if (err != -1) pbm->num_pbm_ranges = (err / sizeof(struct linux_prom_pci_ranges)); else pbm->num_pbm_ranges = 0; err = prom_getproperty(prom_node, "interrupt-map", (char *)pbm->pbm_intmap, sizeof(pbm->pbm_intmap)); if (err != -1) { pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap)); err = prom_getproperty(prom_node, "interrupt-map-mask", (char *)&pbm->pbm_intmask, sizeof(pbm->pbm_intmask)); if (err == -1) { prom_printf("PSYCHO-PBM: Fatal error, no " "interrupt-map-mask.\n"); prom_halt(); } } else { pbm->num_pbm_intmap = 0; memset(&pbm->pbm_intmask, 0, sizeof(pbm->pbm_intmask)); } err = prom_getproperty(prom_node, "bus-range", (char *)&busrange[0], sizeof(busrange)); if (err == 0 || err == -1) { prom_printf("PSYCHO-PBM: Fatal error, no bus-range.\n"); prom_halt(); } pbm->pci_first_busno = busrange[0]; pbm->pci_last_busno = busrange[1]; psycho_pbm_strbuf_init(p, pbm, is_pbm_a);}#define PSYCHO_CONFIGSPACE 0x001000000ULvoid __init psycho_init(int node){ struct linux_prom64_registers pr_regs[3]; struct pci_controller_info *p; unsigned long flags; u32 upa_portid; int is_pbm_a, err; upa_portid = prom_getintdefault(node, "upa-portid", 0xff); spin_lock_irqsave(&pci_controller_lock, flags); for(p = pci_controller_root; p; p = p->next) { if (p->portid == upa_portid) { spin_unlock_irqrestore(&pci_controller_lock, flags); is_pbm_a = (p->pbm_A.prom_node == 0); psycho_pbm_init(p, node, is_pbm_a); return; } } spin_unlock_irqrestore(&pci_controller_lock, flags); p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); if (!p) { prom_printf("PSYCHO: Fatal memory allocation error.\n"); prom_halt(); } memset(p, 0, sizeof(*p)); spin_lock_irqsave(&pci_controller_lock, flags); p->next = pci_controller_root; pci_controller_root = p; spin_unlock_irqrestore(&pci_controller_lock, flags); p->portid = upa_portid; p->index = pci_num_controllers++; p->scan_bus = psycho_scan_bus; p->irq_build = psycho_irq_build; p->base_address_update = psycho_base_address_update; p->resource_adjust = psycho_resource_adjust; p->pci_ops = &psycho_ops; err = prom_getproperty(node, "reg", (char *)&pr_regs[0], sizeof(pr_regs)); if (err == 0 || err == -1) { prom_printf("PSYCHO: Fatal error, no reg property.\n"); prom_halt(); } p->controller_regs = pr_regs[2].phys_addr; printk("PCI: Found PSYCHO, control regs at %016lx\n", p->controller_regs); p->config_space = pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE; printk("PSYCHO: PCI config space at %016lx\n", p->config_space); /* * Psycho's PCI MEM space is mapped to a 2GB aligned area, so * we need to adjust our MEM space mask. */ pci_memspace_mask = 0x7fffffffUL; psycho_controller_hwinit(p); psycho_iommu_init(p); is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); psycho_pbm_init(p, node, is_pbm_a);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -