ARM: 6617/1: mmc, Add zboot from MMC support for SuperH Mobile ARM
Simon Horman [Tue, 11 Jan 2011 03:01:08 +0000 (04:01 +0100)]
This allows a ROM-able zImage to be written to MMC and
for SuperH Mobile ARM to boot directly from the MMCIF
hardware block.

This is achieved by the MaskROM loading the first portion
of the image into MERAM and then jumping to it. This portion
contains loader code which copies the entire image to SDRAM
and jumps to it. From there the zImage boot code proceeds
as normal, uncompressing the image into its final location
and then jumping to it.

Cc: Magnus Damm <magnus.damm@gmail.com>

Russell, please consider merging this for 2.6.38.

This patch depends on:
* "mmc, sh: Move MMCIF_PROGRESS_* into sh_mmcif.h"
  which will be merged though Paul Mundt's rmobile sh-2.6.
  The absence of this patch will break the build if
  the (new) CONFIG_ZBOOT_ROM_MMCIF option is set.
  There are no subtle side-effects.

v2:
Addressed comments by Magnus Damm
* Fix copyright in vrl4.c
* Fix use of #define CONFIG_ZBOOT_ROM_MMCIF in mmcif-sh7372.c
* Initialise LED GPIO lines in head-ap4evb.txt instead of mmcif-sh7372.c
  as this is considered board-specific.

v3:
Addressed comments made in person by Magnus Damm
* Move mmcif_loader to be earlier in the image and
  reduce the number of blocks of boot program loaded by the MaskRom
  from 40 to 8 accordingly.
* Move LED GPIO initialisation into mmcif_progress_init
  - This leaves the partner jet script unbloated
Other
* inline mmcif_update_progress so it is a static inline in a header file

v4:
* Use htole16() and htole32() in v4rl.c to ensure
  that the output is little endian

v5:
Addressed comments by Russell King
* Simplify assembly code
* Jump to code rather than an address <- bug fix
* Use (void __iomem *) as appropriate
Roll in mackerel support
* This was previously a separate patch, only because of the order
  in which this code was developed
Signed-off-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Documentation/arm/SH-Mobile/Makefile [new file with mode: 0644]
Documentation/arm/SH-Mobile/vrl4.c [new file with mode: 0644]
Documentation/arm/SH-Mobile/zboot-rom-mmcif.txt [new file with mode: 0644]
arch/arm/Kconfig
arch/arm/boot/compressed/Makefile
arch/arm/boot/compressed/head-shmobile.S
arch/arm/boot/compressed/mmcif-sh7372.c [new file with mode: 0644]
arch/arm/mach-shmobile/include/mach/mmcif-ap4eb.h [new file with mode: 0644]
arch/arm/mach-shmobile/include/mach/mmcif-mackerel.h [new file with mode: 0644]
arch/arm/mach-shmobile/include/mach/mmcif.h [new file with mode: 0644]

diff --git a/Documentation/arm/SH-Mobile/Makefile b/Documentation/arm/SH-Mobile/Makefile
new file mode 100644 (file)
index 0000000..8771d83
--- /dev/null
@@ -0,0 +1,8 @@
+BIN := vrl4
+
+.PHONY: all
+all: $(BIN)
+
+.PHONY: clean
+clean:
+       rm -f *.o $(BIN)
diff --git a/Documentation/arm/SH-Mobile/vrl4.c b/Documentation/arm/SH-Mobile/vrl4.c
new file mode 100644 (file)
index 0000000..e8a1913
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * vrl4 format generator
+ *
+ * Copyright (C) 2010 Simon Horman
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * usage: vrl4 < zImage > out
+ *       dd if=out of=/dev/sdx bs=512 seek=1 # Write the image to sector 1
+ *
+ * Reads a zImage from stdin and writes a vrl4 image to stdout.
+ * In practice this means writing a padded vrl4 header to stdout followed
+ * by the zImage.
+ *
+ * The padding places the zImage at ALIGN bytes into the output.
+ * The vrl4 uses ALIGN + START_BASE as the start_address.
+ * This is where the mask ROM will jump to after verifying the header.
+ *
+ * The header sets copy_size to min(sizeof(zImage), MAX_BOOT_PROG_LEN) + ALIGN.
+ * That is, the mask ROM will load the padded header (ALIGN bytes)
+ * And then MAX_BOOT_PROG_LEN bytes of the image, or the entire image,
+ * whichever is smaller.
+ *
+ * The zImage is not modified in any way.
+ */
+
+#define _BSD_SOURCE
+#include <endian.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+
+struct hdr {
+       uint32_t magic1;
+       uint32_t reserved1;
+       uint32_t magic2;
+       uint32_t reserved2;
+       uint16_t copy_size;
+       uint16_t boot_options;
+       uint32_t reserved3;
+       uint32_t start_address;
+       uint32_t reserved4;
+       uint32_t reserved5;
+       char     reserved6[308];
+};
+
+#define DECLARE_HDR(h)                                 \
+       struct hdr (h) = {                              \
+               .magic1 =       htole32(0xea000000),    \
+               .reserved1 =    htole32(0x56),          \
+               .magic2 =       htole32(0xe59ff008),    \
+               .reserved3 =    htole16(0x1) }
+
+/* Align to 512 bytes, the MMCIF sector size */
+#define ALIGN_BITS     9
+#define ALIGN          (1 << ALIGN_BITS)
+
+#define START_BASE     0xe55b0000
+
+/*
+ * With an alignment of 512 the header uses the first sector.
+ * There is a 128 sector (64kbyte) limit on the data loaded by the mask ROM.
+ * So there are 127 sectors left for the boot programme. But in practice
+ * Only a small portion of a zImage is needed, 16 sectors should be more
+ * than enough.
+ *
+ * Note that this sets how much of the zImage is copied by the mask ROM.
+ * The entire zImage is present after the header and is loaded
+ * by the code in the boot program (which is the first portion of the zImage).
+ */
+#define        MAX_BOOT_PROG_LEN (16 * 512)
+
+#define ROUND_UP(x)    ((x + ALIGN - 1) & ~(ALIGN - 1))
+
+ssize_t do_read(int fd, void *buf, size_t count)
+{
+       size_t offset = 0;
+       ssize_t l;
+
+       while (offset < count) {
+               l = read(fd, buf + offset, count - offset);
+               if (!l)
+                       break;
+               if (l < 0) {
+                       if (errno == EAGAIN || errno == EWOULDBLOCK)
+                               continue;
+                       perror("read");
+                       return -1;
+               }
+               offset += l;
+       }
+
+       return offset;
+}
+
+ssize_t do_write(int fd, const void *buf, size_t count)
+{
+       size_t offset = 0;
+       ssize_t l;
+
+       while (offset < count) {
+               l = write(fd, buf + offset, count - offset);
+               if (l < 0) {
+                       if (errno == EAGAIN || errno == EWOULDBLOCK)
+                               continue;
+                       perror("write");
+                       return -1;
+               }
+               offset += l;
+       }
+
+       return offset;
+}
+
+ssize_t write_zero(int fd, size_t len)
+{
+       size_t i = len;
+
+       while (i--) {
+               const char x = 0;
+               if (do_write(fd, &x, 1) < 0)
+                       return -1;
+       }
+
+       return len;
+}
+
+int main(void)
+{
+       DECLARE_HDR(hdr);
+       char boot_program[MAX_BOOT_PROG_LEN];
+       size_t aligned_hdr_len, alligned_prog_len;
+       ssize_t prog_len;
+
+       prog_len = do_read(0, boot_program, sizeof(boot_program));
+       if (prog_len <= 0)
+               return -1;
+
+       aligned_hdr_len = ROUND_UP(sizeof(hdr));
+       hdr.start_address = htole32(START_BASE + aligned_hdr_len);
+       alligned_prog_len = ROUND_UP(prog_len);
+       hdr.copy_size = htole16(aligned_hdr_len + alligned_prog_len);
+
+       if (do_write(1, &hdr, sizeof(hdr)) < 0)
+               return -1;
+       if (write_zero(1, aligned_hdr_len - sizeof(hdr)) < 0)
+               return -1;
+
+       if (do_write(1, boot_program, prog_len) < 0)
+               return 1;
+
+       /* Write out the rest of the kernel */
+       while (1) {
+               prog_len = do_read(0, boot_program, sizeof(boot_program));
+               if (prog_len < 0)
+                       return 1;
+               if (prog_len == 0)
+                       break;
+               if (do_write(1, boot_program, prog_len) < 0)
+                       return 1;
+       }
+
+       return 0;
+}
diff --git a/Documentation/arm/SH-Mobile/zboot-rom-mmcif.txt b/Documentation/arm/SH-Mobile/zboot-rom-mmcif.txt
new file mode 100644 (file)
index 0000000..efff8ae
--- /dev/null
@@ -0,0 +1,29 @@
+ROM-able zImage boot from MMC
+-----------------------------
+
+An ROM-able zImage compiled with ZBOOT_ROM_MMCIF may be written to MMC and
+SuperH Mobile ARM will to boot directly from the MMCIF hardware block.
+
+This is achieved by the mask ROM loading the first portion of the image into
+MERAM and then jumping to it. This portion contains loader code which
+copies the entire image to SDRAM and jumps to it. From there the zImage
+boot code proceeds as normal, uncompressing the image into its final
+location and then jumping to it.
+
+This code has been tested on an AP4EB board using the developer 1A eMMC
+boot mode which is configured using the following jumper settings.
+The board used for testing required a patched mask ROM in order for
+this mode to function.
+
+   8 7 6 5 4 3 2 1
+   x|x|x|x|x| |x|
+S4 -+-+-+-+-+-+-+-
+    | | | | |x| |x on
+
+The zImage must be written to the MMC card at sector 1 (512 bytes) in
+vrl4 format. A utility vrl4 is supplied to accomplish this.
+
+e.g.
+       vrl4 < zImage | dd of=/dev/sdX bs=512 seek=1
+
+A dual-voltage MMC 4.0 card was used for testing.
index 5cff165..2d0a1dc 100644 (file)
@@ -1619,6 +1619,18 @@ config ZBOOT_ROM
          Say Y here if you intend to execute your compressed kernel image
          (zImage) directly from ROM or flash.  If unsure, say N.
 
+config ZBOOT_ROM_MMCIF
+       bool "Include MMCIF loader in zImage (EXPERIMENTAL)"
+       depends on ZBOOT_ROM && ARCH_SH7372 && EXPERIMENTAL
+       help
+         Say Y here to include experimental MMCIF loading code in the
+         ROM-able zImage. With this enabled it is possible to write the
+         the ROM-able zImage kernel image to an MMC card and boot the
+         kernel straight from the reset vector. At reset the processor
+         Mask ROM will load the first part of the the ROM-able zImage
+         which in turn loads the rest the kernel image to RAM using the
+         MMCIF hardware block.
+
 config CMDLINE
        string "Default kernel command string"
        default ""
index 0a8f748..198007d 100644 (file)
@@ -4,9 +4,20 @@
 # create a compressed vmlinuz image from the original vmlinux
 #
 
+OBJS           =
+
+# Ensure that mmcif loader code appears early in the image
+# to minimise that number of bocks that have to be read in
+# order to load it.
+ifeq ($(CONFIG_ZBOOT_ROM_MMCIF),y)
+ifeq ($(CONFIG_ARCH_SH7372),y)
+OBJS           += mmcif-sh7372.o
+endif
+endif
+
 AFLAGS_head.o += -DTEXT_OFFSET=$(TEXT_OFFSET)
 HEAD   = head.o
-OBJS   = misc.o decompress.o
+OBJS   += misc.o decompress.o
 FONTC  = $(srctree)/drivers/video/console/font_acorn_8x8.c
 
 #
index 30973b7..c943d2e 100644 (file)
        /* load board-specific initialization code */
 #include <mach/zboot.h>
 
+#ifdef CONFIG_ZBOOT_ROM_MMCIF
+       /* Load image from MMC */
+       adr     sp, __tmp_stack + 128
+       ldr     r0, __image_start
+       ldr     r1, __image_end
+       subs    r1, r1, r0
+       ldr     r0, __load_base
+       bl      mmcif_loader
+
+       /* Jump to loaded code */
+       ldr     r0, __loaded
+       ldr     r1, __image_start
+       sub     r0, r0, r1
+       ldr     r1, __load_base
+       add     pc, r0, r1
+
+__image_start:
+       .long   _start
+__image_end:
+       .long   _got_end
+__load_base:
+       .long   CONFIG_MEMORY_START + 0x02000000 @ Load at 32Mb into SDRAM
+__loaded:
+       .long   __continue
+       .align
+__tmp_stack:
+       .space  128
+__continue:
+#endif /* CONFIG_ZBOOT_ROM_MMCIF */
+
        b       1f
 __atags:@ tag #1
        .long   12                      @ tag->hdr.size = tag_size(tag_core);
diff --git a/arch/arm/boot/compressed/mmcif-sh7372.c b/arch/arm/boot/compressed/mmcif-sh7372.c
new file mode 100644 (file)
index 0000000..e6180af
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * sh7372 MMCIF loader
+ *
+ * Copyright (C) 2010 Magnus Damm
+ * Copyright (C) 2010 Simon Horman
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/mmc/sh_mmcif.h>
+#include <mach/mmcif.h>
+
+#define MMCIF_BASE      (void __iomem *)0xe6bd0000
+
+#define PORT84CR       (void __iomem *)0xe6050054
+#define PORT85CR       (void __iomem *)0xe6050055
+#define PORT86CR       (void __iomem *)0xe6050056
+#define PORT87CR       (void __iomem *)0xe6050057
+#define PORT88CR       (void __iomem *)0xe6050058
+#define PORT89CR       (void __iomem *)0xe6050059
+#define PORT90CR       (void __iomem *)0xe605005a
+#define PORT91CR       (void __iomem *)0xe605005b
+#define PORT92CR       (void __iomem *)0xe605005c
+#define PORT99CR       (void __iomem *)0xe6050063
+
+#define SMSTPCR3       (void __iomem *)0xe615013c
+
+/* SH7372 specific MMCIF loader
+ *
+ * loads the zImage from an MMC card starting from block 1.
+ *
+ * The image must be start with a vrl4 header and
+ * the zImage must start at offset 512 of the image. That is,
+ * at block 2 (=byte 1024) on the media
+ *
+ * Use the following line to write the vrl4 formated zImage
+ * to an MMC card
+ * # dd if=vrl4.out of=/dev/sdx bs=512 seek=1
+ */
+asmlinkage void mmcif_loader(unsigned char *buf, unsigned long len)
+{
+       mmcif_init_progress();
+       mmcif_update_progress(MMCIF_PROGRESS_ENTER);
+
+       /* Initialise MMC
+        * registers: PORT84CR-PORT92CR
+        *            (MMCD0_0-MMCD0_7,MMCCMD0 Control)
+        * value: 0x04 - select function 4
+        */
+        __raw_writeb(0x04, PORT84CR);
+        __raw_writeb(0x04, PORT85CR);
+        __raw_writeb(0x04, PORT86CR);
+        __raw_writeb(0x04, PORT87CR);
+        __raw_writeb(0x04, PORT88CR);
+        __raw_writeb(0x04, PORT89CR);
+        __raw_writeb(0x04, PORT90CR);
+        __raw_writeb(0x04, PORT91CR);
+        __raw_writeb(0x04, PORT92CR);
+
+       /* Initialise MMC
+        * registers: PORT99CR (MMCCLK0 Control)
+        * value: 0x10 | 0x04 - enable output | select function 4
+        */
+       __raw_writeb(0x14, PORT99CR);
+
+       /* Enable clock to MMC hardware block */
+       __raw_writel(__raw_readl(SMSTPCR3) & ~(1 << 12), SMSTPCR3);
+
+       mmcif_update_progress(MMCIF_PROGRESS_INIT);
+
+       /* setup MMCIF hardware */
+       sh_mmcif_boot_init(MMCIF_BASE);
+
+       mmcif_update_progress(MMCIF_PROGRESS_LOAD);
+
+       /* load kernel via MMCIF interface */
+       sh_mmcif_boot_do_read(MMCIF_BASE, 2, /* Kernel is at block 2 */
+                             (len + SH_MMCIF_BBS - 1) / SH_MMCIF_BBS, buf);
+
+
+       /* Disable clock to MMC hardware block */
+       __raw_writel(__raw_readl(SMSTPCR3) & (1 << 12), SMSTPCR3);
+
+       mmcif_update_progress(MMCIF_PROGRESS_DONE);
+}
diff --git a/arch/arm/mach-shmobile/include/mach/mmcif-ap4eb.h b/arch/arm/mach-shmobile/include/mach/mmcif-ap4eb.h
new file mode 100644 (file)
index 0000000..a8d02be
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef MMCIF_AP4EB_H
+#define MMCIF_AP4EB_H
+
+#define PORT185CR      (void __iomem *)0xe60520b9
+#define PORT186CR      (void __iomem *)0xe60520ba
+#define PORT187CR      (void __iomem *)0xe60520bb
+#define PORT188CR      (void __iomem *)0xe60520bc
+
+#define PORTR191_160DR (void __iomem *)0xe6056014
+
+static inline void mmcif_init_progress(void)
+{
+       /* Initialise LEDS1-4
+        * registers: PORT185CR-PORT188CR (LED1-LED4 Control)
+        * value:     0x10 - enable output
+        */
+       __raw_writeb(0x10, PORT185CR);
+       __raw_writeb(0x10, PORT186CR);
+       __raw_writeb(0x10, PORT187CR);
+       __raw_writeb(0x10, PORT188CR);
+}
+
+static inline void mmcif_update_progress(int n)
+{
+       __raw_writel((__raw_readl(PORTR191_160DR) & ~(0xf << 25)) |
+                    (1 << (25 + n)), PORTR191_160DR);
+}
+
+#endif /* MMCIF_AP4EB_H */
diff --git a/arch/arm/mach-shmobile/include/mach/mmcif-mackerel.h b/arch/arm/mach-shmobile/include/mach/mmcif-mackerel.h
new file mode 100644 (file)
index 0000000..4b4f694
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef MMCIF_MACKEREL_H
+#define MMCIF_MACKEREL_H
+
+#define PORT0CR      (void __iomem *)0xe6051000
+#define PORT1CR      (void __iomem *)0xe6051001
+#define PORT2CR      (void __iomem *)0xe6051002
+#define PORT159CR    (void __iomem *)0xe605009f
+
+#define PORTR031_000DR (void __iomem *)0xe6055000
+#define PORTL159_128DR (void __iomem *)0xe6054010
+
+static inline void mmcif_init_progress(void)
+{
+       /* Initialise LEDS0-3
+        * registers: PORT0CR-PORT2CR,PORT159CR (LED0-LED3 Control)
+        * value:     0x10 - enable output
+        */
+       __raw_writeb(0x10, PORT0CR);
+       __raw_writeb(0x10, PORT1CR);
+       __raw_writeb(0x10, PORT2CR);
+       __raw_writeb(0x10, PORT159CR);
+}
+
+static inline void mmcif_update_progress(int n)
+{
+       unsigned a = 0, b = 0;
+
+       if (n < 3)
+               a = 1 << n;
+       else
+               b = 1 << 31;
+
+       __raw_writel((__raw_readl(PORTR031_000DR) & ~0x7) | a,
+                    PORTR031_000DR);
+       __raw_writel((__raw_readl(PORTL159_128DR) & ~(1 << 31)) | b,
+                    PORTL159_128DR);
+}
+
+#endif /* MMCIF_MACKEREL_H */
diff --git a/arch/arm/mach-shmobile/include/mach/mmcif.h b/arch/arm/mach-shmobile/include/mach/mmcif.h
new file mode 100644 (file)
index 0000000..f4dc327
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef MMCIF_H
+#define MMCIF_H
+
+/**************************************************
+ *
+ *             board specific settings
+ *
+ **************************************************/
+
+#ifdef CONFIG_MACH_AP4EVB
+#include "mach/mmcif-ap4eb.h"
+#elif CONFIG_MACH_MACKEREL
+#include "mach/mmcif-mackerel.h"
+#else
+#error "unsupported board."
+#endif
+
+#endif /* MMCIF_H */