Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=6e1af384f1c1742ae6d86bbf779d4fa020c509bc
Commit:     6e1af384f1c1742ae6d86bbf779d4fa020c509bc
Parent:     dbf8eefa2b814d6922492120bfa46d4bc42ceb20
Author:     Scott Wood <[EMAIL PROTECTED]>
AuthorDate: Mon Mar 26 15:52:24 2007 -0500
Committer:  Paul Mackerras <[EMAIL PROTECTED]>
CommitDate: Fri Apr 13 03:55:15 2007 +1000

    [POWERPC] bootwrapper: Add dt_xlate_reg(), and use it to find serial 
registers.
    
    dt_xlate_reg() uses the ranges properties of a node's parentage to find
    the absolute physical address of the node's registers.
    
    The ns16550 driver uses this when no virtual-reg property is found.
    
    Signed-off-by: Scott Wood <[EMAIL PROTECTED]>
    Signed-off-by: Paul Mackerras <[EMAIL PROTECTED]>
---
 arch/powerpc/boot/devtree.c |  178 +++++++++++++++++++++++++++++++++++++++++++
 arch/powerpc/boot/ns16550.c |    9 ++-
 arch/powerpc/boot/ops.h     |    2 +
 3 files changed, 187 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c
index 708cade..23492d7 100644
--- a/arch/powerpc/boot/devtree.c
+++ b/arch/powerpc/boot/devtree.c
@@ -109,3 +109,181 @@ void __dt_fixup_mac_addresses(u32 startindex, ...)
        }
        va_end(ap);
 }
+
+#define MAX_ADDR_CELLS 4
+#define MAX_RANGES 8
+
+static void get_reg_format(void *node, u32 *naddr, u32 *nsize)
+{
+       if (getprop(node, "#address-cells", naddr, 4) != 4)
+               *naddr = 2;
+       if (getprop(node, "#size-cells", nsize, 4) != 4)
+               *nsize = 1;
+}
+
+static void copy_val(u32 *dest, u32 *src, int naddr)
+{
+       memset(dest, 0, (MAX_ADDR_CELLS - naddr) * 4);
+       memcpy(dest, src, naddr * 4);
+}
+
+static int sub_reg(u32 *reg, u32 *sub)
+{
+       int i, borrow = 0;
+
+       for (i = 0; i < MAX_ADDR_CELLS; i++) {
+               int prev_borrow = borrow;
+               borrow = reg[i] < sub[i] + prev_borrow;
+               reg[i] -= sub[i] + prev_borrow;
+       }
+
+       return !borrow;
+}
+
+static int add_reg(u32 *reg, u32 *add)
+{
+       int i, carry = 0;
+
+       for (i = 0; i < MAX_ADDR_CELLS; i++) {
+               u64 tmp = (u64)reg[i] + add[i] + carry;
+               carry = tmp >> 32;
+               reg[i] = (u32)tmp;
+       }
+
+       return !carry;
+}
+
+/* It is assumed that if the first byte of reg fits in a
+ * range, then the whole reg block fits.
+ */
+static int compare_reg(u32 *reg, u32 *range, u32 *rangesize)
+{
+       int i;
+       u32 end;
+
+       for (i = 0; i < MAX_ADDR_CELLS; i++) {
+               if (reg[i] < range[i])
+                       return 0;
+               if (reg[i] > range[i])
+                       break;
+       }
+
+       for (i = 0; i < MAX_ADDR_CELLS; i++) {
+               end = range[i] + rangesize[i];
+
+               if (reg[i] < end)
+                       break;
+               if (reg[i] > end)
+                       return 0;
+       }
+
+       return reg[i] != end;
+}
+
+/* reg must be MAX_ADDR_CELLS */
+static int find_range(u32 *reg, u32 *ranges, int nregaddr,
+                      int naddr, int nsize, int buflen)
+{
+       int nrange = nregaddr + naddr + nsize;
+       int i;
+
+       for (i = 0; i + nrange <= buflen; i += nrange) {
+               u32 range_addr[MAX_ADDR_CELLS];
+               u32 range_size[MAX_ADDR_CELLS];
+
+               copy_val(range_addr, ranges + i, naddr);
+               copy_val(range_size, ranges + i + nregaddr + naddr, nsize);
+
+               if (compare_reg(reg, range_addr, range_size))
+                       return i;
+       }
+
+       return -1;
+}
+
+/* Currently only generic buses without special encodings are supported.
+ * 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)
+{
+       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;
+       int buflen, offset;
+
+       parent = get_parent(node);
+       if (!parent)
+               return 0;
+
+       get_reg_format(parent, &naddr, &nsize);
+
+       if (nsize > 2)
+               return 0;
+
+       buflen = getprop(node, "reg", buf, sizeof(buf)) / 4;
+       offset = (naddr + nsize) * res;
+
+       if (buflen < offset + naddr + nsize)
+               return 0;
+
+       copy_val(last_addr, buf + offset, naddr);
+
+       ret_size = buf[offset + naddr];
+       if (nsize == 2) {
+               ret_size <<= 32;
+               ret_size |= buf[offset + naddr + 1];
+       }
+
+       while ((node = get_parent(node))) {
+               prev_naddr = naddr;
+
+               get_reg_format(node, &naddr, &nsize);
+
+               buflen = getprop(node, "ranges", buf, sizeof(buf));
+               if (buflen < 0)
+                       continue;
+               if (buflen > sizeof(buf))
+                       return 0;
+
+               offset = find_range(last_addr, buf, prev_naddr,
+                                   naddr, nsize, buflen / 4);
+
+               if (offset < 0)
+                       return 0;
+
+               copy_val(this_addr, buf + offset, prev_naddr);
+
+               if (!sub_reg(last_addr, this_addr))
+                       return 0;
+
+               copy_val(this_addr, buf + offset + prev_naddr, naddr);
+
+               if (!add_reg(last_addr, this_addr))
+                       return 0;
+       }
+
+       if (naddr > 2)
+               return 0;
+
+       ret_addr = last_addr[0];
+       if (naddr == 2) {
+               ret_addr <<= 32;
+               ret_addr |= last_addr[1];
+       }
+
+       if (sizeof(void *) == 4 &&
+           (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL ||
+            ret_addr + ret_size > 0x100000000ULL))
+               return 0;
+
+       *addr = ret_addr;
+       if (size)
+               *size = ret_size;
+
+       return 1;
+}
diff --git a/arch/powerpc/boot/ns16550.c b/arch/powerpc/boot/ns16550.c
index 1ffe72e..f8f1b2f 100644
--- a/arch/powerpc/boot/ns16550.c
+++ b/arch/powerpc/boot/ns16550.c
@@ -55,10 +55,15 @@ static u8 ns16550_tstc(void)
 int ns16550_console_init(void *devp, struct serial_console_data *scdp)
 {
        int n;
+       unsigned long reg_phys;
 
        n = getprop(devp, "virtual-reg", &reg_base, sizeof(reg_base));
-       if (n != sizeof(reg_base))
-               return -1;
+       if (n != sizeof(reg_base)) {
+               if (!dt_xlate_reg(devp, 0, &reg_phys, NULL))
+                       return -1;
+
+               reg_base = (void *)reg_phys;
+       }
 
        n = getprop(devp, "reg-shift", &reg_shift, sizeof(reg_shift));
        if (n != sizeof(reg_shift))
diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h
index 8008d61..fbd9030 100644
--- a/arch/powerpc/boot/ops.h
+++ b/arch/powerpc/boot/ops.h
@@ -82,6 +82,8 @@ int ns16550_console_init(void *devp, struct 
serial_console_data *scdp);
 void *simple_alloc_init(char *base, u32 heap_size, u32 granularity,
                u32 max_allocs);
 extern void flush_cache(void *, unsigned long);
+int dt_xlate_reg(void *node, int res, unsigned long *addr,
+                 unsigned long *size);
 
 static inline void *finddevice(const char *name)
 {
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to