From 27fbaa9702e548e74dffd21855769f6cedad42bd Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 22 Mar 2007 17:02:21 +1100 Subject: [POWERPC] Add device tree utility functions to zImage This patch adds a library of useful device tree manipulation functions to the zImage library, for use by platform code. These functions are based on the hooks already in dt_ops, so they're not dependent on a particular device tree implementation. This patch also slightly streamlines the code in main.c using these new functions. This is a consolidation of my work in this area with Scott Wood's patches to a very similar end. Signed-off-by: David Gibson Signed-off-by: Paul Mackerras --- arch/powerpc/boot/devtree.c | 111 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 arch/powerpc/boot/devtree.c (limited to 'arch/powerpc/boot/devtree.c') diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c new file mode 100644 index 00000000000..708cadebeb4 --- /dev/null +++ b/arch/powerpc/boot/devtree.c @@ -0,0 +1,111 @@ +/* + * devtree.c - convenience functions for device tree manipulation + * Copyright 2007 David Gibson, IBM Corporation. + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * + * Authors: David Gibson + * Scott Wood + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "ops.h" + +void dt_fixup_memory(u64 start, u64 size) +{ + void *root, *memory; + int naddr, nsize, i; + u32 memreg[4]; + + root = finddevice("/"); + if (getprop(root, "#address-cells", &naddr, sizeof(naddr)) < 0) + naddr = 2; + if (naddr < 1 || naddr > 2) + fatal("Can't cope with #address-cells == %d in /\n\r", naddr); + + if (getprop(root, "#size-cells", &nsize, sizeof(nsize)) < 0) + nsize = 1; + if (nsize < 1 || nsize > 2) + fatal("Can't cope with #size-cells == %d in /\n\r", nsize); + + i = 0; + if (naddr == 2) + memreg[i++] = start >> 32; + memreg[i++] = start & 0xffffffff; + if (nsize == 2) + memreg[i++] = size >> 32; + memreg[i++] = size & 0xffffffff; + + memory = finddevice("/memory"); + if (! memory) { + memory = create_node(NULL, "memory"); + setprop_str(memory, "device_type", "memory"); + } + + printf("Memory <- <0x%x", memreg[0]); + for (i = 1; i < (naddr + nsize); i++) + printf(" 0x%x", memreg[i]); + printf("> (%ldMB)\n\r", (unsigned long)(size >> 20)); + + setprop(memory, "reg", memreg, (naddr + nsize)*sizeof(u32)); +} + +#define MHZ(x) ((x + 500000) / 1000000) + +void dt_fixup_cpu_clocks(u32 cpu, u32 tb, u32 bus) +{ + void *devp = NULL; + + printf("CPU clock-frequency <- 0x%x (%dMHz)\n\r", cpu, MHZ(cpu)); + printf("CPU timebase-frequency <- 0x%x (%dMHz)\n\r", tb, MHZ(tb)); + if (bus > 0) + printf("CPU bus-frequency <- 0x%x (%dMHz)\n\r", bus, MHZ(bus)); + + while ((devp = find_node_by_devtype(devp, "cpu"))) { + setprop_val(devp, "clock-frequency", cpu); + setprop_val(devp, "timebase-frequency", tb); + if (bus > 0) + setprop_val(devp, "bus-frequency", bus); + } +} + +void dt_fixup_clock(const char *path, u32 freq) +{ + void *devp = finddevice(path); + + if (devp) { + printf("%s: clock-frequency <- %x (%dMHz)\n\r", path, freq, MHZ(freq)); + setprop_val(devp, "clock-frequency", freq); + } +} + +void __dt_fixup_mac_addresses(u32 startindex, ...) +{ + va_list ap; + u32 index = startindex; + void *devp; + const u8 *addr; + + va_start(ap, startindex); + while ((addr = va_arg(ap, const u8 *))) { + devp = find_node_by_prop_value(NULL, "linux,network-index", + (void*)&index, sizeof(index)); + + printf("ENET%d: local-mac-address <-" + " %02x:%02x:%02x:%02x:%02x:%02x\n\r", index, + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + if (devp) + setprop(devp, "local-mac-address", addr, 6); + + index++; + } + va_end(ap); +} -- cgit v1.2.3-70-g09d2 From 6e1af384f1c1742ae6d86bbf779d4fa020c509bc Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 26 Mar 2007 15:52:24 -0500 Subject: [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 Signed-off-by: Paul Mackerras --- 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(-) (limited to 'arch/powerpc/boot/devtree.c') diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c index 708cadebeb4..23492d7fb55 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 1ffe72e35cd..f8f1b2f3141 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", ®_base, sizeof(reg_base)); - if (n != sizeof(reg_base)) - return -1; + if (n != sizeof(reg_base)) { + if (!dt_xlate_reg(devp, 0, ®_phys, NULL)) + return -1; + + reg_base = (void *)reg_phys; + } n = getprop(devp, "reg-shift", ®_shift, sizeof(reg_shift)); if (n != sizeof(reg_shift)) diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 8008d612402..fbd9030d660 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) { -- cgit v1.2.3-70-g09d2 From e4bb688d9f11d7fee927312cc97d443472c7c212 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 27 Apr 2007 03:08:13 +1000 Subject: [POWERPC] bootwrapper: Fix array handling in dt_xlate_reg(). This fixes a few bugs in how dt_xlate_reg() handles address arrays: 1. copy_val() was copying into the wrong end of the array, resulting in random stack garbage at the other end. 2. dt_xlate_reg() was getting the result from the wrong end of the array. 3. add_reg() and sub_reg() were treating the arrays as little-endian rather than big-endian. 4. add_reg() only returned an error on a carry out of the entire array, rather than out of the naddr portion. 5. The requested reg resource was checked to see if it exceeded the size of the reg property, but not to see if it exceeded the size of the buffer. Signed-off-by: Scott Wood Signed-off-by: Paul Mackerras --- arch/powerpc/boot/devtree.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'arch/powerpc/boot/devtree.c') diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c index 23492d7fb55..ac4b5ee92d5 100644 --- a/arch/powerpc/boot/devtree.c +++ b/arch/powerpc/boot/devtree.c @@ -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; @@ -228,7 +230,8 @@ int dt_xlate_reg(void *node, int res, unsigned long *addr, buflen = getprop(node, "reg", buf, sizeof(buf)) / 4; offset = (naddr + nsize) * res; - if (buflen < offset + naddr + nsize) + if (buflen < offset + naddr + nsize || + sizeof(buf) < offset + naddr + nsize) return 0; copy_val(last_addr, buf + offset, naddr); @@ -263,18 +266,14 @@ int dt_xlate_reg(void *node, int res, unsigned long *addr, copy_val(this_addr, 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 || -- cgit v1.2.3-70-g09d2 From 8895ea483e144f8acca16adfff7c60a993e77b7d Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Sat, 28 Apr 2007 06:48:24 +1000 Subject: [POWERPC] Add dt_xlate_addr() to bootwrapper dt_xlate_reg() looks up the 'reg' property in the specified node to get the address and size to translate. Add dt_xlate_addr() which is passed in the address and size to translate. Signed-off-by: Mark A. Greer Signed-off-by: Paul Mackerras --- arch/powerpc/boot/devtree.c | 47 +++++++++++++++++++++++++++++++-------------- arch/powerpc/boot/ops.h | 4 ++-- 2 files changed, 35 insertions(+), 16 deletions(-) (limited to 'arch/powerpc/boot/devtree.c') diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c index ac4b5ee92d5..c9951550ed2 100644 --- a/arch/powerpc/boot/devtree.c +++ b/arch/powerpc/boot/devtree.c @@ -207,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; @@ -227,19 +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 || - sizeof(buf) < 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))) { @@ -247,24 +247,25 @@ 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, naddr)) return 0; @@ -286,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); +} diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 20e87199f6a..73bd47a3a07 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -82,8 +82,8 @@ int ns16550_console_init(void *devp, struct serial_console_data *scdp); void *simple_alloc_init(char *base, unsigned long heap_size, unsigned long granularity, unsigned long max_allocs); extern void flush_cache(void *, unsigned long); -int dt_xlate_reg(void *node, int res, unsigned long *addr, - unsigned long *size); +int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size); +int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr); static inline void *finddevice(const char *name) { -- cgit v1.2.3-70-g09d2