of_device.c

来自「linux 内核源代码」· C语言 代码 · 共 897 行 · 第 1/2 页

C
897
字号
	if (!parent)		return;	p_op = to_of_device(parent);	bus = of_match_bus(p_op->node);	bus->count_cells(op->node, &na, &ns);	preg = of_get_property(op->node, bus->addr_prop_name, &num_reg);	if (!preg || num_reg == 0)		return;	/* Convert to num-cells.  */	num_reg /= 4;	/* Convert to num-entries.  */	num_reg /= na + ns;	/* Prevent overrunning the op->resources[] array.  */	if (num_reg > PROMREG_MAX) {		printk(KERN_WARNING "%s: Too many regs (%d), "		       "limiting to %d.\n",		       op->node->full_name, num_reg, PROMREG_MAX);		num_reg = PROMREG_MAX;	}	for (index = 0; index < num_reg; index++) {		struct resource *r = &op->resource[index];		u32 addr[OF_MAX_ADDR_CELLS];		const u32 *reg = (preg + (index * ((na + ns) * 4)));		struct device_node *dp = op->node;		struct device_node *pp = p_op->node;		struct of_bus *pbus, *dbus;		u64 size, result = OF_BAD_ADDR;		unsigned long flags;		int dna, dns;		int pna, pns;		size = of_read_addr(reg + na, ns);		flags = bus->get_flags(reg);		memcpy(addr, reg, na * 4);		if (use_1to1_mapping(pp)) {			result = of_read_addr(addr, na);			goto build_res;		}		dna = na;		dns = ns;		dbus = bus;		while (1) {			dp = pp;			pp = dp->parent;			if (!pp) {				result = of_read_addr(addr, dna);				break;			}			pbus = of_match_bus(pp);			pbus->count_cells(dp, &pna, &pns);			if (build_one_resource(dp, dbus, pbus, addr,					       dna, dns, pna))				break;			dna = pna;			dns = pns;			dbus = pbus;		}	build_res:		memset(r, 0, sizeof(*r));		if (of_resource_verbose)			printk("%s reg[%d] -> %lx\n",			       op->node->full_name, index,			       result);		if (result != OF_BAD_ADDR) {			if (tlb_type == hypervisor)				result &= 0x0fffffffffffffffUL;			r->start = result;			r->end = result + size - 1;			r->flags = flags;		}		r->name = op->node->name;	}}static struct device_node * __initapply_interrupt_map(struct device_node *dp, struct device_node *pp,		    const u32 *imap, int imlen, const u32 *imask,		    unsigned int *irq_p){	struct device_node *cp;	unsigned int irq = *irq_p;	struct of_bus *bus;	phandle handle;	const u32 *reg;	int na, num_reg, i;	bus = of_match_bus(pp);	bus->count_cells(dp, &na, NULL);	reg = of_get_property(dp, "reg", &num_reg);	if (!reg || !num_reg)		return NULL;	imlen /= ((na + 3) * 4);	handle = 0;	for (i = 0; i < imlen; i++) {		int j;		for (j = 0; j < na; j++) {			if ((reg[j] & imask[j]) != imap[j])				goto next;		}		if (imap[na] == irq) {			handle = imap[na + 1];			irq = imap[na + 2];			break;		}	next:		imap += (na + 3);	}	if (i == imlen) {		/* Psycho and Sabre PCI controllers can have 'interrupt-map'		 * properties that do not include the on-board device		 * interrupts.  Instead, the device's 'interrupts' property		 * is already a fully specified INO value.		 *		 * Handle this by deciding that, if we didn't get a		 * match in the parent's 'interrupt-map', and the		 * parent is an IRQ translater, then use the parent as		 * our IRQ controller.		 */		if (pp->irq_trans)			return pp;		return NULL;	}	*irq_p = irq;	cp = of_find_node_by_phandle(handle);	return cp;}static unsigned int __init pci_irq_swizzle(struct device_node *dp,					   struct device_node *pp,					   unsigned int irq){	const struct linux_prom_pci_registers *regs;	unsigned int bus, devfn, slot, ret;	if (irq < 1 || irq > 4)		return irq;	regs = of_get_property(dp, "reg", NULL);	if (!regs)		return irq;	bus = (regs->phys_hi >> 16) & 0xff;	devfn = (regs->phys_hi >> 8) & 0xff;	slot = (devfn >> 3) & 0x1f;	if (pp->irq_trans) {		/* Derived from Table 8-3, U2P User's Manual.  This branch		 * is handling a PCI controller that lacks a proper set of		 * interrupt-map and interrupt-map-mask properties.  The		 * Ultra-E450 is one example.		 *		 * The bit layout is BSSLL, where:		 * B: 0 on bus A, 1 on bus B		 * D: 2-bit slot number, derived from PCI device number as		 *    (dev - 1) for bus A, or (dev - 2) for bus B		 * L: 2-bit line number		 */		if (bus & 0x80) {			/* PBM-A */			bus  = 0x00;			slot = (slot - 1) << 2;		} else {			/* PBM-B */			bus  = 0x10;			slot = (slot - 2) << 2;		}		irq -= 1;		ret = (bus | slot | irq);	} else {		/* Going through a PCI-PCI bridge that lacks a set of		 * interrupt-map and interrupt-map-mask properties.		 */		ret = ((irq - 1 + (slot & 3)) & 3) + 1;	}	return ret;}static int of_irq_verbose;static unsigned int __init build_one_device_irq(struct of_device *op,						struct device *parent,						unsigned int irq){	struct device_node *dp = op->node;	struct device_node *pp, *ip;	unsigned int orig_irq = irq;	if (irq == 0xffffffff)		return irq;	if (dp->irq_trans) {		irq = dp->irq_trans->irq_build(dp, irq,					       dp->irq_trans->data);		if (of_irq_verbose)			printk("%s: direct translate %x --> %x\n",			       dp->full_name, orig_irq, irq);		return irq;	}	/* Something more complicated.  Walk up to the root, applying	 * interrupt-map or bus specific translations, until we hit	 * an IRQ translator.	 *	 * If we hit a bus type or situation we cannot handle, we	 * stop and assume that the original IRQ number was in a	 * format which has special meaning to it's immediate parent.	 */	pp = dp->parent;	ip = NULL;	while (pp) {		const void *imap, *imsk;		int imlen;		imap = of_get_property(pp, "interrupt-map", &imlen);		imsk = of_get_property(pp, "interrupt-map-mask", NULL);		if (imap && imsk) {			struct device_node *iret;			int this_orig_irq = irq;			iret = apply_interrupt_map(dp, pp,						   imap, imlen, imsk,						   &irq);			if (of_irq_verbose)				printk("%s: Apply [%s:%x] imap --> [%s:%x]\n",				       op->node->full_name,				       pp->full_name, this_orig_irq,				       (iret ? iret->full_name : "NULL"), irq);			if (!iret)				break;			if (iret->irq_trans) {				ip = iret;				break;			}		} else {			if (!strcmp(pp->type, "pci") ||			    !strcmp(pp->type, "pciex")) {				unsigned int this_orig_irq = irq;				irq = pci_irq_swizzle(dp, pp, irq);				if (of_irq_verbose)					printk("%s: PCI swizzle [%s] "					       "%x --> %x\n",					       op->node->full_name,					       pp->full_name, this_orig_irq,					       irq);			}			if (pp->irq_trans) {				ip = pp;				break;			}		}		dp = pp;		pp = pp->parent;	}	if (!ip)		return orig_irq;	irq = ip->irq_trans->irq_build(op->node, irq,				       ip->irq_trans->data);	if (of_irq_verbose)		printk("%s: Apply IRQ trans [%s] %x --> %x\n",		       op->node->full_name, ip->full_name, orig_irq, irq);	return irq;}static struct of_device * __init scan_one_device(struct device_node *dp,						 struct device *parent){	struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);	const unsigned int *irq;	struct dev_archdata *sd;	int len, i;	if (!op)		return NULL;	sd = &op->dev.archdata;	sd->prom_node = dp;	sd->op = op;	op->node = dp;	op->clock_freq = of_getintprop_default(dp, "clock-frequency",					       (25*1000*1000));	op->portid = of_getintprop_default(dp, "upa-portid", -1);	if (op->portid == -1)		op->portid = of_getintprop_default(dp, "portid", -1);	irq = of_get_property(dp, "interrupts", &len);	if (irq) {		memcpy(op->irqs, irq, len);		op->num_irqs = len / 4;	} else {		op->num_irqs = 0;	}	/* Prevent overrunning the op->irqs[] array.  */	if (op->num_irqs > PROMINTR_MAX) {		printk(KERN_WARNING "%s: Too many irqs (%d), "		       "limiting to %d.\n",		       dp->full_name, op->num_irqs, PROMINTR_MAX);		op->num_irqs = PROMINTR_MAX;	}	build_device_resources(op, parent);	for (i = 0; i < op->num_irqs; i++)		op->irqs[i] = build_one_device_irq(op, parent, op->irqs[i]);	op->dev.parent = parent;	op->dev.bus = &of_platform_bus_type;	if (!parent)		strcpy(op->dev.bus_id, "root");	else		sprintf(op->dev.bus_id, "%08x", dp->node);	if (of_device_register(op)) {		printk("%s: Could not register of device.\n",		       dp->full_name);		kfree(op);		op = NULL;	}	return op;}static void __init scan_tree(struct device_node *dp, struct device *parent){	while (dp) {		struct of_device *op = scan_one_device(dp, parent);		if (op)			scan_tree(dp->child, &op->dev);		dp = dp->sibling;	}}static void __init scan_of_devices(void){	struct device_node *root = of_find_node_by_path("/");	struct of_device *parent;	parent = scan_one_device(root, NULL);	if (!parent)		return;	scan_tree(root->child, &parent->dev);}static int __init of_bus_driver_init(void){	int err;	err = of_bus_type_init(&of_platform_bus_type, "of");#ifdef CONFIG_PCI	if (!err)		err = of_bus_type_init(&isa_bus_type, "isa");	if (!err)		err = of_bus_type_init(&ebus_bus_type, "ebus");#endif#ifdef CONFIG_SBUS	if (!err)		err = of_bus_type_init(&sbus_bus_type, "sbus");#endif	if (!err)		scan_of_devices();	return err;}postcore_initcall(of_bus_driver_init);static int __init of_debug(char *str){	int val = 0;	get_option(&str, &val);	if (val & 1)		of_resource_verbose = 1;	if (val & 2)		of_irq_verbose = 1;	return 1;}__setup("of_debug=", of_debug);struct of_device* of_platform_device_create(struct device_node *np,					    const char *bus_id,					    struct device *parent,					    struct bus_type *bus){	struct of_device *dev;	dev = kzalloc(sizeof(*dev), GFP_KERNEL);	if (!dev)		return NULL;	dev->dev.parent = parent;	dev->dev.bus = bus;	dev->dev.release = of_release_dev;	strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);	if (of_device_register(dev) != 0) {		kfree(dev);		return NULL;	}	return dev;}EXPORT_SYMBOL(of_platform_device_create);

⌨️ 快捷键说明

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