prom.c
来自「linux 内核源代码」· C语言 代码 · 共 1,743 行 · 第 1/3 页
C
1,743 行
: "0" (mask), "1" (val), "i" (FPRS_FEF), "r" (&cacheline[0]), "i" (ASI_BLK_COMMIT_P)); }}struct schizo_irq_data { unsigned long pbm_regs; unsigned long sync_reg; u32 portid; int chip_version;};static unsigned int schizo_irq_build(struct device_node *dp, unsigned int ino, void *_data){ struct schizo_irq_data *irq_data = _data; unsigned long pbm_regs = irq_data->pbm_regs; unsigned long imap, iclr; int ign_fixup; int virt_irq; int is_tomatillo; ino &= 0x3f; /* Now build the IRQ bucket. */ imap = schizo_ino_to_imap(pbm_regs, ino); iclr = schizo_ino_to_iclr(pbm_regs, ino); /* On Schizo, no inofixup occurs. This is because each * INO has it's own IMAP register. On Psycho and Sabre * there is only one IMAP register for each PCI slot even * though four different INOs can be generated by each * PCI slot. * * But, for JBUS variants (essentially, Tomatillo), we have * to fixup the lowest bit of the interrupt group number. */ ign_fixup = 0; is_tomatillo = (irq_data->sync_reg != 0UL); if (is_tomatillo) { if (irq_data->portid & 1) ign_fixup = (1 << 6); } virt_irq = build_irq(ign_fixup, iclr, imap); if (is_tomatillo) { irq_install_pre_handler(virt_irq, tomatillo_wsync_handler, ((irq_data->chip_version <= 4) ? (void *) 1 : (void *) 0), (void *) irq_data->sync_reg); } return virt_irq;}static void __init __schizo_irq_trans_init(struct device_node *dp, int is_tomatillo){ const struct linux_prom64_registers *regs; struct schizo_irq_data *irq_data; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = schizo_irq_build; irq_data = prom_early_alloc(sizeof(struct schizo_irq_data)); regs = of_get_property(dp, "reg", NULL); dp->irq_trans->data = irq_data; irq_data->pbm_regs = regs[0].phys_addr; if (is_tomatillo) irq_data->sync_reg = regs[3].phys_addr + 0x1a18UL; else irq_data->sync_reg = 0UL; irq_data->portid = of_getintprop_default(dp, "portid", 0); irq_data->chip_version = of_getintprop_default(dp, "version#", 0);}static void __init schizo_irq_trans_init(struct device_node *dp){ __schizo_irq_trans_init(dp, 0);}static void __init tomatillo_irq_trans_init(struct device_node *dp){ __schizo_irq_trans_init(dp, 1);}static unsigned int pci_sun4v_irq_build(struct device_node *dp, unsigned int devino, void *_data){ u32 devhandle = (u32) (unsigned long) _data; return sun4v_build_irq(devhandle, devino);}static void __init pci_sun4v_irq_trans_init(struct device_node *dp){ const struct linux_prom64_registers *regs; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = pci_sun4v_irq_build; regs = of_get_property(dp, "reg", NULL); dp->irq_trans->data = (void *) (unsigned long) ((regs->phys_addr >> 32UL) & 0x0fffffff);}struct fire_irq_data { unsigned long pbm_regs; u32 portid;};#define FIRE_IMAP_BASE 0x001000#define FIRE_ICLR_BASE 0x001400static unsigned long fire_imap_offset(unsigned long ino){ return FIRE_IMAP_BASE + (ino * 8UL);}static unsigned long fire_iclr_offset(unsigned long ino){ return FIRE_ICLR_BASE + (ino * 8UL);}static unsigned long fire_ino_to_iclr(unsigned long pbm_regs, unsigned int ino){ return pbm_regs + fire_iclr_offset(ino);}static unsigned long fire_ino_to_imap(unsigned long pbm_regs, unsigned int ino){ return pbm_regs + fire_imap_offset(ino);}static unsigned int fire_irq_build(struct device_node *dp, unsigned int ino, void *_data){ struct fire_irq_data *irq_data = _data; unsigned long pbm_regs = irq_data->pbm_regs; unsigned long imap, iclr; unsigned long int_ctrlr; ino &= 0x3f; /* Now build the IRQ bucket. */ imap = fire_ino_to_imap(pbm_regs, ino); iclr = fire_ino_to_iclr(pbm_regs, ino); /* Set the interrupt controller number. */ int_ctrlr = 1 << 6; upa_writeq(int_ctrlr, imap); /* The interrupt map registers do not have an INO field * like other chips do. They return zero in the INO * field, and the interrupt controller number is controlled * in bits 6 to 9. So in order for build_irq() to get * the INO right we pass it in as part of the fixup * which will get added to the map register zero value * read by build_irq(). */ ino |= (irq_data->portid << 6); ino -= int_ctrlr; return build_irq(ino, iclr, imap);}static void __init fire_irq_trans_init(struct device_node *dp){ const struct linux_prom64_registers *regs; struct fire_irq_data *irq_data; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = fire_irq_build; irq_data = prom_early_alloc(sizeof(struct fire_irq_data)); regs = of_get_property(dp, "reg", NULL); dp->irq_trans->data = irq_data; irq_data->pbm_regs = regs[0].phys_addr; irq_data->portid = of_getintprop_default(dp, "portid", 0);}#endif /* CONFIG_PCI */#ifdef CONFIG_SBUS/* INO number to IMAP register offset for SYSIO external IRQ's. * This should conform to both Sunfire/Wildfire server and Fusion * desktop designs. */#define SYSIO_IMAP_SLOT0 0x2c00UL#define SYSIO_IMAP_SLOT1 0x2c08UL#define SYSIO_IMAP_SLOT2 0x2c10UL#define SYSIO_IMAP_SLOT3 0x2c18UL#define SYSIO_IMAP_SCSI 0x3000UL#define SYSIO_IMAP_ETH 0x3008UL#define SYSIO_IMAP_BPP 0x3010UL#define SYSIO_IMAP_AUDIO 0x3018UL#define SYSIO_IMAP_PFAIL 0x3020UL#define SYSIO_IMAP_KMS 0x3028UL#define SYSIO_IMAP_FLPY 0x3030UL#define SYSIO_IMAP_SHW 0x3038UL#define SYSIO_IMAP_KBD 0x3040UL#define SYSIO_IMAP_MS 0x3048UL#define SYSIO_IMAP_SER 0x3050UL#define SYSIO_IMAP_TIM0 0x3060UL#define SYSIO_IMAP_TIM1 0x3068UL#define SYSIO_IMAP_UE 0x3070UL#define SYSIO_IMAP_CE 0x3078UL#define SYSIO_IMAP_SBERR 0x3080UL#define SYSIO_IMAP_PMGMT 0x3088UL#define SYSIO_IMAP_GFX 0x3090UL#define SYSIO_IMAP_EUPA 0x3098UL#define bogon ((unsigned long) -1)static unsigned long sysio_irq_offsets[] = { /* SBUS Slot 0 --> 3, level 1 --> 7 */ SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, /* Onboard devices (not relevant/used on SunFire). */ SYSIO_IMAP_SCSI, SYSIO_IMAP_ETH, SYSIO_IMAP_BPP, bogon, SYSIO_IMAP_AUDIO, SYSIO_IMAP_PFAIL, bogon, bogon, SYSIO_IMAP_KMS, SYSIO_IMAP_FLPY, SYSIO_IMAP_SHW, SYSIO_IMAP_KBD, SYSIO_IMAP_MS, SYSIO_IMAP_SER, bogon, bogon, SYSIO_IMAP_TIM0, SYSIO_IMAP_TIM1, bogon, bogon, SYSIO_IMAP_UE, SYSIO_IMAP_CE, SYSIO_IMAP_SBERR, SYSIO_IMAP_PMGMT, SYSIO_IMAP_GFX, SYSIO_IMAP_EUPA,};#undef bogon#define NUM_SYSIO_OFFSETS ARRAY_SIZE(sysio_irq_offsets)/* Convert Interrupt Mapping register pointer to associated * Interrupt Clear register pointer, SYSIO specific version. */#define SYSIO_ICLR_UNUSED0 0x3400UL#define SYSIO_ICLR_SLOT0 0x3408UL#define SYSIO_ICLR_SLOT1 0x3448UL#define SYSIO_ICLR_SLOT2 0x3488UL#define SYSIO_ICLR_SLOT3 0x34c8ULstatic unsigned long sysio_imap_to_iclr(unsigned long imap){ unsigned long diff = SYSIO_ICLR_UNUSED0 - SYSIO_IMAP_SLOT0; return imap + diff;}static unsigned int sbus_of_build_irq(struct device_node *dp, unsigned int ino, void *_data){ unsigned long reg_base = (unsigned long) _data; const struct linux_prom_registers *regs; unsigned long imap, iclr; int sbus_slot = 0; int sbus_level = 0; ino &= 0x3f; regs = of_get_property(dp, "reg", NULL); if (regs) sbus_slot = regs->which_io; if (ino < 0x20) ino += (sbus_slot * 8); imap = sysio_irq_offsets[ino]; if (imap == ((unsigned long)-1)) { prom_printf("get_irq_translations: Bad SYSIO INO[%x]\n", ino); prom_halt(); } imap += reg_base; /* SYSIO inconsistency. For external SLOTS, we have to select * the right ICLR register based upon the lower SBUS irq level * bits. */ if (ino >= 0x20) { iclr = sysio_imap_to_iclr(imap); } else { sbus_level = ino & 0x7; switch(sbus_slot) { case 0: iclr = reg_base + SYSIO_ICLR_SLOT0; break; case 1: iclr = reg_base + SYSIO_ICLR_SLOT1; break; case 2: iclr = reg_base + SYSIO_ICLR_SLOT2; break; default: case 3: iclr = reg_base + SYSIO_ICLR_SLOT3; break; }; iclr += ((unsigned long)sbus_level - 1UL) * 8UL; } return build_irq(sbus_level, iclr, imap);}static void __init sbus_irq_trans_init(struct device_node *dp){ const struct linux_prom64_registers *regs; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = sbus_of_build_irq; regs = of_get_property(dp, "reg", NULL); dp->irq_trans->data = (void *) (unsigned long) regs->phys_addr;}#endif /* CONFIG_SBUS */static unsigned int central_build_irq(struct device_node *dp, unsigned int ino, void *_data){ struct device_node *central_dp = _data; struct of_device *central_op = of_find_device_by_node(central_dp); struct resource *res; unsigned long imap, iclr; u32 tmp; if (!strcmp(dp->name, "eeprom")) { res = ¢ral_op->resource[5]; } else if (!strcmp(dp->name, "zs")) { res = ¢ral_op->resource[4]; } else if (!strcmp(dp->name, "clock-board")) { res = ¢ral_op->resource[3]; } else { return ino; } imap = res->start + 0x00UL; iclr = res->start + 0x10UL; /* Set the INO state to idle, and disable. */ upa_writel(0, iclr); upa_readl(iclr); tmp = upa_readl(imap); tmp &= ~0x80000000; upa_writel(tmp, imap); return build_irq(0, iclr, imap);}static void __init central_irq_trans_init(struct device_node *dp){ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = central_build_irq; dp->irq_trans->data = dp;}struct irq_trans { const char *name; void (*init)(struct device_node *);};#ifdef CONFIG_PCIstatic struct irq_trans __initdata pci_irq_trans_table[] = { { "SUNW,sabre", sabre_irq_trans_init }, { "pci108e,a000", sabre_irq_trans_init }, { "pci108e,a001", sabre_irq_trans_init }, { "SUNW,psycho", psycho_irq_trans_init }, { "pci108e,8000", psycho_irq_trans_init }, { "SUNW,schizo", schizo_irq_trans_init }, { "pci108e,8001", schizo_irq_trans_init }, { "SUNW,schizo+", schizo_irq_trans_init }, { "pci108e,8002", schizo_irq_trans_init }, { "SUNW,tomatillo", tomatillo_irq_trans_init }, { "pci108e,a801", tomatillo_irq_trans_init }, { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init }, { "pciex108e,80f0", fire_irq_trans_init },};#endifstatic unsigned int sun4v_vdev_irq_build(struct device_node *dp, unsigned int devino, void *_data){ u32 devhandle = (u32) (unsigned long) _data; return sun4v_build_irq(devhandle, devino);}static void __init sun4v_vdev_irq_trans_init(struct device_node *dp){ const struct linux_prom64_registers *regs; dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller)); dp->irq_trans->irq_build = sun4v_vdev_irq_build; regs = of_get_property(dp, "reg", NULL); dp->irq_trans->data = (void *) (unsigned long) ((regs->phys_addr >> 32UL) & 0x0fffffff);}static void __init irq_trans_init(struct device_node *dp){#ifdef CONFIG_PCI const char *model; int i;#endif#ifdef CONFIG_PCI model = of_get_property(dp, "model", NULL); if (!model) model = of_get_property(dp, "compatible", NULL); if (model) { for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) { struct irq_trans *t = &pci_irq_trans_table[i]; if (!strcmp(model, t->name)) return t->init(dp); } }#endif#ifdef CONFIG_SBUS if (!strcmp(dp->name, "sbus") || !strcmp(dp->name, "sbi")) return sbus_irq_trans_init(dp);#endif if (!strcmp(dp->name, "fhc") && !strcmp(dp->parent->name, "central")) return central_irq_trans_init(dp); if (!strcmp(dp->name, "virtual-devices") || !strcmp(dp->name, "niu")) return sun4v_vdev_irq_trans_init(dp);}static int is_root_node(const struct device_node *dp){ if (!dp) return 0; return (dp->parent == NULL);}/* The following routines deal with the black magic of fully naming a * node. * * Certain well known named nodes are just the simple name string. * * Actual devices have an address specifier appended to the base name * string, like this "foo@addr". The "addr" can be in any number of * formats, and the platform plus the type of the node determine the * format and how it is constructed. * * For children of the ROOT node, the naming convention is fixed and * determined by whether this is a sun4u or sun4v system. * * For children of other nodes, it is bus type specific. So * we walk up the tree until we discover a "device_type" property * we recognize and we go from there. * * As an example, the boot device on my workstation has a full path: * * /pci@1e,600000/ide@d/disk@0,0:c */static void __init sun4v_path_component(struct device_node *dp, char *tmp_buf){ struct linux_prom64_registers *regs; struct property *rprop; u32 high_bits, low_bits, type; rprop = of_find_property(dp, "reg", NULL); if (!rprop) return; regs = rprop->value; if (!is_root_node(dp->parent)) { sprintf(tmp_buf, "%s@%x,%x", dp->name, (unsigned int) (regs->phys_addr >> 32UL), (unsigned int) (regs->phys_addr & 0xffffffffUL)); return; } type = regs->phys_addr >> 60UL; high_bits = (regs->phys_addr >> 32UL) & 0x0fffffffUL; low_bits = (regs->phys_addr & 0xffffffffUL); if (type == 0 || type == 8) { const char *prefix = (type == 0) ? "m" : "i"; if (low_bits) sprintf(tmp_buf, "%s@%s%x,%x", dp->name, prefix, high_bits, low_bits); else sprintf(tmp_buf, "%s@%s%x", dp->name, prefix, high_bits); } else if (type == 12) { sprintf(tmp_buf, "%s@%x", dp->name, high_bits); }}static void __init sun4u_path_component(struct device_node *dp, char *tmp_buf){ struct linux_prom64_registers *regs; struct property *prop; prop = of_find_property(dp, "reg", NULL); if (!prop) return; regs = prop->value; if (!is_root_node(dp->parent)) { sprintf(tmp_buf, "%s@%x,%x", dp->name, (unsigned int) (regs->phys_addr >> 32UL), (unsigned int) (regs->phys_addr & 0xffffffffUL)); return; } prop = of_find_property(dp, "upa-portid", NULL); if (!prop) prop = of_find_property(dp, "portid", NULL); if (prop) { unsigned long mask = 0xffffffffUL; if (tlb_type >= cheetah) mask = 0x7fffff; sprintf(tmp_buf, "%s@%x,%x", dp->name, *(u32 *)prop->value, (unsigned int) (regs->phys_addr & mask)); }}/* "name@slot,offset" */static void __init sbus_path_component(struct device_node *dp, char *tmp_buf){ struct linux_prom_registers *regs; struct property *prop;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?