blob: 99f467f46d228670845e7d02abe4ec48bf3b7913 [file] [log] [blame]
/*
* 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();
}