⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 prom.c

📁 一个2.4.21版本的嵌入式linux内核
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Procedures for interfacing to the Open Firmware PROM on * Power Macintosh computers. * * In particular, we are interested in the device tree * and in using some of its services (exit, write to stdout). * * Paul Mackerras	August 1996. * Copyright (C) 1996 Paul Mackerras. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/init.h>#include <linux/version.h>#include <linux/threads.h>#include <linux/spinlock.h>#include <linux/pci.h>#include <linux/slab.h>#include <asm/sections.h>#include <asm/prom.h>#include <asm/page.h>#include <asm/processor.h>#include <asm/irq.h>#include <asm/open_pic.h>#include <asm/system.h>#include <asm/btext.h>#include <asm/pci-bridge.h>struct pci_address {	unsigned a_hi;	unsigned a_mid;	unsigned a_lo;};struct pci_reg_property {	struct pci_address addr;	unsigned size_hi;	unsigned size_lo;};struct isa_reg_property {	unsigned space;	unsigned address;	unsigned size;};typedef unsigned long interpret_func(struct device_node *, unsigned long,				     int, int);static interpret_func interpret_pci_props;static interpret_func interpret_dbdma_props;static interpret_func interpret_isa_props;static interpret_func interpret_macio_props;static interpret_func interpret_root_props;extern char *klimit;/* Set for a newworld or CHRP machine */int use_of_interrupt_tree;struct device_node *dflt_interrupt_controller;int num_interrupt_controllers;int pmac_newworld;extern unsigned int rtas_entry;  /* physical pointer */extern struct device_node *allnodes;static unsigned long finish_node(struct device_node *, unsigned long,				 interpret_func *, int, int);static unsigned long finish_node_interrupts(struct device_node *, unsigned long);extern void enter_rtas(void *);void phys_call_rtas(int, int, int, ...);extern char cmd_line[512];	/* XXX */extern boot_infos_t *boot_infos;unsigned long dev_tree_size;void __openfirmwarephys_call_rtas(int service, int nargs, int nret, ...){	va_list list;	union {		unsigned long words[16];		double align;	} u;	void (*rtas)(void *, unsigned long);	int i;	u.words[0] = service;	u.words[1] = nargs;	u.words[2] = nret;	va_start(list, nret);	for (i = 0; i < nargs; ++i)		u.words[i+3] = va_arg(list, unsigned long);	va_end(list);	rtas = (void (*)(void *, unsigned long)) rtas_entry;	rtas(&u, rtas_data);}/* * finish_device_tree is called once things are running normally * (i.e. with text and data mapped to the address they were linked at). * It traverses the device tree and fills in the name, type, * {n_}addrs and {n_}intrs fields of each node. */void __initfinish_device_tree(void){	unsigned long mem = (unsigned long) klimit;	struct device_node *np;	/* All newworld pmac machines and CHRPs now use the interrupt tree */	for (np = allnodes; np != NULL; np = np->allnext) {		if (get_property(np, "interrupt-parent", 0)) {			use_of_interrupt_tree = 1;			break;		}	}	if (_machine == _MACH_Pmac && use_of_interrupt_tree)		pmac_newworld = 1;#ifdef CONFIG_BOOTX_TEXT	if (boot_infos && pmac_newworld) {		prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n");		prom_print("          You should use an Open Firmware bootloader\n");	}#endif /* CONFIG_BOOTX_TEXT */	if (use_of_interrupt_tree) {		/*		 * We want to find out here how many interrupt-controller		 * nodes there are, and if we are booted from BootX,		 * we need a pointer to the first (and hopefully only)		 * such node.  But we can't use find_devices here since		 * np->name has not been set yet.  -- paulus		 */		int n = 0;		char *name, *ic;		int iclen;		for (np = allnodes; np != NULL; np = np->allnext) {			ic = get_property(np, "interrupt-controller", &iclen);			name = get_property(np, "name", NULL);			/* checking iclen makes sure we don't get a false			   match on /chosen.interrupt_controller */			if ((name != NULL			     && strcmp(name, "interrupt-controller") == 0)			    || (ic != NULL && iclen == 0 && strcmp(name, "AppleKiwi"))) {				if (n == 0)					dflt_interrupt_controller = np;				++n;			}		}		num_interrupt_controllers = n;	}	mem = finish_node(allnodes, mem, NULL, 1, 1);	dev_tree_size = mem - (unsigned long) allnodes;	klimit = (char *) mem;}static unsigned long __initfinish_node(struct device_node *np, unsigned long mem_start,	    interpret_func *ifunc, int naddrc, int nsizec){	struct device_node *child;	int *ip;	np->name = get_property(np, "name", 0);	np->type = get_property(np, "device_type", 0);	if (!np->name)		np->name = "<NULL>";	if (!np->type)		np->type = "<NULL>";	/* get the device addresses and interrupts */	if (ifunc != NULL)		mem_start = ifunc(np, mem_start, naddrc, nsizec);	if (use_of_interrupt_tree)		mem_start = finish_node_interrupts(np, mem_start);	/* Look for #address-cells and #size-cells properties. */	ip = (int *) get_property(np, "#address-cells", 0);	if (ip != NULL)		naddrc = *ip;	ip = (int *) get_property(np, "#size-cells", 0);	if (ip != NULL)		nsizec = *ip;	if (np->parent == NULL)		ifunc = interpret_root_props;	else if (np->type == 0)		ifunc = NULL;	else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci"))		ifunc = interpret_pci_props;	else if (!strcmp(np->type, "dbdma"))		ifunc = interpret_dbdma_props;	else if (!strcmp(np->type, "mac-io")		 || ifunc == interpret_macio_props)		ifunc = interpret_macio_props;	else if (!strcmp(np->type, "isa"))		ifunc = interpret_isa_props;	else if (!strcmp(np->name, "uni-n"))		ifunc = interpret_root_props;	else if (!((ifunc == interpret_dbdma_props		    || ifunc == interpret_macio_props)		   && (!strcmp(np->type, "escc")		       || !strcmp(np->type, "media-bay"))))		ifunc = NULL;	/* if we were booted from BootX, convert the full name */	if (boot_infos	    && strncmp(np->full_name, "Devices:device-tree", 19) == 0) {		if (np->full_name[19] == 0) {			strcpy(np->full_name, "/");		} else if (np->full_name[19] == ':') {			char *p = np->full_name + 19;			np->full_name = p;			for (; *p; ++p)				if (*p == ':')					*p = '/';		}	}	for (child = np->child; child != NULL; child = child->sibling)		mem_start = finish_node(child, mem_start, ifunc,					naddrc, nsizec);	return mem_start;}/* * Find the interrupt parent of a node. */static struct device_node * __initintr_parent(struct device_node *p){	phandle *parp;	parp = (phandle *) get_property(p, "interrupt-parent", NULL);	if (parp == NULL)		return p->parent;	p = find_phandle(*parp);	if (p != NULL)		return p;	/*	 * On a powermac booted with BootX, we don't get to know the	 * phandles for any nodes, so find_phandle will return NULL.	 * Fortunately these machines only have one interrupt controller	 * so there isn't in fact any ambiguity.  -- paulus	 */	if (num_interrupt_controllers == 1)		p = dflt_interrupt_controller;	return p;}/* * Find out the size of each entry of the interrupts property * for a node. */static int __initprom_n_intr_cells(struct device_node *np){	struct device_node *p;	unsigned int *icp;	for (p = np; (p = intr_parent(p)) != NULL; ) {		icp = (unsigned int *)			get_property(p, "#interrupt-cells", NULL);		if (icp != NULL)			return *icp;		if (get_property(p, "interrupt-controller", NULL) != NULL		    || get_property(p, "interrupt-map", NULL) != NULL) {			printk("oops, node %s doesn't have #interrupt-cells\n",			       p->full_name);			return 1;		}	}	printk("prom_n_intr_cells failed for %s\n", np->full_name);	return 1;}/* * Map an interrupt from a device up to the platform interrupt * descriptor. */static int __initmap_interrupt(unsigned int **irq, struct device_node **ictrler,	      struct device_node *np, unsigned int *ints, int nintrc){	struct device_node *p, *ipar;	unsigned int *imap, *imask, *ip;	int i, imaplen, match;	int newintrc, newaddrc;	unsigned int *reg;	int naddrc;	reg = (unsigned int *) get_property(np, "reg", NULL);	naddrc = prom_n_addr_cells(np);	p = intr_parent(np);	while (p != NULL) {		if (get_property(p, "interrupt-controller", NULL) != NULL)			/* this node is an interrupt controller, stop here */			break;		imap = (unsigned int *)			get_property(p, "interrupt-map", &imaplen);		if (imap == NULL) {			p = intr_parent(p);			continue;		}		imask = (unsigned int *)			get_property(p, "interrupt-map-mask", NULL);		if (imask == NULL) {			printk("oops, %s has interrupt-map but no mask\n",			       p->full_name);			return 0;		}		imaplen /= sizeof(unsigned int);		match = 0;		ipar = NULL;		while (imaplen > 0 && !match) {			/* check the child-interrupt field */			match = 1;			for (i = 0; i < naddrc && match; ++i)				match = ((reg[i] ^ imap[i]) & imask[i]) == 0;			for (; i < naddrc + nintrc && match; ++i)				match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0;			imap += naddrc + nintrc;			imaplen -= naddrc + nintrc;			/* grab the interrupt parent */			ipar = find_phandle((phandle) *imap++);			--imaplen;			if (ipar == NULL && num_interrupt_controllers == 1)				/* cope with BootX not giving us phandles */				ipar = dflt_interrupt_controller;			if (ipar == NULL) {				printk("oops, no int parent %x in map of %s\n",				       imap[-1], p->full_name);				return 0;			}			/* find the parent's # addr and intr cells */			ip = (unsigned int *)				get_property(ipar, "#interrupt-cells", NULL);			if (ip == NULL) {				printk("oops, no #interrupt-cells on %s\n",				       ipar->full_name);				return 0;			}			newintrc = *ip;			ip = (unsigned int *)				get_property(ipar, "#address-cells", NULL);			newaddrc = (ip == NULL)? 0: *ip;			imap += newaddrc + newintrc;			imaplen -= newaddrc + newintrc;		}		if (imaplen < 0) {			printk("oops, error decoding int-map on %s, len=%d\n",			       p->full_name, imaplen);			return 0;		}		if (!match) {			printk("oops, no match in %s int-map for %s\n",			       p->full_name, np->full_name);			return 0;		}		p = ipar;		naddrc = newaddrc;		nintrc = newintrc;		ints = imap - nintrc;		reg = ints - naddrc;	}	if (p == NULL)		printk("hmmm, int tree for %s doesn't have ctrler\n",		       np->full_name);	*irq = ints;	*ictrler = p;	return nintrc;}/* * New version of finish_node_interrupts. */static unsigned long __initfinish_node_interrupts(struct device_node *np, unsigned long mem_start){	unsigned int *ints;	int intlen, intrcells;	int i, j, n, offset;	unsigned int *irq;	struct device_node *ic;	ints = (unsigned int *) get_property(np, "interrupts", &intlen);	if (ints == NULL)		return mem_start;	intrcells = prom_n_intr_cells(np);	intlen /= intrcells * sizeof(unsigned int);	np->n_intrs = intlen;	np->intrs = (struct interrupt_info *) mem_start;	mem_start += intlen * sizeof(struct interrupt_info);	for (i = 0; i < intlen; ++i) {		np->intrs[i].line = 0;		np->intrs[i].sense = 1;		n = map_interrupt(&irq, &ic, np, ints, intrcells);		if (n <= 0)			continue;		offset = 0;		/*		 * On a CHRP we have an 8259 which is subordinate to		 * the openpic in the interrupt tree, but we want the		 * openpic's interrupt numbers offsetted, not the 8259's.		 * So we apply the offset if the controller is at the		 * root of the interrupt tree, i.e. has no interrupt-parent.		 * This doesn't cope with the general case of multiple		 * cascaded interrupt controllers, but then neither will		 * irq.c at the moment either.  -- paulus		 */		if (num_interrupt_controllers > 1 && ic != NULL		    && get_property(ic, "interrupt-parent", NULL) == NULL)			offset = 16;		np->intrs[i].line = irq[0] + offset;		if (n > 1)			np->intrs[i].sense = irq[1];		if (n > 2) {			printk("hmmm, got %d intr cells for %s:", n,			       np->full_name);			for (j = 0; j < n; ++j)				printk(" %d", irq[j]);			printk("\n");		}		ints += intrcells;	}	return mem_start;}/* * When BootX makes a copy of the device tree from the MacOS * Name Registry, it is in the format we use but all of the pointers * are offsets from the start of the tree. * This procedure updates the pointers. */void __initrelocate_nodes(void){	unsigned long base;	struct device_node *np;	struct property *pp;#define ADDBASE(x)	(x = (x)? ((typeof (x))((unsigned long)(x) + base)): 0)	base = (unsigned long) boot_infos + boot_infos->deviceTreeOffset;	allnodes = (struct device_node *)(base + 4);	for (np = allnodes; np != 0; np = np->allnext) {		ADDBASE(np->full_name);		ADDBASE(np->properties);		ADDBASE(np->parent);		ADDBASE(np->child);		ADDBASE(np->sibling);		ADDBASE(np->allnext);		for (pp = np->properties; pp != 0; pp = pp->next) {			ADDBASE(pp->name);			ADDBASE(pp->value);			ADDBASE(pp->next);		}	}}intprom_n_addr_cells(struct device_node* np){	int* ip;	do {		if (np->parent)			np = np->parent;		ip = (int *) get_property(np, "#address-cells", 0);		if (ip != NULL)			return *ip;	} while (np->parent);	/* No #address-cells property for the root node, default to 1 */	return 1;}intprom_n_size_cells(struct device_node* np){	int* ip;	do {		if (np->parent)			np = np->parent;		ip = (int *) get_property(np, "#size-cells", 0);		if (ip != NULL)			return *ip;	} while (np->parent);	/* No #size-cells property for the root node, default to 1 */	return 1;}static unsigned long __initmap_addr(struct device_node *np, unsigned long space, unsigned long addr){	int na;	unsigned int *ranges;	int rlen = 0;	unsigned int type;	type = (space >> 24) & 3;	if (type == 0)		return addr;	while ((np = np->parent) != NULL) {		if (strcmp(np->type, "pci") != 0)			continue;		/* PCI bridge: map the address through the ranges property */		na = prom_n_addr_cells(np);		ranges = (unsigned int *) get_property(np, "ranges", &rlen);		while ((rlen -= (na + 5) * sizeof(unsigned int)) >= 0) {			if (((ranges[0] >> 24) & 3) == type			    && ranges[2] <= addr			    && addr - ranges[2] < ranges[na+4]) {				/* ok, this matches, translate it */				addr += ranges[na+2] - ranges[2];				break;			}			ranges += na + 5;		}	}	return addr;}static unsigned long __initinterpret_pci_props(struct device_node *np, unsigned long mem_start,		    int naddrc, int nsizec){	struct address_range *adr;	struct pci_reg_property *pci_addrs;	int i, l, *ip;	pci_addrs = (struct pci_reg_property *)		get_property(np, "assigned-addresses", &l);	if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {		i = 0;		adr = (struct address_range *) mem_start;		while ((l -= sizeof(struct pci_reg_property)) >= 0) {			adr[i].space = pci_addrs[i].addr.a_hi;			adr[i].address = map_addr(np, pci_addrs[i].addr.a_hi,						  pci_addrs[i].addr.a_lo);			adr[i].size = pci_addrs[i].size_lo;			++i;		}		np->addrs = adr;		np->n_addrs = i;		mem_start += i * sizeof(struct address_range);	}	if (use_of_interrupt_tree)		return mem_start;	ip = (int *) get_property(np, "AAPL,interrupts", &l);	if (ip == 0 && np->parent)		ip = (int *) get_property(np->parent, "AAPL,interrupts", &l);	if (ip == 0)		ip = (int *) get_property(np, "interrupts", &l);	if (ip != 0) {		np->intrs = (struct interrupt_info *) mem_start;		np->n_intrs = l / sizeof(int);		mem_start += np->n_intrs * sizeof(struct interrupt_info);		for (i = 0; i < np->n_intrs; ++i) {			np->intrs[i].line = *ip++;			np->intrs[i].sense = 1;		}	}	return mem_start;}static unsigned long __initinterpret_dbdma_props(struct device_node *np, unsigned long mem_start,		      int naddrc, int nsizec){	struct reg_property *rp;	struct address_range *adr;	unsigned long base_address;	int i, l, *ip;	struct device_node *db;	base_address = 0;	for (db = np->parent; db != NULL; db = db->parent) {		if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) {			base_address = db->addrs[0].address;			break;		}	}	rp = (struct reg_property *) get_property(np, "reg", &l);	if (rp != 0 && l >= sizeof(struct reg_property)) {		i = 0;		adr = (struct address_range *) mem_start;		while ((l -= sizeof(struct reg_property)) >= 0) {			adr[i].space = 2;			adr[i].address = rp[i].address + base_address;			adr[i].size = rp[i].size;			++i;		}		np->addrs = adr;		np->n_addrs = i;		mem_start += i * sizeof(struct address_range);	}	if (use_of_interrupt_tree)		return mem_start;	ip = (int *) get_property(np, "AAPL,interrupts", &l);	if (ip == 0)		ip = (int *) get_property(np, "interrupts", &l);	if (ip != 0) {		np->intrs = (struct interrupt_info *) mem_start;

⌨️ 快捷键说明

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