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 = &central_op->resource[5];	} else if (!strcmp(dp->name, "zs")) {		res = &central_op->resource[4];	} else if (!strcmp(dp->name, "clock-board")) {		res = &central_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 + -
显示快捷键?