| /* |
| * USB bootloader |
| * |
| * Copyright (C) 2011 Rick Bronson |
| * |
| * 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, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| * |
| */ |
| |
| #include <common.h> |
| #include <malloc.h> |
| #include <asm/arch/cpu.h> /* get chip and board defs */ |
| #include "usb.h" |
| |
| extern void udelay (unsigned long usecs); |
| |
| typedef int boot_os_fn (void); |
| |
| /* send a packet via USB */ |
| int usb_send_packet(unsigned char *buffer, unsigned int packet_size) |
| { |
| int ret = 0; |
| |
| if (!(*peri_txcsr & MUSB_TXCSR_TXPKTRDY)) |
| { |
| unsigned int cntr; |
| |
| for (cntr = 0; cntr < packet_size; cntr++) |
| *bulk_fifo = buffer[cntr]; |
| |
| *peri_txcsr |= MUSB_TXCSR_TXPKTRDY; |
| |
| ret = packet_size; |
| } |
| return ret; |
| } |
| |
| //////////////////// |
| |
| static int usb_recv (u8 *buffer, int size) |
| { |
| int cntr; |
| u16 count = 0; |
| |
| if (*peri_rxcsr & MUSB_RXCSR_RXPKTRDY) |
| { |
| count = *rxcount; |
| for (cntr = 0; cntr < count; cntr++) |
| { |
| *buffer++ = *bulk_fifo; |
| } |
| /* Clear the RXPKTRDY bit */ |
| *peri_rxcsr &= ~MUSB_RXCSR_RXPKTRDY; |
| } |
| return count; /* FIXME */ |
| } |
| |
| static unsigned char usb_outbuffer[RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0]; |
| |
| int usb_send_buffer(unsigned char *buffer, unsigned int buffer_size) |
| { |
| int buffer_index = 0, |
| packet_size, |
| retval = 0; |
| |
| while ((buffer_index < buffer_size) && |
| (retval == 0)) { |
| packet_size = RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0; |
| if ((buffer_index + packet_size) > buffer_size) |
| packet_size = buffer_size - buffer_index; |
| |
| retval = usb_send_packet(&buffer[buffer_index], packet_size); |
| if (retval == packet_size) { |
| buffer_index += packet_size; |
| retval = 0; |
| } |
| } |
| |
| return buffer_index; |
| } |
| |
| static void usb_msg (unsigned int cmd, const char *msg) |
| { |
| unsigned char *p_char = usb_outbuffer; |
| |
| * (int *) p_char = cmd; |
| p_char += sizeof (cmd); |
| if (msg) |
| { |
| while (*msg) |
| *p_char++= *msg++; |
| *p_char++= 0; |
| } |
| usb_send_packet (usb_outbuffer, p_char - usb_outbuffer); |
| } |
| |
| static void usb_code (unsigned int cmd, u32 code1, u32 code2) |
| { |
| unsigned int *p_int = (unsigned int *) usb_outbuffer; |
| |
| *p_int++ = cmd; |
| *p_int++ = code1; |
| *p_int++ = code2; |
| usb_send_packet (usb_outbuffer, ((unsigned char *) p_int) - usb_outbuffer); |
| } |
| |
| void do_usb (void) |
| { |
| boot_os_fn *boot_fn; |
| int res; |
| u32 usb_inbuffer[RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0]; |
| u32 total; |
| u8 *addr; |
| u32 bytes; |
| int cntr = 0; |
| |
| usb_msg (USBLOAD_CMD_FILE_REQ, "file req"); |
| while(++cntr < 200000) /* try for 1 second then bail out */ |
| { |
| res = usb_recv ((u8 *) usb_inbuffer, sizeof (usb_inbuffer)); |
| if ((res == USBLOAD_CMD_RESPONSE_LEN) && (usb_inbuffer[0] == USBLOAD_CMD_FILE)) |
| { |
| total = usb_inbuffer[1]; /* get size and address */ |
| addr = (u8 *) usb_inbuffer[2]; |
| printf ("USBLOAD_CMD_FILE: %8uB @ 0x%x\n", |
| total, |
| addr); |
| usb_code (USBLOAD_CMD_ECHO_SZ, total, 0); |
| |
| bytes = 0; |
| while (bytes < total) |
| { |
| bytes += usb_recv(&((u8 *)addr)[bytes], (total - bytes)); |
| } |
| usb_code (USBLOAD_CMD_REPORT_SZ, total, 0); /* tell him we got this many bytes */ |
| printf ("Received file OK\n"); |
| usb_msg (USBLOAD_CMD_FILE_REQ, "file req"); /* see if they have another file for us */ |
| cntr = 0; |
| } |
| else if ((res == USBLOAD_CMD_RESPONSE_LEN) && (usb_inbuffer[0] == USBLOAD_CMD_JUMP)) |
| { |
| printf ("USBLOAD_CMD_JUMP: addr = 0x%x\n", usb_inbuffer[1]); |
| boot_fn = (boot_os_fn *) usb_inbuffer[1]; |
| boot_fn(); /* go to u-boot and maybe kernel */ |
| } |
| else if (res > 0) |
| { |
| printf ("Unrecognized %dB command: %c%c%c%c\n", res, |
| ((char*)usb_inbuffer)[0], ((char*)usb_inbuffer)[1], ((char*)usb_inbuffer)[2], ((char*)usb_inbuffer)[3]); |
| } |
| |
| udelay(1000); /* delay 1 ms -- suspect clocks aren't configured as expected, resulting in a significantly shorter delay */ |
| } |
| printf("USB done\n"); |
| hang(); |
| } |