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 45c9ad23526..0734b2fc1d9 100644 --- a/arch/powerpc/boot/.gitignore +++ b/arch/powerpc/boot/.gitignore @@ -1,19 +1,32 @@ addnote +empty.c +hack-coff infblock.c infblock.h infcodes.c infcodes.h inffast.c inffast.h +inffixed.h inflate.c +inflate.h inftrees.c inftrees.h infutil.c infutil.h kernel-vmlinux.strip.c kernel-vmlinux.strip.gz +mktree uImage zImage +zImage.chrp +zImage.coff +zImage.coff.lds +zImage.lds +zImage.miboot +zImage.pmac +zImage.pseries +zImage.sandpoint zImage.vmode zconf.h zlib.h diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 4b2be611f77..343dbcfdf08 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -40,7 +40,8 @@ zliblinuxheader := zlib.h zconf.h zutil.h $(addprefix $(obj)/,$(zlib) main.o): $(addprefix $(obj)/,$(zliblinuxheader)) \ $(addprefix $(obj)/,$(zlibheader)) -src-wlib := string.S stdio.c main.c div64.S $(zlib) +src-wlib := string.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \ + ns16550.c serial.c simple_alloc.c div64.S util.S $(zlib) src-plat := of.c src-boot := crt0.S $(src-wlib) $(src-plat) empty.c @@ -74,7 +75,7 @@ $(obj)/zImage.lds $(obj)/zImage.coff.lds: $(obj)/%: $(srctree)/$(src)/%.S @cp $< $@ clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \ - $(obj)/empty.c + empty.c zImage zImage.coff.lds zImage.lds zImage.sandpoint quiet_cmd_bootcc = BOOTCC $@ cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $< @@ -93,13 +94,13 @@ $(patsubst %.S,%.o, $(filter %.S, $(src-boot))): %.o: %.S $(obj)/wrapper.a: $(obj-wlib) $(call cmd,bootar) -hostprogs-y := addnote addRamDisk hack-coff +hostprogs-y := addnote addRamDisk hack-coff mktree extra-y := $(obj)/crt0.o $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ $(obj)/zImage.lds $(obj)/zImage.coff.lds wrapper :=$(srctree)/$(src)/wrapper -wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff) +wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) ############# # Bits for building various flavours of zImage @@ -148,13 +149,18 @@ $(obj)/zImage.miboot: vmlinux $(wrapperbits) $(obj)/zImage.initrd.miboot: vmlinux $(wrapperbits) $(call cmd,wrap_initrd,miboot) +$(obj)/zImage.ps3: vmlinux + $(STRIP) -s -R .comment $< -o $@ + $(obj)/uImage: vmlinux $(wrapperbits) $(call cmd,wrap,uboot) image-$(CONFIG_PPC_PSERIES) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.pseries image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries +image-$(CONFIG_PPC_PS3) += zImage.ps3 image-$(CONFIG_PPC_CHRP) += zImage.chrp +image-$(CONFIG_PPC_EFIKA) += zImage.chrp image-$(CONFIG_PPC_PMAC) += zImage.pmac image-$(CONFIG_DEFAULT_UIMAGE) += uImage @@ -176,3 +182,4 @@ install: $(CONFIGURE) $(image-y) clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.strip.gz) clean-files += $(addprefix $(objtree)/, $(obj-boot) vmlinux.bin.gz) +clean-files += $(image-) diff --git a/arch/powerpc/boot/dts/kuroboxHG.dts b/arch/powerpc/boot/dts/kuroboxHG.dts new file mode 100644 index 00000000000..d06b0b01889 --- /dev/null +++ b/arch/powerpc/boot/dts/kuroboxHG.dts @@ -0,0 +1,148 @@ +/* + * Device Tree Souce for Buffalo KuroboxHG + * + * Choose CONFIG_LINKSTATION to build a kernel for KuroboxHG, or use + * the default configuration linkstation_defconfig. + * + * Based on sandpoint.dts + * + * 2006 (c) G. Liakhovetski <g.liakhovetski@gmx.de> + * + * This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + +XXXX add flash parts, rtc, ?? + +build with: "dtc -f -I dts -O dtb -o kuroboxHG.dtb -V 16 kuroboxHG.dts" + + + */ + +/ { + linux,phandle = <1000>; + model = "KuroboxHG"; + compatible = "linkstation"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + linux,phandle = <2000>; + #cpus = <1>; + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,603e { /* Really 8241 */ + linux,phandle = <2100>; + linux,boot-cpu; + device_type = "cpu"; + reg = <0>; + clock-frequency = <fdad680>; /* Fixed by bootwrapper */ + timebase-frequency = <1F04000>; /* Fixed by bootwrapper */ + bus-frequency = <0>; /* From bootloader */ + /* Following required by dtc but not used */ + i-cache-line-size = <0>; + d-cache-line-size = <0>; + i-cache-size = <4000>; + d-cache-size = <4000>; + }; + }; + + memory { + linux,phandle = <3000>; + device_type = "memory"; + reg = <00000000 08000000>; + }; + + soc10x { /* AFAICT need to make soc for 8245's uarts to be defined */ + linux,phandle = <4000>; + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + device_type = "soc"; + compatible = "mpc10x"; + store-gathering = <0>; /* 0 == off, !0 == on */ + reg = <80000000 00100000>; + ranges = <80000000 80000000 70000000 /* pci mem space */ + fc000000 fc000000 00100000 /* EUMB */ + fe000000 fe000000 00c00000 /* pci i/o space */ + fec00000 fec00000 00300000 /* pci cfg regs */ + fef00000 fef00000 00100000>; /* pci iack */ + + i2c@80003000 { + linux,phandle = <4300>; + device_type = "i2c"; + compatible = "fsl-i2c"; + reg = <80003000 1000>; + interrupts = <5 2>; + interrupt-parent = <4400>; + }; + + serial@80004500 { + linux,phandle = <4511>; + device_type = "serial"; + compatible = "ns16550"; + reg = <80004500 8>; + clock-frequency = <7c044a8>; + current-speed = <2580>; + interrupts = <9 2>; + interrupt-parent = <4400>; + }; + + serial@80004600 { + linux,phandle = <4512>; + device_type = "serial"; + compatible = "ns16550"; + reg = <80004600 8>; + clock-frequency = <7c044a8>; + current-speed = <e100>; + interrupts = <a 0>; + interrupt-parent = <4400>; + }; + + pic@80040000 { + linux,phandle = <4400>; + #interrupt-cells = <2>; + #address-cells = <0>; + device_type = "open-pic"; + compatible = "chrp,open-pic"; + interrupt-controller; + reg = <80040000 40000>; + built-in; + }; + + pci@fec00000 { + linux,phandle = <4500>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + device_type = "pci"; + compatible = "mpc10x-pci"; + reg = <fec00000 400000>; + ranges = <01000000 0 0 fe000000 0 00c00000 + 02000000 0 80000000 80000000 0 70000000>; + bus-range = <0 ff>; + clock-frequency = <7f28155>; + interrupt-parent = <4400>; + interrupt-map-mask = <f800 0 0 7>; + interrupt-map = < + /* IDSEL 0x11 - IRQ0 ETH */ + 5800 0 0 1 4400 0 1 + 5800 0 0 2 4400 1 1 + 5800 0 0 3 4400 2 1 + 5800 0 0 4 4400 3 1 + /* IDSEL 0x12 - IRQ1 IDE0 */ + 6000 0 0 1 4400 1 1 + 6000 0 0 2 4400 2 1 + 6000 0 0 3 4400 3 1 + 6000 0 0 4 4400 0 1 + /* IDSEL 0x14 - IRQ3 USB2.0 */ + 7000 0 0 1 4400 3 1 + 7000 0 0 2 4400 3 1 + 7000 0 0 3 4400 3 1 + 7000 0 0 4 4400 3 1 + >; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/lite5200.dts b/arch/powerpc/boot/dts/lite5200.dts new file mode 100644 index 00000000000..8bc0d259796 --- /dev/null +++ b/arch/powerpc/boot/dts/lite5200.dts @@ -0,0 +1,313 @@ +/* + * Lite5200 board Device Tree Source + * + * Copyright 2006 Secret Lab Technologies Ltd. + * Grant Likely <grant.likely@secretlab.ca> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/ { + model = "Lite5200"; + compatible = "lite5200\0lite52xx\0mpc5200\0mpc52xx"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #cpus = <1>; + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,5200@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <20>; + i-cache-line-size = <20>; + d-cache-size = <4000>; // L1, 16K + i-cache-size = <4000>; // L1, 16K + timebase-frequency = <0>; // from bootloader + bus-frequency = <0>; // from bootloader + clock-frequency = <0>; // from bootloader + 32-bit; + }; + }; + + memory { + device_type = "memory"; + reg = <00000000 04000000>; // 64MB + }; + + soc5200@f0000000 { + #interrupt-cells = <3>; + device_type = "soc"; + ranges = <0 f0000000 f0010000>; + reg = <f0000000 00010000>; + bus-frequency = <0>; // from bootloader + + cdm@200 { + compatible = "mpc5200-cdm\0mpc52xx-cdm"; + reg = <200 38>; + }; + + pic@500 { + // 5200 interrupts are encoded into two levels; + linux,phandle = <500>; + interrupt-controller; + #interrupt-cells = <3>; + device_type = "interrupt-controller"; + compatible = "mpc5200-pic\0mpc52xx-pic"; + reg = <500 80>; + built-in; + }; + + gpt@600 { // General Purpose Timer + compatible = "mpc5200-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <600 10>; + interrupts = <1 9 0>; + interrupt-parent = <500>; + }; + + gpt@610 { // General Purpose Timer + compatible = "mpc5200-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <610 10>; + interrupts = <1 a 0>; + interrupt-parent = <500>; + }; + + gpt@620 { // General Purpose Timer + compatible = "mpc5200-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <620 10>; + interrupts = <1 b 0>; + interrupt-parent = <500>; + }; + + gpt@630 { // General Purpose Timer + compatible = "mpc5200-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <630 10>; + interrupts = <1 c 0>; + interrupt-parent = <500>; + }; + + gpt@640 { // General Purpose Timer + compatible = "mpc5200-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <640 10>; + interrupts = <1 d 0>; + interrupt-parent = <500>; + }; + + gpt@650 { // General Purpose Timer + compatible = "mpc5200-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <650 10>; + interrupts = <1 e 0>; + interrupt-parent = <500>; + }; + + gpt@660 { // General Purpose Timer + compatible = "mpc5200-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <660 10>; + interrupts = <1 f 0>; + interrupt-parent = <500>; + }; + + gpt@670 { // General Purpose Timer + compatible = "mpc5200-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <670 10>; + interrupts = <1 10 0>; + interrupt-parent = <500>; + }; + + rtc@800 { // Real time clock + compatible = "mpc5200-rtc\0mpc52xx-rtc"; + device_type = "rtc"; + reg = <800 100>; + interrupts = <1 5 0 1 6 0>; + interrupt-parent = <500>; + }; + + mscan@900 { + device_type = "mscan"; + compatible = "mpc5200-mscan\0mpc52xx-mscan"; + interrupts = <2 11 0>; + interrupt-parent = <500>; + reg = <900 80>; + }; + + mscan@980 { + device_type = "mscan"; + compatible = "mpc5200-mscan\0mpc52xx-mscan"; + interrupts = <1 12 0>; + interrupt-parent = <500>; + reg = <980 80>; + }; + + gpio@b00 { + compatible = "mpc5200-gpio\0mpc52xx-gpio"; + reg = <b00 40>; + interrupts = <1 7 0>; + interrupt-parent = <500>; + }; + + gpio-wkup@b00 { + compatible = "mpc5200-gpio-wkup\0mpc52xx-gpio-wkup"; + reg = <c00 40>; + interrupts = <1 8 0 0 3 0>; + interrupt-parent = <500>; + }; + + pci@0d00 { + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + compatible = "mpc5200-pci\0mpc52xx-pci"; + reg = <d00 100>; + interrupt-map-mask = <f800 0 0 7>; + interrupt-map = <c000 0 0 1 500 0 0 3 + c000 0 0 2 500 0 0 3 + c000 0 0 3 500 0 0 3 + c000 0 0 4 500 0 0 3>; + clock-frequency = <0>; // From boot loader + interrupts = <2 8 0 2 9 0 2 a 0>; + interrupt-parent = <500>; + bus-range = <0 0>; + ranges = <42000000 0 80000000 80000000 0 20000000 + 02000000 0 a0000000 a0000000 0 10000000 + 01000000 0 00000000 b0000000 0 01000000>; + }; + + spi@f00 { + device_type = "spi"; + compatible = "mpc5200-spi\0mpc52xx-spi"; + reg = <f00 20>; + interrupts = <2 d 0 2 e 0>; + interrupt-parent = <500>; + }; + + usb@1000 { + device_type = "usb-ohci-be"; + compatible = "mpc5200-ohci\0mpc52xx-ohci\0ohci-be"; + reg = <1000 ff>; + interrupts = <2 6 0>; + interrupt-parent = <500>; + }; + + bestcomm@1200 { + device_type = "dma-controller"; + compatible = "mpc5200-bestcomm\0mpc52xx-bestcomm"; + reg = <1200 80>; + interrupts = <3 0 0 3 1 0 3 2 0 3 3 0 + 3 4 0 3 5 0 3 6 0 3 7 0 + 3 8 0 3 9 0 3 a 0 3 b 0 + 3 c 0 3 d 0 3 e 0 3 f 0>; + interrupt-parent = <500>; + }; + + xlb@1f00 { + compatible = "mpc5200-xlb\0mpc52xx-xlb"; + reg = <1f00 100>; + }; + + serial@2000 { // PSC1 + device_type = "serial"; + compatible = "mpc5200-psc-uart\0mpc52xx-psc-uart"; + port-number = <0>; // Logical port assignment + reg = <2000 100>; + interrupts = <2 1 0>; + interrupt-parent = <500>; + }; + + // PSC2 in spi mode example + spi@2200 { // PSC2 + device_type = "spi"; + compatible = "mpc5200-psc-spi\0mpc52xx-psc-spi"; + reg = <2200 100>; + interrupts = <2 2 0>; + interrupt-parent = <500>; + }; + + // PSC3 in CODEC mode example + i2s@2400 { // PSC3 + device_type = "i2s"; + compatible = "mpc5200-psc-i2s\0mpc52xx-psc-i2s"; + reg = <2400 100>; + interrupts = <2 3 0>; + interrupt-parent = <500>; + }; + + // PSC4 unconfigured + //serial@2600 { // PSC4 + // device_type = "serial"; + // compatible = "mpc5200-psc-uart\0mpc52xx-psc-uart"; + // reg = <2600 100>; + // interrupts = <2 b 0>; + // interrupt-parent = <500>; + //}; + + // PSC5 unconfigured + //serial@2800 { // PSC5 + // device_type = "serial"; + // compatible = "mpc5200-psc-uart\0mpc52xx-psc-uart"; + // reg = <2800 100>; + // interrupts = <2 c 0>; + // interrupt-parent = <500>; + //}; + + // PSC6 in AC97 mode example + ac97@2c00 { // PSC6 + device_type = "ac97"; + compatible = "mpc5200-psc-ac97\0mpc52xx-psc-ac97"; + reg = <2c00 100>; + interrupts = <2 4 0>; + interrupt-parent = <500>; + }; + + ethernet@3000 { + device_type = "network"; + compatible = "mpc5200-fec\0mpc52xx-fec"; + reg = <3000 800>; + mac-address = [ 02 03 04 05 06 07 ]; // Bad! + interrupts = <2 5 0>; + interrupt-parent = <500>; + }; + + ata@3a00 { + device_type = "ata"; + compatible = "mpc5200-ata\0mpc52xx-ata"; + reg = <3a00 100>; + interrupts = <2 7 0>; + interrupt-parent = <500>; + }; + + i2c@3d00 { + device_type = "i2c"; + compatible = "mpc5200-i2c\0mpc52xx-i2c"; + reg = <3d00 40>; + interrupts = <2 f 0>; + interrupt-parent = <500>; + }; + + i2c@3d40 { + device_type = "i2c"; + compatible = "mpc5200-i2c\0mpc52xx-i2c"; + reg = <3d40 40>; + interrupts = <2 10 0>; + interrupt-parent = <500>; + }; + sram@8000 { + device_type = "sram"; + compatible = "mpc5200-sram\0mpc52xx-sram\0sram"; + reg = <8000 4000>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/lite5200b.dts b/arch/powerpc/boot/dts/lite5200b.dts new file mode 100644 index 00000000000..81cb76418a7 --- /dev/null +++ b/arch/powerpc/boot/dts/lite5200b.dts @@ -0,0 +1,318 @@ +/* + * Lite5200B board Device Tree Source + * + * Copyright 2006 Secret Lab Technologies Ltd. + * Grant Likely <grant.likely@secretlab.ca> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/ { + model = "Lite5200b"; + compatible = "lite5200b\0lite52xx\0mpc5200b\0mpc52xx"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #cpus = <1>; + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,5200@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <20>; + i-cache-line-size = <20>; + d-cache-size = <4000>; // L1, 16K + i-cache-size = <4000>; // L1, 16K + timebase-frequency = <0>; // from bootloader + bus-frequency = <0>; // from bootloader + clock-frequency = <0>; // from bootloader + 32-bit; + }; + }; + + memory { + device_type = "memory"; + reg = <00000000 10000000>; // 256MB + }; + + soc5200@f0000000 { + #interrupt-cells = <3>; + device_type = "soc"; + ranges = <0 f0000000 f0010000>; + reg = <f0000000 00010000>; + bus-frequency = <0>; // from bootloader + + cdm@200 { + compatible = "mpc5200b-cdm\0mpc52xx-cdm"; + reg = <200 38>; + }; + + pic@500 { + // 5200 interrupts are encoded into two levels; + linux,phandle = <500>; + interrupt-controller; + #interrupt-cells = <3>; + device_type = "interrupt-controller"; + compatible = "mpc5200b-pic\0mpc52xx-pic"; + reg = <500 80>; + built-in; + }; + + gpt@600 { // General Purpose Timer + compatible = "mpc5200b-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <600 10>; + interrupts = <1 9 0>; + interrupt-parent = <500>; + }; + + gpt@610 { // General Purpose Timer + compatible = "mpc5200b-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <610 10>; + interrupts = <1 a 0>; + interrupt-parent = <500>; + }; + + gpt@620 { // General Purpose Timer + compatible = "mpc5200b-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <620 10>; + interrupts = <1 b 0>; + interrupt-parent = <500>; + }; + + gpt@630 { // General Purpose Timer + compatible = "mpc5200b-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <630 10>; + interrupts = <1 c 0>; + interrupt-parent = <500>; + }; + + gpt@640 { // General Purpose Timer + compatible = "mpc5200b-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <640 10>; + interrupts = <1 d 0>; + interrupt-parent = <500>; + }; + + gpt@650 { // General Purpose Timer + compatible = "mpc5200b-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <650 10>; + interrupts = <1 e 0>; + interrupt-parent = <500>; + }; + + gpt@660 { // General Purpose Timer + compatible = "mpc5200b-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <660 10>; + interrupts = <1 f 0>; + interrupt-parent = <500>; + }; + + gpt@670 { // General Purpose Timer + compatible = "mpc5200b-gpt\0mpc52xx-gpt"; + device_type = "gpt"; + reg = <670 10>; + interrupts = <1 10 0>; + interrupt-parent = <500>; + }; + + rtc@800 { // Real time clock + compatible = "mpc5200b-rtc\0mpc52xx-rtc"; + device_type = "rtc"; + reg = <800 100>; + interrupts = <1 5 0 1 6 0>; + interrupt-parent = <500>; + }; + + mscan@900 { + device_type = "mscan"; + compatible = "mpc5200b-mscan\0mpc52xx-mscan"; + interrupts = <2 11 0>; + interrupt-parent = <500>; + reg = <900 80>; + }; + + mscan@980 { + device_type = "mscan"; + compatible = "mpc5200b-mscan\0mpc52xx-mscan"; + interrupts = <1 12 0>; + interrupt-parent = <500>; + reg = <980 80>; + }; + + gpio@b00 { + compatible = "mpc5200b-gpio\0mpc52xx-gpio"; + reg = <b00 40>; + interrupts = <1 7 0>; + interrupt-parent = <500>; + }; + + gpio-wkup@b00 { + compatible = "mpc5200b-gpio-wkup\0mpc52xx-gpio-wkup"; + reg = <c00 40>; + interrupts = <1 8 0 0 3 0>; + interrupt-parent = <500>; + }; + + pci@0d00 { + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + compatible = "mpc5200b-pci\0mpc52xx-pci"; + reg = <d00 100>; + interrupt-map-mask = <f800 0 0 7>; + interrupt-map = <c000 0 0 1 500 0 0 3 // 1st slot + c000 0 0 2 500 1 1 3 + c000 0 0 3 500 1 2 3 + c000 0 0 4 500 1 3 3 + + c800 0 0 1 500 1 1 3 // 2nd slot + c800 0 0 2 500 1 2 3 + c800 0 0 3 500 1 3 3 + c800 0 0 4 500 0 0 3>; + clock-frequency = <0>; // From boot loader + interrupts = <2 8 0 2 9 0 2 a 0>; + interrupt-parent = <500>; + bus-range = <0 0>; + ranges = <42000000 0 80000000 80000000 0 20000000 + 02000000 0 a0000000 a0000000 0 10000000 + 01000000 0 00000000 b0000000 0 01000000>; + }; + + spi@f00 { + device_type = "spi"; + compatible = "mpc5200b-spi\0mpc52xx-spi"; + reg = <f00 20>; + interrupts = <2 d 0 2 e 0>; + interrupt-parent = <500>; + }; + + usb@1000 { + device_type = "usb-ohci-be"; + compatible = "mpc5200b-ohci\0mpc52xx-ohci\0ohci-be"; + reg = <1000 ff>; + interrupts = <2 6 0>; + interrupt-parent = <500>; + }; + + bestcomm@1200 { + device_type = "dma-controller"; + compatible = "mpc5200b-bestcomm\0mpc52xx-bestcomm"; + reg = <1200 80>; + interrupts = <3 0 0 3 1 0 3 2 0 3 3 0 + 3 4 0 3 5 0 3 6 0 3 7 0 + 3 8 0 3 9 0 3 a 0 3 b 0 + 3 c 0 3 d 0 3 e 0 3 f 0>; + interrupt-parent = <500>; + }; + + xlb@1f00 { + compatible = "mpc5200b-xlb\0mpc52xx-xlb"; + reg = <1f00 100>; + }; + + serial@2000 { // PSC1 + device_type = "serial"; + compatible = "mpc5200b-psc-uart\0mpc52xx-psc-uart"; + port-number = <0>; // Logical port assignment + reg = <2000 100>; + interrupts = <2 1 0>; + interrupt-parent = <500>; + }; + + // PSC2 in spi mode example + spi@2200 { // PSC2 + device_type = "spi"; + compatible = "mpc5200b-psc-spi\0mpc52xx-psc-spi"; + reg = <2200 100>; + interrupts = <2 2 0>; + interrupt-parent = <500>; + }; + + // PSC3 in CODEC mode example + i2s@2400 { // PSC3 + device_type = "i2s"; + compatible = "mpc5200b-psc-i2s\0mpc52xx-psc-i2s"; + reg = <2400 100>; + interrupts = <2 3 0>; + interrupt-parent = <500>; + }; + + // PSC4 unconfigured + //serial@2600 { // PSC4 + // device_type = "serial"; + // compatible = "mpc5200b-psc-uart\0mpc52xx-psc-uart"; + // reg = <2600 100>; + // interrupts = <2 b 0>; + // interrupt-parent = <500>; + //}; + + // PSC5 unconfigured + //serial@2800 { // PSC5 + // device_type = "serial"; + // compatible = "mpc5200b-psc-uart\0mpc52xx-psc-uart"; + // reg = <2800 100>; + // interrupts = <2 c 0>; + // interrupt-parent = <500>; + //}; + + // PSC6 in AC97 mode example + ac97@2c00 { // PSC6 + device_type = "ac97"; + compatible = "mpc5200b-psc-ac97\0mpc52xx-psc-ac97"; + reg = <2c00 100>; + interrupts = <2 4 0>; + interrupt-parent = <500>; + }; + + ethernet@3000 { + device_type = "network"; + compatible = "mpc5200b-fec\0mpc52xx-fec"; + reg = <3000 800>; + mac-address = [ 02 03 04 05 06 07 ]; // Bad! + interrupts = <2 5 0>; + interrupt-parent = <500>; + }; + + ata@3a00 { + device_type = "ata"; + compatible = "mpc5200b-ata\0mpc52xx-ata"; + reg = <3a00 100>; + interrupts = <2 7 0>; + interrupt-parent = <500>; + }; + + i2c@3d00 { + device_type = "i2c"; + compatible = "mpc5200b-i2c\0mpc52xx-i2c"; + reg = <3d00 40>; + interrupts = <2 f 0>; + interrupt-parent = <500>; + }; + + i2c@3d40 { + device_type = "i2c"; + compatible = "mpc5200b-i2c\0mpc52xx-i2c"; + reg = <3d40 40>; + interrupts = <2 10 0>; + interrupt-parent = <500>; + }; + sram@8000 { + device_type = "sram"; + compatible = "mpc5200b-sram\0mpc52xx-sram\0sram"; + reg = <8000 4000>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc7448hpc2.dts b/arch/powerpc/boot/dts/mpc7448hpc2.dts index d7b985e6bd2..c4d9562cbaa 100644 --- a/arch/powerpc/boot/dts/mpc7448hpc2.dts +++ b/arch/powerpc/boot/dts/mpc7448hpc2.dts @@ -161,29 +161,41 @@ interrupt-map = < /* IDSEL 0x11 */ - 0800 0 0 1 7400 24 0 - 0800 0 0 2 7400 25 0 - 0800 0 0 3 7400 26 0 - 0800 0 0 4 7400 27 0 + 0800 0 0 1 1180 24 0 + 0800 0 0 2 1180 25 0 + 0800 0 0 3 1180 26 0 + 0800 0 0 4 1180 27 0 /* IDSEL 0x12 */ - 1000 0 0 1 7400 25 0 - 1000 0 0 2 7400 26 0 - 1000 0 0 3 7400 27 0 - 1000 0 0 4 7400 24 0 + 1000 0 0 1 1180 25 0 + 1000 0 0 2 1180 26 0 + 1000 0 0 3 1180 27 0 + 1000 0 0 4 1180 24 0 /* IDSEL 0x13 */ - 1800 0 0 1 7400 26 0 - 1800 0 0 2 7400 27 0 - 1800 0 0 3 7400 24 0 - 1800 0 0 4 7400 25 0 + 1800 0 0 1 1180 26 0 + 1800 0 0 2 1180 27 0 + 1800 0 0 3 1180 24 0 + 1800 0 0 4 1180 25 0 /* IDSEL 0x14 */ - 2000 0 0 1 7400 27 0 - 2000 0 0 2 7400 24 0 - 2000 0 0 3 7400 25 0 - 2000 0 0 4 7400 26 0 + 2000 0 0 1 1180 27 0 + 2000 0 0 2 1180 24 0 + 2000 0 0 3 1180 25 0 + 2000 0 0 4 1180 26 0 >; + router@1180 { + linux,phandle = <1180>; + clock-frequency = <0>; + interrupt-controller; + device_type = "pic-router"; + #address-cells = <0>; + #interrupt-cells = <2>; + built-in; + big-endian; + interrupts = <17 2>; + interrupt-parent = <7400>; + }; }; }; diff --git a/arch/powerpc/boot/flatdevtree.c b/arch/powerpc/boot/flatdevtree.c new file mode 100644 index 00000000000..c76c194715b --- /dev/null +++ b/arch/powerpc/boot/flatdevtree.c @@ -0,0 +1,880 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright Pantelis Antoniou 2006 + * Copyright (C) IBM Corporation 2006 + * + * Authors: Pantelis Antoniou <pantelis@embeddedalley.com> + * Hollis Blanchard <hollisb@us.ibm.com> + * Mark A. Greer <mgreer@mvista.com> + * Paul Mackerras <paulus@samba.org> + */ + +#include <string.h> +#include <stddef.h> +#include "flatdevtree.h" +#include "flatdevtree_env.h" + +#define _ALIGN(x, al) (((x) + (al) - 1) & ~((al) - 1)) + +/* Routines for keeping node ptrs returned by ft_find_device current */ +/* First entry not used b/c it would return 0 and be taken as NULL/error */ +static void *ft_node_add(struct ft_cxt *cxt, char *node) +{ + unsigned int i; + + for (i = 1; i < cxt->nodes_used; i++) /* already there? */ + if (cxt->node_tbl[i] == node) + return (void *)i; + + if (cxt->nodes_used < cxt->node_max) { + cxt->node_tbl[cxt->nodes_used] = node; + return (void *)cxt->nodes_used++; + } + + return NULL; +} + +static char *ft_node_ph2node(struct ft_cxt *cxt, const void *phandle) +{ + unsigned int i = (unsigned int)phandle; + + if (i < cxt->nodes_used) + return cxt->node_tbl[i]; + return NULL; +} + +static void ft_node_update_before(struct ft_cxt *cxt, char *addr, int shift) +{ + unsigned int i; + + if (shift == 0) + return; + + for (i = 1; i < cxt->nodes_used; i++) + if (cxt->node_tbl[i] < addr) + cxt->node_tbl[i] += shift; +} + +static void ft_node_update_after(struct ft_cxt *cxt, char *addr, int shift) +{ + unsigned int i; + + if (shift == 0) + return; + + for (i = 1; i < cxt->nodes_used; i++) + if (cxt->node_tbl[i] >= addr) + cxt->node_tbl[i] += shift; +} + +/* Struct used to return info from ft_next() */ +struct ft_atom { + u32 tag; + const char *name; + void *data; + u32 size; +}; + +/* Set ptrs to current one's info; return addr of next one */ +static char *ft_next(struct ft_cxt *cxt, char *p, struct ft_atom *ret) +{ + u32 sz; + + if (p >= cxt->rgn[FT_STRUCT].start + cxt->rgn[FT_STRUCT].size) + return NULL; + + ret->tag = be32_to_cpu(*(u32 *) p); + p += 4; + + switch (ret->tag) { /* Tag */ + case OF_DT_BEGIN_NODE: + ret->name = p; + ret->data = (void *)(p - 4); /* start of node */ + p += _ALIGN(strlen(p) + 1, 4); + break; + case OF_DT_PROP: + ret->size = sz = be32_to_cpu(*(u32 *) p); + ret->name = cxt->str_anchor + be32_to_cpu(*(u32 *) (p + 4)); + ret->data = (void *)(p + 8); + p += 8 + _ALIGN(sz, 4); + break; + case OF_DT_END_NODE: + case OF_DT_NOP: + break; + case OF_DT_END: + default: + p = NULL; + break; + } + + return p; +} + +#define HDR_SIZE _ALIGN(sizeof(struct boot_param_header), 8) +#define EXPAND_INCR 1024 /* alloc this much extra when expanding */ + +/* See if the regions are in the standard order and non-overlapping */ +static int ft_ordered(struct ft_cxt *cxt) +{ + char *p = (char *)cxt->bph + HDR_SIZE; + enum ft_rgn_id r; + + for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) { + if (p > cxt->rgn[r].start) + return 0; + p = cxt->rgn[r].start + cxt->rgn[r].size; + } + return p <= (char *)cxt->bph + cxt->max_size; +} + +/* Copy the tree to a newly-allocated region and put things in order */ +static int ft_reorder(struct ft_cxt *cxt, int nextra) +{ + unsigned long tot; + enum ft_rgn_id r; + char *p, *pend; + int stroff; + + tot = HDR_SIZE + EXPAND_INCR; + for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) + tot += cxt->rgn[r].size; + if (nextra > 0) + tot += nextra; + tot = _ALIGN(tot, 8); + + if (!cxt->realloc) + return 0; + p = cxt->realloc(NULL, tot); + if (!p) + return 0; + + memcpy(p, cxt->bph, sizeof(struct boot_param_header)); + /* offsets get fixed up later */ + + cxt->bph = (struct boot_param_header *)p; + cxt->max_size = tot; + pend = p + tot; + p += HDR_SIZE; + + memcpy(p, cxt->rgn[FT_RSVMAP].start, cxt->rgn[FT_RSVMAP].size); + cxt->rgn[FT_RSVMAP].start = p; + p += cxt->rgn[FT_RSVMAP].size; + + memcpy(p, cxt->rgn[FT_STRUCT].start, cxt->rgn[FT_STRUCT].size); + ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, + p - cxt->rgn[FT_STRUCT].start); + cxt->p += p - cxt->rgn[FT_STRUCT].start; + cxt->rgn[FT_STRUCT].start = p; + + p = pend - cxt->rgn[FT_STRINGS].size; + memcpy(p, cxt->rgn[FT_STRINGS].start, cxt->rgn[FT_STRINGS].size); + stroff = cxt->str_anchor - cxt->rgn[FT_STRINGS].start; + cxt->rgn[FT_STRINGS].start = p; + cxt->str_anchor = p + stroff; + + cxt->isordered = 1; + return 1; +} + +static inline char *prev_end(struct ft_cxt *cxt, enum ft_rgn_id r) +{ + if (r > FT_RSVMAP) + return cxt->rgn[r - 1].start + cxt->rgn[r - 1].size; + return (char *)cxt->bph + HDR_SIZE; +} + +static inline char *next_start(struct ft_cxt *cxt, enum ft_rgn_id r) +{ + if (r < FT_STRINGS) + return cxt->rgn[r + 1].start; + return (char *)cxt->bph + cxt->max_size; +} + +/* + * See if we can expand region rgn by nextra bytes by using up + * free space after or before the region. + */ +static int ft_shuffle(struct ft_cxt *cxt, char **pp, enum ft_rgn_id rgn, + int nextra) +{ + char *p = *pp; + char *rgn_start, *rgn_end; + + rgn_start = cxt->rgn[rgn].start; + rgn_end = rgn_start + cxt->rgn[rgn].size; + if (nextra <= 0 || rgn_end + nextra <= next_start(cxt, rgn)) { + /* move following stuff */ + if (p < rgn_end) { + if (nextra < 0) + memmove(p, p - nextra, rgn_end - p + nextra); + else + memmove(p + nextra, p, rgn_end - p); + if (rgn == FT_STRUCT) + ft_node_update_after(cxt, p, nextra); + } + cxt->rgn[rgn].size += nextra; + if (rgn == FT_STRINGS) + /* assumes strings only added at beginning */ + cxt->str_anchor += nextra; + return 1; + } + if (prev_end(cxt, rgn) <= rgn_start - nextra) { + /* move preceding stuff */ + if (p > rgn_start) { + memmove(rgn_start - nextra, rgn_start, p - rgn_start); + if (rgn == FT_STRUCT) + ft_node_update_before(cxt, p, -nextra); + } + *p -= nextra; + cxt->rgn[rgn].start -= nextra; + cxt->rgn[rgn].size += nextra; + return 1; + } + return 0; +} + +static int ft_make_space(struct ft_cxt *cxt, char **pp, enum ft_rgn_id rgn, + int nextra) +{ + unsigned long size, ssize, tot; + char *str, *next; + enum ft_rgn_id r; + + if (!cxt->isordered && !ft_reorder(cxt, nextra)) + return 0; + if (ft_shuffle(cxt, pp, rgn, nextra)) + return 1; + + /* See if there is space after the strings section */ + ssize = cxt->rgn[FT_STRINGS].size; + if (cxt->rgn[FT_STRINGS].start + ssize + < (char *)cxt->bph + cxt->max_size) { + /* move strings up as far as possible */ + str = (char *)cxt->bph + cxt->max_size - ssize; + cxt->str_anchor += str - cxt->rgn[FT_STRINGS].start; + memmove(str, cxt->rgn[FT_STRINGS].start, ssize); + cxt->rgn[FT_STRINGS].start = str; + /* enough space now? */ + if (rgn >= FT_STRUCT && ft_shuffle(cxt, pp, rgn, nextra)) + return 1; + } + + /* how much total free space is there following this region? */ + tot = 0; + for (r = rgn; r < FT_STRINGS; ++r) { + char *r_end = cxt->rgn[r].start + cxt->rgn[r].size; + tot += next_start(cxt, rgn) - r_end; + } + + /* cast is to shut gcc up; we know nextra >= 0 */ + if (tot < (unsigned int)nextra) { + /* have to reallocate */ + char *newp, *new_start; + int shift; + + if (!cxt->realloc) + return 0; + size = _ALIGN(cxt->max_size + (nextra - tot) + EXPAND_INCR, 8); + newp = cxt->realloc(cxt->bph, size); + if (!newp) + return 0; + cxt->max_size = size; + shift = newp - (char *)cxt->bph; + + if (shift) { /* realloc can return same addr */ + cxt->bph = (struct boot_param_header *)newp; + ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, + shift); + for (r = FT_RSVMAP; r <= FT_STRINGS; ++r) { + new_start = cxt->rgn[r].start + shift; + cxt->rgn[r].start = new_start; + } + *pp += shift; + cxt->str_anchor += shift; + } + + /* move strings up to the end */ + str = newp + size - ssize; + cxt->str_anchor += str - cxt->rgn[FT_STRINGS].start; + memmove(str, cxt->rgn[FT_STRINGS].start, ssize); + cxt->rgn[FT_STRINGS].start = str; + + if (ft_shuffle(cxt, pp, rgn, nextra)) + return 1; + } + + /* must be FT_RSVMAP and we need to move FT_STRUCT up */ + if (rgn == FT_RSVMAP) { + next = cxt->rgn[FT_RSVMAP].start + cxt->rgn[FT_RSVMAP].size + + nextra; + ssize = cxt->rgn[FT_STRUCT].size; + if (next + ssize >= cxt->rgn[FT_STRINGS].start) + return 0; /* "can't happen" */ + memmove(next, cxt->rgn[FT_STRUCT].start, ssize); + ft_node_update_after(cxt, cxt->rgn[FT_STRUCT].start, nextra); + cxt->rgn[FT_STRUCT].start = next; + + if (ft_shuffle(cxt, pp, rgn, nextra)) + return 1; + } + + return 0; /* "can't happen" */ +} + +static void ft_put_word(struct ft_cxt *cxt, u32 v) +{ + *(u32 *) cxt->p = cpu_to_be32(v); + cxt->p += 4; +} + +static void ft_put_bin(struct ft_cxt *cxt, const void *data, unsigned int sz) +{ + unsigned long sza = _ALIGN(sz, 4); + + /* zero out the alignment gap if necessary */ + if (sz < sza) + *(u32 *) (cxt->p + sza - 4) = 0; + + /* copy in the data */ + memcpy(cxt->p, data, sz); + + cxt->p += sza; +} + +int ft_begin_node(struct ft_cxt *cxt, const char *name) +{ + unsigned long nlen = strlen(name) + 1; + unsigned long len = 8 + _ALIGN(nlen, 4); + + if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, len)) + return -1; + ft_put_word(cxt, OF_DT_BEGIN_NODE); + ft_put_bin(cxt, name, strlen(name) + 1); + return 0; +} + +void ft_end_node(struct ft_cxt *cxt) +{ + ft_put_word(cxt, OF_DT_END_NODE); +} + +void ft_nop(struct ft_cxt *cxt) +{ + if (ft_make_space(cxt, &cxt->p, FT_STRUCT, 4)) + ft_put_word(cxt, OF_DT_NOP); +} + +#define NO_STRING 0x7fffffff + +static int lookup_string(struct ft_cxt *cxt, const char *name) +{ + char *p, *end; + + p = cxt->rgn[FT_STRINGS].start; + end = p + cxt->rgn[FT_STRINGS].size; + while (p < end) { + if (strcmp(p, (char *)name) == 0) + return p - cxt->str_anchor; + p += strlen(p) + 1; + } + + return NO_STRING; +} + +/* lookup string and insert if not found */ +static int map_string(struct ft_cxt *cxt, const char *name) +{ + int off; + char *p; + + off = lookup_string(cxt, name); + if (off != NO_STRING) + return off; + p = cxt->rgn[FT_STRINGS].start; + if (!ft_make_space(cxt, &p, FT_STRINGS, strlen(name) + 1)) + return NO_STRING; + strcpy(p, name); + return p - cxt->str_anchor; +} + +int ft_prop(struct ft_cxt *cxt, const char *name, const void *data, + unsigned int sz) +{ + int off, len; + + off = lookup_string(cxt, name); + if (off == NO_STRING) + return -1; + + len = 12 + _ALIGN(sz, 4); + if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, len)) + return -1; + + ft_put_word(cxt, OF_DT_PROP); + ft_put_word(cxt, sz); + ft_put_word(cxt, off); + ft_put_bin(cxt, data, sz); + return 0; +} + +int ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str) +{ + return ft_prop(cxt, name, str, strlen(str) + 1); +} + +int ft_prop_int(struct ft_cxt *cxt, const char *name, unsigned int val) +{ + u32 v = cpu_to_be32((u32) val); + + return ft_prop(cxt, name, &v, 4); +} + +/* Calculate the size of the reserved map */ +static unsigned long rsvmap_size(struct ft_cxt *cxt) +{ + struct ft_reserve *res; + + res = (struct ft_reserve *)cxt->rgn[FT_RSVMAP].start; + while (res->start || res->len) + ++res; + return (char *)(res + 1) - cxt->rgn[FT_RSVMAP].start; +} + +/* Calculate the size of the struct region by stepping through it */ +static unsigned long struct_size(struct ft_cxt *cxt) +{ + char *p = cxt->rgn[FT_STRUCT].start; + char *next; + struct ft_atom atom; + + /* make check in ft_next happy */ + if (cxt->rgn[FT_STRUCT].size == 0) + cxt->rgn[FT_STRUCT].size = 0xfffffffful - (unsigned long)p; + + while ((next = ft_next(cxt, p, &atom)) != NULL) + p = next; + return p + 4 - cxt->rgn[FT_STRUCT].start; +} + +/* add `adj' on to all string offset values in the struct area */ +static void adjust_string_offsets(struct ft_cxt *cxt, int adj) +{ + char *p = cxt->rgn[FT_STRUCT].start; + char *next; + struct ft_atom atom; + int off; + + while ((next = ft_next(cxt, p, &atom)) != NULL) { + if (atom.tag == OF_DT_PROP) { + off = be32_to_cpu(*(u32 *) (p + 8)); + *(u32 *) (p + 8) = cpu_to_be32(off + adj); + } + p = next; + } +} + +/* start construction of the flat OF tree from scratch */ +void ft_begin(struct ft_cxt *cxt, void *blob, unsigned int max_size, + void *(*realloc_fn) (void *, unsigned long)) +{ + struct boot_param_header *bph = blob; + char *p; + struct ft_reserve *pres; + + /* clear the cxt */ + memset(cxt, 0, sizeof(*cxt)); + + cxt->bph = bph; + cxt->max_size = max_size; + cxt->realloc = realloc_fn; + cxt->isordered = 1; + + /* zero everything in the header area */ + memset(bph, 0, sizeof(*bph)); + + bph->magic = cpu_to_be32(OF_DT_HEADER); + bph->version = cpu_to_be32(0x10); + bph->last_comp_version = cpu_to_be32(0x10); + + /* start pointers */ + cxt->rgn[FT_RSVMAP].start = p = blob + HDR_SIZE; + cxt->rgn[FT_RSVMAP].size = sizeof(struct ft_reserve); + pres = (struct ft_reserve *)p; + cxt->rgn[FT_STRUCT].start = p += sizeof(struct ft_reserve); + cxt->rgn[FT_STRUCT].size = 4; + cxt->rgn[FT_STRINGS].start = blob + max_size; + cxt->rgn[FT_STRINGS].size = 0; + + /* init rsvmap and struct */ + pres->start = 0; + pres->len = 0; + *(u32 *) p = cpu_to_be32(OF_DT_END); + + cxt->str_anchor = blob; +} + +/* open up an existing blob to be examined or modified */ +int ft_open(struct ft_cxt *cxt, void *blob, unsigned int max_size, + unsigned int max_find_device, + void *(*realloc_fn) (void *, unsigned long)) +{ + struct boot_param_header *bph = blob; + + /* can't cope with version < 16 */ + if (be32_to_cpu(bph->version) < 16) + return -1; + + /* clear the cxt */ + memset(cxt, 0, sizeof(*cxt)); + + /* alloc node_tbl to track node ptrs returned by ft_find_device */ + ++max_find_device; + cxt->node_tbl = realloc_fn(NULL, max_find_device * sizeof(char *)); + if (!cxt->node_tbl) + return -1; + memset(cxt->node_tbl, 0, max_find_device * sizeof(char *)); + cxt->node_max = max_find_device; + cxt->nodes_used = 1; /* don't use idx 0 b/c looks like NULL */ + + cxt->bph = bph; + cxt->max_size = max_size; + cxt->realloc = realloc_fn; + + cxt->rgn[FT_RSVMAP].start = blob + be32_to_cpu(bph->off_mem_rsvmap); + cxt->rgn[FT_RSVMAP].size = rsvmap_size(cxt); + cxt->rgn[FT_STRUCT].start = blob + be32_to_cpu(bph->off_dt_struct); + cxt->rgn[FT_STRUCT].size = struct_size(cxt); + cxt->rgn[FT_STRINGS].start = blob + be32_to_cpu(bph->off_dt_strings); + cxt->rgn[FT_STRINGS].size = be32_to_cpu(bph->dt_strings_size); + /* Leave as '0' to force first ft_make_space call to do a ft_reorder + * and move dt to an area allocated by realloc. + cxt->isordered = ft_ordered(cxt); + */ + + cxt->p = cxt->rgn[FT_STRUCT].start; + cxt->str_anchor = cxt->rgn[FT_STRINGS].start; + + return 0; +} + +/* add a reserver physical area to the rsvmap */ +int ft_add_rsvmap(struct ft_cxt *cxt, u64 physaddr, u64 size) +{ + char *p; + struct ft_reserve *pres; + + p = cxt->rgn[FT_RSVMAP].start + cxt->rgn[FT_RSVMAP].size + - sizeof(struct ft_reserve); + if (!ft_make_space(cxt, &p, FT_RSVMAP, sizeof(struct ft_reserve))) + return -1; + + pres = (struct ft_reserve *)p; + pres->start = cpu_to_be64(physaddr); + pres->len = cpu_to_be64(size); + + return 0; +} + +void ft_begin_tree(struct ft_cxt *cxt) +{ + cxt->p = cxt->rgn[FT_STRUCT].start; +} + +void ft_end_tree(struct ft_cxt *cxt) +{ + struct boot_param_header *bph = cxt->bph; + char *p, *oldstr, *str, *endp; + unsigned long ssize; + int adj; + + if (!cxt->isordered) + return; /* we haven't touched anything */ + + /* adjust string offsets */ + oldstr = cxt->rgn[FT_STRINGS].start; + adj = cxt->str_anchor - oldstr; + if (adj) + adjust_string_offsets(cxt, adj); + + /* make strings end on 8-byte boundary */ + ssize = cxt->rgn[FT_STRINGS].size; + endp = (char *)_ALIGN((unsigned long)cxt->rgn[FT_STRUCT].start + + cxt->rgn[FT_STRUCT].size + ssize, 8); + str = endp - ssize; + + /* move strings down to end of structs */ + memmove(str, oldstr, ssize); + cxt->str_anchor = str; + cxt->rgn[FT_STRINGS].start = str; + + /* fill in header fields */ + p = (char *)bph; + bph->totalsize = cpu_to_be32(endp - p); + bph->off_mem_rsvmap = cpu_to_be32(cxt->rgn[FT_RSVMAP].start - p); + bph->off_dt_struct = cpu_to_be32(cxt->rgn[FT_STRUCT].start - p); + bph->off_dt_strings = cpu_to_be32(cxt->rgn[FT_STRINGS].start - p); + bph->dt_strings_size = cpu_to_be32(ssize); +} + +void *ft_find_device(struct ft_cxt *cxt, const char *srch_path) +{ + char *node; + + /* require absolute path */ + if (srch_path[0] != '/') + return NULL; + node = ft_find_descendent(cxt, cxt->rgn[FT_STRUCT].start, srch_path); + return ft_node_add(cxt, node); +} + +void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path) +{ + struct ft_atom atom; + char *p; + const char *cp, *q; + int cl; + int depth = -1; + int dmatch = 0; + const char *path_comp[FT_MAX_DEPTH]; + + cp = srch_path; + cl = 0; + p = top; + + while ((p = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + ++depth; + if (depth != dmatch) + break; + cxt->genealogy[depth] = atom.data; + cxt->genealogy[depth + 1] = NULL; + if (depth && !(strncmp(atom.name, cp, cl) == 0 + && (atom.name[cl] == '/' + || atom.name[cl] == '\0' + || atom.name[cl] == '@'))) + break; + path_comp[dmatch] = cp; + /* it matches so far, advance to next path component */ + cp += cl; + /* skip slashes */ + while (*cp == '/') + ++cp; + /* we're done if this is the end of the string */ + if (*cp == 0) + return atom.data; + /* look for end of this component */ + q = strchr(cp, '/'); + if (q) + cl = q - cp; + else + cl = strlen(cp); + ++dmatch; + break; + case OF_DT_END_NODE: + if (depth == 0) + return NULL; + if (dmatch > depth) { + --dmatch; + cl = cp - path_comp[dmatch] - 1; + cp = path_comp[dmatch]; + while (cl > 0 && cp[cl - 1] == '/') + --cl; + } + --depth; + break; + } + } + return NULL; +} + +void *ft_get_parent(struct ft_cxt *cxt, const void *phandle) +{ + void *node; + int d; + struct ft_atom atom; + char *p; + + node = ft_node_ph2node(cxt, phandle); + if (node == NULL) + return NULL; + + for (d = 0; cxt->genealogy[d] != NULL; ++d) + if (cxt->genealogy[d] == node) + return cxt->genealogy[d > 0 ? d - 1 : 0]; + + /* have to do it the hard way... */ + p = cxt->rgn[FT_STRUCT].start; + d = 0; + while ((p = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + cxt->genealogy[d] = atom.data; + if (node == atom.data) { + /* found it */ + cxt->genealogy[d + 1] = NULL; + return d > 0 ? cxt->genealogy[d - 1] : node; + } + ++d; + break; + case OF_DT_END_NODE: + --d; + break; + } + } + return NULL; +} + +int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + void *buf, const unsigned int buflen) +{ + struct ft_atom atom; + void *node; + char *p; + int depth; + unsigned int size; + + node = ft_node_ph2node(cxt, phandle); + if (node == NULL) + return -1; + + depth = 0; + p = (char *)node; + + while ((p = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + ++depth; + break; + case OF_DT_PROP: + if ((depth != 1) || strcmp(atom.name, propname)) + break; + size = min(atom.size, buflen); + memcpy(buf, atom.data, size); + return atom.size; + case OF_DT_END_NODE: + if (--depth <= 0) + return -1; + } + } + return -1; +} + +int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + const void *buf, const unsigned int buflen) +{ + struct ft_atom atom; + void *node; + char *p, *next; + int nextra, depth; + + node = ft_node_ph2node(cxt, phandle); + if (node == NULL) + return -1; + + depth = 0; + p = node; + + while ((next = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + ++depth; + break; + case OF_DT_END_NODE: + if (--depth > 0) + break; + /* haven't found the property, insert here */ + cxt->p = p; + return ft_prop(cxt, propname, buf, buflen); + case OF_DT_PROP: + if ((depth != 1) || strcmp(atom.name, propname)) + break; + /* found an existing property, overwrite it */ + nextra = _ALIGN(buflen, 4) - _ALIGN(atom.size, 4); + cxt->p = atom.data; + if (nextra && !ft_make_space(cxt, &cxt->p, FT_STRUCT, + nextra)) + return -1; + *(u32 *) (cxt->p - 8) = cpu_to_be32(buflen); + ft_put_bin(cxt, buf, buflen); + return 0; + } + p = next; + } + return -1; +} + +int ft_del_prop(struct ft_cxt *cxt, const void *phandle, const char *propname) +{ + struct ft_atom atom; + void *node; + char *p, *next; + int size; + + node = ft_node_ph2node(cxt, phandle); + if (node == NULL) + return -1; + + p = node; + while ((next = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + case OF_DT_END_NODE: + return -1; + case OF_DT_PROP: + if (strcmp(atom.name, propname)) + break; + /* found the property, remove it */ + size = 12 + -_ALIGN(atom.size, 4); + cxt->p = p; + if (!ft_make_space(cxt, &cxt->p, FT_STRUCT, -size)) + return -1; + return 0; + } + p = next; + } + return -1; +} + +void *ft_create_node(struct ft_cxt *cxt, const void *parent, const char *path) +{ + struct ft_atom atom; + char *p, *next; + int depth = 0; + + p = cxt->rgn[FT_STRUCT].start; + while ((next = ft_next(cxt, p, &atom)) != NULL) { + switch (atom.tag) { + case OF_DT_BEGIN_NODE: + ++depth; + if (depth == 1 && strcmp(atom.name, path) == 0) + /* duplicate node path, return error */ + return NULL; + break; + case OF_DT_END_NODE: + --depth; + if (depth > 0) + break; + /* end of node, insert here */ + cxt->p = p; + ft_begin_node(cxt, path); + ft_end_node(cxt); + return p; + } + p = next; + } + return NULL; +} diff --git a/arch/powerpc/boot/flatdevtree.h b/arch/powerpc/boot/flatdevtree.h index 761c8dc8400..b9cd9f61f35 100644 --- a/arch/powerpc/boot/flatdevtree.h +++ b/arch/powerpc/boot/flatdevtree.h @@ -17,7 +17,7 @@ #ifndef FLATDEVTREE_H #define FLATDEVTREE_H -#include "types.h" +#include "flatdevtree_env.h" /* Definitions used by the flattened device tree */ #define OF_DT_HEADER 0xd00dfeed /* marker */ @@ -43,4 +43,64 @@ struct boot_param_header { u32 dt_strings_size; /* size of the DT strings block */ }; +struct ft_reserve { + u64 start; + u64 len; +}; + +struct ft_region { + char *start; + unsigned long size; +}; + +enum ft_rgn_id { + FT_RSVMAP, + FT_STRUCT, + FT_STRINGS, + FT_N_REGION +}; + +#define FT_MAX_DEPTH 50 + +struct ft_cxt { + struct boot_param_header *bph; + int max_size; /* maximum size of tree */ + int isordered; /* everything in standard order */ + void *(*realloc)(void *, unsigned long); + char *str_anchor; + char *p; /* current insertion point in structs */ + struct ft_region rgn[FT_N_REGION]; + void *genealogy[FT_MAX_DEPTH+1]; + char **node_tbl; + unsigned int node_max; + unsigned int nodes_used; +}; + +int ft_begin_node(struct ft_cxt *cxt, const char *name); +void ft_end_node(struct ft_cxt *cxt); + +void ft_begin_tree(struct ft_cxt *cxt); +void ft_end_tree(struct ft_cxt *cxt); + +void ft_nop(struct ft_cxt *cxt); +int ft_prop(struct ft_cxt *cxt, const char *name, + const void *data, unsigned int sz); +int ft_prop_str(struct ft_cxt *cxt, const char *name, const char *str); +int ft_prop_int(struct ft_cxt *cxt, const char *name, unsigned int val); +void ft_begin(struct ft_cxt *cxt, void *blob, unsigned int max_size, + void *(*realloc_fn)(void *, unsigned long)); +int ft_open(struct ft_cxt *cxt, void *blob, unsigned int max_size, + unsigned int max_find_device, + void *(*realloc_fn)(void *, unsigned long)); +int ft_add_rsvmap(struct ft_cxt *cxt, u64 physaddr, u64 size); + +void ft_dump_blob(const void *bphp); +void ft_merge_blob(struct ft_cxt *cxt, void *blob); +void *ft_find_device(struct ft_cxt *cxt, const char *srch_path); +void *ft_find_descendent(struct ft_cxt *cxt, void *top, const char *srch_path); +int ft_get_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + void *buf, const unsigned int buflen); +int ft_set_prop(struct ft_cxt *cxt, const void *phandle, const char *propname, + const void *buf, const unsigned int buflen); + #endif /* FLATDEVTREE_H */ diff --git a/arch/powerpc/boot/flatdevtree_env.h b/arch/powerpc/boot/flatdevtree_env.h new file mode 100644 index 00000000000..83bc1c71883 --- /dev/null +++ b/arch/powerpc/boot/flatdevtree_env.h @@ -0,0 +1,47 @@ +/* + * This file adds the header file glue so that the shared files + * flatdevicetree.[ch] can compile and work in the powerpc bootwrapper. + * + * strncmp & strchr copied from <file:lib/strings.c> + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Maintained by: Mark A. Greer <mgreer@mvista.com> + */ +#ifndef _PPC_BOOT_FLATDEVTREE_ENV_H_ +#define _PPC_BOOT_FLATDEVTREE_ENV_H_ + +#include <stdarg.h> +#include <stddef.h> +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "ops.h" + +#define be16_to_cpu(x) (x) +#define cpu_to_be16(x) (x) +#define be32_to_cpu(x) (x) +#define cpu_to_be32(x) (x) +#define be64_to_cpu(x) (x) +#define cpu_to_be64(x) (x) + +static inline int strncmp(const char *cs, const char *ct, size_t count) +{ + signed char __res = 0; + + while (count) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count--; + } + return __res; +} + +static inline char *strchr(const char *s, int c) +{ + for (; *s != (char)c; ++s) + if (*s == '\0') + return NULL; + return (char *)s; +} + +#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 00000000000..04da38fa477 --- /dev/null +++ b/arch/powerpc/boot/flatdevtree_misc.c @@ -0,0 +1,51 @@ +/* + * This file does the necessary interface mapping between the bootwrapper + * device tree operations and the interface provided by shared source + * files flatdevicetree.[ch]. + * + * Author: Mark A. Greer <mgreer@mvista.com> + * + * 2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include <stddef.h> +#include "flatdevtree.h" +#include "ops.h" + +static struct ft_cxt cxt; + +static void *ft_finddevice(const char *name) +{ + return ft_find_device(&cxt, name); +} + +static int ft_getprop(const void *phandle, const char *propname, void *buf, + const int buflen) +{ + return ft_get_prop(&cxt, phandle, propname, buf, buflen); +} + +static int ft_setprop(const void *phandle, const char *propname, + const void *buf, const int buflen) +{ + return ft_set_prop(&cxt, phandle, propname, buf, buflen); +} + +static unsigned long ft_finalize(void) +{ + ft_end_tree(&cxt); + return (unsigned long)cxt.bph; +} + +int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device) +{ + dt_ops.finddevice = ft_finddevice; + dt_ops.getprop = ft_getprop; + dt_ops.setprop = ft_setprop; + dt_ops.finalize = ft_finalize; + + return ft_open(&cxt, dt_blob, max_size, max_find_device, + platform_ops.realloc); +} diff --git a/arch/powerpc/boot/io.h b/arch/powerpc/boot/io.h new file mode 100644 index 00000000000..32974ed49e0 --- /dev/null +++ b/arch/powerpc/boot/io.h @@ -0,0 +1,53 @@ +#ifndef _IO_H +#define __IO_H +/* + * Low-level I/O routines. + * + * Copied from <file:include/asm-powerpc/io.h> (which has no copyright) + */ +static inline int in_8(const volatile unsigned char *addr) +{ + int ret; + + __asm__ __volatile__("lbz%U1%X1 %0,%1; twi 0,%0,0; isync" + : "=r" (ret) : "m" (*addr)); + return ret; +} + +static inline void out_8(volatile unsigned char *addr, int val) +{ + __asm__ __volatile__("stb%U0%X0 %1,%0; sync" + : "=m" (*addr) : "r" (val)); +} + +static inline unsigned in_le32(const volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("lwbrx %0,0,%1; twi 0,%0,0; isync" + : "=r" (ret) : "r" (addr), "m" (*addr)); + return ret; +} + +static inline unsigned in_be32(const volatile unsigned *addr) +{ + unsigned ret; + + __asm__ __volatile__("lwz%U1%X1 %0,%1; twi 0,%0,0; isync" + : "=r" (ret) : "m" (*addr)); + return ret; +} + +static inline void out_le32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stwbrx %1,0,%2; sync" : "=m" (*addr) + : "r" (val), "r" (addr)); +} + +static inline void out_be32(volatile unsigned *addr, int val) +{ + __asm__ __volatile__("stw%U0%X0 %1,%0; sync" + : "=m" (*addr) : "r" (val)); +} + +#endif /* _IO_H */ diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c index d719bb9333d..6f6b50d238b 100644 --- a/arch/powerpc/boot/main.c +++ b/arch/powerpc/boot/main.c @@ -27,6 +27,8 @@ extern char _vmlinux_start[]; extern char _vmlinux_end[]; extern char _initrd_start[]; extern char _initrd_end[]; +extern char _dtb_start[]; +extern char _dtb_end[]; struct addr_range { unsigned long addr; @@ -167,7 +169,7 @@ static int is_elf32(void *hdr) return 1; } -static void prep_kernel(unsigned long *a1, unsigned long *a2) +static void prep_kernel(unsigned long a1, unsigned long a2) { int len; @@ -203,11 +205,14 @@ static void prep_kernel(unsigned long *a1, unsigned long *a2) } /* - * Now we try to alloc memory for the initrd (and copy it there) + * Now find the initrd + * + * First see if we have an image attached to us. If so + * allocate memory for it and copy it there. */ initrd.size = (unsigned long)(_initrd_end - _initrd_start); initrd.memsize = initrd.size; - if ( initrd.size > 0 ) { + if (initrd.size > 0) { printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd.size); initrd.addr = (unsigned long)malloc((u32)initrd.size); @@ -216,8 +221,6 @@ static void prep_kernel(unsigned long *a1, unsigned long *a2) "ramdisk !\n\r"); exit(); } - *a1 = initrd.addr; - *a2 = initrd.size; printf("initial ramdisk moving 0x%lx <- 0x%lx " "(0x%lx bytes)\n\r", initrd.addr, (unsigned long)_initrd_start, initrd.size); @@ -225,6 +228,12 @@ static void prep_kernel(unsigned long *a1, unsigned long *a2) initrd.size); printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd.addr)); + } else if (a2 != 0) { + /* Otherwise, see if yaboot or another loader gave us an initrd */ + initrd.addr = a1; + initrd.memsize = initrd.size = a2; + printf("Using loader supplied initrd at 0x%lx (0x%lx bytes)\n\r", + initrd.addr, initrd.size); } /* Eventually gunzip the kernel */ @@ -250,10 +259,6 @@ static void prep_kernel(unsigned long *a1, unsigned long *a2) flush_cache((void *)vmlinux.addr, vmlinux.size); } -void __attribute__ ((weak)) ft_init(void *dt_blob) -{ -} - /* A buffer that may be edited by tools operating on a zImage binary so as to * edit the command line passed to vmlinux (by setting /chosen/bootargs). * 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) setprop(devp, "bootargs", buf, strlen(buf) + 1); } -/* Section where ft can be tacked on after zImage is built */ -union blobspace { - struct boot_param_header hdr; - char space[8*1024]; -} dt_blob __attribute__((__section__("__builtin_ft"))); - struct platform_ops platform_ops; struct dt_ops dt_ops; struct console_ops console_ops; void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) { - int have_dt = 0; kernel_entry_t kentry; char cmdline[COMMAND_LINE_SIZE]; + unsigned long ft_addr = 0; memset(__bss_start, 0, _end - __bss_start); memset(&platform_ops, 0, sizeof(platform_ops)); memset(&dt_ops, 0, sizeof(dt_ops)); memset(&console_ops, 0, sizeof(console_ops)); - /* Override the dt_ops and device tree if there was an flat dev - * tree attached to the zImage. - */ - if (dt_blob.hdr.magic == OF_DT_HEADER) { - have_dt = 1; - ft_init(&dt_blob); - } - - if (platform_init(promptr)) + if (platform_init(promptr, _dtb_start, _dtb_end)) exit(); if (console_ops.open && (console_ops.open() < 0)) exit(); @@ -324,7 +315,7 @@ void start(unsigned long a1, unsigned long a2, void *promptr, void *sp) printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r", _start, sp); - prep_kernel(&a1, &a2); + prep_kernel(a1, a2); /* If cmdline came from zimage wrapper or if we can edit the one * 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) set_cmdline(cmdline); } + printf("Finalizing device tree..."); + if (dt_ops.finalize) + ft_addr = dt_ops.finalize(); + if (ft_addr) + printf(" flat tree at 0x%lx\n\r", ft_addr); + else + printf(" using OF tree (promptr=%p)\n\r", promptr); + if (console_ops.close) console_ops.close(); kentry = (kernel_entry_t) vmlinux.addr; - if (have_dt) - kentry(dt_ops.ft_addr(), 0, NULL); + if (ft_addr) + kentry(ft_addr, 0, NULL); else /* XXX initrd addr/size should be passed in properties */ - kentry(a1, a2, promptr); + kentry(initrd.addr, initrd.size, promptr); /* console closed so printf below may not work */ 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 00000000000..4cb89299365 --- /dev/null +++ b/arch/powerpc/boot/mktree.c @@ -0,0 +1,152 @@ +/* + * Makes a tree bootable image for IBM Evaluation boards. + * Basically, just take a zImage, skip the ELF header, and stuff + * a 32 byte header on the front. + * + * We use htonl, which is a network macro, to make sure we're doing + * The Right Thing on an LE machine. It's non-obvious, but it should + * work on anything BSD'ish. + */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <netinet/in.h> +#ifdef __sun__ +#include <inttypes.h> +#else +#include <stdint.h> +#endif + +/* This gets tacked on the front of the image. There are also a few + * bytes allocated after the _start label used by the boot rom (see + * head.S for details). + */ +typedef struct boot_block { + uint32_t bb_magic; /* 0x0052504F */ + uint32_t bb_dest; /* Target address of the image */ + uint32_t bb_num_512blocks; /* Size, rounded-up, in 512 byte blks */ + uint32_t bb_debug_flag; /* Run debugger or image after load */ + uint32_t bb_entry_point; /* The image address to start */ + uint32_t bb_checksum; /* 32 bit checksum including header */ + uint32_t reserved[2]; +} boot_block_t; + +#define IMGBLK 512 +char tmpbuf[IMGBLK]; + +int main(int argc, char *argv[]) +{ + int in_fd, out_fd; + int nblks, i; + uint cksum, *cp; + struct stat st; + boot_block_t bt; + + if (argc < 3) { + fprintf(stderr, "usage: %s <zImage-file> <boot-image> [entry-point]\n",argv[0]); + exit(1); + } + + if (stat(argv[1], &st) < 0) { + perror("stat"); + exit(2); + } + + nblks = (st.st_size + IMGBLK) / IMGBLK; + + bt.bb_magic = htonl(0x0052504F); + + /* If we have the optional entry point parameter, use it */ + if (argc == 4) + bt.bb_dest = bt.bb_entry_point = htonl(strtoul(argv[3], NULL, 0)); + else + bt.bb_dest = bt.bb_entry_point = htonl(0x500000); + + /* We know these from the linker command. + * ...and then move it up into memory a little more so the + * relocation can happen. + */ + bt.bb_num_512blocks = htonl(nblks); + bt.bb_debug_flag = 0; + + bt.bb_checksum = 0; + + /* To be neat and tidy :-). + */ + bt.reserved[0] = 0; + bt.reserved[1] = 0; + + if ((in_fd = open(argv[1], O_RDONLY)) < 0) { + perror("zImage open"); + exit(3); + } + + if ((out_fd = open(argv[2], (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) { + perror("bootfile open"); + exit(3); + } + + cksum = 0; + cp = (void *)&bt; + for (i=0; i<sizeof(bt)/sizeof(uint); i++) + cksum += *cp++; + + /* Assume zImage is an ELF file, and skip the 64K header. + */ + if (read(in_fd, tmpbuf, IMGBLK) != IMGBLK) { + fprintf(stderr, "%s is too small to be an ELF image\n", + argv[1]); + exit(4); + } + + if ((*(uint *)tmpbuf) != htonl(0x7f454c46)) { + fprintf(stderr, "%s is not an ELF image\n", argv[1]); + exit(4); + } + + if (lseek(in_fd, (64 * 1024), SEEK_SET) < 0) { + fprintf(stderr, "%s failed to seek in ELF image\n", argv[1]); + exit(4); + } + + nblks -= (64 * 1024) / IMGBLK; + + /* And away we go...... + */ + if (write(out_fd, &bt, sizeof(bt)) != sizeof(bt)) { + perror("boot-image write"); + exit(5); + } + + while (nblks-- > 0) { + if (read(in_fd, tmpbuf, IMGBLK) < 0) { + perror("zImage read"); + exit(5); + } + cp = (uint *)tmpbuf; + for (i=0; i<sizeof(tmpbuf)/sizeof(uint); i++) + cksum += *cp++; + if (write(out_fd, tmpbuf, sizeof(tmpbuf)) != sizeof(tmpbuf)) { + perror("boot-image write"); + exit(5); + } + } + + /* rewrite the header with the computed checksum. + */ + bt.bb_checksum = htonl(cksum); + if (lseek(out_fd, 0, SEEK_SET) < 0) { + perror("rewrite seek"); + exit(1); + } + if (write(out_fd, &bt, sizeof(bt)) != sizeof(bt)) { + perror("boot-image rewrite"); + exit(1); + } + + exit(0); +} diff --git a/arch/powerpc/boot/ns16550.c b/arch/powerpc/boot/ns16550.c new file mode 100644 index 00000000000..1ffe72e35cd --- /dev/null +++ b/arch/powerpc/boot/ns16550.c @@ -0,0 +1,74 @@ +/* + * 16550 serial console support. + * + * Original copied from <file:arch/ppc/boot/common/ns16550.c> + * (which had no copyright) + * Modifications: 2006 (c) MontaVista Software, Inc. + * + * Modified by: Mark A. Greer <mgreer@mvista.com> + */ +#include <stdarg.h> +#include <stddef.h> +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "io.h" +#include "ops.h" + +#define UART_DLL 0 /* Out: Divisor Latch Low */ +#define UART_DLM 1 /* Out: Divisor Latch High */ +#define UART_FCR 2 /* Out: FIFO Control Register */ +#define UART_LCR 3 /* Out: Line Control Register */ +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_LSR 5 /* In: Line Status Register */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_MSR 6 /* In: Modem Status Register */ +#define UART_SCR 7 /* I/O: Scratch Register */ + +static unsigned char *reg_base; +static u32 reg_shift; + +static int ns16550_open(void) +{ + out_8(reg_base + (UART_FCR << reg_shift), 0x06); + return 0; +} + +static void ns16550_putc(unsigned char c) +{ + while ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_THRE) == 0); + out_8(reg_base, c); +} + +static unsigned char ns16550_getc(void) +{ + while ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_DR) == 0); + return in_8(reg_base); +} + +static u8 ns16550_tstc(void) +{ + return ((in_8(reg_base + (UART_LSR << reg_shift)) & UART_LSR_DR) != 0); +} + +int ns16550_console_init(void *devp, struct serial_console_data *scdp) +{ + int n; + + n = getprop(devp, "virtual-reg", ®_base, sizeof(reg_base)); + if (n != sizeof(reg_base)) + return -1; + + n = getprop(devp, "reg-shift", ®_shift, sizeof(reg_shift)); + if (n != sizeof(reg_shift)) + reg_shift = 0; + + scdp->open = ns16550_open; + scdp->putc = ns16550_putc; + scdp->getc = ns16550_getc; + scdp->tstc = ns16550_tstc; + scdp->close = NULL; + + return 0; +} diff --git a/arch/powerpc/boot/of.c b/arch/powerpc/boot/of.c index 3a71845afc6..0182f384f3e 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) call_prom("write", 3, 1, of_stdout_handle, buf, len); } -int platform_init(void *promptr) +int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end) { - platform_ops.fixups = NULL; platform_ops.image_hdr = of_image_hdr; platform_ops.malloc = of_try_claim; - platform_ops.free = NULL; platform_ops.exit = of_exit; dt_ops.finddevice = of_finddevice; dt_ops.getprop = of_getprop; dt_ops.setprop = of_setprop; - dt_ops.translate_addr = NULL; console_ops.open = of_console_open; console_ops.write = of_console_write; - console_ops.edit_cmdline = NULL; - console_ops.close = NULL; - console_ops.data = NULL; prom = (int (*)(void *))promptr; return 0; diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h index 135eb4bb03b..8abb6516bb7 100644 --- a/arch/powerpc/boot/ops.h +++ b/arch/powerpc/boot/ops.h @@ -22,7 +22,8 @@ struct platform_ops { void (*fixups)(void); void (*image_hdr)(const void *); void * (*malloc)(u32 size); - void (*free)(void *ptr, u32 size); + void (*free)(void *ptr); + void * (*realloc)(void *ptr, unsigned long size); void (*exit)(void); }; extern struct platform_ops platform_ops; @@ -30,13 +31,11 @@ extern struct platform_ops platform_ops; /* Device Tree operations */ struct dt_ops { void * (*finddevice)(const char *name); - int (*getprop)(const void *node, const char *name, void *buf, + int (*getprop)(const void *phandle, const char *name, void *buf, const int buflen); - int (*setprop)(const void *node, const char *name, + int (*setprop)(const void *phandle, const char *name, const void *buf, const int buflen); - u64 (*translate_addr)(const char *path, const u32 *in_addr, - const u32 addr_len); - unsigned long (*ft_addr)(void); + unsigned long (*finalize)(void); }; extern struct dt_ops dt_ops; @@ -59,10 +58,13 @@ struct serial_console_data { void (*close)(void); }; -extern int platform_init(void *promptr); -extern void simple_alloc_init(void); -extern void ft_init(void *dt_blob); -extern int serial_console_init(void); +int platform_init(void *promptr, char *dt_blob_start, char *dt_blob_end); +int ft_init(void *dt_blob, unsigned int max_size, unsigned int max_find_device); +int serial_console_init(void); +int ns16550_console_init(void *devp, struct serial_console_data *scdp); +void *simple_alloc_init(char *base, u32 heap_size, u32 granularity, + u32 max_allocs); + static inline void *finddevice(const char *name) { @@ -84,10 +86,10 @@ static inline void *malloc(u32 size) return (platform_ops.malloc) ? platform_ops.malloc(size) : NULL; } -static inline void free(void *ptr, u32 size) +static inline void free(void *ptr) { if (platform_ops.free) - platform_ops.free(ptr, size); + platform_ops.free(ptr); } static inline void exit(void) diff --git a/arch/powerpc/boot/serial.c b/arch/powerpc/boot/serial.c new file mode 100644 index 00000000000..e8de4cf59be --- /dev/null +++ b/arch/powerpc/boot/serial.c @@ -0,0 +1,142 @@ +/* + * Generic serial console support + * + * Author: Mark A. Greer <mgreer@mvista.com> + * + * Code in serial_edit_cmdline() copied from <file:arch/ppc/boot/simple/misc.c> + * and was written by Matt Porter <mporter@kernel.crashing.org>. + * + * 2001,2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include <stdarg.h> +#include <stddef.h> +#include "types.h" +#include "string.h" +#include "stdio.h" +#include "io.h" +#include "ops.h" + +extern void udelay(long delay); + +static int serial_open(void) +{ + struct serial_console_data *scdp = console_ops.data; + return scdp->open(); +} + +static void serial_write(char *buf, int len) +{ + struct serial_console_data *scdp = console_ops.data; + + while (*buf != '\0') + scdp->putc(*buf++); +} + +static void serial_edit_cmdline(char *buf, int len) +{ + int timer = 0, count; + char ch, *cp; + struct serial_console_data *scdp = console_ops.data; + + cp = buf; + count = strlen(buf); + cp = &buf[count]; + count++; + + while (timer++ < 5*1000) { + if (scdp->tstc()) { + while (((ch = scdp->getc()) != '\n') && (ch != '\r')) { + /* Test for backspace/delete */ + if ((ch == '\b') || (ch == '\177')) { + if (cp != buf) { + cp--; + count--; + printf("\b \b"); + } + /* Test for ^x/^u (and wipe the line) */ + } else if ((ch == '\030') || (ch == '\025')) { + while (cp != buf) { + cp--; + count--; + printf("\b \b"); + } + } else if (count < len) { + *cp++ = ch; + count++; + scdp->putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; +} + +static void serial_close(void) +{ + struct serial_console_data *scdp = console_ops.data; + + if (scdp->close) + scdp->close(); +} + +static void *serial_get_stdout_devp(void) +{ + void *devp; + char devtype[MAX_PROP_LEN]; + char path[MAX_PATH_LEN]; + + devp = finddevice("/chosen"); + if (devp == NULL) + goto err_out; + + if (getprop(devp, "linux,stdout-path", path, MAX_PATH_LEN) > 0) { + devp = finddevice(path); + if (devp == NULL) + goto err_out; + + if ((getprop(devp, "device_type", devtype, sizeof(devtype)) > 0) + && !strcmp(devtype, "serial")) + return devp; + } +err_out: + return NULL; +} + +static struct serial_console_data serial_cd; + +/* Node's "compatible" property determines which serial driver to use */ +int serial_console_init(void) +{ + void *devp; + int rc = -1; + char compat[MAX_PROP_LEN]; + + devp = serial_get_stdout_devp(); + if (devp == NULL) + goto err_out; + + if (getprop(devp, "compatible", compat, sizeof(compat)) < 0) + goto err_out; + + if (!strcmp(compat, "ns16550")) + rc = ns16550_console_init(devp, &serial_cd); + + /* Add other serial console driver calls here */ + + if (!rc) { + console_ops.open = serial_open; + console_ops.write = serial_write; + console_ops.edit_cmdline = serial_edit_cmdline; + console_ops.close = serial_close; + console_ops.data = &serial_cd; + + return 0; + } +err_out: + return -1; +} diff --git a/arch/powerpc/boot/simple_alloc.c b/arch/powerpc/boot/simple_alloc.c new file mode 100644 index 00000000000..cfe3a7505ba --- /dev/null +++ b/arch/powerpc/boot/simple_alloc.c @@ -0,0 +1,149 @@ +/* + * Implement primitive realloc(3) functionality. + * + * Author: Mark A. Greer <mgreer@mvista.com> + * + * 2006 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <stddef.h> +#include "types.h" +#include "page.h" +#include "string.h" +#include "ops.h" + +#define ENTRY_BEEN_USED 0x01 +#define ENTRY_IN_USE 0x02 + +static struct alloc_info { + u32 flags; + u32 base; + u32 size; +} *alloc_tbl; + +static u32 tbl_entries; +static u32 alloc_min; +static u32 next_base; +static u32 space_left; + +/* + * First time an entry is used, its base and size are set. + * An entry can be freed and re-malloc'd but its base & size don't change. + * Should be smart enough for needs of bootwrapper. + */ +static void *simple_malloc(u32 size) +{ + u32 i; + struct alloc_info *p = alloc_tbl; + + if (size == 0) + goto err_out; + + size = _ALIGN_UP(size, alloc_min); + + for (i=0; i<tbl_entries; i++, p++) + if (!(p->flags & ENTRY_BEEN_USED)) { /* never been used */ + if (size <= space_left) { + p->base = next_base; + p->size = size; + p->flags = ENTRY_BEEN_USED | ENTRY_IN_USE; + next_base += size; + space_left -= size; + return (void *)p->base; + } + goto err_out; /* not enough space left */ + } + /* reuse an entry keeping same base & size */ + else if (!(p->flags & ENTRY_IN_USE) && (size <= p->size)) { + p->flags |= ENTRY_IN_USE; + return (void *)p->base; + } +err_out: + return NULL; +} + +static struct alloc_info *simple_find_entry(void *ptr) +{ + u32 i; + struct alloc_info *p = alloc_tbl; + + for (i=0; i<tbl_entries; i++,p++) { + if (!(p->flags & ENTRY_BEEN_USED)) + break; + if ((p->flags & ENTRY_IN_USE) && (p->base == (u32)ptr)) + return p; + } + return NULL; +} + +static void simple_free(void *ptr) +{ + struct alloc_info *p = simple_find_entry(ptr); + + if (p != NULL) + p->flags &= ~ENTRY_IN_USE; +} + +/* + * Change size of area pointed to by 'ptr' to 'size'. + * If 'ptr' is NULL, then its a malloc(). If 'size' is 0, then its a free(). + * 'ptr' must be NULL or a pointer to a non-freed area previously returned by + * simple_realloc() or simple_malloc(). + */ +static void *simple_realloc(void *ptr, unsigned long size) +{ + struct alloc_info *p; + void *new; + + if (size == 0) { + simple_free(ptr); + return NULL; + } + + if (ptr == NULL) + return simple_malloc(size); + + p = simple_find_entry(ptr); + if (p == NULL) /* ptr not from simple_malloc/simple_realloc */ + return NULL; + if (size <= p->size) /* fits in current block */ + return ptr; + + new = simple_malloc(size); + memcpy(new, ptr, p->size); + simple_free(ptr); + return new; +} + +/* + * Returns addr of first byte after heap so caller can see if it took + * too much space. If so, change args & try again. + */ +void *simple_alloc_init(char *base, u32 heap_size, u32 granularity, + u32 max_allocs) +{ + u32 heap_base, tbl_size; + + heap_size = _ALIGN_UP(heap_size, granularity); + alloc_min = granularity; + tbl_entries = max_allocs; + + tbl_size = tbl_entries * sizeof(struct alloc_info); + + alloc_tbl = (struct alloc_info *)_ALIGN_UP((unsigned long)base, 8); + memset(alloc_tbl, 0, tbl_size); + + heap_base = _ALIGN_UP((u32)alloc_tbl + tbl_size, alloc_min); + + next_base = heap_base; + space_left = heap_size; + + platform_ops.malloc = simple_malloc; + platform_ops.free = simple_free; + platform_ops.realloc = simple_realloc; + + return (void *)(heap_base + heap_size); +} diff --git a/arch/powerpc/boot/stdio.c b/arch/powerpc/boot/stdio.c index 6d5f6382e1c..0a9feeb9834 100644 --- a/arch/powerpc/boot/stdio.c +++ b/arch/powerpc/boot/stdio.c @@ -320,6 +320,7 @@ printf(const char *fmt, ...) va_start(args, fmt); n = vsprintf(sprint_buf, fmt, args); va_end(args); - console_ops.write(sprint_buf, n); + if (console_ops.write) + console_ops.write(sprint_buf, n); return n; } diff --git a/arch/powerpc/boot/util.S b/arch/powerpc/boot/util.S new file mode 100644 index 00000000000..427ddfc1199 --- /dev/null +++ b/arch/powerpc/boot/util.S @@ -0,0 +1,88 @@ +/* + * Copied from <file:arch/powerpc/kernel/misc_32.S> + * + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * + * kexec bits: + * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com> + * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ +#include "ppc_asm.h" + +#define SPRN_PVR 0x11F /* Processor Version Register */ + + .text + +/* udelay (on non-601 processors) needs to know the period of the + * timebase in nanoseconds. This used to be hardcoded to be 60ns + * (period of 66MHz/4). Now a variable is used that is initialized to + * 60 for backward compatibility, but it can be overridden as necessary + * with code something like this: + * extern unsigned long timebase_period_ns; + * timebase_period_ns = 1000000000 / bd->bi_tbfreq; + */ + .data + .globl timebase_period_ns +timebase_period_ns: + .long 60 + + .text +/* + * Delay for a number of microseconds + */ + .globl udelay +udelay: + mfspr r4,SPRN_PVR + srwi r4,r4,16 + cmpwi 0,r4,1 /* 601 ? */ + bne .udelay_not_601 +00: li r0,86 /* Instructions / microsecond? */ + mtctr r0 +10: addi r0,r0,0 /* NOP */ + bdnz 10b + subic. r3,r3,1 + bne 00b + blr + +.udelay_not_601: + mulli r4,r3,1000 /* nanoseconds */ + /* Change r4 to be the number of ticks using: + * (nanoseconds + (timebase_period_ns - 1 )) / timebase_period_ns + * timebase_period_ns defaults to 60 (16.6MHz) */ + mflr r5 + bl 0f +0: mflr r6 + mtlr r5 + lis r5,0b@ha + addi r5,r5,0b@l + subf r5,r5,r6 /* In case we're relocated */ + addis r5,r5,timebase_period_ns@ha + lwz r5,timebase_period_ns@l(r5) + add r4,r4,r5 + addi r4,r4,-1 + divw r4,r4,r5 /* BUS ticks */ +1: mftbu r5 + mftb r6 + mftbu r7 + cmpw 0,r5,r7 + bne 1b /* Get [synced] base time */ + addc r9,r6,r4 /* Compute end time */ + addze r8,r5 +2: mftbu r5 + cmpw 0,r5,r8 + blt 2b + bgt 3f + mftb r6 + cmpw 0,r6,r9 + blt 2b +3: blr diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index b5fb1fee76f..024e4d425c5 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -184,6 +184,9 @@ fi if [ -n "$dtb" ]; then addsec $tmp "$dtb" .kernel:dtb + if [ -n "$dts" ]; then + rm $dtb + fi fi if [ "$platform" != "miboot" ]; then diff --git a/arch/powerpc/boot/zImage.coff.lds.S b/arch/powerpc/boot/zImage.coff.lds.S index 05f32388b95..a360905e542 100644 --- a/arch/powerpc/boot/zImage.coff.lds.S +++ b/arch/powerpc/boot/zImage.coff.lds.S @@ -21,6 +21,10 @@ SECTIONS *(.got2) __got2_end = .; + _dtb_start = .; + *(.kernel:dtb) + _dtb_end = .; + _vmlinux_start = .; *(.kernel:vmlinux.strip) _vmlinux_end = .; |