Skip to content
Snippets Groups Projects
fdt_support.c 27.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	 * what Apple understood, and they do have things like /uni-n or
    	 * /ht nodes with no "ranges" property and a lot of perfectly
    	 * useable mapped devices below them. Thus we treat the absence of
    	 * "ranges" as equivalent to an empty "ranges" property which means
    	 * a 1:1 translation at that level. It's up to the caller not to try
    	 * to translate addresses that aren't supposed to be translated in
    	 * the first place. --BenH.
    	 */
    	ranges = (u32 *)fdt_getprop(blob, parent, rprop, &rlen);
    	if (ranges == NULL || rlen == 0) {
    		offset = of_read_number(addr, na);
    		memset(addr, 0, pna * 4);
    		debug("OF: no ranges, 1:1 translation\n");
    		goto finish;
    	}
    
    	debug("OF: walking ranges...\n");
    
    	/* Now walk through the ranges */
    	rlen /= 4;
    	rone = na + pna + ns;
    	for (; rlen >= rone; rlen -= rone, ranges += rone) {
    		offset = bus->map(addr, ranges, na, ns, pna);
    		if (offset != OF_BAD_ADDR)
    			break;
    	}
    	if (offset == OF_BAD_ADDR) {
    		debug("OF: not found !\n");
    		return 1;
    	}
    	memcpy(addr, ranges + na, 4 * pna);
    
     finish:
    	of_dump_addr("OF: parent translation for:", addr, pna);
    	debug("OF: with offset: "PRu64"\n", offset);
    
    	/* Translate it into parent bus space */
    	return pbus->translate(addr, offset, pna);
    }
    
    /*
     * Translate an address from the device-tree into a CPU physical address,
     * this walks up the tree and applies the various bus mappings on the
     * way.
     *
     * Note: We consider that crossing any level with #size-cells == 0 to mean
     * that translation is impossible (that is we are not dealing with a value
     * that can be mapped to a cpu physical address). This is not really specified
     * that way, but this is traditionally the way IBM at least do things
     */
    u64 __of_translate_address(void *blob, int node_offset, const u32 *in_addr,
    			   const char *rprop)
    {
    	int parent;
    	struct of_bus *bus, *pbus;
    	u32 addr[OF_MAX_ADDR_CELLS];
    	int na, ns, pna, pns;
    	u64 result = OF_BAD_ADDR;
    
    	debug("OF: ** translation for device %s **\n",
    		fdt_get_name(blob, node_offset, NULL));
    
    	/* Get parent & match bus type */
    	parent = fdt_parent_offset(blob, node_offset);
    	if (parent < 0)
    		goto bail;
    	bus = &of_busses[0];
    
    	/* Cound address cells & copy address locally */
    	bus->count_cells(blob, node_offset, &na, &ns);
    	if (!OF_CHECK_COUNTS(na, ns)) {
    		printf("%s: Bad cell count for %s\n", __FUNCTION__,
    		       fdt_get_name(blob, node_offset, NULL));
    		goto bail;
    	}
    	memcpy(addr, in_addr, na * 4);
    
    	debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
    	    bus->name, na, ns, fdt_get_name(blob, parent, NULL));
    	of_dump_addr("OF: translating address:", addr, na);
    
    	/* Translate */
    	for (;;) {
    		/* Switch to parent bus */
    		node_offset = parent;
    		parent = fdt_parent_offset(blob, node_offset);
    
    		/* If root, we have finished */
    		if (parent < 0) {
    			debug("OF: reached root node\n");
    			result = of_read_number(addr, na);
    			break;
    		}
    
    		/* Get new parent bus and counts */
    		pbus = &of_busses[0];
    		pbus->count_cells(blob, node_offset, &pna, &pns);
    		if (!OF_CHECK_COUNTS(pna, pns)) {
    			printf("%s: Bad cell count for %s\n", __FUNCTION__,
    				fdt_get_name(blob, node_offset, NULL));
    			break;
    		}
    
    		debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
    		    pbus->name, pna, pns, fdt_get_name(blob, parent, NULL));
    
    		/* Apply bus translation */
    		if (of_translate_one(blob, node_offset, bus, pbus,
    					addr, na, ns, pna, rprop))
    			break;
    
    		/* Complete the move up one level */
    		na = pna;
    		ns = pns;
    		bus = pbus;
    
    		of_dump_addr("OF: one level translation:", addr, na);
    	}
     bail:
    
    	return result;
    }
    
    u64 fdt_translate_address(void *blob, int node_offset, const u32 *in_addr)
    {
    	return __of_translate_address(blob, node_offset, in_addr, "ranges");
    }