From patchwork Tue Jul 30 17:57:23 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulrich Hecht X-Patchwork-Id: 2835816 Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 695D9C0319 for ; Tue, 30 Jul 2013 17:57:51 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 01C8E20336 for ; Tue, 30 Jul 2013 17:57:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7BB5C2034F for ; Tue, 30 Jul 2013 17:57:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756356Ab3G3R5p (ORCPT ); Tue, 30 Jul 2013 13:57:45 -0400 Received: from mail-ee0-f46.google.com ([74.125.83.46]:46216 "EHLO mail-ee0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756311Ab3G3R5m (ORCPT ); Tue, 30 Jul 2013 13:57:42 -0400 Received: by mail-ee0-f46.google.com with SMTP id c13so1166593eek.19 for ; Tue, 30 Jul 2013 10:57:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=KHhH5Wn9bGL3qc28yXLddalwOoIZJ2Fzb2nFBzbP2cA=; b=TWIAQ7yFNgBKaljwNFuphi6VGjyGAIs94dyUXVg5LeTuZLRSwl6cyRgI6izy/rpRao veuHxsEghGDtXJDhokVJwug1ylN0yUmjALqoFKUhb9o1Tij0jpBPNTj4Jt81gYYmSB0+ AZCl016jEsm2dfQRd+hWuC4XaxO7BKu4ZNcq4f4CTsINy+RPqf2LuamA2wx/yQmdi+yb mai7+WUXp0XmDIKowzLA5waPQyJEIIJbXF6jqVHe6DTwWjYyvyrFhPDFhY9JnqFZCo10 17l4C/6Q0hVqk1CIrg6ae3d9ZuU2XNV9d/7zXqx7KIVs3LrTtqzX3Ba7ETbrIKx3AcxJ UJEA== X-Received: by 10.14.100.135 with SMTP id z7mr65558128eef.113.1375207061344; Tue, 30 Jul 2013 10:57:41 -0700 (PDT) Received: from groucho.site (188-195-161-203-dynip.superkabel.de. [188.195.161.203]) by mx.google.com with ESMTPSA id e44sm112264244eeh.11.2013.07.30.10.57.40 for (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 30 Jul 2013 10:57:40 -0700 (PDT) From: Ulrich Hecht To: linux-sh@vger.kernel.org Cc: magnus.damm@gmail.com, kuninori.morimoto.gx@renesas.com, Ulrich Hecht Subject: [RFC 1/5] USB boot tool for SH-Mobile ARM SoCs Date: Tue, 30 Jul 2013 19:57:23 +0200 Message-Id: <1375207047-8655-2-git-send-email-ulrich.hecht@gmail.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1375207047-8655-1-git-send-email-ulrich.hecht@gmail.com> References: <1375207047-8655-1-git-send-email-ulrich.hecht@gmail.com> Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-8.3 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Included in this patch set for convenience. --- Documentation/arm/SH-Mobile/r_usb_boot.c | 544 ++++++++++++++++++++++++++++++ 1 file changed, 544 insertions(+) create mode 100644 Documentation/arm/SH-Mobile/r_usb_boot.c diff --git a/Documentation/arm/SH-Mobile/r_usb_boot.c b/Documentation/arm/SH-Mobile/r_usb_boot.c new file mode 100644 index 0000000..7bda4d9 --- /dev/null +++ b/Documentation/arm/SH-Mobile/r_usb_boot.c @@ -0,0 +1,544 @@ +/* + * USB boot tool for SH-Mobile ARM SoCs + * + * Boots a stand alone Linux kernel directly from the MASK ROM. + * Limited to the size of the MERAM block (1.5 MiB SRAM) + * + * Tested on sh7372 and sh73a0. + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + * + * Requires libusb, use the following line to build: + * $ gcc `pkg-config libusb-1.0 --libs --cflags` r_usb_boot.c -o r_usb_boot + * + * This program is designed to run on a Linux host computer that is + * connected to the target hardware via USB. The target hardware + * needs to have USB boot support built into the MASK ROM and the + * boot mode pins (MDn) should select "USB Development mode". + * + * Usage: + * # r_usb_boot zImage + * + * The kernel must include System RAM setup code among other things, as + * an example see arch/arm/mach-shmobile/include/mach/head-ap4evb.txt + * Important kernel configuration parameters include: + * + * CONFIG_ZBOOT_ROM=y + * CONFIG_ZBOOT_ROM_TEXT=0x0 + * CONFIG_ZBOOT_ROM_BSS=0xe5580000 (Needs to match MERAM base address) + * CONFIG_CMDLINE="..." (This depends on your board) + * CONFIG_CMDLINE_FORCE=y + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libusb.h" + +#define HUGECHUNK 0x40000 +#define MAX_FILESIZE (HUGECHUNK * 5) /* 1.5 MiB MERAM, 256 KiB reserved */ + +struct device_info { + char *name; + unsigned short vid; + unsigned short pid; + + enum { FORMAT_VRL4, FORMAT_VRL5 } format; + unsigned int small_chunksize; + unsigned int large_chunksize; + +} supported_devices[] = { + { + "sh7372", + 0x045b, 0x0055, + FORMAT_VRL4, + 64, 4096, + }, + { + "sh73a0", + 0x045b, 0x0060, + FORMAT_VRL5, + 64, 16384, + }, +}; + +#define TIMEOUT 5000 +#define MAX_CHUNK 16384 + +/* ARM code that copies data in memory */ +static unsigned char mover_code_data[] = { + 0x02, 0x00, 0x00, 0xea, /* b after_size */ + 0x00, 0x00, 0x00, 0x00, /* src */ + 0x00, 0x00, 0x00, 0x00, /* dst */ + 0x00, 0x00, 0x00, 0x00, /* size */ + + 0x14, 0x20, 0x1f, 0xe5, /* ldr r2, src */ + 0x14, 0x30, 0x1f, 0xe5, /* ldr r3, dst */ + 0x14, 0x00, 0x1f, 0xe5, /* ldr r0, len */ + + 0x04, 0x10, 0x92, 0xe4, /* ldr r1,[r2],#4 */ + 0x04, 0x10, 0x83, 0xe4, /* str r1,[r3],#4 */ + 0x04, 0x00, 0x50, 0xe2, /* subs r0,r0,#4 */ + 0xfb, 0xff, 0xff, 0x1a, /* bne before_ldr_r1 */ + + 0x0e, 0xf0, 0xa0, 0xe1, /* mov pc, lr */ +}; + +static int get_hw_version(libusb_device_handle *h) +{ + unsigned char get_version[] = { 0xaf, 0x00 }; + unsigned char data[0x20]; + int r, r2; + + /* Get Version */ + r = libusb_bulk_transfer(h, 0x02, + get_version, sizeof(get_version), + &r2, TIMEOUT); + if (r) + return -1; + + /* Version Information */ + r = libusb_bulk_transfer(h, 0x81, + data, sizeof(data), + &r2, TIMEOUT); + + if (!r && r2 && (data[0] == 0xbf) && (data[1] == (r2 - 2))) { + data[r2] = '\0'; + printf("Hardware Version Information %s\n", &data[2]); + return 0; + } + + return -1; +} + +static int status_request_response(libusb_device_handle *h) +{ + unsigned char data_out[3] = { 0xa1, 0x01, 0x00 }; + unsigned char data_in[0x40]; + int r, r2; + + /* Status Request */ + r = libusb_bulk_transfer(h, 0x02, + data_out, sizeof(data_out), + &r2, TIMEOUT); + if (r || (r2 != sizeof(data_out))) { + fprintf(stderr, "Status Request send error\n"); + return -1; + } + + /* Status Response */ + r = libusb_bulk_transfer(h, 0x81, + data_in, sizeof(data_in), + &r2, TIMEOUT); + + if (r || (r2 != 3) || (data_in[0] != 0xb0)) { + fprintf(stderr, "Status is not ok with %d %d 0x%02x 0x%02x\n", + r, r2, data_in[0], data_in[2]); + return -1; + } + + return 0; +} + +static int setup_clocks(libusb_device_handle *h) +{ + unsigned char initialize[] = { 0xa0, 0x01, 0x00 }; + unsigned char data[0x20]; + int r, r2; + + /* Initialize */ + r = libusb_bulk_transfer(h, 0x02, + initialize, sizeof(initialize), + &r2, TIMEOUT); + if (r) + return -1; + + /* Command Response */ + r = libusb_bulk_transfer(h, 0x81, + data, sizeof(data), + &r2, TIMEOUT); + if (r || (r2 != 3) || (data[0] != 0xb0) || (data[2] != 0x00)) + return -1; + + /* Status Request + Status Response */ + return status_request_response(h); +} + +static int do_start(libusb_device_handle *h) +{ + unsigned char program_start[] = { 0xa5, 0x00 }; + unsigned char data[0x10]; + int r, r2; + + /* Program Start */ + r = libusb_bulk_transfer(h, 0x02, + program_start, sizeof(program_start), + &r2, TIMEOUT); + if (r) + return -1; + + /* Command Response */ + r = libusb_bulk_transfer(h, 0x81, + data, sizeof(data), + &r2, TIMEOUT); + if (r || (r2 != 3) || (data[0] != 0xb0) || (data[1] != 1) + || (data[2] != 0)) { + fprintf(stderr, "Unexpected Program Start Response 0x%02x\n", + data[2]); + return -1; + } + return 0; +} + +static int do_download(libusb_device_handle *h, + int my_chunksize, + unsigned int base_addr, + unsigned char *buf, int no_bytes) +{ + unsigned char data[MAX_CHUNK + 7]; + int k, r, r2; + unsigned int crc, allcrc; + int bytes_to_go; + int seq; + int chunksize; + + allcrc = 0; + seq = 0; + chunksize = my_chunksize < MAX_CHUNK ? my_chunksize : MAX_CHUNK; + + bytes_to_go = (no_bytes + chunksize - 1) & ~(chunksize - 1); + + /* Download Start */ + data[0] = 0xa2; + data[1] = 0x08; + data[2] = (base_addr >> 24) & 0xff; + data[3] = (base_addr >> 16) & 0xff; + data[4] = (base_addr >> 8) & 0xff; + data[5] = base_addr & 0xff; + data[6] = (bytes_to_go >> 24) & 0xff; + data[7] = (bytes_to_go >> 16) & 0xff; + data[8] = (bytes_to_go >> 8) & 0xff; + data[9] = bytes_to_go & 0xff; + r = libusb_bulk_transfer(h, 0x02, + data, 10, + &r2, TIMEOUT); + if (r) + return -1; + + /* Download Response */ + r = libusb_bulk_transfer(h, 0x81, + data, sizeof(data), + &r2, TIMEOUT); + if (r || (r2 != 4) || (data[0] != 0xb1)) + return -1; + + if (data[2] || data[3]) { + fprintf(stderr, "Download Start Response is 0x%02x 0x%02x\n", + data[2], data[3]); + return -1; + } + + /* Status Request + Status Response */ + status_request_response(h); + + while (bytes_to_go > 0) { + + k = chunksize + 4; + + /* Download Data */ + data[0] = 0xa3; + data[1] = (k >> 8) & 0xff; + data[2] = k & 0xff; + data[3] = (seq >> 8) & 0xff; + data[4] = seq & 0xff; + + crc = 0; + for (k = 0; k < chunksize; k++) { + crc += buf[k]; + allcrc += buf[k]; + data[5 + k] = buf[k]; + } + crc = ~(crc - 1); + + data[chunksize + 5] = (crc >> 8) & 0xff; + data[chunksize + 6] = crc & 0xff; + r = libusb_bulk_transfer(h, 0x02, + data, chunksize + 7, + &r2, TIMEOUT); + if (r) + return -1; + + /* Download Response */ + r = libusb_bulk_transfer(h, 0x81, + data, sizeof(data), + &r2, TIMEOUT); + if (r || (r2 != 4) || (data[0] != 0xb1)) + return -1; + + if (data[2] || data[3]) { + fprintf(stderr, "Download Response 0x%02x 0x%02x\n", + data[2], data[3]); + return -1; + } + + /* Status Request + Status Response */ + status_request_response(h); + + /* process next chunk */ + buf += chunksize; + bytes_to_go -= chunksize; + seq++; + } + + /* Download End Confirm */ + data[0] = 0xa4; + data[1] = 0x02; + + allcrc = ~(allcrc - 1); + data[2] = (allcrc >> 8) & 0xff; + data[3] = allcrc & 0xff; + + r = libusb_bulk_transfer(h, 0x02, + data, 4, + &r2, TIMEOUT); + if (r) + return -1; + + /* Download End Status */ + r = libusb_bulk_transfer(h, 0x81, + data, sizeof(data), + &r2, TIMEOUT); + if (r || (r2 != 5) || (data[0] != 0xb2)) + return -1; + + if (data[2]) { + fprintf(stderr, "Download End Status is 0x%02x 0x%02x 0x%02x\n", + data[2], data[3], data[4]); + return -1; + } + + return 0; +} + +/* VRL5 header */ +static unsigned char vrl5_header_file_data[340] = { + 0x10, 0xf0, 0x9f, 0xe5, + 0x56, 0x00, 0x00, 0x00, + 0x20, 0x41, 0x35, 0x47, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, +}; + +/* VRL4 header */ +static unsigned char vrl4_header_file_data[340] = { + 0x00, 0x00, 0x00, 0xea, + 0x56, 0x00, 0x00, 0x00, + 0x08, 0xf0, 0x9f, 0xe5, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, +}; + +static int vrl_header(libusb_device_handle *h, + struct device_info *di, + unsigned int start_addr) +{ + unsigned char header_buf[340]; + + switch (di->format) { + case FORMAT_VRL4: + memcpy(header_buf, vrl4_header_file_data, sizeof(header_buf)); + break; + case FORMAT_VRL5: + memcpy(header_buf, vrl5_header_file_data, sizeof(header_buf)); + break; + default: + return -1; + } + + /* Fill in start address */ + header_buf[24] = start_addr & 0xff; + header_buf[25] = (start_addr >> 8) & 0xff; + header_buf[26] = (start_addr >> 16) & 0xff; + header_buf[27] = (start_addr >> 24) & 0xff; + + /* Download VRL header for file at 0xe55b0000 */ + return do_download(h, di->small_chunksize, + 0xe55b0000, header_buf, sizeof(header_buf)); +} + +static int run_mover_code(libusb_device_handle *h, + struct device_info *di, + unsigned int src_addr, + unsigned int dst_addr, + unsigned int no_bytes) +{ + int ret; + + printf("Moving 0x%08x bytes from 0x%08x to 0x%08x\n", + no_bytes, src_addr, dst_addr); + + mover_code_data[4] = src_addr & 0xff; + mover_code_data[5] = (src_addr >> 8) & 0xff; + mover_code_data[6] = (src_addr >> 16) & 0xff; + mover_code_data[7] = (src_addr >> 24) & 0xff; + mover_code_data[8] = dst_addr & 0xff; + mover_code_data[9] = (dst_addr >> 8) & 0xff; + mover_code_data[10] = (dst_addr >> 16) & 0xff; + mover_code_data[11] = (dst_addr >> 24) & 0xff; + mover_code_data[12] = no_bytes & 0xff; + mover_code_data[13] = (no_bytes >> 8) & 0xff; + mover_code_data[14] = (no_bytes >> 16) & 0xff; + mover_code_data[15] = (no_bytes >> 24) & 0xff; + + /* Begin loading the headerless code */ + do_download(h, di->small_chunksize, 0xe55b8000, + mover_code_data, sizeof(mover_code_data)); + + /* Download VRL header pointing to 0xe55b8000 */ + vrl_header(h, di, 0xe55b8000); + + /* Start the mover code */ + ret = do_start(h); + + /* Check that the code made it back: + * Status Request + Status Response */ + if (ret == 0) + status_request_response(h); + + return ret; +} + +static int rolf(libusb_device_handle *h, + struct device_info *di, + char *filename) +{ + unsigned char file_buf[HUGECHUNK]; + int k, ret; + int fd, filesize; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "unable to open file.\n"); + return -1; + } + + filesize = lseek(fd, 0, SEEK_END); + if (filesize == -1) { + fprintf(stderr, "unable to seek file.\n"); + return -1; + } + + if (filesize > MAX_FILESIZE) { + fprintf(stderr, "filesize %d exceed maximum supported %d.\n", + filesize, MAX_FILESIZE); + return -1; + } + + if (setup_clocks(h) != 0) + return -1; + + /* split file in HUGCHUNK size blocks - download top first */ + for (k = ((filesize + HUGECHUNK - 1) / HUGECHUNK) - 1; k >= 0; k--) { + + printf("Loading 0x%08x bytes to 0x%08x\n", + HUGECHUNK, 0xe55c0000); + + lseek(fd, HUGECHUNK * k, SEEK_SET); + read(fd, file_buf, HUGECHUNK); + + /* Download file data chunk */ + do_download(h, di->large_chunksize, + 0xe55c0000, file_buf, HUGECHUNK); + + /* need to move all file data except first block */ + if (k) { + ret = run_mover_code(h, di, 0xe55c0000, + 0xe55c0000 + (HUGECHUNK * k), + HUGECHUNK); + if (ret != 0) + return ret; + } + } + + /* Download VRL header pointing to 0xe55c0000 */ + vrl_header(h, di, 0xe55c0000); + + printf("Starting program at 0x%08x\n", 0xe55c0000); + + /* Start the data in the file */ + do_start(h); + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct libusb_device_handle *h; + int k; + int ret; + + ret = libusb_init(NULL); + if (ret < 0) { + fprintf(stderr, "Unable to init libusb\n"); + exit(1); + } + + for (k = 0; + k < sizeof(supported_devices) / sizeof(supported_devices[0]); + k++) { + + h = libusb_open_device_with_vid_pid(NULL, + supported_devices[k].vid, + supported_devices[k].pid); + + if (h) { + printf("Detected %s\n", + supported_devices[k].name); + break; + } + } + + if (!h) { + fprintf(stderr, "Unable to locate device\n"); + goto out; + } + + /* ttyACM is assigned by default, disconnect that driver */ + libusb_detach_kernel_driver(h, 0); + + ret = libusb_claim_interface(h, 1); + if (ret < 0) { + fprintf(stderr, "Unable to claim interface\n"); + goto out; + } + + if (get_hw_version(h) != 0) + goto out2; + + if (argc > 1) + rolf(h, &supported_devices[k], argv[1]); + + out2: + libusb_release_interface(h, 0); + out: + libusb_close(h); + libusb_exit(NULL); + + return ret >= 0 ? 0 : 1; +}