diff options
Diffstat (limited to 'arch/powerpc/boot')
-rw-r--r-- | arch/powerpc/boot/.gitignore | 13 | ||||
-rw-r--r-- | arch/powerpc/boot/Makefile | 15 | ||||
-rw-r--r-- | arch/powerpc/boot/dts/kuroboxHG.dts | 148 | ||||
-rw-r--r-- | arch/powerpc/boot/dts/lite5200.dts | 313 | ||||
-rw-r--r-- | arch/powerpc/boot/dts/lite5200b.dts | 318 | ||||
-rw-r--r-- | arch/powerpc/boot/dts/mpc7448hpc2.dts | 44 | ||||
-rw-r--r-- | arch/powerpc/boot/flatdevtree.c | 880 | ||||
-rw-r--r-- | arch/powerpc/boot/flatdevtree.h | 62 | ||||
-rw-r--r-- | arch/powerpc/boot/flatdevtree_env.h | 47 | ||||
-rw-r--r-- | arch/powerpc/boot/flatdevtree_misc.c | 51 | ||||
-rw-r--r-- | arch/powerpc/boot/io.h | 53 | ||||
-rw-r--r-- | arch/powerpc/boot/main.c | 57 | ||||
-rw-r--r-- | arch/powerpc/boot/mktree.c | 152 | ||||
-rw-r--r-- | arch/powerpc/boot/ns16550.c | 74 | ||||
-rw-r--r-- | arch/powerpc/boot/of.c | 8 | ||||
-rw-r--r-- | arch/powerpc/boot/ops.h | 26 | ||||
-rw-r--r-- | arch/powerpc/boot/serial.c | 142 | ||||
-rw-r--r-- | arch/powerpc/boot/simple_alloc.c | 149 | ||||
-rw-r--r-- | arch/powerpc/boot/stdio.c | 3 | ||||
-rw-r--r-- | arch/powerpc/boot/util.S | 88 | ||||
-rwxr-xr-x | arch/powerpc/boot/wrapper | 3 | ||||
-rw-r--r-- | arch/powerpc/boot/zImage.coff.lds.S | 4 |
22 files changed, 2580 insertions, 70 deletions
diff --git a/arch/powerpc/boot/.gitignore b/arch/powerpc/boot/.gitignore index 45c9ad23526e..0734b2fc1d95 100644 --- a/arch/powerpc/boot/.gitignore +++ b/arch/powerpc/boot/.gitignore | |||
@@ -1,19 +1,32 @@ | |||
1 | addnote | 1 | addnote |
2 | empty.c | ||
3 | hack-coff | ||
2 | infblock.c | 4 | infblock.c |
3 | infblock.h | 5 | infblock.h |
4 | infcodes.c | 6 | infcodes.c |
5 | infcodes.h | 7 | infcodes.h |
6 | inffast.c | 8 | inffast.c |
7 | inffast.h | 9 | inffast.h |
10 | inffixed.h | ||
8 | inflate.c | 11 | inflate.c |
12 | inflate.h | ||
9 | inftrees.c | 13 | inftrees.c |
10 | inftrees.h | 14 | inftrees.h |
11 | infutil.c | 15 | infutil.c |
12 | infutil.h | 16 | infutil.h |
13 | kernel-vmlinux.strip.c | 17 | kernel-vmlinux.strip.c |
14 | kernel-vmlinux.strip.gz | 18 | kernel-vmlinux.strip.gz |
19 | mktree | ||
15 | uImage | 20 | uImage |
16 | zImage | 21 | zImage |
22 | zImage.chrp | ||
23 | zImage.coff | ||
24 | zImage.coff.lds | ||
25 | zImage.lds | ||
26 | zImage.miboot | ||
27 | zImage.pmac | ||
28 | zImage.pseries | ||
29 | zImage.sandpoint | ||
17 | zImage.vmode | 30 | zImage.vmode |
18 | zconf.h | 31 | zconf.h |
19 | zlib.h | 32 | zlib.h |
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 4b2be611f77f..343dbcfdf08a 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile | |||
@@ -40,7 +40,8 @@ zliblinuxheader := zlib.h zconf.h zutil.h | |||
40 | $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ | 40 | $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ |
41 | $(addprefix $(obj)/,$(zlibheader)) | 41 | $(addprefix $(obj)/,$(zlibheader)) |
42 | 42 | ||
43 | src-wlib := string.S stdio.c main.c div64.S $(zlib) | 43 | src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ |
44 | ns16550.c serial.c simple_alloc.c div64.S util.S $(zlib) | ||
44 | src-plat := of.c | 45 | src-plat := of.c |
45 | src-boot := crt0.S $(src-wlib) $(src-plat) empty.c | 46 | src-boot := crt0.S $(src-wlib) $(src-plat) empty.c |
46 | 47 | ||
@@ -74,7 +75,7 @@ $(obj)/zImage.lds $(obj)/zImage.coff.lds: $(obj)/%: $(srctree)/$(src)/%.S | |||
74 | @cp $< $@ | 75 | @cp $< $@ |
75 | 76 | ||
76 | clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \ | 77 | clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \ |
77 | $(obj)/empty.c | 78 | empty.c zImage zImage.coff.lds zImage.lds zImage.sandpoint |
78 | 79 | ||
79 | quiet_cmd_bootcc = BOOTCC $@ | 80 | quiet_cmd_bootcc = BOOTCC $@ |
80 | cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $< | 81 | cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $< |
@@ -93,13 +94,13 @@ $(patsubst %.S,%.o, $(filter %.S, $(src-boot))): %.o: %.S | |||
93 | $(obj)/wrapper.a: $(obj-wlib) | 94 | $(obj)/wrapper.a: $(obj-wlib) |
94 | $(call cmd,bootar) | 95 | $(call cmd,bootar) |
95 | 96 | ||
96 | hostprogs-y := addnote addRamDisk hack-coff | 97 | hostprogs-y := addnote addRamDisk hack-coff mktree |
97 | 98 | ||
98 | extra-y := $(obj)/crt0.o $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ | 99 | extra-y := $(obj)/crt0.o $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ |
99 | $(obj)/zImage.lds $(obj)/zImage.coff.lds | 100 | $(obj)/zImage.lds $(obj)/zImage.coff.lds |
100 | 101 | ||
101 | wrapper :=$(srctree)/$(src)/wrapper | 102 | wrapper :=$(srctree)/$(src)/wrapper |
102 | wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff) | 103 | wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) |
103 | 104 | ||
104 | ############# | 105 | ############# |
105 | # Bits for building various flavours of zImage | 106 | # Bits for building various flavours of zImage |
@@ -148,13 +149,18 @@ $(obj)/zImage.miboot: vmlinux $(wrapperbits) | |||
148 | $(obj)/zImage.initrd.miboot: vmlinux $(wrapperbits) | 149 | $(obj)/zImage.initrd.miboot: vmlinux $(wrapperbits) |
149 | $(call cmd,wrap_initrd,miboot) | 150 | $(call cmd,wrap_initrd,miboot) |
150 | 151 | ||
152 | $(obj)/zImage.ps3: vmlinux | ||
153 | $(STRIP) -s -R .comment $< -o $@ | ||
154 | |||
151 | $(obj)/uImage: vmlinux $(wrapperbits) | 155 | $(obj)/uImage: vmlinux $(wrapperbits) |
152 | $(call cmd,wrap,uboot) | 156 | $(call cmd,wrap,uboot) |
153 | 157 | ||
154 | image-$(CONFIG_PPC_PSERIES) += zImage.pseries | 158 | image-$(CONFIG_PPC_PSERIES) += zImage.pseries |
155 | image-$(CONFIG_PPC_MAPLE) += zImage.pseries | 159 | image-$(CONFIG_PPC_MAPLE) += zImage.pseries |
156 | image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries | 160 | image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries |
161 | image-$(CONFIG_PPC_PS3) += zImage.ps3 | ||
157 | image-$(CONFIG_PPC_CHRP) += zImage.chrp | 162 | image-$(CONFIG_PPC_CHRP) += zImage.chrp |
163 | image-$(CONFIG_PPC_EFIKA) += zImage.chrp | ||
158 | image-$(CONFIG_PPC_PMAC) += zImage.pmac | 164 | image-$(CONFIG_PPC_PMAC) += zImage.pmac |
159 | image-$(CONFIG_DEFAULT_UIMAGE) += uImage | 165 | image-$(CONFIG_DEFAULT_UIMAGE) += uImage |
160 | 166 | ||
@@ -176,3 +182,4 @@ install: $(CONFIGURE) $(image-y) | |||
176 | 182 | ||
177 | clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.strip.gz) | 183 | clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.strip.gz) |
178 | clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.bin.gz) | 184 | clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.bin.gz) |
185 | clean-files += $(image-) | ||
diff --git a/arch/powerpc/boot/dts/kuroboxHG.dts b/arch/powerpc/boot/dts/kuroboxHG.dts new file mode 100644 index 000000000000..d06b0b018899 --- /dev/null +++ b/arch/powerpc/boot/dts/kuroboxHG.dts | |||
@@ -0,0 +1,148 @@ | |||
1 | /* | ||
2 | * Device Tree Souce for Buffalo KuroboxHG | ||
3 | * | ||
4 | * Choose CONFIG_LINKSTATION to build a kernel for KuroboxHG, or use | ||
5 | * the default configuration linkstation_defconfig. | ||
6 | * | ||
7 | * Based on sandpoint.dts | ||
8 | * | ||
9 | * 2006 (c) G. Liakhovetski <g.liakhovetski@gmx.de> | ||
10 | * | ||
11 | * This file is licensed under | ||
12 | * the terms of the GNU General Public License version 2. This program | ||
13 | * is licensed "as is" without any warranty of any kind, whether express | ||
14 | * or implied. | ||
15 | |||
16 | XXXX add flash parts, rtc, ?? | ||
17 | |||
18 | build with: "dtc -f -I dts -O dtb -o kuroboxHG.dtb -V 16 kuroboxHG.dts" | ||
19 | |||
20 | |||
21 | */ | ||
22 | |||
23 | / { | ||
24 | linux,phandle = <1000>; | ||
25 | model = "KuroboxHG"; | ||
26 | compatible = "linkstation"; | ||
27 | #address-cells = <1>; | ||
28 | #size-cells = <1>; | ||
29 | |||
30 | cpus { | ||
31 | linux,phandle = <2000>; | ||
32 | #cpus = <1>; | ||
33 | #address-cells = <1>; | ||
34 | #size-cells = <0>; | ||
35 | |||
36 | PowerPC,603e { /* Really 8241 */ | ||
37 | linux,phandle = <2100>; | ||
38 | linux,boot-cpu; | ||
39 | device_type = "cpu"; | ||
40 | reg = <0>; | ||
41 | clock-frequency = <fdad680>; /* Fixed by bootwrapper */ | ||
42 | timebase-frequency = <1F04000>; /* Fixed by bootwrapper */ | ||
43 | bus-frequency = <0>; /* From bootloader */ | ||
44 | /* Following required by dtc but not used */ | ||
45 | i-cache-line-size = <0>; | ||
46 | d-cache-line-size = <0>; | ||
47 | i-cache-size = <4000>; | ||
48 | d-cache-size = <4000>; | ||
49 | }; | ||
50 | }; | ||
51 | |||
52 | memory { | ||
53 | linux,phandle = <3000>; | ||
54 | device_type = "memory"; | ||
55 | reg = <00000000 08000000>; | ||
56 | }; | ||
57 | |||
58 | soc10x { /* AFAICT need to make soc for 8245's uarts to be defined */ | ||
59 | linux,phandle = <4000>; | ||
60 | #address-cells = <1>; | ||
61 | #size-cells = <1>; | ||
62 | #interrupt-cells = <2>; | ||
63 | device_type = "soc"; | ||
64 | compatible = "mpc10x"; | ||
65 | store-gathering = <0>; /* 0 == off, !0 == on */ | ||
66 | reg = <80000000 00100000>; | ||
67 | ranges = <80000000 80000000 70000000 /* pci mem space */ | ||
68 | fc000000 fc000000 00100000 /* EUMB */ | ||
69 | fe000000 fe000000 00c00000 /* pci i/o space */ | ||
70 | fec00000 fec00000 00300000 /* pci cfg regs */ | ||
71 | fef00000 fef00000 00100000>; /* pci iack */ | ||
72 | |||
73 | i2c@80003000 { | ||
74 | linux,phandle = <4300>; | ||
75 | device_type = "i2c"; | ||
76 | compatible = "fsl-i2c"; | ||
77 | reg = <80003000 1000>; | ||
78 | interrupts = <5 2>; | ||
79 | interrupt-parent = <4400>; | ||
80 | }; | ||
81 | |||
82 | serial@80004500 { | ||
83 | linux,phandle = <4511>; | ||
84 | device_type = "serial"; | ||
85 | compatible = "ns16550"; | ||
86 | reg = <80004500 8>; | ||
87 | clock-frequency = <7c044a8>; | ||
88 | current-speed = <2580>; | ||
89 | interrupts = <9 2>; | ||
90 | interrupt-parent = <4400>; | ||
91 | }; | ||
92 | |||
93 | serial@80004600 { | ||
94 | linux,phandle = <4512>; | ||
95 | device_type = "serial"; | ||
96 | compatible = "ns16550"; | ||
97 | reg = <80004600 8>; | ||
98 | clock-frequency = <7c044a8>; | ||
99 | current-speed = <e100>; | ||
100 | interrupts = <a 0>; | ||
101 | interrupt-parent = <4400>; | ||
102 | }; | ||
103 | |||
104 | pic@80040000 { | ||
105 | linux,phandle = <4400>; | ||
106 | #interrupt-cells = <2>; | ||
107 | #address-cells = <0>; | ||
108 | device_type = "open-pic"; | ||
109 | compatible = "chrp,open-pic"; | ||
110 | interrupt-controller; | ||
111 | reg = <80040000 40000>; | ||
112 | built-in; | ||
113 | }; | ||
114 | |||
115 | pci@fec00000 { | ||
116 | linux,phandle = <4500>; | ||
117 | #address-cells = <3>; | ||
118 | #size-cells = <2>; | ||
119 | #interrupt-cells = <1>; | ||
120 | device_type = "pci"; | ||
121 | compatible = "mpc10x-pci"; | ||
122 | reg = <fec00000 400000>; | ||
123 | ranges = <01000000 0 0 fe000000 0 00c00000 | ||
124 | 02000000 0 80000000 80000000 0 70000000>; | ||
125 | bus-range = <0 ff>; | ||
126 | clock-frequency = <7f28155>; | ||
127 | interrupt-parent = <4400>; | ||
128 | interrupt-map-mask = <f800 0 0 7>; | ||
129 | interrupt-map = < | ||
130 | /* IDSEL 0x11 - IRQ0 ETH */ | ||
131 | 5800 0 0 1 4400 0 1 | ||
132 | 5800 0 0 2 4400 1 1 | ||
133 | 5800 0 0 3 4400 2 1 | ||
134 | 5800 0 0 4 4400 3 1 | ||
135 | /* IDSEL 0x12 - IRQ1 IDE0 */ | ||
136 | 6000 0 0 1 4400 1 1 | ||
137 | 6000 0 0 2 4400 2 1 | ||
138 | 6000 0 0 3 4400 3 1 | ||
139 | 6000 0 0 4 4400 0 1 | ||
140 | /* IDSEL 0x14 - IRQ3 USB2.0 */ | ||
141 | 7000 0 0 1 4400 3 1 | ||
142 | 7000 0 0 2 4400 3 1 | ||
143 | 7000 0 0 3 4400 3 1 | ||
144 | 7000 0 0 4 4400 3 1 | ||
145 | >; | ||
146 | }; | ||
147 | }; | ||
148 | }; | ||
diff --git a/arch/powerpc/boot/dts/lite5200.dts b/arch/powerpc/boot/dts/lite5200.dts new file mode 100644 index 000000000000..8bc0d259796d --- /dev/null +++ b/arch/powerpc/boot/dts/lite5200.dts | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * Lite5200 board Device Tree Source | ||
3 | * | ||
4 | * Copyright 2006 Secret Lab Technologies Ltd. | ||
5 | * Grant Likely <grant.likely@secretlab.ca> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | |||
13 | / { | ||
14 | model = "Lite5200"; | ||
15 | compatible = "lite5200\0lite52xx\0mpc5200\0mpc52xx"; | ||
16 | #address-cells = <1>; | ||
17 | #size-cells = <1>; | ||
18 | |||
19 | cpus { | ||
20 | #cpus = <1>; | ||
21 | #address-cells = <1>; | ||
22 | #size-cells = <0>; | ||
23 | |||
24 | PowerPC,5200@0 { | ||
25 | device_type = "cpu"; | ||
26 | reg = <0>; | ||
27 | d-cache-line-size = <20>; | ||
28 | i-cache-line-size = <20>; | ||
29 | d-cache-size = <4000>; // L1, 16K | ||
30 | i-cache-size = <4000>; // L1, 16K | ||
31 | timebase-frequency = <0>; // from bootloader | ||
32 | bus-frequency = <0>; // from bootloader | ||
33 | clock-frequency = <0>; // from bootloader | ||
34 | 32-bit; | ||
35 | }; | ||
36 | }; | ||
37 | |||
38 | memory { | ||
39 | device_type = "memory"; | ||
40 | reg = <00000000 04000000>; // 64MB | ||
41 | }; | ||
42 | |||
43 | soc5200@f0000000 { | ||
44 | #interrupt-cells = <3>; | ||
45 | device_type = "soc"; | ||
46 | ranges = <0 f0000000 f0010000>; | ||
47 | reg = <f0000000 00010000>; | ||
48 | bus-frequency = <0>; // from bootloader | ||
49 | |||
50 | cdm@200 { | ||
51 | compatible = "mpc5200-cdm\0mpc52xx-cdm"; | ||
52 | reg = <200 38>; | ||
53 | }; | ||
54 | |||
55 | pic@500 { | ||
56 | // 5200 interrupts are encoded into two levels; | ||
57 | linux,phandle = <500>; | ||
58 | interrupt-controller; | ||
59 | #interrupt-cells = <3>; | ||
60 | device_type = "interrupt-controller"; | ||
61 | compatible = "mpc5200-pic\0mpc52xx-pic"; | ||
62 | reg = <500 80>; | ||
63 | built-in; | ||
64 | }; | ||
65 | |||
66 | gpt@600 { // General Purpose Timer | ||
67 | compatible = "mpc5200-gpt\0mpc52xx-gpt"; | ||
68 | device_type = "gpt"; | ||
69 | reg = <600 10>; | ||
70 | interrupts = <1 9 0>; | ||
71 | interrupt-parent = <500>; | ||
72 | }; | ||
73 | |||
74 | gpt@610 { // General Purpose Timer | ||
75 | compatible = "mpc5200-gpt\0mpc52xx-gpt"; | ||
76 | device_type = "gpt"; | ||
77 | reg = <610 10>; | ||
78 | interrupts = <1 a 0>; | ||
79 | interrupt-parent = <500>; | ||
80 | }; | ||
81 | |||
82 | gpt@620 { // General Purpose Timer | ||
83 | compatible = "mpc5200-gpt\0mpc52xx-gpt"; | ||
84 | device_type = "gpt"; | ||
85 | reg = <620 10>; | ||
86 | interrupts = <1 b 0>; | ||
87 | interrupt-parent = <500>; | ||
88 | }; | ||
89 | |||
90 | gpt@630 { // General Purpose Timer | ||
91 | compatible = "mpc5200-gpt\0mpc52xx-gpt"; | ||
92 | device_type = "gpt"; | ||
93 | reg = <630 10>; | ||
94 | interrupts = <1 c 0>; | ||
95 | interrupt-parent = <500>; | ||
96 | }; | ||
97 | |||
98 | gpt@640 { // General Purpose Timer | ||
99 | compatible = "mpc5200-gpt\0mpc52xx-gpt"; | ||
100 | device_type = "gpt"; | ||
101 | reg = <640 10>; | ||
102 | interrupts = <1 d 0>; | ||
103 | interrupt-parent = <500>; | ||
104 | }; | ||
105 | |||
106 | gpt@650 { // General Purpose Timer | ||
107 | compatible = "mpc5200-gpt\0mpc52xx-gpt"; | ||
108 | device_type = "gpt"; | ||
109 | reg = <650 10>; | ||
110 | interrupts = <1 e 0>; | ||
111 | interrupt-parent = <500>; | ||
112 | }; | ||
113 | |||
114 | gpt@660 { // General Purpose Timer | ||
115 | compatible = "mpc5200-gpt\0mpc52xx-gpt"; | ||
116 | device_type = "gpt"; | ||
117 | reg = <660 10>; | ||
118 | interrupts = <1 f 0>; | ||
119 | interrupt-parent = <500>; | ||
120 | }; | ||
121 | |||
122 | gpt@670 { // General Purpose Timer | ||
123 | compatible = "mpc5200-gpt\0mpc52xx-gpt"; | ||
124 | device_type = "gpt"; | ||
125 | reg = <670 10>; | ||
126 | interrupts = <1 10 0>; | ||
127 | interrupt-parent = <500>; | ||
128 | }; | ||
129 | |||
130 | rtc@800 { // Real time clock | ||
131 | compatible = "mpc5200-rtc\0mpc52xx-rtc"; | ||
132 | device_type = "rtc"; | ||
133 | reg = <800 100>; | ||
134 | interrupts = <1 5 0 1 6 0>; | ||
135 | interrupt-parent = <500>; | ||
136 | }; | ||
137 | |||
138 | mscan@900 { | ||
139 | device_type = "mscan"; | ||
140 | compatible = "mpc5200-mscan\0mpc52xx-mscan"; | ||
141 | interrupts = <2 11 0>; | ||
142 | interrupt-parent = <500>; | ||
143 | reg = <900 80>; | ||
144 | }; | ||
145 | |||
146 | mscan@980 { | ||
147 | device_type = "mscan"; | ||
148 | compatible = "mpc5200-mscan\0mpc52xx-mscan"; | ||
149 | interrupts = <1 12 0>; | ||
150 | interrupt-parent = <500>; | ||
151 | reg = <980 80>; | ||
152 | }; | ||
153 | |||
154 | gpio@b00 { | ||
155 | compatible = "mpc5200-gpio\0mpc52xx-gpio"; | ||
156 | reg = <b00 40>; | ||
157 | interrupts = <1 7 0>; | ||
158 | interrupt-parent = <500>; | ||
159 | }; | ||
160 | |||
161 | gpio-wkup@b00 { | ||
162 | compatible = "mpc5200-gpio-wkup\0mpc52xx-gpio-wkup"; | ||
163 | reg = <c00 40>; | ||
164 | interrupts = <1 8 0 0 3 0>; | ||
165 | interrupt-parent = <500>; | ||
166 | }; | ||
167 | |||
168 | pci@0d00 { | ||
169 | #interrupt-cells = <1>; | ||
170 | #size-cells = <2>; | ||
171 | #address-cells = <3>; | ||
172 | device_type = "pci"; | ||
173 | compatible = "mpc5200-pci\0mpc52xx-pci"; | ||
174 | reg = <d00 100>; | ||
175 | interrupt-map-mask = <f800 0 0 7>; | ||
176 | interrupt-map = <c000 0 0 1 500 0 0 3 | ||
177 | c000 0 0 2 500 0 0 3 | ||
178 | c000 0 0 3 500 0 0 3 | ||
179 | c000 0 0 4 500 0 0 3>; | ||
180 | clock-frequency = <0>; // From boot loader | ||
181 | interrupts = <2 8 0 2 9 0 2 a 0>; | ||
182 | interrupt-parent = <500>; | ||
183 | bus-range = <0 0>; | ||
184 | ranges = <42000000 0 80000000 80000000 0 20000000 | ||
185 | 02000000 0 a0000000 a0000000 0 10000000 | ||
186 | 01000000 0 00000000 b0000000 0 01000000>; | ||
187 | }; | ||
188 | |||
189 | spi@f00 { | ||
190 | device_type = "spi"; | ||
191 | compatible = "mpc5200-spi\0mpc52xx-spi"; | ||
192 | reg = <f00 20>; | ||
193 | interrupts = <2 d 0 2 e 0>; | ||
194 | interrupt-parent = <500>; | ||
195 | }; | ||
196 | |||
197 | usb@1000 { | ||
198 | device_type = "usb-ohci-be"; | ||
199 | compatible = "mpc5200-ohci\0mpc52xx-ohci\0ohci-be"; | ||
200 | reg = <1000 ff>; | ||
201 | interrupts = <2 6 0>; | ||
202 | interrupt-parent = <500>; | ||
203 | }; | ||
204 | |||
205 | bestcomm@1200 { | ||
206 | device_type = "dma-controller"; | ||
207 | compatible = "mpc5200-bestcomm\0mpc52xx-bestcomm"; | ||
208 | reg = <1200 80>; | ||
209 | interrupts = <3 0 0 3 1 0 3 2 0 3 3 0 | ||
210 | 3 4 0 3 5 0 3 6 0 3 7 0 | ||
211 | 3 8 0 3 9 0 3 a 0 3 b 0 | ||
212 | 3 c 0 3 d 0 3 e 0 3 f 0>; | ||
213 | interrupt-parent = <500>; | ||
214 | }; | ||
215 | |||
216 | xlb@1f00 { | ||
217 | compatible = "mpc5200-xlb\0mpc52xx-xlb"; | ||
218 | reg = <1f00 100>; | ||
219 | }; | ||
220 | |||
221 | serial@2000 { // PSC1 | ||
222 | device_type = "serial"; | ||
223 | compatible = "mpc5200-psc-uart\0mpc52xx-psc-uart"; | ||
224 | port-number = <0>; // Logical port assignment | ||
225 | reg = <2000 100>; | ||
226 | interrupts = <2 1 0>; | ||
227 | interrupt-parent = <500>; | ||
228 | }; | ||
229 | |||
230 | // PSC2 in spi mode example | ||
231 | spi@2200 { // PSC2 | ||
232 | device_type = "spi"; | ||
233 | compatible = "mpc5200-psc-spi\0mpc52xx-psc-spi"; | ||
234 | reg = <2200 100>; | ||
235 | interrupts = <2 2 0>; | ||
236 | interrupt-parent = <500>; | ||
237 | }; | ||
238 | |||
239 | // PSC3 in CODEC mode example | ||
240 | i2s@2400 { // PSC3 | ||
241 | device_type = "i2s"; | ||
242 | compatible = "mpc5200-psc-i2s\0mpc52xx-psc-i2s"; | ||
243 | reg = <2400 100>; | ||
244 | interrupts = <2 3 0>; | ||
245 | interrupt-parent = <500>; | ||
246 | }; | ||
247 | |||
248 | // PSC4 unconfigured | ||
249 | //serial@2600 { // PSC4 | ||
250 | // device_type = "serial"; | ||
251 | // compatible = "mpc5200-psc-uart\0mpc52xx-psc-uart"; | ||
252 | // reg = <2600 100>; | ||
253 | // interrupts = <2 b 0>; | ||
254 | // interrupt-parent = <500>; | ||
255 | //}; | ||
256 | |||
257 | // PSC5 unconfigured | ||
258 | //serial@2800 { // PSC5 | ||
259 | // device_type = "serial"; | ||
260 | // compatible = "mpc5200-psc-uart\0mpc52xx-psc-uart"; | ||
261 | // reg = <2800 100>; | ||
262 | // interrupts = <2 c 0>; | ||
263 | // interrupt-parent = <500>; | ||
264 | //}; | ||
265 | |||
266 | // PSC6 in AC97 mode example | ||
267 | ac97@2c00 { // PSC6 | ||
268 | device_type = "ac97"; | ||
269 | compatible = "mpc5200-psc-ac97\0mpc52xx-psc-ac97"; | ||
270 | reg = <2c00 100>; | ||
271 | interrupts = <2 4 0>; | ||
272 | interrupt-parent = <500>; | ||
273 | }; | ||
274 | |||
275 | ethernet@3000 { | ||
276 | device_type = "network"; | ||
277 | compatible = "mpc5200-fec\0mpc52xx-fec"; | ||
278 | reg = <3000 800>; | ||
279 | mac-address = [ 02 03 04 05 06 07 ]; // Bad! | ||
280 | interrupts = <2 5 0>; | ||
281 | interrupt-parent = <500>; | ||
282 | }; | ||
283 | |||
284 | ata@3a00 { | ||
285 | device_type = "ata"; | ||
286 | compatible = "mpc5200-ata\0mpc52xx-ata"; | ||
287 | reg = <3a00 100>; | ||
288 | interrupts = <2 7 0>; | ||
289 | interrupt-parent = <500>; | ||
290 | }; | ||
291 | |||
292 | i2c@3d00 { | ||
293 | device_type = "i2c"; | ||
294 | compatible = "mpc5200-i2c\0mpc52xx-i2c"; | ||
295 | reg = <3d00 40>; | ||
296 | interrupts = <2 f 0>; | ||
297 | interrupt-parent = <500>; | ||
298 | }; | ||
299 | |||
300 | i2c@3d40 { | ||
301 | device_type = "i2c"; | ||
302 | compatible = "mpc5200-i2c\0mpc52xx-i2c"; | ||
303 | reg = <3d40 40>; | ||
304 | interrupts = <2 10 0>; | ||
305 | interrupt-parent = <500>; | ||
306 | }; | ||
307 | sram@8000 { | ||
308 | device_type = "sram"; | ||
309 | compatible = "mpc5200-sram\0mpc52xx-sram\0sram"; | ||
310 | reg = <8000 4000>; | ||
311 | }; | ||
312 | }; | ||
313 | }; | ||
diff --git a/arch/powerpc/boot/dts/lite5200b.dts b/arch/powerpc/boot/dts/lite5200b.dts new file mode 100644 index 000000000000..81cb76418a78 --- /dev/null +++ b/arch/powerpc/boot/dts/lite5200b.dts | |||
@@ -0,0 +1,318 @@ | |||
1 | /* | ||
2 | * Lite5200B board Device Tree Source | ||
3 | * | ||
4 | * Copyright 2006 Secret Lab Technologies Ltd. | ||
5 | * Grant Likely <grant.likely@secretlab.ca> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | |||
13 | / { | ||
14 | model = "Lite5200b"; | ||
15 | compatible = "lite5200b\0lite52xx\0mpc5200b\0mpc52xx"; | ||
16 | #address-cells = <1>; | ||
17 | #size-cells = <1>; | ||
18 | |||
19 | cpus { | ||
20 | #cpus = <1>; | ||
21 | #address-cells = <1>; | ||
22 | #size-cells = <0>; | ||
23 | |||
24 | PowerPC,5200@0 { | ||
25 | device_type = "cpu"; | ||
26 | reg = <0>; | ||
27 | d-cache-line-size = <20>; | ||
28 | i-cache-line-size = <20>; | ||
29 | d-cache-size = <4000>; // L1, 16K | ||
30 | i-cache-size = <4000>; // L1, 16K | ||
31 | timebase-frequency = <0>; // from bootloader | ||
32 | bus-frequency = <0>; // from bootloader | ||
33 | clock-frequency = <0>; // from bootloader | ||
34 | 32-bit; | ||
35 | }; | ||
36 | }; | ||
37 | |||
38 | memory { | ||
39 | device_type = "memory"; | ||
40 | reg = <00000000 10000000>; // 256MB | ||
41 | }; | ||
42 | |||
43 | soc5200@f0000000 { | ||
44 | #interrupt-cells = <3>; | ||
45 | device_type = "soc"; | ||
46 | ranges = <0 f0000000 f0010000>; | ||
47 | reg = <f0000000 00010000>; | ||
48 | bus-frequency = <0>; // from bootloader | ||
49 | |||
50 | cdm@200 { | ||
51 | compatible = "mpc5200b-cdm\0mpc52xx-cdm"; | ||
52 | reg = <200 38>; | ||
53 | }; | ||
54 | |||
55 | pic@500 { | ||
56 | // 5200 interrupts are encoded into two levels; | ||
57 | linux,phandle = <500>; | ||
58 | interrupt-controller; | ||
59 | #interrupt-cells = <3>; | ||
60 | device_type = "interrupt-controller"; | ||
61 | compatible = "mpc5200b-pic\0mpc52xx-pic"; | ||
62 | reg = <500 80>; | ||
63 | built-in; | ||
64 | }; | ||
65 | |||
66 | gpt@600 { // General Purpose Timer | ||
67 | compatible = "mpc5200b-gpt\0mpc52xx-gpt"; | ||
68 | device_type = "gpt"; | ||
69 | reg = <600 10>; | ||
70 | interrupts = <1 9 0>; | ||
71 | interrupt-parent = <500>; | ||
72 | }; | ||
73 | |||
74 | gpt@610 { // General Purpose Timer | ||
75 | compatible = "mpc5200b-gpt\0mpc52xx-gpt"; | ||
76 | device_type = "gpt"; | ||
77 | reg = <610 10>; | ||
78 | interrupts = <1 a 0>; | ||
79 | interrupt-parent = <500>; | ||
80 | }; | ||
81 | |||
82 | gpt@620 { // General Purpose Timer | ||
83 | compatible = "mpc5200b-gpt\0mpc52xx-gpt"; | ||
84 | device_type = "gpt"; | ||
85 | reg = <620 10>; | ||
86 | interrupts = <1 b 0>; | ||
87 | interrupt-parent = <500>; | ||
88 | }; | ||
89 | |||
90 | gpt@630 { // General Purpose Timer | ||
91 | compatible = "mpc5200b-gpt\0mpc52xx-gpt"; | ||
92 | device_type = "gpt"; | ||
93 | reg = <630 10>; | ||
94 | interrupts = <1 c 0>; | ||
95 | interrupt-parent = <500>; | ||
96 | }; | ||
97 | |||
98 | gpt@640 { // General Purpose Timer | ||
99 | compatible = "mpc5200b-gpt\0mpc52xx-gpt"; | ||
100 | device_type = "gpt"; | ||
101 | reg = <640 10>; | ||
102 | interrupts = <1 d 0>; | ||
103 | interrupt-parent = <500>; | ||
104 | }; | ||
105 | |||
106 | gpt@650 { // General Purpose Timer | ||
107 | compatible = "mpc5200b-gpt\0mpc52xx-gpt"; | ||
108 | device_type = "gpt"; | ||
109 | reg = <650 10>; | ||
110 | interrupts = <1 e 0>; | ||
111 | interrupt-parent = <500>; | ||
112 | }; | ||
113 | |||
114 | gpt@660 { // General Purpose Timer | ||
115 | compatible = "mpc5200b-gpt\0mpc52xx-gpt"; | ||
116 | device_type = "gpt"; | ||
117 | reg = <660 10>; | ||
118 | interrupts = <1 f 0>; | ||
119 | interrupt-parent = <500>; | ||
120 | }; | ||
121 | |||
122 | gpt@670 { // General Purpose Timer | ||
123 | compatible = "mpc5200b-gpt\0mpc52xx-gpt"; | ||
124 | device_type = "gpt"; | ||
125 | reg = <670 10>; | ||
126 | interrupts = <1 10 0>; | ||
127 | interrupt-parent = <500>; | ||
128 | }; | ||
129 | |||
130 | rtc@800 { // Real time clock | ||
131 | compatible = "mpc5200b-rtc\0mpc52xx-rtc"; | ||
132 | device_type = "rtc"; | ||
133 | reg = <800 100>; | ||
134 | interrupts = <1 5 0 1 6 0>; | ||
135 | interrupt-parent = <500>; | ||
136 | }; | ||
137 | |||
138 | mscan@900 { | ||
139 | device_type = "mscan"; | ||
140 | compatible = "mpc5200b-mscan\0mpc52xx-mscan"; | ||
141 | interrupts = <2 11 0>; | ||
142 | interrupt-parent = <500>; | ||
143 | reg = <900 80>; | ||
144 | }; | ||
145 | |||
146 | mscan@980 { | ||
147 | device_type = "mscan"; | ||
148 | compatible = "mpc5200b-mscan\0mpc52xx-mscan"; | ||
149 | interrupts = <1 12 0>; | ||
150 | interrupt-parent = <500>; | ||
151 | reg = <980 80>; | ||
152 | }; | ||
153 | |||
154 | gpio@b00 { | ||
155 | compatible = "mpc5200b-gpio\0mpc52xx-gpio"; | ||
156 | reg = <b00 40>; | ||
157 | interrupts = <1 7 0>; | ||
158 | interrupt-parent = <500>; | ||
159 | }; | ||
160 | |||
161 | gpio-wkup@b00 { | ||
162 | compatible = "mpc5200b-gpio-wkup\0mpc52xx-gpio-wkup"; | ||
163 | reg = <c00 40>; | ||
164 | interrupts = <1 8 0 0 3 0>; | ||
165 | interrupt-parent = <500>; | ||
166 | }; | ||
167 | |||
168 | pci@0d00 { | ||
169 | #interrupt-cells = <1>; | ||
170 | #size-cells = <2>; | ||
171 | #address-cells = <3>; | ||
172 | device_type = "pci"; | ||
173 | compatible = "mpc5200b-pci\0mpc52xx-pci"; | ||
174 | reg = <d00 100>; | ||
175 | interrupt-map-mask = <f800 0 0 7>; | ||
176 | interrupt-map = <c000 0 0 1 500 0 0 3 // 1st slot | ||
177 | c000 0 0 2 500 1 1 3 | ||
178 | c000 0 0 3 500 1 2 3 | ||
179 | c000 0 0 4 500 1 3 3 | ||
180 | |||
181 | c800 0 0 1 500 1 1 3 // 2nd slot | ||
182 | c800 0 0 2 500 1 2 3 | ||
183 | c800 0 0 3 500 1 3 3 | ||
184 | c800 0 0 4 500 0 0 3>; | ||
185 | clock-frequency = <0>; // From boot loader | ||
186 | interrupts = <2 8 0 2 9 0 2 a 0>; | ||
187 | interrupt-parent = <500>; | ||
188 | bus-range = <0 0>; | ||
189 | ranges = <42000000 0 80000000 80000000 0 20000000 | ||
190 | 02000000 0 a0000000 a0000000 0 10000000 | ||
191 | 01000000 0 00000000 b0000000 0 01000000>; | ||
192 | }; | ||
193 | |||
194 | spi@f00 { | ||
195 | device_type = "spi"; | ||
196 | compatible = "mpc5200b-spi\0mpc52xx-spi"; | ||
197 | reg = <f00 20>; | ||
198 | interrupts = <2 d 0 2 e 0>; | ||
199 | interrupt-parent = <500>; | ||
200 | }; | ||
201 | |||
202 | usb@1000 { | ||
203 | device_type = "usb-ohci-be"; | ||
204 | compatible = "mpc5200b-ohci\0mpc52xx-ohci\0ohci-be"; | ||
205 | reg = <1000 ff>; | ||
206 | interrupts = <2 6 0>; | ||
207 | interrupt-parent = <500>; | ||
208 | }; | ||
209 | |||
210 | bestcomm@1200 { | ||
211 | device_type = "dma-controller"; | ||
212 | compatible = "mpc5200b-bestcomm\0mpc52xx-bestcomm"; | ||
213 | reg = <1200 80>; | ||
214 | interrupts = <3 0 0 3 1 0 3 2 0 3 3 0 | ||
215 | 3 4 0 3 5 0 3 6 0 3 7 0 | ||
216 | 3 8 0 3 9 0 3 a 0 3 b 0 | ||
217 | 3 c 0 3 d 0 3 e 0 3 f 0>; | ||
218 | interrupt-parent = <500>; | ||
219 | }; | ||
220 | |||
221 | xlb@1f00 { | ||
222 | compatible = "mpc5200b-xlb\0mpc52xx-xlb"; | ||
223 | reg = <1f00 100>; | ||
224 | }; | ||
225 | |||
226 | serial@2000 { // PSC1 | ||
227 | device_type = "serial"; | ||
228 | compatible = "mpc5200b-psc-uart\0mpc52xx-psc-uart"; | ||
229 | port-number = <0>; // Logical port assignment | ||
230 | reg = <2000 100>; | ||
231 | interrupts = <2 1 0>; | ||
232 | interrupt-parent = <500>; | ||
233 | }; | ||
234 | |||
235 | // PSC2 in spi mode example | ||
236 | spi@2200 { // PSC2 | ||
237 | device_type = "spi"; | ||
238 | compatible = "mpc5200b-psc-spi\0mpc52xx-psc-spi"; | ||
239 | reg = <2200 100>; | ||
240 | interrupts = <2 2 0>; | ||
241 | interrupt-parent = <500>; | ||
242 | }; | ||
243 | |||
244 | // PSC3 in CODEC mode example | ||
245 | i2s@2400 { // PSC3 | ||
246 | device_type = "i2s"; | ||
247 | compatible = "mpc5200b-psc-i2s\0mpc52xx-psc-i2s"; | ||
248 | reg = <2400 100>; | ||
249 | interrupts = <2 3 0>; | ||
250 | interrupt-parent = <500>; | ||
251 | }; | ||
252 | |||
253 | // PSC4 unconfigured | ||
254 | //serial@2600 { // PSC4 | ||
255 | // device_type = "serial"; | ||
256 | // compatible = "mpc5200b-psc-uart\0mpc52xx-psc-uart"; | ||
257 | // reg = <2600 100>; | ||
258 | // interrupts = <2 b 0>; | ||
259 | // interrupt-parent = <500>; | ||
260 | //}; | ||
261 | |||
262 | // PSC5 unconfigured | ||
263 | //serial@2800 { // PSC5 | ||
264 | // device_type = "serial"; | ||
265 | // compatible = "mpc5200b-psc-uart\0mpc52xx-psc-uart"; | ||
266 | // reg = <2800 100>; | ||
267 | // interrupts = <2 c 0>; | ||
268 | // interrupt-parent = <500>; | ||
269 | //}; | ||
270 | |||
271 | // PSC6 in AC97 mode example | ||
272 | ac97@2c00 { // PSC6 | ||
273 | device_type = "ac97"; | ||
274 | compatible = "mpc5200b-psc-ac97\0mpc52xx-psc-ac97"; | ||
275 | reg = <2c00 100>; | ||
276 | interrupts = <2 4 0>; | ||
277 | interrupt-parent = <500>; | ||
278 | }; | ||
279 | |||
280 | ethernet@3000 { | ||
281 | device_type = "network"; | ||
282 | compatible = "mpc5200b-fec\0mpc52xx-fec"; | ||
283 | reg = <3000 800>; | ||
284 | mac-address = [ 02 03 04 05 06 07 ]; // Bad! | ||
285 | interrupts = <2 5 0>; | ||
286 | interrupt-parent = <500>; | ||
287 | }; | ||
288 | |||
289 | ata@3a00 { | ||
290 | device_type = "ata"; | ||
291 | compatible = "mpc5200b-ata\0mpc52xx-ata"; | ||
292 | reg = <3a00 100>; | ||
293 | interrupts = <2 7 0>; | ||
294 | interrupt-parent = <500>; | ||
295 | }; | ||
296 | |||
297 | i2c@3d00 { | ||
298 | device_type = "i2c"; | ||
299 | compatible = "mpc5200b-i2c\0mpc52xx-i2c"; | ||
300 | reg = <3d00 40>; | ||
301 | interrupts = <2 f 0>; | ||
302 | interrupt-parent = <500>; | ||
303 | }; | ||
304 | |||
305 | i2c@3d40 { | ||
306 | device_type = "i2c"; | ||
307 | compatible = "mpc5200b-i2c\0mpc52xx-i2c"; | ||
308 | reg = <3d40 40>; | ||
309 | interrupts = <2 10 0>; | ||
310 | interrupt-parent = <500>; | ||
311 | }; | ||
312 | sram@8000 { | ||
313 | device_type = "sram"; | ||
314 | compatible = "mpc5200b-sram\0mpc52xx-sram\0sram"; | ||
315 | reg = <8000 4000>; | ||
316 | }; | ||
317 | }; | ||
318 | }; | ||
diff --git a/arch/powerpc/boot/dts/mpc7448hpc2.dts b/arch/powerpc/boot/dts/mpc7448hpc2.dts index d7b985e6bd2f..c4d9562cbaad 100644 --- a/arch/powerpc/boot/dts/mpc7448hpc2.dts +++ b/arch/powerpc/boot/dts/mpc7448hpc2.dts | |||
@@ -161,29 +161,41 @@ | |||
161 | interrupt-map = < | 161 | interrupt-map = < |
162 | 162 | ||
163 | /* IDSEL 0x11 */ | 163 | /* IDSEL 0x11 */ |
164 | 0800 0 0 1 7400 24 0 | 164 | 0800 0 0 1 1180 24 0 |
165 | 0800 0 0 2 7400 25 0 | 165 | 0800 0 0 2 1180 25 0 |
166 | 0800 0 0 3 7400 26 0 | 166 | 0800 0 0 3 1180 26 0 |
167 | 0800 0 0 4 7400 27 0 | 167 | 0800 0 0 4 1180 27 0 |
168 | 168 | ||
169 | /* IDSEL 0x12 */ | 169 | /* IDSEL 0x12 */ |
170 | 1000 0 0 1 7400 25 0 | 170 | 1000 0 0 1 1180 25 0 |
171 | 1000 0 0 2 7400 26 0 | 171 | 1000 0 0 2 1180 26 0 |
172 | 1000 0 0 3 7400 27 0 | 172 | 1000 0 0 3 1180 27 0 |
173 | 1000 0 0 4 7400 24 0 | 173 | 1000 0 0 4 1180 24 0 |
174 | 174 | ||
175 | /* IDSEL 0x13 */ | 175 | /* IDSEL 0x13 */ |
176 | 1800 0 0 1 7400 26 0 | 176 | 1800 0 0 1 1180 26 0 |
177 | 1800 0 0 2 7400 27 0 | 177 | 1800 0 0 2 1180 27 0 |
178 | 1800 0 0 3 7400 24 0 | 178 | 1800 0 0 3 1180 24 0 |
179 | 1800 0 0 4 7400 25 0 | 179 | 1800 0 0 4 1180 25 0 |
180 | 180 | ||
181 | /* IDSEL 0x14 */ | 181 | /* IDSEL 0x14 */ |
182 | 2000 0 0 1 7400 27 0 | 182 | 2000 0 0 1 1180 27 0 |
183 | 2000 0 0 2 7400 24 0 | 183 | 2000 0 0 2 1180 24 0 |
184 | 2000 0 0 3 7400 25 0 | 184 | 2000 0 0 3 1180 25 0 |
185 | 2000 0 0 4 7400 26 0 | 185 | 2000 0 0 4 1180 26 0 |
186 | >; | 186 | >; |
187 | router@1180 { | ||
188 | linux,phandle = <1180>; | ||
189 | clock-frequency = <0>; | ||
190 | interrupt-controller; | ||
191 | device_type = "pic-router"; | ||
192 | #address-cells = <0>; | ||
193 | #interrupt-cells = <2>; | ||
194 | built-in; | ||
195 | big-endian; | ||
196 | interrupts = <17 2>; | ||
197 | interrupt-parent = <7400>; | ||
198 | }; | ||
187 | }; | 199 | }; |
188 | }; | 200 | }; |
189 | 201 | ||
diff --git a/arch/powerpc/boot/flatdevtree.c b/arch/powerpc/boot/flatdevtree.c new file mode 100644 index 000000000000..c76c194715b2 --- /dev/null +++ b/arch/powerpc/boot/flatdevtree.c | |||
@@ -0,0 +1,880 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License as published by | ||
4 | * the Free Software Foundation; either version 2 of the License, or | ||
5 | * (at your option) any later version. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | * You should have received a copy of the GNU General Public License | ||
13 | * along with this program; if not, write to the Free Software | ||
14 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
15 | * | ||
16 | * Copyright Pantelis Antoniou 2006 | ||
17 | * Copyright (C) IBM Corporation 2006 | ||
18 | * | ||
19 | * Authors: Pantelis Antoniou <pantelis@embeddedalley.com> | ||
20 | * Hollis Blanchard <hollisb@us.ibm.com> | ||
21 | * Mark A. Greer <mgreer@mvista.com> | ||
22 | * Paul Mackerras <paulus@samba.org> | ||
23 | */ | ||
24 | |||
25 | #include <string.h> | ||
26 | #include <stddef.h> | ||
27 | #include "flatdevtree.h" | ||
28 | #include "flatdevtree_env.h" | ||
29 | |||
30 | #define _ALIGN(x, al) (((x) + (al) - 1) & ~((al) - 1)) | ||
31 | |||
32 | /* Routines for keeping node ptrs returned by ft_find_device current */ | ||
33 | /* First entry not used b/c it would return 0 and be taken as NULL/error */ | ||
34 | static void *ft_node_add(struct ft_cxt *cxt, char *node) | ||
35 | { | ||
36 | unsigned int i; | ||
37 | |||
38 | for (i = 1; i < cxt->nodes_used; i++) /* already there? */ | ||
39 | if (cxt->node_tbl[i] == node) | ||
40 | return (void *)i; | ||
41 | |||
42 | if (cxt->nodes_used < cxt->node_max) { | ||
43 | cxt->node_tbl[cxt->nodes_used] = node; | ||
44 | return (void *)cxt->nodes_used++; | ||
45 | } | ||
46 | |||
47 | return NULL; | ||
48 | } | ||
49 | |||
50 | static char *ft_node_ph2node(struct ft_cxt *cxt, const void *phandle) | ||
51 | { | ||
52 | unsigned int i = (unsigned int)phandle; | ||
53 | |||
54 | if (i < cxt->nodes_used) | ||
55 | return cxt->node_tbl[i]; | ||
56 | return NULL; | ||
57 | } | ||
58 | |||
59 | static void ft_node_update_before(struct ft_cxt *cxt, char *addr, int shift) | ||
60 | { | ||
61 | unsigned int i; | ||
62 | |||
63 | if (shift == 0) | ||
64 | return; | ||
65 | |||
66 | for (i = 1; i < cxt->nodes_used; i++) | ||
67 | if (cxt->node_tbl[i] < addr) | ||
68 | cxt->node_tbl[i] += shift; | ||
69 | } | ||
70 | |||
71 | static void ft_node_update_after(struct ft_cxt *cxt, char *addr, int shift) | ||
72 | { | ||
73 | unsigned int i; | ||
74 | |||
75 | if (shift == 0) | ||
76 | return; | ||
77 | |||
78 | for (i = 1; i < cxt->nodes_used; i++) | ||
79 | if (cxt->node_tbl[i] >= addr) | ||
80 | cxt->node_tbl[i] += shift; | ||
81 | } | ||
82 | |||
83 | /* Struct used to return info from ft_next() */ | ||
84 | struct ft_atom { | ||
85 | u32 tag; | ||
86 | const char *name; | ||
87 | void *data; | ||
88 | u32 size; | ||
89 | }; | ||
90 | |||
91 | /* Set ptrs to current one's info; return addr of next one */ | ||
92 | static char *ft_next(struct ft_cxt *cxt, char *p, struct ft_atom *ret) | ||
93 | { | ||
94 | u32 sz; | ||
95 | |||
96 | if (p >= cxt->rgn[FT_STRUCT].start + cxt->rgn[FT_STRUCT].size) | ||
97 | return NULL; | ||
98 | |||
99 | ret->tag = be32_to_cpu(*(u32 *) p); | ||
100 | p += 4; | ||
101 | |||
102 | switch (ret->tag) { /* Tag */ | ||
103 | case OF_DT_BEGIN_NODE: | ||
104 | ret->name = p; | ||
105 | ret->data = (void *)(p - 4); /* start of node */ | ||
106 | p += _ALIGN(strlen(p) + 1, 4); | ||
107 | break; | ||
108 | case OF_DT_PROP: | ||
109 | ret->size = sz = be32_to_cpu(*(u32 *) p); | ||
110 | ret->name = cxt->str_anchor + be32_to_cpu(*(u32 *) (p + 4)); | ||
111 | ret->data = (void *)(p + 8); | ||
112 | p += 8 + _ALIGN(sz, 4); | ||
113 | break; | ||
114 | case OF_DT_END_NODE: | ||
115 | case OF_DT_NOP: | ||
116 | break; | ||
117 | case OF_DT_END: | ||
118 | default: | ||
119 | p = NULL; | ||
120 | break; | ||
121 | } | ||
122 | |||
123 | return p; | ||
124 | } | ||
125 | |||
126 | #define HDR_SIZE _ALIGN(sizeof(struct boot_param_header), 8) | ||
127 | #define EXPAND_INCR 1024 /* alloc this much extra when expanding */ | ||
128 | |||
129 | /* See if the regions are in the standard order and non-overlapping */ | ||
130 | static int ft_ordered(struct ft_cxt *cxt) | ||
131 | { | ||
132 | char *p = (char *)cxt->bph + HDR_SIZE; | ||
133 | enum ft_rgn_id r; | ||
134 | |||
135 | for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) { | ||
136 | if (p > cxt->rgn[r].start) | ||
137 | return 0; | ||
138 | p = cxt->rgn[r].start + cxt->rgn[r].size; | ||
139 | } | ||
140 | return p <= (char *)cxt->bph + cxt->max_size; | ||
141 | } | ||
142 | |||
143 | /* Copy the tree to a newly-allocated region and put things in order */ | ||
144 | static int ft_reorder(struct ft_cxt *cxt, int nextra) | ||
145 | { | ||
146 | unsigned long tot; | ||
147 | enum ft_rgn_id r; | ||
148 | char *p, *pend; | ||
149 | int stroff; | ||
150 | |||
151 | tot = HDR_SIZE + EXPAND_INCR; | ||
152 | for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) | ||
153 | tot += cxt->rgn[r].size; | ||
154 | if (nextra > 0) | ||
155 | tot += nextra; | ||
156 | tot = _ALIGN(tot, 8); | ||
157 | |||
158 | if (!cxt->realloc) | ||
159 | return 0; | ||
160 | p = cxt->realloc(NULL, tot); | ||
161 | if (!p) | ||
162 | return 0; | ||
163 | |||
164 | memcpy(p, cxt->bph, sizeof(struct boot_param_header)); | ||
165 | /* offsets get fixed up later */ | ||
166 | |||
167 | cxt->bph = (struct boot_param_header *)p; | ||
168 | cxt->max_size = tot; | ||
169 | pend = p + tot; | ||
170 | p += HDR_SIZE; | ||
171 | |||
172 | memcpy(p, cxt->rgn[FT_RSVMAP].start, cxt->rgn[FT_RSVMAP].size); | ||
173 | cxt->rgn[FT_RSVMAP].start = p; | ||
174 | p += cxt->rgn[FT_RSVMAP].size; | ||
175 | |||
176 | memcpy(p, cxt->rgn[FT_STRUCT].start, cxt->rgn[FT_STRUCT].size); | ||
177 | ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, | ||
178 | p - cxt->rgn[FT_STRUCT].start); | ||
179 | cxt->p += p - cxt->rgn[FT_STRUCT].start; | ||
180 | cxt->rgn[FT_STRUCT].start = p; | ||
181 | |||
182 | p = pend - cxt->rgn[FT_STRINGS].size; | ||
183 | memcpy(p, cxt->rgn[FT_STRINGS].start, cxt->rgn[FT_STRINGS].size); | ||
184 | stroff = cxt->str_anchor - cxt->rgn[FT_STRINGS].start; | ||
185 | cxt->rgn[FT_STRINGS].start = p; | ||
186 | cxt->str_anchor = p + stroff; | ||
187 | |||
188 | cxt->isordered = 1; | ||
189 | return 1; | ||
190 | } | ||
191 | |||
192 | static inline char *prev_end(struct ft_cxt *cxt, enum ft_rgn_id r) | ||
193 | { | ||
194 | if (r > FT_RSVMAP) | ||
195 | return cxt->rgn[r - 1].start + cxt->rgn[r - 1].size; | ||
196 | return (char *)cxt->bph + HDR_SIZE; | ||
197 | } | ||
198 | |||
199 | static inline char *next_start(struct ft_cxt *cxt, enum ft_rgn_id r) | ||
200 | { | ||
201 | if (r < FT_STRINGS) | ||
202 | return cxt->rgn[r + 1].start; | ||
203 | return (char *)cxt->bph + cxt->max_size; | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * See if we can expand region rgn by nextra bytes by using up | ||
208 | * free space after or before the region. | ||
209 | */ | ||
210 | static int ft_shuffle(struct ft_cxt *cxt, char **pp, enum ft_rgn_id rgn, | ||
211 | int nextra) | ||
212 | { | ||
213 | char *p = *pp; | ||
214 | char *rgn_start, *rgn_end; | ||
215 | |||
216 | rgn_start = cxt->rgn[rgn].start; | ||
217 | rgn_end = rgn_start + cxt->rgn[rgn].size; | ||
218 | if (nextra <= 0 || rgn_end + nextra <= next_start(cxt, rgn)) { | ||
219 | /* move following stuff */ | ||
220 | if (p < rgn_end) { | ||
221 | if (nextra < 0) | ||
222 | memmove(p, p - nextra, rgn_end - p + nextra); | ||
223 | else | ||
224 | memmove(p + nextra, p, rgn_end - p); | ||
225 | if (rgn == FT_STRUCT) | ||
226 | ft_node_update_after(cxt, p, nextra); | ||
227 | } | ||
228 | cxt->rgn[rgn].size += nextra; | ||
229 | if (rgn == FT_STRINGS) | ||
230 | /* assumes strings only added at beginning */ | ||
231 | cxt->str_anchor += nextra; | ||
232 | return 1; | ||
233 | } | ||
234 | if (prev_end(cxt, rgn) <= rgn_start - nextra) { | ||
235 | /* move preceding stuff */ | ||
236 | if (p > rgn_start) { | ||
237 | memmove(rgn_start - nextra, rgn_start, p - rgn_start); | ||
238 | if (rgn == FT_STRUCT) | ||
239 | ft_node_update_before(cxt, p, -nextra); | ||
240 | } | ||
241 | *p -= nextra; | ||
242 | cxt->rgn[rgn].start -= nextra; | ||
243 | cxt->rgn[rgn].size += nextra; | ||
244 | return 1; | ||
245 | } | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int ft_make_space(struct ft_cxt *cxt, char **pp, enum ft_rgn_id rgn, | ||
250 | int nextra) | ||
251 | { | ||
252 | unsigned long size, ssize, tot; | ||
253 | char *str, *next; | ||
254 | enum ft_rgn_id r; | ||
255 | |||
256 | if (!cxt->isordered && !ft_reorder(cxt, nextra)) | ||
257 | return 0; | ||
258 | if (ft_shuffle(cxt, pp, rgn, nextra)) | ||
259 | return 1; | ||
260 | |||
261 | /* See if there is space after the strings section */ | ||
262 | ssize = cxt->rgn[FT_STRINGS].size; | ||
263 | if (cxt->rgn[FT_STRINGS].start + ssize | ||
264 | < (char *)cxt->bph + cxt->max_size) { | ||
265 | /* move strings up as far as possible */ | ||
266 | str = (char *)cxt->bph + cxt->max_size - ssize; | ||
267 | cxt->str_anchor += str - cxt->rgn[FT_STRINGS].start; | ||
268 | memmove(str, cxt->rgn[FT_STRINGS].start, ssize); | ||
269 | cxt->rgn[FT_STRINGS].start = str; | ||
270 | /* enough space now? */ | ||
271 | if (rgn >= FT_STRUCT && ft_shuffle(cxt, pp, rgn, nextra)) | ||
272 | return 1; | ||
273 | } | ||
274 | |||
275 | /* how much total free space is there following this region? */ | ||
276 | tot = 0; | ||
277 | for (r = rgn; r < FT_STRINGS; ++r) { | ||
278 | char *r_end = cxt->rgn[r].start + cxt->rgn[r].size; | ||
279 | tot += next_start(cxt, rgn) - r_end; | ||
280 | } | ||
281 | |||
282 | /* cast is to shut gcc up; we know nextra >= 0 */ | ||
283 | if (tot < (unsigned int)nextra) { | ||
284 | /* have to reallocate */ | ||
285 | char *newp, *new_start; | ||
286 | int shift; | ||
287 | |||
288 | if (!cxt->realloc) | ||
289 | return 0; | ||
290 | size = _ALIGN(cxt->max_size + (nextra - tot) + EXPAND_INCR, 8); | ||
291 | newp = cxt->realloc(cxt->bph, size); | ||
292 | if (!newp) | ||
293 | return 0; | ||
294 | cxt->max_size = size; | ||
295 | shift = newp - (char *)cxt->bph; | ||
296 | |||
297 | if (shift) { /* realloc can return same addr */ | ||
298 | cxt->bph = (struct boot_param_header *)newp; | ||
299 | ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, | ||
300 | shift); | ||
301 | for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) { | ||
302 | new_start = cxt->rgn[r].start + shift; | ||
303 | cxt->rgn[r].start = new_start; | ||
304 | } | ||
305 | *pp += shift; | ||
306 | cxt->str_anchor += shift; | ||
307 | } | ||
308 | |||
309 | /* move strings up to the end */ | ||
310 | str = newp + size - ssize; | ||
311 | cxt->str_anchor += str - cxt->rgn[FT_STRINGS].start; | ||
312 | memmove(str, cxt->rgn[FT_STRINGS].start, ssize); | ||
313 | cxt->rgn[FT_STRINGS].start = str; | ||
314 | |||
315 | if (ft_shuffle(cxt, pp, rgn, nextra)) | ||
316 | return 1; | ||
317 | } | ||
318 | |||
319 | /* must be FT_RSVMAP and we need to move FT_STRUCT up */ | ||
320 | if (rgn == FT_RSVMAP) { | ||
321 | next = cxt->rgn[FT_RSVMAP].start + cxt->rgn[FT_RSVMAP].size | ||
322 | + nextra; | ||
323 | ssize = cxt->rgn[FT_STRUCT].size; | ||
324 | if (next + ssize >= cxt->rgn[FT_STRINGS].start) | ||
325 | return 0; /* "can't happen" */ | ||
326 | memmove(next, cxt->rgn[FT_STRUCT].start, ssize); | ||
327 | ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, nextra); | ||
328 | cxt->rgn[FT_STRUCT].start = next; | ||
329 | |||
330 | if (ft_shuffle(cxt, pp, rgn, nextra)) | ||
331 | return 1; | ||
332 | } | ||
333 | |||
334 | return 0; /* "can't happen" */ | ||
335 | } | ||
336 | |||
337 | static void ft_put_word(struct ft_cxt *cxt, u32 v) | ||
338 | { | ||
339 | *(u32 *) cxt->p = cpu_to_be32(v); | ||
340 | cxt->p += 4; | ||
341 | } | ||
342 | |||
343 | static void ft_put_bin(struct ft_cxt *cxt, const void *data, unsigned int sz) | ||
344 | { | ||
345 | unsigned long sza = _ALIGN(sz, 4); | ||
346 | |||
347 | /* zero out the alignment gap if necessary */ | ||
348 | if (sz < sza) | ||
349 | *(u32 *) (cxt->p + sza - 4) = 0; | ||
350 | |||
351 | /* copy in the data */ | ||
352 | memcpy(cxt->p, data, sz); | ||
353 | |||
354 | cxt->p += sza; | ||
355 | } | ||
356 | |||
357 | int ft_begin_node(struct ft_cxt *cxt, const char *name) | ||
358 | { | ||
359 | unsigned long nlen = strlen(name) + 1; | ||
360 | unsigned long len = 8 + _ALIGN(nlen, 4); | ||
361 | |||
362 | if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, len)) | ||
363 | return -1; | ||
364 | ft_put_word(cxt, OF_DT_BEGIN_NODE); | ||
365 | ft_put_bin(cxt, name, strlen(name) + 1); | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | void ft_end_node(struct ft_cxt *cxt) | ||
370 | { | ||
371 | ft_put_word(cxt, OF_DT_END_NODE); | ||
372 | } | ||
373 | |||
374 | void ft_nop(struct ft_cxt *cxt) | ||
375 | { | ||
376 | if (ft_make_space(cxt, &cxt->p, FT_STRUCT, 4)) | ||
377 | ft_put_word(cxt, OF_DT_NOP); | ||
378 | } | ||
379 | |||
380 | #define NO_STRING 0x7fffffff | ||
381 | |||
382 | static int lookup_string(struct ft_cxt *cxt, const char *name) | ||
383 | { | ||
384 | char *p, *end; | ||
385 | |||
386 | p = cxt->rgn[FT_STRINGS].start; | ||
387 | end = p + cxt->rgn[FT_STRINGS].size; | ||
388 | while (p < end) { | ||
389 | if (strcmp(p, (char *)name) == 0) | ||
390 | return p - cxt->str_anchor; | ||
391 | p += strlen(p) + 1; | ||
392 | } | ||
393 | |||
394 | return NO_STRING; | ||
395 | } | ||
396 | |||
397 | /* lookup string and insert if not found */ | ||
398 | static int map_string(struct ft_cxt *cxt, const char *name) | ||
399 | { | ||
400 | int off; | ||
401 | char *p; | ||
402 | |||
403 | off = lookup_string(cxt, name); | ||
404 | if (off != NO_STRING) | ||
405 | return off; | ||
406 | p = cxt->rgn[FT_STRINGS].start; | ||
407 | if (!ft_make_space(cxt, &p, FT_STRINGS, strlen(name) + 1)) | ||
408 | return NO_STRING; | ||
409 | strcpy(p, name); | ||
410 | return p - cxt->str_anchor; | ||
411 | } | ||
412 | |||
413 | int ft_prop(struct ft_cxt *cxt, const char *name, const void *data, | ||
414 | unsigned int sz) | ||
415 | { | ||
416 | int off, len; | ||
417 | |||
418 | off = lookup_string(cxt, name); | ||
419 | if (off == NO_STRING) | ||
420 | return -1; | ||
421 | |||
422 | len = 12 + _ALIGN(sz, 4); | ||
423 | if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, len)) | ||
424 | return -1; | ||
425 | |||
426 | ft_put_word(cxt, OF_DT_PROP); | ||
427 | ft_put_word(cxt, sz); | ||
428 | ft_put_word(cxt, off); | ||
429 | ft_put_bin(cxt, data, sz); | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | int ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str) | ||
434 | { | ||
435 | return ft_prop(cxt, name, str, strlen(str) + 1); | ||
436 | } | ||
437 | |||
438 | int ft_prop_int(struct ft_cxt *cxt, const char *name, unsigned int val) | ||
439 | { | ||
440 | u32 v = cpu_to_be32((u32) val); | ||
441 | |||
442 | return ft_prop(cxt, name, &v, 4); | ||
443 | } | ||
444 | |||
445 | /* Calculate the size of the reserved map */ | ||
446 | static unsigned long rsvmap_size(struct ft_cxt *cxt) | ||
447 | { | ||
448 | struct ft_reserve *res; | ||
449 | |||
450 | res = (struct ft_reserve *)cxt->rgn[FT_RSVMAP].start; | ||
451 | while (res->start || res->len) | ||
452 | ++res; | ||
453 | return (char *)(res + 1) - cxt->rgn[FT_RSVMAP].start; | ||
454 | } | ||
455 | |||
456 | /* Calculate the size of the struct region by stepping through it */ | ||
457 | static unsigned long struct_size(struct ft_cxt *cxt) | ||
458 | { | ||
459 | char *p = cxt->rgn[FT_STRUCT].start; | ||
460 | char *next; | ||
461 | struct ft_atom atom; | ||
462 | |||
463 | /* make check in ft_next happy */ | ||
464 | if (cxt->rgn[FT_STRUCT].size == 0) | ||
465 | cxt->rgn[FT_STRUCT].size = 0xfffffffful - (unsigned long)p; | ||
466 | |||
467 | while ((next = ft_next(cxt, p, &atom)) != NULL) | ||
468 | p = next; | ||
469 | return p + 4 - cxt->rgn[FT_STRUCT].start; | ||
470 | } | ||
471 | |||
472 | /* add `adj' on to all string offset values in the struct area */ | ||
473 | static void adjust_string_offsets(struct ft_cxt *cxt, int adj) | ||
474 | { | ||
475 | char *p = cxt->rgn[FT_STRUCT].start; | ||
476 | char *next; | ||
477 | struct ft_atom atom; | ||
478 | int off; | ||
479 | |||
480 | while ((next = ft_next(cxt, p, &atom)) != NULL) { | ||
481 | if (atom.tag == OF_DT_PROP) { | ||
482 | off = be32_to_cpu(*(u32 *) (p + 8)); | ||
483 | *(u32 *) (p + 8) = cpu_to_be32(off + adj); | ||
484 | } | ||
485 | p = next; | ||
486 | } | ||
487 | } | ||
488 | |||
489 | /* start construction of the flat OF tree from scratch */ | ||
490 | void ft_begin(struct ft_cxt *cxt, void *blob, unsigned int max_size, | ||
491 | void *(*realloc_fn) (void *, unsigned long)) | ||
492 | { | ||
493 | struct boot_param_header *bph = blob; | ||
494 | char *p; | ||
495 | struct ft_reserve *pres; | ||
496 | |||
497 | /* clear the cxt */ | ||
498 | memset(cxt, 0, sizeof(*cxt)); | ||
499 | |||
500 | cxt->bph = bph; | ||
501 | cxt->max_size = max_size; | ||
502 | cxt->realloc = realloc_fn; | ||
503 | cxt->isordered = 1; | ||
504 | |||
505 | /* zero everything in the header area */ | ||
506 | memset(bph, 0, sizeof(*bph)); | ||
507 | |||
508 | bph->magic = cpu_to_be32(OF_DT_HEADER); | ||
509 | bph->version = cpu_to_be32(0x10); | ||
510 | bph->last_comp_version = cpu_to_be32(0x10); | ||
511 | |||
512 | /* start pointers */ | ||
513 | cxt->rgn[FT_RSVMAP].start = p = blob + HDR_SIZE; | ||
514 | cxt->rgn[FT_RSVMAP].size = sizeof(struct ft_reserve); | ||
515 | pres = (struct ft_reserve *)p; | ||
516 | cxt->rgn[FT_STRUCT].start = p += sizeof(struct ft_reserve); | ||
517 | cxt->rgn[FT_STRUCT].size = 4; | ||
518 | cxt->rgn[FT_STRINGS].start = blob + max_size; | ||
519 | cxt->rgn[FT_STRINGS].size = 0; | ||
520 | |||
521 | /* init rsvmap and struct */ | ||
522 | pres->start = 0; | ||
523 | pres->len = 0; | ||
524 | *(u32 *) p = cpu_to_be32(OF_DT_END); | ||
525 | |||
526 | cxt->str_anchor = blob; | ||
527 | } | ||
528 | |||
529 | /* open up an existing blob to be examined or modified */ | ||
530 | int ft_open(struct ft_cxt *cxt, void *blob, unsigned int max_size, | ||
531 | unsigned int max_find_device, | ||
532 | void *(*realloc_fn) (void *, unsigned long)) | ||
533 | { | ||
534 | struct boot_param_header *bph = blob; | ||
535 | |||
536 | /* can't cope with version < 16 */ | ||
537 | if (be32_to_cpu(bph->version) < 16) | ||
538 | return -1; | ||
539 | |||
540 | /* clear the cxt */ | ||
541 | memset(cxt, 0, sizeof(*cxt)); | ||
542 | |||
543 | /* alloc node_tbl to track node ptrs returned by ft_find_device */ | ||
544 | ++max_find_device; | ||
545 | cxt->node_tbl = realloc_fn(NULL, max_find_device * sizeof(char *)); | ||
546 | if (!cxt->node_tbl) | ||
547 | return -1; | ||
548 | memset(cxt->node_tbl, 0, max_find_device * sizeof(char *)); | ||
549 | cxt->node_max = max_find_device; | ||
550 | cxt->nodes_used = 1; /* don't use idx 0 b/c looks like NULL */ | ||
551 | |||
552 | cxt->bph = bph; | ||
553 | cxt->max_size = max_size; | ||
554 | cxt->realloc = realloc_fn; | ||
555 | |||
556 | cxt->rgn[FT_RSVMAP].start = blob + be32_to_cpu(bph->off_mem_rsvmap); | ||
557 | cxt->rgn[FT_RSVMAP].size = rsvmap_size(cxt); | ||
558 | cxt->rgn[FT_STRUCT].start = blob + be32_to_cpu(bph->off_dt_struct); | ||
559 | cxt->rgn[FT_STRUCT].size = struct_size(cxt); | ||
560 | cxt->rgn[FT_STRINGS].start = blob + be32_to_cpu(bph->off_dt_strings); | ||
561 | cxt->rgn[FT_STRINGS].size = be32_to_cpu(bph->dt_strings_size); | ||
562 | /* Leave as '0' to force first ft_make_space call to do a ft_reorder | ||
563 | * and move dt to an area allocated by realloc. | ||
564 | cxt->isordered = ft_ordered(cxt); | ||
565 | */ | ||
566 | |||
567 | cxt->p = cxt->rgn[FT_STRUCT].start; | ||
568 | cxt->str_anchor = cxt->rgn[FT_STRINGS].start; | ||
569 | |||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | /* add a reserver physical area to the rsvmap */ | ||
574 | int ft_add_rsvmap(struct ft_cxt *cxt, u64 physaddr, u64 size) | ||
575 | { | ||
576 | char *p; | ||
577 | struct ft_reserve *pres; | ||
578 | |||
579 | p = cxt->rgn[FT_RSVMAP].start + cxt->rgn[FT_RSVMAP].size | ||
580 | - sizeof(struct ft_reserve); | ||
581 | if (!ft_make_space(cxt, &p, FT_RSVMAP, sizeof(struct ft_reserve))) | ||
582 | return -1; | ||
583 | |||
584 | pres = (struct ft_reserve *)p; | ||
585 | pres->start = cpu_to_be64(physaddr); | ||
586 | pres->len = cpu_to_be64(size); | ||
587 | |||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | void ft_begin_tree(struct ft_cxt *cxt) | ||
592 | { | ||
593 | cxt->p = cxt->rgn[FT_STRUCT].start; | ||
594 | } | ||
595 | |||
596 | void ft_end_tree(struct ft_cxt *cxt) | ||
597 | { | ||
598 | struct boot_param_header *bph = cxt->bph; | ||
599 | char *p, *oldstr, *str, *endp; | ||
600 | unsigned long ssize; | ||
601 | int adj; | ||
602 | |||
603 | if (!cxt->isordered) | ||
604 | return; /* we haven't touched anything */ | ||
605 | |||
606 | /* adjust string offsets */ | ||
607 | oldstr = cxt->rgn[FT_STRINGS].start; | ||
608 | adj = cxt->str_anchor - oldstr; | ||
609 | if (adj) | ||
610 | adjust_string_offsets(cxt, adj); | ||
611 | |||
612 | /* make strings end on 8-byte boundary */ | ||
613 | ssize = cxt->rgn[FT_STRINGS].size; | ||
614 | endp = (char *)_ALIGN((unsigned long)cxt->rgn[FT_STRUCT].start | ||
615 | + cxt->rgn[FT_STRUCT].size + ssize, 8); | ||
616 | str = endp - ssize; | ||
617 | |||
618 | /* move strings down to end of structs */ | ||
619 | memmove(str, oldstr, ssize); | ||
620 | cxt->str_anchor = str; | ||
621 | cxt->rgn[FT_STRINGS].start = str; | ||
622 | |||
623 | /* fill in header fields */ | ||
624 | p = (char *)bph; | ||
625 | bph->totalsize = cpu_to_be32(endp - p); | ||
626 | bph->off_mem_rsvmap = cpu_to_be32(cxt->rgn[FT_RSVMAP].start - p); | ||
627 | bph->off_dt_struct = cpu_to_be32(cxt->rgn[FT_STRUCT].start - p); | ||
628 | bph->off_dt_strings = cpu_to_be32(cxt->rgn[FT_STRINGS].start - p); | ||
629 | bph->dt_strings_size = cpu_to_be32(ssize); | ||
630 | } | ||
631 | |||
632 | void *ft_find_device(struct ft_cxt *cxt, const char *srch_path) | ||
633 | { | ||
634 | char *node; | ||
635 | |||
636 | /* require absolute path */ | ||
637 | if (srch_path[0] != '/') | ||
638 | return NULL; | ||
639 | node = ft_find_descendent(cxt, cxt->rgn[FT_STRUCT].start, srch_path); | ||
640 | return ft_node_add(cxt, node); | ||
641 | } | ||
642 | |||
643 | void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path) | ||
644 | { | ||
645 | struct ft_atom atom; | ||
646 | char *p; | ||
647 | const char *cp, *q; | ||
648 | int cl; | ||
649 | int depth = -1; | ||
650 | int dmatch = 0; | ||
651 | const char *path_comp[FT_MAX_DEPTH]; | ||
652 | |||
653 | cp = srch_path; | ||
654 | cl = 0; | ||
655 | p = top; | ||
656 | |||
657 | while ((p = ft_next(cxt, p, &atom)) != NULL) { | ||
658 | switch (atom.tag) { | ||
659 | case OF_DT_BEGIN_NODE: | ||
660 | ++depth; | ||
661 | if (depth != dmatch) | ||
662 | break; | ||
663 | cxt->genealogy[depth] = atom.data; | ||
664 | cxt->genealogy[depth + 1] = NULL; | ||
665 | if (depth && !(strncmp(atom.name, cp, cl) == 0 | ||
666 | && (atom.name[cl] == '/' | ||
667 | || atom.name[cl] == '\0' | ||
668 | || atom.name[cl] == '@'))) | ||
669 | break; | ||
670 | path_comp[dmatch] = cp; | ||
671 | /* it matches so far, advance to next path component */ | ||
672 | cp += cl; | ||
673 | /* skip slashes */ | ||
674 | while (*cp == '/') | ||
675 | ++cp; | ||
676 | /* we're done if this is the end of the string */ | ||
677 | if (*cp == 0) | ||
678 | return atom.data; | ||
679 | /* look for end of this component */ | ||
680 | q = strchr(cp, '/'); | ||
681 | if (q) | ||
682 | cl = q - cp; | ||
683 | else | ||
684 | cl = strlen(cp); | ||
685 | ++dmatch; | ||
686 | break; | ||
687 | case OF_DT_END_NODE: | ||
688 | if (depth == 0) | ||
689 | return NULL; | ||
690 | if (dmatch > depth) { | ||
691 | --dmatch; | ||
692 | cl = cp - path_comp[dmatch] - 1; | ||
693 | cp = path_comp[dmatch]; | ||
694 | while (cl > 0 && cp[cl - 1] == '/') | ||
695 | --cl; | ||
696 | } | ||
697 | --depth; | ||
698 | break; | ||
699 | } | ||
700 | } | ||
701 | return NULL; | ||
702 | } | ||
703 | |||
704 | void *ft_get_parent(struct ft_cxt *cxt, const void *phandle) | ||
705 | { | ||
706 | void *node; | ||
707 | int d; | ||
708 | struct ft_atom atom; | ||
709 | char *p; | ||
710 | |||
711 | node = ft_node_ph2node(cxt, phandle); | ||
712 | if (node == NULL) | ||
713 | return NULL; | ||
714 | |||
715 | for (d = 0; cxt->genealogy[d] != NULL; ++d) | ||
716 | if (cxt->genealogy[d] == node) | ||
717 | return cxt->genealogy[d > 0 ? d - 1 : 0]; | ||
718 | |||
719 | /* have to do it the hard way... */ | ||
720 | p = cxt->rgn[FT_STRUCT].start; | ||
721 | d = 0; | ||
722 | while ((p = ft_next(cxt, p, &atom)) != NULL) { | ||
723 | switch (atom.tag) { | ||
724 | case OF_DT_BEGIN_NODE: | ||
725 | cxt->genealogy[d] = atom.data; | ||
726 | if (node == atom.data) { | ||
727 | /* found it */ | ||
728 | cxt->genealogy[d + 1] = NULL; | ||
729 | return d > 0 ? cxt->genealogy[d - 1] : node; | ||
730 | } | ||
731 | ++d; | ||
732 | break; | ||
733 | case OF_DT_END_NODE: | ||
734 | --d; | ||
735 | break; | ||
736 | } | ||
737 | } | ||
738 | return NULL; | ||
739 | } | ||
740 | |||
741 | int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, | ||
742 | void *buf, const unsigned int buflen) | ||
743 | { | ||
744 | struct ft_atom atom; | ||
745 | void *node; | ||
746 | char *p; | ||
747 | int depth; | ||
748 | unsigned int size; | ||
749 | |||
750 | node = ft_node_ph2node(cxt, phandle); | ||
751 | if (node == NULL) | ||
752 | return -1; | ||
753 | |||
754 | depth = 0; | ||
755 | p = (char *)node; | ||
756 | |||
757 | while ((p = ft_next(cxt, p, &atom)) != NULL) { | ||
758 | switch (atom.tag) { | ||
759 | case OF_DT_BEGIN_NODE: | ||
760 | ++depth; | ||
761 | break; | ||
762 | case OF_DT_PROP: | ||
763 | if ((depth != 1) || strcmp(atom.name, propname)) | ||
764 | break; | ||
765 | size = min(atom.size, buflen); | ||
766 | memcpy(buf, atom.data, size); | ||
767 | return atom.size; | ||
768 | case OF_DT_END_NODE: | ||
769 | if (--depth <= 0) | ||
770 | return -1; | ||
771 | } | ||
772 | } | ||
773 | return -1; | ||
774 | } | ||
775 | |||
776 | int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, | ||
777 | const void *buf, const unsigned int buflen) | ||
778 | { | ||
779 | struct ft_atom atom; | ||
780 | void *node; | ||
781 | char *p, *next; | ||
782 | int nextra, depth; | ||
783 | |||
784 | node = ft_node_ph2node(cxt, phandle); | ||
785 | if (node == NULL) | ||
786 | return -1; | ||
787 | |||
788 | depth = 0; | ||
789 | p = node; | ||
790 | |||
791 | while ((next = ft_next(cxt, p, &atom)) != NULL) { | ||
792 | switch (atom.tag) { | ||
793 | case OF_DT_BEGIN_NODE: | ||
794 | ++depth; | ||
795 | break; | ||
796 | case OF_DT_END_NODE: | ||
797 | if (--depth > 0) | ||
798 | break; | ||
799 | /* haven't found the property, insert here */ | ||
800 | cxt->p = p; | ||
801 | return ft_prop(cxt, propname, buf, buflen); | ||
802 | case OF_DT_PROP: | ||
803 | if ((depth != 1) || strcmp(atom.name, propname)) | ||
804 | break; | ||
805 | /* found an existing property, overwrite it */ | ||
806 | nextra = _ALIGN(buflen, 4) - _ALIGN(atom.size, 4); | ||
807 | cxt->p = atom.data; | ||
808 | if (nextra && !ft_make_space(cxt, &cxt->p, FT_STRUCT, | ||
809 | nextra)) | ||
810 | return -1; | ||
811 | *(u32 *) (cxt->p - 8) = cpu_to_be32(buflen); | ||
812 | ft_put_bin(cxt, buf, buflen); | ||
813 | return 0; | ||
814 | } | ||
815 | p = next; | ||
816 | } | ||
817 | return -1; | ||
818 | } | ||
819 | |||
820 | int ft_del_prop(struct ft_cxt *cxt, const void *phandle, const char *propname) | ||
821 | { | ||
822 | struct ft_atom atom; | ||
823 | void *node; | ||
824 | char *p, *next; | ||
825 | int size; | ||
826 | |||
827 | node = ft_node_ph2node(cxt, phandle); | ||
828 | if (node == NULL) | ||
829 | return -1; | ||
830 | |||
831 | p = node; | ||
832 | while ((next = ft_next(cxt, p, &atom)) != NULL) { | ||
833 | switch (atom.tag) { | ||
834 | case OF_DT_BEGIN_NODE: | ||
835 | case OF_DT_END_NODE: | ||
836 | return -1; | ||
837 | case OF_DT_PROP: | ||
838 | if (strcmp(atom.name, propname)) | ||
839 | break; | ||
840 | /* found the property, remove it */ | ||
841 | size = 12 + -_ALIGN(atom.size, 4); | ||
842 | cxt->p = p; | ||
843 | if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, -size)) | ||
844 | return -1; | ||
845 | return 0; | ||
846 | } | ||
847 | p = next; | ||
848 | } | ||
849 | return -1; | ||
850 | } | ||
851 | |||
852 | void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *path) | ||
853 | { | ||
854 | struct ft_atom atom; | ||
855 | char *p, *next; | ||
856 | int depth = 0; | ||
857 | |||
858 | p = cxt->rgn[FT_STRUCT].start; | ||
859 | while ((next = ft_next(cxt, p, &atom)) != NULL) { | ||
860 | switch (atom.tag) { | ||
861 | case OF_DT_BEGIN_NODE: | ||
862 | ++depth; | ||
863 | if (depth == 1 && strcmp(atom.name, path) == 0) | ||
864 | /* duplicate node path, return error */ | ||
865 | return NULL; | ||
866 | break; | ||
867 | case OF_DT_END_NODE: | ||
868 | --depth; | ||
869 | if (depth > 0) | ||
870 | break; | ||
871 | /* end of node, insert here */ | ||
872 | cxt->p = p; | ||
873 | ft_begin_node(cxt, path); | ||
874 | ft_end_node(cxt); | ||
875 | return p; | ||
876 | } | ||
877 | p = next; | ||
878 | } | ||
879 | return NULL; | ||
880 | } | ||
diff --git a/arch/powerpc/boot/flatdevtree.h b/arch/powerpc/boot/flatdevtree.h index 761c8dc84008..b9cd9f61f351 100644 --- a/arch/powerpc/boot/flatdevtree.h +++ b/arch/powerpc/boot/flatdevtree.h | |||
@@ -17,7 +17,7 @@ | |||
17 | #ifndef FLATDEVTREE_H | 17 | #ifndef FLATDEVTREE_H |
18 | #define FLATDEVTREE_H | 18 | #define FLATDEVTREE_H |
19 | 19 | ||
20 | #include "types.h" | 20 | #include "flatdevtree_env.h" |
21 | 21 | ||
22 | /* Definitions used by the flattened device tree */ | 22 | /* Definitions used by the flattened device tree */ |
23 | #define OF_DT_HEADER 0xd00dfeed /* marker */ | 23 | #define OF_DT_HEADER 0xd00dfeed /* marker */ |
@@ -43,4 +43,64 @@ struct boot_param_header { | |||
43 | u32 dt_strings_size; /* size of the DT strings block */ | 43 | u32 dt_strings_size; /* size of the DT strings block */ |
44 | }; | 44 | }; |
45 | 45 | ||
46 | struct ft_reserve { | ||
47 | u64 start; | ||
48 | u64 len; | ||
49 | }; | ||
50 | |||
51 | struct ft_region { | ||
52 | char *start; | ||
53 | unsigned long size; | ||
54 | }; | ||
55 | |||
56 | enum ft_rgn_id { | ||
57 | FT_RSVMAP, | ||
58 | FT_STRUCT, | ||
59 | FT_STRINGS, | ||
60 | FT_N_REGION | ||
61 | }; | ||
62 | |||
63 | #define FT_MAX_DEPTH 50 | ||
64 | |||
65 | struct ft_cxt { | ||
66 | struct boot_param_header *bph; | ||
67 | int max_size; /* maximum size of tree */ | ||
68 | int isordered; /* everything in standard order */ | ||
69 | void *(*realloc)(void *, unsigned long); | ||
70 | char *str_anchor; | ||
71 | char *p; /* current insertion point in structs */ | ||
72 | struct ft_region rgn[FT_N_REGION]; | ||
73 | void *genealogy[FT_MAX_DEPTH+1]; | ||
74 | char **node_tbl; | ||
75 | unsigned int node_max; | ||
76 | unsigned int nodes_used; | ||
77 | }; | ||
78 | |||
79 | int ft_begin_node(struct ft_cxt *cxt, const char *name); | ||
80 | void ft_end_node(struct ft_cxt *cxt); | ||
81 | |||
82 | void ft_begin_tree(struct ft_cxt *cxt); | ||
83 | void ft_end_tree(struct ft_cxt *cxt); | ||
84 | |||
85 | void ft_nop(struct ft_cxt *cxt); | ||
86 | int ft_prop(struct ft_cxt *cxt, const char *name, | ||
87 | const void *data, unsigned int sz); | ||
88 | int ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str); | ||
89 | int ft_prop_int(struct ft_cxt *cxt, const char *name, unsigned int val); | ||
90 | void ft_begin(struct ft_cxt *cxt, void *blob, unsigned int max_size, | ||
91 | void *(*realloc_fn)(void *, unsigned long)); | ||
92 | int ft_open(struct ft_cxt *cxt, void *blob, unsigned int max_size, | ||
93 | unsigned int max_find_device, | ||
94 | void *(*realloc_fn)(void *, unsigned long)); | ||
95 | int ft_add_rsvmap(struct ft_cxt *cxt, u64 physaddr, u64 size); | ||
96 | |||
97 | void ft_dump_blob(const void *bphp); | ||
98 | void ft_merge_blob(struct ft_cxt *cxt, void *blob); | ||
99 | void *ft_find_device(struct ft_cxt *cxt, const char *srch_path); | ||
100 | void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path); | ||
101 | int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, | ||
102 | void *buf, const unsigned int buflen); | ||
103 | int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, | ||
104 | const void *buf, const unsigned int buflen); | ||
105 | |||
46 | #endif /* FLATDEVTREE_H */ | 106 | #endif /* FLATDEVTREE_H */ |
diff --git a/arch/powerpc/boot/flatdevtree_env.h b/arch/powerpc/boot/flatdevtree_env.h new file mode 100644 index 000000000000..83bc1c718836 --- /dev/null +++ b/arch/powerpc/boot/flatdevtree_env.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * This file adds the header file glue so that the shared files | ||
3 | * flatdevicetree.[ch] can compile and work in the powerpc bootwrapper. | ||
4 | * | ||
5 | * strncmp & strchr copied from <file:lib/strings.c> | ||
6 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
7 | * | ||
8 | * Maintained by: Mark A. Greer <mgreer@mvista.com> | ||
9 | */ | ||
10 | #ifndef _PPC_BOOT_FLATDEVTREE_ENV_H_ | ||
11 | #define _PPC_BOOT_FLATDEVTREE_ENV_H_ | ||
12 | |||
13 | #include <stdarg.h> | ||
14 | #include <stddef.h> | ||
15 | #include "types.h" | ||
16 | #include "string.h" | ||
17 | #include "stdio.h" | ||
18 | #include "ops.h" | ||
19 | |||
20 | #define be16_to_cpu(x) (x) | ||
21 | #define cpu_to_be16(x) (x) | ||
22 | #define be32_to_cpu(x) (x) | ||
23 | #define cpu_to_be32(x) (x) | ||
24 | #define be64_to_cpu(x) (x) | ||
25 | #define cpu_to_be64(x) (x) | ||
26 | |||
27 | static inline int strncmp(const char *cs, const char *ct, size_t count) | ||
28 | { | ||
29 | signed char __res = 0; | ||
30 | |||
31 | while (count) { | ||
32 | if ((__res = *cs - *ct++) != 0 || !*cs++) | ||
33 | break; | ||
34 | count--; | ||
35 | } | ||
36 | return __res; | ||
37 | } | ||
38 | |||
39 | static inline char *strchr(const char *s, int c) | ||
40 | { | ||
41 | for (; *s != (char)c; ++s) | ||
42 | if (*s == '\0') | ||
43 | return NULL; | ||
44 | return (char *)s; | ||
45 | } | ||
46 | |||
47 | #endif /* _PPC_BOOT_FLATDEVTREE_ENV_H_ */ | ||
diff --git a/arch/powerpc/boot/flatdevtree_misc.c b/arch/powerpc/boot/flatdevtree_misc.c new file mode 100644 index 000000000000..04da38fa477f --- /dev/null +++ b/arch/powerpc/boot/flatdevtree_misc.c | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * This file does the necessary interface mapping between the bootwrapper | ||
3 | * device tree operations and the interface provided by shared source | ||
4 | * files flatdevicetree.[ch]. | ||
5 | * | ||
6 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
7 | * | ||
8 | * 2006 (c) MontaVista Software, Inc. This file is licensed under | ||
9 | * the terms of the GNU General Public License version 2. This program | ||
10 | * is licensed "as is" without any warranty of any kind, whether express | ||
11 | * or implied. | ||
12 | */ | ||
13 | #include <stddef.h> | ||
14 | #include "flatdevtree.h" | ||
15 | #include "ops.h" | ||
16 | |||
17 | static struct ft_cxt cxt; | ||
18 | |||
19 | static void *ft_finddevice(const char *name) | ||
20 | { | ||
21 | return ft_find_device(&cxt, name); | ||
22 | } | ||
23 | |||
24 | static int ft_getprop(const void *phandle, const char *propname, void *buf, | ||
25 | const int buflen) | ||
26 | { | ||
27 | return ft_get_prop(&cxt, phandle, propname, buf, buflen); | ||
28 | } | ||
29 | |||
30 | static int ft_setprop(const void *phandle, const char *propname, | ||
31 | const void *buf, const int buflen) | ||
32 | { | ||
33 | return ft_set_prop(&cxt, phandle, propname, buf, buflen); | ||
34 | } | ||
35 | |||
36 | static unsigned long ft_finalize(void) | ||
37 | { | ||
38 | ft_end_tree(&cxt); | ||
39 | return (unsigned long)cxt.bph; | ||
40 | } | ||
41 | |||
42 | int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device) | ||
43 | { | ||
44 | dt_ops.finddevice = ft_finddevice; | ||
45 | dt_ops.getprop = ft_getprop; | ||
46 | dt_ops.setprop = ft_setprop; | ||
47 | dt_ops.finalize = ft_finalize; | ||
48 | |||
49 | return ft_open(&cxt, dt_blob, max_size, max_find_device, | ||
50 | platform_ops.realloc); | ||
51 | } | ||
diff --git a/arch/powerpc/boot/io.h b/arch/powerpc/boot/io.h new file mode 100644 index 000000000000..32974ed49e02 --- /dev/null +++ b/arch/powerpc/boot/io.h | |||
@@ -0,0 +1,53 @@ | |||
1 | #ifndef _IO_H | ||
2 | #define __IO_H | ||
3 | /* | ||
4 | * Low-level I/O routines. | ||
5 | * | ||
6 | * Copied from <file:include/asm-powerpc/io.h> (which has no copyright) | ||
7 | */ | ||
8 | static inline int in_8(const volatile unsigned char *addr) | ||
9 | { | ||
10 | int ret; | ||
11 | |||
12 | __asm__ __volatile__("lbz%U1%X1 %0,%1; twi 0,%0,0; isync" | ||
13 | : "=r" (ret) : "m" (*addr)); | ||
14 | return ret; | ||
15 | } | ||
16 | |||
17 | static inline void out_8(volatile unsigned char *addr, int val) | ||
18 | { | ||
19 | __asm__ __volatile__("stb%U0%X0 %1,%0; sync" | ||
20 | : "=m" (*addr) : "r" (val)); | ||
21 | } | ||
22 | |||
23 | static inline unsigned in_le32(const volatile unsigned *addr) | ||
24 | { | ||
25 | unsigned ret; | ||
26 | |||
27 | __asm__ __volatile__("lwbrx %0,0,%1; twi 0,%0,0; isync" | ||
28 | : "=r" (ret) : "r" (addr), "m" (*addr)); | ||
29 | return ret; | ||
30 | } | ||
31 | |||
32 | static inline unsigned in_be32(const volatile unsigned *addr) | ||
33 | { | ||
34 | unsigned ret; | ||
35 | |||
36 | __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" | ||
37 | : "=r" (ret) : "m" (*addr)); | ||
38 | return ret; | ||
39 | } | ||
40 | |||
41 | static inline void out_le32(volatile unsigned *addr, int val) | ||
42 | { | ||
43 | __asm__ __volatile__("stwbrx %1,0,%2; sync" : "=m" (*addr) | ||
44 | : "r" (val), "r" (addr)); | ||
45 | } | ||
46 | |||
47 | static inline void out_be32(volatile unsigned *addr, int val) | ||
48 | { | ||
49 | __asm__ __volatile__("stw%U0%X0 %1,%0; sync" | ||
50 | : "=m" (*addr) : "r" (val)); | ||
51 | } | ||
52 | |||
53 | #endif /* _IO_H */ | ||
diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index d719bb9333d1..6f6b50d238b6 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c | |||
@@ -27,6 +27,8 @@ extern char _vmlinux_start[]; | |||
27 | extern char _vmlinux_end[]; | 27 | extern char _vmlinux_end[]; |
28 | extern char _initrd_start[]; | 28 | extern char _initrd_start[]; |
29 | extern char _initrd_end[]; | 29 | extern char _initrd_end[]; |
30 | extern char _dtb_start[]; | ||
31 | extern char _dtb_end[]; | ||
30 | 32 | ||
31 | struct addr_range { | 33 | struct addr_range { |
32 | unsigned long addr; | 34 | unsigned long addr; |
@@ -167,7 +169,7 @@ static int is_elf32(void *hdr) | |||
167 | return 1; | 169 | return 1; |
168 | } | 170 | } |
169 | 171 | ||
170 | static void prep_kernel(unsigned long *a1, unsigned long *a2) | 172 | static void prep_kernel(unsigned long a1, unsigned long a2) |
171 | { | 173 | { |
172 | int len; | 174 | int len; |
173 | 175 | ||
@@ -203,11 +205,14 @@ static void prep_kernel(unsigned long *a1, unsigned long *a2) | |||
203 | } | 205 | } |
204 | 206 | ||
205 | /* | 207 | /* |
206 | * Now we try to alloc memory for the initrd (and copy it there) | 208 | * Now find the initrd |
209 | * | ||
210 | * First see if we have an image attached to us. If so | ||
211 | * allocate memory for it and copy it there. | ||
207 | */ | 212 | */ |
208 | initrd.size = (unsigned long)(_initrd_end - _initrd_start); | 213 | initrd.size = (unsigned long)(_initrd_end - _initrd_start); |
209 | initrd.memsize = initrd.size; | 214 | initrd.memsize = initrd.size; |
210 | if ( initrd.size > 0 ) { | 215 | if (initrd.size > 0) { |
211 | printf("Allocating 0x%lx bytes for initrd ...\n\r", | 216 | printf("Allocating 0x%lx bytes for initrd ...\n\r", |
212 | initrd.size); | 217 | initrd.size); |
213 | initrd.addr = (unsigned long)malloc((u32)initrd.size); | 218 | initrd.addr = (unsigned long)malloc((u32)initrd.size); |
@@ -216,8 +221,6 @@ static void prep_kernel(unsigned long *a1, unsigned long *a2) | |||
216 | "ramdisk !\n\r"); | 221 | "ramdisk !\n\r"); |
217 | exit(); | 222 | exit(); |
218 | } | 223 | } |
219 | *a1 = initrd.addr; | ||
220 | *a2 = initrd.size; | ||
221 | printf("initial ramdisk moving 0x%lx <- 0x%lx " | 224 | printf("initial ramdisk moving 0x%lx <- 0x%lx " |
222 | "(0x%lx bytes)\n\r", initrd.addr, | 225 | "(0x%lx bytes)\n\r", initrd.addr, |
223 | (unsigned long)_initrd_start, initrd.size); | 226 | (unsigned long)_initrd_start, initrd.size); |
@@ -225,6 +228,12 @@ static void prep_kernel(unsigned long *a1, unsigned long *a2) | |||
225 | initrd.size); | 228 | initrd.size); |
226 | printf("initrd head: 0x%lx\n\r", | 229 | printf("initrd head: 0x%lx\n\r", |
227 | *((unsigned long *)initrd.addr)); | 230 | *((unsigned long *)initrd.addr)); |
231 | } else if (a2 != 0) { | ||
232 | /* Otherwise, see if yaboot or another loader gave us an initrd */ | ||
233 | initrd.addr = a1; | ||
234 | initrd.memsize = initrd.size = a2; | ||
235 | printf("Using loader supplied initrd at 0x%lx (0x%lx bytes)\n\r", | ||
236 | initrd.addr, initrd.size); | ||
228 | } | 237 | } |
229 | 238 | ||
230 | /* Eventually gunzip the kernel */ | 239 | /* Eventually gunzip the kernel */ |
@@ -250,10 +259,6 @@ static void prep_kernel(unsigned long *a1, unsigned long *a2) | |||
250 | flush_cache((void *)vmlinux.addr, vmlinux.size); | 259 | flush_cache((void *)vmlinux.addr, vmlinux.size); |
251 | } | 260 | } |
252 | 261 | ||
253 | void __attribute__ ((weak)) ft_init(void *dt_blob) | ||
254 | { | ||
255 | } | ||
256 | |||
257 | /* A buffer that may be edited by tools operating on a zImage binary so as to | 262 | /* A buffer that may be edited by tools operating on a zImage binary so as to |
258 | * edit the command line passed to vmlinux (by setting /chosen/bootargs). | 263 | * edit the command line passed to vmlinux (by setting /chosen/bootargs). |
259 | * The buffer is put in it's own section so that tools may locate it easier. | 264 | * The buffer is put in it's own section so that tools may locate it easier. |
@@ -285,36 +290,22 @@ static void set_cmdline(char *buf) | |||
285 | setprop(devp, "bootargs", buf, strlen(buf) + 1); | 290 | setprop(devp, "bootargs", buf, strlen(buf) + 1); |
286 | } | 291 | } |
287 | 292 | ||
288 | /* Section where ft can be tacked on after zImage is built */ | ||
289 | union blobspace { | ||
290 | struct boot_param_header hdr; | ||
291 | char space[8*1024]; | ||
292 | } dt_blob __attribute__((__section__("__builtin_ft"))); | ||
293 | |||
294 | struct platform_ops platform_ops; | 293 | struct platform_ops platform_ops; |
295 | struct dt_ops dt_ops; | 294 | struct dt_ops dt_ops; |
296 | struct console_ops console_ops; | 295 | struct console_ops console_ops; |
297 | 296 | ||
298 | void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) | 297 | void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) |
299 | { | 298 | { |
300 | int have_dt = 0; | ||
301 | kernel_entry_t kentry; | 299 | kernel_entry_t kentry; |
302 | char cmdline[COMMAND_LINE_SIZE]; | 300 | char cmdline[COMMAND_LINE_SIZE]; |
301 | unsigned long ft_addr = 0; | ||
303 | 302 | ||
304 | memset(__bss_start, 0, _end - __bss_start); | 303 | memset(__bss_start, 0, _end - __bss_start); |
305 | memset(&platform_ops, 0, sizeof(platform_ops)); | 304 | memset(&platform_ops, 0, sizeof(platform_ops)); |
306 | memset(&dt_ops, 0, sizeof(dt_ops)); | 305 | memset(&dt_ops, 0, sizeof(dt_ops)); |
307 | memset(&console_ops, 0, sizeof(console_ops)); | 306 | memset(&console_ops, 0, sizeof(console_ops)); |
308 | 307 | ||
309 | /* Override the dt_ops and device tree if there was an flat dev | 308 | if (platform_init(promptr, _dtb_start, _dtb_end)) |
310 | * tree attached to the zImage. | ||
311 | */ | ||
312 | if (dt_blob.hdr.magic == OF_DT_HEADER) { | ||
313 | have_dt = 1; | ||
314 | ft_init(&dt_blob); | ||
315 | } | ||
316 | |||
317 | if (platform_init(promptr)) | ||
318 | exit(); | 309 | exit(); |
319 | if (console_ops.open && (console_ops.open() < 0)) | 310 | if (console_ops.open && (console_ops.open() < 0)) |
320 | exit(); | 311 | exit(); |
@@ -324,7 +315,7 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) | |||
324 | printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", | 315 | printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", |
325 | _start, sp); | 316 | _start, sp); |
326 | 317 | ||
327 | prep_kernel(&a1, &a2); | 318 | prep_kernel(a1, a2); |
328 | 319 | ||
329 | /* If cmdline came from zimage wrapper or if we can edit the one | 320 | /* If cmdline came from zimage wrapper or if we can edit the one |
330 | * in the dt, print it out and edit it, if possible. | 321 | * in the dt, print it out and edit it, if possible. |
@@ -338,15 +329,23 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) | |||
338 | set_cmdline(cmdline); | 329 | set_cmdline(cmdline); |
339 | } | 330 | } |
340 | 331 | ||
332 | printf("Finalizing device tree..."); | ||
333 | if (dt_ops.finalize) | ||
334 | ft_addr = dt_ops.finalize(); | ||
335 | if (ft_addr) | ||
336 | printf(" flat tree at 0x%lx\n\r", ft_addr); | ||
337 | else | ||
338 | printf(" using OF tree (promptr=%p)\n\r", promptr); | ||
339 | |||
341 | if (console_ops.close) | 340 | if (console_ops.close) |
342 | console_ops.close(); | 341 | console_ops.close(); |
343 | 342 | ||
344 | kentry = (kernel_entry_t) vmlinux.addr; | 343 | kentry = (kernel_entry_t) vmlinux.addr; |
345 | if (have_dt) | 344 | if (ft_addr) |
346 | kentry(dt_ops.ft_addr(), 0, NULL); | 345 | kentry(ft_addr, 0, NULL); |
347 | else | 346 | else |
348 | /* XXX initrd addr/size should be passed in properties */ | 347 | /* XXX initrd addr/size should be passed in properties */ |
349 | kentry(a1, a2, promptr); | 348 | kentry(initrd.addr, initrd.size, promptr); |
350 | 349 | ||
351 | /* console closed so printf below may not work */ | 350 | /* console closed so printf below may not work */ |
352 | printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); | 351 | printf("Error: Linux kernel returned to zImage boot wrapper!\n\r"); |
diff --git a/arch/powerpc/boot/mktree.c b/arch/powerpc/boot/mktree.c new file mode 100644 index 000000000000..4cb892993651 --- /dev/null +++ b/arch/powerpc/boot/mktree.c | |||
@@ -0,0 +1,152 @@ | |||
1 | /* | ||
2 | * Makes a tree bootable image for IBM Evaluation boards. | ||
3 | * Basically, just take a zImage, skip the ELF header, and stuff | ||
4 | * a 32 byte header on the front. | ||
5 | * | ||
6 | * We use htonl, which is a network macro, to make sure we're doing | ||
7 | * The Right Thing on an LE machine. It's non-obvious, but it should | ||
8 | * work on anything BSD'ish. | ||
9 | */ | ||
10 | |||
11 | #include <fcntl.h> | ||
12 | #include <stdio.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <string.h> | ||
15 | #include <sys/stat.h> | ||
16 | #include <unistd.h> | ||
17 | #include <netinet/in.h> | ||
18 | #ifdef __sun__ | ||
19 | #include <inttypes.h> | ||
20 | #else | ||
21 | #include <stdint.h> | ||
22 | #endif | ||
23 | |||
24 | /* This gets tacked on the front of the image. There are also a few | ||
25 | * bytes allocated after the _start label used by the boot rom (see | ||
26 | * head.S for details). | ||
27 | */ | ||
28 | typedef struct boot_block { | ||
29 | uint32_t bb_magic; /* 0x0052504F */ | ||
30 | uint32_t bb_dest; /* Target address of the image */ | ||
31 | uint32_t bb_num_512blocks; /* Size, rounded-up, in 512 byte blks */ | ||
32 | uint32_t bb_debug_flag; /* Run debugger or image after load */ | ||
33 | uint32_t bb_entry_point; /* The image address to start */ | ||
34 | uint32_t bb_checksum; /* 32 bit checksum including header */ | ||
35 | uint32_t reserved[2]; | ||
36 | } boot_block_t; | ||
37 | |||
38 | #define IMGBLK 512 | ||
39 | char tmpbuf[IMGBLK]; | ||
40 | |||
41 | int main(int argc, char *argv[]) | ||
42 | { | ||
43 | int in_fd, out_fd; | ||
44 | int nblks, i; | ||
45 | uint cksum, *cp; | ||
46 | struct stat st; | ||
47 | boot_block_t bt; | ||
48 | |||
49 | if (argc < 3) { | ||
50 | fprintf(stderr, "usage: %s <zImage-file> <boot-image> [entry-point]\n",argv[0]); | ||
51 | exit(1); | ||
52 | } | ||
53 | |||
54 | if (stat(argv[1], &st) < 0) { | ||
55 | perror("stat"); | ||
56 | exit(2); | ||
57 | } | ||
58 | |||
59 | nblks = (st.st_size + IMGBLK) / IMGBLK; | ||
60 | |||
61 | bt.bb_magic = htonl(0x0052504F); | ||
62 | |||
63 | /* If we have the optional entry point parameter, use it */ | ||
64 | if (argc == 4) | ||
65 | bt.bb_dest = bt.bb_entry_point = htonl(strtoul(argv[3], NULL, 0)); | ||
66 | else | ||
67 | bt.bb_dest = bt.bb_entry_point = htonl(0x500000); | ||
68 | |||
69 | /* We know these from the linker command. | ||
70 | * ...and then move it up into memory a little more so the | ||
71 | * relocation can happen. | ||
72 | */ | ||
73 | bt.bb_num_512blocks = htonl(nblks); | ||
74 | bt.bb_debug_flag = 0; | ||
75 | |||
76 | bt.bb_checksum = 0; | ||
77 | |||
78 | /* To be neat and tidy :-). | ||
79 | */ | ||
80 | bt.reserved[0] = 0; | ||
81 | bt.reserved[1] = 0; | ||
82 | |||
83 | if ((in_fd = open(argv[1], O_RDONLY)) < 0) { | ||
84 | perror("zImage open"); | ||
85 | exit(3); | ||
86 | } | ||
87 | |||
88 | if ((out_fd = open(argv[2], (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) { | ||
89 | perror("bootfile open"); | ||
90 | exit(3); | ||
91 | } | ||
92 | |||
93 | cksum = 0; | ||
94 | cp = (void *)&bt; | ||
95 | for (i=0; i<sizeof(bt)/sizeof(uint); i++) | ||
96 | cksum += *cp++; | ||
97 | |||
98 | /* Assume zImage is an ELF file, and skip the 64K header. | ||
99 | */ | ||
100 | if (read(in_fd, tmpbuf, IMGBLK) != IMGBLK) { | ||
101 | fprintf(stderr, "%s is too small to be an ELF image\n", | ||
102 | argv[1]); | ||
103 | exit(4); | ||
104 | } | ||
105 | |||
106 | if ((*(uint *)tmpbuf) != htonl(0x7f454c46)) { | ||
107 | fprintf(stderr, "%s is not an ELF image\n", argv[1]); | ||
108 | exit(4); | ||
109 | } | ||
110 | |||
111 | if (lseek(in_fd, (64 * 1024), SEEK_SET) < 0) { | ||
112 | fprintf(stderr, "%s failed to seek in ELF image\n", argv[1]); | ||
113 | exit(4); | ||
114 | } | ||
115 | |||
116 | nblks -= (64 * 1024) / IMGBLK; | ||
117 | |||
118 | /* And away we go...... | ||
119 | */ | ||
120 | if (write(out_fd, &bt, sizeof(bt)) != sizeof(bt)) { | ||
121 | perror("boot-image write"); | ||
122 | exit(5); | ||
123 | } | ||
124 | |||
125 | while (nblks-- > 0) { | ||
126 | if (read(in_fd, tmpbuf, IMGBLK) < 0) { | ||
127 | perror("zImage read"); | ||
128 | exit(5); | ||
129 | } | ||
130 | cp = (uint *)tmpbuf; | ||
131 | for (i=0; i<sizeof(tmpbuf)/sizeof(uint); i++) | ||
132 | cksum += *cp++; | ||
133 | if (write(out_fd, tmpbuf, sizeof(tmpbuf)) != sizeof(tmpbuf)) { | ||
134 | perror("boot-image write"); | ||
135 | exit(5); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /* rewrite the header with the computed checksum. | ||
140 | */ | ||
141 | bt.bb_checksum = htonl(cksum); | ||
142 | if (lseek(out_fd, 0, SEEK_SET) < 0) { | ||
143 | perror("rewrite seek"); | ||
144 | exit(1); | ||
145 | } | ||
146 | if (write(out_fd, &bt, sizeof(bt)) != sizeof(bt)) { | ||
147 | perror("boot-image rewrite"); | ||
148 | exit(1); | ||
149 | } | ||
150 | |||
151 | exit(0); | ||
152 | } | ||
diff --git a/arch/powerpc/boot/ns16550.c b/arch/powerpc/boot/ns16550.c new file mode 100644 index 000000000000..1ffe72e35cdc --- /dev/null +++ b/arch/powerpc/boot/ns16550.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * 16550 serial console support. | ||
3 | * | ||
4 | * Original copied from <file:arch/ppc/boot/common/ns16550.c> | ||
5 | * (which had no copyright) | ||
6 | * Modifications: 2006 (c) MontaVista Software, Inc. | ||
7 | * | ||
8 | * Modified by: Mark A. Greer <mgreer@mvista.com> | ||
9 | */ | ||
10 | #include <stdarg.h> | ||
11 | #include <stddef.h> | ||
12 | #include "types.h" | ||
13 | #include "string.h" | ||
14 | #include "stdio.h" | ||
15 | #include "io.h" | ||
16 | #include "ops.h" | ||
17 | |||
18 | #define UART_DLL 0 /* Out: Divisor Latch Low */ | ||
19 | #define UART_DLM 1 /* Out: Divisor Latch High */ | ||
20 | #define UART_FCR 2 /* Out: FIFO Control Register */ | ||
21 | #define UART_LCR 3 /* Out: Line Control Register */ | ||
22 | #define UART_MCR 4 /* Out: Modem Control Register */ | ||
23 | #define UART_LSR 5 /* In: Line Status Register */ | ||
24 | #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ | ||
25 | #define UART_LSR_DR 0x01 /* Receiver data ready */ | ||
26 | #define UART_MSR 6 /* In: Modem Status Register */ | ||
27 | #define UART_SCR 7 /* I/O: Scratch Register */ | ||
28 | |||
29 | static unsigned char *reg_base; | ||
30 | static u32 reg_shift; | ||
31 | |||
32 | static int ns16550_open(void) | ||
33 | { | ||
34 | out_8(reg_base + (UART_FCR << reg_shift), 0x06); | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | static void ns16550_putc(unsigned char c) | ||
39 | { | ||
40 | while ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_THRE) == 0); | ||
41 | out_8(reg_base, c); | ||
42 | } | ||
43 | |||
44 | static unsigned char ns16550_getc(void) | ||
45 | { | ||
46 | while ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_DR) == 0); | ||
47 | return in_8(reg_base); | ||
48 | } | ||
49 | |||
50 | static u8 ns16550_tstc(void) | ||
51 | { | ||
52 | return ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_DR) != 0); | ||
53 | } | ||
54 | |||
55 | int ns16550_console_init(void *devp, struct serial_console_data *scdp) | ||
56 | { | ||
57 | int n; | ||
58 | |||
59 | n = getprop(devp, "virtual-reg", ®_base, sizeof(reg_base)); | ||
60 | if (n != sizeof(reg_base)) | ||
61 | return -1; | ||
62 | |||
63 | n = getprop(devp, "reg-shift", ®_shift, sizeof(reg_shift)); | ||
64 | if (n != sizeof(reg_shift)) | ||
65 | reg_shift = 0; | ||
66 | |||
67 | scdp->open = ns16550_open; | ||
68 | scdp->putc = ns16550_putc; | ||
69 | scdp->getc = ns16550_getc; | ||
70 | scdp->tstc = ns16550_tstc; | ||
71 | scdp->close = NULL; | ||
72 | |||
73 | return 0; | ||
74 | } | ||
diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index 3a71845afc6c..0182f384f3e6 100644 --- a/arch/powerpc/boot/of.c +++ b/arch/powerpc/boot/of.c | |||
@@ -256,24 +256,18 @@ static void of_console_write(char *buf, int len) | |||
256 | call_prom("write", 3, 1, of_stdout_handle, buf, len); | 256 | call_prom("write", 3, 1, of_stdout_handle, buf, len); |
257 | } | 257 | } |
258 | 258 | ||
259 | int platform_init(void *promptr) | 259 | int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end) |
260 | { | 260 | { |
261 | platform_ops.fixups = NULL; | ||
262 | platform_ops.image_hdr = of_image_hdr; | 261 | platform_ops.image_hdr = of_image_hdr; |
263 | platform_ops.malloc = of_try_claim; | 262 | platform_ops.malloc = of_try_claim; |
264 | platform_ops.free = NULL; | ||
265 | platform_ops.exit = of_exit; | 263 | platform_ops.exit = of_exit; |
266 | 264 | ||
267 | dt_ops.finddevice = of_finddevice; | 265 | dt_ops.finddevice = of_finddevice; |
268 | dt_ops.getprop = of_getprop; | 266 | dt_ops.getprop = of_getprop; |
269 | dt_ops.setprop = of_setprop; | 267 | dt_ops.setprop = of_setprop; |
270 | dt_ops.translate_addr = NULL; | ||
271 | 268 | ||
272 | console_ops.open = of_console_open; | 269 | console_ops.open = of_console_open; |
273 | console_ops.write = of_console_write; | 270 | console_ops.write = of_console_write; |
274 | console_ops.edit_cmdline = NULL; | ||
275 | console_ops.close = NULL; | ||
276 | console_ops.data = NULL; | ||
277 | 271 | ||
278 | prom = (int (*)(void *))promptr; | 272 | prom = (int (*)(void *))promptr; |
279 | return 0; | 273 | return 0; |
diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 135eb4bb03b4..8abb6516bb7c 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h | |||
@@ -22,7 +22,8 @@ struct platform_ops { | |||
22 | void (*fixups)(void); | 22 | void (*fixups)(void); |
23 | void (*image_hdr)(const void *); | 23 | void (*image_hdr)(const void *); |
24 | void * (*malloc)(u32 size); | 24 | void * (*malloc)(u32 size); |
25 | void (*free)(void *ptr, u32 size); | 25 | void (*free)(void *ptr); |
26 | void * (*realloc)(void *ptr, unsigned long size); | ||
26 | void (*exit)(void); | 27 | void (*exit)(void); |
27 | }; | 28 | }; |
28 | extern struct platform_ops platform_ops; | 29 | extern struct platform_ops platform_ops; |
@@ -30,13 +31,11 @@ extern struct platform_ops platform_ops; | |||
30 | /* Device Tree operations */ | 31 | /* Device Tree operations */ |
31 | struct dt_ops { | 32 | struct dt_ops { |
32 | void * (*finddevice)(const char *name); | 33 | void * (*finddevice)(const char *name); |
33 | int (*getprop)(const void *node, const char *name, void *buf, | 34 | int (*getprop)(const void *phandle, const char *name, void *buf, |
34 | const int buflen); | 35 | const int buflen); |
35 | int (*setprop)(const void *node, const char *name, | 36 | int (*setprop)(const void *phandle, const char *name, |
36 | const void *buf, const int buflen); | 37 | const void *buf, const int buflen); |
37 | u64 (*translate_addr)(const char *path, const u32 *in_addr, | 38 | unsigned long (*finalize)(void); |
38 | const u32 addr_len); | ||
39 | unsigned long (*ft_addr)(void); | ||
40 | }; | 39 | }; |
41 | extern struct dt_ops dt_ops; | 40 | extern struct dt_ops dt_ops; |
42 | 41 | ||
@@ -59,10 +58,13 @@ struct serial_console_data { | |||
59 | void (*close)(void); | 58 | void (*close)(void); |
60 | }; | 59 | }; |
61 | 60 | ||
62 | extern int platform_init(void *promptr); | 61 | int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end); |
63 | extern void simple_alloc_init(void); | 62 | int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device); |
64 | extern void ft_init(void *dt_blob); | 63 | int serial_console_init(void); |
65 | extern int serial_console_init(void); | 64 | int ns16550_console_init(void *devp, struct serial_console_data *scdp); |
65 | void *simple_alloc_init(char *base, u32 heap_size, u32 granularity, | ||
66 | u32 max_allocs); | ||
67 | |||
66 | 68 | ||
67 | static inline void *finddevice(const char *name) | 69 | static inline void *finddevice(const char *name) |
68 | { | 70 | { |
@@ -84,10 +86,10 @@ static inline void *malloc(u32 size) | |||
84 | return (platform_ops.malloc) ? platform_ops.malloc(size) : NULL; | 86 | return (platform_ops.malloc) ? platform_ops.malloc(size) : NULL; |
85 | } | 87 | } |
86 | 88 | ||
87 | static inline void free(void *ptr, u32 size) | 89 | static inline void free(void *ptr) |
88 | { | 90 | { |
89 | if (platform_ops.free) | 91 | if (platform_ops.free) |
90 | platform_ops.free(ptr, size); | 92 | platform_ops.free(ptr); |
91 | } | 93 | } |
92 | 94 | ||
93 | static inline void exit(void) | 95 | static inline void exit(void) |
diff --git a/arch/powerpc/boot/serial.c b/arch/powerpc/boot/serial.c new file mode 100644 index 000000000000..e8de4cf59be7 --- /dev/null +++ b/arch/powerpc/boot/serial.c | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | * Generic serial console support | ||
3 | * | ||
4 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
5 | * | ||
6 | * Code in serial_edit_cmdline() copied from <file:arch/ppc/boot/simple/misc.c> | ||
7 | * and was written by Matt Porter <mporter@kernel.crashing.org>. | ||
8 | * | ||
9 | * 2001,2006 (c) MontaVista Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | #include <stdarg.h> | ||
15 | #include <stddef.h> | ||
16 | #include "types.h" | ||
17 | #include "string.h" | ||
18 | #include "stdio.h" | ||
19 | #include "io.h" | ||
20 | #include "ops.h" | ||
21 | |||
22 | extern void udelay(long delay); | ||
23 | |||
24 | static int serial_open(void) | ||
25 | { | ||
26 | struct serial_console_data *scdp = console_ops.data; | ||
27 | return scdp->open(); | ||
28 | } | ||
29 | |||
30 | static void serial_write(char *buf, int len) | ||
31 | { | ||
32 | struct serial_console_data *scdp = console_ops.data; | ||
33 | |||
34 | while (*buf != '\0') | ||
35 | scdp->putc(*buf++); | ||
36 | } | ||
37 | |||
38 | static void serial_edit_cmdline(char *buf, int len) | ||
39 | { | ||
40 | int timer = 0, count; | ||
41 | char ch, *cp; | ||
42 | struct serial_console_data *scdp = console_ops.data; | ||
43 | |||
44 | cp = buf; | ||
45 | count = strlen(buf); | ||
46 | cp = &buf[count]; | ||
47 | count++; | ||
48 | |||
49 | while (timer++ < 5*1000) { | ||
50 | if (scdp->tstc()) { | ||
51 | while (((ch = scdp->getc()) != '\n') && (ch != '\r')) { | ||
52 | /* Test for backspace/delete */ | ||
53 | if ((ch == '\b') || (ch == '\177')) { | ||
54 | if (cp != buf) { | ||
55 | cp--; | ||
56 | count--; | ||
57 | printf("\b \b"); | ||
58 | } | ||
59 | /* Test for ^x/^u (and wipe the line) */ | ||
60 | } else if ((ch == '\030') || (ch == '\025')) { | ||
61 | while (cp != buf) { | ||
62 | cp--; | ||
63 | count--; | ||
64 | printf("\b \b"); | ||
65 | } | ||
66 | } else if (count < len) { | ||
67 | *cp++ = ch; | ||
68 | count++; | ||
69 | scdp->putc(ch); | ||
70 | } | ||
71 | } | ||
72 | break; /* Exit 'timer' loop */ | ||
73 | } | ||
74 | udelay(1000); /* 1 msec */ | ||
75 | } | ||
76 | *cp = 0; | ||
77 | } | ||
78 | |||
79 | static void serial_close(void) | ||
80 | { | ||
81 | struct serial_console_data *scdp = console_ops.data; | ||
82 | |||
83 | if (scdp->close) | ||
84 | scdp->close(); | ||
85 | } | ||
86 | |||
87 | static void *serial_get_stdout_devp(void) | ||
88 | { | ||
89 | void *devp; | ||
90 | char devtype[MAX_PROP_LEN]; | ||
91 | char path[MAX_PATH_LEN]; | ||
92 | |||
93 | devp = finddevice("/chosen"); | ||
94 | if (devp == NULL) | ||
95 | goto err_out; | ||
96 | |||
97 | if (getprop(devp, "linux,stdout-path", path, MAX_PATH_LEN) > 0) { | ||
98 | devp = finddevice(path); | ||
99 | if (devp == NULL) | ||
100 | goto err_out; | ||
101 | |||
102 | if ((getprop(devp, "device_type", devtype, sizeof(devtype)) > 0) | ||
103 | && !strcmp(devtype, "serial")) | ||
104 | return devp; | ||
105 | } | ||
106 | err_out: | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | static struct serial_console_data serial_cd; | ||
111 | |||
112 | /* Node's "compatible" property determines which serial driver to use */ | ||
113 | int serial_console_init(void) | ||
114 | { | ||
115 | void *devp; | ||
116 | int rc = -1; | ||
117 | char compat[MAX_PROP_LEN]; | ||
118 | |||
119 | devp = serial_get_stdout_devp(); | ||
120 | if (devp == NULL) | ||
121 | goto err_out; | ||
122 | |||
123 | if (getprop(devp, "compatible", compat, sizeof(compat)) < 0) | ||
124 | goto err_out; | ||
125 | |||
126 | if (!strcmp(compat, "ns16550")) | ||
127 | rc = ns16550_console_init(devp, &serial_cd); | ||
128 | |||
129 | /* Add other serial console driver calls here */ | ||
130 | |||
131 | if (!rc) { | ||
132 | console_ops.open = serial_open; | ||
133 | console_ops.write = serial_write; | ||
134 | console_ops.edit_cmdline = serial_edit_cmdline; | ||
135 | console_ops.close = serial_close; | ||
136 | console_ops.data = &serial_cd; | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | err_out: | ||
141 | return -1; | ||
142 | } | ||
diff --git a/arch/powerpc/boot/simple_alloc.c b/arch/powerpc/boot/simple_alloc.c new file mode 100644 index 000000000000..cfe3a7505ba0 --- /dev/null +++ b/arch/powerpc/boot/simple_alloc.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Implement primitive realloc(3) functionality. | ||
3 | * | ||
4 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
5 | * | ||
6 | * 2006 (c) MontaVista, Software, Inc. This file is licensed under | ||
7 | * the terms of the GNU General Public License version 2. This program | ||
8 | * is licensed "as is" without any warranty of any kind, whether express | ||
9 | * or implied. | ||
10 | */ | ||
11 | |||
12 | #include <stddef.h> | ||
13 | #include "types.h" | ||
14 | #include "page.h" | ||
15 | #include "string.h" | ||
16 | #include "ops.h" | ||
17 | |||
18 | #define ENTRY_BEEN_USED 0x01 | ||
19 | #define ENTRY_IN_USE 0x02 | ||
20 | |||
21 | static struct alloc_info { | ||
22 | u32 flags; | ||
23 | u32 base; | ||
24 | u32 size; | ||
25 | } *alloc_tbl; | ||
26 | |||
27 | static u32 tbl_entries; | ||
28 | static u32 alloc_min; | ||
29 | static u32 next_base; | ||
30 | static u32 space_left; | ||
31 | |||
32 | /* | ||
33 | * First time an entry is used, its base and size are set. | ||
34 | * An entry can be freed and re-malloc'd but its base & size don't change. | ||
35 | * Should be smart enough for needs of bootwrapper. | ||
36 | */ | ||
37 | static void *simple_malloc(u32 size) | ||
38 | { | ||
39 | u32 i; | ||
40 | struct alloc_info *p = alloc_tbl; | ||
41 | |||
42 | if (size == 0) | ||
43 | goto err_out; | ||
44 | |||
45 | size = _ALIGN_UP(size, alloc_min); | ||
46 | |||
47 | for (i=0; i<tbl_entries; i++, p++) | ||
48 | if (!(p->flags & ENTRY_BEEN_USED)) { /* never been used */ | ||
49 | if (size <= space_left) { | ||
50 | p->base = next_base; | ||
51 | p->size = size; | ||
52 | p->flags = ENTRY_BEEN_USED | ENTRY_IN_USE; | ||
53 | next_base += size; | ||
54 | space_left -= size; | ||
55 | return (void *)p->base; | ||
56 | } | ||
57 | goto err_out; /* not enough space left */ | ||
58 | } | ||
59 | /* reuse an entry keeping same base & size */ | ||
60 | else if (!(p->flags & ENTRY_IN_USE) && (size <= p->size)) { | ||
61 | p->flags |= ENTRY_IN_USE; | ||
62 | return (void *)p->base; | ||
63 | } | ||
64 | err_out: | ||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | static struct alloc_info *simple_find_entry(void *ptr) | ||
69 | { | ||
70 | u32 i; | ||
71 | struct alloc_info *p = alloc_tbl; | ||
72 | |||
73 | for (i=0; i<tbl_entries; i++,p++) { | ||
74 | if (!(p->flags & ENTRY_BEEN_USED)) | ||
75 | break; | ||
76 | if ((p->flags & ENTRY_IN_USE) && (p->base == (u32)ptr)) | ||
77 | return p; | ||
78 | } | ||
79 | return NULL; | ||
80 | } | ||
81 | |||
82 | static void simple_free(void *ptr) | ||
83 | { | ||
84 | struct alloc_info *p = simple_find_entry(ptr); | ||
85 | |||
86 | if (p != NULL) | ||
87 | p->flags &= ~ENTRY_IN_USE; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Change size of area pointed to by 'ptr' to 'size'. | ||
92 | * If 'ptr' is NULL, then its a malloc(). If 'size' is 0, then its a free(). | ||
93 | * 'ptr' must be NULL or a pointer to a non-freed area previously returned by | ||
94 | * simple_realloc() or simple_malloc(). | ||
95 | */ | ||
96 | static void *simple_realloc(void *ptr, unsigned long size) | ||
97 | { | ||
98 | struct alloc_info *p; | ||
99 | void *new; | ||
100 | |||
101 | if (size == 0) { | ||
102 | simple_free(ptr); | ||
103 | return NULL; | ||
104 | } | ||
105 | |||
106 | if (ptr == NULL) | ||
107 | return simple_malloc(size); | ||
108 | |||
109 | p = simple_find_entry(ptr); | ||
110 | if (p == NULL) /* ptr not from simple_malloc/simple_realloc */ | ||
111 | return NULL; | ||
112 | if (size <= p->size) /* fits in current block */ | ||
113 | return ptr; | ||
114 | |||
115 | new = simple_malloc(size); | ||
116 | memcpy(new, ptr, p->size); | ||
117 | simple_free(ptr); | ||
118 | return new; | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * Returns addr of first byte after heap so caller can see if it took | ||
123 | * too much space. If so, change args & try again. | ||
124 | */ | ||
125 | void *simple_alloc_init(char *base, u32 heap_size, u32 granularity, | ||
126 | u32 max_allocs) | ||
127 | { | ||
128 | u32 heap_base, tbl_size; | ||
129 | |||
130 | heap_size = _ALIGN_UP(heap_size, granularity); | ||
131 | alloc_min = granularity; | ||
132 | tbl_entries = max_allocs; | ||
133 | |||
134 | tbl_size = tbl_entries * sizeof(struct alloc_info); | ||
135 | |||
136 | alloc_tbl = (struct alloc_info *)_ALIGN_UP((unsigned long)base, 8); | ||
137 | memset(alloc_tbl, 0, tbl_size); | ||
138 | |||
139 | heap_base = _ALIGN_UP((u32)alloc_tbl + tbl_size, alloc_min); | ||
140 | |||
141 | next_base = heap_base; | ||
142 | space_left = heap_size; | ||
143 | |||
144 | platform_ops.malloc = simple_malloc; | ||
145 | platform_ops.free = simple_free; | ||
146 | platform_ops.realloc = simple_realloc; | ||
147 | |||
148 | return (void *)(heap_base + heap_size); | ||
149 | } | ||
diff --git a/arch/powerpc/boot/stdio.c b/arch/powerpc/boot/stdio.c index 6d5f6382e1ce..0a9feeb98342 100644 --- a/arch/powerpc/boot/stdio.c +++ b/arch/powerpc/boot/stdio.c | |||
@@ -320,6 +320,7 @@ printf(const char *fmt, ...) | |||
320 | va_start(args, fmt); | 320 | va_start(args, fmt); |
321 | n = vsprintf(sprint_buf, fmt, args); | 321 | n = vsprintf(sprint_buf, fmt, args); |
322 | va_end(args); | 322 | va_end(args); |
323 | console_ops.write(sprint_buf, n); | 323 | if (console_ops.write) |
324 | console_ops.write(sprint_buf, n); | ||
324 | return n; | 325 | return n; |
325 | } | 326 | } |
diff --git a/arch/powerpc/boot/util.S b/arch/powerpc/boot/util.S new file mode 100644 index 000000000000..427ddfc11991 --- /dev/null +++ b/arch/powerpc/boot/util.S | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Copied from <file:arch/powerpc/kernel/misc_32.S> | ||
3 | * | ||
4 | * This file contains miscellaneous low-level functions. | ||
5 | * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) | ||
6 | * | ||
7 | * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) | ||
8 | * and Paul Mackerras. | ||
9 | * | ||
10 | * kexec bits: | ||
11 | * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com> | ||
12 | * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version | ||
17 | * 2 of the License, or (at your option) any later version. | ||
18 | * | ||
19 | */ | ||
20 | #include "ppc_asm.h" | ||
21 | |||
22 | #define SPRN_PVR 0x11F /* Processor Version Register */ | ||
23 | |||
24 | .text | ||
25 | |||
26 | /* udelay (on non-601 processors) needs to know the period of the | ||
27 | * timebase in nanoseconds. This used to be hardcoded to be 60ns | ||
28 | * (period of 66MHz/4). Now a variable is used that is initialized to | ||
29 | * 60 for backward compatibility, but it can be overridden as necessary | ||
30 | * with code something like this: | ||
31 | * extern unsigned long timebase_period_ns; | ||
32 | * timebase_period_ns = 1000000000 / bd->bi_tbfreq; | ||
33 | */ | ||
34 | .data | ||
35 | .globl timebase_period_ns | ||
36 | timebase_period_ns: | ||
37 | .long 60 | ||
38 | |||
39 | .text | ||
40 | /* | ||
41 | * Delay for a number of microseconds | ||
42 | */ | ||
43 | .globl udelay | ||
44 | udelay: | ||
45 | mfspr r4,SPRN_PVR | ||
46 | srwi r4,r4,16 | ||
47 | cmpwi 0,r4,1 /* 601 ? */ | ||
48 | bne .udelay_not_601 | ||
49 | 00: li r0,86 /* Instructions / microsecond? */ | ||
50 | mtctr r0 | ||
51 | 10: addi r0,r0,0 /* NOP */ | ||
52 | bdnz 10b | ||
53 | subic. r3,r3,1 | ||
54 | bne 00b | ||
55 | blr | ||
56 | |||
57 | .udelay_not_601: | ||
58 | mulli r4,r3,1000 /* nanoseconds */ | ||
59 | /* Change r4 to be the number of ticks using: | ||
60 | * (nanoseconds + (timebase_period_ns - 1 )) / timebase_period_ns | ||
61 | * timebase_period_ns defaults to 60 (16.6MHz) */ | ||
62 | mflr r5 | ||
63 | bl 0f | ||
64 | 0: mflr r6 | ||
65 | mtlr r5 | ||
66 | lis r5,0b@ha | ||
67 | addi r5,r5,0b@l | ||
68 | subf r5,r5,r6 /* In case we're relocated */ | ||
69 | addis r5,r5,timebase_period_ns@ha | ||
70 | lwz r5,timebase_period_ns@l(r5) | ||
71 | add r4,r4,r5 | ||
72 | addi r4,r4,-1 | ||
73 | divw r4,r4,r5 /* BUS ticks */ | ||
74 | 1: mftbu r5 | ||
75 | mftb r6 | ||
76 | mftbu r7 | ||
77 | cmpw 0,r5,r7 | ||
78 | bne 1b /* Get [synced] base time */ | ||
79 | addc r9,r6,r4 /* Compute end time */ | ||
80 | addze r8,r5 | ||
81 | 2: mftbu r5 | ||
82 | cmpw 0,r5,r8 | ||
83 | blt 2b | ||
84 | bgt 3f | ||
85 | mftb r6 | ||
86 | cmpw 0,r6,r9 | ||
87 | blt 2b | ||
88 | 3: blr | ||
diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index b5fb1fee76f8..024e4d425c59 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper | |||
@@ -184,6 +184,9 @@ fi | |||
184 | 184 | ||
185 | if [ -n "$dtb" ]; then | 185 | if [ -n "$dtb" ]; then |
186 | addsec $tmp "$dtb" .kernel:dtb | 186 | addsec $tmp "$dtb" .kernel:dtb |
187 | if [ -n "$dts" ]; then | ||
188 | rm $dtb | ||
189 | fi | ||
187 | fi | 190 | fi |
188 | 191 | ||
189 | if [ "$platform" != "miboot" ]; then | 192 | if [ "$platform" != "miboot" ]; then |
diff --git a/arch/powerpc/boot/zImage.coff.lds.S b/arch/powerpc/boot/zImage.coff.lds.S index 05f32388b953..a360905e5428 100644 --- a/arch/powerpc/boot/zImage.coff.lds.S +++ b/arch/powerpc/boot/zImage.coff.lds.S | |||
@@ -21,6 +21,10 @@ SECTIONS | |||
21 | *(.got2) | 21 | *(.got2) |
22 | __got2_end = .; | 22 | __got2_end = .; |
23 | 23 | ||
24 | _dtb_start = .; | ||
25 | *(.kernel:dtb) | ||
26 | _dtb_end = .; | ||
27 | |||
24 | _vmlinux_start = .; | 28 | _vmlinux_start = .; |
25 | *(.kernel:vmlinux.strip) | 29 | *(.kernel:vmlinux.strip) |
26 | _vmlinux_end = .; | 30 | _vmlinux_end = .; |