diff options
author | Wolfgang Denk <wd@denx.de> | 2012-05-20 22:51:41 +0200 |
---|---|---|
committer | Wolfgang Denk <wd@denx.de> | 2012-05-20 22:51:41 +0200 |
commit | 2ab5be7af009b4a40efe2fa5471497c97e70ed28 (patch) | |
tree | 7c489b7db4927ef433d83d10a26fe48e2e9d1a4a /drivers | |
parent | 8fa3d2b8161bb73b759c9db5c811c885ca5ec60c (diff) | |
parent | 5f0ffea4559abe3fc83a6023717658a50b22e66c (diff) | |
download | u-boot-2ab5be7af009b4a40efe2fa5471497c97e70ed28.tar.gz u-boot-2ab5be7af009b4a40efe2fa5471497c97e70ed28.tar.xz u-boot-2ab5be7af009b4a40efe2fa5471497c97e70ed28.zip |
Merge branch 'master' of git://git.denx.de/u-boot-usb
* 'master' of git://git.denx.de/u-boot-usb:
USB: S5P: Add ehci support
usb:udc:samsung Add functions for storing private gadget data in UDC driver
usb:gadget:composite: Support for composite at gadget.h
usb:gadget:composite USB composite gadget support
usb:udc:samsung:cleanup Replace DEBUG_* macros with debug_cond() calls
usb:udc: Remove duplicated USB definitions from include/linux/usb/ch9.h file
USB: Document the QH and qTD antics in EHCI-HCD
USB: Drop cache flush bloat in EHCI-HCD
USB: Drop ehci_alloc/ehci_free in ehci-hcd
USB: Align buffers at cacheline
usb: use noinline define
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/gadget/composite.c | 1082 | ||||
-rw-r--r-- | drivers/usb/gadget/config.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/epautoconf.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/ether.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/s3c_udc_otg.c | 53 | ||||
-rw-r--r-- | drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 287 | ||||
-rw-r--r-- | drivers/usb/gadget/usbstring.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 288 | ||||
-rw-r--r-- | drivers/usb/host/ehci-s5p.c | 110 |
10 files changed, 1497 insertions, 328 deletions
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c new file mode 100644 index 0000000000..ebb5131a9c --- /dev/null +++ b/drivers/usb/gadget/composite.c @@ -0,0 +1,1082 @@ +/* + * composite.c - infrastructure for Composite USB Gadgets + * + * Copyright (C) 2006-2008 David Brownell + * U-boot porting: Lukasz Majewski <l.majewski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#undef DEBUG + +#include <linux/bitops.h> +#include <linux/usb/composite.h> + +#define USB_BUFSIZ 4096 + +static struct usb_composite_driver *composite; + +/** + * usb_add_function() - add a function to a configuration + * @config: the configuration + * @function: the function being added + * Context: single threaded during gadget setup + * + * After initialization, each configuration must have one or more + * functions added to it. Adding a function involves calling its @bind() + * method to allocate resources such as interface and string identifiers + * and endpoints. + * + * This function returns the value of the function's bind(), which is + * zero for success else a negative errno value. + */ +int usb_add_function(struct usb_configuration *config, + struct usb_function *function) +{ + int value = -EINVAL; + + debug("adding '%s'/%p to config '%s'/%p\n", + function->name, function, + config->label, config); + + if (!function->set_alt || !function->disable) + goto done; + + function->config = config; + list_add_tail(&function->list, &config->functions); + + if (function->bind) { + value = function->bind(config, function); + if (value < 0) { + list_del(&function->list); + function->config = NULL; + } + } else + value = 0; + + if (!config->fullspeed && function->descriptors) + config->fullspeed = 1; + if (!config->highspeed && function->hs_descriptors) + config->highspeed = 1; + +done: + if (value) + debug("adding '%s'/%p --> %d\n", + function->name, function, value); + return value; +} + +/** + * usb_function_deactivate - prevent function and gadget enumeration + * @function: the function that isn't yet ready to respond + * + * Blocks response of the gadget driver to host enumeration by + * preventing the data line pullup from being activated. This is + * normally called during @bind() processing to change from the + * initial "ready to respond" state, or when a required resource + * becomes available. + * + * For example, drivers that serve as a passthrough to a userspace + * daemon can block enumeration unless that daemon (such as an OBEX, + * MTP, or print server) is ready to handle host requests. + * + * Not all systems support software control of their USB peripheral + * data pullups. + * + * Returns zero on success, else negative errno. + */ +int usb_function_deactivate(struct usb_function *function) +{ + struct usb_composite_dev *cdev = function->config->cdev; + int status = 0; + + if (cdev->deactivations == 0) + status = usb_gadget_disconnect(cdev->gadget); + if (status == 0) + cdev->deactivations++; + + return status; +} + +/** + * usb_function_activate - allow function and gadget enumeration + * @function: function on which usb_function_activate() was called + * + * Reverses effect of usb_function_deactivate(). If no more functions + * are delaying their activation, the gadget driver will respond to + * host enumeration procedures. + * + * Returns zero on success, else negative errno. + */ +int usb_function_activate(struct usb_function *function) +{ + struct usb_composite_dev *cdev = function->config->cdev; + int status = 0; + + if (cdev->deactivations == 0) + status = -EINVAL; + else { + cdev->deactivations--; + if (cdev->deactivations == 0) + status = usb_gadget_connect(cdev->gadget); + } + + return status; +} + +/** + * usb_interface_id() - allocate an unused interface ID + * @config: configuration associated with the interface + * @function: function handling the interface + * Context: single threaded during gadget setup + * + * usb_interface_id() is called from usb_function.bind() callbacks to + * allocate new interface IDs. The function driver will then store that + * ID in interface, association, CDC union, and other descriptors. It + * will also handle any control requests targetted at that interface, + * particularly changing its altsetting via set_alt(). There may + * also be class-specific or vendor-specific requests to handle. + * + * All interface identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. Note that since interface + * identifers are configuration-specific, functions used in more than + * one configuration (or more than once in a given configuration) need + * multiple versions of the relevant descriptors. + * + * Returns the interface ID which was allocated; or -ENODEV if no + * more interface IDs can be allocated. + */ +int usb_interface_id(struct usb_configuration *config, + struct usb_function *function) +{ + unsigned char id = config->next_interface_id; + + if (id < MAX_CONFIG_INTERFACES) { + config->interface[id] = function; + config->next_interface_id = id + 1; + return id; + } + return -ENODEV; +} + +static int config_buf(struct usb_configuration *config, + enum usb_device_speed speed, void *buf, u8 type) +{ + int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + void *next = buf + USB_DT_CONFIG_SIZE; + struct usb_descriptor_header **descriptors; + struct usb_config_descriptor *c = buf; + int status; + struct usb_function *f; + + /* write the config descriptor */ + c = buf; + c->bLength = USB_DT_CONFIG_SIZE; + c->bDescriptorType = type; + + c->bNumInterfaces = config->next_interface_id; + c->bConfigurationValue = config->bConfigurationValue; + c->iConfiguration = config->iConfiguration; + c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; + c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2); + + /* There may be e.g. OTG descriptors */ + if (config->descriptors) { + status = usb_descriptor_fillbuf(next, len, + config->descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + /* add each function's descriptors */ + list_for_each_entry(f, &config->functions, list) { + if (speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + if (!descriptors) + continue; + status = usb_descriptor_fillbuf(next, len, + (const struct usb_descriptor_header **) descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + len = next - buf; + c->wTotalLength = cpu_to_le16(len); + return len; +} + +static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) +{ + enum usb_device_speed speed = USB_SPEED_UNKNOWN; + struct usb_gadget *gadget = cdev->gadget; + u8 type = w_value >> 8; + int hs = 0; + struct usb_configuration *c; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + } + + w_value &= 0xff; + list_for_each_entry(c, &cdev->configs, list) { + if (speed == USB_SPEED_HIGH) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + if (w_value == 0) + return config_buf(c, speed, cdev->req->buf, type); + w_value--; + } + return -EINVAL; +} + +static int count_configs(struct usb_composite_dev *cdev, unsigned type) +{ + struct usb_gadget *gadget = cdev->gadget; + unsigned count = 0; + int hs = 0; + struct usb_configuration *c; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_DEVICE_QUALIFIER) + hs = !hs; + } + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (hs) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + count++; + } + return count; +} + +static void device_qual(struct usb_composite_dev *cdev) +{ + struct usb_qualifier_descriptor *qual = cdev->req->buf; + + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + /* POLICY: same bcdUSB and device type info at both speeds */ + qual->bcdUSB = cdev->desc.bcdUSB; + qual->bDeviceClass = cdev->desc.bDeviceClass; + qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; + qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; + /* ASSUME same EP0 fifo size at both speeds */ + qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); + qual->bRESERVED = 0; +} + +static void reset_config(struct usb_composite_dev *cdev) +{ + struct usb_function *f; + + debug("%s:\n", __func__); + + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->disable) + f->disable(f); + + bitmap_zero(f->endpoints, 32); + } + cdev->config = NULL; +} + +static int set_config(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl, unsigned number) +{ + struct usb_gadget *gadget = cdev->gadget; + unsigned power = gadget_is_otg(gadget) ? 8 : 100; + struct usb_descriptor_header **descriptors; + int result = -EINVAL; + struct usb_endpoint_descriptor *ep; + struct usb_configuration *c = NULL; + int addr; + int tmp; + struct usb_function *f; + + if (cdev->config) + reset_config(cdev); + + if (number) { + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == number) { + result = 0; + break; + } + } + if (result < 0) + goto done; + } else + result = 0; + + debug("%s: %s speed config #%d: %s\n", __func__, + ({ char *speed; + switch (gadget->speed) { + case USB_SPEED_LOW: + speed = "low"; + break; + case USB_SPEED_FULL: + speed = "full"; + break; + case USB_SPEED_HIGH: + speed = "high"; + break; + default: + speed = "?"; + break; + }; + speed; + }), number, c ? c->label : "unconfigured"); + + if (!c) + goto done; + + cdev->config = c; + + /* Initialize all interfaces by setting them to altsetting zero. */ + for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { + f = c->interface[tmp]; + if (!f) + break; + + /* + * Record which endpoints are used by the function. This is used + * to dispatch control requests targeted at that endpoint to the + * function's setup callback instead of the current + * configuration's setup callback. + */ + if (gadget->speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + + for (; *descriptors; ++descriptors) { + if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT) + continue; + + ep = (struct usb_endpoint_descriptor *)*descriptors; + addr = ((ep->bEndpointAddress & 0x80) >> 3) + | (ep->bEndpointAddress & 0x0f); + __set_bit(addr, f->endpoints); + } + + result = f->set_alt(f, tmp, 0); + if (result < 0) { + debug("interface %d (%s/%p) alt 0 --> %d\n", + tmp, f->name, f, result); + + reset_config(cdev); + goto done; + } + } + + /* when we return, be sure our power usage is valid */ + power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; +done: + usb_gadget_vbus_draw(gadget, power); + return result; +} + +/** + * usb_add_config() - add a configuration to a device. + * @cdev: wraps the USB gadget + * @config: the configuration, with bConfigurationValue assigned + * Context: single threaded during gadget setup + * + * One of the main tasks of a composite driver's bind() routine is to + * add each of the configurations it supports, using this routine. + * + * This function returns the value of the configuration's bind(), which + * is zero for success else a negative errno value. Binding configurations + * assigns global resources including string IDs, and per-configuration + * resources such as interface IDs and endpoints. + */ +int usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + int status = -EINVAL; + struct usb_configuration *c; + struct usb_function *f; + unsigned int i; + + debug("%s: adding config #%u '%s'/%p\n", __func__, + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue || !config->bind) + goto done; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) { + status = -EBUSY; + goto done; + } + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + + status = config->bind(config); + if (status < 0) { + list_del(&config->list); + config->cdev = NULL; + } else { + debug("cfg %d/%p speeds:%s%s\n", + config->bConfigurationValue, config, + config->highspeed ? " high" : "", + config->fullspeed + ? (gadget_is_dualspeed(cdev->gadget) + ? " full" + : " full/low") + : ""); + + for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { + f = config->interface[i]; + if (!f) + continue; + debug("%s: interface %d = %s/%p\n", + __func__, i, f->name, f); + } + } + + usb_ep_autoconfig_reset(cdev->gadget); + +done: + if (status) + debug("added config '%s'/%u --> %d\n", config->label, + config->bConfigurationValue, status); + return status; +} + +/* + * We support strings in multiple languages ... string descriptor zero + * says which languages are supported. The typical case will be that + * only one language (probably English) is used, with I18N handled on + * the host side. + */ + +static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +{ + const struct usb_gadget_strings *s; + u16 language; + __le16 *tmp; + + while (*sp) { + s = *sp; + language = cpu_to_le16(s->language); + for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + if (*tmp == language) + goto repeat; + } + *tmp++ = language; +repeat: + sp++; + } +} + +static int lookup_string( + struct usb_gadget_strings **sp, + void *buf, + u16 language, + int id +) +{ + int value; + struct usb_gadget_strings *s; + + while (*sp) { + s = *sp++; + if (s->language != language) + continue; + value = usb_gadget_get_string(s, id, buf); + if (value > 0) + return value; + } + return -EINVAL; +} + +static int get_string(struct usb_composite_dev *cdev, + void *buf, u16 language, int id) +{ + struct usb_string_descriptor *s = buf; + struct usb_gadget_strings **sp; + int len; + struct usb_configuration *c; + struct usb_function *f; + + /* + * Yes, not only is USB's I18N support probably more than most + * folk will ever care about ... also, it's all supported here. + * (Except for UTF8 support for Unicode's "Astral Planes".) + */ + + /* 0 == report all available language codes */ + if (id == 0) { + memset(s, 0, 256); + s->bDescriptorType = USB_DT_STRING; + + sp = composite->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(c, &cdev->configs, list) { + sp = c->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(f, &c->functions, list) { + sp = f->strings; + if (sp) + collect_langs(sp, s->wData); + } + } + + for (len = 0; len <= 126 && s->wData[len]; len++) + continue; + if (!len) + return -EINVAL; + + s->bLength = 2 * (len + 1); + return s->bLength; + } + + /* + * Otherwise, look up and return a specified string. String IDs + * are device-scoped, so we look up each string table we're told + * about. These lookups are infrequent; simpler-is-better here. + */ + if (composite->strings) { + len = lookup_string(composite->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(c, &cdev->configs, list) { + if (c->strings) { + len = lookup_string(c->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(f, &c->functions, list) { + if (!f->strings) + continue; + len = lookup_string(f->strings, buf, language, id); + if (len > 0) + return len; + } + } + return -EINVAL; +} + +/** + * usb_string_id() - allocate an unused string ID + * @cdev: the device whose string descriptor IDs are being allocated + * Context: single threaded during gadget setup + * + * @usb_string_id() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this, + * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure + * that for example different functions don't wrongly assign different + * meanings to the same identifier. + */ +int usb_string_id(struct usb_composite_dev *cdev) +{ + if (cdev->next_string_id < 254) { + /* + * string id 0 is reserved by USB spec for list of + * supported languages + * 255 reserved as well? -- mina86 + */ + cdev->next_string_id++; + return cdev->next_string_id; + } + return -ENODEV; +} + +/** + * usb_string_ids() - allocate unused string IDs in batch + * @cdev: the device whose string descriptor IDs are being allocated + * @str: an array of usb_string objects to assign numbers to + * Context: single threaded during gadget setup + * + * @usb_string_ids() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then copy IDs from the string table to the appropriate descriptors + * and string table for other languages. + * + * All string identifier should be allocated using this, + * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for + * example different functions don't wrongly assign different meanings + * to the same identifier. + */ +int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str) +{ + u8 next = cdev->next_string_id; + + for (; str->s; ++str) { + if (next >= 254) + return -ENODEV; + str->id = ++next; + } + + cdev->next_string_id = next; + + return 0; +} + +/** + * usb_string_ids_n() - allocate unused string IDs in batch + * @c: the device whose string descriptor IDs are being allocated + * @n: number of string IDs to allocate + * Context: single threaded during gadget setup + * + * Returns the first requested ID. This ID and next @n-1 IDs are now + * valid IDs. At least provided that @n is non-zero because if it + * is, returns last requested ID which is now very useful information. + * + * @usb_string_ids_n() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this, + * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for + * example different functions don't wrongly assign different meanings + * to the same identifier. + */ +int usb_string_ids_n(struct usb_composite_dev *c, unsigned n) +{ + u8 next = c->next_string_id; + + if (n > 254 || next + n > 254) + return -ENODEV; + + c->next_string_id += n; + return next + 1; +} + +static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + debug("%s: setup complete --> %d, %d/%d\n", __func__, + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver(like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config and function specific setup. + */ +static int +composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + u16 w_length = le16_to_cpu(ctrl->wLength); + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + struct usb_composite_dev *cdev = get_gadget_data(gadget); + u8 intf = w_index & 0xFF; + int value = -EOPNOTSUPP; + struct usb_request *req = cdev->req; + struct usb_function *f = NULL; + int standard; + u8 endp; + struct usb_configuration *c; + + /* + * partial re-init of the response message; the function or the + * gadget might need to intercept e.g. a control-OUT completion + * when we delegate to it. + */ + req->zero = 0; + req->complete = composite_setup_complete; + req->length = USB_BUFSIZ; + gadget->ep0->driver_data = cdev; + standard = (ctrl->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + if (!standard) + goto unknown; + + switch (ctrl->bRequest) { + + /* we handle all standard USB descriptors */ + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + cdev->desc.bNumConfigurations = + count_configs(cdev, USB_DT_DEVICE); + value = min(w_length, (u16) sizeof cdev->desc); + memcpy(req->buf, &cdev->desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + device_qual(cdev); + value = min(w_length, + sizeof(struct usb_qualifier_descriptor)); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + + case USB_DT_CONFIG: + value = config_desc(cdev, w_value); + if (value >= 0) + value = min(w_length, (u16) value); + break; + case USB_DT_STRING: + value = get_string(cdev, req->buf, + w_index, w_value & 0xff); + if (value >= 0) + value = min(w_length, (u16) value); + break; + default: + goto unknown; + } + break; + + /* any number of configs can work */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + goto unknown; + if (gadget_is_otg(gadget)) { + if (gadget->a_hnp_support) + debug("HNP available\n"); + else if (gadget->a_alt_hnp_support) + debug("HNP on another port\n"); + else + debug("HNP inactive\n"); + } + + value = set_config(cdev, ctrl, w_value); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + if (cdev->config) + *(u8 *)req->buf = cdev->config->bConfigurationValue; + else + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + /* + * function drivers must handle get/set altsetting; if there's + * no get() method, we know only altsetting zero works. + */ + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[intf]; + if (!f) + break; + if (w_value && !f->set_alt) + break; + value = f->set_alt(f, w_index, w_value); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[intf]; + if (!f) + break; + /* lots of interfaces only need altsetting zero... */ + value = f->get_alt ? f->get_alt(f, w_index) : 0; + if (value < 0) + break; + *((u8 *)req->buf) = value; + value = min(w_length, (u16) 1); + break; + default: +unknown: + debug("non-core control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* + * functions always handle their interfaces and endpoints... + * punt other recipients (other, WUSB, ...) to the current + * configuration code. + */ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + f = cdev->config->interface[intf]; + break; + + case USB_RECIP_ENDPOINT: + endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); + list_for_each_entry(f, &cdev->config->functions, list) { + if (test_bit(endp, f->endpoints)) + break; + } + if (&f->list == &cdev->config->functions) + f = NULL; + break; + } + + if (f && f->setup) + value = f->setup(f, ctrl); + else { + c = cdev->config; + if (c && c->setup) + value = c->setup(c, ctrl); + } + + goto done; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_KERNEL); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + +done: + /* device either stalls (value < 0) or reports success */ + return value; +} + +static void composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + if (cdev->config) + reset_config(cdev); + if (composite->disconnect) + composite->disconnect(cdev); +} + +static void composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_configuration *c; + struct usb_function *f; + + /* + * composite_disconnect() must already have been called + * by the underlying peripheral controller driver! + * so there's no i/o concurrency that could affect the + * state protected by cdev->lock. + */ + BUG_ON(cdev->config); + + while (!list_empty(&cdev->configs)) { + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); + while (!list_empty(&c->functions)) { + f = list_first_entry(&c->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + debug("unbind function '%s'/%p\n", + f->name, f); + f->unbind(c, f); + } + } + list_del(&c->list); + if (c->unbind) { + debug("unbind config '%s'/%p\n", c->label, c); + c->unbind(c); + } + } + if (composite->unbind) + composite->unbind(cdev); + + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(gadget->ep0, cdev->req); + } + kfree(cdev); + set_gadget_data(gadget, NULL); + + composite = NULL; +} + +static int composite_bind(struct usb_gadget *gadget) +{ + int status = -ENOMEM; + struct usb_composite_dev *cdev; + + cdev = calloc(sizeof *cdev, 1); + if (!cdev) + return status; + + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + + /* preallocate control response and buffer */ + cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!cdev->req) + goto fail; + cdev->req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, USB_BUFSIZ); + if (!cdev->req->buf) + goto fail; + cdev->req->complete = composite_setup_complete; + gadget->ep0->driver_data = cdev; + + cdev->bufsiz = USB_BUFSIZ; + cdev->driver = composite; + + usb_gadget_set_selfpowered(gadget); + usb_ep_autoconfig_reset(cdev->gadget); + + status = composite->bind(cdev); + if (status < 0) + goto fail; + + cdev->desc = *composite->dev; + cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + debug("%s: ready\n", composite->name); + return 0; + +fail: + composite_unbind(gadget); + return status; +} + +static void +composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + debug("%s: suspend\n", __func__); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->suspend) + f->suspend(f); + } + } + if (composite->suspend) + composite->suspend(cdev); + + cdev->suspended = 1; +} + +static void +composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + debug("%s: resume\n", __func__); + if (composite->resume) + composite->resume(cdev); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->resume) + f->resume(f); + } + } + + cdev->suspended = 0; +} + +static struct usb_gadget_driver composite_driver = { + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, + .unbind = composite_unbind, + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .suspend = composite_suspend, + .resume = composite_resume, +}; + +/** + * usb_composite_register() - register a composite driver + * @driver: the driver to register + * Context: single threaded during gadget setup + * + * This function is used to register drivers using the composite driver + * framework. The return value is zero, or a negative errno value. + * Those values normally come from the driver's @bind method, which does + * all the work of setting up the driver to match the hardware. + * + * On successful return, the gadget is ready to respond to requests from + * the host, unless one of its components invokes usb_gadget_disconnect() + * while it was binding. That would usually be done in order to wait for + * some userspace participation. + */ +int usb_composite_register(struct usb_composite_driver *driver) +{ + if (!driver || !driver->dev || !driver->bind || composite) + return -EINVAL; + + if (!driver->name) + driver->name = "composite"; + composite = driver; + + return usb_gadget_register_driver(&composite_driver); +} + +/** + * usb_composite_unregister() - unregister a composite driver + * @driver: the driver to unregister + * + * This function is used to unregister drivers using the composite + * driver framework. + */ +void usb_composite_unregister(struct usb_composite_driver *driver) +{ + if (composite != driver) + return; + usb_gadget_unregister_driver(&composite_driver); +} diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index f9163a80ed..f88d0c190c 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -27,6 +27,7 @@ #include <linux/string.h> #include <linux/usb/ch9.h> +#include <usbdescriptors.h> #include <linux/usb/gadget.h> diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 5b8776e0b7..b656c8b9f4 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -23,6 +23,7 @@ #include <common.h> #include <linux/usb/ch9.h> +#include <usbdescriptors.h> #include <asm/errno.h> #include <linux/usb/gadget.h> #include <asm/unaligned.h> diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 9fb0e80ad1..f693fea1e9 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -24,6 +24,7 @@ #include <asm/errno.h> #include <linux/netdevice.h> #include <linux/usb/ch9.h> +#include <usbdescriptors.h> #include <linux/usb/cdc.h> #include <linux/usb/gadget.h> #include <net.h> diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c index 9d11aea8e4..3fdfdf7af0 100644 --- a/drivers/usb/gadget/s3c_udc_otg.c +++ b/drivers/usb/gadget/s3c_udc_otg.c @@ -30,13 +30,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ - +#undef DEBUG #include <common.h> #include <asm/errno.h> #include <linux/list.h> #include <malloc.h> #include <linux/usb/ch9.h> +#include <usbdescriptors.h> #include <linux/usb/gadget.h> #include <asm/byteorder.h> @@ -53,19 +54,11 @@ #define OTG_DMA_MODE 1 -#undef DEBUG_S3C_UDC_SETUP -#undef DEBUG_S3C_UDC_EP0 -#undef DEBUG_S3C_UDC_ISR -#undef DEBUG_S3C_UDC_OUT_EP -#undef DEBUG_S3C_UDC_IN_EP -#undef DEBUG_S3C_UDC - -/* #define DEBUG_S3C_UDC_SETUP */ -/* #define DEBUG_S3C_UDC_EP0 */ -/* #define DEBUG_S3C_UDC_ISR */ -/* #define DEBUG_S3C_UDC_OUT_EP */ -/* #define DEBUG_S3C_UDC_IN_EP */ -/* #define DEBUG_S3C_UDC */ +#define DEBUG_SETUP 0 +#define DEBUG_EP0 0 +#define DEBUG_ISR 0 +#define DEBUG_OUT_EP 0 +#define DEBUG_IN_EP 0 #include <usb/s3c_udc.h> @@ -132,6 +125,19 @@ static void nuke(struct s3c_ep *ep, int status); static int s3c_udc_set_halt(struct usb_ep *_ep, int value); static void s3c_udc_set_nak(struct s3c_ep *ep); +void set_udc_gadget_private_data(void *p) +{ + debug_cond(DEBUG_SETUP != 0, + "%s: the_controller: 0x%p, p: 0x%p\n", __func__, + the_controller, p); + the_controller->gadget.dev.device_data = p; +} + +void *get_udc_gadget_private_data(struct usb_gadget *gadget) +{ + return gadget->dev.device_data; +} + static struct usb_ep_ops s3c_ep_ops = { .enable = s3c_ep_enable, .disable = s3c_ep_disable, @@ -216,7 +222,7 @@ void otg_phy_off(struct s3c_udc *dev) */ static void udc_disable(struct s3c_udc *dev) { - DEBUG_SETUP("%s: %p\n", __func__, dev); + debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev); udc_set_address(dev, 0); @@ -234,7 +240,7 @@ static void udc_reinit(struct s3c_udc *dev) { unsigned int i; - DEBUG_SETUP("%s: %p\n", __func__, dev); + debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev); /* device/ep0 records init */ INIT_LIST_HEAD(&dev->gadget.ep_list); @@ -265,12 +271,13 @@ static void udc_reinit(struct s3c_udc *dev) */ static int udc_enable(struct s3c_udc *dev) { - DEBUG_SETUP("%s: %p\n", __func__, dev); + debug_cond(DEBUG_SETUP != 0, "%s: %p\n", __func__, dev); otg_phy_init(dev); reconfig_usbd(); - DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", + debug_cond(DEBUG_SETUP != 0, + "S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", readl(®->gintmsk)); dev->gadget.speed = USB_SPEED_UNKNOWN; @@ -287,7 +294,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) int retval = 0; unsigned long flags; - DEBUG_SETUP("%s: %s\n", __func__, "no name"); + debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name"); if (!driver || (driver->speed != USB_SPEED_FULL @@ -311,7 +318,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) retval = driver->bind(&dev->gadget); if (retval) { - DEBUG_SETUP("%s: bind to driver --> error %d\n", + debug_cond(DEBUG_SETUP != 0, + "%s: bind to driver --> error %d\n", dev->gadget.name, retval); dev->driver = 0; return retval; @@ -319,7 +327,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) enable_irq(IRQ_OTG); - DEBUG_SETUP("Registered gadget driver %s\n", dev->gadget.name); + debug_cond(DEBUG_SETUP != 0, + "Registered gadget driver %s\n", dev->gadget.name); udc_enable(dev); return 0; @@ -377,7 +386,7 @@ static void done(struct s3c_ep *ep, struct s3c_request *req, int status) /* don't modify queue heads during completion callback */ ep->stopped = 1; -#ifdef DEBUG_S3C_UDC +#ifdef DEBUG printf("calling complete callback\n"); { int i, len = req->req.length; diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index 56e6e53de6..4be603783c 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -53,7 +53,7 @@ static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev) writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, ®->in_endp[EP0_CON].diepctl); - DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n", __func__, readl(®->in_endp[EP0_CON].diepctl)); dev->ep0state = WAIT_FOR_IN_COMPLETE; } @@ -62,7 +62,8 @@ void s3c_udc_pre_setup(void) { u32 ep_ctrl; - debug_cond(DEBUG_IN_EP, "%s : Prepare Setup packets.\n", __func__); + debug_cond(DEBUG_IN_EP, + "%s : Prepare Setup packets.\n", __func__); invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, (unsigned long) usb_ctrl_dma_addr @@ -75,9 +76,9 @@ void s3c_udc_pre_setup(void) ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); writel(ep_ctrl|DEPCTL_EPENA, ®->out_endp[EP0_CON].doepctl); - DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n", __func__, readl(®->in_endp[EP0_CON].diepctl)); - DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n", __func__, readl(®->out_endp[EP0_CON].doepctl)); } @@ -86,9 +87,9 @@ static inline void s3c_ep0_complete_out(void) { u32 ep_ctrl; - DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n", __func__, readl(®->in_endp[EP0_CON].diepctl)); - DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n", __func__, readl(®->out_endp[EP0_CON].doepctl)); debug_cond(DEBUG_IN_EP, @@ -106,9 +107,9 @@ static inline void s3c_ep0_complete_out(void) writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, ®->out_endp[EP0_CON].doepctl); - DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DIEPCTL0 = 0x%x\n", __func__, readl(®->in_endp[EP0_CON].diepctl)); - DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + debug_cond(DEBUG_EP0 != 0, "%s:EP0 ZLP DOEPCTL0 = 0x%x\n", __func__, readl(®->out_endp[EP0_CON].doepctl)); } @@ -145,14 +146,15 @@ static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) ®->out_endp[ep_num].doeptsiz); writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->out_endp[ep_num].doepctl); - DEBUG_OUT_EP("%s: EP%d RX DMA start : DOEPDMA = 0x%x," - "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n" - "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", - __func__, ep_num, - readl(®->out_endp[ep_num].doepdma), - readl(®->out_endp[ep_num].doeptsiz), - readl(®->out_endp[ep_num].doepctl), - buf, pktcnt, length); + debug_cond(DEBUG_OUT_EP != 0, + "%s: EP%d RX DMA start : DOEPDMA = 0x%x," + "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n" + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", + __func__, ep_num, + readl(®->out_endp[ep_num].doepdma), + readl(®->out_endp[ep_num].doeptsiz), + readl(®->out_endp[ep_num].doepctl), + buf, pktcnt, length); return 0; } @@ -225,8 +227,9 @@ static void complete_rx(struct s3c_udc *dev, u8 ep_num) u32 *p = the_controller->dma_buf[ep_index(ep)+1]; if (list_empty(&ep->queue)) { - DEBUG_OUT_EP("%s: RX DMA done : NULL REQ on OUT EP-%d\n", - __func__, ep_num); + debug_cond(DEBUG_OUT_EP != 0, + "%s: RX DMA done : NULL REQ on OUT EP-%d\n", + __func__, ep_num); return; } @@ -249,14 +252,15 @@ static void complete_rx(struct s3c_udc *dev, u8 ep_num) req->req.actual += min(xfer_size, req->req.length - req->req.actual); is_short = (xfer_size < ep->ep.maxpacket); - DEBUG_OUT_EP("%s: RX DMA done : ep = %d, rx bytes = %d/%d, " - "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n", - __func__, ep_num, req->req.actual, req->req.length, - is_short, ep_tsr, xfer_size); + debug_cond(DEBUG_OUT_EP != 0, + "%s: RX DMA done : ep = %d, rx bytes = %d/%d, " + "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n", + __func__, ep_num, req->req.actual, req->req.length, + is_short, ep_tsr, xfer_size); if (is_short || req->req.actual == req->req.length) { if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) { - DEBUG_OUT_EP(" => Send ZLP\n"); + debug_cond(DEBUG_OUT_EP != 0, " => Send ZLP\n"); s3c_udc_ep0_zlp(dev); /* packet will be completed in complete_tx() */ dev->ep0state = WAIT_FOR_IN_COMPLETE; @@ -266,8 +270,9 @@ static void complete_rx(struct s3c_udc *dev, u8 ep_num) if (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct s3c_request, queue); - DEBUG_OUT_EP("%s: Next Rx request start...\n", - __func__); + debug_cond(DEBUG_OUT_EP != 0, + "%s: Next Rx request start...\n", + __func__); setdma_rx(ep, req); } } @@ -392,8 +397,9 @@ static void process_ep_in_intr(struct s3c_udc *dev) while (ep_intr) { if (ep_intr & DAINT_IN_EP_INT(1)) { ep_intr_status = readl(®->in_endp[ep_num].diepint); - debug_cond(DEBUG_IN_EP, "\tEP%d-IN : DIEPINT = 0x%x\n", - ep_num, ep_intr_status); + debug_cond(DEBUG_IN_EP, + "\tEP%d-IN : DIEPINT = 0x%x\n", + ep_num, ep_intr_status); /* Interrupt Clear */ writel(ep_intr_status, ®->in_endp[ep_num].diepint); @@ -430,16 +436,18 @@ static void process_ep_out_intr(struct s3c_udc *dev) u8 ep_num = 0; ep_intr = readl(®->daint); - DEBUG_OUT_EP("*** %s: EP OUT interrupt : DAINT = 0x%x\n", - __func__, ep_intr); + debug_cond(DEBUG_OUT_EP != 0, + "*** %s: EP OUT interrupt : DAINT = 0x%x\n", + __func__, ep_intr); ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK; while (ep_intr) { if (ep_intr & 0x1) { ep_intr_status = readl(®->out_endp[ep_num].doepint); - DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n", - ep_num, ep_intr_status); + debug_cond(DEBUG_OUT_EP != 0, + "\tEP%d-OUT : DOEPINT = 0x%x\n", + ep_num, ep_intr_status); /* Interrupt Clear */ writel(ep_intr_status, ®->out_endp[ep_num].doepint); @@ -457,7 +465,8 @@ static void process_ep_out_intr(struct s3c_udc *dev) if (ep_intr_status & CTRL_OUT_EP_SETUP_PHASE_DONE) { - DEBUG_OUT_EP("SETUP packet arrived\n"); + debug_cond(DEBUG_OUT_EP != 0, + "SETUP packet arrived\n"); s3c_handle_ep0(dev); } } else { @@ -503,7 +512,8 @@ static int s3c_udc_irq(int irq, void *_dev) usb_status = (readl(®->dsts) & 0x6); if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) { - debug_cond(DEBUG_ISR, "\t\tFull Speed Detection\n"); + debug_cond(DEBUG_ISR, + "\t\tFull Speed Detection\n"); set_max_pktsize(dev, USB_SPEED_FULL); } else { @@ -571,7 +581,8 @@ static int s3c_udc_irq(int irq, void *_dev) } else { reset_available = 1; - debug_cond(DEBUG_ISR, "\t\tRESET handling skipped\n"); + debug_cond(DEBUG_ISR, + "\t\tRESET handling skipped\n"); } } @@ -635,7 +646,7 @@ static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req, _req, _req->length, _req->buf, list_empty(&ep->queue), ep->stopped); -#ifdef DEBUG_S3C_UDC +#ifdef DEBUG { int i, len = _req->length; @@ -662,14 +673,15 @@ static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req, } else if (ep_is_in(ep)) { gintsts = readl(®->gintsts); debug_cond(DEBUG_IN_EP, - "%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n", - __func__, gintsts); + "%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); setdma_tx(ep, req); } else { gintsts = readl(®->gintsts); - DEBUG_OUT_EP("%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n", - __func__, gintsts); + debug_cond(DEBUG_OUT_EP != 0, + "%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); setdma_rx(ep, req); } @@ -697,7 +709,7 @@ static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req) max = ep_maxpacket(ep); - DEBUG_EP0("%s: max = %d\n", __func__, max); + debug_cond(DEBUG_EP0 != 0, "%s: max = %d\n", __func__, max); count = setdma_tx(ep, req); @@ -712,10 +724,11 @@ static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req) is_last = 1; } - DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__, - ep->ep.name, count, - is_last ? "/L" : "", - req->req.length - req->req.actual - count, req); + debug_cond(DEBUG_EP0 != 0, + "%s: wrote %s %d bytes%s %d left %p\n", __func__, + ep->ep.name, count, + is_last ? "/L" : "", + req->req.length - req->req.actual - count, req); /* requests complete when all IN data is in the FIFO */ if (is_last) { @@ -736,8 +749,9 @@ int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max) (unsigned long) ep->dev->dma_buf[ep_index(ep)] + DMA_BUFFER_SIZE); - DEBUG_EP0("%s: bytes=%d, ep_index=%d %p\n", __func__, - bytes, ep_index(ep), ep->dev->dma_buf[ep_index(ep)]); + debug_cond(DEBUG_EP0 != 0, + "%s: bytes=%d, ep_index=%d %p\n", __func__, + bytes, ep_index(ep), ep->dev->dma_buf[ep_index(ep)]); return bytes; } @@ -756,8 +770,9 @@ static void udc_set_address(struct s3c_udc *dev, unsigned char address) s3c_udc_ep0_zlp(dev); - DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", - __func__, address, readl(®->dcfg)); + debug_cond(DEBUG_EP0 != 0, + "%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", + __func__, address, readl(®->dcfg)); dev->usb_address = address; } @@ -778,8 +793,9 @@ static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep) writel(ep_ctrl, ®->in_endp[EP0_CON].diepctl); - DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n", - __func__, ep_index(ep), ®->in_endp[EP0_CON].diepctl); + debug_cond(DEBUG_EP0 != 0, + "%s: set ep%d stall, DIEPCTL0 = 0x%p\n", + __func__, ep_index(ep), ®->in_endp[EP0_CON].diepctl); /* * The application can only set this bit, and the core clears it, * when a SETUP token is received for this endpoint @@ -803,8 +819,9 @@ static void s3c_ep0_read(struct s3c_udc *dev) return; } - DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", - __func__, req, req->req.length, req->req.actual); + debug_cond(DEBUG_EP0 != 0, + "%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", + __func__, req, req->req.length, req->req.actual); if (req->req.length == 0) { /* zlp for Set_configuration, Set_interface, @@ -813,8 +830,9 @@ static void s3c_ep0_read(struct s3c_udc *dev) ep->len = 0; s3c_udc_ep0_zlp(dev); - DEBUG_EP0("%s: req.length = 0, bRequest = %d\n", - __func__, usb_ctrl->bRequest); + debug_cond(DEBUG_EP0 != 0, + "%s: req.length = 0, bRequest = %d\n", + __func__, usb_ctrl->bRequest); return; } @@ -836,12 +854,13 @@ static int s3c_ep0_write(struct s3c_udc *dev) req = list_entry(ep->queue.next, struct s3c_request, queue); if (!req) { - DEBUG_EP0("%s: NULL REQ\n", __func__); + debug_cond(DEBUG_EP0 != 0, "%s: NULL REQ\n", __func__); return 0; } - DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", - __func__, req, req->req.length, req->req.actual); + debug_cond(DEBUG_EP0 != 0, + "%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", + __func__, req, req->req.length, req->req.actual); if (req->req.length - req->req.actual == ep0_fifo_size) { /* Next write will end with the packet size, */ @@ -854,11 +873,13 @@ static int s3c_ep0_write(struct s3c_udc *dev) if ((ret == 1) && !need_zlp) { /* Last packet */ dev->ep0state = WAIT_FOR_COMPLETE; - DEBUG_EP0("%s: finished, waiting for status\n", __func__); + debug_cond(DEBUG_EP0 != 0, + "%s: finished, waiting for status\n", __func__); } else { dev->ep0state = DATA_STATE_XMIT; - DEBUG_EP0("%s: not finished\n", __func__); + debug_cond(DEBUG_EP0 != 0, + "%s: not finished\n", __func__); } return 1; @@ -873,30 +894,35 @@ int s3c_udc_get_status(struct s3c_udc *dev, u32 ep_ctrl; u32 *p = the_controller->dma_buf[1]; - DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__); + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_GET_STATUS\n", __func__); printf("crq->brequest:0x%x\n", crq->bRequestType & USB_RECIP_MASK); switch (crq->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: g_status = 0; - DEBUG_SETUP("\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n", - g_status); + debug_cond(DEBUG_SETUP != 0, + "\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n", + g_status); break; case USB_RECIP_DEVICE: g_status = 0x1; /* Self powered */ - DEBUG_SETUP("\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n", - g_status); + debug_cond(DEBUG_SETUP != 0, + "\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n", + g_status); break; case USB_RECIP_ENDPOINT: if (crq->wLength > 2) { - DEBUG_SETUP("\tGET_STATUS:Not support EP or wLength\n"); + debug_cond(DEBUG_SETUP != 0, + "\tGET_STATUS:Not support EP or wLength\n"); return 1; } g_status = dev->ep[ep_num].stopped; - DEBUG_SETUP("\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n", - g_status); + debug_cond(DEBUG_SETUP != 0, + "\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n", + g_status); break; @@ -1134,11 +1160,13 @@ static int s3c_udc_clear_feature(struct usb_ep *_ep) ep_num = ep_index(ep); dev = ep->dev; - DEBUG_SETUP("%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n", - __func__, ep_num, ep_is_in(ep), clear_feature_flag); + debug_cond(DEBUG_SETUP != 0, + "%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n", + __func__, ep_num, ep_is_in(ep), clear_feature_flag); if (usb_ctrl->wLength != 0) { - DEBUG_SETUP("\tCLEAR_FEATURE: wLength is not zero.....\n"); + debug_cond(DEBUG_SETUP != 0, + "\tCLEAR_FEATURE: wLength is not zero.....\n"); return 1; } @@ -1146,11 +1174,13 @@ static int s3c_udc_clear_feature(struct usb_ep *_ep) case USB_RECIP_DEVICE: switch (usb_ctrl->wValue) { case USB_DEVICE_REMOTE_WAKEUP: - DEBUG_SETUP("\tOFF:USB_DEVICE_REMOTE_WAKEUP\n"); + debug_cond(DEBUG_SETUP != 0, + "\tOFF:USB_DEVICE_REMOTE_WAKEUP\n"); break; case USB_DEVICE_TEST_MODE: - DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n"); + debug_cond(DEBUG_SETUP != 0, + "\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n"); /** @todo Add CLEAR_FEATURE for TEST modes. */ break; } @@ -1159,8 +1189,9 @@ static int s3c_udc_clear_feature(struct usb_ep *_ep) break; case USB_RECIP_ENDPOINT: - DEBUG_SETUP("\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n", - usb_ctrl->wValue); + debug_cond(DEBUG_SETUP != 0, + "\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n", + usb_ctrl->wValue); if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { if (ep_num == 0) { @@ -1193,11 +1224,13 @@ static int s3c_udc_set_feature(struct usb_ep *_ep) ep_num = ep_index(ep); dev = ep->dev; - DEBUG_SETUP("%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n", + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n", __func__, ep_num); if (usb_ctrl->wLength != 0) { - DEBUG_SETUP("\tSET_FEATURE: wLength is not zero.....\n"); + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE: wLength is not zero.....\n"); return 1; } @@ -1205,20 +1238,24 @@ static int s3c_udc_set_feature(struct usb_ep *_ep) case USB_RECIP_DEVICE: switch (usb_ctrl->wValue) { case USB_DEVICE_REMOTE_WAKEUP: - DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n"); + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n"); break; case USB_DEVICE_B_HNP_ENABLE: - DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); break; case USB_DEVICE_A_HNP_SUPPORT: /* RH port supports HNP */ - DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n"); + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n"); break; case USB_DEVICE_A_ALT_HNP_SUPPORT: /* other RH port does */ - DEBUG_SETUP("\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); + debug_cond(DEBUG_SETUP != 0, + "\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); break; } @@ -1226,11 +1263,13 @@ static int s3c_udc_set_feature(struct usb_ep *_ep) return 0; case USB_RECIP_INTERFACE: - DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_INTERFACE\n"); + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE: USB_RECIP_INTERFACE\n"); break; case USB_RECIP_ENDPOINT: - DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_ENDPOINT\n"); + debug_cond(DEBUG_SETUP != 0, + "\tSET_FEATURE: USB_RECIP_ENDPOINT\n"); if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { if (ep_num == 0) { s3c_udc_ep0_set_stall(ep); @@ -1262,14 +1301,15 @@ void s3c_ep0_setup(struct s3c_udc *dev) /* read control req from fifo (8 bytes) */ s3c_fifo_read(ep, (u32 *)usb_ctrl, 8); - DEBUG_SETUP("%s: bRequestType = 0x%x(%s), bRequest = 0x%x" - "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n", - __func__, usb_ctrl->bRequestType, - (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", - usb_ctrl->bRequest, - usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex); + debug_cond(DEBUG_SETUP != 0, + "%s: bRequestType = 0x%x(%s), bRequest = 0x%x" + "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n", + __func__, usb_ctrl->bRequestType, + (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", + usb_ctrl->bRequest, + usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex); -#ifdef DEBUG_S3C_UDC +#ifdef DEBUG { int i, len = sizeof(*usb_ctrl); char *p = (char *)usb_ctrl; @@ -1286,10 +1326,12 @@ void s3c_ep0_setup(struct s3c_udc *dev) if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST && usb_ctrl->wLength != 1) { - DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid", - __func__); - DEBUG_SETUP("wLength = %d, setup returned\n", - usb_ctrl->wLength); + debug_cond(DEBUG_SETUP != 0, + "\t%s:GET_MAX_LUN_REQUEST:invalid", + __func__); + debug_cond(DEBUG_SETUP != 0, + "wLength = %d, setup returned\n", + usb_ctrl->wLength); s3c_udc_ep0_set_stall(ep); dev->ep0state = WAIT_FOR_SETUP; @@ -1298,8 +1340,9 @@ void s3c_ep0_setup(struct s3c_udc *dev) } else if (usb_ctrl->bRequest == BOT_RESET_REQUEST && usb_ctrl->wLength != 0) { /* Bulk-Only *mass storge reset of class-specific request */ - DEBUG_SETUP("%s:BOT Rest:invalid wLength =%d, setup returned\n", - __func__, usb_ctrl->wLength); + debug_cond(DEBUG_SETUP != 0, + "%s:BOT Rest:invalid wLength =%d, setup returned\n", + __func__, usb_ctrl->wLength); s3c_udc_ep0_set_stall(ep); dev->ep0state = WAIT_FOR_SETUP; @@ -1323,8 +1366,9 @@ void s3c_ep0_setup(struct s3c_udc *dev) if (dev->req_std) { switch (usb_ctrl->bRequest) { case USB_REQ_SET_ADDRESS: - DEBUG_SETUP("%s: *** USB_REQ_SET_ADDRESS (%d)\n", - __func__, usb_ctrl->wValue); + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_SET_ADDRESS (%d)\n", + __func__, usb_ctrl->wValue); if (usb_ctrl->bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; @@ -1333,9 +1377,11 @@ void s3c_ep0_setup(struct s3c_udc *dev) return; case USB_REQ_SET_CONFIGURATION: - DEBUG_SETUP("=====================================\n"); - DEBUG_SETUP("%s: USB_REQ_SET_CONFIGURATION (%d)\n", - __func__, usb_ctrl->wValue); + debug_cond(DEBUG_SETUP != 0, + "=====================================\n"); + debug_cond(DEBUG_SETUP != 0, + "%s: USB_REQ_SET_CONFIGURATION (%d)\n", + __func__, usb_ctrl->wValue); if (usb_ctrl->bRequestType == USB_RECIP_DEVICE) reset_available = 1; @@ -1343,13 +1389,15 @@ void s3c_ep0_setup(struct s3c_udc *dev) break; case USB_REQ_GET_DESCRIPTOR: - DEBUG_SETUP("%s: *** USB_REQ_GET_DESCRIPTOR\n", - __func__); + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_GET_DESCRIPTOR\n", + __func__); break; case USB_REQ_SET_INTERFACE: - DEBUG_SETUP("%s: *** USB_REQ_SET_INTERFACE (%d)\n", - __func__, usb_ctrl->wValue); + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_SET_INTERFACE (%d)\n", + __func__, usb_ctrl->wValue); if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE) reset_available = 1; @@ -1357,8 +1405,9 @@ void s3c_ep0_setup(struct s3c_udc *dev) break; case USB_REQ_GET_CONFIGURATION: - DEBUG_SETUP("%s: *** USB_REQ_GET_CONFIGURATION\n", - __func__); + debug_cond(DEBUG_SETUP != 0, + "%s: *** USB_REQ_GET_CONFIGURATION\n", + __func__); break; case USB_REQ_GET_STATUS: @@ -1384,8 +1433,9 @@ void s3c_ep0_setup(struct s3c_udc *dev) break; default: - DEBUG_SETUP("%s: *** Default of usb_ctrl->bRequest=0x%x" - "happened.\n", __func__, usb_ctrl->bRequest); + debug_cond(DEBUG_SETUP != 0, + "%s: *** Default of usb_ctrl->bRequest=0x%x" + "happened.\n", __func__, usb_ctrl->bRequest); break; } } @@ -1394,7 +1444,8 @@ void s3c_ep0_setup(struct s3c_udc *dev) if (likely(dev->driver)) { /* device-2-host (IN) or no data setup command, * process immediately */ - DEBUG_SETUP("%s:usb_ctrlreq will be passed to fsg_setup()\n", + debug_cond(DEBUG_SETUP != 0, + "%s:usb_ctrlreq will be passed to fsg_setup()\n", __func__); spin_unlock(&dev->lock); @@ -1406,17 +1457,20 @@ void s3c_ep0_setup(struct s3c_udc *dev) s3c_udc_ep0_set_stall(ep); dev->ep0state = WAIT_FOR_SETUP; - DEBUG_SETUP("\tdev->driver->setup failed (%d)," + debug_cond(DEBUG_SETUP != 0, + "\tdev->driver->setup failed (%d)," " bRequest = %d\n", i, usb_ctrl->bRequest); } else if (dev->req_pending) { dev->req_pending = 0; - DEBUG_SETUP("\tdev->req_pending...\n"); + debug_cond(DEBUG_SETUP != 0, + "\tdev->req_pending...\n"); } - DEBUG_SETUP("\tep0state = %s\n", state_names[dev->ep0state]); + debug_cond(DEBUG_SETUP != 0, + "\tep0state = %s\n", state_names[dev->ep0state]); } } @@ -1427,18 +1481,21 @@ void s3c_ep0_setup(struct s3c_udc *dev) static void s3c_handle_ep0(struct s3c_udc *dev) { if (dev->ep0state == WAIT_FOR_SETUP) { - DEBUG_OUT_EP("%s: WAIT_FOR_SETUP\n", __func__); + debug_cond(DEBUG_OUT_EP != 0, + "%s: WAIT_FOR_SETUP\n", __func__); s3c_ep0_setup(dev); } else { - DEBUG_OUT_EP("%s: strange state!!(state = %s)\n", + debug_cond(DEBUG_OUT_EP != 0, + "%s: strange state!!(state = %s)\n", __func__, state_names[dev->ep0state]); } } static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep) { - DEBUG_EP0("%s: ep_is_in = %d\n", __func__, ep_is_in(ep)); + debug_cond(DEBUG_EP0 != 0, + "%s: ep_is_in = %d\n", __func__, ep_is_in(ep)); if (ep_is_in(ep)) { dev->ep0state = DATA_STATE_XMIT; s3c_ep0_write(dev); diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 95555cf96b..4dbe060d6f 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -13,6 +13,7 @@ #include <common.h> #include <asm/errno.h> #include <linux/usb/ch9.h> +#include <usbdescriptors.h> #include <linux/usb/gadget.h> #include <asm/unaligned.h> diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 0d4657edf2..59c3e57563 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -50,6 +50,7 @@ COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o COBJS-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o +COBJS-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.o COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 38d6ae03d3..04300be110 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -108,99 +108,6 @@ static struct descriptor { #define ehci_is_TDI() (0) #endif -#if defined(CONFIG_EHCI_DCACHE) -/* - * Routines to handle (flush/invalidate) the dcache for the QH and qTD - * structures and data buffers. This is needed on platforms using this - * EHCI support with dcache enabled. - */ -static void flush_invalidate(u32 addr, int size, int flush) -{ - if (flush) - flush_dcache_range(addr, addr + size); - else - invalidate_dcache_range(addr, addr + size); -} - -static void cache_qtd(struct qTD *qtd, int flush) -{ - u32 *ptr = (u32 *)qtd->qt_buffer[0]; - int len = (qtd->qt_token & 0x7fff0000) >> 16; - - flush_invalidate((u32)qtd, sizeof(struct qTD), flush); - if (ptr && len) - flush_invalidate((u32)ptr, len, flush); -} - - -static inline struct QH *qh_addr(struct QH *qh) -{ - return (struct QH *)((u32)qh & 0xffffffe0); -} - -static void cache_qh(struct QH *qh, int flush) -{ - struct qTD *qtd; - struct qTD *next; - static struct qTD *first_qtd; - - /* - * Walk the QH list and flush/invalidate all entries - */ - while (1) { - flush_invalidate((u32)qh_addr(qh), sizeof(struct QH), flush); - if ((u32)qh & QH_LINK_TYPE_QH) - break; - qh = qh_addr(qh); - qh = (struct QH *)qh->qh_link; - } - qh = qh_addr(qh); - - /* - * Save first qTD pointer, needed for invalidating pass on this QH - */ - if (flush) - first_qtd = qtd = (struct qTD *)(*(u32 *)&qh->qh_overlay & - 0xffffffe0); - else - qtd = first_qtd; - - /* - * Walk the qTD list and flush/invalidate all entries - */ - while (1) { - if (qtd == NULL) - break; - cache_qtd(qtd, flush); - next = (struct qTD *)((u32)qtd->qt_next & 0xffffffe0); - if (next == qtd) - break; - qtd = next; - } -} - -static inline void ehci_flush_dcache(struct QH *qh) -{ - cache_qh(qh, 1); -} - -static inline void ehci_invalidate_dcache(struct QH *qh) -{ - cache_qh(qh, 0); -} -#else /* CONFIG_EHCI_DCACHE */ -/* - * - */ -static inline void ehci_flush_dcache(struct QH *qh) -{ -} - -static inline void ehci_invalidate_dcache(struct QH *qh) -{ -} -#endif /* CONFIG_EHCI_DCACHE */ - void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) { mdelay(50); @@ -225,11 +132,6 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) return -1; } -static void ehci_free(void *p, size_t sz) -{ - -} - static int ehci_reset(void) { uint32_t cmd; @@ -266,43 +168,22 @@ out: return ret; } -static void *ehci_alloc(size_t sz, size_t align) -{ - static struct QH qh __attribute__((aligned(32))); - static struct qTD td[3] __attribute__((aligned (32))); - static int ntds; - void *p; - - switch (sz) { - case sizeof(struct QH): - p = &qh; - ntds = 0; - break; - case sizeof(struct qTD): - if (ntds == 3) { - debug("out of TDs\n"); - return NULL; - } - p = &td[ntds]; - ntds++; - break; - default: - debug("unknown allocation size\n"); - return NULL; - } - - memset(p, 0, sz); - return p; -} - static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz) { - uint32_t addr, delta, next; + uint32_t delta, next; + uint32_t addr = (uint32_t)buf; + size_t rsz = roundup(sz, 32); int idx; - addr = (uint32_t) buf; + if (sz != rsz) + debug("EHCI-HCD: Misaligned buffer size (%08x)\n", sz); + + if (addr & 31) + debug("EHCI-HCD: Misaligned buffer address (%p)\n", buf); + idx = 0; while (idx < 5) { + flush_dcache_range(addr, addr + rsz); td->qt_buffer[idx] = cpu_to_hc32(addr); td->qt_buffer_hi[idx] = 0; next = (addr + 4096) & ~4095; @@ -326,8 +207,10 @@ static int ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) { - struct QH *qh; - struct qTD *td; + static struct QH qh __attribute__((aligned(32))); + static struct qTD qtd[3] __attribute__((aligned (32))); + int qtd_counter = 0; + volatile struct qTD *vtd; unsigned long ts; uint32_t *tdp; @@ -346,12 +229,22 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, le16_to_cpu(req->value), le16_to_cpu(req->value), le16_to_cpu(req->index)); - qh = ehci_alloc(sizeof(struct QH), 32); - if (qh == NULL) { - debug("unable to allocate QH\n"); - return -1; - } - qh->qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); + memset(&qh, 0, sizeof(struct QH)); + memset(qtd, 0, sizeof(qtd)); + + toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + + /* + * Setup QH (3.6 in ehci-r10.pdf) + * + * qh_link ................. 03-00 H + * qh_endpt1 ............... 07-04 H + * qh_endpt2 ............... 0B-08 H + * - qh_curtd + * qh_overlay.qt_next ...... 13-10 H + * - qh_overlay.qt_altnext + */ + qh.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); c = (usb_pipespeed(pipe) != USB_SPEED_HIGH && usb_pipeendpoint(pipe) == 0) ? 1 : 0; endpt = (8 << 28) | @@ -362,88 +255,98 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, (usb_pipespeed(pipe) << 12) | (usb_pipeendpoint(pipe) << 8) | (0 << 7) | (usb_pipedevice(pipe) << 0); - qh->qh_endpt1 = cpu_to_hc32(endpt); + qh.qh_endpt1 = cpu_to_hc32(endpt); endpt = (1 << 30) | (dev->portnr << 23) | (dev->parent->devnum << 16) | (0 << 8) | (0 << 0); - qh->qh_endpt2 = cpu_to_hc32(endpt); - qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - - td = NULL; - tdp = &qh->qh_overlay.qt_next; + qh.qh_endpt2 = cpu_to_hc32(endpt); + qh.qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - toggle = - usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + tdp = &qh.qh_overlay.qt_next; if (req != NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); - if (td == NULL) { - debug("unable to allocate SETUP td\n"); - goto fail; - } - td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + /* + * Setup request qTD (3.5 in ehci-r10.pdf) + * + * qt_next ................ 03-00 H + * qt_altnext ............. 07-04 H + * qt_token ............... 0B-08 H + * + * [ buffer, buffer_hi ] loaded with "req". + */ + qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (0 << 31) | (sizeof(*req) << 16) | (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0); - td->qt_token = cpu_to_hc32(token); - if (ehci_td_buffer(td, req, sizeof(*req)) != 0) { + qtd[qtd_counter].qt_token = cpu_to_hc32(token); + if (ehci_td_buffer(&qtd[qtd_counter], req, sizeof(*req)) != 0) { debug("unable construct SETUP td\n"); - ehci_free(td, sizeof(*td)); goto fail; } - *tdp = cpu_to_hc32((uint32_t) td); - tdp = &td->qt_next; + /* Update previous qTD! */ + *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + tdp = &qtd[qtd_counter++].qt_next; toggle = 1; } if (length > 0 || req == NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); - if (td == NULL) { - debug("unable to allocate DATA td\n"); - goto fail; - } - td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + /* + * Setup request qTD (3.5 in ehci-r10.pdf) + * + * qt_next ................ 03-00 H + * qt_altnext ............. 07-04 H + * qt_token ............... 0B-08 H + * + * [ buffer, buffer_hi ] loaded with "buffer". + */ + qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (toggle << 31) | (length << 16) | ((req == NULL ? 1 : 0) << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0); - td->qt_token = cpu_to_hc32(token); - if (ehci_td_buffer(td, buffer, length) != 0) { + qtd[qtd_counter].qt_token = cpu_to_hc32(token); + if (ehci_td_buffer(&qtd[qtd_counter], buffer, length) != 0) { debug("unable construct DATA td\n"); - ehci_free(td, sizeof(*td)); goto fail; } - *tdp = cpu_to_hc32((uint32_t) td); - tdp = &td->qt_next; + /* Update previous qTD! */ + *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + tdp = &qtd[qtd_counter++].qt_next; } if (req != NULL) { - td = ehci_alloc(sizeof(struct qTD), 32); - if (td == NULL) { - debug("unable to allocate ACK td\n"); - goto fail; - } - td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); - td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + /* + * Setup request qTD (3.5 in ehci-r10.pdf) + * + * qt_next ................ 03-00 H + * qt_altnext ............. 07-04 H + * qt_token ............... 0B-08 H + */ + qtd[qtd_counter].qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); + qtd[qtd_counter].qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); token = (toggle << 31) | (0 << 16) | (1 << 15) | (0 << 12) | (3 << 10) | ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0); - td->qt_token = cpu_to_hc32(token); - *tdp = cpu_to_hc32((uint32_t) td); - tdp = &td->qt_next; + qtd[qtd_counter].qt_token = cpu_to_hc32(token); + /* Update previous qTD! */ + *tdp = cpu_to_hc32((uint32_t)&qtd[qtd_counter]); + tdp = &qtd[qtd_counter++].qt_next; } - qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); + qh_list.qh_link = cpu_to_hc32((uint32_t)&qh | QH_LINK_TYPE_QH); /* Flush dcache */ - ehci_flush_dcache(&qh_list); + flush_dcache_range((uint32_t)&qh_list, + (uint32_t)&qh_list + sizeof(struct QH)); + flush_dcache_range((uint32_t)&qh, (uint32_t)&qh + sizeof(struct QH)); + flush_dcache_range((uint32_t)qtd, (uint32_t)qtd + sizeof(qtd)); usbsts = ehci_readl(&hcor->or_usbsts); ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); @@ -462,17 +365,27 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, /* Wait for TDs to be processed. */ ts = get_timer(0); - vtd = td; + vtd = &qtd[qtd_counter - 1]; timeout = USB_TIMEOUT_MS(pipe); do { /* Invalidate dcache */ - ehci_invalidate_dcache(&qh_list); + invalidate_dcache_range((uint32_t)&qh_list, + (uint32_t)&qh_list + sizeof(struct QH)); + invalidate_dcache_range((uint32_t)&qh, + (uint32_t)&qh + sizeof(struct QH)); + invalidate_dcache_range((uint32_t)qtd, + (uint32_t)qtd + sizeof(qtd)); + token = hc32_to_cpu(vtd->qt_token); if (!(token & 0x80)) break; WATCHDOG_RESET(); } while (get_timer(ts) < timeout); + /* Invalidate the memory area occupied by buffer */ + invalidate_dcache_range(((uint32_t)buffer & ~31), + ((uint32_t)buffer & ~31) + roundup(length, 32)); + /* Check that the TD processing happened */ if (token & 0x80) { printf("EHCI timed out on TD - token=%#x\n", token); @@ -492,7 +405,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); - token = hc32_to_cpu(qh->qh_overlay.qt_token); + token = hc32_to_cpu(qh.qh_overlay.qt_token); if (!(token & 0x80)) { debug("TOKEN=%#x\n", token); switch (token & 0xfc) { @@ -531,13 +444,6 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; fail: - td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); - while (td != (void *)QT_NEXT_TERMINATE) { - qh->qh_overlay.qt_next = td->qt_next; - ehci_free(td, sizeof(*td)); - td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next); - } - ehci_free(qh, sizeof(*qh)); return -1; } diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c new file mode 100644 index 0000000000..4dd4ec168f --- /dev/null +++ b/drivers/usb/host/ehci-s5p.c @@ -0,0 +1,110 @@ +/* + * SAMSUNG S5P USB HOST EHCI Controller + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Vivek Gautam <gautam.vivek@samsung.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <usb.h> +#include <asm/arch/cpu.h> +#include <asm/arch/ehci-s5p.h> +#include "ehci.h" +#include "ehci-core.h" + +/* Setup the EHCI host controller. */ +static void setup_usb_phy(struct s5p_usb_phy *usb) +{ + clrbits_le32(&usb->usbphyctrl0, + HOST_CTRL0_FSEL_MASK | + HOST_CTRL0_COMMONON_N | + /* HOST Phy setting */ + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + setbits_le32(&usb->usbphyctrl0, + /* Setting up the ref freq */ + (CLK_24MHZ << 16) | + /* HOST Phy setting */ + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + udelay(10); + clrbits_le32(&usb->usbphyctrl0, + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + udelay(20); + + /* EHCI Ctrl setting */ + setbits_le32(&usb->ehcictrl, + EHCICTRL_ENAINCRXALIGN | + EHCICTRL_ENAINCR4 | + EHCICTRL_ENAINCR8 | + EHCICTRL_ENAINCR16); +} + +/* Reset the EHCI host controller. */ +static void reset_usb_phy(struct s5p_usb_phy *usb) +{ + /* HOST_PHY reset */ + setbits_le32(&usb->usbphyctrl0, + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); +} + +/* + * EHCI-initialization + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(void) +{ + struct s5p_usb_phy *usb; + + usb = (struct s5p_usb_phy *)samsung_get_base_usb_phy(); + setup_usb_phy(usb); + + hccr = (struct ehci_hccr *)(EXYNOS5_USB_HOST_EHCI_BASE); + hcor = (struct ehci_hcor *)((uint32_t) hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + debug("Exynos5-ehci: init hccr %x and hcor %x hc_length %d\n", + (uint32_t)hccr, (uint32_t)hcor, + (uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the EHCI host controller. + */ +int ehci_hcd_stop() +{ + struct s5p_usb_phy *usb; + + usb = (struct s5p_usb_phy *)samsung_get_base_usb_phy(); + reset_usb_phy(usb); + + return 0; +} |