]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/powerpc/boot/devtree.c
ACPI: cpuidle: port idle timer suspend/resume workaround to cpuidle
[linux-2.6-omap-h63xx.git] / arch / powerpc / boot / devtree.c
index 23492d7fb5567ee62b95b05ec273d7c4a8083bea..c9951550ed2c48ce26c1ef9e8028196be2cb4eb3 100644 (file)
@@ -123,15 +123,17 @@ static void get_reg_format(void *node, u32 *naddr, u32 *nsize)
 
 static void copy_val(u32 *dest, u32 *src, int naddr)
 {
-       memset(dest, 0, (MAX_ADDR_CELLS - naddr) * 4);
-       memcpy(dest, src, naddr * 4);
+       int pad = MAX_ADDR_CELLS - naddr;
+
+       memset(dest, 0, pad * 4);
+       memcpy(dest + pad, src, naddr * 4);
 }
 
 static int sub_reg(u32 *reg, u32 *sub)
 {
        int i, borrow = 0;
 
-       for (i = 0; i < MAX_ADDR_CELLS; i++) {
+       for (i = MAX_ADDR_CELLS - 1; i >= 0; i--) {
                int prev_borrow = borrow;
                borrow = reg[i] < sub[i] + prev_borrow;
                reg[i] -= sub[i] + prev_borrow;
@@ -140,11 +142,11 @@ static int sub_reg(u32 *reg, u32 *sub)
        return !borrow;
 }
 
-static int add_reg(u32 *reg, u32 *add)
+static int add_reg(u32 *reg, u32 *add, int naddr)
 {
        int i, carry = 0;
 
-       for (i = 0; i < MAX_ADDR_CELLS; i++) {
+       for (i = MAX_ADDR_CELLS - 1; i >= MAX_ADDR_CELLS - naddr; i--) {
                u64 tmp = (u64)reg[i] + add[i] + carry;
                carry = tmp >> 32;
                reg[i] = (u32)tmp;
@@ -205,12 +207,13 @@ static int find_range(u32 *reg, u32 *ranges, int nregaddr,
  * In particular, PCI is not supported.  Also, only the beginning of the
  * reg block is tracked; size is ignored except in ranges.
  */
-int dt_xlate_reg(void *node, int res, unsigned long *addr,
-                 unsigned long *size)
+static u32 dt_xlate_buf[MAX_ADDR_CELLS * MAX_RANGES * 3];
+
+static int dt_xlate(void *node, int res, int reglen, unsigned long *addr,
+               unsigned long *size)
 {
        u32 last_addr[MAX_ADDR_CELLS];
        u32 this_addr[MAX_ADDR_CELLS];
-       u32 buf[MAX_ADDR_CELLS * MAX_RANGES * 3];
        void *parent;
        u64 ret_addr, ret_size;
        u32 naddr, nsize, prev_naddr;
@@ -225,18 +228,18 @@ int dt_xlate_reg(void *node, int res, unsigned long *addr,
        if (nsize > 2)
                return 0;
 
-       buflen = getprop(node, "reg", buf, sizeof(buf)) / 4;
        offset = (naddr + nsize) * res;
 
-       if (buflen < offset + naddr + nsize)
+       if (reglen < offset + naddr + nsize ||
+           sizeof(dt_xlate_buf) < offset + naddr + nsize)
                return 0;
 
-       copy_val(last_addr, buf + offset, naddr);
+       copy_val(last_addr, dt_xlate_buf + offset, naddr);
 
-       ret_size = buf[offset + naddr];
+       ret_size = dt_xlate_buf[offset + naddr];
        if (nsize == 2) {
                ret_size <<= 32;
-               ret_size |= buf[offset + naddr + 1];
+               ret_size |= dt_xlate_buf[offset + naddr + 1];
        }
 
        while ((node = get_parent(node))) {
@@ -244,37 +247,34 @@ int dt_xlate_reg(void *node, int res, unsigned long *addr,
 
                get_reg_format(node, &naddr, &nsize);
 
-               buflen = getprop(node, "ranges", buf, sizeof(buf));
+               buflen = getprop(node, "ranges", dt_xlate_buf,
+                               sizeof(dt_xlate_buf));
                if (buflen < 0)
                        continue;
-               if (buflen > sizeof(buf))
+               if (buflen > sizeof(dt_xlate_buf))
                        return 0;
 
-               offset = find_range(last_addr, buf, prev_naddr,
+               offset = find_range(last_addr, dt_xlate_buf, prev_naddr,
                                    naddr, nsize, buflen / 4);
 
                if (offset < 0)
                        return 0;
 
-               copy_val(this_addr, buf + offset, prev_naddr);
+               copy_val(this_addr, dt_xlate_buf + offset, prev_naddr);
 
                if (!sub_reg(last_addr, this_addr))
                        return 0;
 
-               copy_val(this_addr, buf + offset + prev_naddr, naddr);
+               copy_val(this_addr, dt_xlate_buf + offset + prev_naddr, naddr);
 
-               if (!add_reg(last_addr, this_addr))
+               if (!add_reg(last_addr, this_addr, naddr))
                        return 0;
        }
 
        if (naddr > 2)
                return 0;
 
-       ret_addr = last_addr[0];
-       if (naddr == 2) {
-               ret_addr <<= 32;
-               ret_addr |= last_addr[1];
-       }
+       ret_addr = ((u64)last_addr[2] << 32) | last_addr[3];
 
        if (sizeof(void *) == 4 &&
            (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL ||
@@ -287,3 +287,21 @@ int dt_xlate_reg(void *node, int res, unsigned long *addr,
 
        return 1;
 }
+
+int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size)
+{
+       int reglen;
+
+       reglen = getprop(node, "reg", dt_xlate_buf, sizeof(dt_xlate_buf)) / 4;
+       return dt_xlate(node, res, reglen, addr, size);
+}
+
+int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr)
+{
+
+       if (buflen > sizeof(dt_xlate_buf))
+               return 0;
+
+       memcpy(dt_xlate_buf, buf, buflen);
+       return dt_xlate(node, 0, buflen / 4, xlated_addr, NULL);
+}