diff options
Diffstat (limited to 'drivers/usb/gadget')
58 files changed, 4840 insertions, 709 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 02ff850278b1..c6db0a0a340c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -483,34 +483,6 @@ config USB_CONFIGFS_F_TCM Both protocols can work on USB2.0 and USB3.0. UAS utilizes the USB 3.0 feature called streams support. -choice - tristate "USB Gadget precomposed configurations" - default USB_ETH - optional - help - A Linux "Gadget Driver" talks to the USB Peripheral Controller - driver through the abstract "gadget" API. Some other operating - systems call these "client" drivers, of which "class drivers" - are a subset (implementing a USB device class specification). - A gadget driver implements one or more USB functions using - the peripheral hardware. - - Gadget drivers are hardware-neutral, or "platform independent", - except that they sometimes must understand quirks or limitations - of the particular controllers they work with. For example, when - a controller doesn't support alternate configurations or provide - enough of the right types of endpoints, the gadget driver might - not be able work with that controller, or might need to implement - a less common variant of a device class protocol. - - The available choices each represent a single precomposed USB - gadget configuration. In the device model, each option contains - both the device instantiation as a child for a USB gadget - controller, and the relevant drivers for each function declared - by the device. - source "drivers/usb/gadget/legacy/Kconfig" -endchoice - endif # USB_GADGET diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 76883ff4f5bb..3b4f67000315 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -612,6 +612,7 @@ static int bos_desc(struct usb_composite_dev *cdev) struct usb_ext_cap_descriptor *usb_ext; struct usb_dcd_config_params dcd_config_params; struct usb_bos_descriptor *bos = cdev->req->buf; + unsigned int besl = 0; bos->bLength = USB_DT_BOS_SIZE; bos->bDescriptorType = USB_DT_BOS; @@ -619,6 +620,29 @@ static int bos_desc(struct usb_composite_dev *cdev) bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE); bos->bNumDeviceCaps = 0; + /* Get Controller configuration */ + if (cdev->gadget->ops->get_config_params) { + cdev->gadget->ops->get_config_params(cdev->gadget, + &dcd_config_params); + } else { + dcd_config_params.besl_baseline = + USB_DEFAULT_BESL_UNSPECIFIED; + dcd_config_params.besl_deep = + USB_DEFAULT_BESL_UNSPECIFIED; + dcd_config_params.bU1devExitLat = + USB_DEFAULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU2DevExitLat = + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); + } + + if (dcd_config_params.besl_baseline != USB_DEFAULT_BESL_UNSPECIFIED) + besl = USB_BESL_BASELINE_VALID | + USB_SET_BESL_BASELINE(dcd_config_params.besl_baseline); + + if (dcd_config_params.besl_deep != USB_DEFAULT_BESL_UNSPECIFIED) + besl |= USB_BESL_DEEP_VALID | + USB_SET_BESL_DEEP(dcd_config_params.besl_deep); + /* * A SuperSpeed device shall include the USB2.0 extension descriptor * and shall support LPM when operating in USB2.0 HS mode. @@ -629,7 +653,8 @@ static int bos_desc(struct usb_composite_dev *cdev) usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; - usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT); + usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | + USB_BESL_SUPPORT | besl); /* * The Superspeed USB Capability descriptor shall be implemented by all @@ -650,17 +675,6 @@ static int bos_desc(struct usb_composite_dev *cdev) USB_HIGH_SPEED_OPERATION | USB_5GBPS_OPERATION); ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; - - /* Get Controller configuration */ - if (cdev->gadget->ops->get_config_params) { - cdev->gadget->ops->get_config_params(cdev->gadget, - &dcd_config_params); - } else { - dcd_config_params.bU1devExitLat = - USB_DEFAULT_U1_DEV_EXIT_LAT; - dcd_config_params.bU2DevExitLat = - cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); - } ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; } @@ -780,9 +794,9 @@ static int set_config(struct usb_composite_dev *cdev, result = 0; } - INFO(cdev, "%s config #%d: %s\n", - usb_speed_string(gadget->speed), - number, c ? c->label : "unconfigured"); + DBG(cdev, "%s config #%d: %s\n", + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; @@ -2156,14 +2170,18 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req); kfree(cdev->os_desc_req->buf); + cdev->os_desc_req->buf = NULL; usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req); + cdev->os_desc_req = NULL; } if (cdev->req) { if (cdev->setup_pending) usb_ep_dequeue(cdev->gadget->ep0, cdev->req); kfree(cdev->req->buf); + cdev->req->buf = NULL; usb_ep_free_request(cdev->gadget->ep0, cdev->req); + cdev->req = NULL; } cdev->next_string_id = 0; device_remove_file(&cdev->gadget->dev, &dev_attr_suspended); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 025129942894..32b637e3e1fa 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -61,6 +61,8 @@ struct gadget_info { bool use_os_desc; char b_vendor_code; char qw_sign[OS_STRING_QW_SIGN_LEN]; + spinlock_t spinlock; + bool unbind; }; static inline struct gadget_info *to_gadget_info(struct config_item *item) @@ -291,6 +293,47 @@ err: return ret; } +static ssize_t gadget_dev_desc_max_speed_show(struct config_item *item, + char *page) +{ + enum usb_device_speed speed = to_gadget_info(item)->composite.max_speed; + + return sprintf(page, "%s\n", usb_speed_string(speed)); +} + +static ssize_t gadget_dev_desc_max_speed_store(struct config_item *item, + const char *page, size_t len) +{ + struct gadget_info *gi = to_gadget_info(item); + + mutex_lock(&gi->lock); + + /* Prevent changing of max_speed after the driver is binded */ + if (gi->composite.gadget_driver.udc_name) + goto err; + + if (strncmp(page, "super-speed-plus", 16) == 0) + gi->composite.max_speed = USB_SPEED_SUPER_PLUS; + else if (strncmp(page, "super-speed", 11) == 0) + gi->composite.max_speed = USB_SPEED_SUPER; + else if (strncmp(page, "high-speed", 10) == 0) + gi->composite.max_speed = USB_SPEED_HIGH; + else if (strncmp(page, "full-speed", 10) == 0) + gi->composite.max_speed = USB_SPEED_FULL; + else if (strncmp(page, "low-speed", 9) == 0) + gi->composite.max_speed = USB_SPEED_LOW; + else + goto err; + + gi->composite.gadget_driver.max_speed = gi->composite.max_speed; + + mutex_unlock(&gi->lock); + return len; +err: + mutex_unlock(&gi->lock); + return -EINVAL; +} + CONFIGFS_ATTR(gadget_dev_desc_, bDeviceClass); CONFIGFS_ATTR(gadget_dev_desc_, bDeviceSubClass); CONFIGFS_ATTR(gadget_dev_desc_, bDeviceProtocol); @@ -300,6 +343,7 @@ CONFIGFS_ATTR(gadget_dev_desc_, idProduct); CONFIGFS_ATTR(gadget_dev_desc_, bcdDevice); CONFIGFS_ATTR(gadget_dev_desc_, bcdUSB); CONFIGFS_ATTR(gadget_dev_desc_, UDC); +CONFIGFS_ATTR(gadget_dev_desc_, max_speed); static struct configfs_attribute *gadget_root_attrs[] = { &gadget_dev_desc_attr_bDeviceClass, @@ -311,6 +355,7 @@ static struct configfs_attribute *gadget_root_attrs[] = { &gadget_dev_desc_attr_bcdDevice, &gadget_dev_desc_attr_bcdUSB, &gadget_dev_desc_attr_UDC, + &gadget_dev_desc_attr_max_speed, NULL, }; @@ -1244,6 +1289,7 @@ static int configfs_composite_bind(struct usb_gadget *gadget, int ret; /* the gi->lock is hold by the caller */ + gi->unbind = 0; cdev->gadget = gadget; set_gadget_data(gadget, cdev); ret = composite_dev_prepare(composite, cdev); @@ -1376,31 +1422,128 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev; struct gadget_info *gi; + unsigned long flags; /* the gi->lock is hold by the caller */ cdev = get_gadget_data(gadget); gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + gi->unbind = 1; + spin_unlock_irqrestore(&gi->spinlock, flags); kfree(otg_desc[0]); otg_desc[0] = NULL; purge_configs_funcs(gi); composite_dev_cleanup(cdev); usb_ep_autoconfig_reset(cdev->gadget); + spin_lock_irqsave(&gi->spinlock, flags); cdev->gadget = NULL; set_gadget_data(gadget, NULL); + spin_unlock_irqrestore(&gi->spinlock, flags); +} + +static int configfs_composite_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + unsigned long flags; + int ret; + + cdev = get_gadget_data(gadget); + if (!cdev) + return 0; + + gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + cdev = get_gadget_data(gadget); + if (!cdev || gi->unbind) { + spin_unlock_irqrestore(&gi->spinlock, flags); + return 0; + } + + ret = composite_setup(gadget, ctrl); + spin_unlock_irqrestore(&gi->spinlock, flags); + return ret; +} + +static void configfs_composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + unsigned long flags; + + cdev = get_gadget_data(gadget); + if (!cdev) + return; + + gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + cdev = get_gadget_data(gadget); + if (!cdev || gi->unbind) { + spin_unlock_irqrestore(&gi->spinlock, flags); + return; + } + + composite_disconnect(gadget); + spin_unlock_irqrestore(&gi->spinlock, flags); +} + +static void configfs_composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + unsigned long flags; + + cdev = get_gadget_data(gadget); + if (!cdev) + return; + + gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + cdev = get_gadget_data(gadget); + if (!cdev || gi->unbind) { + spin_unlock_irqrestore(&gi->spinlock, flags); + return; + } + + composite_suspend(gadget); + spin_unlock_irqrestore(&gi->spinlock, flags); +} + +static void configfs_composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + struct gadget_info *gi; + unsigned long flags; + + cdev = get_gadget_data(gadget); + if (!cdev) + return; + + gi = container_of(cdev, struct gadget_info, cdev); + spin_lock_irqsave(&gi->spinlock, flags); + cdev = get_gadget_data(gadget); + if (!cdev || gi->unbind) { + spin_unlock_irqrestore(&gi->spinlock, flags); + return; + } + + composite_resume(gadget); + spin_unlock_irqrestore(&gi->spinlock, flags); } static const struct usb_gadget_driver configfs_driver_template = { .bind = configfs_composite_bind, .unbind = configfs_composite_unbind, - .setup = composite_setup, - .reset = composite_disconnect, - .disconnect = composite_disconnect, + .setup = configfs_composite_setup, + .reset = configfs_composite_disconnect, + .disconnect = configfs_composite_disconnect, - .suspend = composite_suspend, - .resume = composite_resume, + .suspend = configfs_composite_suspend, + .resume = configfs_composite_resume, .max_speed = USB_SPEED_SUPER, .driver = { @@ -1444,6 +1587,7 @@ static struct config_group *gadgets_make( gi->composite.resume = NULL; gi->composite.max_speed = USB_SPEED_SUPER; + spin_lock_init(&gi->spinlock); mutex_init(&gi->lock); INIT_LIST_HEAD(&gi->string_list); INIT_LIST_HEAD(&gi->available_func); diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 9fc98de83624..7c152c28b26c 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -771,6 +771,24 @@ static struct configfs_item_operations acm_item_ops = { .release = acm_attr_release, }; +#ifdef CONFIG_U_SERIAL_CONSOLE + +static ssize_t f_acm_console_store(struct config_item *item, + const char *page, size_t count) +{ + return gserial_set_console(to_f_serial_opts(item)->port_num, + page, count); +} + +static ssize_t f_acm_console_show(struct config_item *item, char *page) +{ + return gserial_get_console(to_f_serial_opts(item)->port_num, page); +} + +CONFIGFS_ATTR(f_acm_, console); + +#endif /* CONFIG_U_SERIAL_CONSOLE */ + static ssize_t f_acm_port_num_show(struct config_item *item, char *page) { return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num); @@ -779,6 +797,9 @@ static ssize_t f_acm_port_num_show(struct config_item *item, char *page) CONFIGFS_ATTR_RO(f_acm_, port_num); static struct configfs_attribute *acm_attrs[] = { +#ifdef CONFIG_U_SERIAL_CONSOLE + &f_acm_attr_console, +#endif &f_acm_attr_port_num, NULL, }; diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 6ce044008cf6..7f5cf488b2b1 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -52,6 +52,7 @@ struct f_ecm { struct usb_ep *notify; struct usb_request *notify_req; u8 notify_state; + atomic_t notify_count; bool is_open; /* FIXME is_open needs some irq-ish locking @@ -380,7 +381,7 @@ static void ecm_do_notify(struct f_ecm *ecm) int status; /* notification already in flight? */ - if (!req) + if (atomic_read(&ecm->notify_count)) return; event = req->buf; @@ -420,10 +421,10 @@ static void ecm_do_notify(struct f_ecm *ecm) event->bmRequestType = 0xA1; event->wIndex = cpu_to_le16(ecm->ctrl_id); - ecm->notify_req = NULL; + atomic_inc(&ecm->notify_count); status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); if (status < 0) { - ecm->notify_req = req; + atomic_dec(&ecm->notify_count); DBG(cdev, "notify --> %d\n", status); } } @@ -448,17 +449,19 @@ static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req) switch (req->status) { case 0: /* no fault */ + atomic_dec(&ecm->notify_count); break; case -ECONNRESET: case -ESHUTDOWN: + atomic_set(&ecm->notify_count, 0); ecm->notify_state = ECM_NOTIFY_NONE; break; default: DBG(cdev, "event %02x --> %d\n", event->bNotificationType, req->status); + atomic_dec(&ecm->notify_count); break; } - ecm->notify_req = req; ecm_do_notify(ecm); } @@ -621,8 +624,12 @@ static void ecm_disable(struct usb_function *f) DBG(cdev, "ecm deactivated\n"); - if (ecm->port.in_ep->enabled) + if (ecm->port.in_ep->enabled) { gether_disconnect(&ecm->port); + } else { + ecm->port.in_ep->desc = NULL; + ecm->port.out_ep->desc = NULL; + } usb_ep_disable(ecm->notify); ecm->notify->desc = NULL; @@ -903,6 +910,11 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) usb_free_all_descriptors(f); + if (atomic_read(&ecm->notify_count)) { + usb_ep_dequeue(ecm->notify, ecm->notify_req); + atomic_set(&ecm->notify_count, 0); + } + kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); } diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 213ff03c8a9f..6171d28331e6 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -17,6 +17,7 @@ #include <linux/blkdev.h> #include <linux/pagemap.h> #include <linux/export.h> +#include <linux/fs_parser.h> #include <linux/hid.h> #include <linux/mm.h> #include <linux/module.h> @@ -1061,6 +1062,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->num_sgs = io_data->sgt.nents; } else { req->buf = data; + req->num_sgs = 0; } req->length = data_len; @@ -1104,6 +1106,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->num_sgs = io_data->sgt.nents; } else { req->buf = data; + req->num_sgs = 0; } req->length = data_len; @@ -1351,14 +1354,6 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, return ret; } -#ifdef CONFIG_COMPAT -static long ffs_epfile_compat_ioctl(struct file *file, unsigned code, - unsigned long value) -{ - return ffs_epfile_ioctl(file, code, value); -} -#endif - static const struct file_operations ffs_epfile_operations = { .llseek = no_llseek, @@ -1367,9 +1362,7 @@ static const struct file_operations ffs_epfile_operations = { .read_iter = ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = ffs_epfile_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, }; @@ -1451,9 +1444,9 @@ struct ffs_sb_fill_data { struct ffs_data *ffs_data; }; -static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) +static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc) { - struct ffs_sb_fill_data *data = _data; + struct ffs_sb_fill_data *data = fc->fs_private; struct inode *inode; struct ffs_data *ffs = data->ffs_data; @@ -1486,147 +1479,147 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) return 0; } -static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) -{ - ENTER(); - - if (!opts || !*opts) - return 0; +enum { + Opt_no_disconnect, + Opt_rmode, + Opt_fmode, + Opt_mode, + Opt_uid, + Opt_gid, +}; - for (;;) { - unsigned long value; - char *eq, *comma; - - /* Option limit */ - comma = strchr(opts, ','); - if (comma) - *comma = 0; - - /* Value limit */ - eq = strchr(opts, '='); - if (unlikely(!eq)) { - pr_err("'=' missing in %s\n", opts); - return -EINVAL; - } - *eq = 0; +static const struct fs_parameter_spec ffs_fs_fs_parameters[] = { + fsparam_bool ("no_disconnect", Opt_no_disconnect), + fsparam_u32 ("rmode", Opt_rmode), + fsparam_u32 ("fmode", Opt_fmode), + fsparam_u32 ("mode", Opt_mode), + fsparam_u32 ("uid", Opt_uid), + fsparam_u32 ("gid", Opt_gid), + {} +}; - /* Parse value */ - if (kstrtoul(eq + 1, 0, &value)) { - pr_err("%s: invalid value: %s\n", opts, eq + 1); - return -EINVAL; - } +static int ffs_fs_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct ffs_sb_fill_data *data = fc->fs_private; + struct fs_parse_result result; + int opt; - /* Interpret option */ - switch (eq - opts) { - case 13: - if (!memcmp(opts, "no_disconnect", 13)) - data->no_disconnect = !!value; - else - goto invalid; - break; - case 5: - if (!memcmp(opts, "rmode", 5)) - data->root_mode = (value & 0555) | S_IFDIR; - else if (!memcmp(opts, "fmode", 5)) - data->perms.mode = (value & 0666) | S_IFREG; - else - goto invalid; - break; + ENTER(); - case 4: - if (!memcmp(opts, "mode", 4)) { - data->root_mode = (value & 0555) | S_IFDIR; - data->perms.mode = (value & 0666) | S_IFREG; - } else { - goto invalid; - } - break; + opt = fs_parse(fc, ffs_fs_fs_parameters, param, &result); + if (opt < 0) + return opt; - case 3: - if (!memcmp(opts, "uid", 3)) { - data->perms.uid = make_kuid(current_user_ns(), value); - if (!uid_valid(data->perms.uid)) { - pr_err("%s: unmapped value: %lu\n", opts, value); - return -EINVAL; - } - } else if (!memcmp(opts, "gid", 3)) { - data->perms.gid = make_kgid(current_user_ns(), value); - if (!gid_valid(data->perms.gid)) { - pr_err("%s: unmapped value: %lu\n", opts, value); - return -EINVAL; - } - } else { - goto invalid; - } - break; + switch (opt) { + case Opt_no_disconnect: + data->no_disconnect = result.boolean; + break; + case Opt_rmode: + data->root_mode = (result.uint_32 & 0555) | S_IFDIR; + break; + case Opt_fmode: + data->perms.mode = (result.uint_32 & 0666) | S_IFREG; + break; + case Opt_mode: + data->root_mode = (result.uint_32 & 0555) | S_IFDIR; + data->perms.mode = (result.uint_32 & 0666) | S_IFREG; + break; - default: -invalid: - pr_err("%s: invalid option\n", opts); - return -EINVAL; - } + case Opt_uid: + data->perms.uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(data->perms.uid)) + goto unmapped_value; + break; + case Opt_gid: + data->perms.gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(data->perms.gid)) + goto unmapped_value; + break; - /* Next iteration */ - if (!comma) - break; - opts = comma + 1; + default: + return -ENOPARAM; } return 0; -} -/* "mount -t functionfs dev_name /dev/function" ends up here */ +unmapped_value: + return invalf(fc, "%s: unmapped value: %u", param->key, result.uint_32); +} -static struct dentry * -ffs_fs_mount(struct file_system_type *t, int flags, - const char *dev_name, void *opts) -{ - struct ffs_sb_fill_data data = { - .perms = { - .mode = S_IFREG | 0600, - .uid = GLOBAL_ROOT_UID, - .gid = GLOBAL_ROOT_GID, - }, - .root_mode = S_IFDIR | 0500, - .no_disconnect = false, - }; - struct dentry *rv; - int ret; +/* + * Set up the superblock for a mount. + */ +static int ffs_fs_get_tree(struct fs_context *fc) +{ + struct ffs_sb_fill_data *ctx = fc->fs_private; void *ffs_dev; struct ffs_data *ffs; ENTER(); - ret = ffs_fs_parse_opts(&data, opts); - if (unlikely(ret < 0)) - return ERR_PTR(ret); + if (!fc->source) + return invalf(fc, "No source specified"); - ffs = ffs_data_new(dev_name); + ffs = ffs_data_new(fc->source); if (unlikely(!ffs)) - return ERR_PTR(-ENOMEM); - ffs->file_perms = data.perms; - ffs->no_disconnect = data.no_disconnect; + return -ENOMEM; + ffs->file_perms = ctx->perms; + ffs->no_disconnect = ctx->no_disconnect; - ffs->dev_name = kstrdup(dev_name, GFP_KERNEL); + ffs->dev_name = kstrdup(fc->source, GFP_KERNEL); if (unlikely(!ffs->dev_name)) { ffs_data_put(ffs); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } - ffs_dev = ffs_acquire_dev(dev_name); + ffs_dev = ffs_acquire_dev(ffs->dev_name); if (IS_ERR(ffs_dev)) { ffs_data_put(ffs); - return ERR_CAST(ffs_dev); + return PTR_ERR(ffs_dev); } + ffs->private_data = ffs_dev; - data.ffs_data = ffs; + ctx->ffs_data = ffs; + return get_tree_nodev(fc, ffs_sb_fill); +} + +static void ffs_fs_free_fc(struct fs_context *fc) +{ + struct ffs_sb_fill_data *ctx = fc->fs_private; - rv = mount_nodev(t, flags, &data, ffs_sb_fill); - if (IS_ERR(rv) && data.ffs_data) { - ffs_release_dev(data.ffs_data); - ffs_data_put(data.ffs_data); + if (ctx) { + if (ctx->ffs_data) { + ffs_release_dev(ctx->ffs_data); + ffs_data_put(ctx->ffs_data); + } + + kfree(ctx); } - return rv; +} + +static const struct fs_context_operations ffs_fs_context_ops = { + .free = ffs_fs_free_fc, + .parse_param = ffs_fs_parse_param, + .get_tree = ffs_fs_get_tree, +}; + +static int ffs_fs_init_fs_context(struct fs_context *fc) +{ + struct ffs_sb_fill_data *ctx; + + ctx = kzalloc(sizeof(struct ffs_sb_fill_data), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->perms.mode = S_IFREG | 0600; + ctx->perms.uid = GLOBAL_ROOT_UID; + ctx->perms.gid = GLOBAL_ROOT_GID; + ctx->root_mode = S_IFDIR | 0500; + ctx->no_disconnect = false; + + fc->fs_private = ctx; + fc->ops = &ffs_fs_context_ops; + return 0; } static void @@ -1644,7 +1637,8 @@ ffs_fs_kill_sb(struct super_block *sb) static struct file_system_type ffs_fs_type = { .owner = THIS_MODULE, .name = "functionfs", - .mount = ffs_fs_mount, + .init_fs_context = ffs_fs_init_fs_context, + .parameters = ffs_fs_fs_parameters, .kill_sb = ffs_fs_kill_sb, }; MODULE_ALIAS_FS("functionfs"); @@ -3512,7 +3506,7 @@ static void ffs_free_inst(struct usb_function_instance *f) static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name) { - if (strlen(name) >= FIELD_SIZEOF(struct ffs_dev, name)) + if (strlen(name) >= sizeof_field(struct ffs_dev, name)) return -ENAMETOOLONG; return ffs_name_dev(to_f_fs_opts(fi)->dev, name); } diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 2d6e76e4cffa..1d900081b1f0 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -53,6 +53,7 @@ struct f_ncm { struct usb_ep *notify; struct usb_request *notify_req; u8 notify_state; + atomic_t notify_count; bool is_open; const struct ndp_parser_opts *parser_opts; @@ -547,7 +548,7 @@ static void ncm_do_notify(struct f_ncm *ncm) int status; /* notification already in flight? */ - if (!req) + if (atomic_read(&ncm->notify_count)) return; event = req->buf; @@ -587,7 +588,8 @@ static void ncm_do_notify(struct f_ncm *ncm) event->bmRequestType = 0xA1; event->wIndex = cpu_to_le16(ncm->ctrl_id); - ncm->notify_req = NULL; + atomic_inc(&ncm->notify_count); + /* * In double buffering if there is a space in FIFO, * completion callback can be called right after the call, @@ -597,7 +599,7 @@ static void ncm_do_notify(struct f_ncm *ncm) status = usb_ep_queue(ncm->notify, req, GFP_ATOMIC); spin_lock(&ncm->lock); if (status < 0) { - ncm->notify_req = req; + atomic_dec(&ncm->notify_count); DBG(cdev, "notify --> %d\n", status); } } @@ -632,17 +634,19 @@ static void ncm_notify_complete(struct usb_ep *ep, struct usb_request *req) case 0: VDBG(cdev, "Notification %02x sent\n", event->bNotificationType); + atomic_dec(&ncm->notify_count); break; case -ECONNRESET: case -ESHUTDOWN: + atomic_set(&ncm->notify_count, 0); ncm->notify_state = NCM_NOTIFY_NONE; break; default: DBG(cdev, "event %02x --> %d\n", event->bNotificationType, req->status); + atomic_dec(&ncm->notify_count); break; } - ncm->notify_req = req; ncm_do_notify(ncm); spin_unlock(&ncm->lock); } @@ -1649,6 +1653,11 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); + if (atomic_read(&ncm->notify_count)) { + usb_ep_dequeue(ncm->notify, ncm->notify_req); + atomic_set(&ncm->notify_count, 0); + } + kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); } diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index 55b7f57d2dc7..ab26d84ed95e 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -432,7 +432,7 @@ static struct usb_function_instance *obex_alloc_inst(void) return ERR_PTR(-ENOMEM); opts->func_inst.free_func_inst = obex_free_inst; - ret = gserial_alloc_line(&opts->port_num); + ret = gserial_alloc_line_no_console(&opts->port_num); if (ret) { kfree(opts); return ERR_PTR(ret); diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index d48df36622b7..0d8e4a364ca6 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -618,6 +618,7 @@ static void rndis_disable(struct usb_function *f) gether_disconnect(&rndis->port); usb_ep_disable(rndis->notify); + rndis->notify->desc = NULL; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index c860f30a0ea2..1406255d0865 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -266,6 +266,24 @@ static struct configfs_item_operations serial_item_ops = { .release = serial_attr_release, }; +#ifdef CONFIG_U_SERIAL_CONSOLE + +static ssize_t f_serial_console_store(struct config_item *item, + const char *page, size_t count) +{ + return gserial_set_console(to_f_serial_opts(item)->port_num, + page, count); +} + +static ssize_t f_serial_console_show(struct config_item *item, char *page) +{ + return gserial_get_console(to_f_serial_opts(item)->port_num, page); +} + +CONFIGFS_ATTR(f_serial_, console); + +#endif /* CONFIG_U_SERIAL_CONSOLE */ + static ssize_t f_serial_port_num_show(struct config_item *item, char *page) { return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num); @@ -274,6 +292,9 @@ static ssize_t f_serial_port_num_show(struct config_item *item, char *page) CONFIGFS_ATTR_RO(f_serial_, port_num); static struct configfs_attribute *acm_attrs[] = { +#ifdef CONFIG_U_SERIAL_CONSOLE + &f_serial_attr_console, +#endif &f_serial_attr_port_num, NULL, }; diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 7f01f78b1d23..36504931b2d1 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -846,7 +846,7 @@ static void uasp_set_alt(struct f_uas *fu) fu->flags = USBG_IS_UAS; - if (gadget->speed == USB_SPEED_SUPER) + if (gadget->speed >= USB_SPEED_SUPER) fu->flags |= USBG_USE_STREAMS; config_ep_by_speed(gadget, f, fu->ep_in); @@ -2093,6 +2093,16 @@ static void tcm_delayed_set_alt(struct work_struct *wq) usb_composite_setup_continue(fu->function.config->cdev); } +static int tcm_get_alt(struct usb_function *f, unsigned intf) +{ + if (intf == bot_intf_desc.bInterfaceNumber) + return USB_G_ALT_INT_BBB; + if (intf == uasp_intf_desc.bInterfaceNumber) + return USB_G_ALT_INT_UAS; + + return -EOPNOTSUPP; +} + static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_uas *fu = to_f_uas(f); @@ -2300,6 +2310,7 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi) fu->function.bind = tcm_bind; fu->function.unbind = tcm_unbind; fu->function.set_alt = tcm_set_alt; + fu->function.get_alt = tcm_get_alt; fu->function.setup = tcm_setup; fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 04c142c13075..64de9f1b874c 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -72,7 +72,7 @@ static rndis_resp_t *rndis_add_response(struct rndis_params *params, #ifdef CONFIG_USB_GADGET_DEBUG_FILES -static const struct file_operations rndis_proc_fops; +static const struct proc_ops rndis_proc_ops; #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ @@ -902,7 +902,7 @@ struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) sprintf(name, NAME_TEMPLATE, i); proc_entry = proc_create_data(name, 0660, NULL, - &rndis_proc_fops, params); + &rndis_proc_ops, params); if (!proc_entry) { kfree(params); rndis_put_nr(i); @@ -1164,13 +1164,12 @@ static int rndis_proc_open(struct inode *inode, struct file *file) return single_open(file, rndis_proc_show, PDE_DATA(inode)); } -static const struct file_operations rndis_proc_fops = { - .owner = THIS_MODULE, - .open = rndis_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = rndis_proc_write, +static const struct proc_ops rndis_proc_ops = { + .proc_open = rndis_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = rndis_proc_write, }; #define NAME_TEMPLATE "driver/rndis-%03d" diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 56906d15fb55..6d956f190f5a 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -239,18 +239,6 @@ static snd_pcm_uframes_t uac_pcm_pointer(struct snd_pcm_substream *substream) return bytes_to_frames(substream->runtime, prm->hw_ptr); } -static int uac_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); -} - -static int uac_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - static int uac_pcm_open(struct snd_pcm_substream *substream) { struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); @@ -326,9 +314,6 @@ static int uac_pcm_null(struct snd_pcm_substream *substream) static const struct snd_pcm_ops uac_pcm_ops = { .open = uac_pcm_open, .close = uac_pcm_null, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = uac_pcm_hw_params, - .hw_free = uac_pcm_hw_free, .trigger = uac_pcm_trigger, .pointer = uac_pcm_pointer, .prepare = uac_pcm_null, @@ -422,7 +407,7 @@ int u_audio_start_playback(struct g_audio *audio_dev) struct usb_ep *ep; struct uac_rtd_params *prm; struct uac_params *params = &audio_dev->params; - unsigned int factor, rate; + unsigned int factor; const struct usb_endpoint_descriptor *ep_desc; int req_len, i; @@ -441,13 +426,15 @@ int u_audio_start_playback(struct g_audio *audio_dev) /* pre-compute some values for iso_complete() */ uac->p_framesize = params->p_ssize * num_channels(params->p_chmask); - rate = params->p_srate * uac->p_framesize; uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); - uac->p_pktsize = min_t(unsigned int, rate / uac->p_interval, + uac->p_pktsize = min_t(unsigned int, + uac->p_framesize * + (params->p_srate / uac->p_interval), prm->max_psize); if (uac->p_pktsize < prm->max_psize) - uac->p_pktsize_residue = rate % uac->p_interval; + uac->p_pktsize_residue = uac->p_framesize * + (params->p_srate % uac->p_interval); else uac->p_pktsize_residue = 0; @@ -584,8 +571,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, strlcpy(card->shortname, card_name, sizeof(card->shortname)); sprintf(card->longname, "%s %i", card_name, card->dev->id); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + NULL, 0, BUFF_SIZE_MAX); err = snd_card_register(card); diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 65f634ec7fc2..f986e5c55974 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -82,14 +82,13 @@ #define GS_CONSOLE_BUF_SIZE 8192 /* console info */ -struct gscons_info { - struct gs_port *port; - struct task_struct *console_thread; - struct kfifo con_buf; - /* protect the buf and busy flag */ - spinlock_t con_lock; - int req_busy; - struct usb_request *console_req; +struct gs_console { + struct console console; + struct work_struct work; + spinlock_t lock; + struct usb_request *req; + struct kfifo buf; + size_t missed; }; /* @@ -101,8 +100,10 @@ struct gs_port { spinlock_t port_lock; /* guard port_* access */ struct gserial *port_usb; +#ifdef CONFIG_U_SERIAL_CONSOLE + struct gs_console *console; +#endif - bool openclose; /* open/close in progress */ u8 port_num; struct list_head read_pool; @@ -586,82 +587,45 @@ static int gs_open(struct tty_struct *tty, struct file *file) { int port_num = tty->index; struct gs_port *port; - int status; - - do { - mutex_lock(&ports[port_num].lock); - port = ports[port_num].port; - if (!port) - status = -ENODEV; - else { - spin_lock_irq(&port->port_lock); - - /* already open? Great. */ - if (port->port.count) { - status = 0; - port->port.count++; - - /* currently opening/closing? wait ... */ - } else if (port->openclose) { - status = -EBUSY; - - /* ... else we do the work */ - } else { - status = -EAGAIN; - port->openclose = true; - } - spin_unlock_irq(&port->port_lock); - } - mutex_unlock(&ports[port_num].lock); + int status = 0; - switch (status) { - default: - /* fully handled */ - return status; - case -EAGAIN: - /* must do the work */ - break; - case -EBUSY: - /* wait for EAGAIN task to finish */ - msleep(1); - /* REVISIT could have a waitchannel here, if - * concurrent open performance is important - */ - break; - } - } while (status != -EAGAIN); + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; + if (!port) { + status = -ENODEV; + goto out; + } - /* Do the "real open" */ spin_lock_irq(&port->port_lock); /* allocate circular buffer on first open */ if (!kfifo_initialized(&port->port_write_buf)) { spin_unlock_irq(&port->port_lock); + + /* + * portmaster's mutex still protects from simultaneous open(), + * and close() can't happen, yet. + */ + status = kfifo_alloc(&port->port_write_buf, WRITE_BUF_SIZE, GFP_KERNEL); - spin_lock_irq(&port->port_lock); - if (status) { pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n", - port->port_num, tty, file); - port->openclose = false; - goto exit_unlock_port; + port_num, tty, file); + goto out; } - } - /* REVISIT if REMOVED (ports[].port NULL), abort the open - * to let rmmod work faster (but this way isn't wrong). - */ + spin_lock_irq(&port->port_lock); + } - /* REVISIT maybe wait for "carrier detect" */ + /* already open? Great. */ + if (port->port.count++) + goto exit_unlock_port; tty->driver_data = port; port->port.tty = tty; - port->port.count = 1; - port->openclose = false; - /* if connected, start the I/O stream */ if (port->port_usb) { struct gserial *gser = port->port_usb; @@ -675,20 +639,21 @@ static int gs_open(struct tty_struct *tty, struct file *file) pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); - status = 0; - exit_unlock_port: spin_unlock_irq(&port->port_lock); +out: + mutex_unlock(&ports[port_num].lock); return status; } -static int gs_writes_finished(struct gs_port *p) +static int gs_close_flush_done(struct gs_port *p) { int cond; - /* return true on disconnect or empty buffer */ + /* return true on disconnect or empty buffer or if raced with open() */ spin_lock_irq(&p->port_lock); - cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf); + cond = p->port_usb == NULL || !kfifo_len(&p->port_write_buf) || + p->port.count > 1; spin_unlock_irq(&p->port_lock); return cond; @@ -702,6 +667,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) spin_lock_irq(&port->port_lock); if (port->port.count != 1) { +raced_with_open: if (port->port.count == 0) WARN_ON(1); else @@ -711,12 +677,6 @@ static void gs_close(struct tty_struct *tty, struct file *file) pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file); - /* mark port as closing but in use; we can drop port lock - * and sleep if necessary - */ - port->openclose = true; - port->port.count = 0; - gser = port->port_usb; if (gser && gser->disconnect) gser->disconnect(gser); @@ -727,9 +687,13 @@ static void gs_close(struct tty_struct *tty, struct file *file) if (kfifo_len(&port->port_write_buf) > 0 && gser) { spin_unlock_irq(&port->port_lock); wait_event_interruptible_timeout(port->drain_wait, - gs_writes_finished(port), + gs_close_flush_done(port), GS_CLOSE_TIMEOUT * HZ); spin_lock_irq(&port->port_lock); + + if (port->port.count != 1) + goto raced_with_open; + gser = port->port_usb; } @@ -742,10 +706,9 @@ static void gs_close(struct tty_struct *tty, struct file *file) else kfifo_reset(&port->port_write_buf); + port->port.count = 0; port->port.tty = NULL; - port->openclose = false; - pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", port->port_num, tty, file); @@ -889,36 +852,9 @@ static struct tty_driver *gs_tty_driver; #ifdef CONFIG_U_SERIAL_CONSOLE -static struct gscons_info gscons_info; -static struct console gserial_cons; - -static struct usb_request *gs_request_new(struct usb_ep *ep) -{ - struct usb_request *req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (!req) - return NULL; - - req->buf = kmalloc(ep->maxpacket, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - return NULL; - } - - return req; -} - -static void gs_request_free(struct usb_request *req, struct usb_ep *ep) +static void gs_console_complete_out(struct usb_ep *ep, struct usb_request *req) { - if (!req) - return; - - kfree(req->buf); - usb_ep_free_request(ep, req); -} - -static void gs_complete_out(struct usb_ep *ep, struct usb_request *req) -{ - struct gscons_info *info = &gscons_info; + struct gs_console *cons = req->context; switch (req->status) { default: @@ -927,12 +863,12 @@ static void gs_complete_out(struct usb_ep *ep, struct usb_request *req) /* fall through */ case 0: /* normal completion */ - spin_lock(&info->con_lock); - info->req_busy = 0; - spin_unlock(&info->con_lock); - - wake_up_process(info->console_thread); + spin_lock(&cons->lock); + req->length = 0; + schedule_work(&cons->work); + spin_unlock(&cons->lock); break; + case -ECONNRESET: case -ESHUTDOWN: /* disconnect */ pr_vdebug("%s: %s shutdown\n", __func__, ep->name); @@ -940,190 +876,250 @@ static void gs_complete_out(struct usb_ep *ep, struct usb_request *req) } } -static int gs_console_connect(int port_num) +static void __gs_console_push(struct gs_console *cons) { - struct gscons_info *info = &gscons_info; - struct gs_port *port; + struct usb_request *req = cons->req; struct usb_ep *ep; + size_t size; - if (port_num != gserial_cons.index) { - pr_err("%s: port num [%d] is not support console\n", - __func__, port_num); - return -ENXIO; - } + if (!req) + return; /* disconnected */ - port = ports[port_num].port; - ep = port->port_usb->in; - if (!info->console_req) { - info->console_req = gs_request_new(ep); - if (!info->console_req) - return -ENOMEM; - info->console_req->complete = gs_complete_out; + if (req->length) + return; /* busy */ + + ep = cons->console.data; + size = kfifo_out(&cons->buf, req->buf, ep->maxpacket); + if (!size) + return; + + if (cons->missed && ep->maxpacket >= 64) { + char buf[64]; + size_t len; + + len = sprintf(buf, "\n[missed %zu bytes]\n", cons->missed); + kfifo_in(&cons->buf, buf, len); + cons->missed = 0; } - info->port = port; - spin_lock(&info->con_lock); - info->req_busy = 0; - spin_unlock(&info->con_lock); - pr_vdebug("port[%d] console connect!\n", port_num); - return 0; + req->length = size; + if (usb_ep_queue(ep, req, GFP_ATOMIC)) + req->length = 0; } -static void gs_console_disconnect(struct usb_ep *ep) +static void gs_console_work(struct work_struct *work) { - struct gscons_info *info = &gscons_info; - struct usb_request *req = info->console_req; + struct gs_console *cons = container_of(work, struct gs_console, work); + + spin_lock_irq(&cons->lock); - gs_request_free(req, ep); - info->console_req = NULL; + __gs_console_push(cons); + + spin_unlock_irq(&cons->lock); } -static int gs_console_thread(void *data) +static void gs_console_write(struct console *co, + const char *buf, unsigned count) { - struct gscons_info *info = &gscons_info; - struct gs_port *port; + struct gs_console *cons = container_of(co, struct gs_console, console); + unsigned long flags; + size_t n; + + spin_lock_irqsave(&cons->lock, flags); + + n = kfifo_in(&cons->buf, buf, count); + if (n < count) + cons->missed += count - n; + + if (cons->req && !cons->req->length) + schedule_work(&cons->work); + + spin_unlock_irqrestore(&cons->lock, flags); +} + +static struct tty_driver *gs_console_device(struct console *co, int *index) +{ + *index = co->index; + return gs_tty_driver; +} + +static int gs_console_connect(struct gs_port *port) +{ + struct gs_console *cons = port->console; struct usb_request *req; struct usb_ep *ep; - int xfer, ret, count, size; - - do { - port = info->port; - set_current_state(TASK_INTERRUPTIBLE); - if (!port || !port->port_usb - || !port->port_usb->in || !info->console_req) - goto sched; - - req = info->console_req; - ep = port->port_usb->in; - - spin_lock_irq(&info->con_lock); - count = kfifo_len(&info->con_buf); - size = ep->maxpacket; - - if (count > 0 && !info->req_busy) { - set_current_state(TASK_RUNNING); - if (count < size) - size = count; - - xfer = kfifo_out(&info->con_buf, req->buf, size); - req->length = xfer; - - spin_unlock(&info->con_lock); - ret = usb_ep_queue(ep, req, GFP_ATOMIC); - spin_lock(&info->con_lock); - if (ret < 0) - info->req_busy = 0; - else - info->req_busy = 1; - - spin_unlock_irq(&info->con_lock); - } else { - spin_unlock_irq(&info->con_lock); -sched: - if (kthread_should_stop()) { - set_current_state(TASK_RUNNING); - break; - } - schedule(); - } - } while (1); + + if (!cons) + return 0; + + ep = port->port_usb->in; + req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC); + if (!req) + return -ENOMEM; + req->complete = gs_console_complete_out; + req->context = cons; + req->length = 0; + + spin_lock(&cons->lock); + cons->req = req; + cons->console.data = ep; + spin_unlock(&cons->lock); + + pr_debug("ttyGS%d: console connected!\n", port->port_num); + + schedule_work(&cons->work); return 0; } -static int gs_console_setup(struct console *co, char *options) +static void gs_console_disconnect(struct gs_port *port) { - struct gscons_info *info = &gscons_info; - int status; + struct gs_console *cons = port->console; + struct usb_request *req; + struct usb_ep *ep; + + if (!cons) + return; - info->port = NULL; - info->console_req = NULL; - info->req_busy = 0; - spin_lock_init(&info->con_lock); + spin_lock(&cons->lock); - status = kfifo_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL); - if (status) { - pr_err("%s: allocate console buffer failed\n", __func__); - return status; - } + req = cons->req; + ep = cons->console.data; + cons->req = NULL; + + spin_unlock(&cons->lock); - info->console_thread = kthread_create(gs_console_thread, - co, "gs_console"); - if (IS_ERR(info->console_thread)) { - pr_err("%s: cannot create console thread\n", __func__); - kfifo_free(&info->con_buf); - return PTR_ERR(info->console_thread); + if (!req) + return; + + usb_ep_dequeue(ep, req); + gs_free_req(ep, req); +} + +static int gs_console_init(struct gs_port *port) +{ + struct gs_console *cons; + int err; + + if (port->console) + return 0; + + cons = kzalloc(sizeof(*port->console), GFP_KERNEL); + if (!cons) + return -ENOMEM; + + strcpy(cons->console.name, "ttyGS"); + cons->console.write = gs_console_write; + cons->console.device = gs_console_device; + cons->console.flags = CON_PRINTBUFFER; + cons->console.index = port->port_num; + + INIT_WORK(&cons->work, gs_console_work); + spin_lock_init(&cons->lock); + + err = kfifo_alloc(&cons->buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL); + if (err) { + pr_err("ttyGS%d: allocate console buffer failed\n", port->port_num); + kfree(cons); + return err; } - wake_up_process(info->console_thread); + + port->console = cons; + register_console(&cons->console); + + spin_lock_irq(&port->port_lock); + if (port->port_usb) + gs_console_connect(port); + spin_unlock_irq(&port->port_lock); return 0; } -static void gs_console_write(struct console *co, - const char *buf, unsigned count) +static void gs_console_exit(struct gs_port *port) { - struct gscons_info *info = &gscons_info; - unsigned long flags; + struct gs_console *cons = port->console; + + if (!cons) + return; + + unregister_console(&cons->console); - spin_lock_irqsave(&info->con_lock, flags); - kfifo_in(&info->con_buf, buf, count); - spin_unlock_irqrestore(&info->con_lock, flags); + spin_lock_irq(&port->port_lock); + if (cons->req) + gs_console_disconnect(port); + spin_unlock_irq(&port->port_lock); - wake_up_process(info->console_thread); + cancel_work_sync(&cons->work); + kfifo_free(&cons->buf); + kfree(cons); + port->console = NULL; } -static struct tty_driver *gs_console_device(struct console *co, int *index) +ssize_t gserial_set_console(unsigned char port_num, const char *page, size_t count) { - struct tty_driver **p = (struct tty_driver **)co->data; + struct gs_port *port; + bool enable; + int ret; - if (!*p) - return NULL; + ret = strtobool(page, &enable); + if (ret) + return ret; - *index = co->index; - return *p; -} + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; -static struct console gserial_cons = { - .name = "ttyGS", - .write = gs_console_write, - .device = gs_console_device, - .setup = gs_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &gs_tty_driver, -}; + if (WARN_ON(port == NULL)) { + ret = -ENXIO; + goto out; + } -static void gserial_console_init(void) -{ - register_console(&gserial_cons); + if (enable) + ret = gs_console_init(port); + else + gs_console_exit(port); +out: + mutex_unlock(&ports[port_num].lock); + + return ret < 0 ? ret : count; } +EXPORT_SYMBOL_GPL(gserial_set_console); -static void gserial_console_exit(void) +ssize_t gserial_get_console(unsigned char port_num, char *page) { - struct gscons_info *info = &gscons_info; + struct gs_port *port; + ssize_t ret; - unregister_console(&gserial_cons); - if (!IS_ERR_OR_NULL(info->console_thread)) - kthread_stop(info->console_thread); - kfifo_free(&info->con_buf); + mutex_lock(&ports[port_num].lock); + port = ports[port_num].port; + + if (WARN_ON(port == NULL)) + ret = -ENXIO; + else + ret = sprintf(page, "%u\n", !!port->console); + + mutex_unlock(&ports[port_num].lock); + + return ret; } +EXPORT_SYMBOL_GPL(gserial_get_console); #else -static int gs_console_connect(int port_num) +static int gs_console_connect(struct gs_port *port) { return 0; } -static void gs_console_disconnect(struct usb_ep *ep) +static void gs_console_disconnect(struct gs_port *port) { } -static void gserial_console_init(void) +static int gs_console_init(struct gs_port *port) { + return -ENOSYS; } -static void gserial_console_exit(void) +static void gs_console_exit(struct gs_port *port) { } @@ -1172,8 +1168,9 @@ static int gs_closed(struct gs_port *port) int cond; spin_lock_irq(&port->port_lock); - cond = (port->port.count == 0) && !port->openclose; + cond = port->port.count == 0; spin_unlock_irq(&port->port_lock); + return cond; } @@ -1197,18 +1194,19 @@ void gserial_free_line(unsigned char port_num) return; } port = ports[port_num].port; + gs_console_exit(port); ports[port_num].port = NULL; mutex_unlock(&ports[port_num].lock); gserial_free_port(port); tty_unregister_device(gs_tty_driver, port_num); - gserial_console_exit(); } EXPORT_SYMBOL_GPL(gserial_free_line); -int gserial_alloc_line(unsigned char *line_num) +int gserial_alloc_line_no_console(unsigned char *line_num) { struct usb_cdc_line_coding coding; + struct gs_port *port; struct device *tty_dev; int ret; int port_num; @@ -1231,24 +1229,35 @@ int gserial_alloc_line(unsigned char *line_num) /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - tty_dev = tty_port_register_device(&ports[port_num].port->port, + port = ports[port_num].port; + tty_dev = tty_port_register_device(&port->port, gs_tty_driver, port_num, NULL); if (IS_ERR(tty_dev)) { - struct gs_port *port; pr_err("%s: failed to register tty for port %d, err %ld\n", __func__, port_num, PTR_ERR(tty_dev)); ret = PTR_ERR(tty_dev); - port = ports[port_num].port; + mutex_lock(&ports[port_num].lock); ports[port_num].port = NULL; + mutex_unlock(&ports[port_num].lock); gserial_free_port(port); goto err; } *line_num = port_num; - gserial_console_init(); err: return ret; } +EXPORT_SYMBOL_GPL(gserial_alloc_line_no_console); + +int gserial_alloc_line(unsigned char *line_num) +{ + int ret = gserial_alloc_line_no_console(line_num); + + if (!ret && !*line_num) + gs_console_init(ports[*line_num].port); + + return ret; +} EXPORT_SYMBOL_GPL(gserial_alloc_line); /** @@ -1327,7 +1336,7 @@ int gserial_connect(struct gserial *gser, u8 port_num) gser->disconnect(gser); } - status = gs_console_connect(port_num); + status = gs_console_connect(port); spin_unlock_irqrestore(&port->port_lock, flags); return status; @@ -1359,12 +1368,14 @@ void gserial_disconnect(struct gserial *gser) /* tell the TTY glue not to do I/O here any more */ spin_lock_irqsave(&port->port_lock, flags); + gs_console_disconnect(port); + /* REVISIT as above: how best to track this? */ port->port_line_coding = gser->port_line_coding; port->port_usb = NULL; gser->ioport = NULL; - if (port->port.count > 0 || port->openclose) { + if (port->port.count > 0) { wake_up_interruptible(&port->drain_wait); if (port->port.tty) tty_hangup(port->port.tty); @@ -1377,7 +1388,7 @@ void gserial_disconnect(struct gserial *gser) /* finally, free any unused/unusable I/O buffers */ spin_lock_irqsave(&port->port_lock, flags); - if (port->port.count == 0 && !port->openclose) + if (port->port.count == 0) kfifo_free(&port->port_write_buf); gs_free_requests(gser->out, &port->read_pool, NULL); gs_free_requests(gser->out, &port->read_queue, NULL); @@ -1386,7 +1397,6 @@ void gserial_disconnect(struct gserial *gser) port->read_allocated = port->read_started = port->write_allocated = port->write_started = 0; - gs_console_disconnect(gser->in); spin_unlock_irqrestore(&port->port_lock, flags); } EXPORT_SYMBOL_GPL(gserial_disconnect); diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h index 9acaac1cbb75..e5b08ab8cf7a 100644 --- a/drivers/usb/gadget/function/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -54,9 +54,17 @@ struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags); void gs_free_req(struct usb_ep *, struct usb_request *req); /* management of individual TTY ports */ +int gserial_alloc_line_no_console(unsigned char *port_line); int gserial_alloc_line(unsigned char *port_line); void gserial_free_line(unsigned char port_line); +#ifdef CONFIG_U_SERIAL_CONSOLE + +ssize_t gserial_set_console(unsigned char port_num, const char *page, size_t count); +ssize_t gserial_get_console(unsigned char port_num, char *page); + +#endif /* CONFIG_U_SERIAL_CONSOLE */ + /* connect/disconnect is handled by individual functions */ int gserial_connect(struct gserial *, u8 port_num); void gserial_disconnect(struct gserial *); diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 69ff7f8c86f5..6e7e1a9202e6 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -14,6 +14,32 @@ # both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). # +choice + tristate "USB Gadget precomposed configurations" + default USB_ETH + optional + help + A Linux "Gadget Driver" talks to the USB Peripheral Controller + driver through the abstract "gadget" API. Some other operating + systems call these "client" drivers, of which "class drivers" + are a subset (implementing a USB device class specification). + A gadget driver implements one or more USB functions using + the peripheral hardware. + + Gadget drivers are hardware-neutral, or "platform independent", + except that they sometimes must understand quirks or limitations + of the particular controllers they work with. For example, when + a controller doesn't support alternate configurations or provide + enough of the right types of endpoints, the gadget driver might + not be able work with that controller, or might need to implement + a less common variant of a device class protocol. + + The available choices each represent a single precomposed USB + gadget configuration. In the device model, each option contains + both the device instantiation as a child for a USB gadget + controller, and the relevant drivers for each function declared + by the device. + config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE @@ -149,21 +175,21 @@ config USB_ETH_RNDIS is given in comments found in that info file. config USB_ETH_EEM - bool "Ethernet Emulation Model (EEM) support" - depends on USB_ETH + bool "Ethernet Emulation Model (EEM) support" + depends on USB_ETH select USB_LIBCOMPOSITE select USB_F_EEM - help - CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM - and therefore can be supported by more hardware. Technically ECM and - EEM are designed for different applications. The ECM model extends - the network interface to the target (e.g. a USB cable modem), and the - EEM model is for mobile devices to communicate with hosts using - ethernet over USB. For Linux gadgets, however, the interface with - the host is the same (a usbX device), so the differences are minimal. - - If you say "y" here, the Ethernet gadget driver will use the EEM - protocol rather than ECM. If unsure, say "n". + help + CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM + and therefore can be supported by more hardware. Technically ECM and + EEM are designed for different applications. The ECM model extends + the network interface to the target (e.g. a USB cable modem), and the + EEM model is for mobile devices to communicate with hosts using + ethernet over USB. For Linux gadgets, however, the interface with + the host is the same (a usbX device), so the differences are minimal. + + If you say "y" here, the Ethernet gadget driver will use the EEM + protocol rather than ECM. If unsure, say "n". config USB_G_NCM tristate "Network Control Model (NCM) support" @@ -489,3 +515,5 @@ config USB_G_WEBCAM Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_webcam". + +endchoice diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c index af16672d5118..59be2d8417c9 100644 --- a/drivers/usb/gadget/legacy/acm_ms.c +++ b/drivers/usb/gadget/legacy/acm_ms.c @@ -105,7 +105,6 @@ static struct usb_function *f_msg; */ static int acm_ms_do_config(struct usb_configuration *c) { - struct fsg_opts *opts; int status; if (gadget_is_otg(c->cdev->gadget)) { @@ -113,8 +112,6 @@ static int acm_ms_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - opts = fsg_opts_from_func_inst(fi_msg); - f_acm = usb_get_function(f_acm_inst); if (IS_ERR(f_acm)) return PTR_ERR(f_acm); diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c index da1c37933ca1..8d7a556ece30 100644 --- a/drivers/usb/gadget/legacy/cdc2.c +++ b/drivers/usb/gadget/legacy/cdc2.c @@ -225,7 +225,7 @@ static struct usb_composite_driver cdc_driver = { .name = "g_cdc", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = cdc_bind, .unbind = cdc_unbind, }; diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index b640ed3fcf70..ae6d8f7092b8 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -149,7 +149,7 @@ static struct usb_composite_driver gfs_driver = { .name = DRIVER_NAME, .dev = &gfs_dev_desc, .strings = gfs_dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = gfs_bind, .unbind = gfs_unbind, }; diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index fd5595ac5bf7..f18f77584fc2 100644 --- a/drivers/usb/gadget/legacy/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c @@ -105,7 +105,6 @@ FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); static int msg_do_config(struct usb_configuration *c) { - struct fsg_opts *opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -113,8 +112,6 @@ static int msg_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - opts = fsg_opts_from_func_inst(fi_msg); - f_msg = usb_get_function(fi_msg); if (IS_ERR(f_msg)) return PTR_ERR(f_msg); diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c index 50515f9e1022..ec9749845660 100644 --- a/drivers/usb/gadget/legacy/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -482,7 +482,7 @@ static struct usb_composite_driver multi_driver = { .name = "g_multi", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = multi_bind, .unbind = multi_unbind, .needs_serial = 1, diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c index 8465f081e921..c61e71ba7045 100644 --- a/drivers/usb/gadget/legacy/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -197,7 +197,7 @@ static struct usb_composite_driver ncm_driver = { .name = "g_ncm", .dev = &device_desc, .strings = dev_strings, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .bind = gncm_bind, .unbind = gncm_unbind, }; diff --git a/drivers/usb/gadget/legacy/serial.c b/drivers/usb/gadget/legacy/serial.c index de30d7628eef..da44f89f5e73 100644 --- a/drivers/usb/gadget/legacy/serial.c +++ b/drivers/usb/gadget/legacy/serial.c @@ -97,6 +97,36 @@ static unsigned n_ports = 1; module_param(n_ports, uint, 0); MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); +static bool enable = true; + +static int switch_gserial_enable(bool do_enable); + +static int enable_set(const char *s, const struct kernel_param *kp) +{ + bool do_enable; + int ret; + + if (!s) /* called for no-arg enable == default */ + return 0; + + ret = strtobool(s, &do_enable); + if (ret || enable == do_enable) + return ret; + + ret = switch_gserial_enable(do_enable); + if (!ret) + enable = do_enable; + + return ret; +} + +static const struct kernel_param_ops enable_ops = { + .set = enable_set, + .get = param_get_bool, +}; + +module_param_cb(enable, &enable_ops, &enable, 0644); + /*-------------------------------------------------------------------------*/ static struct usb_configuration serial_config_driver = { @@ -240,6 +270,19 @@ static struct usb_composite_driver gserial_driver = { .unbind = gs_unbind, }; +static int switch_gserial_enable(bool do_enable) +{ + if (!serial_config_driver.label) + /* init() was not called, yet */ + return 0; + + if (do_enable) + return usb_composite_probe(&gserial_driver); + + usb_composite_unregister(&gserial_driver); + return 0; +} + static int __init init(void) { /* We *could* export two configs; that'd be much cleaner... @@ -266,12 +309,16 @@ static int __init init(void) } strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label; + if (!enable) + return 0; + return usb_composite_probe(&gserial_driver); } module_init(init); static void __exit cleanup(void) { - usb_composite_unregister(&gserial_driver); + if (enable) + usb_composite_unregister(&gserial_driver); } module_exit(cleanup); diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index ef0259a950ba..797d6ace8994 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -45,7 +45,8 @@ config USB_AT91 config USB_LPC32XX tristate "LPC32XX USB Peripheral Controller" - depends on ARCH_LPC32XX && I2C + depends on ARCH_LPC32XX || COMPILE_TEST + depends on I2C select USB_ISP1301 help This option selects the USB device controller in the LPC32xx SoC. @@ -119,10 +120,10 @@ config USB_FOTG210_UDC dynamically linked module called "fotg210_udc". config USB_GR_UDC - tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" - depends on HAS_DMA - help - Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB + tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" + depends on HAS_DMA + help + Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB VHDL IP core library. config USB_OMAP @@ -440,6 +441,18 @@ config USB_GADGET_XILINX dynamically linked module called "udc-xilinx" and force all gadget drivers to also be dynamically linked. +config USB_TEGRA_XUDC + tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller" + depends on ARCH_TEGRA || COMPILE_TEST + depends on PHY_TEGRA_XUSB + select USB_ROLE_SWITCH + help + Enables NVIDIA Tegra USB 3.0 device mode controller driver. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "tegra_xudc" and force all + gadget drivers to also be dynamically linked. + source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig" # diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index 897f648f3cf1..f6777e654a8e 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o fsl_usb2_udc-y := fsl_udc_core.o fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o +obj-$(CONFIG_USB_TEGRA_XUDC) += tegra-xudc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_RENESAS_USB3) += renesas_usb3.o diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c index 57b6f66331cf..bfd1c9e80a1f 100644 --- a/drivers/usb/gadget/udc/amd5536udc_pci.c +++ b/drivers/usb/gadget/udc/amd5536udc_pci.c @@ -116,7 +116,7 @@ static int udc_pci_probe( goto err_memreg; } - dev->virt_addr = ioremap_nocache(resource, len); + dev->virt_addr = ioremap(resource, len); if (!dev->virt_addr) { dev_dbg(&pdev->dev, "start address cannot be mapped\n"); retval = -EFAULT; diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index db3628be38c0..90b134d5dca9 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -65,14 +65,16 @@ void ast_vhub_done(struct ast_vhub_ep *ep, struct ast_vhub_req *req, void ast_vhub_nuke(struct ast_vhub_ep *ep, int status) { struct ast_vhub_req *req; - - EPDBG(ep, "Nuking\n"); + int count = 0; /* Beware, lock will be dropped & req-acquired by done() */ while (!list_empty(&ep->queue)) { req = list_first_entry(&ep->queue, struct ast_vhub_req, queue); ast_vhub_done(ep, req, status); + count++; } + if (count) + EPDBG(ep, "Nuked %d request(s)\n", count); } struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep, @@ -348,7 +350,6 @@ static int ast_vhub_probe(struct platform_device *pdev) /* Find interrupt and install handler */ vhub->irq = platform_get_irq(pdev, 0); if (vhub->irq < 0) { - dev_err(&pdev->dev, "Failed to get interrupt\n"); rc = vhub->irq; goto err; } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 6b1b16b17d7d..4008e7a51188 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -50,11 +50,14 @@ void ast_vhub_dev_irq(struct ast_vhub_dev *d) static void ast_vhub_dev_enable(struct ast_vhub_dev *d) { - u32 reg, hmsk; + u32 reg, hmsk, i; if (d->enabled) return; + /* Cleanup EP0 state */ + ast_vhub_reset_ep0(d); + /* Enable device and its EP0 interrupts */ reg = VHUB_DEV_EN_ENABLE_PORT | VHUB_DEV_EN_EP0_IN_ACK_IRQEN | @@ -73,6 +76,19 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d) /* Set EP0 DMA buffer address */ writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA); + /* Clear stall on all EPs */ + for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) { + struct ast_vhub_ep *ep = d->epns[i]; + + if (ep && (ep->epn.stalled || ep->epn.wedged)) { + ep->epn.stalled = false; + ep->epn.wedged = false; + ast_vhub_update_epn_stall(ep); + } + } + + /* Additional cleanups */ + d->wakeup_en = false; d->enabled = true; } @@ -93,7 +109,6 @@ static void ast_vhub_dev_disable(struct ast_vhub_dev *d) writel(0, d->regs + AST_VHUB_DEV_EN_CTRL); d->gadget.speed = USB_SPEED_UNKNOWN; d->enabled = false; - d->suspended = false; } static int ast_vhub_dev_feature(struct ast_vhub_dev *d, @@ -201,14 +216,19 @@ int ast_vhub_std_dev_request(struct ast_vhub_ep *ep, u16 wValue, wIndex; /* No driver, we shouldn't be enabled ... */ - if (!d->driver || !d->enabled || d->suspended) { + if (!d->driver || !d->enabled) { EPDBG(ep, - "Device is wrong state driver=%p enabled=%d" - " suspended=%d\n", - d->driver, d->enabled, d->suspended); + "Device is wrong state driver=%p enabled=%d\n", + d->driver, d->enabled); return std_req_stall; } + /* + * Note: we used to reject/stall requests while suspended, + * we don't do that anymore as we seem to have cases of + * mass storage getting very upset. + */ + /* First packet, grab speed */ if (d->gadget.speed == USB_SPEED_UNKNOWN) { d->gadget.speed = ep->vhub->speed; @@ -449,8 +469,7 @@ static const struct usb_gadget_ops ast_vhub_udc_ops = { void ast_vhub_dev_suspend(struct ast_vhub_dev *d) { - d->suspended = true; - if (d->driver) { + if (d->driver && d->driver->suspend) { spin_unlock(&d->vhub->lock); d->driver->suspend(&d->gadget); spin_lock(&d->vhub->lock); @@ -459,8 +478,7 @@ void ast_vhub_dev_suspend(struct ast_vhub_dev *d) void ast_vhub_dev_resume(struct ast_vhub_dev *d) { - d->suspended = false; - if (d->driver) { + if (d->driver && d->driver->resume) { spin_unlock(&d->vhub->lock); d->driver->resume(&d->gadget); spin_lock(&d->vhub->lock); @@ -469,46 +487,28 @@ void ast_vhub_dev_resume(struct ast_vhub_dev *d) void ast_vhub_dev_reset(struct ast_vhub_dev *d) { - /* - * If speed is not set, we enable the port. If it is, - * send reset to the gadget and reset "speed". - * - * Speed is an indication that we have got the first - * setup packet to the device. - */ - if (d->gadget.speed == USB_SPEED_UNKNOWN && !d->enabled) { - DDBG(d, "Reset at unknown speed of disabled device, enabling...\n"); - ast_vhub_dev_enable(d); - d->suspended = false; + /* No driver, just disable the device and return */ + if (!d->driver) { + ast_vhub_dev_disable(d); + return; } - if (d->gadget.speed != USB_SPEED_UNKNOWN && d->driver) { - unsigned int i; - DDBG(d, "Reset at known speed of bound device, resetting...\n"); + /* If the port isn't enabled, just enable it */ + if (!d->enabled) { + DDBG(d, "Reset of disabled device, enabling...\n"); + ast_vhub_dev_enable(d); + } else { + DDBG(d, "Reset of enabled device, resetting...\n"); spin_unlock(&d->vhub->lock); - d->driver->reset(&d->gadget); + usb_gadget_udc_reset(&d->gadget, d->driver); spin_lock(&d->vhub->lock); /* - * Disable/re-enable HW, this will clear the address + * Disable and maybe re-enable HW, this will clear the address * and speed setting. */ ast_vhub_dev_disable(d); ast_vhub_dev_enable(d); - - /* Clear stall on all EPs */ - for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) { - struct ast_vhub_ep *ep = d->epns[i]; - - if (ep && ep->epn.stalled) { - ep->epn.stalled = false; - ast_vhub_update_epn_stall(ep); - } - } - - /* Additional cleanups */ - d->wakeup_en = false; - d->suspended = false; } } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c index e2927fb083cf..022b777b85f8 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c @@ -105,18 +105,20 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep) (crq.bRequestType & USB_DIR_IN) ? "in" : "out", ep->ep0.state); - /* Check our state, cancel pending requests if needed */ - if (ep->ep0.state != ep0_state_token) { + /* + * Check our state, cancel pending requests if needed + * + * Note: Under some circumstances, we can get a new setup + * packet while waiting for the stall ack, just accept it. + * + * In any case, a SETUP packet in wrong state should have + * reset the HW state machine, so let's just log, nuke + * requests, move on. + */ + if (ep->ep0.state != ep0_state_token && + ep->ep0.state != ep0_state_stall) { EPDBG(ep, "wrong state\n"); ast_vhub_nuke(ep, -EIO); - - /* - * Accept the packet regardless, this seems to happen - * when stalling a SETUP packet that has an OUT data - * phase. - */ - ast_vhub_nuke(ep, 0); - goto stall; } /* Calculate next state for EP0 */ @@ -165,7 +167,7 @@ void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep) stall: EPDBG(ep, "stalling\n"); writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat); - ep->ep0.state = ep0_state_status; + ep->ep0.state = ep0_state_stall; ep->ep0.dir_in = false; return; @@ -299,8 +301,8 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack) if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) || (!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) || (ep->ep0.dir_in != in_ack)) { + /* In that case, ignore interrupt */ dev_warn(dev, "irq state mismatch"); - stall = true; break; } /* @@ -335,12 +337,22 @@ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack) dev_warn(dev, "status direction mismatch\n"); stall = true; } + break; + case ep0_state_stall: + /* + * There shouldn't be any request left, but nuke just in case + * otherwise the stale request will block subsequent ones + */ + ast_vhub_nuke(ep, -EIO); + break; } - /* Reset to token state */ - ep->ep0.state = ep0_state_token; - if (stall) + /* Reset to token state or stall */ + if (stall) { writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat); + ep->ep0.state = ep0_state_stall; + } else + ep->ep0.state = ep0_state_token; } static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req, @@ -367,7 +379,7 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req, return -EINVAL; /* Disabled device */ - if (ep->dev && (!ep->dev->enabled || ep->dev->suspended)) + if (ep->dev && !ep->dev->enabled) return -ESHUTDOWN; /* Data, no buffer and not internal ? */ @@ -390,8 +402,12 @@ static int ast_vhub_ep0_queue(struct usb_ep* u_ep, struct usb_request *u_req, spin_lock_irqsave(&vhub->lock, flags); /* EP0 can only support a single request at a time */ - if (!list_empty(&ep->queue) || ep->ep0.state == ep0_state_token) { + if (!list_empty(&ep->queue) || + ep->ep0.state == ep0_state_token || + ep->ep0.state == ep0_state_stall) { dev_warn(dev, "EP0: Request in wrong state\n"); + EPVDBG(ep, "EP0: list_empty=%d state=%d\n", + list_empty(&ep->queue), ep->ep0.state); spin_unlock_irqrestore(&vhub->lock, flags); return -EBUSY; } @@ -459,6 +475,15 @@ static const struct usb_ep_ops ast_vhub_ep0_ops = { .free_request = ast_vhub_free_request, }; +void ast_vhub_reset_ep0(struct ast_vhub_dev *dev) +{ + struct ast_vhub_ep *ep = &dev->ep0; + + ast_vhub_nuke(ep, -EIO); + ep->ep0.state = ep0_state_token; +} + + void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep, struct ast_vhub_dev *dev) { diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c index 35941dc125f9..7475c74aa5c5 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c @@ -352,7 +352,7 @@ static int ast_vhub_epn_queue(struct usb_ep* u_ep, struct usb_request *u_req, /* Endpoint enabled ? */ if (!ep->epn.enabled || !u_ep->desc || !ep->dev || !ep->d_idx || - !ep->dev->enabled || ep->dev->suspended) { + !ep->dev->enabled) { EPDBG(ep, "Enqueuing request on wrong or disabled EP\n"); return -ESHUTDOWN; } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index 7c040f56100e..19b3517e04c0 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -449,8 +449,15 @@ static void ast_vhub_change_port_stat(struct ast_vhub *vhub, USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_L1; - p->change |= chg; + /* + * We only set USB_PORT_STAT_C_ENABLE if we are disabling + * the port as per USB spec, otherwise MacOS gets upset + */ + if (p->status & USB_PORT_STAT_ENABLE) + chg &= ~USB_PORT_STAT_C_ENABLE; + + p->change = chg; ast_vhub_update_hub_ep1(vhub, port); } } @@ -723,6 +730,12 @@ enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep, case ClearPortFeature: EPDBG(ep, "ClearPortFeature(%d,%d)\n", wIndex & 0xf, wValue); return ast_vhub_clr_port_feature(ep, wIndex & 0xf, wValue); + case ClearTTBuffer: + case ResetTT: + case StopTT: + return std_req_complete; + case GetTTState: + return ast_vhub_simple_reply(ep, 0, 0, 0, 0); default: EPDBG(ep, "Unknown class request\n"); } diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h index 4ed03d33a5a9..761919e220d3 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h @@ -257,6 +257,7 @@ enum ep0_state { ep0_state_token, ep0_state_data, ep0_state_status, + ep0_state_stall, }; /* @@ -353,7 +354,6 @@ struct ast_vhub_dev { struct usb_gadget_driver *driver; bool registered : 1; bool wakeup_en : 1; - bool suspended : 1; bool enabled : 1; /* Endpoint structures */ @@ -507,6 +507,7 @@ void ast_vhub_init_hw(struct ast_vhub *vhub); /* ep0.c */ void ast_vhub_ep0_handle_ack(struct ast_vhub_ep *ep, bool in_ack); void ast_vhub_ep0_handle_setup(struct ast_vhub_ep *ep); +void ast_vhub_reset_ep0(struct ast_vhub_dev *dev); void ast_vhub_init_ep0(struct ast_vhub *vhub, struct ast_vhub_ep *ep, struct ast_vhub_dev *dev); int ast_vhub_reply(struct ast_vhub_ep *ep, char *ptr, int len); diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 194ffb1ed462..1b2b548c59a0 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1808,7 +1808,6 @@ static int at91udc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct at91_udc *udc; int retval; - struct resource *res; struct at91_ep *ep; int i; @@ -1839,8 +1838,7 @@ static int at91udc_probe(struct platform_device *pdev) ep->is_pingpong = 1; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->udp_baseaddr = devm_ioremap_resource(dev, res); + udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->udp_baseaddr)) return PTR_ERR(udc->udp_baseaddr); diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 503d275bc4c4..6e0432141c40 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/ctype.h> +#include <linux/usb.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/delay.h> @@ -226,7 +227,7 @@ static void usba_init_debugfs(struct usba_udc *udc) struct dentry *root; struct resource *regs_resource; - root = debugfs_create_dir(udc->gadget.name, NULL); + root = debugfs_create_dir(udc->gadget.name, usb_debug_root); udc->debugfs_root = root; regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM, @@ -327,6 +328,7 @@ static int usba_config_fifo_table(struct usba_udc *udc) switch (fifo_mode) { default: fifo_mode = 0; + /* fall through */ case 0: udc->fifo_cfg = NULL; n = 0; @@ -448,9 +450,11 @@ static void submit_request(struct usba_ep *ep, struct usba_request *req) next_fifo_transaction(ep, req); if (req->last_transaction) { usba_ep_writel(ep, CTL_DIS, USBA_TX_PK_RDY); - usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); + if (ep_is_control(ep)) + usba_ep_writel(ep, CTL_ENB, USBA_TX_COMPLETE); } else { - usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); + if (ep_is_control(ep)) + usba_ep_writel(ep, CTL_DIS, USBA_TX_COMPLETE); usba_ep_writel(ep, CTL_ENB, USBA_TX_PK_RDY); } } @@ -1118,7 +1122,7 @@ static struct usb_endpoint_descriptor usba_ep0_desc = { .bInterval = 1, }; -static struct usb_gadget usba_gadget_template = { +static const struct usb_gadget usba_gadget_template = { .ops = &usba_udc_ops, .max_speed = USB_SPEED_HIGH, .name = "atmel_usba_udc", diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index c1fcc77403ea..54501814dc3f 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2248,7 +2248,7 @@ static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc) if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS)) return; - root = debugfs_create_dir(udc->gadget.name, NULL); + root = debugfs_create_dir(udc->gadget.name, usb_debug_root); udc->debugfs_root = root; debugfs_create_file("usbd", 0400, root, udc, &bcm63xx_usbd_dbg_fops); @@ -2282,7 +2282,6 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev); struct bcm63xx_udc *udc; - struct resource *res; int rc = -ENOMEM, i, irq; udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL); @@ -2298,13 +2297,11 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->usbd_regs = devm_ioremap_resource(dev, res); + udc->usbd_regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->usbd_regs)) return PTR_ERR(udc->usbd_regs); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - udc->iudma_regs = devm_ioremap_resource(dev, res); + udc->iudma_regs = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(udc->iudma_regs)) return PTR_ERR(udc->iudma_regs); @@ -2328,10 +2325,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) /* IRQ resource #0: control interrupt (VBUS, speed, etc.) */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "missing IRQ resource #0\n"); + if (irq < 0) goto out_uninit; - } if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0, dev_name(dev), udc) < 0) goto report_request_failure; @@ -2339,10 +2334,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) /* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) { irq = platform_get_irq(pdev, i + 1); - if (irq < 0) { - dev_err(dev, "missing IRQ resource #%d\n", i + 1); + if (irq < 0) goto out_uninit; - } if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0, dev_name(dev), &udc->iudma[i]) < 0) goto report_request_failure; diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index ccbd1d34eb2a..02a3a774670b 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -480,7 +480,6 @@ static void bdc_phy_exit(struct bdc *bdc) static int bdc_probe(struct platform_device *pdev) { struct bdc *bdc; - struct resource *res; int ret = -ENOMEM; int irq; u32 temp; @@ -508,17 +507,14 @@ static int bdc_probe(struct platform_device *pdev) bdc->clk = clk; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bdc->regs = devm_ioremap_resource(dev, res); + bdc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(bdc->regs)) { dev_err(dev, "ioremap error\n"); return -ENOMEM; } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "platform_get_irq failed:%d\n", irq); + if (irq < 0) return irq; - } spin_lock_init(&bdc->lock); platform_set_drvdata(pdev, bdc); bdc->irq = irq; diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index 7bfd58c846f7..248426a3e88a 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -195,7 +195,7 @@ static void handle_link_state_change(struct bdc *bdc, u32 uspc) break; case BDC_LINK_STATE_U0: if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) { - bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED; + bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED; if (bdc->gadget.speed == USB_SPEED_SUPER) { bdc_function_wake_fh(bdc, 0); bdc->devstatus |= FUNC_WAKE_ISSUED; diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 7cf34beb50df..9b11046480fe 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -98,6 +98,17 @@ int usb_ep_enable(struct usb_ep *ep) if (ep->enabled) goto out; + /* UDC drivers can't handle endpoints with maxpacket size 0 */ + if (usb_endpoint_maxp(ep->desc) == 0) { + /* + * We should log an error message here, but we can't call + * dev_err() because there's no way to find the gadget + * given only ep. + */ + ret = -EINVAL; + goto out; + } + ret = ep->ops->enable(ep, ep->desc); if (ret) goto out; @@ -1143,7 +1154,7 @@ static int check_pending_gadget_drivers(struct usb_udc *udc) dev_name(&udc->dev)) == 0) { ret = udc_bind_to_driver(udc, driver); if (ret != -EPROBE_DEFER) - list_del(&driver->pending); + list_del_init(&driver->pending); break; } @@ -1403,6 +1414,8 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver) } mutex_unlock(&udc_lock); + if (ret) + pr_warn("udc-core: couldn't find an available UDC or it's busy\n"); return ret; found: ret = udc_bind_to_driver(udc, driver); diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 8414fac74493..4c9d1e49d5ed 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -48,6 +48,7 @@ #define DRIVER_VERSION "02 May 2005" #define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */ +#define POWER_BUDGET_3 900 /* in mA */ static const char driver_name[] = "dummy_hcd"; static const char driver_desc[] = "USB Host+Gadget Emulator"; @@ -1320,7 +1321,7 @@ static int dummy_perform_transfer(struct urb *urb, struct dummy_request *req, u32 this_sg; bool next_sg; - to_host = usb_pipein(urb->pipe); + to_host = usb_urb_dir_in(urb); rbuf = req->req.buf + req->req.actual; if (!urb->num_sgs) { @@ -1408,7 +1409,7 @@ top: /* FIXME update emulated data toggle too */ - to_host = usb_pipein(urb->pipe); + to_host = usb_urb_dir_in(urb); if (unlikely(len == 0)) is_short = 1; else { @@ -1829,7 +1830,7 @@ restart: /* find the gadget's ep for this request (if configured) */ address = usb_pipeendpoint (urb->pipe); - if (usb_pipein(urb->pipe)) + if (usb_urb_dir_in(urb)) address |= USB_DIR_IN; ep = find_endpoint(dum, address); if (!ep) { @@ -2384,7 +2385,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) s = "?"; break; } s; }), - ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "", + ep, ep ? (usb_urb_dir_in(urb) ? "in" : "out") : "", ({ char *s; \ switch (usb_pipetype(urb->pipe)) { \ case PIPE_CONTROL: \ @@ -2432,7 +2433,7 @@ static int dummy_start_ss(struct dummy_hcd *dum_hcd) dum_hcd->rh_state = DUMMY_RH_RUNNING; dum_hcd->stream_en_ep = 0; INIT_LIST_HEAD(&dum_hcd->urbp_list); - dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET; + dummy_hcd_to_hcd(dum_hcd)->power_budget = POWER_BUDGET_3; dummy_hcd_to_hcd(dum_hcd)->state = HC_STATE_RUNNING; dummy_hcd_to_hcd(dum_hcd)->uses_new_polling = 1; #ifdef CONFIG_USB_OTG @@ -2724,7 +2725,7 @@ static struct platform_driver dummy_hcd_driver = { }; /*-------------------------------------------------------------------------*/ -#define MAX_NUM_UDC 2 +#define MAX_NUM_UDC 32 static struct platform_device *the_udc_pdev[MAX_NUM_UDC]; static struct platform_device *the_hcd_pdev[MAX_NUM_UDC]; diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h index 2c537a904ee7..53ca0ff7c2cb 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.h +++ b/drivers/usb/gadget/udc/fsl_qe_udc.h @@ -333,8 +333,8 @@ struct qe_udc { u32 resume_state; /* USB state to resume*/ u32 usb_state; /* USB current state */ u32 usb_next_state; /* USB next state */ - u32 ep0_state; /* Enpoint zero state */ - u32 ep0_dir; /* Enpoint zero direction: can be + u32 ep0_state; /* Endpoint zero state */ + u32 ep0_dir; /* Endpoint zero direction: can be USB_DIR_IN or USB_DIR_OUT*/ u32 usb_sof_count; /* SOF count */ u32 errors; /* USB ERRORs count */ diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 20141c3096f6..ec6eda426223 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1052,10 +1052,11 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep) u32 bitmask; struct ep_queue_head *qh; - ep = container_of(_ep, struct fsl_ep, ep); - if (!_ep || (!ep->ep.desc && ep_index(ep) != 0)) + if (!_ep || _ep->desc || !(_ep->desc->bEndpointAddress&0xF)) return -ENODEV; + ep = container_of(_ep, struct fsl_ep, ep); + udc = (struct fsl_udc *)ep->udc; if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) @@ -1208,7 +1209,7 @@ static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA) } /* Change Data+ pullup status - * this func is used by usb_gadget_connect/disconnet + * this func is used by usb_gadget_connect/disconnect */ static int fsl_pullup(struct usb_gadget *gadget, int is_on) { @@ -1595,14 +1596,13 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, struct fsl_req *curr_req) { struct ep_td_struct *curr_td; - int td_complete, actual, remaining_length, j, tmp; + int actual, remaining_length, j, tmp; int status = 0; int errors = 0; struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; int direction = pipe % 2; curr_td = curr_req->head; - td_complete = 0; actual = curr_req->req.length; for (j = 0; j < curr_req->dtd_count; j++) { @@ -1647,11 +1647,9 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, status = -EPROTO; break; } else { - td_complete++; break; } } else { - td_complete++; VDBG("dTD transmitted successful"); } @@ -2576,7 +2574,7 @@ static int fsl_udc_remove(struct platform_device *pdev) dma_pool_destroy(udc_controller->td_pool); free_irq(udc_controller->irq, udc_controller); iounmap(dr_regs); - if (pdata->operating_mode == FSL_USB2_DR_DEVICE) + if (res && (pdata->operating_mode == FSL_USB2_DR_DEVICE)) release_mem_region(res->start, resource_size(res)); /* free udc --wait for the release() finished */ diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index c3721225b61e..4a46f661d0e4 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1782,7 +1782,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) } dev->got_region = 1; - base = ioremap_nocache(resource, len); + base = ioremap(resource, len); if (base == NULL) { DBG(dev, "can't map memory\n"); retval = -EFAULT; diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 729e60e49564..aaf975c809bf 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -29,6 +29,7 @@ #include <linux/list.h> #include <linux/interrupt.h> #include <linux/device.h> +#include <linux/usb.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/dma-mapping.h> @@ -208,7 +209,7 @@ static void gr_dfs_create(struct gr_udc *dev) { const char *name = "gr_udc_state"; - dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL); + dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), usb_debug_root); debugfs_create_file(name, 0444, dev->dfs_root, dev, &gr_dfs_fops); } @@ -2118,7 +2119,6 @@ static int gr_request_irq(struct gr_udc *dev, int irq) static int gr_probe(struct platform_device *pdev) { struct gr_udc *dev; - struct resource *res; struct gr_regs __iomem *regs; int retval; u32 status; @@ -2128,25 +2128,20 @@ static int gr_probe(struct platform_device *pdev) return -ENOMEM; dev->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(dev->dev, res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); dev->irq = platform_get_irq(pdev, 0); - if (dev->irq <= 0) { - dev_err(dev->dev, "No irq found\n"); + if (dev->irq <= 0) return -ENODEV; - } /* Some core configurations has separate irqs for IN and OUT events */ dev->irqi = platform_get_irq(pdev, 1); if (dev->irqi > 0) { dev->irqo = platform_get_irq(pdev, 2); - if (dev->irqo <= 0) { - dev_err(dev->dev, "Found irqi but not irqo\n"); + if (dev->irqo <= 0) return -ENODEV; - } } else { dev->irqi = 0; } @@ -2180,8 +2175,6 @@ static int gr_probe(struct platform_device *pdev) return -ENOMEM; } - spin_lock(&dev->lock); - /* Inside lock so that no gadget can use this udc until probe is done */ retval = usb_add_gadget_udc(dev->dev, &dev->gadget); if (retval) { @@ -2190,15 +2183,21 @@ static int gr_probe(struct platform_device *pdev) } dev->added = 1; + spin_lock(&dev->lock); + retval = gr_udc_init(dev); - if (retval) + if (retval) { + spin_unlock(&dev->lock); goto out; - - gr_dfs_create(dev); + } /* Clear all interrupt enables that might be left on since last boot */ gr_disable_interrupts_and_pullup(dev); + spin_unlock(&dev->lock); + + gr_dfs_create(dev); + retval = gr_request_irq(dev, dev->irq); if (retval) { dev_err(dev->dev, "Failed to request irq %d\n", dev->irq); @@ -2227,8 +2226,6 @@ static int gr_probe(struct platform_device *pdev) dev_info(dev->dev, "regs: %p, irq %d\n", dev->regs, dev->irq); out: - spin_unlock(&dev->lock); - if (retval) gr_remove(pdev); diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 5f1b14f3e5a0..d14b2bb3f67c 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/prefetch.h> #include <linux/proc_fs.h> #include <linux/slab.h> #include <linux/usb/ch9.h> @@ -35,8 +36,6 @@ #include <linux/seq_file.h> #endif -#include <mach/hardware.h> - /* * USB device configuration structure */ @@ -742,7 +741,6 @@ static inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd, * response data */ static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd) { - u32 tmp; int to = 1000; /* Write a command and read data from the protocol engine */ @@ -752,7 +750,6 @@ static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd) /* Write command code */ udc_protocol_cmd_w(udc, cmd); - tmp = readl(USBD_DEVINTST(udc->udp_baseaddr)); while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL)) && (to > 0)) to--; @@ -1154,7 +1151,7 @@ static void udc_pop_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) u32 *p32, tmp, cbytes; /* Use optimal data transfer method based on source address and size */ - switch (((u32) data) & 0x3) { + switch (((uintptr_t) data) & 0x3) { case 0: /* 32-bit aligned */ p32 = (u32 *) data; cbytes = (bytes & ~0x3); @@ -1180,11 +1177,11 @@ static void udc_pop_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); bl = bytes - n; - if (bl > 3) - bl = 3; + if (bl > 4) + bl = 4; for (i = 0; i < bl; i++) - data[n + i] = (u8) ((tmp >> (n * 8)) & 0xFF); + data[n + i] = (u8) ((tmp >> (i * 8)) & 0xFF); } break; @@ -1255,7 +1252,7 @@ static void udc_stuff_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) u32 *p32, tmp, cbytes; /* Use optimal data transfer method based on source address and size */ - switch (((u32) data) & 0x3) { + switch (((uintptr_t) data) & 0x3) { case 0: /* 32-bit aligned */ p32 = (u32 *) data; cbytes = (bytes & ~0x3); @@ -1992,7 +1989,7 @@ void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) /* DMA end of transfer completion */ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) { - u32 status, epstatus; + u32 status; struct lpc32xx_request *req; struct lpc32xx_usbd_dd_gad *dd; @@ -2086,7 +2083,7 @@ static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) { udc_clearep_getsts(udc, ep->hwep_num); uda_enable_hwepint(udc, ep->hwep_num); - epstatus = udc_clearep_getsts(udc, ep->hwep_num); + udc_clearep_getsts(udc, ep->hwep_num); /* Let the EP interrupt handle the ZLP */ return; @@ -2198,7 +2195,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) struct lpc32xx_ep *ep, *ep0 = &udc->ep[0]; struct usb_ctrlrequest ctrlpkt; int i, bytes; - u16 wIndex, wValue, wLength, reqtype, req, tmp; + u16 wIndex, wValue, reqtype, req, tmp; /* Nuke previous transfers */ nuke(ep0, -EPROTO); @@ -2214,7 +2211,6 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) /* Native endianness */ wIndex = le16_to_cpu(ctrlpkt.wIndex); wValue = le16_to_cpu(ctrlpkt.wValue); - wLength = le16_to_cpu(ctrlpkt.wLength); reqtype = le16_to_cpu(ctrlpkt.bRequestType); /* Set direction of EP0 */ @@ -2265,7 +2261,7 @@ static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) default: break; } - + break; case USB_REQ_SET_ADDRESS: if (reqtype == (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) { @@ -3004,7 +3000,6 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct lpc32xx_udc *udc; int retval, i; - struct resource *res; dma_addr_t dma_handle; struct device_node *isp1301_node; @@ -3052,23 +3047,17 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) * IORESOURCE_IRQ, USB device interrupt number * IORESOURCE_IRQ, USB transceiver interrupt number */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; spin_lock_init(&udc->lock); /* Get IRQs */ for (i = 0; i < 4; i++) { udc->udp_irq[i] = platform_get_irq(pdev, i); - if (udc->udp_irq[i] < 0) { - dev_err(udc->dev, - "irq resource %d not available!\n", i); + if (udc->udp_irq[i] < 0) return udc->udp_irq[i]; - } } - udc->udp_baseaddr = devm_ioremap_resource(dev, res); + udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->udp_baseaddr)) { dev_err(udc->dev, "IO map failure\n"); return PTR_ERR(udc->udp_baseaddr); diff --git a/drivers/usb/gadget/udc/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h index 982625b7197a..66b84f792f64 100644 --- a/drivers/usb/gadget/udc/mv_u3d.h +++ b/drivers/usb/gadget/udc/mv_u3d.h @@ -138,7 +138,7 @@ struct mv_u3d_op_regs { u32 doorbell; /* doorbell register */ }; -/* control enpoint enable registers */ +/* control endpoint enable registers */ struct epxcr { u32 epxoutcr0; /* ep out control 0 register */ u32 epxoutcr1; /* ep out control 1 register */ diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 247de0faaeb7..a8273b589456 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -2323,7 +2323,7 @@ net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev) goto err; } - mem_mapped_addr[i] = ioremap_nocache(resource, len); + mem_mapped_addr[i] = ioremap(resource, len); if (mem_mapped_addr[i] == NULL) { release_mem_region(resource, len); dev_dbg(dev->dev, "can't map memory\n"); @@ -2401,7 +2401,7 @@ net2272_rdk2_probe(struct pci_dev *pdev, struct net2272 *dev) goto err; } - mem_mapped_addr[i] = ioremap_nocache(resource, len); + mem_mapped_addr[i] = ioremap(resource, len); if (mem_mapped_addr[i] == NULL) { release_mem_region(resource, len); dev_dbg(dev->dev, "can't map memory\n"); @@ -2625,7 +2625,7 @@ net2272_plat_probe(struct platform_device *pdev) ret = -EBUSY; goto err; } - dev->base_addr = ioremap_nocache(base, len); + dev->base_addr = ioremap(base, len); if (!dev->base_addr) { dev_dbg(dev->dev, "can't map memory\n"); ret = -EFAULT; diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index b6bbe2e448ba..1fd1b9186e46 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2244,30 +2244,40 @@ static void usb_reinit_338x(struct net2280 *dev) } /* Hardware Defect and Workaround */ - val = readl(&dev->ll_lfps_regs->ll_lfps_5); + val = readl(&dev->llregs->ll_lfps_5); val &= ~(0xf << TIMER_LFPS_6US); val |= 0x5 << TIMER_LFPS_6US; - writel(val, &dev->ll_lfps_regs->ll_lfps_5); + writel(val, &dev->llregs->ll_lfps_5); - val = readl(&dev->ll_lfps_regs->ll_lfps_6); + val = readl(&dev->llregs->ll_lfps_6); val &= ~(0xffff << TIMER_LFPS_80US); val |= 0x0100 << TIMER_LFPS_80US; - writel(val, &dev->ll_lfps_regs->ll_lfps_6); + writel(val, &dev->llregs->ll_lfps_6); /* * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB * Hot Reset Exit Handshake may Fail in Specific Case using * Default Register Settings. Workaround for Enumeration test. */ - val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2); + val = readl(&dev->llregs->ll_tsn_counters_2); val &= ~(0x1f << HOT_TX_NORESET_TS2); val |= 0x10 << HOT_TX_NORESET_TS2; - writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2); + writel(val, &dev->llregs->ll_tsn_counters_2); - val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3); + val = readl(&dev->llregs->ll_tsn_counters_3); val &= ~(0x1f << HOT_RX_RESET_TS2); val |= 0x3 << HOT_RX_RESET_TS2; - writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3); + writel(val, &dev->llregs->ll_tsn_counters_3); + + /* + * AB errata. Errata 11. Workaround for Default Duration of LFPS + * Handshake Signaling for Device-Initiated U1 Exit is too short. + * Without this, various enumeration failures observed with + * modern superspeed hosts. + */ + val = readl(&dev->llregs->ll_lfps_timers_2); + writel((val & 0xffff0000) | LFPS_TIMERS_2_WORKAROUND_VALUE, + &dev->llregs->ll_lfps_timers_2); /* * Set Recovery Idle to Recover bit: @@ -2276,10 +2286,10 @@ static void usb_reinit_338x(struct net2280 *dev) * - It is safe to set for all connection speeds; all chip revisions. * - R-M-W to leave other bits undisturbed. * - Reference PLX TT-7372 - */ - val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit); + */ + val = readl(&dev->llregs->ll_tsn_chicken_bit); val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW); - writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit); + writel(val, &dev->llregs->ll_tsn_chicken_bit); INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); @@ -3649,7 +3659,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) * 8051 code into the chip, e.g. to turn on PCI PM. */ - base = ioremap_nocache(resource, len); + base = ioremap(resource, len); if (base == NULL) { ep_dbg(dev, "can't map memory\n"); retval = -EFAULT; @@ -3669,12 +3679,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) (base + 0x00b4); dev->llregs = (struct usb338x_ll_regs __iomem *) (base + 0x0700); - dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) - (base + 0x0748); - dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *) - (base + 0x077c); - dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *) - (base + 0x079c); dev->plregs = (struct usb338x_pl_regs __iomem *) (base + 0x0800); usbstat = readl(&dev->usb->usbstat); diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index b65a797544d7..85d3ca1698ba 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -178,9 +178,6 @@ struct net2280 { struct net2280_dep_regs __iomem *dep; struct net2280_ep_regs __iomem *epregs; struct usb338x_ll_regs __iomem *llregs; - struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; - struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; - struct usb338x_ll_chi_regs __iomem *ll_chicken_reg; struct usb338x_pl_regs __iomem *plregs; struct dma_pool *requests; diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index f36f0730afab..bd12417996db 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -2757,7 +2757,7 @@ static int omap_udc_probe(struct platform_device *pdev) /* NOTE: "knows" the order of the resources! */ if (!request_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1, + resource_size(&pdev->resource[0]), driver_name)) { DBG("request_mem_region failed\n"); return -EBUSY; @@ -2934,7 +2934,7 @@ cleanup0: } release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + resource_size(&pdev->resource[0])); return status; } @@ -2950,7 +2950,7 @@ static int omap_udc_remove(struct platform_device *pdev) wait_for_completion(&done); release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + resource_size(&pdev->resource[0])); return 0; } diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index cded51f36fc1..3344fb8c4181 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1519,7 +1519,6 @@ static void pch_udc_free_dma_chain(struct pch_udc_dev *dev, td = phys_to_virt(addr); addr2 = (dma_addr_t)td->next; dma_pool_free(dev->data_requests, td, addr); - td->next = 0x00; addr = addr2; } req->chain_len = 1; @@ -3046,8 +3045,7 @@ static void pch_udc_remove(struct pci_dev *pdev) #ifdef CONFIG_PM_SLEEP static int pch_udc_suspend(struct device *d) { - struct pci_dev *pdev = to_pci_dev(d); - struct pch_udc_dev *dev = pci_get_drvdata(pdev); + struct pch_udc_dev *dev = dev_get_drvdata(d); pch_udc_disable_interrupts(dev, UDC_DEVINT_MSK); pch_udc_disable_ep_interrupts(dev, UDC_EPINT_MSK_DISABLE_ALL); diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index d4be53559f2e..cfafdd92c2a8 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -2321,7 +2321,6 @@ static int pxa25x_udc_probe(struct platform_device *pdev) struct pxa25x_udc *dev = &memory; int retval, irq; u32 chiprev; - struct resource *res; pr_info("%s: version %s\n", driver_name, DRIVER_VERSION); @@ -2367,8 +2366,7 @@ static int pxa25x_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs = devm_ioremap_resource(&pdev->dev, res); + dev->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dev->regs)) return PTR_ERR(dev->regs); diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 014233252299..78902d13fc27 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -207,7 +207,7 @@ static void pxa_init_debugfs(struct pxa_udc *udc) { struct dentry *root; - root = debugfs_create_dir(udc->gadget.name, NULL); + root = debugfs_create_dir(udc->gadget.name, usb_debug_root); udc->debugfs_root = root; debugfs_create_file("udcstate", 0400, root, udc, &state_dbg_fops); @@ -2356,7 +2356,6 @@ MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids); */ static int pxa_udc_probe(struct platform_device *pdev) { - struct resource *regs; struct pxa_udc *udc = &memory; int retval = 0, gpio; struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev); @@ -2378,8 +2377,7 @@ static int pxa_udc_probe(struct platform_device *pdev) udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS); } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - udc->regs = devm_ioremap_resource(&pdev->dev, regs); + udc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->regs)) return PTR_ERR(udc->regs); udc->irq = platform_get_irq(pdev, 0); diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 11e25a3f4f1f..582a16165ea9 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1838,7 +1838,7 @@ static int r8a66597_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; char clk_name[8]; - struct resource *res, *ires; + struct resource *ires; int irq; void __iomem *reg = NULL; struct r8a66597 *r8a66597 = NULL; @@ -1846,8 +1846,7 @@ static int r8a66597_probe(struct platform_device *pdev) int i; unsigned long irq_trigger; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg = devm_ioremap_resource(&pdev->dev, res); + reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg)) return PTR_ERR(reg); diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 1f4c3fbd1df8..c5c3c14df67a 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -775,6 +775,18 @@ static void usb3_irq_epc_int_1_resume(struct renesas_usb3 *usb3) usb3_transition_to_default_state(usb3, false); } +static void usb3_irq_epc_int_1_suspend(struct renesas_usb3 *usb3) +{ + usb3_disable_irq_1(usb3, USB_INT_1_B2_SPND); + + if (usb3->gadget.speed != USB_SPEED_UNKNOWN && + usb3->gadget.state != USB_STATE_NOTATTACHED) { + if (usb3->driver && usb3->driver->suspend) + usb3->driver->suspend(&usb3->gadget); + usb_gadget_set_state(&usb3->gadget, USB_STATE_SUSPENDED); + } +} + static void usb3_irq_epc_int_1_disable(struct renesas_usb3 *usb3) { usb3_stop_usb3_connection(usb3); @@ -860,6 +872,9 @@ static void usb3_irq_epc_int_1(struct renesas_usb3 *usb3, u32 int_sta_1) if (int_sta_1 & USB_INT_1_B2_RSUM) usb3_irq_epc_int_1_resume(usb3); + if (int_sta_1 & USB_INT_1_B2_SPND) + usb3_irq_epc_int_1_suspend(usb3); + if (int_sta_1 & USB_INT_1_SPEED) usb3_irq_epc_int_1_speed(usb3); @@ -1544,10 +1559,10 @@ static void usb3_set_device_address(struct renesas_usb3 *usb3, u16 addr) static bool usb3_std_req_set_address(struct renesas_usb3 *usb3, struct usb_ctrlrequest *ctrl) { - if (ctrl->wValue >= 128) + if (le16_to_cpu(ctrl->wValue) >= 128) return true; /* stall */ - usb3_set_device_address(usb3, ctrl->wValue); + usb3_set_device_address(usb3, le16_to_cpu(ctrl->wValue)); usb3_set_p0_con_for_no_data(usb3); return false; @@ -1582,6 +1597,7 @@ static bool usb3_std_req_get_status(struct renesas_usb3 *usb3, struct renesas_usb3_ep *usb3_ep; int num; u16 status = 0; + __le16 tx_data; switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: @@ -1604,10 +1620,10 @@ static bool usb3_std_req_get_status(struct renesas_usb3 *usb3, } if (!stall) { - status = cpu_to_le16(status); + tx_data = cpu_to_le16(status); dev_dbg(usb3_to_dev(usb3), "get_status: req = %p\n", usb_req_to_usb3_req(usb3->ep0_req)); - usb3_pipe0_internal_xfer(usb3, &status, sizeof(status), + usb3_pipe0_internal_xfer(usb3, &tx_data, sizeof(tx_data), usb3_pipe0_get_status_completion); } @@ -1772,7 +1788,7 @@ static bool usb3_std_req_set_sel(struct renesas_usb3 *usb3, static bool usb3_std_req_set_configuration(struct renesas_usb3 *usb3, struct usb_ctrlrequest *ctrl) { - if (ctrl->wValue > 0) + if (le16_to_cpu(ctrl->wValue) > 0) usb3_set_bit(usb3, USB_COM_CON_CONF, USB3_USB_COM_CON); else usb3_clear_bit(usb3, USB_COM_CON_CONF, USB3_USB_COM_CON); @@ -2535,7 +2551,7 @@ static const struct file_operations renesas_usb3_b_device_fops = { static void renesas_usb3_debugfs_init(struct renesas_usb3 *usb3, struct device *dev) { - usb3->dentry = debugfs_create_dir(dev_name(dev), NULL); + usb3->dentry = debugfs_create_dir(dev_name(dev), usb_debug_root); debugfs_create_file("b_device", 0644, usb3->dentry, usb3, &renesas_usb3_b_device_fops); @@ -2732,7 +2748,6 @@ static struct usb_role_switch_desc renesas_usb3_role_switch_desc = { static int renesas_usb3_probe(struct platform_device *pdev) { struct renesas_usb3 *usb3; - struct resource *res; int irq, ret; const struct renesas_usb3_priv *priv; const struct soc_device_attribute *attr; @@ -2744,17 +2759,14 @@ static int renesas_usb3_probe(struct platform_device *pdev) priv = of_device_get_match_data(&pdev->dev); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq); + if (irq < 0) return irq; - } usb3 = devm_kzalloc(&pdev->dev, sizeof(*usb3), GFP_KERNEL); if (!usb3) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - usb3->reg = devm_ioremap_resource(&pdev->dev, res); + usb3->reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(usb3->reg)) return PTR_ERR(usb3->reg); diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index 31c7c5587cf9..21252fbc0319 100644 --- a/drivers/usb/gadget/udc/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c @@ -1263,7 +1263,6 @@ static const struct usb_gadget_ops s3c_hsudc_gadget_ops = { static int s3c_hsudc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct s3c_hsudc *hsudc; struct s3c24xx_hsudc_platdata *pd = dev_get_platdata(&pdev->dev); int ret, i; @@ -1290,9 +1289,7 @@ static int s3c_hsudc_probe(struct platform_device *pdev) goto err_supplies; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - hsudc->regs = devm_ioremap_resource(&pdev->dev, res); + hsudc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hsudc->regs)) { ret = PTR_ERR(hsudc->regs); goto err_res; @@ -1311,10 +1308,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev) s3c_hsudc_setup_ep(hsudc); ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "unable to obtain IRQ number\n"); + if (ret < 0) goto err_res; - } hsudc->irq = ret; ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0, diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index af3e63316ace..0507a2ca0f55 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -312,6 +312,7 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep, switch (idx) { default: idx = 0; + /* fall through */ case 0: fifo_reg = S3C2410_UDC_EP0_FIFO_REG; break; @@ -416,6 +417,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep, switch (idx) { default: idx = 0; + /* fall through */ case 0: fifo_reg = S3C2410_UDC_EP0_FIFO_REG; break; @@ -1976,7 +1978,8 @@ static int __init udc_init(void) dprintk(DEBUG_NORMAL, "%s\n", gadget_name); - s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL); + s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, + usb_debug_root); retval = platform_driver_register(&udc_driver_24x0); if (retval) diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c new file mode 100644 index 000000000000..634c2c19a176 --- /dev/null +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -0,0 +1,3810 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NVIDIA Tegra XUSB device mode controller + * + * Copyright (c) 2013-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2015, Google Inc. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/phy/tegra/xusb.h> +#include <linux/pm_domain.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/role.h> +#include <linux/workqueue.h> + +/* XUSB_DEV registers */ +#define SPARAM 0x000 +#define SPARAM_ERSTMAX_MASK GENMASK(20, 16) +#define SPARAM_ERSTMAX(x) (((x) << 16) & SPARAM_ERSTMAX_MASK) +#define DB 0x004 +#define DB_TARGET_MASK GENMASK(15, 8) +#define DB_TARGET(x) (((x) << 8) & DB_TARGET_MASK) +#define DB_STREAMID_MASK GENMASK(31, 16) +#define DB_STREAMID(x) (((x) << 16) & DB_STREAMID_MASK) +#define ERSTSZ 0x008 +#define ERSTSZ_ERSTXSZ_SHIFT(x) ((x) * 16) +#define ERSTSZ_ERSTXSZ_MASK GENMASK(15, 0) +#define ERSTXBALO(x) (0x010 + 8 * (x)) +#define ERSTXBAHI(x) (0x014 + 8 * (x)) +#define ERDPLO 0x020 +#define ERDPLO_EHB BIT(3) +#define ERDPHI 0x024 +#define EREPLO 0x028 +#define EREPLO_ECS BIT(0) +#define EREPLO_SEGI BIT(1) +#define EREPHI 0x02c +#define CTRL 0x030 +#define CTRL_RUN BIT(0) +#define CTRL_LSE BIT(1) +#define CTRL_IE BIT(4) +#define CTRL_SMI_EVT BIT(5) +#define CTRL_SMI_DSE BIT(6) +#define CTRL_EWE BIT(7) +#define CTRL_DEVADDR_MASK GENMASK(30, 24) +#define CTRL_DEVADDR(x) (((x) << 24) & CTRL_DEVADDR_MASK) +#define CTRL_ENABLE BIT(31) +#define ST 0x034 +#define ST_RC BIT(0) +#define ST_IP BIT(4) +#define RT_IMOD 0x038 +#define RT_IMOD_IMODI_MASK GENMASK(15, 0) +#define RT_IMOD_IMODI(x) ((x) & RT_IMOD_IMODI_MASK) +#define RT_IMOD_IMODC_MASK GENMASK(31, 16) +#define RT_IMOD_IMODC(x) (((x) << 16) & RT_IMOD_IMODC_MASK) +#define PORTSC 0x03c +#define PORTSC_CCS BIT(0) +#define PORTSC_PED BIT(1) +#define PORTSC_PR BIT(4) +#define PORTSC_PLS_SHIFT 5 +#define PORTSC_PLS_MASK GENMASK(8, 5) +#define PORTSC_PLS_U0 0x0 +#define PORTSC_PLS_U2 0x2 +#define PORTSC_PLS_U3 0x3 +#define PORTSC_PLS_DISABLED 0x4 +#define PORTSC_PLS_RXDETECT 0x5 +#define PORTSC_PLS_INACTIVE 0x6 +#define PORTSC_PLS_RESUME 0xf +#define PORTSC_PLS(x) (((x) << PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK) +#define PORTSC_PS_SHIFT 10 +#define PORTSC_PS_MASK GENMASK(13, 10) +#define PORTSC_PS_UNDEFINED 0x0 +#define PORTSC_PS_FS 0x1 +#define PORTSC_PS_LS 0x2 +#define PORTSC_PS_HS 0x3 +#define PORTSC_PS_SS 0x4 +#define PORTSC_LWS BIT(16) +#define PORTSC_CSC BIT(17) +#define PORTSC_WRC BIT(19) +#define PORTSC_PRC BIT(21) +#define PORTSC_PLC BIT(22) +#define PORTSC_CEC BIT(23) +#define PORTSC_WPR BIT(30) +#define PORTSC_CHANGE_MASK (PORTSC_CSC | PORTSC_WRC | PORTSC_PRC | \ + PORTSC_PLC | PORTSC_CEC) +#define ECPLO 0x040 +#define ECPHI 0x044 +#define MFINDEX 0x048 +#define MFINDEX_FRAME_SHIFT 3 +#define MFINDEX_FRAME_MASK GENMASK(13, 3) +#define PORTPM 0x04c +#define PORTPM_L1S_MASK GENMASK(1, 0) +#define PORTPM_L1S_DROP 0x0 +#define PORTPM_L1S_ACCEPT 0x1 +#define PORTPM_L1S_NYET 0x2 +#define PORTPM_L1S_STALL 0x3 +#define PORTPM_L1S(x) ((x) & PORTPM_L1S_MASK) +#define PORTPM_RWE BIT(3) +#define PORTPM_U2TIMEOUT_MASK GENMASK(15, 8) +#define PORTPM_U1TIMEOUT_MASK GENMASK(23, 16) +#define PORTPM_FLA BIT(24) +#define PORTPM_VBA BIT(25) +#define PORTPM_WOC BIT(26) +#define PORTPM_WOD BIT(27) +#define PORTPM_U1E BIT(28) +#define PORTPM_U2E BIT(29) +#define PORTPM_FRWE BIT(30) +#define PORTPM_PNG_CYA BIT(31) +#define EP_HALT 0x050 +#define EP_PAUSE 0x054 +#define EP_RELOAD 0x058 +#define EP_STCHG 0x05c +#define DEVNOTIF_LO 0x064 +#define DEVNOTIF_LO_TRIG BIT(0) +#define DEVNOTIF_LO_TYPE_MASK GENMASK(7, 4) +#define DEVNOTIF_LO_TYPE(x) (((x) << 4) & DEVNOTIF_LO_TYPE_MASK) +#define DEVNOTIF_LO_TYPE_FUNCTION_WAKE 0x1 +#define DEVNOTIF_HI 0x068 +#define PORTHALT 0x06c +#define PORTHALT_HALT_LTSSM BIT(0) +#define PORTHALT_HALT_REJECT BIT(1) +#define PORTHALT_STCHG_REQ BIT(20) +#define PORTHALT_STCHG_INTR_EN BIT(24) +#define PORT_TM 0x070 +#define EP_THREAD_ACTIVE 0x074 +#define EP_STOPPED 0x078 +#define HSFSPI_COUNT0 0x100 +#define HSFSPI_COUNT13 0x134 +#define HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK GENMASK(29, 0) +#define HSFSPI_COUNT13_U2_RESUME_K_DURATION(x) ((x) & \ + HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK) +#define BLCG 0x840 +#define SSPX_CORE_CNT0 0x610 +#define SSPX_CORE_CNT0_PING_TBURST_MASK GENMASK(7, 0) +#define SSPX_CORE_CNT0_PING_TBURST(x) ((x) & SSPX_CORE_CNT0_PING_TBURST_MASK) +#define SSPX_CORE_CNT30 0x688 +#define SSPX_CORE_CNT30_LMPITP_TIMER_MASK GENMASK(19, 0) +#define SSPX_CORE_CNT30_LMPITP_TIMER(x) ((x) & \ + SSPX_CORE_CNT30_LMPITP_TIMER_MASK) +#define SSPX_CORE_CNT32 0x690 +#define SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK GENMASK(7, 0) +#define SSPX_CORE_CNT32_POLL_TBURST_MAX(x) ((x) & \ + SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK) +#define SSPX_CORE_PADCTL4 0x750 +#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK GENMASK(19, 0) +#define SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(x) ((x) & \ + SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK) +#define BLCG_DFPCI BIT(0) +#define BLCG_UFPCI BIT(1) +#define BLCG_FE BIT(2) +#define BLCG_COREPLL_PWRDN BIT(8) +#define BLCG_IOPLL_0_PWRDN BIT(9) +#define BLCG_IOPLL_1_PWRDN BIT(10) +#define BLCG_IOPLL_2_PWRDN BIT(11) +#define BLCG_ALL 0x1ff +#define CFG_DEV_SSPI_XFER 0x858 +#define CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK GENMASK(31, 0) +#define CFG_DEV_SSPI_XFER_ACKTIMEOUT(x) ((x) & \ + CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK) +#define CFG_DEV_FE 0x85c +#define CFG_DEV_FE_PORTREGSEL_MASK GENMASK(1, 0) +#define CFG_DEV_FE_PORTREGSEL_SS_PI 1 +#define CFG_DEV_FE_PORTREGSEL_HSFS_PI 2 +#define CFG_DEV_FE_PORTREGSEL(x) ((x) & CFG_DEV_FE_PORTREGSEL_MASK) +#define CFG_DEV_FE_INFINITE_SS_RETRY BIT(29) + +/* FPCI registers */ +#define XUSB_DEV_CFG_1 0x004 +#define XUSB_DEV_CFG_1_IO_SPACE_EN BIT(0) +#define XUSB_DEV_CFG_1_MEMORY_SPACE_EN BIT(1) +#define XUSB_DEV_CFG_1_BUS_MASTER_EN BIT(2) +#define XUSB_DEV_CFG_4 0x010 +#define XUSB_DEV_CFG_4_BASE_ADDR_MASK GENMASK(31, 15) +#define XUSB_DEV_CFG_5 0x014 + +/* IPFS registers */ +#define XUSB_DEV_CONFIGURATION_0 0x180 +#define XUSB_DEV_CONFIGURATION_0_EN_FPCI BIT(0) +#define XUSB_DEV_INTR_MASK_0 0x188 +#define XUSB_DEV_INTR_MASK_0_IP_INT_MASK BIT(16) + +struct tegra_xudc_ep_context { + __le32 info0; + __le32 info1; + __le32 deq_lo; + __le32 deq_hi; + __le32 tx_info; + __le32 rsvd[11]; +}; + +#define EP_STATE_DISABLED 0 +#define EP_STATE_RUNNING 1 +#define EP_STATE_HALTED 2 +#define EP_STATE_STOPPED 3 +#define EP_STATE_ERROR 4 + +#define EP_TYPE_INVALID 0 +#define EP_TYPE_ISOCH_OUT 1 +#define EP_TYPE_BULK_OUT 2 +#define EP_TYPE_INTERRUPT_OUT 3 +#define EP_TYPE_CONTROL 4 +#define EP_TYPE_ISCOH_IN 5 +#define EP_TYPE_BULK_IN 6 +#define EP_TYPE_INTERRUPT_IN 7 + +#define BUILD_EP_CONTEXT_RW(name, member, shift, mask) \ +static inline u32 ep_ctx_read_##name(struct tegra_xudc_ep_context *ctx) \ +{ \ + return (le32_to_cpu(ctx->member) >> (shift)) & (mask); \ +} \ +static inline void \ +ep_ctx_write_##name(struct tegra_xudc_ep_context *ctx, u32 val) \ +{ \ + u32 tmp; \ + \ + tmp = le32_to_cpu(ctx->member) & ~((mask) << (shift)); \ + tmp |= (val & (mask)) << (shift); \ + ctx->member = cpu_to_le32(tmp); \ +} + +BUILD_EP_CONTEXT_RW(state, info0, 0, 0x7) +BUILD_EP_CONTEXT_RW(mult, info0, 8, 0x3) +BUILD_EP_CONTEXT_RW(max_pstreams, info0, 10, 0x1f) +BUILD_EP_CONTEXT_RW(lsa, info0, 15, 0x1) +BUILD_EP_CONTEXT_RW(interval, info0, 16, 0xff) +BUILD_EP_CONTEXT_RW(cerr, info1, 1, 0x3) +BUILD_EP_CONTEXT_RW(type, info1, 3, 0x7) +BUILD_EP_CONTEXT_RW(hid, info1, 7, 0x1) +BUILD_EP_CONTEXT_RW(max_burst_size, info1, 8, 0xff) +BUILD_EP_CONTEXT_RW(max_packet_size, info1, 16, 0xffff) +BUILD_EP_CONTEXT_RW(dcs, deq_lo, 0, 0x1) +BUILD_EP_CONTEXT_RW(deq_lo, deq_lo, 4, 0xfffffff) +BUILD_EP_CONTEXT_RW(deq_hi, deq_hi, 0, 0xffffffff) +BUILD_EP_CONTEXT_RW(avg_trb_len, tx_info, 0, 0xffff) +BUILD_EP_CONTEXT_RW(max_esit_payload, tx_info, 16, 0xffff) +BUILD_EP_CONTEXT_RW(edtla, rsvd[0], 0, 0xffffff) +BUILD_EP_CONTEXT_RW(seq_num, rsvd[0], 24, 0xff) +BUILD_EP_CONTEXT_RW(partial_td, rsvd[0], 25, 0x1) +BUILD_EP_CONTEXT_RW(cerrcnt, rsvd[1], 18, 0x3) +BUILD_EP_CONTEXT_RW(data_offset, rsvd[2], 0, 0x1ffff) +BUILD_EP_CONTEXT_RW(numtrbs, rsvd[2], 22, 0x1f) +BUILD_EP_CONTEXT_RW(devaddr, rsvd[6], 0, 0x7f) + +static inline u64 ep_ctx_read_deq_ptr(struct tegra_xudc_ep_context *ctx) +{ + return ((u64)ep_ctx_read_deq_hi(ctx) << 32) | + (ep_ctx_read_deq_lo(ctx) << 4); +} + +static inline void +ep_ctx_write_deq_ptr(struct tegra_xudc_ep_context *ctx, u64 addr) +{ + ep_ctx_write_deq_lo(ctx, lower_32_bits(addr) >> 4); + ep_ctx_write_deq_hi(ctx, upper_32_bits(addr)); +} + +struct tegra_xudc_trb { + __le32 data_lo; + __le32 data_hi; + __le32 status; + __le32 control; +}; + +#define TRB_TYPE_RSVD 0 +#define TRB_TYPE_NORMAL 1 +#define TRB_TYPE_SETUP_STAGE 2 +#define TRB_TYPE_DATA_STAGE 3 +#define TRB_TYPE_STATUS_STAGE 4 +#define TRB_TYPE_ISOCH 5 +#define TRB_TYPE_LINK 6 +#define TRB_TYPE_TRANSFER_EVENT 32 +#define TRB_TYPE_PORT_STATUS_CHANGE_EVENT 34 +#define TRB_TYPE_STREAM 48 +#define TRB_TYPE_SETUP_PACKET_EVENT 63 + +#define TRB_CMPL_CODE_INVALID 0 +#define TRB_CMPL_CODE_SUCCESS 1 +#define TRB_CMPL_CODE_DATA_BUFFER_ERR 2 +#define TRB_CMPL_CODE_BABBLE_DETECTED_ERR 3 +#define TRB_CMPL_CODE_USB_TRANS_ERR 4 +#define TRB_CMPL_CODE_TRB_ERR 5 +#define TRB_CMPL_CODE_STALL 6 +#define TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR 10 +#define TRB_CMPL_CODE_SHORT_PACKET 13 +#define TRB_CMPL_CODE_RING_UNDERRUN 14 +#define TRB_CMPL_CODE_RING_OVERRUN 15 +#define TRB_CMPL_CODE_EVENT_RING_FULL_ERR 21 +#define TRB_CMPL_CODE_STOPPED 26 +#define TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN 31 +#define TRB_CMPL_CODE_STREAM_NUMP_ERROR 219 +#define TRB_CMPL_CODE_PRIME_PIPE_RECEIVED 220 +#define TRB_CMPL_CODE_HOST_REJECTED 221 +#define TRB_CMPL_CODE_CTRL_DIR_ERR 222 +#define TRB_CMPL_CODE_CTRL_SEQNUM_ERR 223 + +#define BUILD_TRB_RW(name, member, shift, mask) \ +static inline u32 trb_read_##name(struct tegra_xudc_trb *trb) \ +{ \ + return (le32_to_cpu(trb->member) >> (shift)) & (mask); \ +} \ +static inline void \ +trb_write_##name(struct tegra_xudc_trb *trb, u32 val) \ +{ \ + u32 tmp; \ + \ + tmp = le32_to_cpu(trb->member) & ~((mask) << (shift)); \ + tmp |= (val & (mask)) << (shift); \ + trb->member = cpu_to_le32(tmp); \ +} + +BUILD_TRB_RW(data_lo, data_lo, 0, 0xffffffff) +BUILD_TRB_RW(data_hi, data_hi, 0, 0xffffffff) +BUILD_TRB_RW(seq_num, status, 0, 0xffff) +BUILD_TRB_RW(transfer_len, status, 0, 0xffffff) +BUILD_TRB_RW(td_size, status, 17, 0x1f) +BUILD_TRB_RW(cmpl_code, status, 24, 0xff) +BUILD_TRB_RW(cycle, control, 0, 0x1) +BUILD_TRB_RW(toggle_cycle, control, 1, 0x1) +BUILD_TRB_RW(isp, control, 2, 0x1) +BUILD_TRB_RW(chain, control, 4, 0x1) +BUILD_TRB_RW(ioc, control, 5, 0x1) +BUILD_TRB_RW(type, control, 10, 0x3f) +BUILD_TRB_RW(stream_id, control, 16, 0xffff) +BUILD_TRB_RW(endpoint_id, control, 16, 0x1f) +BUILD_TRB_RW(tlbpc, control, 16, 0xf) +BUILD_TRB_RW(data_stage_dir, control, 16, 0x1) +BUILD_TRB_RW(frame_id, control, 20, 0x7ff) +BUILD_TRB_RW(sia, control, 31, 0x1) + +static inline u64 trb_read_data_ptr(struct tegra_xudc_trb *trb) +{ + return ((u64)trb_read_data_hi(trb) << 32) | + trb_read_data_lo(trb); +} + +static inline void trb_write_data_ptr(struct tegra_xudc_trb *trb, u64 addr) +{ + trb_write_data_lo(trb, lower_32_bits(addr)); + trb_write_data_hi(trb, upper_32_bits(addr)); +} + +struct tegra_xudc_request { + struct usb_request usb_req; + + size_t buf_queued; + unsigned int trbs_queued; + unsigned int trbs_needed; + bool need_zlp; + + struct tegra_xudc_trb *first_trb; + struct tegra_xudc_trb *last_trb; + + struct list_head list; +}; + +struct tegra_xudc_ep { + struct tegra_xudc *xudc; + struct usb_ep usb_ep; + unsigned int index; + char name[8]; + + struct tegra_xudc_ep_context *context; + +#define XUDC_TRANSFER_RING_SIZE 64 + struct tegra_xudc_trb *transfer_ring; + dma_addr_t transfer_ring_phys; + + unsigned int enq_ptr; + unsigned int deq_ptr; + bool pcs; + bool ring_full; + bool stream_rejected; + + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + const struct usb_ss_ep_comp_descriptor *comp_desc; +}; + +struct tegra_xudc_sel_timing { + __u8 u1sel; + __u8 u1pel; + __le16 u2sel; + __le16 u2pel; +}; + +enum tegra_xudc_setup_state { + WAIT_FOR_SETUP, + DATA_STAGE_XFER, + DATA_STAGE_RECV, + STATUS_STAGE_XFER, + STATUS_STAGE_RECV, +}; + +struct tegra_xudc_setup_packet { + struct usb_ctrlrequest ctrl_req; + unsigned int seq_num; +}; + +struct tegra_xudc_save_regs { + u32 ctrl; + u32 portpm; +}; + +struct tegra_xudc { + struct device *dev; + const struct tegra_xudc_soc *soc; + struct tegra_xusb_padctl *padctl; + + spinlock_t lock; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + +#define XUDC_NR_EVENT_RINGS 2 +#define XUDC_EVENT_RING_SIZE 4096 + struct tegra_xudc_trb *event_ring[XUDC_NR_EVENT_RINGS]; + dma_addr_t event_ring_phys[XUDC_NR_EVENT_RINGS]; + unsigned int event_ring_index; + unsigned int event_ring_deq_ptr; + bool ccs; + +#define XUDC_NR_EPS 32 + struct tegra_xudc_ep ep[XUDC_NR_EPS]; + struct tegra_xudc_ep_context *ep_context; + dma_addr_t ep_context_phys; + + struct device *genpd_dev_device; + struct device *genpd_dev_ss; + struct device_link *genpd_dl_device; + struct device_link *genpd_dl_ss; + + struct dma_pool *transfer_ring_pool; + + bool queued_setup_packet; + struct tegra_xudc_setup_packet setup_packet; + enum tegra_xudc_setup_state setup_state; + u16 setup_seq_num; + + u16 dev_addr; + u16 isoch_delay; + struct tegra_xudc_sel_timing sel_timing; + u8 test_mode_pattern; + u16 status_buf; + struct tegra_xudc_request *ep0_req; + + bool pullup; + + unsigned int nr_enabled_eps; + unsigned int nr_isoch_eps; + + unsigned int device_state; + unsigned int resume_state; + + int irq; + + void __iomem *base; + resource_size_t phys_base; + void __iomem *ipfs; + void __iomem *fpci; + + struct regulator_bulk_data *supplies; + + struct clk_bulk_data *clks; + + enum usb_role device_mode; + struct usb_role_switch *usb_role_sw; + struct work_struct usb_role_sw_work; + + struct phy *usb3_phy; + struct phy *utmi_phy; + + struct tegra_xudc_save_regs saved_regs; + bool suspended; + bool powergated; + + struct completion disconnect_complete; + + bool selfpowered; + +#define TOGGLE_VBUS_WAIT_MS 100 + struct delayed_work plc_reset_work; + bool wait_csc; + + struct delayed_work port_reset_war_work; + bool wait_for_sec_prc; +}; + +#define XUDC_TRB_MAX_BUFFER_SIZE 65536 +#define XUDC_MAX_ISOCH_EPS 4 +#define XUDC_INTERRUPT_MODERATION_US 0 + +static struct usb_endpoint_descriptor tegra_xudc_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(64), +}; + +struct tegra_xudc_soc { + const char * const *supply_names; + unsigned int num_supplies; + const char * const *clock_names; + unsigned int num_clks; + bool u1_enable; + bool u2_enable; + bool lpm_enable; + bool invalid_seq_num; + bool pls_quirk; + bool port_reset_quirk; + bool has_ipfs; +}; + +static inline u32 fpci_readl(struct tegra_xudc *xudc, unsigned int offset) +{ + return readl(xudc->fpci + offset); +} + +static inline void fpci_writel(struct tegra_xudc *xudc, u32 val, + unsigned int offset) +{ + writel(val, xudc->fpci + offset); +} + +static inline u32 ipfs_readl(struct tegra_xudc *xudc, unsigned int offset) +{ + return readl(xudc->ipfs + offset); +} + +static inline void ipfs_writel(struct tegra_xudc *xudc, u32 val, + unsigned int offset) +{ + writel(val, xudc->ipfs + offset); +} + +static inline u32 xudc_readl(struct tegra_xudc *xudc, unsigned int offset) +{ + return readl(xudc->base + offset); +} + +static inline void xudc_writel(struct tegra_xudc *xudc, u32 val, + unsigned int offset) +{ + writel(val, xudc->base + offset); +} + +static inline int xudc_readl_poll(struct tegra_xudc *xudc, + unsigned int offset, u32 mask, u32 val) +{ + u32 regval; + + return readl_poll_timeout_atomic(xudc->base + offset, regval, + (regval & mask) == val, 1, 100); +} + +static inline struct tegra_xudc *to_xudc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct tegra_xudc, gadget); +} + +static inline struct tegra_xudc_ep *to_xudc_ep(struct usb_ep *ep) +{ + return container_of(ep, struct tegra_xudc_ep, usb_ep); +} + +static inline struct tegra_xudc_request *to_xudc_req(struct usb_request *req) +{ + return container_of(req, struct tegra_xudc_request, usb_req); +} + +static inline void dump_trb(struct tegra_xudc *xudc, const char *type, + struct tegra_xudc_trb *trb) +{ + dev_dbg(xudc->dev, + "%s: %p, lo = %#x, hi = %#x, status = %#x, control = %#x\n", + type, trb, trb->data_lo, trb->data_hi, trb->status, + trb->control); +} + +static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc) +{ + int err; + + pm_runtime_get_sync(xudc->dev); + + err = phy_power_on(xudc->utmi_phy); + if (err < 0) + dev_err(xudc->dev, "utmi power on failed %d\n", err); + + err = phy_power_on(xudc->usb3_phy); + if (err < 0) + dev_err(xudc->dev, "usb3 phy power on failed %d\n", err); + + dev_dbg(xudc->dev, "device mode on\n"); + + tegra_xusb_padctl_set_vbus_override(xudc->padctl, true); + + xudc->device_mode = USB_ROLE_DEVICE; +} + +static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) +{ + bool connected = false; + u32 pls, val; + int err; + + dev_dbg(xudc->dev, "device mode off\n"); + + connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS); + + reinit_completion(&xudc->disconnect_complete); + + tegra_xusb_padctl_set_vbus_override(xudc->padctl, false); + + pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> + PORTSC_PLS_SHIFT; + + /* Direct link to U0 if disconnected in RESUME or U2. */ + if (xudc->soc->pls_quirk && xudc->gadget.speed == USB_SPEED_SUPER && + (pls == PORTSC_PLS_RESUME || pls == PORTSC_PLS_U2)) { + val = xudc_readl(xudc, PORTPM); + val |= PORTPM_FRWE; + xudc_writel(xudc, val, PORTPM); + + val = xudc_readl(xudc, PORTSC); + val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); + val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0); + xudc_writel(xudc, val, PORTSC); + } + + xudc->device_mode = USB_ROLE_NONE; + + /* Wait for disconnect event. */ + if (connected) + wait_for_completion(&xudc->disconnect_complete); + + /* Make sure interrupt handler has completed before powergating. */ + synchronize_irq(xudc->irq); + + err = phy_power_off(xudc->utmi_phy); + if (err < 0) + dev_err(xudc->dev, "utmi_phy power off failed %d\n", err); + + err = phy_power_off(xudc->usb3_phy); + if (err < 0) + dev_err(xudc->dev, "usb3_phy power off failed %d\n", err); + + pm_runtime_put(xudc->dev); +} + +static void tegra_xudc_usb_role_sw_work(struct work_struct *work) +{ + struct tegra_xudc *xudc = container_of(work, struct tegra_xudc, + usb_role_sw_work); + + if (!xudc->usb_role_sw || + usb_role_switch_get_role(xudc->usb_role_sw) == USB_ROLE_DEVICE) + tegra_xudc_device_mode_on(xudc); + else + tegra_xudc_device_mode_off(xudc); + +} + +static int tegra_xudc_usb_role_sw_set(struct device *dev, enum usb_role role) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + unsigned long flags; + + dev_dbg(dev, "%s role is %d\n", __func__, role); + + spin_lock_irqsave(&xudc->lock, flags); + + if (!xudc->suspended) + schedule_work(&xudc->usb_role_sw_work); + + spin_unlock_irqrestore(&xudc->lock, flags); + + return 0; +} + +static void tegra_xudc_plc_reset_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tegra_xudc *xudc = container_of(dwork, struct tegra_xudc, + plc_reset_work); + unsigned long flags; + + spin_lock_irqsave(&xudc->lock, flags); + + if (xudc->wait_csc) { + u32 pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> + PORTSC_PLS_SHIFT; + + if (pls == PORTSC_PLS_INACTIVE) { + dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n"); + tegra_xusb_padctl_set_vbus_override(xudc->padctl, + false); + tegra_xusb_padctl_set_vbus_override(xudc->padctl, true); + xudc->wait_csc = false; + } + } + + spin_unlock_irqrestore(&xudc->lock, flags); +} + +static void tegra_xudc_port_reset_war_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tegra_xudc *xudc = + container_of(dwork, struct tegra_xudc, port_reset_war_work); + unsigned long flags; + u32 pls; + int ret; + + spin_lock_irqsave(&xudc->lock, flags); + + if ((xudc->device_mode == USB_ROLE_DEVICE) + && xudc->wait_for_sec_prc) { + pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >> + PORTSC_PLS_SHIFT; + dev_dbg(xudc->dev, "pls = %x\n", pls); + + if (pls == PORTSC_PLS_DISABLED) { + dev_dbg(xudc->dev, "toggle vbus\n"); + /* PRC doesn't complete in 100ms, toggle the vbus */ + ret = tegra_phy_xusb_utmi_port_reset(xudc->utmi_phy); + if (ret == 1) + xudc->wait_for_sec_prc = 0; + } + } + + spin_unlock_irqrestore(&xudc->lock, flags); +} + +static dma_addr_t trb_virt_to_phys(struct tegra_xudc_ep *ep, + struct tegra_xudc_trb *trb) +{ + unsigned int index; + + index = trb - ep->transfer_ring; + + if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE)) + return 0; + + return (ep->transfer_ring_phys + index * sizeof(*trb)); +} + +static struct tegra_xudc_trb *trb_phys_to_virt(struct tegra_xudc_ep *ep, + dma_addr_t addr) +{ + struct tegra_xudc_trb *trb; + unsigned int index; + + index = (addr - ep->transfer_ring_phys) / sizeof(*trb); + + if (WARN_ON(index >= XUDC_TRANSFER_RING_SIZE)) + return NULL; + + trb = &ep->transfer_ring[index]; + + return trb; +} + +static void ep_reload(struct tegra_xudc *xudc, unsigned int ep) +{ + xudc_writel(xudc, BIT(ep), EP_RELOAD); + xudc_readl_poll(xudc, EP_RELOAD, BIT(ep), 0); +} + +static void ep_pause(struct tegra_xudc *xudc, unsigned int ep) +{ + u32 val; + + val = xudc_readl(xudc, EP_PAUSE); + if (val & BIT(ep)) + return; + val |= BIT(ep); + + xudc_writel(xudc, val, EP_PAUSE); + + xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); + + xudc_writel(xudc, BIT(ep), EP_STCHG); +} + +static void ep_unpause(struct tegra_xudc *xudc, unsigned int ep) +{ + u32 val; + + val = xudc_readl(xudc, EP_PAUSE); + if (!(val & BIT(ep))) + return; + val &= ~BIT(ep); + + xudc_writel(xudc, val, EP_PAUSE); + + xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); + + xudc_writel(xudc, BIT(ep), EP_STCHG); +} + +static void ep_unpause_all(struct tegra_xudc *xudc) +{ + u32 val; + + val = xudc_readl(xudc, EP_PAUSE); + + xudc_writel(xudc, 0, EP_PAUSE); + + xudc_readl_poll(xudc, EP_STCHG, val, val); + + xudc_writel(xudc, val, EP_STCHG); +} + +static void ep_halt(struct tegra_xudc *xudc, unsigned int ep) +{ + u32 val; + + val = xudc_readl(xudc, EP_HALT); + if (val & BIT(ep)) + return; + val |= BIT(ep); + xudc_writel(xudc, val, EP_HALT); + + xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); + + xudc_writel(xudc, BIT(ep), EP_STCHG); +} + +static void ep_unhalt(struct tegra_xudc *xudc, unsigned int ep) +{ + u32 val; + + val = xudc_readl(xudc, EP_HALT); + if (!(val & BIT(ep))) + return; + val &= ~BIT(ep); + xudc_writel(xudc, val, EP_HALT); + + xudc_readl_poll(xudc, EP_STCHG, BIT(ep), BIT(ep)); + + xudc_writel(xudc, BIT(ep), EP_STCHG); +} + +static void ep_unhalt_all(struct tegra_xudc *xudc) +{ + u32 val; + + val = xudc_readl(xudc, EP_HALT); + if (!val) + return; + xudc_writel(xudc, 0, EP_HALT); + + xudc_readl_poll(xudc, EP_STCHG, val, val); + + xudc_writel(xudc, val, EP_STCHG); +} + +static void ep_wait_for_stopped(struct tegra_xudc *xudc, unsigned int ep) +{ + xudc_readl_poll(xudc, EP_STOPPED, BIT(ep), BIT(ep)); + xudc_writel(xudc, BIT(ep), EP_STOPPED); +} + +static void ep_wait_for_inactive(struct tegra_xudc *xudc, unsigned int ep) +{ + xudc_readl_poll(xudc, EP_THREAD_ACTIVE, BIT(ep), 0); +} + +static void tegra_xudc_req_done(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req, int status) +{ + struct tegra_xudc *xudc = ep->xudc; + + dev_dbg(xudc->dev, "completing request %p on EP %u with status %d\n", + req, ep->index, status); + + if (likely(req->usb_req.status == -EINPROGRESS)) + req->usb_req.status = status; + + list_del_init(&req->list); + + if (usb_endpoint_xfer_control(ep->desc)) { + usb_gadget_unmap_request(&xudc->gadget, &req->usb_req, + (xudc->setup_state == + DATA_STAGE_XFER)); + } else { + usb_gadget_unmap_request(&xudc->gadget, &req->usb_req, + usb_endpoint_dir_in(ep->desc)); + } + + spin_unlock(&xudc->lock); + usb_gadget_giveback_request(&ep->usb_ep, &req->usb_req); + spin_lock(&xudc->lock); +} + +static void tegra_xudc_ep_nuke(struct tegra_xudc_ep *ep, int status) +{ + struct tegra_xudc_request *req; + + while (!list_empty(&ep->queue)) { + req = list_first_entry(&ep->queue, struct tegra_xudc_request, + list); + tegra_xudc_req_done(ep, req, status); + } +} + +static unsigned int ep_available_trbs(struct tegra_xudc_ep *ep) +{ + if (ep->ring_full) + return 0; + + if (ep->deq_ptr > ep->enq_ptr) + return ep->deq_ptr - ep->enq_ptr - 1; + + return XUDC_TRANSFER_RING_SIZE - (ep->enq_ptr - ep->deq_ptr) - 2; +} + +static void tegra_xudc_queue_one_trb(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req, + struct tegra_xudc_trb *trb, + bool ioc) +{ + struct tegra_xudc *xudc = ep->xudc; + dma_addr_t buf_addr; + size_t len; + + len = min_t(size_t, XUDC_TRB_MAX_BUFFER_SIZE, req->usb_req.length - + req->buf_queued); + if (len > 0) + buf_addr = req->usb_req.dma + req->buf_queued; + else + buf_addr = 0; + + trb_write_data_ptr(trb, buf_addr); + + trb_write_transfer_len(trb, len); + trb_write_td_size(trb, req->trbs_needed - req->trbs_queued - 1); + + if (req->trbs_queued == req->trbs_needed - 1 || + (req->need_zlp && req->trbs_queued == req->trbs_needed - 2)) + trb_write_chain(trb, 0); + else + trb_write_chain(trb, 1); + + trb_write_ioc(trb, ioc); + + if (usb_endpoint_dir_out(ep->desc) || + (usb_endpoint_xfer_control(ep->desc) && + (xudc->setup_state == DATA_STAGE_RECV))) + trb_write_isp(trb, 1); + else + trb_write_isp(trb, 0); + + if (usb_endpoint_xfer_control(ep->desc)) { + if (xudc->setup_state == DATA_STAGE_XFER || + xudc->setup_state == DATA_STAGE_RECV) + trb_write_type(trb, TRB_TYPE_DATA_STAGE); + else + trb_write_type(trb, TRB_TYPE_STATUS_STAGE); + + if (xudc->setup_state == DATA_STAGE_XFER || + xudc->setup_state == STATUS_STAGE_XFER) + trb_write_data_stage_dir(trb, 1); + else + trb_write_data_stage_dir(trb, 0); + } else if (usb_endpoint_xfer_isoc(ep->desc)) { + trb_write_type(trb, TRB_TYPE_ISOCH); + trb_write_sia(trb, 1); + trb_write_frame_id(trb, 0); + trb_write_tlbpc(trb, 0); + } else if (usb_ss_max_streams(ep->comp_desc)) { + trb_write_type(trb, TRB_TYPE_STREAM); + trb_write_stream_id(trb, req->usb_req.stream_id); + } else { + trb_write_type(trb, TRB_TYPE_NORMAL); + trb_write_stream_id(trb, 0); + } + + trb_write_cycle(trb, ep->pcs); + + req->trbs_queued++; + req->buf_queued += len; + + dump_trb(xudc, "TRANSFER", trb); +} + +static unsigned int tegra_xudc_queue_trbs(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req) +{ + unsigned int i, count, available; + bool wait_td = false; + + available = ep_available_trbs(ep); + count = req->trbs_needed - req->trbs_queued; + if (available < count) { + count = available; + ep->ring_full = true; + } + + /* + * To generate zero-length packet on USB bus, SW needs schedule a + * standalone zero-length TD. According to HW's behavior, SW needs + * to schedule TDs in different ways for different endpoint types. + * + * For control endpoint: + * - Data stage TD (IOC = 1, CH = 0) + * - Ring doorbell and wait transfer event + * - Data stage TD for ZLP (IOC = 1, CH = 0) + * - Ring doorbell + * + * For bulk and interrupt endpoints: + * - Normal transfer TD (IOC = 0, CH = 0) + * - Normal transfer TD for ZLP (IOC = 1, CH = 0) + * - Ring doorbell + */ + + if (req->need_zlp && usb_endpoint_xfer_control(ep->desc) && count > 1) + wait_td = true; + + if (!req->first_trb) + req->first_trb = &ep->transfer_ring[ep->enq_ptr]; + + for (i = 0; i < count; i++) { + struct tegra_xudc_trb *trb = &ep->transfer_ring[ep->enq_ptr]; + bool ioc = false; + + if ((i == count - 1) || (wait_td && i == count - 2)) + ioc = true; + + tegra_xudc_queue_one_trb(ep, req, trb, ioc); + req->last_trb = trb; + + ep->enq_ptr++; + if (ep->enq_ptr == XUDC_TRANSFER_RING_SIZE - 1) { + trb = &ep->transfer_ring[ep->enq_ptr]; + trb_write_cycle(trb, ep->pcs); + ep->pcs = !ep->pcs; + ep->enq_ptr = 0; + } + + if (ioc) + break; + } + + return count; +} + +static void tegra_xudc_ep_ring_doorbell(struct tegra_xudc_ep *ep) +{ + struct tegra_xudc *xudc = ep->xudc; + u32 val; + + if (list_empty(&ep->queue)) + return; + + val = DB_TARGET(ep->index); + if (usb_endpoint_xfer_control(ep->desc)) { + val |= DB_STREAMID(xudc->setup_seq_num); + } else if (usb_ss_max_streams(ep->comp_desc) > 0) { + struct tegra_xudc_request *req; + + /* Don't ring doorbell if the stream has been rejected. */ + if (ep->stream_rejected) + return; + + req = list_first_entry(&ep->queue, struct tegra_xudc_request, + list); + val |= DB_STREAMID(req->usb_req.stream_id); + } + + dev_dbg(xudc->dev, "ring doorbell: %#x\n", val); + xudc_writel(xudc, val, DB); +} + +static void tegra_xudc_ep_kick_queue(struct tegra_xudc_ep *ep) +{ + struct tegra_xudc_request *req; + bool trbs_queued = false; + + list_for_each_entry(req, &ep->queue, list) { + if (ep->ring_full) + break; + + if (tegra_xudc_queue_trbs(ep, req) > 0) + trbs_queued = true; + } + + if (trbs_queued) + tegra_xudc_ep_ring_doorbell(ep); +} + +static int +__tegra_xudc_ep_queue(struct tegra_xudc_ep *ep, struct tegra_xudc_request *req) +{ + struct tegra_xudc *xudc = ep->xudc; + int err; + + if (usb_endpoint_xfer_control(ep->desc) && !list_empty(&ep->queue)) { + dev_err(xudc->dev, "control EP has pending transfers\n"); + return -EINVAL; + } + + if (usb_endpoint_xfer_control(ep->desc)) { + err = usb_gadget_map_request(&xudc->gadget, &req->usb_req, + (xudc->setup_state == + DATA_STAGE_XFER)); + } else { + err = usb_gadget_map_request(&xudc->gadget, &req->usb_req, + usb_endpoint_dir_in(ep->desc)); + } + + if (err < 0) { + dev_err(xudc->dev, "failed to map request: %d\n", err); + return err; + } + + req->first_trb = NULL; + req->last_trb = NULL; + req->buf_queued = 0; + req->trbs_queued = 0; + req->need_zlp = false; + req->trbs_needed = DIV_ROUND_UP(req->usb_req.length, + XUDC_TRB_MAX_BUFFER_SIZE); + if (req->usb_req.length == 0) + req->trbs_needed++; + + if (!usb_endpoint_xfer_isoc(ep->desc) && + req->usb_req.zero && req->usb_req.length && + ((req->usb_req.length % ep->usb_ep.maxpacket) == 0)) { + req->trbs_needed++; + req->need_zlp = true; + } + + req->usb_req.status = -EINPROGRESS; + req->usb_req.actual = 0; + + list_add_tail(&req->list, &ep->queue); + + tegra_xudc_ep_kick_queue(ep); + + return 0; +} + +static int +tegra_xudc_ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, + gfp_t gfp) +{ + struct tegra_xudc_request *req; + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep || !usb_req) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + req = to_xudc_req(usb_req); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated || !ep->desc) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = __tegra_xudc_ep_queue(ep, req); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static void squeeze_transfer_ring(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req) +{ + struct tegra_xudc_trb *trb = req->first_trb; + bool pcs_enq = trb_read_cycle(trb); + bool pcs; + + /* + * Clear out all the TRBs part of or after the cancelled request, + * and must correct trb cycle bit to the last un-enqueued state. + */ + while (trb != &ep->transfer_ring[ep->enq_ptr]) { + pcs = trb_read_cycle(trb); + memset(trb, 0, sizeof(*trb)); + trb_write_cycle(trb, !pcs); + trb++; + + if (trb_read_type(trb) == TRB_TYPE_LINK) + trb = ep->transfer_ring; + } + + /* Requests will be re-queued at the start of the cancelled request. */ + ep->enq_ptr = req->first_trb - ep->transfer_ring; + /* + * Retrieve the correct cycle bit state from the first trb of + * the cancelled request. + */ + ep->pcs = pcs_enq; + ep->ring_full = false; + list_for_each_entry_continue(req, &ep->queue, list) { + req->usb_req.status = -EINPROGRESS; + req->usb_req.actual = 0; + + req->first_trb = NULL; + req->last_trb = NULL; + req->buf_queued = 0; + req->trbs_queued = 0; + } +} + +/* + * Determine if the given TRB is in the range [first trb, last trb] for the + * given request. + */ +static bool trb_in_request(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req, + struct tegra_xudc_trb *trb) +{ + dev_dbg(ep->xudc->dev, "%s: request %p -> %p; trb %p\n", __func__, + req->first_trb, req->last_trb, trb); + + if (trb >= req->first_trb && (trb <= req->last_trb || + req->last_trb < req->first_trb)) + return true; + + if (trb < req->first_trb && trb <= req->last_trb && + req->last_trb < req->first_trb) + return true; + + return false; +} + +/* + * Determine if the given TRB is in the range [EP enqueue pointer, first TRB) + * for the given endpoint and request. + */ +static bool trb_before_request(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req, + struct tegra_xudc_trb *trb) +{ + struct tegra_xudc_trb *enq_trb = &ep->transfer_ring[ep->enq_ptr]; + + dev_dbg(ep->xudc->dev, "%s: request %p -> %p; enq ptr: %p; trb %p\n", + __func__, req->first_trb, req->last_trb, enq_trb, trb); + + if (trb < req->first_trb && (enq_trb <= trb || + req->first_trb < enq_trb)) + return true; + + if (trb > req->first_trb && req->first_trb < enq_trb && enq_trb <= trb) + return true; + + return false; +} + +static int +__tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep, + struct tegra_xudc_request *req) +{ + struct tegra_xudc *xudc = ep->xudc; + struct tegra_xudc_request *r; + struct tegra_xudc_trb *deq_trb; + bool busy, kick_queue = false; + int ret = 0; + + /* Make sure the request is actually queued to this endpoint. */ + list_for_each_entry(r, &ep->queue, list) { + if (r == req) + break; + } + + if (r != req) + return -EINVAL; + + /* Request hasn't been queued in the transfer ring yet. */ + if (!req->trbs_queued) { + tegra_xudc_req_done(ep, req, -ECONNRESET); + return 0; + } + + /* Halt DMA for this endpiont. */ + if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) { + ep_pause(xudc, ep->index); + ep_wait_for_inactive(xudc, ep->index); + } + + deq_trb = trb_phys_to_virt(ep, ep_ctx_read_deq_ptr(ep->context)); + /* Is the hardware processing the TRB at the dequeue pointer? */ + busy = (trb_read_cycle(deq_trb) == ep_ctx_read_dcs(ep->context)); + + if (trb_in_request(ep, req, deq_trb) && busy) { + /* + * Request has been partially completed or it hasn't + * started processing yet. + */ + dma_addr_t deq_ptr; + + squeeze_transfer_ring(ep, req); + + req->usb_req.actual = ep_ctx_read_edtla(ep->context); + tegra_xudc_req_done(ep, req, -ECONNRESET); + kick_queue = true; + + /* EDTLA is > 0: request has been partially completed */ + if (req->usb_req.actual > 0) { + /* + * Abort the pending transfer and update the dequeue + * pointer + */ + ep_ctx_write_edtla(ep->context, 0); + ep_ctx_write_partial_td(ep->context, 0); + ep_ctx_write_data_offset(ep->context, 0); + + deq_ptr = trb_virt_to_phys(ep, + &ep->transfer_ring[ep->enq_ptr]); + + if (dma_mapping_error(xudc->dev, deq_ptr)) { + ret = -EINVAL; + } else { + ep_ctx_write_deq_ptr(ep->context, deq_ptr); + ep_ctx_write_dcs(ep->context, ep->pcs); + ep_reload(xudc, ep->index); + } + } + } else if (trb_before_request(ep, req, deq_trb) && busy) { + /* Request hasn't started processing yet. */ + squeeze_transfer_ring(ep, req); + + tegra_xudc_req_done(ep, req, -ECONNRESET); + kick_queue = true; + } else { + /* + * Request has completed, but we haven't processed the + * completion event yet. + */ + tegra_xudc_req_done(ep, req, -ECONNRESET); + ret = -EINVAL; + } + + /* Resume the endpoint. */ + ep_unpause(xudc, ep->index); + + if (kick_queue) + tegra_xudc_ep_kick_queue(ep); + + return ret; +} + +static int +tegra_xudc_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) +{ + struct tegra_xudc_request *req; + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep || !usb_req) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + req = to_xudc_req(usb_req); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + + if (xudc->powergated || !ep->desc) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = __tegra_xudc_ep_dequeue(ep, req); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt) +{ + struct tegra_xudc *xudc = ep->xudc; + + if (!ep->desc) + return -EINVAL; + + if (usb_endpoint_xfer_isoc(ep->desc)) { + dev_err(xudc->dev, "can't halt isoc EP\n"); + return -ENOTSUPP; + } + + if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) { + dev_dbg(xudc->dev, "EP %u already %s\n", ep->index, + halt ? "halted" : "not halted"); + return 0; + } + + if (halt) { + ep_halt(xudc, ep->index); + } else { + ep_ctx_write_state(ep->context, EP_STATE_DISABLED); + + ep_reload(xudc, ep->index); + + ep_ctx_write_state(ep->context, EP_STATE_RUNNING); + ep_ctx_write_seq_num(ep->context, 0); + + ep_reload(xudc, ep->index); + ep_unpause(xudc, ep->index); + ep_unhalt(xudc, ep->index); + + tegra_xudc_ep_ring_doorbell(ep); + } + + return 0; +} + +static int tegra_xudc_ep_set_halt(struct usb_ep *usb_ep, int value) +{ + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + + if (value && usb_endpoint_dir_in(ep->desc) && + !list_empty(&ep->queue)) { + dev_err(xudc->dev, "can't halt EP with requests pending\n"); + ret = -EAGAIN; + goto unlock; + } + + ret = __tegra_xudc_ep_set_halt(ep, value); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static void tegra_xudc_ep_context_setup(struct tegra_xudc_ep *ep) +{ + const struct usb_endpoint_descriptor *desc = ep->desc; + const struct usb_ss_ep_comp_descriptor *comp_desc = ep->comp_desc; + struct tegra_xudc *xudc = ep->xudc; + u16 maxpacket, maxburst = 0, esit = 0; + u32 val; + + maxpacket = usb_endpoint_maxp(desc) & 0x7ff; + if (xudc->gadget.speed == USB_SPEED_SUPER) { + if (!usb_endpoint_xfer_control(desc)) + maxburst = comp_desc->bMaxBurst; + + if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) + esit = le16_to_cpu(comp_desc->wBytesPerInterval); + } else if ((xudc->gadget.speed < USB_SPEED_SUPER) && + (usb_endpoint_xfer_int(desc) || + usb_endpoint_xfer_isoc(desc))) { + if (xudc->gadget.speed == USB_SPEED_HIGH) { + maxburst = (usb_endpoint_maxp(desc) >> 11) & 0x3; + if (maxburst == 0x3) { + dev_warn(xudc->dev, + "invalid endpoint maxburst\n"); + maxburst = 0x2; + } + } + esit = maxpacket * (maxburst + 1); + } + + memset(ep->context, 0, sizeof(*ep->context)); + + ep_ctx_write_state(ep->context, EP_STATE_RUNNING); + ep_ctx_write_interval(ep->context, desc->bInterval); + if (xudc->gadget.speed == USB_SPEED_SUPER) { + if (usb_endpoint_xfer_isoc(desc)) { + ep_ctx_write_mult(ep->context, + comp_desc->bmAttributes & 0x3); + } + + if (usb_endpoint_xfer_bulk(desc)) { + ep_ctx_write_max_pstreams(ep->context, + comp_desc->bmAttributes & + 0x1f); + ep_ctx_write_lsa(ep->context, 1); + } + } + + if (!usb_endpoint_xfer_control(desc) && usb_endpoint_dir_out(desc)) + val = usb_endpoint_type(desc); + else + val = usb_endpoint_type(desc) + EP_TYPE_CONTROL; + + ep_ctx_write_type(ep->context, val); + ep_ctx_write_cerr(ep->context, 0x3); + ep_ctx_write_max_packet_size(ep->context, maxpacket); + ep_ctx_write_max_burst_size(ep->context, maxburst); + + ep_ctx_write_deq_ptr(ep->context, ep->transfer_ring_phys); + ep_ctx_write_dcs(ep->context, ep->pcs); + + /* Select a reasonable average TRB length based on endpoint type. */ + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + val = 8; + break; + case USB_ENDPOINT_XFER_INT: + val = 1024; + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_ISOC: + default: + val = 3072; + break; + } + + ep_ctx_write_avg_trb_len(ep->context, val); + ep_ctx_write_max_esit_payload(ep->context, esit); + + ep_ctx_write_cerrcnt(ep->context, 0x3); +} + +static void setup_link_trb(struct tegra_xudc_ep *ep, + struct tegra_xudc_trb *trb) +{ + trb_write_data_ptr(trb, ep->transfer_ring_phys); + trb_write_type(trb, TRB_TYPE_LINK); + trb_write_toggle_cycle(trb, 1); +} + +static int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep) +{ + struct tegra_xudc *xudc = ep->xudc; + + if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) { + dev_err(xudc->dev, "endpoint %u already disabled\n", + ep->index); + return -EINVAL; + } + + ep_ctx_write_state(ep->context, EP_STATE_DISABLED); + + ep_reload(xudc, ep->index); + + tegra_xudc_ep_nuke(ep, -ESHUTDOWN); + + xudc->nr_enabled_eps--; + if (usb_endpoint_xfer_isoc(ep->desc)) + xudc->nr_isoch_eps--; + + ep->desc = NULL; + ep->comp_desc = NULL; + + memset(ep->context, 0, sizeof(*ep->context)); + + ep_unpause(xudc, ep->index); + ep_unhalt(xudc, ep->index); + if (xudc_readl(xudc, EP_STOPPED) & BIT(ep->index)) + xudc_writel(xudc, BIT(ep->index), EP_STOPPED); + + /* + * If this is the last endpoint disabled in a de-configure request, + * switch back to address state. + */ + if ((xudc->device_state == USB_STATE_CONFIGURED) && + (xudc->nr_enabled_eps == 1)) { + u32 val; + + xudc->device_state = USB_STATE_ADDRESS; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + val = xudc_readl(xudc, CTRL); + val &= ~CTRL_RUN; + xudc_writel(xudc, val, CTRL); + } + + dev_info(xudc->dev, "ep %u disabled\n", ep->index); + + return 0; +} + +static int tegra_xudc_ep_disable(struct usb_ep *usb_ep) +{ + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = __tegra_xudc_ep_disable(ep); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static int __tegra_xudc_ep_enable(struct tegra_xudc_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct tegra_xudc *xudc = ep->xudc; + unsigned int i; + u32 val; + + if (xudc->gadget.speed == USB_SPEED_SUPER && + !usb_endpoint_xfer_control(desc) && !ep->usb_ep.comp_desc) + return -EINVAL; + + /* Disable the EP if it is not disabled */ + if (ep_ctx_read_state(ep->context) != EP_STATE_DISABLED) + __tegra_xudc_ep_disable(ep); + + ep->desc = desc; + ep->comp_desc = ep->usb_ep.comp_desc; + + if (usb_endpoint_xfer_isoc(desc)) { + if (xudc->nr_isoch_eps > XUDC_MAX_ISOCH_EPS) { + dev_err(xudc->dev, "too many isoch endpoints\n"); + return -EBUSY; + } + xudc->nr_isoch_eps++; + } + + memset(ep->transfer_ring, 0, XUDC_TRANSFER_RING_SIZE * + sizeof(*ep->transfer_ring)); + setup_link_trb(ep, &ep->transfer_ring[XUDC_TRANSFER_RING_SIZE - 1]); + + ep->enq_ptr = 0; + ep->deq_ptr = 0; + ep->pcs = true; + ep->ring_full = false; + xudc->nr_enabled_eps++; + + tegra_xudc_ep_context_setup(ep); + + /* + * No need to reload and un-halt EP0. This will be done automatically + * once a valid SETUP packet is received. + */ + if (usb_endpoint_xfer_control(desc)) + goto out; + + /* + * Transition to configured state once the first non-control + * endpoint is enabled. + */ + if (xudc->device_state == USB_STATE_ADDRESS) { + val = xudc_readl(xudc, CTRL); + val |= CTRL_RUN; + xudc_writel(xudc, val, CTRL); + + xudc->device_state = USB_STATE_CONFIGURED; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + } + + if (usb_endpoint_xfer_isoc(desc)) { + /* + * Pause all bulk endpoints when enabling an isoch endpoint + * to ensure the isoch endpoint is allocated enough bandwidth. + */ + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { + if (xudc->ep[i].desc && + usb_endpoint_xfer_bulk(xudc->ep[i].desc)) + ep_pause(xudc, i); + } + } + + ep_reload(xudc, ep->index); + ep_unpause(xudc, ep->index); + ep_unhalt(xudc, ep->index); + + if (usb_endpoint_xfer_isoc(desc)) { + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { + if (xudc->ep[i].desc && + usb_endpoint_xfer_bulk(xudc->ep[i].desc)) + ep_unpause(xudc, i); + } + } + +out: + dev_info(xudc->dev, "EP %u (type: %s, dir: %s) enabled\n", ep->index, + usb_ep_type_string(usb_endpoint_type(ep->desc)), + usb_endpoint_dir_in(ep->desc) ? "in" : "out"); + + return 0; +} + +static int tegra_xudc_ep_enable(struct usb_ep *usb_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct tegra_xudc_ep *ep; + struct tegra_xudc *xudc; + unsigned long flags; + int ret; + + if (!usb_ep || !desc || (desc->bDescriptorType != USB_DT_ENDPOINT)) + return -EINVAL; + + ep = to_xudc_ep(usb_ep); + xudc = ep->xudc; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = __tegra_xudc_ep_enable(ep, desc); +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static struct usb_request * +tegra_xudc_ep_alloc_request(struct usb_ep *usb_ep, gfp_t gfp) +{ + struct tegra_xudc_request *req; + + req = kzalloc(sizeof(*req), gfp); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->list); + + return &req->usb_req; +} + +static void tegra_xudc_ep_free_request(struct usb_ep *usb_ep, + struct usb_request *usb_req) +{ + struct tegra_xudc_request *req = to_xudc_req(usb_req); + + kfree(req); +} + +static struct usb_ep_ops tegra_xudc_ep_ops = { + .enable = tegra_xudc_ep_enable, + .disable = tegra_xudc_ep_disable, + .alloc_request = tegra_xudc_ep_alloc_request, + .free_request = tegra_xudc_ep_free_request, + .queue = tegra_xudc_ep_queue, + .dequeue = tegra_xudc_ep_dequeue, + .set_halt = tegra_xudc_ep_set_halt, +}; + +static int tegra_xudc_ep0_enable(struct usb_ep *usb_ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EBUSY; +} + +static int tegra_xudc_ep0_disable(struct usb_ep *usb_ep) +{ + return -EBUSY; +} + +static struct usb_ep_ops tegra_xudc_ep0_ops = { + .enable = tegra_xudc_ep0_enable, + .disable = tegra_xudc_ep0_disable, + .alloc_request = tegra_xudc_ep_alloc_request, + .free_request = tegra_xudc_ep_free_request, + .queue = tegra_xudc_ep_queue, + .dequeue = tegra_xudc_ep_dequeue, + .set_halt = tegra_xudc_ep_set_halt, +}; + +static int tegra_xudc_gadget_get_frame(struct usb_gadget *gadget) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + int ret; + + spin_lock_irqsave(&xudc->lock, flags); + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + + ret = (xudc_readl(xudc, MFINDEX) & MFINDEX_FRAME_MASK) >> + MFINDEX_FRAME_SHIFT; +unlock: + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static void tegra_xudc_resume_device_state(struct tegra_xudc *xudc) +{ + unsigned int i; + u32 val; + + ep_unpause_all(xudc); + + /* Direct link to U0. */ + val = xudc_readl(xudc, PORTSC); + if (((val & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT) != PORTSC_PLS_U0) { + val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); + val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_U0); + xudc_writel(xudc, val, PORTSC); + } + + if (xudc->device_state == USB_STATE_SUSPENDED) { + xudc->device_state = xudc->resume_state; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + xudc->resume_state = 0; + } + + /* + * Doorbells may be dropped if they are sent too soon (< ~200ns) + * after unpausing the endpoint. Wait for 500ns just to be safe. + */ + ndelay(500); + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) + tegra_xudc_ep_ring_doorbell(&xudc->ep[i]); +} + +static int tegra_xudc_gadget_wakeup(struct usb_gadget *gadget) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + int ret = 0; + u32 val; + + spin_lock_irqsave(&xudc->lock, flags); + + if (xudc->powergated) { + ret = -ESHUTDOWN; + goto unlock; + } + val = xudc_readl(xudc, PORTPM); + dev_dbg(xudc->dev, "%s: PORTPM=%#x, speed=%x\n", __func__, + val, gadget->speed); + + if (((xudc->gadget.speed <= USB_SPEED_HIGH) && + (val & PORTPM_RWE)) || + ((xudc->gadget.speed == USB_SPEED_SUPER) && + (val & PORTPM_FRWE))) { + tegra_xudc_resume_device_state(xudc); + + /* Send Device Notification packet. */ + if (xudc->gadget.speed == USB_SPEED_SUPER) { + val = DEVNOTIF_LO_TYPE(DEVNOTIF_LO_TYPE_FUNCTION_WAKE) + | DEVNOTIF_LO_TRIG; + xudc_writel(xudc, 0, DEVNOTIF_HI); + xudc_writel(xudc, val, DEVNOTIF_LO); + } + } + +unlock: + dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret); + spin_unlock_irqrestore(&xudc->lock, flags); + + return ret; +} + +static int tegra_xudc_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + u32 val; + + pm_runtime_get_sync(xudc->dev); + + spin_lock_irqsave(&xudc->lock, flags); + + if (is_on != xudc->pullup) { + val = xudc_readl(xudc, CTRL); + if (is_on) + val |= CTRL_ENABLE; + else + val &= ~CTRL_ENABLE; + xudc_writel(xudc, val, CTRL); + } + + xudc->pullup = is_on; + dev_dbg(xudc->dev, "%s: pullup:%d", __func__, is_on); + + spin_unlock_irqrestore(&xudc->lock, flags); + + pm_runtime_put(xudc->dev); + + return 0; +} + +static int tegra_xudc_gadget_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + u32 val; + int ret; + + if (!driver) + return -EINVAL; + + pm_runtime_get_sync(xudc->dev); + + spin_lock_irqsave(&xudc->lock, flags); + + if (xudc->driver) { + ret = -EBUSY; + goto unlock; + } + + xudc->setup_state = WAIT_FOR_SETUP; + xudc->device_state = USB_STATE_DEFAULT; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + ret = __tegra_xudc_ep_enable(&xudc->ep[0], &tegra_xudc_ep0_desc); + if (ret < 0) + goto unlock; + + val = xudc_readl(xudc, CTRL); + val |= CTRL_IE | CTRL_LSE; + xudc_writel(xudc, val, CTRL); + + val = xudc_readl(xudc, PORTHALT); + val |= PORTHALT_STCHG_INTR_EN; + xudc_writel(xudc, val, PORTHALT); + + if (xudc->pullup) { + val = xudc_readl(xudc, CTRL); + val |= CTRL_ENABLE; + xudc_writel(xudc, val, CTRL); + } + + xudc->driver = driver; +unlock: + dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret); + spin_unlock_irqrestore(&xudc->lock, flags); + + pm_runtime_put(xudc->dev); + + return ret; +} + +static int tegra_xudc_gadget_stop(struct usb_gadget *gadget) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + unsigned long flags; + u32 val; + + pm_runtime_get_sync(xudc->dev); + + spin_lock_irqsave(&xudc->lock, flags); + + val = xudc_readl(xudc, CTRL); + val &= ~(CTRL_IE | CTRL_ENABLE); + xudc_writel(xudc, val, CTRL); + + __tegra_xudc_ep_disable(&xudc->ep[0]); + + xudc->driver = NULL; + dev_dbg(xudc->dev, "Gadget stopped"); + + spin_unlock_irqrestore(&xudc->lock, flags); + + pm_runtime_put(xudc->dev); + + return 0; +} + +static int tegra_xudc_set_selfpowered(struct usb_gadget *gadget, int is_on) +{ + struct tegra_xudc *xudc = to_xudc(gadget); + + dev_dbg(xudc->dev, "%s: %d\n", __func__, is_on); + xudc->selfpowered = !!is_on; + + return 0; +} + +static struct usb_gadget_ops tegra_xudc_gadget_ops = { + .get_frame = tegra_xudc_gadget_get_frame, + .wakeup = tegra_xudc_gadget_wakeup, + .pullup = tegra_xudc_gadget_pullup, + .udc_start = tegra_xudc_gadget_start, + .udc_stop = tegra_xudc_gadget_stop, + .set_selfpowered = tegra_xudc_set_selfpowered, +}; + +static void no_op_complete(struct usb_ep *ep, struct usb_request *req) +{ +} + +static int +tegra_xudc_ep0_queue_status(struct tegra_xudc *xudc, + void (*cmpl)(struct usb_ep *, struct usb_request *)) +{ + xudc->ep0_req->usb_req.buf = NULL; + xudc->ep0_req->usb_req.dma = 0; + xudc->ep0_req->usb_req.length = 0; + xudc->ep0_req->usb_req.complete = cmpl; + xudc->ep0_req->usb_req.context = xudc; + + return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req); +} + +static int +tegra_xudc_ep0_queue_data(struct tegra_xudc *xudc, void *buf, size_t len, + void (*cmpl)(struct usb_ep *, struct usb_request *)) +{ + xudc->ep0_req->usb_req.buf = buf; + xudc->ep0_req->usb_req.length = len; + xudc->ep0_req->usb_req.complete = cmpl; + xudc->ep0_req->usb_req.context = xudc; + + return __tegra_xudc_ep_queue(&xudc->ep[0], xudc->ep0_req); +} + +static void tegra_xudc_ep0_req_done(struct tegra_xudc *xudc) +{ + switch (xudc->setup_state) { + case DATA_STAGE_XFER: + xudc->setup_state = STATUS_STAGE_RECV; + tegra_xudc_ep0_queue_status(xudc, no_op_complete); + break; + case DATA_STAGE_RECV: + xudc->setup_state = STATUS_STAGE_XFER; + tegra_xudc_ep0_queue_status(xudc, no_op_complete); + break; + default: + xudc->setup_state = WAIT_FOR_SETUP; + break; + } +} + +static int tegra_xudc_ep0_delegate_req(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + int ret; + + spin_unlock(&xudc->lock); + ret = xudc->driver->setup(&xudc->gadget, ctrl); + spin_lock(&xudc->lock); + + return ret; +} + +static void set_feature_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct tegra_xudc *xudc = req->context; + + if (xudc->test_mode_pattern) { + xudc_writel(xudc, xudc->test_mode_pattern, PORT_TM); + xudc->test_mode_pattern = 0; + } +} + +static int tegra_xudc_ep0_set_feature(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); + u32 feature = le16_to_cpu(ctrl->wValue); + u32 index = le16_to_cpu(ctrl->wIndex); + u32 val, ep; + int ret; + + if (le16_to_cpu(ctrl->wLength) != 0) + return -EINVAL; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (feature) { + case USB_DEVICE_REMOTE_WAKEUP: + if ((xudc->gadget.speed == USB_SPEED_SUPER) || + (xudc->device_state == USB_STATE_DEFAULT)) + return -EINVAL; + + val = xudc_readl(xudc, PORTPM); + if (set) + val |= PORTPM_RWE; + else + val &= ~PORTPM_RWE; + + xudc_writel(xudc, val, PORTPM); + break; + case USB_DEVICE_U1_ENABLE: + case USB_DEVICE_U2_ENABLE: + if ((xudc->device_state != USB_STATE_CONFIGURED) || + (xudc->gadget.speed != USB_SPEED_SUPER)) + return -EINVAL; + + val = xudc_readl(xudc, PORTPM); + if ((feature == USB_DEVICE_U1_ENABLE) && + xudc->soc->u1_enable) { + if (set) + val |= PORTPM_U1E; + else + val &= ~PORTPM_U1E; + } + + if ((feature == USB_DEVICE_U2_ENABLE) && + xudc->soc->u2_enable) { + if (set) + val |= PORTPM_U2E; + else + val &= ~PORTPM_U2E; + } + + xudc_writel(xudc, val, PORTPM); + break; + case USB_DEVICE_TEST_MODE: + if (xudc->gadget.speed != USB_SPEED_HIGH) + return -EINVAL; + + if (!set) + return -EINVAL; + + xudc->test_mode_pattern = index >> 8; + break; + default: + return -EINVAL; + } + + break; + case USB_RECIP_INTERFACE: + if (xudc->device_state != USB_STATE_CONFIGURED) + return -EINVAL; + + switch (feature) { + case USB_INTRF_FUNC_SUSPEND: + if (set) { + val = xudc_readl(xudc, PORTPM); + + if (index & USB_INTRF_FUNC_SUSPEND_RW) + val |= PORTPM_FRWE; + else + val &= ~PORTPM_FRWE; + + xudc_writel(xudc, val, PORTPM); + } + + return tegra_xudc_ep0_delegate_req(xudc, ctrl); + default: + return -EINVAL; + } + + break; + case USB_RECIP_ENDPOINT: + ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 + + ((index & USB_DIR_IN) ? 1 : 0); + + if ((xudc->device_state == USB_STATE_DEFAULT) || + ((xudc->device_state == USB_STATE_ADDRESS) && + (index != 0))) + return -EINVAL; + + ret = __tegra_xudc_ep_set_halt(&xudc->ep[ep], set); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + return tegra_xudc_ep0_queue_status(xudc, set_feature_complete); +} + +static int tegra_xudc_ep0_get_status(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + struct tegra_xudc_ep_context *ep_ctx; + u32 val, ep, index = le16_to_cpu(ctrl->wIndex); + u16 status = 0; + + if (!(ctrl->bRequestType & USB_DIR_IN)) + return -EINVAL; + + if ((le16_to_cpu(ctrl->wValue) != 0) || + (le16_to_cpu(ctrl->wLength) != 2)) + return -EINVAL; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + val = xudc_readl(xudc, PORTPM); + + if (xudc->selfpowered) + status |= BIT(USB_DEVICE_SELF_POWERED); + + if ((xudc->gadget.speed < USB_SPEED_SUPER) && + (val & PORTPM_RWE)) + status |= BIT(USB_DEVICE_REMOTE_WAKEUP); + + if (xudc->gadget.speed == USB_SPEED_SUPER) { + if (val & PORTPM_U1E) + status |= BIT(USB_DEV_STAT_U1_ENABLED); + if (val & PORTPM_U2E) + status |= BIT(USB_DEV_STAT_U2_ENABLED); + } + break; + case USB_RECIP_INTERFACE: + if (xudc->gadget.speed == USB_SPEED_SUPER) { + status |= USB_INTRF_STAT_FUNC_RW_CAP; + val = xudc_readl(xudc, PORTPM); + if (val & PORTPM_FRWE) + status |= USB_INTRF_STAT_FUNC_RW; + } + break; + case USB_RECIP_ENDPOINT: + ep = (index & USB_ENDPOINT_NUMBER_MASK) * 2 + + ((index & USB_DIR_IN) ? 1 : 0); + ep_ctx = &xudc->ep_context[ep]; + + if ((xudc->device_state != USB_STATE_CONFIGURED) && + ((xudc->device_state != USB_STATE_ADDRESS) || (ep != 0))) + return -EINVAL; + + if (ep_ctx_read_state(ep_ctx) == EP_STATE_DISABLED) + return -EINVAL; + + if (xudc_readl(xudc, EP_HALT) & BIT(ep)) + status |= BIT(USB_ENDPOINT_HALT); + break; + default: + return -EINVAL; + } + + xudc->status_buf = cpu_to_le16(status); + return tegra_xudc_ep0_queue_data(xudc, &xudc->status_buf, + sizeof(xudc->status_buf), + no_op_complete); +} + +static void set_sel_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* Nothing to do with SEL values */ +} + +static int tegra_xudc_ep0_set_sel(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | + USB_TYPE_STANDARD)) + return -EINVAL; + + if (xudc->device_state == USB_STATE_DEFAULT) + return -EINVAL; + + if ((le16_to_cpu(ctrl->wIndex) != 0) || + (le16_to_cpu(ctrl->wValue) != 0) || + (le16_to_cpu(ctrl->wLength) != 6)) + return -EINVAL; + + return tegra_xudc_ep0_queue_data(xudc, &xudc->sel_timing, + sizeof(xudc->sel_timing), + set_sel_complete); +} + +static void set_isoch_delay_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* Nothing to do with isoch delay */ +} + +static int tegra_xudc_ep0_set_isoch_delay(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + u32 delay = le16_to_cpu(ctrl->wValue); + + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | + USB_TYPE_STANDARD)) + return -EINVAL; + + if ((delay > 65535) || (le16_to_cpu(ctrl->wIndex) != 0) || + (le16_to_cpu(ctrl->wLength) != 0)) + return -EINVAL; + + xudc->isoch_delay = delay; + + return tegra_xudc_ep0_queue_status(xudc, set_isoch_delay_complete); +} + +static void set_address_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct tegra_xudc *xudc = req->context; + + if ((xudc->device_state == USB_STATE_DEFAULT) && + (xudc->dev_addr != 0)) { + xudc->device_state = USB_STATE_ADDRESS; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + } else if ((xudc->device_state == USB_STATE_ADDRESS) && + (xudc->dev_addr == 0)) { + xudc->device_state = USB_STATE_DEFAULT; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + } +} + +static int tegra_xudc_ep0_set_address(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + struct tegra_xudc_ep *ep0 = &xudc->ep[0]; + u32 val, addr = le16_to_cpu(ctrl->wValue); + + if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE | + USB_TYPE_STANDARD)) + return -EINVAL; + + if ((addr > 127) || (le16_to_cpu(ctrl->wIndex) != 0) || + (le16_to_cpu(ctrl->wLength) != 0)) + return -EINVAL; + + if (xudc->device_state == USB_STATE_CONFIGURED) + return -EINVAL; + + dev_dbg(xudc->dev, "set address: %u\n", addr); + + xudc->dev_addr = addr; + val = xudc_readl(xudc, CTRL); + val &= ~(CTRL_DEVADDR_MASK); + val |= CTRL_DEVADDR(addr); + xudc_writel(xudc, val, CTRL); + + ep_ctx_write_devaddr(ep0->context, addr); + + return tegra_xudc_ep0_queue_status(xudc, set_address_complete); +} + +static int tegra_xudc_ep0_standard_req(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl) +{ + int ret; + + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + dev_dbg(xudc->dev, "USB_REQ_GET_STATUS\n"); + ret = tegra_xudc_ep0_get_status(xudc, ctrl); + break; + case USB_REQ_SET_ADDRESS: + dev_dbg(xudc->dev, "USB_REQ_SET_ADDRESS\n"); + ret = tegra_xudc_ep0_set_address(xudc, ctrl); + break; + case USB_REQ_SET_SEL: + dev_dbg(xudc->dev, "USB_REQ_SET_SEL\n"); + ret = tegra_xudc_ep0_set_sel(xudc, ctrl); + break; + case USB_REQ_SET_ISOCH_DELAY: + dev_dbg(xudc->dev, "USB_REQ_SET_ISOCH_DELAY\n"); + ret = tegra_xudc_ep0_set_isoch_delay(xudc, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + dev_dbg(xudc->dev, "USB_REQ_CLEAR/SET_FEATURE\n"); + ret = tegra_xudc_ep0_set_feature(xudc, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + dev_dbg(xudc->dev, "USB_REQ_SET_CONFIGURATION\n"); + /* + * In theory we need to clear RUN bit before status stage of + * deconfig request sent, but this seems to be causing problems. + * Clear RUN once all endpoints are disabled instead. + */ + fallthrough; + default: + ret = tegra_xudc_ep0_delegate_req(xudc, ctrl); + break; + } + + return ret; +} + +static void tegra_xudc_handle_ep0_setup_packet(struct tegra_xudc *xudc, + struct usb_ctrlrequest *ctrl, + u16 seq_num) +{ + int ret; + + xudc->setup_seq_num = seq_num; + + /* Ensure EP0 is unhalted. */ + ep_unhalt(xudc, 0); + + /* + * On Tegra210, setup packets with sequence numbers 0xfffe or 0xffff + * are invalid. Halt EP0 until we get a valid packet. + */ + if (xudc->soc->invalid_seq_num && + (seq_num == 0xfffe || seq_num == 0xffff)) { + dev_warn(xudc->dev, "invalid sequence number detected\n"); + ep_halt(xudc, 0); + return; + } + + if (ctrl->wLength) + xudc->setup_state = (ctrl->bRequestType & USB_DIR_IN) ? + DATA_STAGE_XFER : DATA_STAGE_RECV; + else + xudc->setup_state = STATUS_STAGE_XFER; + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + ret = tegra_xudc_ep0_standard_req(xudc, ctrl); + else + ret = tegra_xudc_ep0_delegate_req(xudc, ctrl); + + if (ret < 0) { + dev_warn(xudc->dev, "setup request failed: %d\n", ret); + xudc->setup_state = WAIT_FOR_SETUP; + ep_halt(xudc, 0); + } +} + +static void tegra_xudc_handle_ep0_event(struct tegra_xudc *xudc, + struct tegra_xudc_trb *event) +{ + struct usb_ctrlrequest *ctrl = (struct usb_ctrlrequest *)event; + u16 seq_num = trb_read_seq_num(event); + + if (xudc->setup_state != WAIT_FOR_SETUP) { + /* + * The controller is in the process of handling another + * setup request. Queue subsequent requests and handle + * the last one once the controller reports a sequence + * number error. + */ + memcpy(&xudc->setup_packet.ctrl_req, ctrl, sizeof(*ctrl)); + xudc->setup_packet.seq_num = seq_num; + xudc->queued_setup_packet = true; + } else { + tegra_xudc_handle_ep0_setup_packet(xudc, ctrl, seq_num); + } +} + +static struct tegra_xudc_request * +trb_to_request(struct tegra_xudc_ep *ep, struct tegra_xudc_trb *trb) +{ + struct tegra_xudc_request *req; + + list_for_each_entry(req, &ep->queue, list) { + if (!req->trbs_queued) + break; + + if (trb_in_request(ep, req, trb)) + return req; + } + + return NULL; +} + +static void tegra_xudc_handle_transfer_completion(struct tegra_xudc *xudc, + struct tegra_xudc_ep *ep, + struct tegra_xudc_trb *event) +{ + struct tegra_xudc_request *req; + struct tegra_xudc_trb *trb; + bool short_packet; + + short_packet = (trb_read_cmpl_code(event) == + TRB_CMPL_CODE_SHORT_PACKET); + + trb = trb_phys_to_virt(ep, trb_read_data_ptr(event)); + req = trb_to_request(ep, trb); + + /* + * TDs are complete on short packet or when the completed TRB is the + * last TRB in the TD (the CHAIN bit is unset). + */ + if (req && (short_packet || (!trb_read_chain(trb) && + (req->trbs_needed == req->trbs_queued)))) { + struct tegra_xudc_trb *last = req->last_trb; + unsigned int residual; + + residual = trb_read_transfer_len(event); + req->usb_req.actual = req->usb_req.length - residual; + + dev_dbg(xudc->dev, "bytes transferred %u / %u\n", + req->usb_req.actual, req->usb_req.length); + + tegra_xudc_req_done(ep, req, 0); + + if (ep->desc && usb_endpoint_xfer_control(ep->desc)) + tegra_xudc_ep0_req_done(xudc); + + /* + * Advance the dequeue pointer past the end of the current TD + * on short packet completion. + */ + if (short_packet) { + ep->deq_ptr = (last - ep->transfer_ring) + 1; + if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1) + ep->deq_ptr = 0; + } + } else if (!req) { + dev_warn(xudc->dev, "transfer event on dequeued request\n"); + } + + if (ep->desc) + tegra_xudc_ep_kick_queue(ep); +} + +static void tegra_xudc_handle_transfer_event(struct tegra_xudc *xudc, + struct tegra_xudc_trb *event) +{ + unsigned int ep_index = trb_read_endpoint_id(event); + struct tegra_xudc_ep *ep = &xudc->ep[ep_index]; + struct tegra_xudc_trb *trb; + u16 comp_code; + + if (ep_ctx_read_state(ep->context) == EP_STATE_DISABLED) { + dev_warn(xudc->dev, "transfer event on disabled EP %u\n", + ep_index); + return; + } + + /* Update transfer ring dequeue pointer. */ + trb = trb_phys_to_virt(ep, trb_read_data_ptr(event)); + comp_code = trb_read_cmpl_code(event); + if (comp_code != TRB_CMPL_CODE_BABBLE_DETECTED_ERR) { + ep->deq_ptr = (trb - ep->transfer_ring) + 1; + + if (ep->deq_ptr == XUDC_TRANSFER_RING_SIZE - 1) + ep->deq_ptr = 0; + ep->ring_full = false; + } + + switch (comp_code) { + case TRB_CMPL_CODE_SUCCESS: + case TRB_CMPL_CODE_SHORT_PACKET: + tegra_xudc_handle_transfer_completion(xudc, ep, event); + break; + case TRB_CMPL_CODE_HOST_REJECTED: + dev_info(xudc->dev, "stream rejected on EP %u\n", ep_index); + + ep->stream_rejected = true; + break; + case TRB_CMPL_CODE_PRIME_PIPE_RECEIVED: + dev_info(xudc->dev, "prime pipe received on EP %u\n", ep_index); + + if (ep->stream_rejected) { + ep->stream_rejected = false; + /* + * An EP is stopped when a stream is rejected. Wait + * for the EP to report that it is stopped and then + * un-stop it. + */ + ep_wait_for_stopped(xudc, ep_index); + } + tegra_xudc_ep_ring_doorbell(ep); + break; + case TRB_CMPL_CODE_BABBLE_DETECTED_ERR: + /* + * Wait for the EP to be stopped so the controller stops + * processing doorbells. + */ + ep_wait_for_stopped(xudc, ep_index); + ep->enq_ptr = ep->deq_ptr; + tegra_xudc_ep_nuke(ep, -EIO); + /* FALLTHROUGH */ + case TRB_CMPL_CODE_STREAM_NUMP_ERROR: + case TRB_CMPL_CODE_CTRL_DIR_ERR: + case TRB_CMPL_CODE_INVALID_STREAM_TYPE_ERR: + case TRB_CMPL_CODE_RING_UNDERRUN: + case TRB_CMPL_CODE_RING_OVERRUN: + case TRB_CMPL_CODE_ISOCH_BUFFER_OVERRUN: + case TRB_CMPL_CODE_USB_TRANS_ERR: + case TRB_CMPL_CODE_TRB_ERR: + dev_err(xudc->dev, "completion error %#x on EP %u\n", + comp_code, ep_index); + + ep_halt(xudc, ep_index); + break; + case TRB_CMPL_CODE_CTRL_SEQNUM_ERR: + dev_info(xudc->dev, "sequence number error\n"); + + /* + * Kill any queued control request and skip to the last + * setup packet we received. + */ + tegra_xudc_ep_nuke(ep, -EINVAL); + xudc->setup_state = WAIT_FOR_SETUP; + if (!xudc->queued_setup_packet) + break; + + tegra_xudc_handle_ep0_setup_packet(xudc, + &xudc->setup_packet.ctrl_req, + xudc->setup_packet.seq_num); + xudc->queued_setup_packet = false; + break; + case TRB_CMPL_CODE_STOPPED: + dev_dbg(xudc->dev, "stop completion code on EP %u\n", + ep_index); + + /* Disconnected. */ + tegra_xudc_ep_nuke(ep, -ECONNREFUSED); + break; + default: + dev_dbg(xudc->dev, "completion event %#x on EP %u\n", + comp_code, ep_index); + break; + } +} + +static void tegra_xudc_reset(struct tegra_xudc *xudc) +{ + struct tegra_xudc_ep *ep0 = &xudc->ep[0]; + dma_addr_t deq_ptr; + unsigned int i; + + xudc->setup_state = WAIT_FOR_SETUP; + xudc->device_state = USB_STATE_DEFAULT; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + ep_unpause_all(xudc); + + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) + tegra_xudc_ep_nuke(&xudc->ep[i], -ESHUTDOWN); + + /* + * Reset sequence number and dequeue pointer to flush the transfer + * ring. + */ + ep0->deq_ptr = ep0->enq_ptr; + ep0->ring_full = false; + + xudc->setup_seq_num = 0; + xudc->queued_setup_packet = false; + + ep_ctx_write_seq_num(ep0->context, xudc->setup_seq_num); + + deq_ptr = trb_virt_to_phys(ep0, &ep0->transfer_ring[ep0->deq_ptr]); + + if (!dma_mapping_error(xudc->dev, deq_ptr)) { + ep_ctx_write_deq_ptr(ep0->context, deq_ptr); + ep_ctx_write_dcs(ep0->context, ep0->pcs); + } + + ep_unhalt_all(xudc); + ep_reload(xudc, 0); + ep_unpause(xudc, 0); +} + +static void tegra_xudc_port_connect(struct tegra_xudc *xudc) +{ + struct tegra_xudc_ep *ep0 = &xudc->ep[0]; + u16 maxpacket; + u32 val; + + val = (xudc_readl(xudc, PORTSC) & PORTSC_PS_MASK) >> PORTSC_PS_SHIFT; + switch (val) { + case PORTSC_PS_LS: + xudc->gadget.speed = USB_SPEED_LOW; + break; + case PORTSC_PS_FS: + xudc->gadget.speed = USB_SPEED_FULL; + break; + case PORTSC_PS_HS: + xudc->gadget.speed = USB_SPEED_HIGH; + break; + case PORTSC_PS_SS: + xudc->gadget.speed = USB_SPEED_SUPER; + break; + default: + xudc->gadget.speed = USB_SPEED_UNKNOWN; + break; + } + + xudc->device_state = USB_STATE_DEFAULT; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + xudc->setup_state = WAIT_FOR_SETUP; + + if (xudc->gadget.speed == USB_SPEED_SUPER) + maxpacket = 512; + else + maxpacket = 64; + + ep_ctx_write_max_packet_size(ep0->context, maxpacket); + tegra_xudc_ep0_desc.wMaxPacketSize = cpu_to_le16(maxpacket); + usb_ep_set_maxpacket_limit(&ep0->usb_ep, maxpacket); + + if (!xudc->soc->u1_enable) { + val = xudc_readl(xudc, PORTPM); + val &= ~(PORTPM_U1TIMEOUT_MASK); + xudc_writel(xudc, val, PORTPM); + } + + if (!xudc->soc->u2_enable) { + val = xudc_readl(xudc, PORTPM); + val &= ~(PORTPM_U2TIMEOUT_MASK); + xudc_writel(xudc, val, PORTPM); + } + + if (xudc->gadget.speed <= USB_SPEED_HIGH) { + val = xudc_readl(xudc, PORTPM); + val &= ~(PORTPM_L1S_MASK); + if (xudc->soc->lpm_enable) + val |= PORTPM_L1S(PORTPM_L1S_ACCEPT); + else + val |= PORTPM_L1S(PORTPM_L1S_NYET); + xudc_writel(xudc, val, PORTPM); + } + + val = xudc_readl(xudc, ST); + if (val & ST_RC) + xudc_writel(xudc, ST_RC, ST); +} + +static void tegra_xudc_port_disconnect(struct tegra_xudc *xudc) +{ + tegra_xudc_reset(xudc); + + if (xudc->driver && xudc->driver->disconnect) { + spin_unlock(&xudc->lock); + xudc->driver->disconnect(&xudc->gadget); + spin_lock(&xudc->lock); + } + + xudc->device_state = USB_STATE_NOTATTACHED; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + complete(&xudc->disconnect_complete); +} + +static void tegra_xudc_port_reset(struct tegra_xudc *xudc) +{ + tegra_xudc_reset(xudc); + + if (xudc->driver) { + spin_unlock(&xudc->lock); + usb_gadget_udc_reset(&xudc->gadget, xudc->driver); + spin_lock(&xudc->lock); + } + + tegra_xudc_port_connect(xudc); +} + +static void tegra_xudc_port_suspend(struct tegra_xudc *xudc) +{ + dev_dbg(xudc->dev, "port suspend\n"); + + xudc->resume_state = xudc->device_state; + xudc->device_state = USB_STATE_SUSPENDED; + usb_gadget_set_state(&xudc->gadget, xudc->device_state); + + if (xudc->driver->suspend) { + spin_unlock(&xudc->lock); + xudc->driver->suspend(&xudc->gadget); + spin_lock(&xudc->lock); + } +} + +static void tegra_xudc_port_resume(struct tegra_xudc *xudc) +{ + dev_dbg(xudc->dev, "port resume\n"); + + tegra_xudc_resume_device_state(xudc); + + if (xudc->driver->resume) { + spin_unlock(&xudc->lock); + xudc->driver->resume(&xudc->gadget); + spin_lock(&xudc->lock); + } +} + +static inline void clear_port_change(struct tegra_xudc *xudc, u32 flag) +{ + u32 val; + + val = xudc_readl(xudc, PORTSC); + val &= ~PORTSC_CHANGE_MASK; + val |= flag; + xudc_writel(xudc, val, PORTSC); +} + +static void __tegra_xudc_handle_port_status(struct tegra_xudc *xudc) +{ + u32 portsc, porthalt; + + porthalt = xudc_readl(xudc, PORTHALT); + if ((porthalt & PORTHALT_STCHG_REQ) && + (porthalt & PORTHALT_HALT_LTSSM)) { + dev_dbg(xudc->dev, "STCHG_REQ, PORTHALT = %#x\n", porthalt); + porthalt &= ~PORTHALT_HALT_LTSSM; + xudc_writel(xudc, porthalt, PORTHALT); + } + + portsc = xudc_readl(xudc, PORTSC); + if ((portsc & PORTSC_PRC) && (portsc & PORTSC_PR)) { + dev_dbg(xudc->dev, "PRC, PR, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_PRC | PORTSC_PED); +#define TOGGLE_VBUS_WAIT_MS 100 + if (xudc->soc->port_reset_quirk) { + schedule_delayed_work(&xudc->port_reset_war_work, + msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS)); + xudc->wait_for_sec_prc = 1; + } + } + + if ((portsc & PORTSC_PRC) && !(portsc & PORTSC_PR)) { + dev_dbg(xudc->dev, "PRC, Not PR, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_PRC | PORTSC_PED); + tegra_xudc_port_reset(xudc); + cancel_delayed_work(&xudc->port_reset_war_work); + xudc->wait_for_sec_prc = 0; + } + + portsc = xudc_readl(xudc, PORTSC); + if (portsc & PORTSC_WRC) { + dev_dbg(xudc->dev, "WRC, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_WRC | PORTSC_PED); + if (!(xudc_readl(xudc, PORTSC) & PORTSC_WPR)) + tegra_xudc_port_reset(xudc); + } + + portsc = xudc_readl(xudc, PORTSC); + if (portsc & PORTSC_CSC) { + dev_dbg(xudc->dev, "CSC, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_CSC); + + if (portsc & PORTSC_CCS) + tegra_xudc_port_connect(xudc); + else + tegra_xudc_port_disconnect(xudc); + + if (xudc->wait_csc) { + cancel_delayed_work(&xudc->plc_reset_work); + xudc->wait_csc = false; + } + } + + portsc = xudc_readl(xudc, PORTSC); + if (portsc & PORTSC_PLC) { + u32 pls = (portsc & PORTSC_PLS_MASK) >> PORTSC_PLS_SHIFT; + + dev_dbg(xudc->dev, "PLC, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_PLC); + switch (pls) { + case PORTSC_PLS_U3: + tegra_xudc_port_suspend(xudc); + break; + case PORTSC_PLS_U0: + if (xudc->gadget.speed < USB_SPEED_SUPER) + tegra_xudc_port_resume(xudc); + break; + case PORTSC_PLS_RESUME: + if (xudc->gadget.speed == USB_SPEED_SUPER) + tegra_xudc_port_resume(xudc); + break; + case PORTSC_PLS_INACTIVE: + schedule_delayed_work(&xudc->plc_reset_work, + msecs_to_jiffies(TOGGLE_VBUS_WAIT_MS)); + xudc->wait_csc = true; + break; + default: + break; + } + } + + if (portsc & PORTSC_CEC) { + dev_warn(xudc->dev, "CEC, PORTSC = %#x\n", portsc); + clear_port_change(xudc, PORTSC_CEC); + } + + dev_dbg(xudc->dev, "PORTSC = %#x\n", xudc_readl(xudc, PORTSC)); +} + +static void tegra_xudc_handle_port_status(struct tegra_xudc *xudc) +{ + while ((xudc_readl(xudc, PORTSC) & PORTSC_CHANGE_MASK) || + (xudc_readl(xudc, PORTHALT) & PORTHALT_STCHG_REQ)) + __tegra_xudc_handle_port_status(xudc); +} + +static void tegra_xudc_handle_event(struct tegra_xudc *xudc, + struct tegra_xudc_trb *event) +{ + u32 type = trb_read_type(event); + + dump_trb(xudc, "EVENT", event); + + switch (type) { + case TRB_TYPE_PORT_STATUS_CHANGE_EVENT: + tegra_xudc_handle_port_status(xudc); + break; + case TRB_TYPE_TRANSFER_EVENT: + tegra_xudc_handle_transfer_event(xudc, event); + break; + case TRB_TYPE_SETUP_PACKET_EVENT: + tegra_xudc_handle_ep0_event(xudc, event); + break; + default: + dev_info(xudc->dev, "Unrecognized TRB type = %#x\n", type); + break; + } +} + +static void tegra_xudc_process_event_ring(struct tegra_xudc *xudc) +{ + struct tegra_xudc_trb *event; + dma_addr_t erdp; + + while (true) { + event = xudc->event_ring[xudc->event_ring_index] + + xudc->event_ring_deq_ptr; + + if (trb_read_cycle(event) != xudc->ccs) + break; + + tegra_xudc_handle_event(xudc, event); + + xudc->event_ring_deq_ptr++; + if (xudc->event_ring_deq_ptr == XUDC_EVENT_RING_SIZE) { + xudc->event_ring_deq_ptr = 0; + xudc->event_ring_index++; + } + + if (xudc->event_ring_index == XUDC_NR_EVENT_RINGS) { + xudc->event_ring_index = 0; + xudc->ccs = !xudc->ccs; + } + } + + erdp = xudc->event_ring_phys[xudc->event_ring_index] + + xudc->event_ring_deq_ptr * sizeof(*event); + + xudc_writel(xudc, upper_32_bits(erdp), ERDPHI); + xudc_writel(xudc, lower_32_bits(erdp) | ERDPLO_EHB, ERDPLO); +} + +static irqreturn_t tegra_xudc_irq(int irq, void *data) +{ + struct tegra_xudc *xudc = data; + unsigned long flags; + u32 val; + + val = xudc_readl(xudc, ST); + if (!(val & ST_IP)) + return IRQ_NONE; + xudc_writel(xudc, ST_IP, ST); + + spin_lock_irqsave(&xudc->lock, flags); + tegra_xudc_process_event_ring(xudc); + spin_unlock_irqrestore(&xudc->lock, flags); + + return IRQ_HANDLED; +} + +static int tegra_xudc_alloc_ep(struct tegra_xudc *xudc, unsigned int index) +{ + struct tegra_xudc_ep *ep = &xudc->ep[index]; + + ep->xudc = xudc; + ep->index = index; + ep->context = &xudc->ep_context[index]; + INIT_LIST_HEAD(&ep->queue); + + /* + * EP1 would be the input endpoint corresponding to EP0, but since + * EP0 is bi-directional, EP1 is unused. + */ + if (index == 1) + return 0; + + ep->transfer_ring = dma_pool_alloc(xudc->transfer_ring_pool, + GFP_KERNEL, + &ep->transfer_ring_phys); + if (!ep->transfer_ring) + return -ENOMEM; + + if (index) { + snprintf(ep->name, sizeof(ep->name), "ep%u%s", index / 2, + (index % 2 == 0) ? "out" : "in"); + ep->usb_ep.name = ep->name; + usb_ep_set_maxpacket_limit(&ep->usb_ep, 1024); + ep->usb_ep.max_streams = 16; + ep->usb_ep.ops = &tegra_xudc_ep_ops; + ep->usb_ep.caps.type_bulk = true; + ep->usb_ep.caps.type_int = true; + if (index & 1) + ep->usb_ep.caps.dir_in = true; + else + ep->usb_ep.caps.dir_out = true; + list_add_tail(&ep->usb_ep.ep_list, &xudc->gadget.ep_list); + } else { + strscpy(ep->name, "ep0", 3); + ep->usb_ep.name = ep->name; + usb_ep_set_maxpacket_limit(&ep->usb_ep, 512); + ep->usb_ep.ops = &tegra_xudc_ep0_ops; + ep->usb_ep.caps.type_control = true; + ep->usb_ep.caps.dir_in = true; + ep->usb_ep.caps.dir_out = true; + } + + return 0; +} + +static void tegra_xudc_free_ep(struct tegra_xudc *xudc, unsigned int index) +{ + struct tegra_xudc_ep *ep = &xudc->ep[index]; + + /* + * EP1 would be the input endpoint corresponding to EP0, but since + * EP0 is bi-directional, EP1 is unused. + */ + if (index == 1) + return; + + dma_pool_free(xudc->transfer_ring_pool, ep->transfer_ring, + ep->transfer_ring_phys); +} + +static int tegra_xudc_alloc_eps(struct tegra_xudc *xudc) +{ + struct usb_request *req; + unsigned int i; + int err; + + xudc->ep_context = + dma_alloc_coherent(xudc->dev, XUDC_NR_EPS * + sizeof(*xudc->ep_context), + &xudc->ep_context_phys, GFP_KERNEL); + if (!xudc->ep_context) + return -ENOMEM; + + xudc->transfer_ring_pool = + dmam_pool_create(dev_name(xudc->dev), xudc->dev, + XUDC_TRANSFER_RING_SIZE * + sizeof(struct tegra_xudc_trb), + sizeof(struct tegra_xudc_trb), 0); + if (!xudc->transfer_ring_pool) { + err = -ENOMEM; + goto free_ep_context; + } + + INIT_LIST_HEAD(&xudc->gadget.ep_list); + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) { + err = tegra_xudc_alloc_ep(xudc, i); + if (err < 0) + goto free_eps; + } + + req = tegra_xudc_ep_alloc_request(&xudc->ep[0].usb_ep, GFP_KERNEL); + if (!req) { + err = -ENOMEM; + goto free_eps; + } + xudc->ep0_req = to_xudc_req(req); + + return 0; + +free_eps: + for (; i > 0; i--) + tegra_xudc_free_ep(xudc, i - 1); +free_ep_context: + dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context), + xudc->ep_context, xudc->ep_context_phys); + return err; +} + +static void tegra_xudc_init_eps(struct tegra_xudc *xudc) +{ + xudc_writel(xudc, lower_32_bits(xudc->ep_context_phys), ECPLO); + xudc_writel(xudc, upper_32_bits(xudc->ep_context_phys), ECPHI); +} + +static void tegra_xudc_free_eps(struct tegra_xudc *xudc) +{ + unsigned int i; + + tegra_xudc_ep_free_request(&xudc->ep[0].usb_ep, + &xudc->ep0_req->usb_req); + + for (i = 0; i < ARRAY_SIZE(xudc->ep); i++) + tegra_xudc_free_ep(xudc, i); + + dma_free_coherent(xudc->dev, XUDC_NR_EPS * sizeof(*xudc->ep_context), + xudc->ep_context, xudc->ep_context_phys); +} + +static int tegra_xudc_alloc_event_ring(struct tegra_xudc *xudc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { + xudc->event_ring[i] = + dma_alloc_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * + sizeof(*xudc->event_ring[i]), + &xudc->event_ring_phys[i], + GFP_KERNEL); + if (!xudc->event_ring[i]) + goto free_dma; + } + + return 0; + +free_dma: + for (; i > 0; i--) { + dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * + sizeof(*xudc->event_ring[i - 1]), + xudc->event_ring[i - 1], + xudc->event_ring_phys[i - 1]); + } + return -ENOMEM; +} + +static void tegra_xudc_init_event_ring(struct tegra_xudc *xudc) +{ + unsigned int i; + u32 val; + + val = xudc_readl(xudc, SPARAM); + val &= ~(SPARAM_ERSTMAX_MASK); + val |= SPARAM_ERSTMAX(XUDC_NR_EVENT_RINGS); + xudc_writel(xudc, val, SPARAM); + + for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { + memset(xudc->event_ring[i], 0, XUDC_EVENT_RING_SIZE * + sizeof(*xudc->event_ring[i])); + + val = xudc_readl(xudc, ERSTSZ); + val &= ~(ERSTSZ_ERSTXSZ_MASK << ERSTSZ_ERSTXSZ_SHIFT(i)); + val |= XUDC_EVENT_RING_SIZE << ERSTSZ_ERSTXSZ_SHIFT(i); + xudc_writel(xudc, val, ERSTSZ); + + xudc_writel(xudc, lower_32_bits(xudc->event_ring_phys[i]), + ERSTXBALO(i)); + xudc_writel(xudc, upper_32_bits(xudc->event_ring_phys[i]), + ERSTXBAHI(i)); + } + + val = lower_32_bits(xudc->event_ring_phys[0]); + xudc_writel(xudc, val, ERDPLO); + val |= EREPLO_ECS; + xudc_writel(xudc, val, EREPLO); + + val = upper_32_bits(xudc->event_ring_phys[0]); + xudc_writel(xudc, val, ERDPHI); + xudc_writel(xudc, val, EREPHI); + + xudc->ccs = true; + xudc->event_ring_index = 0; + xudc->event_ring_deq_ptr = 0; +} + +static void tegra_xudc_free_event_ring(struct tegra_xudc *xudc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xudc->event_ring); i++) { + dma_free_coherent(xudc->dev, XUDC_EVENT_RING_SIZE * + sizeof(*xudc->event_ring[i]), + xudc->event_ring[i], + xudc->event_ring_phys[i]); + } +} + +static void tegra_xudc_fpci_ipfs_init(struct tegra_xudc *xudc) +{ + u32 val; + + if (xudc->soc->has_ipfs) { + val = ipfs_readl(xudc, XUSB_DEV_CONFIGURATION_0); + val |= XUSB_DEV_CONFIGURATION_0_EN_FPCI; + ipfs_writel(xudc, val, XUSB_DEV_CONFIGURATION_0); + usleep_range(10, 15); + } + + /* Enable bus master */ + val = XUSB_DEV_CFG_1_IO_SPACE_EN | XUSB_DEV_CFG_1_MEMORY_SPACE_EN | + XUSB_DEV_CFG_1_BUS_MASTER_EN; + fpci_writel(xudc, val, XUSB_DEV_CFG_1); + + /* Program BAR0 space */ + val = fpci_readl(xudc, XUSB_DEV_CFG_4); + val &= ~(XUSB_DEV_CFG_4_BASE_ADDR_MASK); + val |= xudc->phys_base & (XUSB_DEV_CFG_4_BASE_ADDR_MASK); + + fpci_writel(xudc, val, XUSB_DEV_CFG_4); + fpci_writel(xudc, upper_32_bits(xudc->phys_base), XUSB_DEV_CFG_5); + + usleep_range(100, 200); + + if (xudc->soc->has_ipfs) { + /* Enable interrupt assertion */ + val = ipfs_readl(xudc, XUSB_DEV_INTR_MASK_0); + val |= XUSB_DEV_INTR_MASK_0_IP_INT_MASK; + ipfs_writel(xudc, val, XUSB_DEV_INTR_MASK_0); + } +} + +static void tegra_xudc_device_params_init(struct tegra_xudc *xudc) +{ + u32 val, imod; + + if (xudc->soc->has_ipfs) { + val = xudc_readl(xudc, BLCG); + val |= BLCG_ALL; + val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE | + BLCG_COREPLL_PWRDN); + val |= BLCG_IOPLL_0_PWRDN; + val |= BLCG_IOPLL_1_PWRDN; + val |= BLCG_IOPLL_2_PWRDN; + + xudc_writel(xudc, val, BLCG); + } + + /* Set a reasonable U3 exit timer value. */ + val = xudc_readl(xudc, SSPX_CORE_PADCTL4); + val &= ~(SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK); + val |= SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(0x5dc0); + xudc_writel(xudc, val, SSPX_CORE_PADCTL4); + + /* Default ping LFPS tBurst is too large. */ + val = xudc_readl(xudc, SSPX_CORE_CNT0); + val &= ~(SSPX_CORE_CNT0_PING_TBURST_MASK); + val |= SSPX_CORE_CNT0_PING_TBURST(0xa); + xudc_writel(xudc, val, SSPX_CORE_CNT0); + + /* Default tPortConfiguration timeout is too small. */ + val = xudc_readl(xudc, SSPX_CORE_CNT30); + val &= ~(SSPX_CORE_CNT30_LMPITP_TIMER_MASK); + val |= SSPX_CORE_CNT30_LMPITP_TIMER(0x978); + xudc_writel(xudc, val, SSPX_CORE_CNT30); + + if (xudc->soc->lpm_enable) { + /* Set L1 resume duration to 95 us. */ + val = xudc_readl(xudc, HSFSPI_COUNT13); + val &= ~(HSFSPI_COUNT13_U2_RESUME_K_DURATION_MASK); + val |= HSFSPI_COUNT13_U2_RESUME_K_DURATION(0x2c88); + xudc_writel(xudc, val, HSFSPI_COUNT13); + } + + /* + * Compliacne suite appears to be violating polling LFPS tBurst max + * of 1.4us. Send 1.45us instead. + */ + val = xudc_readl(xudc, SSPX_CORE_CNT32); + val &= ~(SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK); + val |= SSPX_CORE_CNT32_POLL_TBURST_MAX(0xb0); + xudc_writel(xudc, val, SSPX_CORE_CNT32); + + /* Direct HS/FS port instance to RxDetect. */ + val = xudc_readl(xudc, CFG_DEV_FE); + val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); + val |= CFG_DEV_FE_PORTREGSEL(CFG_DEV_FE_PORTREGSEL_HSFS_PI); + xudc_writel(xudc, val, CFG_DEV_FE); + + val = xudc_readl(xudc, PORTSC); + val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); + val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT); + xudc_writel(xudc, val, PORTSC); + + /* Direct SS port instance to RxDetect. */ + val = xudc_readl(xudc, CFG_DEV_FE); + val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); + val |= CFG_DEV_FE_PORTREGSEL_SS_PI & CFG_DEV_FE_PORTREGSEL_MASK; + xudc_writel(xudc, val, CFG_DEV_FE); + + val = xudc_readl(xudc, PORTSC); + val &= ~(PORTSC_CHANGE_MASK | PORTSC_PLS_MASK); + val |= PORTSC_LWS | PORTSC_PLS(PORTSC_PLS_RXDETECT); + xudc_writel(xudc, val, PORTSC); + + /* Restore port instance. */ + val = xudc_readl(xudc, CFG_DEV_FE); + val &= ~(CFG_DEV_FE_PORTREGSEL_MASK); + xudc_writel(xudc, val, CFG_DEV_FE); + + /* + * Enable INFINITE_SS_RETRY to prevent device from entering + * Disabled.Error when attached to buggy SuperSpeed hubs. + */ + val = xudc_readl(xudc, CFG_DEV_FE); + val |= CFG_DEV_FE_INFINITE_SS_RETRY; + xudc_writel(xudc, val, CFG_DEV_FE); + + /* Set interrupt moderation. */ + imod = XUDC_INTERRUPT_MODERATION_US * 4; + val = xudc_readl(xudc, RT_IMOD); + val &= ~((RT_IMOD_IMODI_MASK) | (RT_IMOD_IMODC_MASK)); + val |= (RT_IMOD_IMODI(imod) | RT_IMOD_IMODC(imod)); + xudc_writel(xudc, val, RT_IMOD); + + /* increase SSPI transaction timeout from 32us to 512us */ + val = xudc_readl(xudc, CFG_DEV_SSPI_XFER); + val &= ~(CFG_DEV_SSPI_XFER_ACKTIMEOUT_MASK); + val |= CFG_DEV_SSPI_XFER_ACKTIMEOUT(0xf000); + xudc_writel(xudc, val, CFG_DEV_SSPI_XFER); +} + +static int tegra_xudc_phy_init(struct tegra_xudc *xudc) +{ + int err; + + err = phy_init(xudc->utmi_phy); + if (err < 0) { + dev_err(xudc->dev, "utmi phy init failed: %d\n", err); + return err; + } + + err = phy_init(xudc->usb3_phy); + if (err < 0) { + dev_err(xudc->dev, "usb3 phy init failed: %d\n", err); + goto exit_utmi_phy; + } + + return 0; + +exit_utmi_phy: + phy_exit(xudc->utmi_phy); + return err; +} + +static void tegra_xudc_phy_exit(struct tegra_xudc *xudc) +{ + phy_exit(xudc->usb3_phy); + phy_exit(xudc->utmi_phy); +} + +static const char * const tegra210_xudc_supply_names[] = { + "hvdd-usb", + "avddio-usb", +}; + +static const char * const tegra210_xudc_clock_names[] = { + "dev", + "ss", + "ss_src", + "hs_src", + "fs_src", +}; + +static const char * const tegra186_xudc_clock_names[] = { + "dev", + "ss", + "ss_src", + "fs_src", +}; + +static struct tegra_xudc_soc tegra210_xudc_soc_data = { + .supply_names = tegra210_xudc_supply_names, + .num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names), + .clock_names = tegra210_xudc_clock_names, + .num_clks = ARRAY_SIZE(tegra210_xudc_clock_names), + .u1_enable = false, + .u2_enable = true, + .lpm_enable = false, + .invalid_seq_num = true, + .pls_quirk = true, + .port_reset_quirk = true, + .has_ipfs = true, +}; + +static struct tegra_xudc_soc tegra186_xudc_soc_data = { + .clock_names = tegra186_xudc_clock_names, + .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names), + .u1_enable = true, + .u2_enable = true, + .lpm_enable = false, + .invalid_seq_num = false, + .pls_quirk = false, + .port_reset_quirk = false, + .has_ipfs = false, +}; + +static const struct of_device_id tegra_xudc_of_match[] = { + { + .compatible = "nvidia,tegra210-xudc", + .data = &tegra210_xudc_soc_data + }, + { + .compatible = "nvidia,tegra186-xudc", + .data = &tegra186_xudc_soc_data + }, + { } +}; +MODULE_DEVICE_TABLE(of, tegra_xudc_of_match); + +static void tegra_xudc_powerdomain_remove(struct tegra_xudc *xudc) +{ + if (xudc->genpd_dl_ss) + device_link_del(xudc->genpd_dl_ss); + if (xudc->genpd_dl_device) + device_link_del(xudc->genpd_dl_device); + if (xudc->genpd_dev_ss) + dev_pm_domain_detach(xudc->genpd_dev_ss, true); + if (xudc->genpd_dev_device) + dev_pm_domain_detach(xudc->genpd_dev_device, true); +} + +static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) +{ + struct device *dev = xudc->dev; + int err; + + xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, + "dev"); + if (IS_ERR(xudc->genpd_dev_device)) { + err = PTR_ERR(xudc->genpd_dev_device); + dev_err(dev, "failed to get dev pm-domain: %d\n", err); + return err; + } + + xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss"); + if (IS_ERR(xudc->genpd_dev_ss)) { + err = PTR_ERR(xudc->genpd_dev_ss); + dev_err(dev, "failed to get superspeed pm-domain: %d\n", err); + return err; + } + + xudc->genpd_dl_device = device_link_add(dev, xudc->genpd_dev_device, + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS); + if (!xudc->genpd_dl_device) { + dev_err(dev, "adding usb device device link failed!\n"); + return -ENODEV; + } + + xudc->genpd_dl_ss = device_link_add(dev, xudc->genpd_dev_ss, + DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS); + if (!xudc->genpd_dl_ss) { + dev_err(dev, "adding superspeed device link failed!\n"); + return -ENODEV; + } + + return 0; +} + +static int tegra_xudc_probe(struct platform_device *pdev) +{ + struct tegra_xudc *xudc; + struct resource *res; + struct usb_role_switch_desc role_sx_desc = { 0 }; + unsigned int i; + int err; + + xudc = devm_kzalloc(&pdev->dev, sizeof(*xudc), GFP_ATOMIC); + if (!xudc) + return -ENOMEM; + + xudc->dev = &pdev->dev; + platform_set_drvdata(pdev, xudc); + + xudc->soc = of_device_get_match_data(&pdev->dev); + if (!xudc->soc) + return -ENODEV; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); + xudc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xudc->base)) + return PTR_ERR(xudc->base); + xudc->phys_base = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fpci"); + xudc->fpci = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xudc->fpci)) + return PTR_ERR(xudc->fpci); + + if (xudc->soc->has_ipfs) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "ipfs"); + xudc->ipfs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xudc->ipfs)) + return PTR_ERR(xudc->ipfs); + } + + xudc->irq = platform_get_irq(pdev, 0); + if (xudc->irq < 0) { + dev_err(xudc->dev, "failed to get IRQ: %d\n", + xudc->irq); + return xudc->irq; + } + + err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0, + dev_name(&pdev->dev), xudc); + if (err < 0) { + dev_err(xudc->dev, "failed to claim IRQ#%u: %d\n", xudc->irq, + err); + return err; + } + + xudc->clks = devm_kcalloc(&pdev->dev, xudc->soc->num_clks, + sizeof(*xudc->clks), GFP_KERNEL); + if (!xudc->clks) + return -ENOMEM; + + for (i = 0; i < xudc->soc->num_clks; i++) + xudc->clks[i].id = xudc->soc->clock_names[i]; + + err = devm_clk_bulk_get(&pdev->dev, xudc->soc->num_clks, + xudc->clks); + if (err) { + dev_err(xudc->dev, "failed to request clks %d\n", err); + return err; + } + + xudc->supplies = devm_kcalloc(&pdev->dev, xudc->soc->num_supplies, + sizeof(*xudc->supplies), GFP_KERNEL); + if (!xudc->supplies) + return -ENOMEM; + + for (i = 0; i < xudc->soc->num_supplies; i++) + xudc->supplies[i].supply = xudc->soc->supply_names[i]; + + err = devm_regulator_bulk_get(&pdev->dev, xudc->soc->num_supplies, + xudc->supplies); + if (err) { + dev_err(xudc->dev, "failed to request regulators %d\n", err); + return err; + } + + xudc->padctl = tegra_xusb_padctl_get(&pdev->dev); + if (IS_ERR(xudc->padctl)) + return PTR_ERR(xudc->padctl); + + err = regulator_bulk_enable(xudc->soc->num_supplies, xudc->supplies); + if (err) { + dev_err(xudc->dev, "failed to enable regulators %d\n", err); + goto put_padctl; + } + + xudc->usb3_phy = devm_phy_optional_get(&pdev->dev, "usb3"); + if (IS_ERR(xudc->usb3_phy)) { + err = PTR_ERR(xudc->usb3_phy); + dev_err(xudc->dev, "failed to get usb3 phy: %d\n", err); + goto disable_regulator; + } + + xudc->utmi_phy = devm_phy_optional_get(&pdev->dev, "usb2"); + if (IS_ERR(xudc->utmi_phy)) { + err = PTR_ERR(xudc->utmi_phy); + dev_err(xudc->dev, "failed to get usb2 phy: %d\n", err); + goto disable_regulator; + } + + err = tegra_xudc_powerdomain_init(xudc); + if (err) + goto put_powerdomains; + + err = tegra_xudc_phy_init(xudc); + if (err) + goto put_powerdomains; + + err = tegra_xudc_alloc_event_ring(xudc); + if (err) + goto disable_phy; + + err = tegra_xudc_alloc_eps(xudc); + if (err) + goto free_event_ring; + + spin_lock_init(&xudc->lock); + + init_completion(&xudc->disconnect_complete); + + INIT_WORK(&xudc->usb_role_sw_work, tegra_xudc_usb_role_sw_work); + + INIT_DELAYED_WORK(&xudc->plc_reset_work, tegra_xudc_plc_reset_work); + + INIT_DELAYED_WORK(&xudc->port_reset_war_work, + tegra_xudc_port_reset_war_work); + + if (of_property_read_bool(xudc->dev->of_node, "usb-role-switch")) { + role_sx_desc.set = tegra_xudc_usb_role_sw_set; + role_sx_desc.fwnode = dev_fwnode(xudc->dev); + + xudc->usb_role_sw = usb_role_switch_register(xudc->dev, + &role_sx_desc); + if (IS_ERR(xudc->usb_role_sw)) { + err = PTR_ERR(xudc->usb_role_sw); + dev_err(xudc->dev, "Failed to register USB role SW: %d", + err); + goto free_eps; + } + } else { + /* Set the mode as device mode and this keeps phy always ON */ + dev_info(xudc->dev, "Set usb role to device mode always"); + schedule_work(&xudc->usb_role_sw_work); + } + + pm_runtime_enable(&pdev->dev); + + xudc->gadget.ops = &tegra_xudc_gadget_ops; + xudc->gadget.ep0 = &xudc->ep[0].usb_ep; + xudc->gadget.name = "tegra-xudc"; + xudc->gadget.max_speed = USB_SPEED_SUPER; + + err = usb_add_gadget_udc(&pdev->dev, &xudc->gadget); + if (err) { + dev_err(&pdev->dev, "failed to add USB gadget: %d\n", err); + goto free_eps; + } + + return 0; + +free_eps: + tegra_xudc_free_eps(xudc); +free_event_ring: + tegra_xudc_free_event_ring(xudc); +disable_phy: + tegra_xudc_phy_exit(xudc); +put_powerdomains: + tegra_xudc_powerdomain_remove(xudc); +disable_regulator: + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); +put_padctl: + tegra_xusb_padctl_put(xudc->padctl); + + return err; +} + +static int tegra_xudc_remove(struct platform_device *pdev) +{ + struct tegra_xudc *xudc = platform_get_drvdata(pdev); + + pm_runtime_get_sync(xudc->dev); + + cancel_delayed_work(&xudc->plc_reset_work); + + if (xudc->usb_role_sw) { + usb_role_switch_unregister(xudc->usb_role_sw); + cancel_work_sync(&xudc->usb_role_sw_work); + } + + usb_del_gadget_udc(&xudc->gadget); + + tegra_xudc_free_eps(xudc); + tegra_xudc_free_event_ring(xudc); + + tegra_xudc_powerdomain_remove(xudc); + + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); + + phy_power_off(xudc->utmi_phy); + phy_power_off(xudc->usb3_phy); + + tegra_xudc_phy_exit(xudc); + + pm_runtime_disable(xudc->dev); + pm_runtime_put(xudc->dev); + + tegra_xusb_padctl_put(xudc->padctl); + + return 0; +} + +static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc) +{ + unsigned long flags; + + dev_dbg(xudc->dev, "entering ELPG\n"); + + spin_lock_irqsave(&xudc->lock, flags); + + xudc->powergated = true; + xudc->saved_regs.ctrl = xudc_readl(xudc, CTRL); + xudc->saved_regs.portpm = xudc_readl(xudc, PORTPM); + xudc_writel(xudc, 0, CTRL); + + spin_unlock_irqrestore(&xudc->lock, flags); + + clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks); + + regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); + + dev_dbg(xudc->dev, "entering ELPG done\n"); + return 0; +} + +static int __maybe_unused tegra_xudc_unpowergate(struct tegra_xudc *xudc) +{ + unsigned long flags; + int err; + + dev_dbg(xudc->dev, "exiting ELPG\n"); + + err = regulator_bulk_enable(xudc->soc->num_supplies, + xudc->supplies); + if (err < 0) + return err; + + err = clk_bulk_prepare_enable(xudc->soc->num_clks, xudc->clks); + if (err < 0) + return err; + + tegra_xudc_fpci_ipfs_init(xudc); + + tegra_xudc_device_params_init(xudc); + + tegra_xudc_init_event_ring(xudc); + + tegra_xudc_init_eps(xudc); + + xudc_writel(xudc, xudc->saved_regs.portpm, PORTPM); + xudc_writel(xudc, xudc->saved_regs.ctrl, CTRL); + + spin_lock_irqsave(&xudc->lock, flags); + xudc->powergated = false; + spin_unlock_irqrestore(&xudc->lock, flags); + + dev_dbg(xudc->dev, "exiting ELPG done\n"); + return 0; +} + +static int __maybe_unused tegra_xudc_suspend(struct device *dev) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&xudc->lock, flags); + xudc->suspended = true; + spin_unlock_irqrestore(&xudc->lock, flags); + + flush_work(&xudc->usb_role_sw_work); + + /* Forcibly disconnect before powergating. */ + tegra_xudc_device_mode_off(xudc); + + if (!pm_runtime_status_suspended(dev)) + tegra_xudc_powergate(xudc); + + pm_runtime_disable(dev); + + return 0; +} + +static int __maybe_unused tegra_xudc_resume(struct device *dev) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + unsigned long flags; + int err; + + err = tegra_xudc_unpowergate(xudc); + if (err < 0) + return err; + + spin_lock_irqsave(&xudc->lock, flags); + xudc->suspended = false; + spin_unlock_irqrestore(&xudc->lock, flags); + + schedule_work(&xudc->usb_role_sw_work); + + pm_runtime_enable(dev); + + return 0; +} + +static int __maybe_unused tegra_xudc_runtime_suspend(struct device *dev) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + + return tegra_xudc_powergate(xudc); +} + +static int __maybe_unused tegra_xudc_runtime_resume(struct device *dev) +{ + struct tegra_xudc *xudc = dev_get_drvdata(dev); + + return tegra_xudc_unpowergate(xudc); +} + +static const struct dev_pm_ops tegra_xudc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_xudc_suspend, tegra_xudc_resume) + SET_RUNTIME_PM_OPS(tegra_xudc_runtime_suspend, + tegra_xudc_runtime_resume, NULL) +}; + +static struct platform_driver tegra_xudc_driver = { + .probe = tegra_xudc_probe, + .remove = tegra_xudc_remove, + .driver = { + .name = "tegra-xudc", + .pm = &tegra_xudc_pm_ops, + .of_match_table = tegra_xudc_of_match, + }, +}; +module_platform_driver(tegra_xudc_driver); + +MODULE_DESCRIPTION("NVIDIA Tegra XUSB Device Controller"); +MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>"); +MODULE_AUTHOR("Hui Fu <hfu@nvidia.com>"); +MODULE_AUTHOR("Nagarjuna Kristam <nkristam@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index b1f4104d1283..29d8e5f8bb58 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -2074,10 +2074,8 @@ static int xudc_probe(struct platform_device *pdev) return PTR_ERR(udc->addr); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "unable to get irq\n"); + if (irq < 0) return irq; - } ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0, dev_name(&pdev->dev), udc); if (ret < 0) { |