diff options
-rw-r--r-- | common/cmd_fastboot.c | 65 | ||||
-rw-r--r-- | drivers/fastboot/fastboot.c | 157 | ||||
-rw-r--r-- | drivers/usb/gadget/ep0.c | 118 | ||||
-rw-r--r-- | drivers/usb/gadget/ep0.h | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/imx_udc.c | 625 | ||||
-rw-r--r-- | include/fastboot.h | 24 | ||||
-rw-r--r-- | include/usb/imx_udc.h | 53 |
7 files changed, 1044 insertions, 2 deletions
diff --git a/common/cmd_fastboot.c b/common/cmd_fastboot.c index 6987f88..ebcbb61 100644 --- a/common/cmd_fastboot.c +++ b/common/cmd_fastboot.c @@ -1609,6 +1609,15 @@ int do_fastboot (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) if (2 == argc) { long try_seconds; char *try_seconds_end; + + if (argv[1][0] == 'q') { + if ((argv[1][1] >= '0') && (argv[1][1] <= '2')) + fastboot_quick(argv[1][1] - '0'); + else + fastboot_quick(0); + } + + /* Check for timeout */ try_seconds = simple_strtol(argv[1], &try_seconds_end, 10); @@ -1758,6 +1767,62 @@ unsigned int fastboot_flash_get_ptn_count(void) return pcount; } +int fastboot_write_mmc(u8 *partition_name, u32 write_len) +{ + struct fastboot_ptentry *ptn; + + char source[32], dest[32], length[32]; + char part_no[32], slot_no[32]; + unsigned int temp; + + memset(source, 0, sizeof(source)); + memset(dest, 0, sizeof(dest)); + memset(length, 0, sizeof(length)); + memset(part_no, 0, sizeof(part_no)); + memset(slot_no, 0, sizeof(slot_no)); + + char *mmc_write[5] = {"mmc", "write", source, dest, length}; + char *mmc_dev[4] = {"mmc", "dev", slot_no, part_no}; + + if (0 == write_len) { + DBG_ERR("WriteMMC with 0 lenght\n"); + return -1; + } + + ptn = fastboot_flash_find_ptn((const char *)partition_name); + if (!ptn) { + DBG_ERR("Partition:'%s' does not exist\n", ptn->name); + return -1; + } + DBG_DEBUG("PTN, name=%s, start=0x%x, leng=0x%x, flags=0x%x, partid=0x%x\n", + ptn->name, ptn->start, ptn->length, ptn->flags, ptn->partition_id); + + sprintf(slot_no, "%d", fastboot_devinfo.dev_id); + sprintf(part_no, "%d", ptn->partition_id); + + DBG_ALWS("Init MMC%s(%s)...\n", slot_no, ptn->name); + if (do_mmcops(NULL, 0, 4, mmc_dev)) { + DBG_ERR("MMC%s(%s) init fail\n", slot_no, ptn->name); + return -1; + } else { + DBG_ALWS("MMC%s(%s) init done\n", slot_no, ptn->name); + } +#define MMC_SATA_BLOCK_SIZE 512 + sprintf(source, "0x%x", CONFIG_FASTBOOT_TRANSFER_BUF); + sprintf(dest, "0x%x", ptn->start); + temp = (write_len + MMC_SATA_BLOCK_SIZE - 1) / MMC_SATA_BLOCK_SIZE; + sprintf(length, "0x%x", temp); + + DBG_ALWS("Writing MMC%s(%s)...", slot_no, ptn->name); + + if (do_mmcops(NULL, 0, 5, mmc_write)) { + DBG_ERR("MMC%s(%s) write fail\n", slot_no, ptn->name); + return -1; + } else { + DBG_ALWS("MMC%s(%s) write done\n", slot_no, ptn->name); + return write_len; + } +} #endif /* CONFIG_FASTBOOT */ diff --git a/drivers/fastboot/fastboot.c b/drivers/fastboot/fastboot.c index 3dcab32..fe0b6b8 100644 --- a/drivers/fastboot/fastboot.c +++ b/drivers/fastboot/fastboot.c @@ -788,3 +788,160 @@ void check_fastboot_mode(void) if (fastboot_check_and_clean_flag()) do_fastboot(NULL, 0, 0, 0); } + +u8 fastboot_debug_level; +void fastboot_dump_memory(u32 *ptr, u32 len) +{ + u32 i; + for (i = 0; i < len; i++) { + DBG_DEBUG("0x%p: %08x %08x %08x %08x\n", ptr, + *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); + ptr += 4; + } +} + +#define FASTBOOT_STS_CMD 0 +#define FASTBOOT_STS_CMD_WAIT 1 +#define FASTBOOT_STS_DATA 2 +#define FASTBOOT_STS_DATA_WAIT 3 + +static u8 fastboot_status; +static u8 g_fastboot_recvbuf[MAX_PAKET_LEN]; +static u8 g_fastboot_sendbuf[MAX_PAKET_LEN]; + +static u32 g_fastboot_datalen; +static u8 g_fastboot_outep_index, g_fastboot_inep_index; +static u8 g_usb_connected; + +void fastboot_get_ep_num(u8 *in, u8 *out) +{ + if (out) + *out = rx_endpoint + EP0_OUT_INDEX + 1; + if (in) + *in = tx_endpoint + EP0_IN_INDEX + 1; +} + +static void fastboot_data_handler(u32 len, u8 *recvbuf) +{ + if (len != g_fastboot_datalen) + DBG_ERR("Fastboot data recv error, want:%d, recv:%d\n", + g_fastboot_datalen, len); + sprintf((char *)g_fastboot_sendbuf, "OKAY"); + udc_send_data(g_fastboot_inep_index, g_fastboot_sendbuf, 4, NULL); + fastboot_status = FASTBOOT_STS_CMD; +} + +static void fastboot_cmd_handler(u32 len, u8 *recvbuf) +{ + u32 *databuf = (u32 *)CONFIG_FASTBOOT_TRANSFER_BUF; + + if (len > sizeof(g_fastboot_recvbuf)) { + DBG_ERR("%s, recv len=%d error\n", __func__, len); + return; + } + recvbuf[len] = 0; + DBG_ALWS("\nFastboot Cmd, len=%u, %s\n", len, recvbuf); + + if (memcmp(recvbuf, "download:", 9) == 0) { + g_fastboot_datalen = simple_strtoul((const char *)recvbuf + 9, + NULL, 16); + if (g_fastboot_datalen > CONFIG_FASTBOOT_TRANSFER_BUF_SIZE) { + DBG_ERR("Download too much data\n"); + sprintf((char *)g_fastboot_sendbuf, "FAIL"); + udc_send_data(g_fastboot_inep_index, g_fastboot_sendbuf, + 4, NULL); + fastboot_status = FASTBOOT_STS_CMD; + } else { + sprintf((char *)g_fastboot_sendbuf, "DATA%08x", + g_fastboot_datalen); + udc_send_data(g_fastboot_inep_index, g_fastboot_sendbuf, + 12, NULL); + DBG_ALWS("Fastboot is receiveing data...\n"); + udc_recv_data(g_fastboot_outep_index, (u8 *)databuf, + g_fastboot_datalen, fastboot_data_handler); + fastboot_status = FASTBOOT_STS_DATA_WAIT; + } + } else if (memcmp(recvbuf, "flash:", 6) == 0) { + if (g_fastboot_datalen == + fastboot_write_mmc(recvbuf+6, g_fastboot_datalen)) { + DBG_ALWS("Fastboot write OK, send OKAY...\n"); + sprintf((char *)g_fastboot_sendbuf, "OKAY"); + udc_send_data(g_fastboot_inep_index, g_fastboot_sendbuf, + 4, NULL); + } else { + DBG_ERR("Fastboot write error, write 0x%x\n", + g_fastboot_datalen); + sprintf((char *)g_fastboot_sendbuf, "FAIL"); + udc_send_data(g_fastboot_inep_index, g_fastboot_sendbuf, + 4, NULL); + } + g_fastboot_datalen = 0; + fastboot_status = FASTBOOT_STS_CMD; + } else { + DBG_ERR("Not support command:%s\n", recvbuf); + sprintf((char *)g_fastboot_sendbuf, "FAIL"); + udc_send_data(g_fastboot_inep_index, g_fastboot_sendbuf, + 4, NULL); + g_fastboot_datalen = 0; + fastboot_status = FASTBOOT_STS_CMD; + } +} + +static struct cmd_fastboot_interface interface = { + .rx_handler = NULL, + .reset_handler = NULL, + .product_name = NULL, + .serial_no = NULL, + .nand_block_size = 0, + .transfer_buffer = (unsigned char *)0xffffffff, + .transfer_buffer_size = 0, +}; + +void *fastboot_get_string_table(void) +{ + return fastboot_string_table; +} + +/* + * fastboot main process, only support 'download', 'flash' command now + * + * @debug control debug level, support three level now, + * 0(normal), 1(debug), 2(info), default is 0 + */ +void fastboot_quick(u8 debug) +{ + u32 plug_cnt = 0; + if (debug > 2) + debug = 0; + fastboot_debug_level = debug; + + fastboot_init(&interface); + fastboot_get_ep_num(&g_fastboot_inep_index, &g_fastboot_outep_index); + DBG_INFO("g_fastboot_inep_index=%d, g_fastboot_outep_index=%d\n", + g_fastboot_inep_index, g_fastboot_outep_index); + while (++plug_cnt) { + fastboot_status = FASTBOOT_STS_CMD; + udc_hal_data_init(); + udc_run(); + if (plug_cnt > 1) + DBG_ALWS("wait usb cable into the connector!\n"); + udc_wait_connect(); + g_usb_connected = 1; + if (plug_cnt > 1) + DBG_ALWS("USB Mini b cable Connected!\n"); + while (g_usb_connected) { + int usb_irq = udc_irq_handler(); + if (usb_irq > 0) { + if (fastboot_status == FASTBOOT_STS_CMD) { + memset(g_fastboot_recvbuf, 0 , MAX_PAKET_LEN); + udc_recv_data(g_fastboot_outep_index, + g_fastboot_recvbuf, MAX_PAKET_LEN, + fastboot_cmd_handler); + fastboot_status = FASTBOOT_STS_CMD_WAIT; + } + } + if (usb_irq < 0) + g_usb_connected = 0; + } + } +} diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c index 2b4ec44..6c2f68d 100644 --- a/drivers/usb/gadget/ep0.c +++ b/drivers/usb/gadget/ep0.c @@ -595,3 +595,121 @@ int ep0_recv_setup (struct urb *urb) } return -1; } + +#ifdef CONFIG_FASTBOOT + +#include <usb/imx_udc.h> +#include <fastboot.h> + +/* Standard requests */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +struct USB_SETUP_T { + u8 bmRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +}; + +void ep0_parse_setup(void *ctrl) +{ + struct USB_SETUP_T *s = (struct USB_SETUP_T *)ctrl; + DBG_DEBUG("SETUP, type=0x%x, req=0x%x, value=0x%x, index=0x%x, len=0x%x\n", + s->bmRequestType, s->bRequest, s->wValue, s->wIndex, s->wLength); + switch (s->bRequest) { + case USB_REQ_GET_DESCRIPTOR: + { + u8 type = (s->wValue >> 8) & 0xff; + u8 *pdesc, len; + + udc_recv_data(EP0_OUT_INDEX, NULL, 0, NULL); + switch (type) { + case USB_DESCRIPTOR_TYPE_DEVICE: + case USB_DESCRIPTOR_TYPE_CONFIGURATION: + pdesc = udc_get_descriptor(type, &len); + len = MIN(s->wLength, len); + udc_send_data(EP0_IN_INDEX, pdesc, len, NULL); + break; + + case USB_DESCRIPTOR_TYPE_STRING: + { + struct usb_string_descriptor **string_table; + string_table = fastboot_get_string_table(); + len = string_table[(s->wValue)&0xff]->bLength; + len = MIN(s->wLength, len); + udc_send_data(EP0_IN_INDEX, + (u8 *)string_table[(s->wValue)&0xff], len, NULL); + break; + } + + case USB_DESCRIPTOR_TYPE_ENDPOINT: + case USB_DESCRIPTOR_TYPE_INTERFACE: + case USB_DESCRIPTOR_TYPE_HID: + case USB_DESCRIPTOR_TYPE_REPORT: + case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER: + default: + DBG_ERR("not support type=0x%x\n", type); + return; + } + break; + } + case USB_REQ_SET_ADDRESS: + udc_set_addr((s->wValue)&0xff); + udc_send_data(EP0_IN_INDEX, NULL, 0, NULL); + DBG_INFO("USB addr=0x%x\n", (s->wValue)&0xff); + break; + + case USB_REQ_GET_STATUS: + { + static u8 tmp[2]; +#define USB_RECIP_MASK 0x03 + udc_recv_data(EP0_OUT_INDEX, NULL, 0, NULL); + tmp[0] = tmp[1] = 0; + if ((s->bmRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + tmp[0] = 1 << 0; /* self powerd */ + tmp[0] |= 0 << 1; /* not remote wakeup able */ + } else if ((s->bmRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { + tmp[0] = 0; + } else if ((s->bmRequestType & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { + tmp[0] = 0; + } + udc_send_data(EP0_IN_INDEX, (u8 *)&tmp, 2, NULL); + break; + } + case USB_REQ_SET_CONFIGURATION: + udc_send_data(EP0_IN_INDEX, NULL, 0, NULL); + if (s->wValue == 0x01) { + u8 in, out; + fastboot_get_ep_num(&in, &out); + udc_qh_dtd_init(in); + udc_qh_dtd_init(out); + udc_qh_setup(in, USB_ENDPOINT_XFER_BULK, MAX_PAKET_LEN, 1, 0); + udc_qh_setup(out, USB_ENDPOINT_XFER_BULK, MAX_PAKET_LEN, 1, 0); + udc_dtd_setup(in, USB_ENDPOINT_XFER_BULK); + udc_dtd_setup(out, USB_ENDPOINT_XFER_BULK); + udc_qh_dtd_init(in); + udc_qh_dtd_init(out); + + udc_set_configure(1); + } + break; + default: + DBG_ERR("Setup Error. rq=0x%x, type=0x%x, value=0x%x, index=0x%x," + "len=0x%x\n", s->bRequest, s->bmRequestType, + s->wValue, s->wIndex, s->wLength); + break; + } +} + +#endif /* CONFIG_FASTBOOT */ diff --git a/drivers/usb/gadget/ep0.h b/drivers/usb/gadget/ep0.h index 3bec106..8659415 100644 --- a/drivers/usb/gadget/ep0.h +++ b/drivers/usb/gadget/ep0.h @@ -35,5 +35,7 @@ int ep0_recv_setup (struct urb *urb); - +#ifdef CONFIG_FASTBOOT +void ep0_parse_setup(void *s); +#endif /* CONFIG_FASTBOOT */ #endif diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index d0aa5a1..796d783 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -44,7 +44,7 @@ #define mdelay(n) udelay((n)*1000) -#define EP_TQ_ITEM_SIZE 4 +#define EP_TQ_ITEM_SIZE 16 #define inc_index(x) (x = ((x+1) % EP_TQ_ITEM_SIZE)) @@ -944,3 +944,626 @@ void udc_set_nak(int epid) void udc_unset_nak(int epid) { } + +#ifdef CONFIG_FASTBOOT + +#include <fastboot.h> + +struct EP_QUEUE_HEAD_T { + unsigned int config; + unsigned int current; /* read-only */ + + unsigned int next_queue_item; + unsigned int info; + unsigned int page0; + unsigned int page1; + unsigned int page2; + unsigned int page3; + unsigned int page4; + unsigned int reserved_0; + + unsigned char setup_data[8]; + unsigned int self_buf; + unsigned int deal_cnt; + unsigned int total_len; + unsigned char *deal_buf; +}; + +struct EP_DTD_ITEM_T { + unsigned int next_item_ptr; + unsigned int info; + unsigned int page0; + unsigned int page1; + unsigned int page2; + unsigned int page3; + unsigned int page4; + unsigned int reserved[9]; +}; + +struct USB_CTRL_T { + struct EP_QUEUE_HEAD_T *qh_ptr; + struct EP_DTD_ITEM_T *dtd_ptr; + EP_HANDLER_P *handler_ptr; + u32 configed; + u32 max_ep; +}; + +struct USB_CTRL_T g_usb_ctrl; + +u8 *udc_get_descriptor(u8 type, u8 *plen) +{ + if (USB_DESCRIPTOR_TYPE_DEVICE == type) { + *plen = ep0_urb->device->device_descriptor->bLength; + return (u8 *)(ep0_urb->device->device_descriptor); + } else if (USB_DESCRIPTOR_TYPE_CONFIGURATION == type) { + *plen = ep0_urb->device->configuration_instance_array-> + configuration_descriptor->wTotalLength; + return (u8 *)(ep0_urb->device->configuration_instance_array-> + configuration_descriptor); + } else { + *plen = 0; + DBG_ERR("%s, wrong type:%x\n", __func__, type); + return NULL; + } +} + +void udc_qh_setup(u32 index, u8 ep_type, u32 max_pkt_len, u32 zlt, u8 mult) +{ + u32 tmp = max_pkt_len << 16; + u32 offset; + struct EP_QUEUE_HEAD_T *qh_ptr; + + if (index > EP15_IN_INDEX) { + DBG_ERR("%s:%d error, wrong index=%d\n", __func__, __LINE__, index); + return; + } + + if (index >= EP0_IN_INDEX) + offset = (index-EP0_IN_INDEX) * 2 + 1; + else + offset = index * 2; + + qh_ptr = g_usb_ctrl.qh_ptr + offset; + + switch (ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + tmp |= (1 << 15); + break; + case USB_ENDPOINT_XFER_ISOC: + tmp |= (mult << 30); + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + break; + default: + DBG_ERR("%s:%d, index=%d, error_type=%d\n", + __func__, __LINE__, index, ep_type); + return; + } + if (zlt) + tmp |= (1<<29); + + qh_ptr->config = tmp; +} + +void udc_dtd_setup(u32 index, u8 ep_type) +{ + u32 epctrl = 0; + u8 ep_num, dir; + + if (index > EP15_IN_INDEX) { + DBG_ERR("%s:%d error, wrong index=%d", __func__, __LINE__, index); + return; + } + + if (index >= EP0_IN_INDEX) { + ep_num = index - EP0_IN_INDEX; + dir = 1; + } else { + ep_num = index; + dir = 0; + } + + epctrl = readl(USB_ENDPTCTRL(ep_num)); + if (dir) { + if (ep_num != 0) + epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + epctrl |= EPCTRL_TX_ENABLE; + epctrl |= ((u32)(ep_type) << EPCTRL_TX_EP_TYPE_SHIFT); + } else { + if (ep_num != 0) + epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + epctrl |= EPCTRL_RX_ENABLE; + epctrl |= ((u32)(ep_type) << EPCTRL_RX_EP_TYPE_SHIFT); + } + writel(epctrl, USB_ENDPTCTRL(ep_num)); +} + + +void udc_qh_dtd_init(u32 index) +{ + u32 i, tmp, max_len = 0; + u32 offset; + + if (index > EP15_IN_INDEX) { + DBG_ERR("%s:%d error, wrong index=%d", __func__, __LINE__, index); + return; + } + + if (index >= EP0_IN_INDEX) + offset = (index-EP0_IN_INDEX) * 2 + 1; + else + offset = index * 2; + + struct EP_QUEUE_HEAD_T *qh_ptr = g_usb_ctrl.qh_ptr + offset; + struct EP_DTD_ITEM_T *dtd_ptr = g_usb_ctrl.dtd_ptr + + offset * EP_TQ_ITEM_SIZE; + + if ((index == EP0_IN_INDEX) || (index == EP0_OUT_INDEX)) + max_len = 64; + else + max_len = MAX_PAKET_LEN; + + qh_ptr->self_buf = (u32)malloc_dma_buffer(&tmp, max_len, + USB_MEM_ALIGN_BYTE); + if (!qh_ptr->self_buf) { + DBG_ERR("malloc ep data buffer error\n"); + return; + } + qh_ptr->next_queue_item = 0x1; + + for (i = 0; i < EP_TQ_ITEM_SIZE; i++) { + dtd_ptr[i].next_item_ptr = 1; + dtd_ptr[i].info = 0x0; + } +} + +int udc_recv_data(u32 index, u8 *buf, u32 recvlen, EP_HANDLER_P cb) +{ + u32 offset; + struct EP_DTD_ITEM_T *dtd_ptr; + struct EP_QUEUE_HEAD_T *qh_ptr; + u32 max_pkt_len, i; + u32 dtd_cnt = 0; + u32 len = recvlen; + + if (index >= EP0_IN_INDEX) { + DBG_ERR("IN %d is trying to recv data\n", index); + return 0; + } + + if (index != EP0_OUT_INDEX) + max_pkt_len = MAX_PAKET_LEN; + else + max_pkt_len = 64; + + if ((len > max_pkt_len) && (!buf)) { + DBG_ERR("RecvData, wrong param\n"); + return 0; + } + + offset = index * 2; + + dtd_ptr = g_usb_ctrl.dtd_ptr + offset*EP_TQ_ITEM_SIZE; + qh_ptr = g_usb_ctrl.qh_ptr + offset; + if (!buf) + buf = (u8 *)(qh_ptr->self_buf); + + DBG_INFO("\n\n\n\nRecvData, index=%d, qh_ptr=0x%p, recvlen=0x%x, " + "recvbuf=0x%p\n", index, qh_ptr, len, buf); + for (i = 0; i < EP_TQ_ITEM_SIZE; i++) { + if (dtd_ptr[i].info & 0x80) { + DBG_ERR("We got index=%d dtd%u error[0x%08x]\n", + index, i, dtd_ptr[i].info); + } + } + + if (qh_ptr->total_len == 0) { + qh_ptr->total_len = recvlen; + qh_ptr->deal_buf = buf; + qh_ptr->deal_cnt = 0; + g_usb_ctrl.handler_ptr[index] = cb; + } + + while (dtd_cnt < EP_TQ_ITEM_SIZE) { + u32 remain = 0x1000 - (((u32)buf)&0xfff); + dtd_ptr[dtd_cnt].page0 = (u32)buf; + dtd_ptr[dtd_cnt].page1 = (dtd_ptr[dtd_cnt].page0&0xfffff000) + 0x1000; + dtd_ptr[dtd_cnt].page2 = dtd_ptr[dtd_cnt].page1 + 0x1000; + dtd_ptr[dtd_cnt].page3 = dtd_ptr[dtd_cnt].page2 + 0x1000; + dtd_ptr[dtd_cnt].page4 = dtd_ptr[dtd_cnt].page3 + 0x1000; + + if (len > (remain+16*1024)) { /*we need another dtd*/ + len -= (remain+16*1024); + buf += (remain+16*1024); + /*no interrupt here*/ + dtd_ptr[dtd_cnt].info = (((remain+16*1024)<<16) | (1<<7)); + if (dtd_cnt < (EP_TQ_ITEM_SIZE-1)) { /*we have another dtd*/ + /*get dtd linked*/ + dtd_ptr[dtd_cnt].next_item_ptr = (u32)(dtd_ptr+dtd_cnt+1); + DBG_INFO("we have another dtd, cnt=%u, remain=0x%x\n", + dtd_cnt, remain); + dtd_cnt++; + continue; + } else { /*there is no more dtd, so we need to recv the dtd chain*/ + dtd_ptr[dtd_cnt].next_item_ptr = 0x1; /*dtd terminate*/ + /*interrupt when transfer done*/ + dtd_ptr[dtd_cnt].info |= (1<<15); + DBG_INFO("we have none dtd, cnt=%u, remain=%u, " + "len_left=0x%x\n", dtd_cnt, remain, len); + break; + } + } else { /*we could recv all data in this dtd*/ + /*interrupt when transfer done*/ + dtd_ptr[dtd_cnt].info = ((len<<16) | (1<<15) | (1<<7)); + dtd_ptr[dtd_cnt].next_item_ptr = 0x1; /*dtd terminate*/ + len = 0; + DBG_INFO("we could done in this dtd, cnt=%u, remain=%u, " + "len_left=0x%x\n", dtd_cnt, remain, len); + break; + } + } + + for (i = dtd_cnt+1; i < EP_TQ_ITEM_SIZE; i++) + dtd_ptr[i].info = 0; + + qh_ptr->next_queue_item = (u32)dtd_ptr; + qh_ptr->deal_cnt += (recvlen - len); + writel(1 << index, USB_ENDPTPRIME); + return qh_ptr->deal_cnt; +} + +int udc_send_data(u32 index, u8 *buf, u32 sendlen, EP_HANDLER_P cb) +{ + u32 offset; + struct EP_DTD_ITEM_T *dtd_ptr; + struct EP_QUEUE_HEAD_T *qh_ptr; + u32 max_pkt_len, i; + u32 dtd_cnt = 0; + u32 len = sendlen; + + if (index < EP0_IN_INDEX) { + DBG_ERR("OUT %d is trying to send data\n", index); + return 0; + } + + if (index != EP0_IN_INDEX) + max_pkt_len = MAX_PAKET_LEN; + else + max_pkt_len = 64; + + if ((len > max_pkt_len) && (!buf)) { + DBG_ERR("SendData, wrong param\n"); + return 0; + } + + offset = (index - EP0_IN_INDEX) * 2 + 1; + + dtd_ptr = g_usb_ctrl.dtd_ptr + offset*EP_TQ_ITEM_SIZE; + qh_ptr = g_usb_ctrl.qh_ptr + offset; + if (!buf) + buf = (u8 *)(qh_ptr->self_buf); + + DBG_INFO("\n\n\n\nSendData, index=%d, qh_ptr=0x%p, sendlen=0x%x, " + "sendbuf=0x%p\n", index, qh_ptr, len, buf); + for (i = 0; i < EP_TQ_ITEM_SIZE; i++) { + if (dtd_ptr[i].info & 0x80) { + DBG_ERR("We got index=%d dtd%u error[0x%08x]\n", + index, i, dtd_ptr[i].info); + } + } + + if (qh_ptr->total_len == 0) { + qh_ptr->total_len = sendlen; + qh_ptr->deal_buf = buf; + qh_ptr->deal_cnt = 0; + g_usb_ctrl.handler_ptr[index] = cb; + } + + while (dtd_cnt < EP_TQ_ITEM_SIZE) { + u32 remain = 0x1000 - (((u32)buf)&0xfff); + dtd_ptr[dtd_cnt].page0 = (u32)buf; + dtd_ptr[dtd_cnt].page1 = (dtd_ptr[dtd_cnt].page0&0xfffff000) + 0x1000; + dtd_ptr[dtd_cnt].page2 = dtd_ptr[dtd_cnt].page1 + 0x1000; + dtd_ptr[dtd_cnt].page3 = dtd_ptr[dtd_cnt].page2 + 0x1000; + dtd_ptr[dtd_cnt].page4 = dtd_ptr[dtd_cnt].page3 + 0x1000; + + if (len > (remain+16*1024)) { /*we need another dtd*/ + len -= (remain+16*1024); + buf += (remain+16*1024); + /*no interrupt here*/ + dtd_ptr[dtd_cnt].info = (((remain+16*1024)<<16) | (1<<7)); + if (dtd_cnt < (EP_TQ_ITEM_SIZE-1)) { /*we have another dtd*/ + /*get dtd linked*/ + dtd_ptr[dtd_cnt].next_item_ptr = (u32)(dtd_ptr+dtd_cnt+1); + DBG_INFO("we have another dtd, cnt=%u, remain=0x%x\n", + dtd_cnt, remain); + dtd_cnt++; + continue; + } else { /*there is no more dtd, so we need to recv the dtd chain*/ + dtd_ptr[dtd_cnt].next_item_ptr = 0x1; /*dtd terminate*/ + /*interrupt when transfer done*/ + dtd_ptr[dtd_cnt].info |= (1<<15); + DBG_INFO("we have none dtd, cnt=%u, remain=%u, " + "len_left=0x%x\n", dtd_cnt, remain, len); + break; + } + } else { /* we could recv all data in this dtd */ + /*interrupt when transfer done*/ + dtd_ptr[dtd_cnt].info = ((len<<16) | (1<<15) | (1<<7)); + dtd_ptr[dtd_cnt].next_item_ptr = 0x1; /*dtd terminate*/ + len = 0; + DBG_INFO("we could done in this dtd, cnt=%u, remain=%u, " + "len_left=0x%x\n", dtd_cnt, remain, len); + break; + } + } + + for (i = dtd_cnt+1; i < EP_TQ_ITEM_SIZE; i++) + dtd_ptr[i].info = 0; + + qh_ptr->next_queue_item = (u32)dtd_ptr; + qh_ptr->deal_cnt += (sendlen - len); + writel(1 << index, USB_ENDPTPRIME); + return qh_ptr->deal_cnt; +} + +static void udc_get_setup(void *s) +{ + u32 temp; + temp = readl(USB_ENDPTSETUPSTAT); + writel(temp, USB_ENDPTSETUPSTAT); + DBG_INFO("setup stat %x\n", temp); + do { + temp = readl(USB_USBCMD); + temp |= USB_CMD_SUTW; + writel(temp, USB_USBCMD); + memcpy((void *)s, (void *)g_usb_ctrl.qh_ptr[0].setup_data, 8); + } while (!(readl(USB_USBCMD) & USB_CMD_SUTW)); + + temp = readl(USB_USBCMD); + temp &= ~USB_CMD_SUTW; + writel(temp, USB_ENDPTSETUPSTAT); +} + +static void ep_out_handler(u32 index) +{ + u32 offset = index*2; + struct EP_DTD_ITEM_T *dtd_ptr = g_usb_ctrl.dtd_ptr + + offset * EP_TQ_ITEM_SIZE; + struct EP_QUEUE_HEAD_T *qh_ptr = g_usb_ctrl.qh_ptr + offset; + + u32 i, len = 0; + + for (i = 0; i < EP_TQ_ITEM_SIZE; i++) { + len += (dtd_ptr[i].info >> 16 & 0x7fff); + dtd_ptr[i].info = 0; + } + + DBG_INFO("%s, index=%d, qh_ptr=0x%p\n", __func__, index, qh_ptr); + DBG_DEBUG("%s, index=%d, dealbuf=0x%p, len_cnt=0x%x, total_len=0x%x\n", + __func__, index, qh_ptr->deal_buf, qh_ptr->deal_cnt, qh_ptr->total_len); + + if (qh_ptr->total_len < qh_ptr->deal_cnt) + DBG_ERR("index %d recv error, total=0x%x, cnt=0x%x\n", + index, qh_ptr->total_len, qh_ptr->deal_cnt); + if (qh_ptr->total_len != qh_ptr->deal_cnt) { + u32 nowcnt, precnt = qh_ptr->deal_cnt; + nowcnt = udc_recv_data(index, (u8 *)(qh_ptr->deal_buf+qh_ptr->deal_cnt), + qh_ptr->total_len - qh_ptr->deal_cnt, + g_usb_ctrl.handler_ptr[index]); + if (nowcnt > qh_ptr->total_len) + DBG_ERR("index %d recv error, total=0x%x, cnt=0x%x\n", + index, qh_ptr->total_len, qh_ptr->deal_cnt); + else if (nowcnt == qh_ptr->total_len) + DBG_ALWS("\rreceive 100%\n"); + else + DBG_ALWS("\rreceive %d%", precnt/(qh_ptr->total_len/100)); + } else { + qh_ptr->total_len = 0; + if (g_usb_ctrl.handler_ptr[index]) { + g_usb_ctrl.handler_ptr[index](qh_ptr->deal_cnt-len, + (u8 *)(qh_ptr->deal_buf)); + } + } +} + +static void ep_in_handler(u32 index) +{ + u32 offset = (index-EP0_IN_INDEX) * 2 + 1; + struct EP_DTD_ITEM_T *dtd_ptr = g_usb_ctrl.dtd_ptr + + offset * EP_TQ_ITEM_SIZE; + struct EP_QUEUE_HEAD_T *qh_ptr = g_usb_ctrl.qh_ptr + offset; + + u32 i, len = 0; + for (i = 0; i < EP_TQ_ITEM_SIZE; i++) { + len += (dtd_ptr[i].info >> 16 & 0x7fff); + dtd_ptr[i].info = 0; + } + + DBG_INFO("%s, index=%d, qh_ptr=0x%p\n", __func__, index, qh_ptr); + DBG_DEBUG("%s, index=%d, dealbuf=0x%p, len_cnt=0x%x, total_len=0x%x\n", + __func__, index, qh_ptr->deal_buf, qh_ptr->deal_cnt, qh_ptr->total_len); + + if (qh_ptr->total_len < qh_ptr->deal_cnt) + DBG_ERR("index %d send error, total=0x%x, cnt=0x%x\n", + index, qh_ptr->total_len, qh_ptr->deal_cnt); + if (qh_ptr->total_len != qh_ptr->deal_cnt) { + u32 nowcnt, precnt = qh_ptr->deal_cnt; + nowcnt = udc_send_data(index, (u8 *)(qh_ptr->deal_buf+qh_ptr->deal_cnt), + qh_ptr->total_len - qh_ptr->deal_cnt, + g_usb_ctrl.handler_ptr[index]); + if (nowcnt > (qh_ptr->total_len)) + DBG_ERR("index %d recv error, total=0x%x, cnt=0x%x\n", + index, qh_ptr->total_len, qh_ptr->deal_cnt); + else if (nowcnt == qh_ptr->total_len) + DBG_ALWS("\rreceive 100%\n"); + else + DBG_ALWS("\rreceive %d%", precnt/(qh_ptr->total_len/100)); + } else { + qh_ptr->total_len = 0; + if (g_usb_ctrl.handler_ptr[index]) { + g_usb_ctrl.handler_ptr[index](qh_ptr->deal_cnt-len, + (u8 *)(qh_ptr->deal_buf)); + } + } +} + +static void setup_handler(void) +{ + u32 setup[2]; + udc_get_setup(setup); + ep0_parse_setup(setup); +} + +int udc_irq_handler(void) +{ + u32 irq_src = readl(USB_USBSTS) & readl(USB_USBINTR); + writel(irq_src, USB_USBSTS); + + if (!(readl(USB_OTGSC) & OTGSC_B_SESSION_VALID)) { + DBG_ALWS("USB disconnect\n"); + return -1; + } + + if (irq_src == 0) + return g_usb_ctrl.configed; + + DBG_DEBUG("\nGet USB irq: 0x%x\n", irq_src); + + if (irq_src & USB_STS_INT) { + u32 complete, i; + if (readl(USB_ENDPTSETUPSTAT)) { + DBG_INFO("recv setup packet\n"); + if (g_usb_ctrl.handler_ptr) + setup_handler(); + } + + complete = readl(USB_ENDPTCOMPLETE); + writel(complete, USB_ENDPTCOMPLETE); + if (complete) { + DBG_INFO("Dtd complete irq, 0x%x\n", complete); + for (i = 0; i < g_usb_ctrl.max_ep; i++) { + if (complete & (1<<i)) + ep_out_handler(i); + + if (complete & (1<<(i+EP0_IN_INDEX))) + ep_in_handler(i+EP0_IN_INDEX); + } + } + } + + if (irq_src & USB_STS_RESET) { + u32 temp; + temp = readl(USB_DEVICEADDR); + temp &= ~0xfe000000; + writel(temp, USB_DEVICEADDR); + writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT); + writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE); + while (readl(USB_ENDPTPRIME)) + ; + writel(0xffffffff, USB_ENDPTFLUSH); + DBG_DEBUG("USB_RESET, PORTSC = 0x%x\n", readl(USB_PORTSC1)); + } + + if (irq_src & USB_STS_PORT_CHANGE) { + u32 speed; + while (readl(USB_PORTSC1) & PORTSCX_PORT_RESET) + ; + speed = readl(USB_PORTSC1) & PORTSCX_PORT_SPEED_MASK; + DBG_DEBUG("USB port changed, speed = 0x%x\n", speed); + } + + if (irq_src & USB_STS_SUSPEND) + DBG_DEBUG("USB_SUSPEND\n"); + + if (irq_src & USB_STS_ERR) + DBG_ERR("USB_ERR\n"); + + return g_usb_ctrl.configed; +} + +void udc_set_addr(u8 addr) +{ + writel((addr << 25) | (1<<24), USB_DEVICEADDR); +} + +void udc_set_configure(u8 config) +{ + g_usb_ctrl.configed = config; +} + +void udc_run(void) +{ + unsigned int reg; + /* Set controller to Run */ + reg = readl(USB_USBCMD); + reg |= USB_CMD_RUN_STOP; + writel(reg, USB_USBCMD); +} + + +void udc_wait_connect(void) +{ + unsigned int reg; + do { + udelay(50); + reg = readl(USB_OTGSC); + if (reg & (OTGSC_B_SESSION_VALID)) + break; + } while (1); +} + + +void udc_hal_data_init(void) +{ + u32 size, tmp; + + g_usb_ctrl.max_ep = (readl(USB_DCCPARAMS) & DCCPARAMS_DEN_MASK); + udc_set_configure(0); + size = g_usb_ctrl.max_ep * 2 * sizeof(struct EP_QUEUE_HEAD_T); + if (g_usb_ctrl.qh_ptr == NULL) + g_usb_ctrl.qh_ptr = malloc_dma_buffer(&tmp, size, USB_MEM_ALIGN_BYTE); + if (g_usb_ctrl.qh_ptr == NULL) { + DBG_ERR("malloc ep qh error\n"); + return; + } + memset(g_usb_ctrl.qh_ptr, 0, size); + writel(((u32)(g_usb_ctrl.qh_ptr)) & 0xfffff800, USB_ENDPOINTLISTADDR); + + size = g_usb_ctrl.max_ep * 2 + * sizeof(struct EP_DTD_ITEM_T) * EP_TQ_ITEM_SIZE; + if (g_usb_ctrl.dtd_ptr == NULL) + g_usb_ctrl.dtd_ptr = malloc_dma_buffer(&tmp, size, USB_MEM_ALIGN_BYTE); + if (g_usb_ctrl.dtd_ptr == NULL) { + DBG_ERR("malloc ep dtd error\n"); + return; + } + memset(g_usb_ctrl.dtd_ptr, 0, size); + + size = (EP15_IN_INDEX + 1) * sizeof(EP_HANDLER_P); + if (g_usb_ctrl.handler_ptr == NULL) + g_usb_ctrl.handler_ptr = malloc_dma_buffer(&tmp, size, 4); + + if (g_usb_ctrl.handler_ptr == NULL) { + DBG_ERR("malloc ep dtd error\n"); + return; + } + memset(g_usb_ctrl.handler_ptr, 0, size); + + DBG_INFO("USB max ep = %d, qh_addr = 0x%p, dtd_addr = 0x%p\n", + g_usb_ctrl.max_ep, g_usb_ctrl.qh_ptr, g_usb_ctrl.dtd_ptr); + + udc_qh_dtd_init(EP0_OUT_INDEX); + udc_qh_dtd_init(EP0_IN_INDEX); + + udc_qh_setup(EP0_OUT_INDEX, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 0, 0); + udc_qh_setup(EP0_IN_INDEX, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 0, 0); + + udc_dtd_setup(EP0_OUT_INDEX, USB_ENDPOINT_XFER_CONTROL); + udc_dtd_setup(EP0_IN_INDEX, USB_ENDPOINT_XFER_CONTROL); +} + +#endif /* CONFIG_FASTBOOT */ diff --git a/include/fastboot.h b/include/fastboot.h index f0ed108..e10c9af 100644 --- a/include/fastboot.h +++ b/include/fastboot.h @@ -331,6 +331,19 @@ int fastboot_check_and_clean_flag(void); int do_fastboot(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); void check_fastboot_mode(void); + +void fastboot_quick(u8 debug); +void *fastboot_get_string_table(void); +int fastboot_write_mmc(u8 *partition_name, u32 write_len); +void fastboot_dump_memory(u32 *ptr, u32 lEN); +void fastboot_get_ep_num(u8 *in, u8 *out); +extern u8 fastboot_debug_level; +#define DBG_ALWS(x...) printf(x) +#define DBG_ERR(x...) printf(x) +#define DBG_DEBUG(x...) if (fastboot_debug_level >= 1) printf(x) +#define DBG_INFO(x...) if (fastboot_debug_level >= 2) printf(x) + + #else /* Stubs for when CONFIG_FASTBOOT is not defined */ @@ -356,5 +369,16 @@ void check_fastboot_mode(void); #define fastboot_flash_write(a, b, c, d) 0 #define do_fastboot(a, b, c, d) 0 + +#define fastboot_quick(a) 0 +#define fastboot_get_ep_num(a, b) 0 +#define fastboot_get_string_table() 1 +#define fastboot_dump_memory(a, b) 0 +#define DBG_ALWS(x...) +#define DBG_ERR(x...) +#define DBG_DEBUG(x...) +#define DBG_INFO(x...) + + #endif /* CONFIG_FASTBOOT */ #endif /* FASTBOOT_H */ diff --git a/include/usb/imx_udc.h b/include/usb/imx_udc.h index 3809640..da3bcd6 100644 --- a/include/usb/imx_udc.h +++ b/include/usb/imx_udc.h @@ -493,4 +493,57 @@ void enable_usb_phy1_clk(unsigned char enable); void enable_usboh3_clk(unsigned char enable); void udc_pins_setting(void); +#ifdef CONFIG_FASTBOOT + +#define EP0_OUT_INDEX 0 +#define EP0_IN_INDEX 16 +#define EP1_OUT_INDEX 1 +#define EP1_IN_INDEX 17 +#define EP2_OUT_INDEX 2 +#define EP2_IN_INDEX 18 +#define EP3_OUT_INDEX 3 +#define EP3_IN_INDEX 19 +#define EP4_OUT_INDEX 4 +#define EP4_IN_INDEX 20 +#define EP5_OUT_INDEX 5 +#define EP5_IN_INDEX 21 +#define EP6_OUT_INDEX 6 +#define EP6_IN_INDEX 22 +#define EP7_OUT_INDEX 7 +#define EP7_IN_INDEX 23 +#define EP8_OUT_INDEX 8 +#define EP8_IN_INDEX 24 +#define EP9_OUT_INDEX 9 +#define EP9_IN_INDEX 25 +#define EP10_OUT_INDEX 10 +#define EP10_IN_INDEX 26 +#define EP11_OUT_INDEX 11 +#define EP11_IN_INDEX 27 +#define EP12_OUT_INDEX 12 +#define EP12_IN_INDEX 28 +#define EP13_OUT_INDEX 13 +#define EP13_IN_INDEX 29 +#define EP14_OUT_INDEX 14 +#define EP14_IN_INDEX 30 +#define EP15_OUT_INDEX 15 +#define EP15_IN_INDEX 31 + +#define MAX_PAKET_LEN 512 +typedef void (*EP_HANDLER_P)(u32 index, u8 *buf); + +int udc_irq_handler(void); +void udc_hal_data_init(void); +void udc_wait_connect(void); +void udc_run(void); +int udc_recv_data(u32 index, u8 *recvbuf, u32 recvlen, EP_HANDLER_P cb); +int udc_send_data(u32 index, u8 *buf, u32 sendlen, EP_HANDLER_P cb); +void udc_qh_dtd_init(u32 index); +void udc_dtd_setup(u32 index, u8 ep_type); +void udc_qh_setup(u32 index, u8 ep_type, u32 max_pkt_len, u32 zlt, u8 mult); +u8 *udc_get_descriptor(u8 type, u8 *plen); +void udc_set_addr(u8 addr); +void udc_set_configure(u8 config); + +#endif /* CONFIG_FASTBOOT */ + #endif |