allwinner_a64/android/device/linaro/bootloader/arm-trusted-firmware/plat/hikey/usb.c
2018-08-08 17:48:24 +08:00

1515 lines
40 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2014-2015, Linaro Ltd and Contributors. All rights reserved.
* Copyright (c) 2014-2015, Hisilicon Ltd and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <ctype.h>
#include <debug.h>
#include <gpio.h>
#include <hi6220.h>
#include <mmio.h>
#include <partitions.h>
#include <platform_def.h>
#include <sp804_timer.h>
#include <string.h>
#include <usb.h>
#include "hikey_private.h"
#include <bl_common.h>
#define NUM_ENDPOINTS 16
#define USB_BLOCK_HIGH_SPEED_SIZE 512
#define VERSION_BOOTLOADER "0.4"
struct ep_type {
unsigned char active;
unsigned char busy;
unsigned char done;
unsigned int rc;
unsigned int size;
};
struct usb_endpoint {
struct usb_endpoint *next;
unsigned int maxpkt;
struct usb_request *req;
unsigned char num;
unsigned char in;
};
struct usb_config_bundle {
struct usb_config_descriptor config;
struct usb_interface_descriptor interface;
struct usb_endpoint_descriptor ep1;
struct usb_endpoint_descriptor ep2;
} __attribute__ ((packed));
static setup_packet ctrl_req[NUM_ENDPOINTS]
__attribute__ ((section("tzfw_coherent_mem")));
static unsigned char ctrl_resp[2]
__attribute__ ((section("tzfw_coherent_mem")));
static struct ep_type endpoints[NUM_ENDPOINTS]
__attribute__ ((section("tzfw_coherent_mem")));
dwc_otg_dev_dma_desc_t dma_desc
__attribute__ ((section("tzfw_coherent_mem")));
dwc_otg_dev_dma_desc_t dma_desc_ep0
__attribute__ ((section("tzfw_coherent_mem")));
dwc_otg_dev_dma_desc_t dma_desc_in
__attribute__ ((section("tzfw_coherent_mem")));
dwc_otg_dev_dma_desc_t dma_desc_addr
__attribute__ ((section("tzfw_coherent_mem")));
static struct usb_config_bundle config_bundle
__attribute__ ((section("tzfw_coherent_mem")));
static struct usb_device_descriptor device_descriptor
__attribute__ ((section("tzfw_coherent_mem")));
static struct usb_request rx_req
__attribute__ ((section("tzfw_coherent_mem")));
static struct usb_request tx_req
__attribute__ ((section("tzfw_coherent_mem")));
static struct usb_string_descriptor serial_string
__attribute__ ((section("tzfw_coherent_mem")));
static const struct usb_string_descriptor string_devicename = {
24,
USB_DT_STRING,
{'A', 'n', 'd', 'r', 'o', 'i', 'd', ' ', '2', '.', '0'}
};
static const struct usb_string_descriptor serial_string_descriptor = {
34,
USB_DT_STRING,
{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}
};
static const struct usb_string_descriptor lang_descriptor = {
4,
USB_DT_STRING,
{0x0409} /* en-US */
};
static void usb_rx_cmd_complete(unsigned actual, int stat);
static void usb_rx_data_complete(unsigned actual, int status);
static unsigned int rx_desc_bytes = 0;
static unsigned long rx_addr;
static unsigned long rx_length;
static unsigned int last_one = 0;
static char *cmdbuf;
static struct usb_endpoint ep1in, ep1out;
static int g_usb_enum_flag = 0;
int usb_need_reset = 0;
static int usb_drv_port_speed(void)
{
/* 2'b00 High speed (PHY clock is at 30MHz or 60MHz) */
return (mmio_read_32(DSTS) & 2) == 0 ? 1 : 0;
}
static void reset_endpoints(void)
{
int i;
unsigned int data;
INFO("enter reset_endpoints.\n");
for (i = 0; i < NUM_ENDPOINTS; i++) {
endpoints[i].active = 0;
endpoints[i].busy = 0;
endpoints[i].rc = -1;
endpoints[i].done = 1;
}
/* EP0 IN ACTIVE NEXT=1 */
mmio_write_32(DIEPCTL0, 0x8800);
/* EP0 OUT ACTIVE */
mmio_write_32(DOEPCTL0, 0x8000);
/* Clear any pending OTG Interrupts */
mmio_write_32(GOTGINT, ~0);
/* Clear any pending interrupts */
mmio_write_32(GINTSTS, ~0);
mmio_write_32(DIEPINT0, ~0);
mmio_write_32(DOEPINT0, ~0);
mmio_write_32(DIEPINT1, ~0);
mmio_write_32(DOEPINT1, ~0);
/* IN EP interrupt mask */
mmio_write_32(DIEPMSK, 0x0D);
/* OUT EP interrupt mask */
mmio_write_32(DOEPMSK, 0x0D);
/* Enable interrupts on Ep0 */
mmio_write_32(DAINTMSK, 0x00010001);
/* EP0 OUT Transfer Size:64 Bytes, 1 Packet, 3 Setup Packet, Read to receive setup packet*/
data = DOEPTSIZ0_SUPCNT(3) | DOEPTSIZ0_PKTCNT |
(64 << DOEPTSIZ0_XFERSIZE_SHIFT);
mmio_write_32(DOEPTSIZ0, data);
//notes that:the compulsive conversion is expectable.
dma_desc_ep0.status.b.bs = 0x3;
dma_desc_ep0.status.b.mtrf = 0;
dma_desc_ep0.status.b.sr = 0;
dma_desc_ep0.status.b.l = 1;
dma_desc_ep0.status.b.ioc = 1;
dma_desc_ep0.status.b.sp = 0;
dma_desc_ep0.status.b.bytes = 64;
dma_desc_ep0.buf = (unsigned long)&ctrl_req;
dma_desc_ep0.status.b.sts = 0;
dma_desc_ep0.status.b.bs = 0x0;
mmio_write_32(DOEPDMA0, ((unsigned long)&(dma_desc_ep0)));
VERBOSE("%s, &ctrl_req:%llx:%x, &dms_desc_ep0:%llx:%x\n",
__func__, (unsigned long)&ctrl_req, (unsigned long)&ctrl_req,
(unsigned long)&dma_desc_ep0, (unsigned long)&dma_desc_ep0);
/* EP0 OUT ENABLE CLEARNAK */
data = mmio_read_32(DOEPCTL0);
mmio_write_32(DOEPCTL0, (data | 0x84000000));
VERBOSE("exit reset_endpoints. \n");
}
static int usb_drv_request_endpoint(int type, int dir)
{
int ep = 1; /*FIXME*/
unsigned int newbits, data;
newbits = (type << 18) | 0x10000000;
/*
* (type << 18):Endpoint Type (EPType)
* 0x10000000:Endpoint Enable (EPEna)
* 0x000C000:Endpoint Type (EPType);Hardcoded to 00 for control.
* (ep<<22):TxFIFO Number (TxFNum)
* 0x20000:NAK Status (NAKSts);The core is transmitting NAK handshakes on this endpoint.
*/
if (dir) { // IN: to host
data = mmio_read_32(DIEPCTL(ep));
data &= ~0x000c0000;
data |= newbits | (ep << 22) | 0x20000;
mmio_write_32(DIEPCTL(ep), data);
} else { // OUT: to device
data = mmio_read_32(DOEPCTL(ep));
data &= ~0x000c0000;
data |= newbits;
mmio_write_32(DOEPCTL(ep), data);
}
endpoints[ep].active = 1; // true
return ep | dir;
}
void usb_drv_release_endpoint(int ep)
{
ep = ep % NUM_ENDPOINTS;
if (ep < 1 || ep > NUM_ENDPOINTS)
return;
endpoints[ep].active = 0;
}
void usb_config(void)
{
unsigned int data;
INFO("enter usb_config\n");
mmio_write_32(GDFIFOCFG, DATA_FIFO_CONFIG);
mmio_write_32(GRXFSIZ, RX_SIZE);
mmio_write_32(GNPTXFSIZ, ENDPOINT_TX_SIZE);
mmio_write_32(DIEPTXF1, DATA_IN_ENDPOINT_TX_FIFO1);
mmio_write_32(DIEPTXF2, DATA_IN_ENDPOINT_TX_FIFO2);
mmio_write_32(DIEPTXF3, DATA_IN_ENDPOINT_TX_FIFO3);
mmio_write_32(DIEPTXF4, DATA_IN_ENDPOINT_TX_FIFO4);
mmio_write_32(DIEPTXF5, DATA_IN_ENDPOINT_TX_FIFO5);
mmio_write_32(DIEPTXF6, DATA_IN_ENDPOINT_TX_FIFO6);
mmio_write_32(DIEPTXF7, DATA_IN_ENDPOINT_TX_FIFO7);
mmio_write_32(DIEPTXF8, DATA_IN_ENDPOINT_TX_FIFO8);
mmio_write_32(DIEPTXF9, DATA_IN_ENDPOINT_TX_FIFO9);
mmio_write_32(DIEPTXF10, DATA_IN_ENDPOINT_TX_FIFO10);
mmio_write_32(DIEPTXF11, DATA_IN_ENDPOINT_TX_FIFO11);
mmio_write_32(DIEPTXF12, DATA_IN_ENDPOINT_TX_FIFO12);
mmio_write_32(DIEPTXF13, DATA_IN_ENDPOINT_TX_FIFO13);
mmio_write_32(DIEPTXF14, DATA_IN_ENDPOINT_TX_FIFO14);
mmio_write_32(DIEPTXF15, DATA_IN_ENDPOINT_TX_FIFO15);
/*Init global csr register.*/
/*
* set Periodic TxFIFO Empty Level,
* Non-Periodic TxFIFO Empty Level,
* Enable DMA, Unmask Global Intr
*/
INFO("USB: DMA mode.\n");
mmio_write_32(GAHBCFG, GAHBCFG_CTRL_MASK);
/*select 8bit UTMI+, ULPI Inerface*/
INFO("USB ULPI PHY\n");
mmio_write_32(GUSBCFG, 0x2400);
/* Detect usb work mode,host or device? */
do {
data = mmio_read_32(GINTSTS);
} while (data & GINTSTS_CURMODE_HOST);
VERBOSE("Enter device mode\n");
udelay(3);
/*Init global and device mode csr register.*/
/*set Non-Zero-Length status out handshake */
data = (0x20 << DCFG_EPMISCNT_SHIFT) | DCFG_NZ_STS_OUT_HSHK;
mmio_write_32(DCFG, data);
/* Interrupt unmask: IN event, OUT event, bus reset */
data = GINTSTS_OEPINT | GINTSTS_IEPINT | GINTSTS_ENUMDONE |
GINTSTS_USBRST | GINTSTS_USBSUSP | GINTSTS_ERLYSUSP |
GINTSTS_GOUTNAKEFF;
mmio_write_32(GINTMSK, data);
do {
data = mmio_read_32(GINTSTS) & GINTSTS_ENUMDONE;
} while (data);
VERBOSE("USB Enum Done.\n");
/* Clear any pending OTG Interrupts */
mmio_write_32(GOTGINT, ~0);
/* Clear any pending interrupts */
mmio_write_32(GINTSTS, ~0);
mmio_write_32(GINTMSK, ~0);
data = mmio_read_32(GOTGINT);
data &= ~0x3000;
mmio_write_32(GOTGINT, data);
/*endpoint settings cfg*/
reset_endpoints();
udelay(1);
/*init finish. and ready to transfer data*/
/* Soft Disconnect */
mmio_write_32(DCTL, 0x802);
udelay(10000);
/* Soft Reconnect */
mmio_write_32(DCTL, 0x800);
VERBOSE("exit usb_config.\n");
}
void usb_drv_set_address(int address)
{
unsigned int cfg;
cfg = mmio_read_32(DCFG);
cfg &= ~0x7F0;
cfg |= address << 4;
mmio_write_32(DCFG, cfg); // 0x7F0: device address
}
static void ep_send(int ep, const void *ptr, int len)
{
unsigned int data;
endpoints[ep].busy = 1; // true
endpoints[ep].size = len;
/* EPx OUT ACTIVE */
data = mmio_read_32(DIEPCTL(ep)) | DXEPCTL_USBACTEP;
mmio_write_32(DIEPCTL(ep), data);
/* set DMA Address */
if (!len) {
/* send one empty packet */
dma_desc_in.buf = 0;
} else {
dma_desc_in.buf = (unsigned long)ptr;
}
dma_desc_in.status.b.bs = 0x3;
dma_desc_in.status.b.l = 1;
dma_desc_in.status.b.ioc = 1;
dma_desc_in.status.b.sp = 1;
dma_desc_in.status.b.sts = 0;
dma_desc_in.status.b.bs = 0x0;
dma_desc_in.status.b.bytes = len;
mmio_write_32(DIEPDMA(ep), (unsigned long)&dma_desc_in);
data = mmio_read_32(DIEPCTL(ep));
data |= DXEPCTL_EPENA | DXEPCTL_CNAK | DXEPCTL_NEXTEP(ep + 1);
mmio_write_32(DIEPCTL(ep), data);
}
void usb_drv_stall(int endpoint, char stall, char in)
{
unsigned int data;
/*
* STALL Handshake (Stall)
*/
data = mmio_read_32(DIEPCTL(endpoint));
if (in) {
if (stall)
mmio_write_32(DIEPCTL(endpoint), data | 0x00200000);
else
mmio_write_32(DIEPCTL(endpoint), data & ~0x00200000);
} else {
if (stall)
mmio_write_32(DOEPCTL(endpoint), data | 0x00200000);
else
mmio_write_32(DOEPCTL(endpoint), data & ~0x00200000);
}
}
int usb_drv_send_nonblocking(int endpoint, const void *ptr, int len)
{
VERBOSE("%s, endpoint = %d, ptr = 0x%x, Len=%d.\n",
__func__, endpoint, ptr, len);
ep_send(endpoint % NUM_ENDPOINTS, ptr, len);
return 0;
}
void usb_drv_cancel_all_transfers(void)
{
reset_endpoints();
}
int hiusb_epx_tx(unsigned ep, void *buf, unsigned len)
{
int blocksize,packets;
unsigned int epints;
unsigned int cycle = 0;
unsigned int data;
endpoints[ep].busy = 1; //true
endpoints[ep].size = len;
while (mmio_read_32(GINTSTS) & 0x40) {
data = mmio_read_32(DCTL);
data |= 0x100;
mmio_write_32(DCTL, data);
}
data = mmio_read_32(DIEPCTL(ep));
data |= 0x08000000;
mmio_write_32(DIEPCTL(ep), data);
/* EPx OUT ACTIVE */
mmio_write_32(DIEPCTL(ep), data | 0x8000);
if (!ep) {
blocksize = 64;
} else {
blocksize = usb_drv_port_speed() ? USB_BLOCK_HIGH_SPEED_SIZE : 64;
}
packets = (len + blocksize - 1) / blocksize;
if (!len) {
/* one empty packet */
mmio_write_32(DIEPTSIZ(ep), 1 << 19);
/* NULL */
dma_desc_in.status.b.bs = 0x3;
dma_desc_in.status.b.l = 1;
dma_desc_in.status.b.ioc = 1;
dma_desc_in.status.b.sp = last_one;
dma_desc_in.status.b.bytes = 0;
dma_desc_in.buf = 0;
dma_desc_in.status.b.sts = 0;
dma_desc_in.status.b.bs = 0x0;
mmio_write_32(DIEPDMA(ep), (unsigned long)&dma_desc_in);
} else {
mmio_write_32(DIEPTSIZ(ep), len | (packets << 19));
dma_desc_in.status.b.bs = 0x3;
dma_desc_in.status.b.l = 1;
dma_desc_in.status.b.ioc = 1;
dma_desc_in.status.b.sp = last_one;
dma_desc_in.status.b.bytes = len;
dma_desc_in.buf = (unsigned long)buf;
dma_desc_in.status.b.sts = 0;
dma_desc_in.status.b.bs = 0x0;
mmio_write_32(DIEPDMA(ep), (unsigned long)&dma_desc_in);
}
cycle = 0;
while(1){
data = mmio_read_32(DIEPINT(ep));
if ((data & 0x2000) || (cycle > 10000)) {
if (cycle > 10000) {
NOTICE("Phase 2:ep(%d) status, DIEPCTL(%d) is [0x%x],"
"DTXFSTS(%d) is [0x%x], DIEPINT(%d) is [0x%x],"
"DIEPTSIZ(%d) is [0x%x] GINTSTS is [0x%x]\n",
ep, ep, data,
ep, mmio_read_32(DTXFSTS(ep)),
ep, mmio_read_32(DIEPINT(ep)),
ep, mmio_read_32(DIEPTSIZ(ep)),
mmio_read_32(GINTSTS));
}
break;
}
cycle++;
udelay(10);
}
VERBOSE("ep(%d) enable, DIEPCTL(%d) is [0x%x], DTXFSTS(%d) is [0x%x],"
"DIEPINT(%d) is [0x%x], DIEPTSIZ(%d) is [0x%x] \n",
ep, ep, mmio_read_32(DIEPCTL(ep)),
ep, mmio_read_32(DTXFSTS(ep)),
ep, mmio_read_32(DIEPINT(ep)),
ep, mmio_read_32(DIEPTSIZ(ep)));
__asm__ volatile("dsb sy\n"
"isb sy\n");
data = mmio_read_32(DIEPCTL(ep));
data |= 0x84000000;
/* epena & cnak*/
mmio_write_32(DIEPCTL(ep), data);
__asm__ volatile("dsb sy\n"
"isb sy\n");
cycle = 0;
while (1) {
epints = mmio_read_32(DIEPINT(ep)) & 1;
if ((mmio_read_32(GINTSTS) & 0x40000) && epints) {
VERBOSE("Tx succ:ep(%d), DTXFSTS(%d) is [0x%x] \n",
ep, ep, mmio_read_32(DTXFSTS(ep)));
mmio_write_32(DIEPINT(ep), epints);
if (endpoints[ep].busy) {
endpoints[ep].busy = 0;//false
endpoints[ep].rc = 0;
endpoints[ep].done = 1;//true
}
break;
}
cycle++;
udelay(10);
VERBOSE("loop for intr: ep(%d), DIEPCTL(%d) is [0x%x], ",
"DTXFSTS(%d) is [0x%x], DIEPINT(%d) is [0x%x] \n",
ep, ep, mmio_read_32(DIEPCTL(ep)),
ep, mmio_read_32(DTXFSTS(ep)),
ep, mmio_read_32(DIEPINT(ep)));
if (cycle > 1000000) {
WARN("Wait IOC intr over 10s! USB will reset\n");
usb_need_reset = 1;
return 1;
}
}
cycle = 0;
while (1) {
if ((mmio_read_32(DIEPINT(ep)) & 0x2000) || (cycle > 100000)) {
if (cycle > 100000){
WARN("all wait cycle is [%d]\n",cycle);
}
break;
}
cycle++;
udelay(10);
}
return 0;
}
int hiusb_epx_rx(unsigned ep, void *buf, unsigned len)
{
unsigned int blocksize = 0, data;
int packets;
VERBOSE("ep%d rx, len = 0x%x, buf = 0x%x.\n", ep, len, buf);
endpoints[ep].busy = 1;//true
/* EPx UNSTALL */
data = mmio_read_32(DOEPCTL(ep)) & ~0x00200000;
mmio_write_32(DOEPCTL(ep), data);
/* EPx OUT ACTIVE */
data = mmio_read_32(DOEPCTL(ep)) | 0x8000;
mmio_write_32(DOEPCTL(ep), data);
blocksize = usb_drv_port_speed() ? USB_BLOCK_HIGH_SPEED_SIZE : 64;
packets = (len + blocksize - 1) / blocksize;
#define MAX_RX_PACKET 0x3FF
/*Max recv packets is 1023*/
if (packets > MAX_RX_PACKET) {
endpoints[ep].size = MAX_RX_PACKET * blocksize;
len = MAX_RX_PACKET * blocksize;
} else {
endpoints[ep].size = len;
}
if (!len) {
/* one empty packet */
mmio_write_32(DOEPTSIZ(ep), 1 << 19);
//NULL /* dummy address */
dma_desc.status.b.bs = 0x3;
dma_desc.status.b.mtrf = 0;
dma_desc.status.b.sr = 0;
dma_desc.status.b.l = 1;
dma_desc.status.b.ioc = 1;
dma_desc.status.b.sp = 0;
dma_desc.status.b.bytes = 0;
dma_desc.buf = 0;
dma_desc.status.b.sts = 0;
dma_desc.status.b.bs = 0x0;
mmio_write_32(DOEPDMA(ep), (unsigned long)&dma_desc);
} else {
if (len >= blocksize * 64) {
rx_desc_bytes = blocksize*64;
} else {
rx_desc_bytes = len;
}
VERBOSE("rx len %d, rx_desc_bytes %d \n",len,rx_desc_bytes);
dma_desc.status.b.bs = 0x3;
dma_desc.status.b.mtrf = 0;
dma_desc.status.b.sr = 0;
dma_desc.status.b.l = 1;
dma_desc.status.b.ioc = 1;
dma_desc.status.b.sp = 0;
dma_desc.status.b.bytes = rx_desc_bytes;
dma_desc.buf = (unsigned long)buf;
dma_desc.status.b.sts = 0;
dma_desc.status.b.bs = 0x0;
mmio_write_32(DOEPDMA(ep), (unsigned long)&dma_desc);
}
/* EPx OUT ENABLE CLEARNAK */
data = mmio_read_32(DOEPCTL(ep));
data |= 0x84000000;
mmio_write_32(DOEPCTL(ep), data);
return 0;
}
int usb_queue_req(struct usb_endpoint *ept, struct usb_request *req)
{
if (ept->in)
hiusb_epx_tx(ept->num, req->buf, req->length);
else
hiusb_epx_rx(ept->num, req->buf, req->length);
return 0;
}
static void rx_cmd(void)
{
struct usb_request *req = &rx_req;
req->buf = cmdbuf;
req->length = RX_REQ_LEN;
req->complete = usb_rx_cmd_complete;
usb_queue_req(&ep1out, req);
}
static void rx_data(void)
{
struct usb_request *req = &rx_req;
req->buf = (void *)((unsigned long) rx_addr);
req->length = rx_length;
req->complete = usb_rx_data_complete;
usb_queue_req(&ep1out, req);
}
void tx_status(const char *status)
{
struct usb_request *req = &tx_req;
int len = strlen(status);
memcpy(req->buf, status, (unsigned int)len);
req->length = (unsigned int)len;
req->complete = 0;
usb_queue_req(&ep1in, req);
}
void fastboot_tx_status(const char *status)
{
tx_status(status);
rx_cmd();
}
void tx_dump_page(const char *ptr, int len)
{
struct usb_request *req = &tx_req;
memcpy(req->buf, ptr, (unsigned int)len);
req->length = (unsigned int)len;
req->complete = 0;
usb_queue_req(&ep1in, req);
}
static void usb_rx_data_complete(unsigned actual, int status)
{
if(status != 0)
return;
if(actual > rx_length) {
actual = rx_length;
}
rx_addr += actual;
rx_length -= actual;
if(rx_length > 0) {
rx_data();
} else {
tx_status("OKAY");
rx_cmd();
}
}
static void usb_status(unsigned online, unsigned highspeed)
{
if (online) {
INFO("usb: online (%s)\n", highspeed ? "highspeed" : "fullspeed");
rx_cmd();
}
}
void usb_handle_control_request(setup_packet* req)
{
const void* addr = NULL;
int size = -1;
int i;
int maxpacket;
unsigned int data;
char *serialno;
struct usb_endpoint_descriptor epx;
struct usb_config_bundle const_bundle = {
.config = {
.bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = sizeof(struct usb_config_descriptor) +
sizeof(struct usb_interface_descriptor) +
sizeof(struct usb_endpoint_descriptor) *
USB_NUM_ENDPOINTS,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = USB_CONFIG_ATT_ONE,
.bMaxPower = 0x80
},
.interface = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = USB_NUM_ENDPOINTS,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 0x42,
.bInterfaceProtocol = 0x03,
.iInterface = 0
}
};
/* avoid to hang on accessing unaligned memory */
struct usb_endpoint_descriptor const_ep1 = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 0,
.bInterval = 0
};
struct usb_endpoint_descriptor const_ep2 = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x01,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 0,
.bInterval = 1
};
struct usb_device_descriptor const_device = {
.bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 0x40,
.idVendor = 0x18d1,
.idProduct = 0xd00d,
.bcdDevice = 0x0100,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1
};
memcpy(&config_bundle, &const_bundle, sizeof(struct usb_config_bundle));
memcpy(&config_bundle.ep1, &const_ep1, sizeof(struct usb_endpoint_descriptor));
memcpy(&config_bundle.ep2, &const_ep2, sizeof(struct usb_endpoint_descriptor));
memcpy(&device_descriptor, &const_device,
sizeof(struct usb_device_descriptor));
switch (req->request) {
case USB_REQ_GET_STATUS:
if (req->type == USB_DIR_IN)
ctrl_resp[0] = 1;
else
ctrl_resp[0] = 0;
ctrl_resp[1] = 0;
addr = ctrl_resp;
size = 2;
break;
case USB_REQ_CLEAR_FEATURE:
if ((req->type == USB_RECIP_ENDPOINT) &&
(req->value == USB_ENDPOINT_HALT))
usb_drv_stall(req->index & 0xf, 0, req->index >> 7);
size = 0;
break;
case USB_REQ_SET_FEATURE:
size = 0;
break;
case USB_REQ_SET_ADDRESS:
size = 0;
usb_drv_cancel_all_transfers(); // all endpoints reset
usb_drv_set_address(req->value); // set device address
break;
case USB_REQ_GET_DESCRIPTOR:
VERBOSE("USB_REQ_GET_DESCRIPTOR: 0x%x\n", req->value >> 8);
switch (req->value >> 8) {
case USB_DT_DEVICE:
addr = &device_descriptor;
size = sizeof(device_descriptor);
VERBOSE("Get device descriptor.\n");
break;
case USB_DT_OTHER_SPEED_CONFIG:
case USB_DT_CONFIG:
if ((req->value >> 8) == USB_DT_CONFIG) {
maxpacket = usb_drv_port_speed() ? USB_BLOCK_HIGH_SPEED_SIZE : 64;
config_bundle.config.bDescriptorType = USB_DT_CONFIG;
} else {
maxpacket = usb_drv_port_speed() ? 64 : USB_BLOCK_HIGH_SPEED_SIZE;
config_bundle.config.bDescriptorType = USB_DT_OTHER_SPEED_CONFIG;
}
/* avoid hang when access unaligned structure */
memcpy(&epx, &config_bundle.ep1, sizeof(struct usb_endpoint_descriptor));
epx.wMaxPacketSize = maxpacket;
memcpy(&config_bundle.ep1, &epx, sizeof(struct usb_endpoint_descriptor));
memcpy(&epx, &config_bundle.ep2, sizeof(struct usb_endpoint_descriptor));
epx.wMaxPacketSize = maxpacket;
memcpy(&config_bundle.ep2, &epx, sizeof(struct usb_endpoint_descriptor));
addr = &config_bundle;
size = sizeof(config_bundle);
VERBOSE("Get config descriptor.\n");
break;
case USB_DT_STRING:
switch (req->value & 0xff) {
case 0:
addr = &lang_descriptor;
size = lang_descriptor.bLength;
break;
case 1:
addr = &string_devicename;
size = 14;
break;
case 2:
addr = &string_devicename;
size = string_devicename.bLength;
break;
case 3:
serialno = load_serialno();
if (serialno == NULL) {
addr = &serial_string_descriptor;
size = serial_string_descriptor.bLength;
} else {
i = 0;
memcpy((void *)&serial_string,
(void *)&serial_string_descriptor,
sizeof(serial_string));
while (1) {
serial_string.wString[i] = serialno[i];
if (serialno[i] == '\0')
break;
i++;
}
addr = &serial_string;
size = serial_string.bLength;
}
break;
default:
break;
}
break;
default:
break;
}
break;
case USB_REQ_GET_CONFIGURATION:
ctrl_resp[0] = 1;
addr = ctrl_resp;
size = 1;
break;
case USB_REQ_SET_CONFIGURATION:
usb_drv_cancel_all_transfers(); // call reset_endpoints reset all EPs
usb_drv_request_endpoint(USB_ENDPOINT_XFER_BULK, USB_DIR_OUT);
usb_drv_request_endpoint(USB_ENDPOINT_XFER_BULK, USB_DIR_IN);
/*
* 0x10088800:
* 1:EP enable; 8:EP type:BULK; 8:USB Active Endpoint; 8:Next Endpoint
*/
data = mmio_read_32(DIEPCTL1) | 0x10088800;
mmio_write_32(DIEPCTL1, data);
data = mmio_read_32(DIEPCTL(1)) | 0x08000000;
mmio_write_32(DIEPCTL(1), data);
/* Enable interrupts on all endpoints */
mmio_write_32(DAINTMSK, 0xffffffff);
usb_status(req->value? 1 : 0, usb_drv_port_speed() ? 1 : 0);
size = 0;
VERBOSE("Set config descriptor.\n");
/* USB ö<>ٳɹ<D9B3><C9B9><EFBFBD>,<2C><><EFBFBD>ϱ<EFBFBD>ʶ */
g_usb_enum_flag = 1;
break;
default:
break;
}
if (!size) {
usb_drv_send_nonblocking(0, 0, 0); // send an empty packet
} else if (size == -1) { // stall:Applies to non-control, non-isochronous IN and OUT endpoints only.
usb_drv_stall(0, 1, 1); // IN
usb_drv_stall(0, 1, 0); // OUT
} else { // stall:Applies to control endpoints only.
usb_drv_stall(0, 0, 1); // IN
usb_drv_stall(0, 0, 0); // OUT
usb_drv_send_nonblocking(0, addr, size > req->length ? req->length : size);
}
}
/* IRQ handler */
static void usb_poll(void)
{
uint32_t ints;
uint32_t epints, data;
ints = mmio_read_32(GINTSTS); /* interrupt status */
if ((ints & 0xc3010) == 0)
return;
/*
* bus reset
* The core sets this bit to indicate that a reset is detected on the USB.
*/
if (ints & GINTSTS_USBRST) {
VERBOSE("bus reset intr\n");
/*set Non-Zero-Length status out handshake */
/*
* DCFG:This register configures the core in Device mode after power-on
* or after certain control commands or enumeration. Do not make changes
* to this register after initial programming.
* Send a STALL handshake on a nonzero-length status OUT transaction and
* do not send the received OUT packet to the application.
*/
mmio_write_32(DCFG, 0x800004);
reset_endpoints();
}
/*
* enumeration done, we now know the speed
* The core sets this bit to indicate that speed enumeration is complete. The
* application must read the Device Status (DSTS) register to obtain the
* enumerated speed.
*/
if (ints & GINTSTS_ENUMDONE) {
/* Set up the maximum packet sizes accordingly */
uint32_t maxpacket = usb_drv_port_speed() ? USB_BLOCK_HIGH_SPEED_SIZE : 64; // high speed maxpacket=512
VERBOSE("enum done intr. Maxpacket:%d\n", maxpacket);
//Set Maximum In Packet Size (MPS)
data = mmio_read_32(DIEPCTL1) & ~0x000003ff;
mmio_write_32(DIEPCTL1, data | maxpacket);
//Set Maximum Out Packet Size (MPS)
data = mmio_read_32(DOEPCTL1) & ~0x000003ff;
mmio_write_32(DOEPCTL1, data | maxpacket);
}
/*
* IN EP event
* The core sets this bit to indicate that an interrupt is pending on one of the IN
* endpoints of the core (in Device mode). The application must read the
* Device All Endpoints Interrupt (DAINT) register to determine the exact
* number of the IN endpoint on which the interrupt occurred, and then read
* the corresponding Device IN Endpoint-n Interrupt (DIEPINTn) register to
* determine the exact cause of the interrupt. The application must clear the
* appropriate status bit in the corresponding DIEPINTn register to clear this bit.
*/
if (ints & GINTSTS_IEPINT) {
epints = mmio_read_32(DIEPINT0);
mmio_write_32(DIEPINT0, epints);
//VERBOSE("IN EP event,ints:0x%x, DIEPINT0:%x, DAINT:%x, DAINTMSK:%x.\n",
// ints, epints, mmio_read_32(DAINT), mmio_read_32(DAINTMSK));
if (epints & 0x1) { /* Transfer Completed Interrupt (XferCompl) */
VERBOSE("TX completed.DIEPTSIZ(0) = 0x%x.\n", mmio_read_32(DIEPTSIZ0));
/*FIXME,Maybe you can use bytes*/
/*int bytes = endpoints[0].size - (DIEPTSIZ(0) & 0x3FFFF);*/ //actual transfer
if (endpoints[0].busy) {
endpoints[0].busy = 0;//false
endpoints[0].rc = 0;
endpoints[0].done = 1;//true
}
}
if (epints & 0x4) { /* AHB error */
WARN("AHB error on IN EP0.\n");
}
if (epints & 0x8) { /* Timeout */
WARN("Timeout on IN EP0.\n");
if (endpoints[0].busy) {
endpoints[0].busy = 1;//false
endpoints[0].rc = 1;
endpoints[0].done = 1;//true
}
}
}
/*
* OUT EP event
* The core sets this bit to indicate that an interrupt is pending on one of the
* OUT endpoints of the core (in Device mode). The application must read the
* Device All Endpoints Interrupt (DAINT) register to determine the exact
* number of the OUT endpoint on which the interrupt occurred, and then read
* the corresponding Device OUT Endpoint-n Interrupt (DOEPINTn) register
* to determine the exact cause of the interrupt. The application must clear the
* appropriate status bit in the corresponding DOEPINTn register to clear this bit.
*/
if (ints & GINTSTS_OEPINT) {
/* indicates the status of an endpoint
* with respect to USB- and AHB-related events. */
epints = mmio_read_32(DOEPINT(0));
//VERBOSE("OUT EP event,ints:0x%x, DOEPINT0:%x, DAINT:%x, DAINTMSK:%x.\n",
// ints, epints, mmio_read_32(DAINT), mmio_read_32(DAINTMSK));
if (epints) {
mmio_write_32(DOEPINT(0), epints);
/* Transfer completed */
if (epints & DXEPINT_XFERCOMPL) {
/*FIXME,need use bytes*/
VERBOSE("EP0 RX completed. DOEPTSIZ(0) = 0x%x.\n",
mmio_read_32(DOEPTSIZ(0)));
if (endpoints[0].busy) {
endpoints[0].busy = 0;
endpoints[0].rc = 0;
endpoints[0].done = 1;
}
}
if (epints & DXEPINT_AHBERR) { /* AHB error */
WARN("AHB error on OUT EP0.\n");
}
/*
* IN Token Received When TxFIFO is Empty (INTknTXFEmp)
* Indicates that an IN token was received when the associated TxFIFO (periodic/nonperiodic)
* was empty. This interrupt is asserted on the endpoint for which the IN token
* was received.
*/
if (epints & DXEPINT_SETUP) { /* SETUP phase done */
VERBOSE("Setup phase \n");
data = mmio_read_32(DIEPCTL(0)) | DXEPCTL_SNAK;
mmio_write_32(DIEPCTL(0), data);
data = mmio_read_32(DOEPCTL(0)) | DXEPCTL_SNAK;
mmio_write_32(DOEPCTL(0), data);
/*clear IN EP intr*/
mmio_write_32(DIEPINT(0), ~0);
usb_handle_control_request((setup_packet *)&ctrl_req);
}
/* Make sure EP0 OUT is set up to accept the next request */
/* memset(p_ctrlreq, 0, NUM_ENDPOINTS*8); */
data = DOEPTSIZ0_SUPCNT(3) | DOEPTSIZ0_PKTCNT |
(64 << DOEPTSIZ0_XFERSIZE_SHIFT);
mmio_write_32(DOEPTSIZ0, data);
/*
* IN Token Received When TxFIFO is Empty (INTknTXFEmp)
* Indicates that an IN token was received when the associated TxFIFO (periodic/nonperiodic)
* was empty. This interrupt is asserted on the endpoint for which the IN token
* was received.
*/
// notes that:the compulsive conversion is expectable.
// Holds the start address of the external memory for storing or fetching endpoint data.
dma_desc_ep0.status.b.bs = 0x3;
dma_desc_ep0.status.b.mtrf = 0;
dma_desc_ep0.status.b.sr = 0;
dma_desc_ep0.status.b.l = 1;
dma_desc_ep0.status.b.ioc = 1;
dma_desc_ep0.status.b.sp = 0;
dma_desc_ep0.status.b.bytes = 64;
dma_desc_ep0.buf = (uintptr_t)&ctrl_req;
dma_desc_ep0.status.b.sts = 0;
dma_desc_ep0.status.b.bs = 0x0;
mmio_write_32(DOEPDMA0, (uintptr_t)&dma_desc_ep0);
// endpoint enable; clear NAK
mmio_write_32(DOEPCTL0, 0x84000000);
}
epints = mmio_read_32(DOEPINT1);
if(epints) {
mmio_write_32(DOEPINT1, epints);
VERBOSE("OUT EP1: epints :0x%x,DOEPTSIZ1 :0x%x.\n",epints, mmio_read_32(DOEPTSIZ1));
/* Transfer Completed Interrupt (XferCompl);Transfer completed */
if (epints & DXEPINT_XFERCOMPL) {
/* ((readl(DOEPTSIZ(1))) & 0x7FFFF is Transfer Size (XferSize) */
/*int bytes = (p_endpoints + 1)->size - ((readl(DOEPTSIZ(1))) & 0x7FFFF);*/
int bytes = rx_desc_bytes - dma_desc.status.b.bytes;
VERBOSE("OUT EP1: recv %d bytes \n",bytes);
if (endpoints[1].busy) {
endpoints[1].busy = 0;
endpoints[1].rc = 0;
endpoints[1].done = 1;
rx_req.complete(bytes, 0);
}
}
if (epints & DXEPINT_AHBERR) { /* AHB error */
WARN("AHB error on OUT EP1.\n");
}
if (epints & DXEPINT_SETUP) { /* SETUP phase done */
WARN("SETUP phase done on OUT EP1.\n");
}
}
}
/* write to clear interrupts */
mmio_write_32(GINTSTS, ints);
}
#define EYE_PATTERN 0x70533483
/*
* pico phy exit siddq, nano phy enter siddq,
* and open the clock of pico phy and dvc,
*/
static void dvc_and_picophy_init_chip(void)
{
unsigned int data;
/* enable USB clock */
mmio_write_32(PERI_SC_PERIPH_CLKEN0, PERI_CLK0_USBOTG);
do {
data = mmio_read_32(PERI_SC_PERIPH_CLKSTAT0);
} while ((data & PERI_CLK0_USBOTG) == 0);
/* out of reset */
mmio_write_32(PERI_SC_PERIPH_RSTDIS0,
PERI_RST0_USBOTG_BUS | PERI_RST0_POR_PICOPHY |
PERI_RST0_USBOTG | PERI_RST0_USBOTG_32K);
do {
data = mmio_read_32(PERI_SC_PERIPH_RSTSTAT0);
data &= PERI_RST0_USBOTG_BUS | PERI_RST0_POR_PICOPHY |
PERI_RST0_USBOTG | PERI_RST0_USBOTG_32K;
} while (data);
mmio_write_32(PERI_SC_PERIPH_CTRL8, EYE_PATTERN);
/* configure USB PHY */
data = mmio_read_32(PERI_SC_PERIPH_CTRL4);
/* make PHY out of low power mode */
data &= ~PERI_CTRL4_PICO_SIDDQ;
/* detect VBUS by external circuit, switch D+ to 1.5KOhm pullup */
data |= PERI_CTRL4_PICO_VBUSVLDEXTSEL | PERI_CTRL4_PICO_VBUSVLDEXT;
data &= ~PERI_CTRL4_FPGA_EXT_PHY_SEL;
/* select PHY */
data &= ~PERI_CTRL4_OTG_PHY_SEL;
mmio_write_32(PERI_SC_PERIPH_CTRL4, data);
udelay(1000);
data = mmio_read_32(PERI_SC_PERIPH_CTRL5);
data &= ~PERI_CTRL5_PICOPHY_BC_MODE;
mmio_write_32(PERI_SC_PERIPH_CTRL5, data);
udelay(20000);
}
int init_usb(void)
{
static int init_flag = 0;
uint32_t data;
if (init_flag == 0) {
memset(&ctrl_req, 0, sizeof(setup_packet));
memset(&ctrl_resp, 0, 2);
memset(&endpoints, 0, sizeof(struct ep_type) * NUM_ENDPOINTS);
memset(&dma_desc, 0, sizeof(struct dwc_otg_dev_dma_desc));
memset(&dma_desc_ep0, 0, sizeof(struct dwc_otg_dev_dma_desc));
memset(&dma_desc_in, 0, sizeof(struct dwc_otg_dev_dma_desc));
}
VERBOSE("Pico PHY and DVC init start.\n");
dvc_and_picophy_init_chip();
VERBOSE("Pico PHY and DVC init done.\n");
/* wait for OTG AHB master idle */
do {
data = mmio_read_32(GRSTCTL) & GRSTCTL_AHBIDLE;
} while (data == 0);
VERBOSE("Reset usb controller\n");
/* OTG: Assert software reset */
mmio_write_32(GRSTCTL, GRSTCTL_CSFTRST);
/* wait for OTG to ack reset */
while (mmio_read_32(GRSTCTL) & GRSTCTL_CSFTRST);
/* wait for OTG AHB master idle */
while ((mmio_read_32(GRSTCTL) & GRSTCTL_AHBIDLE) == 0);
VERBOSE("Reset usb controller done\n");
usb_config();
VERBOSE("exit usb_init()\n");
return 0;
}
#define LOCK_STATE_LOCKED 0
#define LOCK_STATE_UNLOCKED 1
#define LOCK_STATE_RELOCKED 2
#define FB_MAX_FILE_SIZE (256 * 1024 * 1024)
static struct ptentry *flash_ptn = NULL;
static void fb_getvar(char *cmdbuf)
{
char response[64];
char part_name[32];
int bytes;
struct ptentry *ptn = 0;
if (!strncmp(cmdbuf + 7, "max-download-size", 17)) {
bytes = sprintf(response, "OKAY0x%08x",
FB_MAX_FILE_SIZE);
response[bytes] = '\0';
tx_status(response);
rx_cmd();
} else if (!strncmp(cmdbuf + 7, "partition-type:", 15)) {
bytes = sprintf(part_name, "%s", cmdbuf + 22);
ptn = find_ptn(part_name);
if (ptn == NULL) {
bytes = sprintf(response, "FAIL%s",
"invalid partition");
response[bytes] = '\0';
flash_ptn = NULL;
} else {
flash_ptn = ptn;
if (!strncmp(cmdbuf +22, "system", 6) || !strncmp(cmdbuf +22, "userdata", 8) ||
!strncmp(cmdbuf +22, "cache", 5)) {
bytes = sprintf(response, "OKAYext4");
response[bytes] = '\0';
} else {
bytes = sprintf(response, "OKAYraw");
response[bytes] = '\0';
}
}
tx_status(response);
rx_cmd();
} else if (!strncmp(cmdbuf + 7, "partition-size:", 15)) {
bytes = sprintf(part_name, "%s", cmdbuf + 22);
ptn = find_ptn(part_name);
if (ptn == NULL) {
bytes = sprintf(response, "FAIL%s",
"invalid partition");
response[bytes] = '\0';
flash_ptn = NULL;
} else {
bytes = sprintf(response, "OKAY%llx",ptn->length);
response[bytes] = '\0';
flash_ptn = ptn;
}
tx_status(response);
rx_cmd();
} else if (!strncmp(cmdbuf + 7, "serialno", 8)) {
bytes = sprintf(response, "OKAY%s",
load_serialno());
response[bytes] = '\0';
tx_status(response);
rx_cmd();
} else if (!strncmp(cmdbuf + 7, "version-bootloader", 18)) {
bytes = sprintf(response, "OKAY%s", VERSION_BOOTLOADER);
response[bytes] = '\0';
tx_status(response);
rx_cmd();
} else if (!strncmp(cmdbuf + 7, "version-baseband", 16)) {
bytes = sprintf(response, "OKAYN/A");
response[bytes] = '\0';
tx_status(response);
rx_cmd();
} else if (!strncmp(cmdbuf + 7, "product", 8)) {
bytes = sprintf(response, "OKAYhikey");
response[bytes] = '\0';
tx_status(response);
rx_cmd();
} else {
bytes = sprintf(response, "FAIL%s",
"unknown var");
response[bytes] = '\0';
tx_status(response);
rx_cmd();
}
}
/* FIXME: do not support endptr yet */
static unsigned long strtoul(const char *nptr, char **endptr, int base)
{
unsigned long step, data;
int i;
if (base == 0)
step = 10;
else if ((base < 2) || (base > 36)) {
VERBOSE("%s: invalid base %d\n", __func__, base);
return 0;
} else
step = base;
for (i = 0, data = 0; ; i++) {
if (nptr[i] == '\0')
break;
else if (!isalpha(nptr[i]) && !isdigit(nptr[i])) {
VERBOSE("%s: invalid string %s at %d [%x]\n",
__func__, nptr, i, nptr[i]);
return 0;
} else {
data *= step;
if (isupper(nptr[i]))
data += nptr[i] - 'A' + 10;
else if (islower(nptr[i]))
data += nptr[i] - 'a' + 10;
else if (isdigit(nptr[i]))
data += nptr[i] - '0';
}
}
return data;
}
static void fb_serialno(char *cmdbuf)
{
struct random_serial_num random;
generate_serialno(&random);
flush_random_serialno((unsigned long)&random, sizeof(random));
}
static int fb_assigned_sn(char *cmdbuf)
{
struct random_serial_num random;
int ret;
ret = assign_serialno(cmdbuf, &random);
if (ret < 0)
return ret;
flush_random_serialno((unsigned long)&random, sizeof(random));
return 0;
}
#define FB_DOWNLOAD_BASE 0x20000000
static unsigned long fb_download_base, fb_download_size;
static void fb_download(char *cmdbuf)
{
char response[64];
int bytes;
if (!flash_ptn) {
bytes = sprintf(response, "FAIL%s",
"invalid partition");
response[bytes] = '\0';
tx_status(response);
rx_cmd();
} else {
rx_addr = FB_DOWNLOAD_BASE;
rx_length = strtoul(cmdbuf + 9, NULL, 16);
fb_download_base = rx_addr;
fb_download_size = rx_length;
if (rx_length > FB_MAX_FILE_SIZE) {
bytes = sprintf(response, "FAIL%s",
"file is too large");
response[bytes] = '\0';
tx_status(response);
rx_cmd();
} else {
bytes = sprintf(response, "DATA%08x",
rx_length);
VERBOSE("start:0x%x, length:0x%x, res:%s\n",
rx_addr, rx_length, response);
response[bytes] = '\0';
tx_status(response);
rx_data();
}
}
}
static void fb_flash(char *cmdbuf)
{
flush_user_images(cmdbuf + 6, fb_download_base, fb_download_size);
tx_status("OKAY");
rx_cmd();
}
static void fb_reboot(char *cmdbuf)
{
/* Send the system reset request */
mmio_write_32(AO_SC_SYS_STAT0, 0x48698284);
wfi();
panic();
}
static void usb_rx_cmd_complete(unsigned actual, int stat)
{
if(stat != 0) return;
if(actual > 4095)
actual = 4095;
cmdbuf[actual] = 0;
INFO("cmd :%s\n",cmdbuf);
if(memcmp(cmdbuf, (void *)"reboot", 6) == 0) {
tx_status("OKAY");
fb_reboot(cmdbuf);
return;
} else if (!memcmp(cmdbuf, (void *)"getvar:", 7)) {
fb_getvar(cmdbuf);
return;
} else if (!memcmp(cmdbuf, (void *)"download:", 9)) {
fb_download(cmdbuf);
return;
} else if(memcmp(cmdbuf, (void *)"erase:", 6) == 0) {
/* FIXME erase is not supported but we return success */
tx_status("OKAY");
rx_cmd();
return;
} else if(memcmp(cmdbuf, (void *)"flash:", 6) == 0) {
INFO("recog updatefile\n");
fb_flash(cmdbuf);
return;
} else if(memcmp(cmdbuf, (void *)"boot", 4) == 0) {
INFO(" - OKAY\n");
return;
} else if (memcmp(cmdbuf, (void *)"oem serialno", 12) == 0) {
if (*(cmdbuf + 12) == '\0') {
fb_serialno(cmdbuf);
tx_status("OKAY");
rx_cmd();
return;
} else if (memcmp(cmdbuf + 12, (void *)" set", 4) == 0) {
if (fb_assigned_sn(cmdbuf + 16) == 0) {
tx_status("OKAY");
rx_cmd();
return;
}
}
} else if (memcmp(cmdbuf, (void *)"oem led", 7) == 0) {
if ((*(cmdbuf + 7) >= '1') && (*(cmdbuf + 7) <= '4')) {
int led;
led = *(cmdbuf + 7) - '0';
if (memcmp(cmdbuf + 8, (void *)" on", 3) == 0) {
gpio_set_value(31 + led, 1);
tx_status("OKAY");
rx_cmd();
return;
} else if (memcmp(cmdbuf + 8, (void *)" off", 4) == 0) {
gpio_set_value(31 + led, 0);
tx_status("OKAY");
rx_cmd();
return;
}
}
}
tx_status("FAILinvalid command");
rx_cmd();
}
static void usbloader_init(void)
{
VERBOSE("enter usbloader_init\n");
/*usb sw and hw init*/
init_usb();
/*alloc and init sth for transfer*/
ep1in.num = BULK_IN_EP;
ep1in.in = 1;
ep1in.req = NULL;
ep1in.maxpkt = MAX_PACKET_LEN;
ep1in.next = &ep1in;
ep1out.num = BULK_OUT_EP;
ep1out.in = 0;
ep1out.req = NULL;
ep1out.maxpkt = MAX_PACKET_LEN;
ep1out.next = &ep1out;
cmdbuf = (char *)(rx_req.buf);
VERBOSE("exit usbloader_init\n");
}
void usb_reinit()
{
if (usb_need_reset)
{
usb_need_reset = 0;
init_usb();
}
}
void usb_download(void)
{
usbloader_init();
INFO("Enter downloading mode. Please run fastboot command on Host.\n");
for (;;) {
usb_poll();
usb_reinit();
}
}