/* * Copyright (c) 2015 Sascha Hauer , Pengutronix * * Based on a patch by: * * 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 version 2 * as published by the Free Software Foundation. * * 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. * */ #include #include #include #include #include static void __iomem *omap3_usb_base = (void __iomem *)OMAP3_MUSB0_BASE; #define OMAP34XX_USB_EP(n) (omap3_usb_base + 0x100 + 0x10 * (n)) #define MUSB_RXCSR 0x06 #define MUSB_RXCOUNT 0x08 #define MUSB_TXCSR 0x02 #define MUSB_FIFOSIZE 0x0F #define OMAP34XX_USB_RXCSR(n) (OMAP34XX_USB_EP(n) + MUSB_RXCSR) #define OMAP34XX_USB_RXCOUNT(n) (OMAP34XX_USB_EP(n) + MUSB_RXCOUNT) #define OMAP34XX_USB_TXCSR(n) (OMAP34XX_USB_EP(n) + MUSB_TXCSR) #define OMAP34XX_USB_FIFOSIZE(n) (OMAP34XX_USB_EP(n) + MUSB_FIFOSIZE) #define OMAP34XX_USB_FIFO(n) (omap3_usb_base + 0x20 + ((n) * 4)) /* memory mapped registers */ #define BULK_ENDPOINT 1 #define MUSB_RXCSR_RXPKTRDY 0x0001 #define MUSB_TXCSR_TXPKTRDY 0x0001 #define PACK4(a,b,c,d) (((d) << 24) | ((c) << 16) | ((b) << 8) | (a)) #define USBLOAD_CMD_FILE PACK4('U', 'S', 'B', 's') /* send file size */ #define USBLOAD_CMD_JUMP PACK4('U', 'S', 'B', 'j') /* go where I tell you */ #define USBLOAD_CMD_FILE_REQ PACK4('U', 'S', 'B', 'f') /* file request */ #define USBLOAD_CMD_ECHO_SZ PACK4('U', 'S', 'B', 'n') /* echo file size */ #define USBLOAD_CMD_REPORT_SZ PACK4('U', 'S', 'B', 'o') /* report file size */ #define USBLOAD_CMD_MESSAGE PACK4('U', 'S', 'B', 'm') /* message for debug */ static int usb_send(unsigned char *buffer, unsigned int buffer_size) { unsigned int cntr; u16 txcsr; void __iomem *reg = (void *)OMAP34XX_USB_TXCSR(BULK_ENDPOINT); void __iomem *bulk_fifo = (void *)OMAP34XX_USB_FIFO(BULK_ENDPOINT); txcsr = readw(reg); if (txcsr & MUSB_TXCSR_TXPKTRDY) return 0; for (cntr = 0; cntr < buffer_size; cntr++) writeb(buffer[cntr], bulk_fifo); txcsr = readw(reg); txcsr |= MUSB_TXCSR_TXPKTRDY; writew(txcsr, reg); return buffer_size; } static int usb_recv(u8 *buffer) { int cntr; u16 count = 0; u16 rxcsr; void __iomem *reg = (void *)OMAP34XX_USB_RXCSR(BULK_ENDPOINT); void __iomem *bulk_fifo = (void *)OMAP34XX_USB_FIFO(BULK_ENDPOINT); rxcsr = readw(reg); if (!(rxcsr & MUSB_RXCSR_RXPKTRDY)) return 0; count = readw((void *)OMAP34XX_USB_RXCOUNT(BULK_ENDPOINT)); for (cntr = 0; cntr < count; cntr++) *buffer++ = readb(bulk_fifo); /* Clear the RXPKTRDY bit */ rxcsr = readw(reg); rxcsr &= ~MUSB_RXCSR_RXPKTRDY; writew(rxcsr, reg); return count; } static unsigned char usb_outbuffer[64]; 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(usb_outbuffer, p_char - usb_outbuffer); } static void usb_code(unsigned int cmd, u32 code) { unsigned int *p_int = (unsigned int *)usb_outbuffer; *p_int++ = cmd; *p_int++ = code; usb_send (usb_outbuffer, ((unsigned char *) p_int) - usb_outbuffer); } void *omap3_xload_boot_usb(void) { int res; void *buf; u32 *buf32; u32 total; void *addr; u32 bytes; int size; int cntr; void *fn; u8 __buf[512]; buf32 = buf = __buf; usb_msg (USBLOAD_CMD_FILE_REQ, "file req"); for (cntr = 0; cntr < 10000000; cntr++) { size = usb_recv(buf); if (!size) continue; switch (buf32[0]) { case USBLOAD_CMD_FILE: pr_debug ("USBLOAD_CMD_FILE total = %d size = 0x%x addr = 0x%x\n", res, buf32[1], buf32[2]); total = buf32[1]; /* get size and address */ addr = (void *)buf32[2]; usb_code(USBLOAD_CMD_ECHO_SZ, total); bytes = 0; while (bytes < total) { size = usb_recv(buf); memcpy(addr, buf, size); addr += size; bytes += size; } usb_code(USBLOAD_CMD_REPORT_SZ, total); /* tell him we got this many bytes */ usb_msg (USBLOAD_CMD_FILE_REQ, "file req"); /* see if they have another file for us */ break; case USBLOAD_CMD_JUMP: pr_debug("USBLOAD_CMD_JUMP total = %d addr = 0x%x val = 0x%x\n", res, buf32[0], buf32[1]); fn = (void *)buf32[1]; goto out; default: break; } } fn = NULL; out: return fn; }