summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorPeng Fan <Peng.Fan@freescale.com>2015-03-10 15:05:10 +0800
committerMax Krummenacher <max.krummenacher@toradex.com>2016-03-09 14:42:14 +0100
commit8902c144dff43270e100c5f19e03d3a5c5a670ab (patch)
tree054bc2ffe70e50b1fc7e97d74e36c3fb74d86b04 /drivers
parent71393a220d09ccdf9c47463a2316772e5149f291 (diff)
MLK-10774-33 imx:mx6 add udc and fastboot support
Add udc and fastboot support We did not use the upstream way. Currently use CI_UDC and USB_GAGDET of upstream can make fastboot work, but lack of flash operation, so we still use our way. Signed-off-by: Peng Fan <Peng.Fan@freescale.com> Signed-off-by: Nitin Garg <nitin.garg@freescale.com> Signed-off-by: Ye.Li <B37916@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/fastboot/Makefile8
-rw-r--r--drivers/fastboot/fastboot.c1127
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/imx_udc.c1190
5 files changed, 2327 insertions, 0 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 5ef58c051e..2350a9a1d7 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -23,3 +23,4 @@ obj-y += input/
# SOC specific infrastructure drivers.
obj-y += soc/
obj-y += thermal/
+obj-y += fastboot/
diff --git a/drivers/fastboot/Makefile b/drivers/fastboot/Makefile
new file mode 100644
index 0000000000..251120d85c
--- /dev/null
+++ b/drivers/fastboot/Makefile
@@ -0,0 +1,8 @@
+#
+# (C) Copyright 2000-2007
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_FASTBOOT) += fastboot.o
diff --git a/drivers/fastboot/fastboot.c b/drivers/fastboot/fastboot.c
new file mode 100644
index 0000000000..0b5bebb652
--- /dev/null
+++ b/drivers/fastboot/fastboot.c
@@ -0,0 +1,1127 @@
+/*
+ * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <config.h>
+#include <malloc.h>
+#include <fastboot.h>
+#include <usb/imx_udc.h>
+#include <asm/io.h>
+#include <usbdevice.h>
+#include <mmc.h>
+#include <sata.h>
+#ifdef CONFIG_ANDROID_RECOVERY
+#include <recovery.h>
+#endif
+
+/*
+ * Defines
+ */
+#define NUM_ENDPOINTS 2
+
+#define CONFIG_USBD_OUT_PKTSIZE 0x200
+#define CONFIG_USBD_IN_PKTSIZE 0x200
+#define MAX_BUFFER_SIZE 0x200
+
+/*
+ * imx family android layout
+ * mbr - 0 ~ 0x3FF byte
+ * bootloader - 0x400 ~ 0xFFFFF byte
+ * kernel - 0x100000 ~ 5FFFFF byte
+ * uramedisk - 0x600000 ~ 0x6FFFFF supposing 1M temporarily
+ * SYSTEM partition - /dev/mmcblk0p2 or /dev/sda2
+ * RECOVERY parittion - dev/mmcblk0p4 or /dev/sda4
+ */
+#define ANDROID_MBR_OFFSET 0
+#define ANDROID_MBR_SIZE 0x200
+#define ANDROID_BOOTLOADER_OFFSET 0x400
+#define ANDROID_BOOTLOADER_SIZE 0xFFC00
+#define ANDROID_KERNEL_OFFSET 0x100000
+#define ANDROID_KERNEL_SIZE 0x500000
+#define ANDROID_URAMDISK_OFFSET 0x600000
+#define ANDROID_URAMDISK_SIZE 0x100000
+
+#define STR_LANG_INDEX 0x00
+#define STR_MANUFACTURER_INDEX 0x01
+#define STR_PRODUCT_INDEX 0x02
+#define STR_SERIAL_INDEX 0x03
+#define STR_CONFIG_INDEX 0x04
+#define STR_DATA_INTERFACE_INDEX 0x05
+#define STR_CTRL_INTERFACE_INDEX 0x06
+#define STR_COUNT 0x07
+
+#define FASTBOOT_FBPARTS_ENV_MAX_LEN 1024
+/* To support the Android-style naming of flash */
+#define MAX_PTN 16
+
+
+/*pentry index internally*/
+enum {
+ PTN_MBR_INDEX = 0,
+ PTN_BOOTLOADER_INDEX,
+ PTN_KERNEL_INDEX,
+ PTN_URAMDISK_INDEX,
+ PTN_SYSTEM_INDEX,
+ PTN_RECOVERY_INDEX
+};
+
+struct fastboot_device_info fastboot_devinfo;
+
+/* defined and used by gadget/ep0.c */
+extern struct usb_string_descriptor **usb_strings;
+
+static struct usb_device_instance device_instance[1];
+static struct usb_bus_instance bus_instance[1];
+static struct usb_configuration_instance config_instance[1];
+static struct usb_interface_instance interface_instance[1];
+static struct usb_alternate_instance alternate_instance[1];
+/* one extra for control endpoint */
+static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1];
+
+static struct cmd_fastboot_interface *fastboot_interface;
+static int fastboot_configured_flag;
+static int usb_disconnected;
+
+/* Indicies, References */
+static u8 rx_endpoint;
+static u8 tx_endpoint;
+static struct usb_string_descriptor *fastboot_string_table[STR_COUNT];
+
+/* USB Descriptor Strings */
+static u8 wstrLang[4] = {4, USB_DT_STRING, 0x9, 0x4};
+static u8 wstrManufacturer[2 * (sizeof(CONFIG_FASTBOOT_MANUFACTURER_STR))];
+static u8 wstrProduct[2 * (sizeof(CONFIG_FASTBOOT_PRODUCT_NAME_STR))];
+static u8 wstrSerial[2*(sizeof(CONFIG_FASTBOOT_SERIAL_NUM))];
+static u8 wstrConfiguration[2 * (sizeof(CONFIG_FASTBOOT_CONFIGURATION_STR))];
+static u8 wstrDataInterface[2 * (sizeof(CONFIG_FASTBOOT_INTERFACE_STR))];
+
+/* Standard USB Data Structures */
+static struct usb_interface_descriptor interface_descriptors[1];
+static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS];
+static struct usb_configuration_descriptor *configuration_descriptor;
+static struct usb_device_descriptor device_descriptor = {
+ .bLength = sizeof(struct usb_device_descriptor),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(USB_BCD_VERSION),
+ .bDeviceClass = 0xff,
+ .bDeviceSubClass = 0xff,
+ .bDeviceProtocol = 0xff,
+ .bMaxPacketSize0 = 0x40,
+ .idVendor = cpu_to_le16(CONFIG_FASTBOOT_VENDOR_ID),
+ .idProduct = cpu_to_le16(CONFIG_FASTBOOT_PRODUCT_ID),
+ .bcdDevice = cpu_to_le16(CONFIG_FASTBOOT_BCD_DEVICE),
+ .iManufacturer = STR_MANUFACTURER_INDEX,
+ .iProduct = STR_PRODUCT_INDEX,
+ .iSerialNumber = STR_SERIAL_INDEX,
+ .bNumConfigurations = 1
+};
+
+/*
+ * Static Generic Serial specific data
+ */
+
+struct fastboot_config_desc {
+ struct usb_configuration_descriptor configuration_desc;
+ struct usb_interface_descriptor interface_desc[1];
+ struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS];
+};
+
+static struct fastboot_config_desc
+fastboot_configuration_descriptors[1] = {
+ {
+ .configuration_desc = {
+ .bLength = sizeof(struct usb_configuration_descriptor),
+ .bDescriptorType = USB_DT_CONFIG,
+ .wTotalLength =
+ cpu_to_le16(sizeof(struct fastboot_config_desc)),
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_INDEX,
+ .bmAttributes =
+ BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED,
+ .bMaxPower = 0x32
+ },
+ .interface_desc = {
+ {
+ .bLength =
+ sizeof(struct usb_interface_descriptor),
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = 0,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = NUM_ENDPOINTS,
+ .bInterfaceClass =
+ FASTBOOT_INTERFACE_CLASS,
+ .bInterfaceSubClass =
+ FASTBOOT_INTERFACE_SUB_CLASS,
+ .bInterfaceProtocol =
+ FASTBOOT_INTERFACE_PROTOCOL,
+ .iInterface = STR_DATA_INTERFACE_INDEX
+ },
+ },
+ .data_endpoints = {
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = UDC_OUT_ENDPOINT |
+ USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_OUT_PKTSIZE),
+ .bInterval = 0x00,
+ },
+ {
+ .bLength =
+ sizeof(struct usb_endpoint_descriptor),
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = UDC_IN_ENDPOINT |
+ USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize =
+ cpu_to_le16(CONFIG_USBD_IN_PKTSIZE),
+ .bInterval = 0x00,
+ },
+ },
+ },
+};
+
+
+
+static struct fastboot_ptentry ptable[MAX_PTN];
+static unsigned int pcount;
+
+
+/* Static Function Prototypes */
+static void _fastboot_init_strings(void);
+static void _fastboot_init_instances(void);
+static void _fastboot_init_endpoints(void);
+static void _fastboot_event_handler(struct usb_device_instance *device,
+ usb_device_event_t event, int data);
+static int _fastboot_cdc_setup(struct usb_device_request *request,
+ struct urb *urb);
+static int _fastboot_usb_configured(void);
+#if defined(CONFIG_FASTBOOT_STORAGE_SATA) \
+ || defined(CONFIG_FASTBOOT_STORAGE_MMC)
+static int _fastboot_parts_load_from_ptable(void);
+#endif
+#if defined(CONFIG_FASTBOOT_STORAGE_NAND)
+static int _fastboot_parts_load_from_env(void);
+#endif
+static int _fastboot_setup_dev(void);
+static void _fastboot_load_partitions(void);
+
+/* utility function for converting char* to wide string used by USB */
+static void str2wide(char *str, u16 * wide)
+{
+ int i;
+ for (i = 0; i < strlen(str) && str[i]; i++) {
+ #if defined(__LITTLE_ENDIAN)
+ wide[i] = (u16) str[i];
+ #elif defined(__BIG_ENDIAN)
+ wide[i] = ((u16)(str[i])<<8);
+ #else
+ #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined"
+ #endif
+ }
+}
+
+/*
+ Get mmc control number from passed string, eg, "mmc1" mean device 1. Only
+ support "mmc0" to "mmc9" currently. It will be treated as device 0 for
+ other string.
+*/
+static int _fastboot_get_mmc_no(char *env_str)
+{
+ int digit = 0;
+ unsigned char a;
+
+ if (env_str && (strlen(env_str) >= 4) &&
+ !strncmp(env_str, "mmc", 3)) {
+ a = env_str[3];
+ if (a >= '0' && a <= '9')
+ digit = a - '0';
+ }
+
+ return digit;
+}
+
+static int _fastboot_setup_dev(void)
+{
+ char *fastboot_env;
+ fastboot_env = getenv("fastboot_dev");
+
+ if (fastboot_env) {
+ if (!strcmp(fastboot_env, "sata")) {
+ fastboot_devinfo.type = DEV_SATA;
+ fastboot_devinfo.dev_id = 0;
+ } else if (!strcmp(fastboot_env, "nand")) {
+ fastboot_devinfo.type = DEV_NAND;
+ fastboot_devinfo.dev_id = 0;
+ } else if (!strncmp(fastboot_env, "mmc", 3)) {
+ fastboot_devinfo.type = DEV_MMC;
+ fastboot_devinfo.dev_id = _fastboot_get_mmc_no(fastboot_env);
+ }
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Initialize fastboot
+ */
+int fastboot_init(struct cmd_fastboot_interface *interface)
+{
+ printf("fastboot is in init......");
+
+ fastboot_interface = interface;
+ fastboot_interface->product_name = CONFIG_FASTBOOT_PRODUCT_NAME_STR;
+ fastboot_interface->serial_no = CONFIG_FASTBOOT_SERIAL_NUM;
+ fastboot_interface->nand_block_size = 4096;
+ fastboot_interface->transfer_buffer =
+ (unsigned char *)CONFIG_FASTBOOT_TRANSFER_BUF;
+ fastboot_interface->transfer_buffer_size =
+ CONFIG_FASTBOOT_TRANSFER_BUF_SIZE;
+
+ _fastboot_init_strings();
+ /* Basic USB initialization */
+ udc_init();
+
+ _fastboot_init_instances();
+
+ udc_startup_events(device_instance);
+ udc_connect(); /* Enable pullup for host detection */
+
+ return 0;
+}
+
+static void _fastboot_init_strings(void)
+{
+ struct usb_string_descriptor *string;
+
+ fastboot_string_table[STR_LANG_INDEX] =
+ (struct usb_string_descriptor *)wstrLang;
+
+ string = (struct usb_string_descriptor *)wstrManufacturer;
+ string->bLength = sizeof(wstrManufacturer);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide(CONFIG_FASTBOOT_MANUFACTURER_STR, string->wData);
+ fastboot_string_table[STR_MANUFACTURER_INDEX] = string;
+
+ string = (struct usb_string_descriptor *)wstrProduct;
+ string->bLength = sizeof(wstrProduct);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide(CONFIG_FASTBOOT_PRODUCT_NAME_STR, string->wData);
+ fastboot_string_table[STR_PRODUCT_INDEX] = string;
+
+ string = (struct usb_string_descriptor *)wstrSerial;
+ string->bLength = sizeof(wstrSerial);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide(CONFIG_FASTBOOT_SERIAL_NUM, string->wData);
+ fastboot_string_table[STR_SERIAL_INDEX] = string;
+
+ string = (struct usb_string_descriptor *)wstrConfiguration;
+ string->bLength = sizeof(wstrConfiguration);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide(CONFIG_FASTBOOT_CONFIGURATION_STR, string->wData);
+ fastboot_string_table[STR_CONFIG_INDEX] = string;
+
+ string = (struct usb_string_descriptor *) wstrDataInterface;
+ string->bLength = sizeof(wstrDataInterface);
+ string->bDescriptorType = USB_DT_STRING;
+ str2wide(CONFIG_FASTBOOT_INTERFACE_STR, string->wData);
+ fastboot_string_table[STR_DATA_INTERFACE_INDEX] = string;
+
+ /* Now, initialize the string table for ep0 handling */
+ usb_strings = fastboot_string_table;
+}
+
+static void _fastboot_init_instances(void)
+{
+ int i;
+ u16 temp;
+
+ /* Assign endpoint descriptors */
+ ep_descriptor_ptrs[0] =
+ &fastboot_configuration_descriptors[0].data_endpoints[0];
+ ep_descriptor_ptrs[1] =
+ &fastboot_configuration_descriptors[0].data_endpoints[1];
+
+ /* Configuration Descriptor */
+ configuration_descriptor =
+ (struct usb_configuration_descriptor *)
+ &fastboot_configuration_descriptors;
+
+ fastboot_configured_flag = 0;
+
+ /* initialize device instance */
+ memset(device_instance, 0, sizeof(struct usb_device_instance));
+ device_instance->device_state = STATE_INIT;
+ device_instance->device_descriptor = &device_descriptor;
+ device_instance->event = _fastboot_event_handler;
+ device_instance->cdc_recv_setup = _fastboot_cdc_setup;
+ device_instance->bus = bus_instance;
+ device_instance->configurations = 1;
+ device_instance->configuration_instance_array = config_instance;
+
+ /* initialize bus instance */
+ memset(bus_instance, 0, sizeof(struct usb_bus_instance));
+ bus_instance->device = device_instance;
+ bus_instance->endpoint_array = endpoint_instance;
+ bus_instance->max_endpoints = NUM_ENDPOINTS + 1;
+ bus_instance->maxpacketsize = 0xFF;
+ bus_instance->serial_number_str = CONFIG_FASTBOOT_SERIAL_NUM;
+
+ /* configuration instance */
+ memset(config_instance, 0,
+ sizeof(struct usb_configuration_instance));
+ config_instance->interfaces = 1;
+ config_instance->configuration_descriptor = configuration_descriptor;
+ config_instance->interface_instance_array = interface_instance;
+
+ /* interface instance */
+ memset(interface_instance, 0,
+ sizeof(struct usb_interface_instance));
+ interface_instance->alternates = 1;
+ interface_instance->alternates_instance_array = alternate_instance;
+
+ /* alternates instance */
+ memset(alternate_instance, 0,
+ sizeof(struct usb_alternate_instance));
+ alternate_instance->interface_descriptor = interface_descriptors;
+ alternate_instance->endpoints = NUM_ENDPOINTS;
+ alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs;
+
+ /* endpoint instances */
+ memset(&endpoint_instance[0], 0,
+ sizeof(struct usb_endpoint_instance));
+ endpoint_instance[0].endpoint_address = 0;
+ endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE;
+ endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL;
+ endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE;
+ endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL;
+ udc_setup_ep(device_instance, 0, &endpoint_instance[0]);
+
+ for (i = 1; i <= NUM_ENDPOINTS; i++) {
+ memset(&endpoint_instance[i], 0,
+ sizeof(struct usb_endpoint_instance));
+
+ endpoint_instance[i].endpoint_address =
+ ep_descriptor_ptrs[i - 1]->bEndpointAddress;
+
+ endpoint_instance[i].rcv_attributes =
+ ep_descriptor_ptrs[i - 1]->bmAttributes;
+
+ /*fix the abort caused by unalignment*/
+ temp = *(u8 *)&ep_descriptor_ptrs[i - 1]->wMaxPacketSize;
+ temp |=
+ (*(((u8 *)&ep_descriptor_ptrs[i - 1]->wMaxPacketSize) + 1) << 8);
+
+ endpoint_instance[i].rcv_packetSize =
+ le16_to_cpu(temp);
+
+ endpoint_instance[i].tx_attributes =
+ ep_descriptor_ptrs[i - 1]->bmAttributes;
+
+ endpoint_instance[i].tx_packetSize =
+ le16_to_cpu(temp);
+
+ endpoint_instance[i].tx_attributes =
+ ep_descriptor_ptrs[i - 1]->bmAttributes;
+
+ urb_link_init(&endpoint_instance[i].rcv);
+ urb_link_init(&endpoint_instance[i].rdy);
+ urb_link_init(&endpoint_instance[i].tx);
+ urb_link_init(&endpoint_instance[i].done);
+
+ if (endpoint_instance[i].endpoint_address & USB_DIR_IN) {
+ tx_endpoint = i;
+ endpoint_instance[i].tx_urb =
+ usbd_alloc_urb(device_instance,
+ &endpoint_instance[i]);
+ } else {
+ rx_endpoint = i;
+ endpoint_instance[i].rcv_urb =
+ usbd_alloc_urb(device_instance,
+ &endpoint_instance[i]);
+ }
+ }
+}
+
+static void _fastboot_init_endpoints(void)
+{
+ int i;
+
+ bus_instance->max_endpoints = NUM_ENDPOINTS + 1;
+ for (i = 1; i <= NUM_ENDPOINTS; i++)
+ udc_setup_ep(device_instance, i, &endpoint_instance[i]);
+}
+
+static void _fastboot_destroy_endpoints(void)
+{
+ int i;
+ struct urb *tx_urb;
+
+ for (i = 1; i <= NUM_ENDPOINTS; i++) {
+ /*dealloc urb*/
+ if (endpoint_instance[i].endpoint_address & USB_DIR_IN) {
+ if (endpoint_instance[i].tx_urb)
+ usbd_dealloc_urb(endpoint_instance[i].tx_urb);
+
+ while (endpoint_instance[i].tx_queue) {
+ tx_urb = first_urb_detached(&endpoint_instance[i].tx);
+ if (tx_urb) {
+ usbd_dealloc_urb(tx_urb);
+ endpoint_instance[i].tx_queue--;
+ } else {
+ break;
+ }
+ }
+ endpoint_instance[i].tx_queue = 0;
+
+ do {
+ tx_urb = first_urb_detached(&endpoint_instance[i].done);
+ if (tx_urb)
+ usbd_dealloc_urb(tx_urb);
+ } while (tx_urb);
+
+ } else {
+ if (endpoint_instance[i].rcv_urb)
+ usbd_dealloc_urb(endpoint_instance[i].rcv_urb);
+ }
+
+ udc_destroy_ep(device_instance, &endpoint_instance[i]);
+ }
+}
+
+
+static int _fill_buffer(u8 *buf)
+{
+ struct usb_endpoint_instance *endpoint =
+ &endpoint_instance[rx_endpoint];
+
+ if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) {
+ unsigned int nb = 0;
+ char *src = (char *)endpoint->rcv_urb->buffer;
+ unsigned int rx_avail = MAX_BUFFER_SIZE;
+
+ if (rx_avail >= endpoint->rcv_urb->actual_length) {
+ nb = endpoint->rcv_urb->actual_length;
+ memcpy(buf, src, nb);
+ endpoint->rcv_urb->actual_length = 0;
+ }
+ return nb;
+ }
+ return 0;
+}
+
+static struct urb *_next_urb(struct usb_device_instance *device,
+ struct usb_endpoint_instance *endpoint)
+{
+ struct urb *current_urb = NULL;
+ int space;
+
+ /* If there's a queue, then we should add to the last urb */
+ if (!endpoint->tx_queue)
+ current_urb = endpoint->tx_urb;
+ else
+ /* Last urb from tx chain */
+ current_urb =
+ p2surround(struct urb, link, endpoint->tx.prev);
+
+ /* Make sure this one has enough room */
+ space = current_urb->buffer_length - current_urb->actual_length;
+ if (space > 0)
+ return current_urb;
+ else { /* No space here */
+ /* First look at done list */
+ current_urb = first_urb_detached(&endpoint->done);
+ if (!current_urb)
+ current_urb = usbd_alloc_urb(device, endpoint);
+
+ urb_append(&endpoint->tx, current_urb);
+ endpoint->tx_queue++;
+ }
+ return current_urb;
+}
+
+static int _fastboot_usb_configured(void)
+{
+ return fastboot_configured_flag;
+}
+
+static void _fastboot_event_handler(struct usb_device_instance *device,
+ usb_device_event_t event, int data)
+{
+ switch (event) {
+ case DEVICE_RESET:
+ case DEVICE_BUS_INACTIVE:
+ fastboot_configured_flag = 0;
+ break;
+ case DEVICE_CONFIGURED:
+ fastboot_configured_flag = 1;
+ _fastboot_init_endpoints();
+ break;
+ case DEVICE_ADDRESS_ASSIGNED:
+ default:
+ break;
+ }
+}
+
+static int _fastboot_cdc_setup(struct usb_device_request *request,
+ struct urb *urb)
+{
+ return 0;
+}
+
+
+/*!
+ * Function to receive data from host through channel
+ *
+ * @buf buffer to fill in
+ * @count read data size
+ *
+ * @return 0
+ */
+int fastboot_usb_recv(u8 *buf, int count)
+{
+ int len = 0;
+
+ while (!_fastboot_usb_configured())
+ udc_irq();
+
+ /* update rxqueue to wait new data */
+ mxc_udc_rxqueue_update(2, count);
+
+ while (!len) {
+ if (is_usb_disconnected()) {
+ /*it will not unconfigure when disconnect
+ from host, so here needs manual unconfigure
+ anyway, it's just a workaround*/
+ fastboot_configured_flag = 0;
+ usb_disconnected = 1;
+ return 0;
+ }
+ udc_irq();
+ if (_fastboot_usb_configured())
+ len = _fill_buffer(buf);
+ }
+ return len;
+}
+
+int fastboot_getvar(const char *rx_buffer, char *tx_buffer)
+{
+ /* Place board specific variables here */
+ return 0;
+}
+
+int fastboot_poll()
+{
+ u8 buffer[MAX_BUFFER_SIZE];
+ int length = 0;
+
+ memset(buffer, 0, MAX_BUFFER_SIZE);
+
+ length = fastboot_usb_recv(buffer, MAX_BUFFER_SIZE);
+
+ /* If usb disconnected, blocked here to wait */
+ if (usb_disconnected) {
+ udc_disconnect();
+ udc_connect();
+ /*the udc_connect will be blocked until connect to host
+ so, the usb_disconnect should be 0 after udc_connect,
+ and should be set manually. Anyway, it's just a workaround*/
+ usb_disconnected = 0;
+ }
+
+ if (!length)
+ return FASTBOOT_INACTIVE;
+
+ /* Pass this up to the interface's handler */
+ if (fastboot_interface && fastboot_interface->rx_handler) {
+ if (!fastboot_interface->rx_handler(buffer, length))
+ return FASTBOOT_OK;
+ }
+ return FASTBOOT_OK;
+}
+
+int fastboot_tx(unsigned char *buffer, unsigned int buffer_size)
+{
+ /* Not realized yet */
+ return 0;
+}
+
+static int _fastboot_write_buffer(const char *buffer,
+ unsigned int buffer_size)
+{
+ struct usb_endpoint_instance *endpoint =
+ (struct usb_endpoint_instance *)&endpoint_instance[tx_endpoint];
+ struct urb *current_urb = NULL;
+
+ if (!_fastboot_usb_configured())
+ return 0;
+
+ current_urb = _next_urb(device_instance, endpoint);
+ if (buffer_size) {
+ char *dest;
+ int space_avail, popnum, count, total = 0;
+
+ /* Break buffer into urb sized pieces,
+ * and link each to the endpoint
+ */
+ count = buffer_size;
+ while (count > 0) {
+ if (!current_urb) {
+ printf("current_urb is NULL, buffer_size %d\n",
+ buffer_size);
+ return total;
+ }
+
+ dest = (char *)current_urb->buffer +
+ current_urb->actual_length;
+
+ space_avail = current_urb->buffer_length -
+ current_urb->actual_length;
+ popnum = min(space_avail, count);
+ if (popnum == 0)
+ break;
+
+ memcpy(dest, buffer + total, popnum);
+ printf("send: %s\n", (char *)buffer);
+
+ current_urb->actual_length += popnum;
+ total += popnum;
+
+ if (udc_endpoint_write(endpoint))
+ /* Write pre-empted by RX */
+ return 0;
+ count -= popnum;
+ } /* end while */
+ return total;
+ }
+ return 0;
+}
+
+int fastboot_tx_status(const char *buffer, unsigned int buffer_size)
+{
+ int len = 0;
+
+ while (buffer_size > 0) {
+ len = _fastboot_write_buffer(buffer + len, buffer_size);
+ buffer_size -= len;
+
+ udc_irq();
+ }
+ udc_irq();
+
+ return 0;
+}
+
+void fastboot_shutdown(void)
+{
+ usb_shutdown();
+
+ /* Reset interface*/
+ if (fastboot_interface &&
+ fastboot_interface->reset_handler) {
+ fastboot_interface->reset_handler();
+ }
+
+ /* Reset some globals */
+ _fastboot_destroy_endpoints();
+ fastboot_interface = NULL;
+ fastboot_configured_flag = 0;
+ usb_disconnected = 0;
+
+ /*free memory*/
+ udc_destroy();
+}
+
+/*
+ * CPU and board-specific fastboot initializations. Aliased function
+ * signals caller to move on
+ */
+static void __def_fastboot_setup(void)
+{
+ /*do nothing here*/
+}
+void board_fastboot_setup(void) \
+ __attribute__((weak, alias("__def_fastboot_setup")));
+
+
+void fastboot_setup(void)
+{
+ /*execute board relevant initilizations for preparing fastboot */
+ board_fastboot_setup();
+
+ /*get the fastboot dev*/
+ _fastboot_setup_dev();
+
+ /*check if we need to setup recovery*/
+#ifdef CONFIG_ANDROID_RECOVERY
+ check_recovery_mode();
+#endif
+
+ /*load partitions information for the fastboot dev*/
+ _fastboot_load_partitions();
+}
+
+/* export to lib_arm/board.c */
+void check_fastboot(void)
+{
+ if (fastboot_check_and_clean_flag())
+ do_fastboot(NULL, 0, 0, 0);
+}
+
+#if defined(CONFIG_FASTBOOT_STORAGE_SATA) \
+ || defined(CONFIG_FASTBOOT_STORAGE_MMC)
+/**
+ @mmc_dos_partition_index: the partition index in mbr.
+ @mmc_partition_index: the boot partition or user partition index,
+ not related to the partition table.
+ */
+static int _fastboot_parts_add_ptable_entry(int ptable_index,
+ int mmc_dos_partition_index,
+ int mmc_partition_index,
+ const char *name,
+ block_dev_desc_t *dev_desc,
+ struct fastboot_ptentry *ptable)
+{
+ disk_partition_t info;
+ strcpy(ptable[ptable_index].name, name);
+
+ if (get_partition_info(dev_desc,
+ mmc_dos_partition_index, &info)) {
+ printf("Bad partition index:%d for partition:%s\n",
+ mmc_dos_partition_index, name);
+ return -1;
+ } else {
+ ptable[ptable_index].start = info.start;
+ ptable[ptable_index].length = info.size;
+ ptable[ptable_index].partition_id = mmc_partition_index;
+ }
+ return 0;
+}
+
+static int _fastboot_parts_load_from_ptable(void)
+{
+ int i;
+#ifdef CONFIG_CMD_SATA
+ int sata_device_no;
+#endif
+
+ /* mmc boot partition: -1 means no partition, 0 user part., 1 boot part.
+ * default is no partition, for emmc default user part, except emmc*/
+ int boot_partition = FASTBOOT_MMC_NONE_PARTITION_ID;
+ int user_partition = FASTBOOT_MMC_NONE_PARTITION_ID;
+
+ struct mmc *mmc;
+ block_dev_desc_t *dev_desc;
+ struct fastboot_ptentry ptable[PTN_RECOVERY_INDEX + 1];
+
+ /* sata case in env */
+ if (fastboot_devinfo.type == DEV_SATA) {
+#ifdef CONFIG_CMD_SATA
+ puts("flash target is SATA\n");
+ if (sata_initialize())
+ return -1;
+ sata_device_no = CONFIG_FASTBOOT_SATA_NO;
+ if (sata_device_no >= CONFIG_SYS_SATA_MAX_DEVICE) {
+ printf("Unknown SATA(%d) device for fastboot\n",
+ sata_device_no);
+ return -1;
+ }
+ dev_desc = sata_get_dev(sata_device_no);
+#else /*! CONFIG_CMD_SATA*/
+ puts("SATA isn't buildin\n");
+ return -1;
+#endif /*! CONFIG_CMD_SATA*/
+ } else if (fastboot_devinfo.type == DEV_MMC) {
+ int mmc_no = 0;
+ mmc_no = fastboot_devinfo.dev_id;
+
+ printf("flash target is MMC:%d\n", mmc_no);
+ mmc = find_mmc_device(mmc_no);
+ if (mmc && mmc_init(mmc))
+ printf("MMC card init failed!\n");
+
+ dev_desc = get_dev("mmc", mmc_no);
+ if (NULL == dev_desc) {
+ printf("** Block device MMC %d not supported\n",
+ mmc_no);
+ return -1;
+ }
+
+ /* multiple boot paritions for eMMC 4.3 later */
+ if (mmc->part_config != MMCPART_NOAVAILABLE) {
+ boot_partition = FASTBOOT_MMC_BOOT_PARTITION_ID;
+ user_partition = FASTBOOT_MMC_USER_PARTITION_ID;
+ }
+ } else {
+ printf("Can't setup partition table on this device %d\n",
+ fastboot_devinfo.type);
+ return -1;
+ }
+
+ memset((char *)ptable, 0,
+ sizeof(struct fastboot_ptentry) * (PTN_RECOVERY_INDEX + 1));
+ /* MBR */
+ strcpy(ptable[PTN_MBR_INDEX].name, "mbr");
+ ptable[PTN_MBR_INDEX].start = ANDROID_MBR_OFFSET / dev_desc->blksz;
+ ptable[PTN_MBR_INDEX].length = ANDROID_MBR_SIZE / dev_desc->blksz;
+ ptable[PTN_MBR_INDEX].partition_id = user_partition;
+ /* Bootloader */
+ strcpy(ptable[PTN_BOOTLOADER_INDEX].name, "bootloader");
+ ptable[PTN_BOOTLOADER_INDEX].start =
+ ANDROID_BOOTLOADER_OFFSET / dev_desc->blksz;
+ ptable[PTN_BOOTLOADER_INDEX].length =
+ ANDROID_BOOTLOADER_SIZE / dev_desc->blksz;
+ ptable[PTN_BOOTLOADER_INDEX].partition_id = boot_partition;
+
+ _fastboot_parts_add_ptable_entry(PTN_KERNEL_INDEX,
+ CONFIG_ANDROID_BOOT_PARTITION_MMC,
+ user_partition, "boot", dev_desc, ptable);
+ _fastboot_parts_add_ptable_entry(PTN_RECOVERY_INDEX,
+ CONFIG_ANDROID_RECOVERY_PARTITION_MMC,
+ user_partition,
+ "recovery", dev_desc, ptable);
+ _fastboot_parts_add_ptable_entry(PTN_SYSTEM_INDEX,
+ CONFIG_ANDROID_SYSTEM_PARTITION_MMC,
+ user_partition,
+ "system", dev_desc, ptable);
+
+ for (i = 0; i <= PTN_RECOVERY_INDEX; i++)
+ fastboot_flash_add_ptn(&ptable[i]);
+
+ return 0;
+}
+#endif /*CONFIG_FASTBOOT_STORAGE_SATA || CONFIG_FASTBOOT_STORAGE_MMC*/
+
+#if defined(CONFIG_FASTBOOT_STORAGE_NAND)
+static unsigned long long _memparse(char *ptr, char **retptr)
+{
+ char *endptr; /* local pointer to end of parsed string */
+
+ unsigned long ret = simple_strtoul(ptr, &endptr, 0);
+
+ switch (*endptr) {
+ case 'M':
+ case 'm':
+ ret <<= 10;
+ case 'K':
+ case 'k':
+ ret <<= 10;
+ endptr++;
+ default:
+ break;
+ }
+
+ if (retptr)
+ *retptr = endptr;
+
+ return ret;
+}
+
+static int _fastboot_parts_add_env_entry(char *s, char **retptr)
+{
+ unsigned long size;
+ unsigned long offset = 0;
+ char *name;
+ int name_len;
+ int delim;
+ unsigned int flags;
+ struct fastboot_ptentry part;
+
+ size = _memparse(s, &s);
+ if (0 == size) {
+ printf("Error:FASTBOOT size of parition is 0\n");
+ return 1;
+ }
+
+ /* fetch partition name and flags */
+ flags = 0; /* this is going to be a regular partition */
+ delim = 0;
+ /* check for offset */
+ if (*s == '@') {
+ s++;
+ offset = _memparse(s, &s);
+ } else {
+ printf("Error:FASTBOOT offset of parition is not given\n");
+ return 1;
+ }
+
+ /* now look for name */
+ if (*s == '(')
+ delim = ')';
+
+ if (delim) {
+ char *p;
+
+ name = ++s;
+ p = strchr((const char *)name, delim);
+ if (!p) {
+ printf("Error:FASTBOOT no closing %c found in partition name\n",
+ delim);
+ return 1;
+ }
+ name_len = p - name;
+ s = p + 1;
+ } else {
+ printf("Error:FASTBOOT no partition name for \'%s\'\n", s);
+ return 1;
+ }
+
+ /* check for options */
+ while (1) {
+ if (strncmp(s, "i", 1) == 0) {
+ flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_I;
+ s += 1;
+ } else if (strncmp(s, "ubifs", 5) == 0) {
+ /* ubifs */
+ flags |= FASTBOOT_PTENTRY_FLAGS_WRITE_TRIMFFS;
+ s += 5;
+ } else {
+ break;
+ }
+ if (strncmp(s, "|", 1) == 0)
+ s += 1;
+ }
+
+ /* enter this partition (offset will be calculated later if it is zero at this point) */
+ part.length = size;
+ part.start = offset;
+ part.flags = flags;
+
+ if (name) {
+ if (name_len >= sizeof(part.name)) {
+ printf("Error:FASTBOOT partition name is too long\n");
+ return 1;
+ }
+ strncpy(&part.name[0], name, name_len);
+ /* name is not null terminated */
+ part.name[name_len] = '\0';
+ } else {
+ printf("Error:FASTBOOT no name\n");
+ return 1;
+ }
+
+ fastboot_flash_add_ptn(&part);
+
+ /*if the nand partitions envs are not initialized, try to init them*/
+ if (check_parts_values(&part))
+ save_parts_values(&part, part.start, part.length);
+
+ /* return (updated) pointer command line string */
+ *retptr = s;
+
+ /* return partition table */
+ return 0;
+}
+
+static int _fastboot_parts_load_from_env(void)
+{
+ char fbparts[FASTBOOT_FBPARTS_ENV_MAX_LEN], *env;
+
+ env = getenv("fbparts");
+ if (env) {
+ unsigned int len;
+ len = strlen(env);
+ if (len && len < FASTBOOT_FBPARTS_ENV_MAX_LEN) {
+ char *s, *e;
+
+ memcpy(&fbparts[0], env, len + 1);
+ printf("Fastboot: Adding partitions from environment\n");
+ s = &fbparts[0];
+ e = s + len;
+ while (s < e) {
+ if (_fastboot_parts_add_env_entry(s, &s)) {
+ printf("Error:Fastboot: Abort adding partitions\n");
+ pcount = 0;
+ return 1;
+ }
+ /* Skip a bunch of delimiters */
+ while (s < e) {
+ if ((' ' == *s) ||
+ ('\t' == *s) ||
+ ('\n' == *s) ||
+ ('\r' == *s) ||
+ (',' == *s)) {
+ s++;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+#endif /*CONFIG_FASTBOOT_STORAGE_NAND*/
+
+static void _fastboot_load_partitions(void)
+{
+ pcount = 0;
+#if defined(CONFIG_FASTBOOT_STORAGE_NAND)
+ _fastboot_parts_load_from_env();
+#elif defined(CONFIG_FASTBOOT_STORAGE_SATA) \
+ || defined(CONFIG_FASTBOOT_STORAGE_MMC)
+ _fastboot_parts_load_from_ptable();
+#endif
+}
+
+/*
+ * Android style flash utilties */
+void fastboot_flash_add_ptn(struct fastboot_ptentry *ptn)
+{
+ if (pcount < MAX_PTN) {
+ memcpy(ptable + pcount, ptn, sizeof(struct fastboot_ptentry));
+ pcount++;
+ }
+}
+
+void fastboot_flash_dump_ptn(void)
+{
+ unsigned int n;
+ for (n = 0; n < pcount; n++) {
+ struct fastboot_ptentry *ptn = ptable + n;
+ printf("ptn %d name='%s' start=%d len=%d\n",
+ n, ptn->name, ptn->start, ptn->length);
+ }
+}
+
+
+struct fastboot_ptentry *fastboot_flash_find_ptn(const char *name)
+{
+ unsigned int n;
+
+ for (n = 0; n < pcount; n++) {
+ /* Make sure a substring is not accepted */
+ if (strlen(name) == strlen(ptable[n].name)) {
+ if (0 == strcmp(ptable[n].name, name))
+ return ptable + n;
+ }
+ }
+
+ printf("can't find partition: %s, dump the partition table\n", name);
+ fastboot_flash_dump_ptn();
+ return 0;
+}
+
+struct fastboot_ptentry *fastboot_flash_get_ptn(unsigned int n)
+{
+ if (n < pcount)
+ return ptable + n;
+ else
+ return 0;
+}
+
+unsigned int fastboot_flash_get_ptn_count(void)
+{
+ return pcount;
+}
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 70bb550fa4..4869867cbf 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -34,5 +34,6 @@ obj-y += ep0.o
obj-$(CONFIG_DW_UDC) += designware_udc.o
obj-$(CONFIG_MPC885_FAMILY) += mpc8xx_udc.o
obj-$(CONFIG_CPU_PXA27X) += pxa27x_udc.o
+obj-$(CONFIG_IMX_UDC) += imx_udc.o
endif
endif
diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c
new file mode 100644
index 0000000000..9371ef5e6a
--- /dev/null
+++ b/drivers/usb/gadget/imx_udc.c
@@ -0,0 +1,1190 @@
+/*
+ * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/types.h>
+#include <malloc.h>
+#include <command.h>
+#include <asm/errno.h>
+#include <usbdevice.h>
+#include <usb/imx_udc.h>
+
+#include "ep0.h"
+
+#ifdef DEBUG
+#define DBG(x...) printf(x)
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+#define mdelay(n) udelay((n)*1000)
+
+#define EP_TQ_ITEM_SIZE 16
+
+#define inc_index(x) (x = ((x+1) % EP_TQ_ITEM_SIZE))
+
+#define ep_is_in(e, tx) ((e == 0) ? (mxc_udc.ep0_dir == USB_DIR_IN) : tx)
+
+#define USB_RECIP_MASK 0x03
+#define USB_TYPE_MASK (0x03 << 5)
+#define USB_MEM_ALIGN_BYTE 4096
+#define USB_DEV_DQH_ALIGN 64
+#define USB_DEV_DTD_ALIGN 64
+
+/*fixed the dtd buffer to 4096 bytes, even it could be 20KB*/
+#define USB_DEV_DTD_MAX_BUFFER_SIZE 4096
+
+#define CACHE_ALIGNED_END(start, length) \
+ (ALIGN((uint32_t)start + length, ARCH_DMA_MINALIGN))
+
+struct mxc_ep_t{
+ int epnum;
+ int dir;
+ /* change from int to u32 to make `min` happy */
+ u32 max_pkt_size;
+ struct usb_endpoint_instance *epi;
+ struct ep_queue_item *ep_dtd[EP_TQ_ITEM_SIZE];
+ int index; /* to index the free tx tqi */
+ int done; /* to index the complete rx tqi */
+ struct ep_queue_item *tail; /* last item in the dtd chain */
+ struct ep_queue_head *ep_qh;
+} ;
+
+struct mxc_udc_ctrl{
+ int max_ep;
+ int ep0_dir;
+ int setaddr;
+ struct ep_queue_head *ep_qh;
+ struct mxc_ep_t *mxc_ep;
+ u32 qh_unaligned;
+};
+
+static int usb_highspeed;
+static int usb_inited;
+static struct mxc_udc_ctrl mxc_udc;
+static struct usb_device_instance *udc_device;
+static struct urb *ep0_urb;
+/*
+ * malloc an aligned memory
+ * unaligned_addr: return a unaligned address for memory free
+ * size : memory size
+ * align : alignment for this memroy
+ * return : aligned address(NULL when malloc failt)
+*/
+static void *malloc_aligned_buffer(u32 *unaligned_addr,
+ int size, int align)
+{
+ int msize = (size + align - 1);
+ u32 vir, vir_align;
+
+ /* force the allocated memory size to be aligned with min cache operation unit.
+ * So it is safe to flush/invalidate the cache.
+ */
+ msize = (msize + ARCH_DMA_MINALIGN - 1) / ARCH_DMA_MINALIGN;
+ msize = msize * ARCH_DMA_MINALIGN;
+
+ vir = (u32)malloc(msize);
+ memset((void *)vir, 0, msize);
+ vir_align = (vir + align - 1) & (~(align - 1));
+ *unaligned_addr = vir;
+
+ DBG("alloc aligned vir addr %x\n", vir_align);
+ return (void *)vir_align;
+}
+
+int is_usb_disconnected()
+{
+ int ret = 0;
+
+ ret = readl(USB_OTGSC) & OTGSC_B_SESSION_VALID ? 0 : 1;
+ return ret;
+}
+
+static int mxc_init_usb_qh(void)
+{
+ int size;
+ memset(&mxc_udc, 0, sizeof(mxc_udc));
+ mxc_udc.max_ep = (readl(USB_DCCPARAMS) & DCCPARAMS_DEN_MASK) * 2;
+ DBG("udc max ep = %d\n", mxc_udc.max_ep);
+ size = mxc_udc.max_ep * sizeof(struct ep_queue_head);
+ mxc_udc.ep_qh = malloc_aligned_buffer(&mxc_udc.qh_unaligned,
+ size, USB_MEM_ALIGN_BYTE);
+ if (!mxc_udc.ep_qh) {
+ printf("malloc ep qh dma buffer failure\n");
+ return -1;
+ }
+ memset(mxc_udc.ep_qh, 0, size);
+
+ /*flush cache to physical memory*/
+ flush_dcache_range((unsigned long)mxc_udc.ep_qh,
+ CACHE_ALIGNED_END(mxc_udc.ep_qh, size));
+
+ writel(virt_to_phys(mxc_udc.ep_qh) & 0xfffff800, USB_ENDPOINTLISTADDR);
+ return 0;
+}
+
+static int mxc_destroy_usb_qh(void)
+{
+ if (mxc_udc.ep_qh && mxc_udc.qh_unaligned) {
+ free((void *)mxc_udc.qh_unaligned);
+ mxc_udc.ep_qh = 0;
+ mxc_udc.qh_unaligned = 0;
+ mxc_udc.max_ep = 0;
+ }
+
+ return 0;
+}
+
+static int mxc_init_ep_struct(void)
+{
+ int i;
+
+ DBG("init mxc ep\n");
+ mxc_udc.mxc_ep = malloc(mxc_udc.max_ep * sizeof(struct mxc_ep_t));
+ if (!mxc_udc.mxc_ep) {
+ printf("malloc ep struct failure\n");
+ return -1;
+ }
+ memset((void *)mxc_udc.mxc_ep, 0,
+ sizeof(struct mxc_ep_t) * mxc_udc.max_ep);
+ for (i = 0; i < mxc_udc.max_ep / 2; i++) {
+ struct mxc_ep_t *ep;
+ ep = mxc_udc.mxc_ep + i * 2;
+ ep->epnum = i;
+ ep->index = ep->done = 0;
+ ep->dir = USB_RECV; /* data from host to device */
+ ep->ep_qh = &mxc_udc.ep_qh[i * 2];
+
+ ep = mxc_udc.mxc_ep + (i * 2 + 1);
+ ep->epnum = i;
+ ep->index = ep->done = 0;
+ ep->dir = USB_SEND; /* data to host from device */
+ ep->ep_qh = &mxc_udc.ep_qh[(i * 2 + 1)];
+ }
+ return 0;
+}
+
+static int mxc_destroy_ep_struct(void)
+{
+ if (mxc_udc.mxc_ep) {
+ free(mxc_udc.mxc_ep);
+ mxc_udc.mxc_ep = 0;
+ }
+ return 0;
+}
+
+static int mxc_init_ep_dtd(u8 index)
+{
+ struct mxc_ep_t *ep;
+ u32 unaligned_addr;
+ int i;
+
+ if (index >= mxc_udc.max_ep)
+ DBG("%s ep %d is not valid\n", __func__, index);
+
+ ep = mxc_udc.mxc_ep + index;
+ for (i = 0; i < EP_TQ_ITEM_SIZE; i++) {
+ ep->ep_dtd[i] = malloc_aligned_buffer(&unaligned_addr,
+ sizeof(struct ep_queue_item), USB_DEV_DTD_ALIGN);
+ ep->ep_dtd[i]->item_unaligned_addr = unaligned_addr;
+
+ if (NULL == ep->ep_dtd[i]) {
+ printf("%s malloc tq item failure\n", __func__);
+
+ /*free other already allocated dtd*/
+ while (i) {
+ i--;
+ free((void *)(ep->ep_dtd[i]->item_unaligned_addr));
+ ep->ep_dtd[i] = 0;
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int mxc_destroy_ep_dtd(u8 index)
+{
+ struct mxc_ep_t *ep;
+ int i;
+
+ if (index >= mxc_udc.max_ep)
+ DBG("%s ep %d is not valid\n", __func__, index);
+
+ ep = mxc_udc.mxc_ep + index;
+ for (i = 0; i < EP_TQ_ITEM_SIZE; i++) {
+ if (ep->ep_dtd[i]) {
+ free((void *)(ep->ep_dtd[i]->item_unaligned_addr));
+ ep->ep_dtd[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+static void mxc_ep_qh_setup(u8 ep_num, u8 dir, u8 ep_type,
+ u32 max_pkt_len, u32 zlt, u8 mult)
+{
+ struct ep_queue_head *p_qh = mxc_udc.ep_qh + (2 * ep_num + dir);
+ u32 tmp = 0;
+
+ tmp = max_pkt_len << 16;
+ 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("error ep type is %d\n", ep_type);
+ return;
+ }
+ if (zlt)
+ tmp |= (1<<29);
+
+ p_qh->config = tmp;
+
+ /*flush qh's config field to physical memory*/
+ flush_dcache_range((unsigned long)p_qh,
+ CACHE_ALIGNED_END(p_qh, sizeof(struct ep_queue_head)));
+}
+
+static void mxc_ep_setup(u8 ep_num, u8 dir, u8 ep_type)
+{
+ u32 epctrl = 0;
+ epctrl = readl(USB_ENDPTCTRL(ep_num));
+ if (dir) {
+ if (ep_num)
+ epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
+ epctrl |= EPCTRL_TX_ENABLE;
+ epctrl |= ((u32)(ep_type) << EPCTRL_TX_EP_TYPE_SHIFT);
+ } else {
+ if (ep_num)
+ 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));
+}
+
+static void mxc_ep_destroy(u8 ep_num, u8 dir)
+{
+ u32 epctrl = 0;
+ epctrl = readl(USB_ENDPTCTRL(ep_num));
+ if (dir)
+ epctrl &= ~EPCTRL_TX_ENABLE;
+ else
+ epctrl &= ~EPCTRL_RX_ENABLE;
+
+ writel(epctrl, USB_ENDPTCTRL(ep_num));
+}
+
+
+static void mxc_tqi_init_page(struct ep_queue_item *tqi)
+{
+ tqi->page0 = virt_to_phys((void *)(tqi->page_vir));
+ tqi->page1 = tqi->page0 + 0x1000;
+ tqi->page2 = tqi->page1 + 0x1000;
+ tqi->page3 = tqi->page2 + 0x1000;
+ tqi->page4 = tqi->page3 + 0x1000;
+}
+
+static int mxc_malloc_ep0_ptr(struct mxc_ep_t *ep)
+{
+ int i;
+ struct ep_queue_item *tqi;
+ int max_pkt_size = USB_MAX_CTRL_PAYLOAD;
+
+ ep->max_pkt_size = max_pkt_size;
+ for (i = 0; i < EP_TQ_ITEM_SIZE; i++) {
+ tqi = ep->ep_dtd[i];
+ tqi->page_vir = (u32)malloc_aligned_buffer(&tqi->page_unaligned,
+ max_pkt_size,
+ USB_MEM_ALIGN_BYTE);
+ if ((void *)tqi->page_vir == NULL) {
+ printf("malloc ep's dtd bufer failure, i=%d\n", i);
+ return -1;
+ }
+ mxc_tqi_init_page(tqi);
+
+ /*flush dtd's config field to physical memory*/
+ flush_dcache_range((unsigned long)tqi,
+ CACHE_ALIGNED_END(tqi, sizeof(struct ep_queue_item)));
+ }
+ return 0;
+}
+
+static int mxc_free_ep0_ptr(struct mxc_ep_t *ep)
+{
+ int i;
+ struct ep_queue_item *tqi;
+ for (i = 0; i < EP_TQ_ITEM_SIZE; i++) {
+ tqi = ep->ep_dtd[i];
+ if (tqi->page_vir) {
+ free((void *)(tqi->page_unaligned));
+ tqi->page_vir = 0;
+ tqi->page_unaligned = 0;
+ tqi->page0 = 0;
+ tqi->page1 = 0;
+ tqi->page2 = 0;
+ tqi->page3 = 0;
+ tqi->page4 = 0;
+
+ /*flush dtd's config field to physical memory*/
+ flush_dcache_range((unsigned long)tqi,
+ CACHE_ALIGNED_END(tqi, sizeof(struct ep_queue_item)));
+ }
+ }
+
+ return 0;
+}
+
+static void ep0_setup(void)
+{
+ mxc_ep_qh_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
+ USB_MAX_CTRL_PAYLOAD, 0, 0);
+ mxc_ep_qh_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,
+ USB_MAX_CTRL_PAYLOAD, 0, 0);
+ mxc_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);
+ mxc_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);
+ mxc_init_ep_dtd(0 * 2 + USB_RECV);
+ mxc_init_ep_dtd(0 * 2 + USB_SEND);
+ mxc_malloc_ep0_ptr(mxc_udc.mxc_ep + (USB_RECV));
+ mxc_malloc_ep0_ptr(mxc_udc.mxc_ep + (USB_SEND));
+}
+
+static void ep0_destroy(void)
+{
+ mxc_ep_destroy(0, USB_RECV);
+ mxc_ep_destroy(0, USB_SEND);
+ mxc_free_ep0_ptr(mxc_udc.mxc_ep + (USB_RECV));
+ mxc_free_ep0_ptr(mxc_udc.mxc_ep + (USB_SEND));
+ mxc_destroy_ep_dtd(0 * 2 + USB_RECV);
+ mxc_destroy_ep_dtd(0 * 2 + USB_SEND);
+}
+
+static int mxc_tqi_is_busy(struct ep_queue_item *tqi)
+{
+ /* bit 7 is set by software when send, clear by controller
+ when finish */
+ /*Invalidate cache to gain dtd content from physical memory*/
+ invalidate_dcache_range((unsigned long)tqi,
+ CACHE_ALIGNED_END(tqi, sizeof(struct ep_queue_item)));
+ return tqi->info & (1 << 7);
+}
+
+static int mxc_ep_xfer_is_working(struct mxc_ep_t *ep, u32 in)
+{
+ /* in: means device -> host */
+ u32 bitmask = 1 << (ep->epnum + in * 16);
+ u32 temp, prime, tstat;
+
+ prime = (bitmask & readl(USB_ENDPTPRIME));
+ if (prime)
+ return 1;
+ do {
+ temp = readl(USB_USBCMD);
+ writel(temp|USB_CMD_ATDTW, USB_USBCMD);
+ tstat = readl(USB_ENDPTSTAT) & bitmask;
+ } while (!(readl(USB_USBCMD) & USB_CMD_ATDTW));
+ writel(temp & (~USB_CMD_ATDTW), USB_USBCMD);
+
+ if (tstat)
+ return 1;
+ return 0;
+}
+
+static void mxc_update_qh(struct mxc_ep_t *ep,
+ struct ep_queue_item *tqi, u32 in)
+{
+ /* in: means device -> host */
+ struct ep_queue_head *qh = ep->ep_qh;
+ u32 bitmask = 1 << (ep->epnum + in * 16);
+ DBG("%s, line %d, epnum=%d, in=%d\n", __func__,
+ __LINE__, ep->epnum, in);
+ qh->next_queue_item = virt_to_phys(tqi);
+ qh->info = 0;
+
+ /*flush qh''s config field to physical memory*/
+ flush_dcache_range((unsigned long)qh,
+ CACHE_ALIGNED_END(qh, sizeof(struct ep_queue_head)));
+
+ writel(bitmask, USB_ENDPTPRIME);
+}
+
+static void _dump_buf(u8 *buf, u32 len)
+{
+#ifdef DEBUG
+ char *data = (char *)buf;
+ int i;
+ for (i = 0; i < len; i++) {
+ printf("0x%02x ", data[i]);
+ if (i%16 == 15)
+ printf("\n");
+ }
+ printf("\n");
+#endif
+}
+
+static void mxc_udc_queue_update(u8 epnum,
+ u8 *data, u32 len, u32 tx)
+{
+ struct mxc_ep_t *ep;
+ struct ep_queue_item *tqi, *head, *last;
+ int send = 0;
+ int in;
+
+ head = last = NULL;
+ in = ep_is_in(epnum, tx);
+ ep = mxc_udc.mxc_ep + (epnum * 2 + in);
+ DBG("epnum = %d, in = %d\n", epnum, in);
+ do {
+ tqi = ep->ep_dtd[ep->index];
+ DBG("%s, index = %d, tqi = %p\n", __func__, ep->index, tqi);
+ while (mxc_tqi_is_busy(tqi))
+ ;
+ mxc_tqi_init_page(tqi);
+ DBG("%s, line = %d, len = %d\n", __func__, __LINE__, len);
+ inc_index(ep->index);
+ send = min(len, ep->max_pkt_size);
+ if (data) {
+ memcpy((void *)tqi->page_vir, (void *)data, send);
+ _dump_buf((u8 *)(tqi->page_vir), send);
+
+ flush_dcache_range((unsigned long)(tqi->page_vir),
+ CACHE_ALIGNED_END(tqi->page_vir, send));
+ }
+ if (!head)
+ last = head = tqi;
+ else {
+ last->next_item_ptr = virt_to_phys(tqi);
+ last->next_item_vir = tqi;
+ last = tqi;
+ }
+ if (!tx)
+ tqi->reserved[0] = send;
+ /* we set IOC for every dtd */
+ tqi->info = ((send << 16) | (1 << 15) | (1 << 7));
+ data += send;
+ len -= send;
+
+ flush_dcache_range((unsigned long)tqi,
+ CACHE_ALIGNED_END(tqi, sizeof(struct ep_queue_item)));
+ } while (len);
+
+ last->next_item_ptr = 0x1; /* end */
+ flush_dcache_range((unsigned long)last,
+ CACHE_ALIGNED_END(last, sizeof(struct ep_queue_item)));
+
+ if (ep->tail) {
+ ep->tail->next_item_ptr = virt_to_phys(head);
+ ep->tail->next_item_vir = head;
+ flush_dcache_range((unsigned long)(ep->tail),
+ CACHE_ALIGNED_END(ep->tail, sizeof(struct ep_queue_item)));
+
+ if (mxc_ep_xfer_is_working(ep, in)) {
+ DBG("ep is working\n");
+ goto out;
+ }
+ }
+ mxc_update_qh(ep, head, in);
+out:
+ ep->tail = last;
+}
+
+static void mxc_udc_txqueue_update(u8 ep, u8 *data, u32 len)
+{
+ printf("[SEND DATA] EP= %d, Len = 0x%x\n", ep, len);
+ _dump_buf(data, len);
+ mxc_udc_queue_update(ep, data, len, 1);
+}
+
+void mxc_udc_rxqueue_update(u8 ep, u32 len)
+{
+ mxc_udc_queue_update(ep, NULL, len, 0);
+}
+
+static void mxc_ep0_stall(void)
+{
+ u32 temp;
+ temp = readl(USB_ENDPTCTRL(0));
+ temp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
+ writel(temp, USB_ENDPTCTRL(0));
+}
+
+static void mxc_usb_run(void)
+{
+ unsigned int temp = 0;
+
+ /* Enable DR irq reg */
+ temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
+ | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN
+ | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;
+
+ writel(temp, USB_USBINTR);
+
+ /* Set controller to Run */
+ temp = readl(USB_USBCMD);
+ temp |= USB_CMD_RUN_STOP;
+ writel(temp, USB_USBCMD);
+}
+
+static void mxc_usb_stop(void)
+{
+ unsigned int temp = 0;
+
+ writel(temp, USB_USBINTR);
+
+ /* Set controller to Stop */
+ temp = readl(USB_USBCMD);
+ temp &= ~USB_CMD_RUN_STOP;
+ writel(temp, USB_USBCMD);
+}
+
+static void usb_phy_init(void)
+{
+ u32 temp;
+ /* select 24M clk */
+ temp = readl(USB_PHY1_CTRL);
+ temp &= ~3;
+ temp |= 1;
+ writel(temp, USB_PHY1_CTRL);
+ /* Config PHY interface */
+ temp = readl(USB_PORTSC1);
+ temp &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
+ temp |= PORTSCX_PTW_16BIT;
+ writel(temp, USB_PORTSC1);
+ DBG("Config PHY END\n");
+}
+
+static void usb_set_mode_device(void)
+{
+ u32 temp;
+
+ /* Set controller to stop */
+ temp = readl(USB_USBCMD);
+ temp &= ~USB_CMD_RUN_STOP;
+ writel(temp, USB_USBCMD);
+
+ while (readl(USB_USBCMD) & USB_CMD_RUN_STOP)
+ ;
+ /* Do core reset */
+ temp = readl(USB_USBCMD);
+ temp |= USB_CMD_CTRL_RESET;
+ writel(temp, USB_USBCMD);
+ while (readl(USB_USBCMD) & USB_CMD_CTRL_RESET)
+ ;
+ DBG("DOORE RESET END\n");
+
+#if defined(CONFIG_MX6)
+ reset_usb_phy1();
+#endif
+ DBG("init core to device mode\n");
+ temp = readl(USB_USBMODE);
+ temp &= ~USB_MODE_CTRL_MODE_MASK; /* clear mode bits */
+ temp |= USB_MODE_CTRL_MODE_DEVICE;
+ /* Disable Setup Lockout */
+ temp |= USB_MODE_SETUP_LOCK_OFF;
+ writel(temp, USB_USBMODE);
+
+ temp = readl(USB_OTGSC);
+ temp |= (1<<3);
+ writel(temp, USB_OTGSC);
+ DBG("init core to device mode end\n");
+}
+
+static void usb_init_eps(void)
+{
+ u32 temp;
+ DBG("Flush begin\n");
+ temp = readl(USB_ENDPTNAKEN);
+ temp |= 0x10001; /* clear mode bits */
+ writel(temp, USB_ENDPTNAKEN);
+ writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
+ writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
+ writel(0xffffffff, USB_ENDPTFLUSH);
+ DBG("FLUSH END\n");
+}
+
+static void usb_udc_init(void)
+{
+ DBG("\n************************\n");
+ DBG(" usb init start\n");
+ DBG("\n************************\n");
+
+ usb_phy_init();
+ usb_set_mode_device();
+ mxc_init_usb_qh();
+ usb_init_eps();
+ mxc_init_ep_struct();
+ ep0_setup();
+ usb_inited = 1;
+}
+
+static void usb_udc_destroy(void)
+{
+ usb_set_mode_device();
+
+ usb_inited = 0;
+
+ ep0_destroy();
+ mxc_destroy_ep_struct();
+ mxc_destroy_usb_qh();
+}
+
+void usb_shutdown(void)
+{
+ u32 temp;
+ /* disable pullup */
+ temp = readl(USB_USBCMD);
+ temp &= ~USB_CMD_RUN_STOP;
+ writel(temp, USB_USBCMD);
+ mdelay(2);
+}
+
+static void ch9getstatus(u8 request_type,
+ u16 value, u16 index, u16 length)
+{
+ u16 tmp;
+
+ if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+ tmp = 1 << 0; /* self powerd */
+ tmp |= 0 << 1; /* not remote wakeup able */
+ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
+ tmp = 0;
+ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
+ tmp = 0;
+ }
+ mxc_udc.ep0_dir = USB_DIR_IN;
+ mxc_udc_queue_update(0, (u8 *)&tmp, 2, 0xffffffff);
+}
+
+static void mxc_udc_read_setup_pkt(struct usb_device_request *s)
+{
+ u32 temp;
+ temp = readl(USB_ENDPTSETUPSTAT);
+ writel(temp, USB_ENDPTSETUPSTAT);
+ DBG("setup stat %x\n", temp);
+ do {
+ temp = readl(USB_USBCMD);
+ temp |= USB_CMD_SUTW;
+ writel(temp, USB_USBCMD);
+
+ invalidate_dcache_range((unsigned long)(mxc_udc.mxc_ep[0].ep_qh),
+ CACHE_ALIGNED_END((mxc_udc.mxc_ep[0].ep_qh),
+ sizeof(struct ep_queue_head)));
+
+ memcpy((void *)s,
+ (void *)mxc_udc.mxc_ep[0].ep_qh->setup_data, 8);
+ } while (!(readl(USB_USBCMD) & USB_CMD_SUTW));
+
+ DBG("handle_setup s.type=%x req=%x len=%x\n",
+ s->bmRequestType, s->bRequest, s->wLength);
+ temp = readl(USB_USBCMD);
+ temp &= ~USB_CMD_SUTW;
+ writel(temp, USB_USBCMD);
+
+ DBG("[SETUP] type=%x req=%x val=%x index=%x len=%x\n",
+ s->bmRequestType, s->bRequest,
+ s->wValue, s->wIndex,
+ s->wLength);
+}
+
+static void mxc_udc_recv_setup(void)
+{
+ struct usb_device_request *s = &ep0_urb->device_request;
+
+ mxc_udc_read_setup_pkt(s);
+ if (s->wLength) {
+ /* If has a data phase,
+ * then prime a dtd for status stage which has zero length DATA0.
+ * The direction of status stage should oppsite to direction of data phase.
+ */
+ mxc_udc.ep0_dir = (s->bmRequestType & USB_DIR_IN) ?
+ USB_DIR_OUT : USB_DIR_IN;
+ mxc_udc_queue_update(0, NULL, 0, 0xffffffff);
+ }
+ if (ep0_recv_setup(ep0_urb)) {
+ mxc_ep0_stall();
+ return;
+ }
+ switch (s->bRequest) {
+ case USB_REQ_GET_STATUS:
+ if ((s->bmRequestType & (USB_DIR_IN | USB_TYPE_MASK)) !=
+ (USB_DIR_IN | USB_TYPE_STANDARD))
+ break;
+ ch9getstatus(s->bmRequestType, s->wValue,
+ s->wIndex, s->wLength);
+
+ DBG("[SETUP] REQ_GET_STATUS\n");
+ return;
+ case USB_REQ_SET_ADDRESS:
+ if (s->bmRequestType != (USB_DIR_OUT |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+ break;
+ mxc_udc.setaddr = 1;
+ mxc_udc.ep0_dir = USB_DIR_IN;
+ mxc_udc_queue_update(0, NULL, 0, 0xffffffff);
+ usbd_device_event_irq(udc_device, DEVICE_ADDRESS_ASSIGNED, 0);
+ DBG("[SETUP] REQ_SET_ADDRESS\n");
+ return;
+ case USB_REQ_SET_CONFIGURATION:
+ usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0);
+ DBG("[SETUP] REQ_SET_CONFIGURATION\n");
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ {
+ int rc = -1;
+ if ((s->bmRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) ==
+ (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD))
+ rc = 0;
+ else if ((s->bmRequestType &
+ (USB_RECIP_MASK | USB_TYPE_MASK)) ==
+ (USB_RECIP_DEVICE | USB_TYPE_STANDARD))
+ rc = 0;
+ else
+ break;
+ if (rc == 0) {
+ mxc_udc.ep0_dir = USB_DIR_IN;
+ mxc_udc_queue_update(0, NULL, 0, 0xffffffff);
+ }
+ return;
+ }
+ default:
+ break;
+ }
+ if (s->wLength) {
+ mxc_udc.ep0_dir = (s->bmRequestType & USB_DIR_IN) ?
+ USB_DIR_IN : USB_DIR_OUT;
+ mxc_udc_queue_update(0, ep0_urb->buffer,
+ ep0_urb->actual_length, 0xffffffff);
+ ep0_urb->actual_length = 0;
+ } else {
+ mxc_udc.ep0_dir = USB_DIR_IN;
+ mxc_udc_queue_update(0, NULL, 0, 0xffffffff);
+ }
+}
+
+static int mxc_udc_tqi_empty(struct ep_queue_item *tqi)
+{
+ int ret;
+
+ invalidate_dcache_range((unsigned long)tqi,
+ CACHE_ALIGNED_END(tqi, sizeof(struct ep_queue_item)));
+
+ ret = tqi->info & (1 << 7);
+ return ret;
+}
+
+static struct usb_endpoint_instance *mxc_get_epi(u8 epnum)
+{
+ int i;
+ for (i = 0; i < udc_device->bus->max_endpoints; i++) {
+ if ((udc_device->bus->endpoint_array[i].endpoint_address &
+ USB_ENDPOINT_NUMBER_MASK) == epnum)
+ return &udc_device->bus->endpoint_array[i];
+ }
+ return NULL;
+}
+
+static u32 _mxc_ep_recv_data(u8 epnum, struct ep_queue_item *tqi)
+{
+ struct usb_endpoint_instance *epi = mxc_get_epi(epnum);
+ struct urb *urb;
+ u32 len = 0;
+
+ if (!epi)
+ return 0;
+
+ invalidate_dcache_range((unsigned long)tqi,
+ CACHE_ALIGNED_END(tqi, sizeof(struct ep_queue_item)));
+
+ urb = epi->rcv_urb;
+ if (urb) {
+ u8 *data = urb->buffer + urb->actual_length;
+ int remain_len = (tqi->info >> 16) & (0xefff);
+ len = tqi->reserved[0] - remain_len;
+ DBG("recv len %d-%d-%d\n", len, tqi->reserved[0], remain_len);
+
+
+ invalidate_dcache_range((unsigned long)tqi->page_vir,
+ CACHE_ALIGNED_END(tqi->page_vir, len));
+ memcpy(data, (void *)tqi->page_vir, len);
+
+ _dump_buf(data, len);
+ }
+ return len;
+}
+
+static void mxc_udc_ep_recv(u8 epnum)
+{
+ struct mxc_ep_t *ep = mxc_udc.mxc_ep + (epnum * 2 + USB_RECV);
+ struct ep_queue_item *tqi;
+ while (1) {
+ u32 nbytes;
+ tqi = ep->ep_dtd[ep->done];
+ if (mxc_udc_tqi_empty(tqi))
+ break;
+ nbytes = _mxc_ep_recv_data(epnum, tqi);
+ usbd_rcv_complete(ep->epi, nbytes, 0);
+ inc_index(ep->done);
+ if (ep->done == ep->index)
+ break;
+ }
+}
+
+static void mxc_udc_handle_xfer_complete(void)
+{
+ int i;
+ u32 bitpos = readl(USB_ENDPTCOMPLETE);
+
+ writel(bitpos, USB_ENDPTCOMPLETE);
+
+ for (i = 0; i < mxc_udc.max_ep; i++) {
+ int epnum = i >> 1;
+ int dir = i % 2;
+ u32 bitmask = 1 << (epnum + 16 * dir);
+ if (!(bitmask & bitpos))
+ continue;
+ DBG("ep %d, dir %d, complete\n", epnum, dir);
+ if (!epnum) {
+ if (mxc_udc.setaddr) {
+ writel(udc_device->address << 25,
+ USB_DEVICEADDR);
+ mxc_udc.setaddr = 0;
+ }
+ continue;
+ }
+ DBG("############### dir = %d ***************\n", dir);
+ if (dir == USB_SEND)
+ continue;
+ mxc_udc_ep_recv(epnum);
+ }
+}
+
+static void usb_dev_hand_usbint(void)
+{
+ if (readl(USB_ENDPTSETUPSTAT)) {
+ DBG("recv one setup packet\n");
+ mxc_udc_recv_setup();
+ }
+ if (readl(USB_ENDPTCOMPLETE)) {
+ DBG("Dtd complete irq\n");
+ mxc_udc_handle_xfer_complete();
+ }
+}
+
+static void usb_dev_hand_reset(void)
+{
+ 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("reset-PORTSC=%x\n", readl(USB_PORTSC1));
+ usbd_device_event_irq(udc_device, DEVICE_RESET, 0);
+}
+
+void usb_dev_hand_pci(void)
+{
+ u32 speed;
+ while (readl(USB_PORTSC1) & PORTSCX_PORT_RESET)
+ ;
+ speed = readl(USB_PORTSC1) & PORTSCX_PORT_SPEED_MASK;
+ switch (speed) {
+ case PORTSCX_PORT_SPEED_HIGH:
+ usb_highspeed = 2;
+ break;
+ case PORTSCX_PORT_SPEED_FULL:
+ usb_highspeed = 1;
+ break;
+ case PORTSCX_PORT_SPEED_LOW:
+ usb_highspeed = 0;
+ break;
+ default:
+ break;
+ }
+ DBG("portspeed=%d, speed = %x, PORTSC = %x\n",
+ usb_highspeed, speed, readl(USB_PORTSC1));
+}
+
+void usb_dev_hand_suspend(void)
+{
+}
+
+void mxc_irq_poll(void)
+{
+ unsigned irq_src = readl(USB_USBSTS) & readl(USB_USBINTR);
+ writel(irq_src, USB_USBSTS);
+
+ if (irq_src == 0)
+ return;
+
+ if (irq_src & USB_STS_INT)
+ usb_dev_hand_usbint();
+
+ if (irq_src & USB_STS_RESET) {
+ printf("USB_RESET\n");
+ usb_dev_hand_reset();
+ }
+ if (irq_src & USB_STS_PORT_CHANGE) {
+ printf("USB_PORT_CHANGE 0x%x\n", irq_src);
+ usb_dev_hand_pci();
+ }
+ if (irq_src & USB_STS_SUSPEND)
+ printf("USB_SUSPEND\n");
+ if (irq_src & USB_STS_ERR)
+ printf("USB_ERR\n");
+}
+
+void mxc_udc_wait_cable_insert(void)
+{
+ u32 temp;
+ int cable_connect = 1;
+
+ do {
+ udelay(50);
+
+ temp = readl(USB_OTGSC);
+ if (temp & (OTGSC_B_SESSION_VALID)) {
+ printf("USB Mini b cable Connected!\n");
+ break;
+ } else if (cable_connect == 1) {
+ printf("wait usb cable into the connector!\n");
+ cable_connect = 0;
+ }
+ } while (1);
+}
+
+void udc_disable_over_current(void)
+{
+ u32 temp;
+ temp = readl(USB_OTG_CTRL);
+ temp |= (UCTRL_OVER_CUR_POL);
+ writel(temp, USB_OTG_CTRL);
+}
+
+/*
+ * mxc_udc_init function
+ */
+int mxc_udc_init(void)
+{
+ udc_pins_setting();
+ set_usb_phy1_clk();
+ enable_usboh3_clk(1);
+#if defined(CONFIG_MX6)
+ udc_disable_over_current();
+#endif
+ enable_usb_phy1_clk(1);
+ usb_udc_init();
+
+ return 0;
+}
+
+/*
+ * mxc_udc_init function
+ */
+int mxc_udc_destroy(void)
+{
+ usb_udc_destroy();
+ enable_usboh3_clk(0);
+ enable_usb_phy1_clk(0);
+
+ return 0;
+}
+
+void mxc_udc_poll(void)
+{
+ mxc_irq_poll();
+}
+
+/*
+ * Functions for gadget APIs
+ */
+int udc_init(void)
+{
+ mxc_udc_init();
+ return 0;
+}
+
+int udc_destroy(void)
+{
+ udc_disable();
+ mxc_udc_destroy();
+ return 0;
+}
+
+void udc_setup_ep(struct usb_device_instance *device, u32 index,
+ struct usb_endpoint_instance *epi)
+{
+ u8 dir, epnum, zlt, mult;
+ u8 ep_type;
+ u32 max_pkt_size;
+ int ep_addr;
+ struct mxc_ep_t *ep;
+
+ if (epi) {
+ zlt = 1;
+ mult = 0;
+ ep_addr = epi->endpoint_address;
+ epnum = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+ DBG("setup ep %d\n", epnum);
+ if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
+ dir = USB_SEND;
+ ep_type = epi->tx_attributes;
+ max_pkt_size = epi->tx_packetSize;
+ } else {
+ dir = USB_RECV;
+ ep_type = epi->rcv_attributes;
+ max_pkt_size = epi->rcv_packetSize;
+ }
+ if (ep_type == USB_ENDPOINT_XFER_ISOC) {
+ mult = (u32)(1 + ((max_pkt_size >> 11) & 0x03));
+ max_pkt_size = max_pkt_size & 0x7ff;
+ DBG("mult = %d\n", mult);
+ }
+ ep = mxc_udc.mxc_ep + (epnum * 2 + dir);
+ ep->epi = epi;
+ if (epnum) {
+ struct ep_queue_item *tqi;
+ int i;
+
+ mxc_ep_qh_setup(epnum, dir, ep_type,
+ max_pkt_size, zlt, mult);
+ mxc_ep_setup(epnum, dir, ep_type);
+ mxc_init_ep_dtd(epnum * 2 + dir);
+
+ /* malloc endpoint's dtd's data buffer*/
+ ep->max_pkt_size = max_pkt_size;
+ for (i = 0; i < EP_TQ_ITEM_SIZE; i++) {
+ tqi = ep->ep_dtd[i];
+ tqi->page_vir = (u32)malloc_aligned_buffer(
+ &tqi->page_unaligned, max_pkt_size,
+ USB_MEM_ALIGN_BYTE);
+ if ((void *)tqi->page_vir == NULL) {
+ printf("malloc dtd bufer failure\n");
+ return;
+ }
+ mxc_tqi_init_page(tqi);
+
+ flush_dcache_range((unsigned long)tqi,
+ CACHE_ALIGNED_END(tqi, sizeof(struct ep_queue_item)));
+ }
+ }
+ }
+}
+
+void udc_destroy_ep(struct usb_device_instance *device,
+ struct usb_endpoint_instance *epi)
+{
+ struct mxc_ep_t *ep;
+ int ep_addr;
+ u8 dir, epnum;
+
+ if (epi) {
+ ep_addr = epi->endpoint_address;
+ epnum = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+
+ if ((ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+ dir = USB_SEND;
+ else
+ dir = USB_RECV;
+
+ ep = mxc_udc.mxc_ep + (epnum * 2 + dir);
+ ep->epi = 0;
+
+ if (epnum) {
+ struct ep_queue_item *tqi;
+ int i;
+
+ for (i = 0; i < EP_TQ_ITEM_SIZE; i++) {
+ tqi = ep->ep_dtd[i];
+ if (tqi->page_vir) {
+ free((void *)(tqi->page_unaligned));
+ tqi->page_unaligned = 0;
+ tqi->page_vir = 0;
+ tqi->page0 = 0;
+ tqi->page1 = 0;
+ tqi->page2 = 0;
+ tqi->page3 = 0;
+ tqi->page4 = 0;
+ }
+ }
+
+ mxc_destroy_ep_dtd(epnum * 2 + dir);
+ mxc_ep_destroy(epnum, dir);
+ }
+ }
+}
+
+int udc_endpoint_write(struct usb_endpoint_instance *epi)
+{
+ struct urb *urb = epi->tx_urb;
+ int ep_num = epi->endpoint_address & USB_ENDPOINT_NUMBER_MASK;
+ u8 *data = (u8 *)urb->buffer + epi->sent;
+ int n = urb->actual_length - epi->sent;
+ mxc_udc_txqueue_update(ep_num, data, n);
+ epi->last = n;
+
+ /* usbd_tx_complete will take care of updating 'sent' */
+ usbd_tx_complete(epi);
+ return 0;
+}
+
+void udc_enable(struct usb_device_instance *device)
+{
+ udc_device = device;
+ ep0_urb = usbd_alloc_urb(udc_device, udc_device->bus->endpoint_array);
+}
+
+void udc_disable(void)
+{
+ usbd_dealloc_urb(ep0_urb);
+ udc_device = NULL;
+}
+
+void udc_startup_events(struct usb_device_instance *device)
+{
+ usbd_device_event_irq(device, DEVICE_INIT, 0);
+ usbd_device_event_irq(device, DEVICE_CREATE, 0);
+ udc_enable(device);
+}
+
+void udc_irq(void)
+{
+ mxc_irq_poll();
+}
+
+void udc_connect(void)
+{
+ mxc_usb_run();
+ mxc_udc_wait_cable_insert();
+}
+
+void udc_disconnect(void)
+{
+ /* imx6 will hang if access usb register without init oh3
+ * clock, so not access it if not init. */
+ if (usb_inited)
+ mxc_usb_stop();
+}
+
+void udc_set_nak(int epid)
+{
+}
+
+void udc_unset_nak(int epid)
+{
+}