Near-term plans include converting all of them, except for "gadgetfs".
</para>
- !Edrivers/usb/gadget/f_acm.c
- !Edrivers/usb/gadget/f_ecm.c
- !Edrivers/usb/gadget/f_subset.c
- !Edrivers/usb/gadget/f_obex.c
- !Edrivers/usb/gadget/f_serial.c
+ !Edrivers/usb/gadget/function/f_acm.c
+ !Edrivers/usb/gadget/function/f_ecm.c
+ !Edrivers/usb/gadget/function/f_subset.c
+ !Edrivers/usb/gadget/function/f_obex.c
+ !Edrivers/usb/gadget/function/f_serial.c
</sect1>
<para>Systems need specialized hardware support to implement OTG,
notably including a special <emphasis>Mini-AB</emphasis> jack
-and associated transciever to support <emphasis>Dual-Role</emphasis>
+and associated transceiver to support <emphasis>Dual-Role</emphasis>
operation:
they can act either as a host, using the standard
Linux-USB host side driver stack,
#define USBOTGSS_DEV_EBC_EN 0x0110
#define USBOTGSS_DEBUG_OFFSET 0x0600
- /* REVISION REGISTER */
- #define USBOTGSS_REVISION_XMAJOR(reg) ((reg >> 8) & 0x7)
- #define USBOTGSS_REVISION_XMAJOR1 1
- #define USBOTGSS_REVISION_XMAJOR2 2
/* SYSCONFIG REGISTER */
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
u32 irq_eoi_offset;
u32 debug_offset;
u32 irq0_offset;
- u32 revision;
u32 dma_status:1;
{
struct platform_device *pdev = to_platform_device(dev);
- platform_device_unregister(pdev);
+ of_device_unregister(pdev);
return 0;
}
return NOTIFY_DONE;
}
+ static void dwc3_omap_map_offset(struct dwc3_omap *omap)
+ {
+ struct device_node *node = omap->dev->of_node;
+
+ /*
+ * Differentiate between OMAP5 and AM437x.
+ *
+ * For OMAP5(ES2.0) and AM437x wrapper revision is same, even
+ * though there are changes in wrapper register offsets.
+ *
+ * Using dt compatible to differentiate AM437x.
+ */
+ if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
+ omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
+ omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
+ omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
+ omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
+ omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
+ }
+ }
+
+ static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap)
+ {
+ u32 reg;
+ struct device_node *node = omap->dev->of_node;
+ int utmi_mode = 0;
+
+ reg = dwc3_omap_read_utmi_status(omap);
+
+ of_property_read_u32(node, "utmi-mode", &utmi_mode);
+
+ switch (utmi_mode) {
+ case DWC3_OMAP_UTMI_MODE_SW:
+ reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ case DWC3_OMAP_UTMI_MODE_HW:
+ reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ default:
+ dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode);
+ }
+
+ dwc3_omap_write_utmi_status(omap, reg);
+ }
+
+ static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
+ {
+ u32 ret;
+ struct device_node *node = omap->dev->of_node;
+ struct extcon_dev *edev;
+
+ if (of_property_read_bool(node, "extcon")) {
+ edev = extcon_get_edev_by_phandle(omap->dev, 0);
+ if (IS_ERR(edev)) {
+ dev_vdbg(omap->dev, "couldn't get extcon device\n");
+ return -EPROBE_DEFER;
+ }
+
+ omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
+ ret = extcon_register_interest(&omap->extcon_vbus_dev,
+ edev->name, "USB",
+ &omap->vbus_nb);
+ if (ret < 0)
+ dev_vdbg(omap->dev, "failed to register notifier for USB\n");
+
+ omap->id_nb.notifier_call = dwc3_omap_id_notifier;
+ ret = extcon_register_interest(&omap->extcon_id_dev,
+ edev->name, "USB-HOST",
+ &omap->id_nb);
+ if (ret < 0)
+ dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n");
+
+ if (extcon_get_cable_state(edev, "USB") == true)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
+ if (extcon_get_cable_state(edev, "USB-HOST") == true)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
+ }
+
+ return 0;
+ }
+
static int dwc3_omap_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct dwc3_omap *omap;
struct resource *res;
struct device *dev = &pdev->dev;
- struct extcon_dev *edev;
struct regulator *vbus_reg = NULL;
int ret;
int irq;
- int utmi_mode = 0;
- int x_major;
-
u32 reg;
void __iomem *base;
goto err0;
}
- reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION);
- omap->revision = reg;
- x_major = USBOTGSS_REVISION_XMAJOR(reg);
-
- /* Differentiate between OMAP5 and AM437x */
- switch (x_major) {
- case USBOTGSS_REVISION_XMAJOR1:
- case USBOTGSS_REVISION_XMAJOR2:
- omap->irq_eoi_offset = 0;
- omap->irq0_offset = 0;
- omap->irqmisc_offset = 0;
- omap->utmi_otg_offset = 0;
- omap->debug_offset = 0;
- break;
- default:
- /* Default to the latest revision */
- omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
- omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
- omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
- omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
- omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
- break;
- }
-
- /* For OMAP5(ES2.0) and AM437x x_major is 2 even though there are
- * changes in wrapper registers, Using dt compatible for aegis
- */
-
- if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
- omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
- omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
- omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
- omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
- omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
- }
-
- reg = dwc3_omap_read_utmi_status(omap);
-
- of_property_read_u32(node, "utmi-mode", &utmi_mode);
-
- switch (utmi_mode) {
- case DWC3_OMAP_UTMI_MODE_SW:
- reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
- break;
- case DWC3_OMAP_UTMI_MODE_HW:
- reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
- break;
- default:
- dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
- }
-
- dwc3_omap_write_utmi_status(omap, reg);
+ dwc3_omap_map_offset(omap);
+ dwc3_omap_set_utmi_mode(omap);
/* check the DMA Status */
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
dwc3_omap_enable_irqs(omap);
- if (of_property_read_bool(node, "extcon")) {
- edev = extcon_get_edev_by_phandle(dev, 0);
- if (IS_ERR(edev)) {
- dev_vdbg(dev, "couldn't get extcon device\n");
- ret = -EPROBE_DEFER;
- goto err2;
- }
-
- omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
- ret = extcon_register_interest(&omap->extcon_vbus_dev,
- edev->name, "USB", &omap->vbus_nb);
- if (ret < 0)
- dev_vdbg(dev, "failed to register notifier for USB\n");
- omap->id_nb.notifier_call = dwc3_omap_id_notifier;
- ret = extcon_register_interest(&omap->extcon_id_dev, edev->name,
- "USB-HOST", &omap->id_nb);
- if (ret < 0)
- dev_vdbg(dev,
- "failed to register notifier for USB-HOST\n");
-
- if (extcon_get_cable_state(edev, "USB") == true)
- dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
- if (extcon_get_cable_state(edev, "USB-HOST") == true)
- dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
- }
+ ret = dwc3_omap_extcon_register(omap);
+ if (ret < 0)
+ goto err2;
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
- dwc3_omap_disable_irqs(omap);
+ dwc3_omap_write_irqmisc_set(omap, 0x00);
return 0;
}
static void dwc3_omap_complete(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
+ u32 reg;
- dwc3_omap_enable_irqs(omap);
+ reg = (USBOTGSS_IRQMISC_OEVT |
+ USBOTGSS_IRQMISC_DRVVBUS_RISE |
+ USBOTGSS_IRQMISC_CHRGVBUS_RISE |
+ USBOTGSS_IRQMISC_DISCHRGVBUS_RISE |
+ USBOTGSS_IRQMISC_IDPULLUP_RISE |
+ USBOTGSS_IRQMISC_DRVVBUS_FALL |
+ USBOTGSS_IRQMISC_CHRGVBUS_FALL |
+ USBOTGSS_IRQMISC_DISCHRGVBUS_FALL |
+ USBOTGSS_IRQMISC_IDPULLUP_FALL);
+
+ dwc3_omap_write_irqmisc_set(omap, reg);
}
static int dwc3_omap_suspend(struct device *dev)
length, last ? " last" : "",
chain ? " chain" : "");
- /* Skip the LINK-TRB on ISOC */
- if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
- usb_endpoint_xfer_isoc(dep->endpoint.desc))
- dep->free_slot++;
trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
}
dep->free_slot++;
+ /* Skip the LINK-TRB on ISOC */
+ if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ dep->free_slot++;
trb->size = DWC3_TRB_SIZE_LENGTH(length);
trb->bpl = lower_32_bits(dma);
}
static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event,
- int start_new)
+ struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
{
unsigned status = 0;
int clean_busy;
return;
}
- dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
+ dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERINPROGRESS:
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
return;
}
- dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
+ dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
if (page[len - 1] == '\n' || page[len - 1] == '\0')
--len;
- new_data = kzalloc(len, GFP_KERNEL);
+ new_data = kmemdup(page, len, GFP_KERNEL);
if (!new_data)
return -ENOMEM;
- memcpy(new_data, page, len);
-
if (desc->opts_mutex)
mutex_lock(desc->opts_mutex);
kfree(ext_prop->data);
.store_attribute = usb_os_desc_attr_store,
};
-static ssize_t rndis_grp_compatible_id_show(struct usb_os_desc *desc,
- char *page)
+static ssize_t interf_grp_compatible_id_show(struct usb_os_desc *desc,
+ char *page)
{
memcpy(page, desc->ext_compat_id, 8);
return 8;
}
-static ssize_t rndis_grp_compatible_id_store(struct usb_os_desc *desc,
- const char *page, size_t len)
+static ssize_t interf_grp_compatible_id_store(struct usb_os_desc *desc,
+ const char *page, size_t len)
{
int l;
return len;
}
-static struct usb_os_desc_attribute rndis_grp_attr_compatible_id =
+static struct usb_os_desc_attribute interf_grp_attr_compatible_id =
__CONFIGFS_ATTR(compatible_id, S_IRUGO | S_IWUSR,
- rndis_grp_compatible_id_show,
- rndis_grp_compatible_id_store);
+ interf_grp_compatible_id_show,
+ interf_grp_compatible_id_store);
-static ssize_t rndis_grp_sub_compatible_id_show(struct usb_os_desc *desc,
- char *page)
+static ssize_t interf_grp_sub_compatible_id_show(struct usb_os_desc *desc,
+ char *page)
{
memcpy(page, desc->ext_compat_id + 8, 8);
return 8;
}
-static ssize_t rndis_grp_sub_compatible_id_store(struct usb_os_desc *desc,
- const char *page, size_t len)
+static ssize_t interf_grp_sub_compatible_id_store(struct usb_os_desc *desc,
+ const char *page, size_t len)
{
int l;
return len;
}
-static struct usb_os_desc_attribute rndis_grp_attr_sub_compatible_id =
+static struct usb_os_desc_attribute interf_grp_attr_sub_compatible_id =
__CONFIGFS_ATTR(sub_compatible_id, S_IRUGO | S_IWUSR,
- rndis_grp_sub_compatible_id_show,
- rndis_grp_sub_compatible_id_store);
+ interf_grp_sub_compatible_id_show,
+ interf_grp_sub_compatible_id_store);
static struct configfs_attribute *interf_grp_attrs[] = {
- &rndis_grp_attr_compatible_id.attr,
- &rndis_grp_attr_sub_compatible_id.attr,
+ &interf_grp_attr_compatible_id.attr,
+ &interf_grp_attr_sub_compatible_id.attr,
NULL
};
int usb_os_desc_prepare_interf_dir(struct config_group *parent,
int n_interf,
struct usb_os_desc **desc,
+ char **names,
struct module *owner)
{
struct config_group **f_default_groups, *os_desc_group,
d = desc[n_interf];
d->owner = owner;
config_group_init_type_name(&d->group, "", interface_type);
- config_item_set_name(&d->group.cg_item, "interface.%d",
- n_interf);
+ config_item_set_name(&d->group.cg_item, "interface.%s",
+ names[n_interf]);
interface_groups[n_interf] = &d->group;
}
#include "u_fs.h"
#include "u_f.h"
+ #include "u_os_desc.h"
#include "configfs.h"
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
ffs->ep0req->context = ffs;
lang = ffs->stringtabs;
- for (lang = ffs->stringtabs; *lang; ++lang) {
- struct usb_string *str = (*lang)->strings;
- int id = first_id;
- for (; str->s; ++id, ++str)
- str->id = id;
+ if (lang) {
+ for (; *lang; ++lang) {
+ struct usb_string *str = (*lang)->strings;
+ int id = first_id;
+ for (; str->s; ++id, ++str)
+ str->id = id;
+ }
}
ffs->gadget = cdev->gadget;
FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
};
+ enum ffs_os_desc_type {
+ FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
+ };
+
typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
u8 *valuep,
struct usb_descriptor_header *desc,
void *priv);
- static int __must_check ffs_do_desc(char *data, unsigned len,
- ffs_entity_callback entity, void *priv)
+ typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv);
+
+ static int __must_check ffs_do_single_desc(char *data, unsigned len,
+ ffs_entity_callback entity,
+ void *priv)
{
struct usb_descriptor_header *_ds = (void *)data;
u8 length;
if (!data)
return _len - len;
- ret = ffs_do_desc(data, len, entity, priv);
+ ret = ffs_do_single_desc(data, len, entity, priv);
if (unlikely(ret < 0)) {
pr_debug("%s returns %d\n", __func__, ret);
return ret;
return 0;
}
+ static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
+ struct usb_os_desc_header *desc)
+ {
+ u16 bcd_version = le16_to_cpu(desc->bcdVersion);
+ u16 w_index = le16_to_cpu(desc->wIndex);
+
+ if (bcd_version != 1) {
+ pr_vdebug("unsupported os descriptors version: %d",
+ bcd_version);
+ return -EINVAL;
+ }
+ switch (w_index) {
+ case 0x4:
+ *next_type = FFS_OS_DESC_EXT_COMPAT;
+ break;
+ case 0x5:
+ *next_type = FFS_OS_DESC_EXT_PROP;
+ break;
+ default:
+ pr_vdebug("unsupported os descriptor type: %d", w_index);
+ return -EINVAL;
+ }
+
+ return sizeof(*desc);
+ }
+
+ /*
+ * Process all extended compatibility/extended property descriptors
+ * of a feature descriptor
+ */
+ static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
+ enum ffs_os_desc_type type,
+ u16 feature_count,
+ ffs_os_desc_callback entity,
+ void *priv,
+ struct usb_os_desc_header *h)
+ {
+ int ret;
+ const unsigned _len = len;
+
+ ENTER();
+
+ /* loop over all ext compat/ext prop descriptors */
+ while (feature_count--) {
+ ret = entity(type, h, data, len, priv);
+ if (unlikely(ret < 0)) {
+ pr_debug("bad OS descriptor, type: %d\n", type);
+ return ret;
+ }
+ data += ret;
+ len -= ret;
+ }
+ return _len - len;
+ }
+
+ /* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
+ static int __must_check ffs_do_os_descs(unsigned count,
+ char *data, unsigned len,
+ ffs_os_desc_callback entity, void *priv)
+ {
+ const unsigned _len = len;
+ unsigned long num = 0;
+
+ ENTER();
+
+ for (num = 0; num < count; ++num) {
+ int ret;
+ enum ffs_os_desc_type type;
+ u16 feature_count;
+ struct usb_os_desc_header *desc = (void *)data;
+
+ if (len < sizeof(*desc))
+ return -EINVAL;
+
+ /*
+ * Record "descriptor" entity.
+ * Process dwLength, bcdVersion, wIndex, get b/wCount.
+ * Move the data pointer to the beginning of extended
+ * compatibilities proper or extended properties proper
+ * portions of the data
+ */
+ if (le32_to_cpu(desc->dwLength) > len)
+ return -EINVAL;
+
+ ret = __ffs_do_os_desc_header(&type, desc);
+ if (unlikely(ret < 0)) {
+ pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
+ num, ret);
+ return ret;
+ }
+ /*
+ * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
+ */
+ feature_count = le16_to_cpu(desc->wCount);
+ if (type == FFS_OS_DESC_EXT_COMPAT &&
+ (feature_count > 255 || desc->Reserved))
+ return -EINVAL;
+ len -= ret;
+ data += ret;
+
+ /*
+ * Process all function/property descriptors
+ * of this Feature Descriptor
+ */
+ ret = ffs_do_single_os_desc(data, len, type,
+ feature_count, entity, priv, desc);
+ if (unlikely(ret < 0)) {
+ pr_debug("%s returns %d\n", __func__, ret);
+ return ret;
+ }
+
+ len -= ret;
+ data += ret;
+ }
+ return _len - len;
+ }
+
+ /**
+ * Validate contents of the buffer from userspace related to OS descriptors.
+ */
+ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv)
+ {
+ struct ffs_data *ffs = priv;
+ u8 length;
+
+ ENTER();
+
+ switch (type) {
+ case FFS_OS_DESC_EXT_COMPAT: {
+ struct usb_ext_compat_desc *d = data;
+ int i;
+
+ if (len < sizeof(*d) ||
+ d->bFirstInterfaceNumber >= ffs->interfaces_count ||
+ d->Reserved1)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
+ if (d->Reserved2[i])
+ return -EINVAL;
+
+ length = sizeof(struct usb_ext_compat_desc);
+ }
+ break;
+ case FFS_OS_DESC_EXT_PROP: {
+ struct usb_ext_prop_desc *d = data;
+ u32 type, pdl;
+ u16 pnl;
+
+ if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
+ return -EINVAL;
+ length = le32_to_cpu(d->dwSize);
+ type = le32_to_cpu(d->dwPropertyDataType);
+ if (type < USB_EXT_PROP_UNICODE ||
+ type > USB_EXT_PROP_UNICODE_MULTI) {
+ pr_vdebug("unsupported os descriptor property type: %d",
+ type);
+ return -EINVAL;
+ }
+ pnl = le16_to_cpu(d->wPropertyNameLength);
+ pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
+ if (length != 14 + pnl + pdl) {
+ pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
+ length, pnl, pdl, type);
+ return -EINVAL;
+ }
+ ++ffs->ms_os_descs_ext_prop_count;
+ /* property name reported to the host as "WCHAR"s */
+ ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
+ ffs->ms_os_descs_ext_prop_data_len += pdl;
+ }
+ break;
+ default:
+ pr_vdebug("unknown descriptor: %d\n", type);
+ return -EINVAL;
+ }
+ return length;
+ }
+
static int __ffs_data_got_descs(struct ffs_data *ffs,
char *const _data, size_t len)
{
char *data = _data, *raw_descs;
- unsigned counts[3], flags;
+ unsigned os_descs_count = 0, counts[3], flags;
int ret = -EINVAL, i;
ENTER();
flags = get_unaligned_le32(data + 8);
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
FUNCTIONFS_HAS_HS_DESC |
- FUNCTIONFS_HAS_SS_DESC)) {
+ FUNCTIONFS_HAS_SS_DESC |
+ FUNCTIONFS_HAS_MS_OS_DESC)) {
ret = -ENOSYS;
goto error;
}
len -= 4;
}
}
+ if (flags & (1 << i)) {
+ os_descs_count = get_unaligned_le32(data);
+ data += 4;
+ len -= 4;
+ };
/* Read descriptors */
raw_descs = data;
data += ret;
len -= ret;
}
+ if (os_descs_count) {
+ ret = ffs_do_os_descs(os_descs_count, data, len,
+ __ffs_data_do_os_desc, ffs);
+ if (ret < 0)
+ goto error;
+ data += ret;
+ len -= ret;
+ }
if (raw_descs == data || len) {
ret = -EINVAL;
ffs->fs_descs_count = counts[0];
ffs->hs_descs_count = counts[1];
ffs->ss_descs_count = counts[2];
+ ffs->ms_os_descs_count = os_descs_count;
return 0;
return 0;
}
+ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv)
+ {
+ struct ffs_function *func = priv;
+ u8 length = 0;
+
+ switch (type) {
+ case FFS_OS_DESC_EXT_COMPAT: {
+ struct usb_ext_compat_desc *desc = data;
+ struct usb_os_desc_table *t;
+
+ t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
+ t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
+ memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
+ ARRAY_SIZE(desc->CompatibleID) +
+ ARRAY_SIZE(desc->SubCompatibleID));
+ length = sizeof(*desc);
+ }
+ break;
+ case FFS_OS_DESC_EXT_PROP: {
+ struct usb_ext_prop_desc *desc = data;
+ struct usb_os_desc_table *t;
+ struct usb_os_desc_ext_prop *ext_prop;
+ char *ext_prop_name;
+ char *ext_prop_data;
+
+ t = &func->function.os_desc_table[h->interface];
+ t->if_id = func->interfaces_nums[h->interface];
+
+ ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
+ func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
+
+ ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
+ ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
+ ext_prop->data_len = le32_to_cpu(*(u32 *)
+ usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
+ length = ext_prop->name_len + ext_prop->data_len + 14;
+
+ ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
+ func->ffs->ms_os_descs_ext_prop_name_avail +=
+ ext_prop->name_len;
+
+ ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
+ func->ffs->ms_os_descs_ext_prop_data_avail +=
+ ext_prop->data_len;
+ memcpy(ext_prop_data,
+ usb_ext_prop_data_ptr(data, ext_prop->name_len),
+ ext_prop->data_len);
+ /* unicode data reported to the host as "WCHAR"s */
+ switch (ext_prop->type) {
+ case USB_EXT_PROP_UNICODE:
+ case USB_EXT_PROP_UNICODE_ENV:
+ case USB_EXT_PROP_UNICODE_LINK:
+ case USB_EXT_PROP_UNICODE_MULTI:
+ ext_prop->data_len *= 2;
+ break;
+ }
+ ext_prop->data = ext_prop_data;
+
+ memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
+ ext_prop->name_len);
+ /* property name reported to the host as "WCHAR"s */
+ ext_prop->name_len *= 2;
+ ext_prop->name = ext_prop_name;
+
+ t->os_desc->ext_prop_len +=
+ ext_prop->name_len + ext_prop->data_len + 14;
+ ++t->os_desc->ext_prop_count;
+ list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
+ }
+ break;
+ default:
+ pr_vdebug("unknown descriptor: %d\n", type);
+ }
+
+ return length;
+ }
+
static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
struct usb_configuration *c)
{
const int super = gadget_is_superspeed(func->gadget) &&
func->ffs->ss_descs_count;
- int fs_len, hs_len, ret;
+ int fs_len, hs_len, ss_len, ret, i;
/* Make it a single chunk, less management later on */
vla_group(d);
vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
super ? ffs->ss_descs_count + 1 : 0);
vla_item_with_sz(d, short, inums, ffs->interfaces_count);
+ vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, char[16], ext_compat,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, struct usb_os_desc, os_desc,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
+ ffs->ms_os_descs_ext_prop_count);
+ vla_item_with_sz(d, char, ext_prop_name,
+ ffs->ms_os_descs_ext_prop_name_len);
+ vla_item_with_sz(d, char, ext_prop_data,
+ ffs->ms_os_descs_ext_prop_data_len);
vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
char *vlabuf;
return -ENOTSUPP;
/* Allocate a single chunk, less management later on */
- vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
+ vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
if (unlikely(!vlabuf))
return -ENOMEM;
- /* Zero */
- memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
+ ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
+ ffs->ms_os_descs_ext_prop_name_avail =
+ vla_ptr(vlabuf, d, ext_prop_name);
+ ffs->ms_os_descs_ext_prop_data_avail =
+ vla_ptr(vlabuf, d, ext_prop_data);
+
/* Copy descriptors */
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
ffs->raw_descs_length);
if (likely(super)) {
func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
- ret = ffs_do_descs(ffs->ss_descs_count,
+ ss_len = ffs_do_descs(ffs->ss_descs_count,
vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
d_raw_descs__sz - fs_len - hs_len,
__ffs_func_bind_do_descs, func);
- if (unlikely(ret < 0))
+ if (unlikely(ss_len < 0)) {
+ ret = ss_len;
goto error;
+ }
+ } else {
+ ss_len = 0;
}
/*
if (unlikely(ret < 0))
goto error;
+ func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
+ if (c->cdev->use_os_string)
+ for (i = 0; i < ffs->interfaces_count; ++i) {
+ struct usb_os_desc *desc;
+
+ desc = func->function.os_desc_table[i].os_desc =
+ vla_ptr(vlabuf, d, os_desc) +
+ i * sizeof(struct usb_os_desc);
+ desc->ext_compat_id =
+ vla_ptr(vlabuf, d, ext_compat) + i * 16;
+ INIT_LIST_HEAD(&desc->ext_prop);
+ }
+ ret = ffs_do_os_descs(ffs->ms_os_descs_count,
+ vla_ptr(vlabuf, d, raw_descs) +
+ fs_len + hs_len + ss_len,
+ d_raw_descs__sz - fs_len - hs_len - ss_len,
+ __ffs_func_bind_do_os_desc, func);
+ if (unlikely(ret < 0))
+ goto error;
+ func->function.os_desc_n =
+ c->cdev->use_os_string ? ffs->interfaces_count : 0;
+
/* And we're done */
ffs_event_add(ffs, FUNCTIONFS_BIND);
return 0;
ffs_dev = _ffs_find_dev(dev_name);
if (!ffs_dev)
- ffs_dev = ERR_PTR(-ENODEV);
+ ffs_dev = ERR_PTR(-ENOENT);
else if (ffs_dev->mounted)
ffs_dev = ERR_PTR(-EBUSY);
else if (ffs_dev->ffs_acquire_dev_callback &&
ffs_dev->ffs_acquire_dev_callback(ffs_dev))
- ffs_dev = ERR_PTR(-ENODEV);
+ ffs_dev = ERR_PTR(-ENOENT);
else
ffs_dev->mounted = true;
f->os_desc_table = kzalloc(sizeof(*f->os_desc_table),
GFP_KERNEL);
if (!f->os_desc_table)
- return PTR_ERR(f->os_desc_table);
+ return -ENOMEM;
f->os_desc_n = 1;
f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc;
}
rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = status;
+ if (cdev->use_os_string)
+ f->os_desc_table[0].if_id =
+ rndis_iad_descriptor.bFirstInterface;
+
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
{
struct f_rndis_opts *opts;
struct usb_os_desc *descs[1];
+ char *names[1];
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
INIT_LIST_HEAD(&opts->rndis_os_desc.ext_prop);
descs[0] = &opts->rndis_os_desc;
+ names[0] = "rndis";
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
- THIS_MODULE);
+ names, THIS_MODULE);
config_group_init_type_name(&opts->func_inst.group, "",
&rndis_func_type);
struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
- int length = skb->len;
+ int length = 0;
int retval;
struct usb_request *req = NULL;
unsigned long flags;
}
spin_unlock_irqrestore(&dev->lock, flags);
- if (!in) {
+ if (skb && !in) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
/* apply outgoing CDC or RNDIS filters */
- if (!is_promisc(cdc_filter)) {
+ if (skb && !is_promisc(cdc_filter)) {
u8 *dest = skb->data;
if (is_multicast_ether_addr(dest)) {
if (dev->port_usb)
skb = dev->wrap(dev->port_usb, skb);
spin_unlock_irqrestore(&dev->lock, flags);
- if (!skb)
+ if (!skb) {
+ /* Multi frame CDC protocols may store the frame for
+ * later which is not a dropped frame.
+ */
+ if (dev->port_usb->supports_multi_frame)
+ goto multiframe;
goto drop;
-
- length = skb->len;
+ }
}
+
+ length = skb->len;
req->buf = skb->data;
req->context = skb;
req->complete = tx_complete;
dev_kfree_skb_any(skb);
drop:
dev->net->stats.tx_dropped++;
+ multiframe:
spin_lock_irqsave(&dev->req_lock, flags);
if (list_empty(&dev->tx_reqs))
netif_start_queue(net);
DBG(dev, "%s\n", __func__);
+ netif_tx_lock(dev->net);
netif_stop_queue(dev->net);
+ netif_tx_unlock(dev->net);
+
netif_carrier_off(dev->net);
/* disable endpoints, forcing (synchronous) completion
kfree (dev->buf);
dev->buf = NULL;
- put_dev (dev);
+ /* other endpoints were all decoupled from this device */
+ spin_lock_irq(&dev->lock);
+ dev->state = STATE_DEV_DISABLED;
+ spin_unlock_irq(&dev->lock);
+
+ put_dev (dev);
return 0;
}
"%s mode: multiple trans./microframe not valid\n",
(mode == 2 ? "Bulk" : "Control"));
return -EINVAL;
- } else if (nt == 0x11) {
- dev_err(dev->dev, "Invalid value for trans./microframe\n");
+ } else if (nt == 0x3) {
+ dev_err(dev->dev,
+ "Invalid value 0x3 for additional trans./microframe\n");
return -EINVAL;
} else if ((nt + 1) * max > buffer_size) {
dev_err(dev->dev, "Hw buffer size %d < max payload %d * %d\n",
return retval;
}
- static struct of_device_id gr_match[] = {
+ static const struct of_device_id gr_match[] = {
{.name = "GAISLER_USBDC"},
{.name = "01_021"},
{},
/* clear active and halt bit, in case set from a previous error */
dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED);
- /* Ensure that updates to the QH will occure before priming. */
+ /* Ensure that updates to the QH will occur before priming. */
wmb();
/* Prime the Endpoint */
dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n",
setup->bRequestType, setup->bRequest,
setup->wValue, setup->wIndex, setup->wLength);
- /* We process some stardard setup requests here */
+ /* We process some standard setup requests here */
if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
switch (setup->bRequest) {
case USB_REQ_GET_STATUS:
u32 transferred;
u32 packet_sz;
struct list_head tx_check;
- struct work_struct dma_completion;
};
#define MUSB_DMA_NUM_CHANNELS 15
static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
{
+ struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
u16 csr;
u8 toggle;
if (cppi41_channel->is_tx)
return;
- if (!is_host_active(cppi41_channel->controller->musb))
+ if (!is_host_active(musb))
return;
- csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR);
+ musb_ep_select(musb->mregs, hw_ep->epnum);
+ csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0;
/*
void __iomem *epio = musb->endpoints[epnum].regs;
u16 csr;
+ musb_ep_select(musb->mregs, hw_ep->epnum);
csr = musb_readw(epio, MUSB_TXCSR);
if (csr & MUSB_TXCSR_TXPKTRDY)
return false;
return true;
}
- static bool is_isoc(struct musb_hw_ep *hw_ep, bool in)
- {
- if (in && hw_ep->in_qh) {
- if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC)
- return true;
- } else if (hw_ep->out_qh) {
- if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC)
- return true;
- }
- return false;
- }
-
static void cppi41_dma_callback(void *private_data);
static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
cppi41_channel->channel.actual_len =
cppi41_channel->transferred;
cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ cppi41_channel->channel.rx_packet_done = true;
musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx);
} else {
/* next iteration, reload */
dma_async_issue_pending(dc);
if (!cppi41_channel->is_tx) {
+ musb_ep_select(musb->mregs, hw_ep->epnum);
csr = musb_readw(epio, MUSB_RXCSR);
csr |= MUSB_RXCSR_H_REQPKT;
musb_writew(epio, MUSB_RXCSR, csr);
}
}
- static void cppi_trans_done_work(struct work_struct *work)
- {
- unsigned long flags;
- struct cppi41_dma_channel *cppi41_channel =
- container_of(work, struct cppi41_dma_channel, dma_completion);
- struct cppi41_dma_controller *controller = cppi41_channel->controller;
- struct musb *musb = controller->musb;
- struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
- bool empty;
-
- if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) {
- spin_lock_irqsave(&musb->lock, flags);
- cppi41_trans_done(cppi41_channel);
- spin_unlock_irqrestore(&musb->lock, flags);
- } else {
- empty = musb_is_tx_fifo_empty(hw_ep);
- if (empty) {
- spin_lock_irqsave(&musb->lock, flags);
- cppi41_trans_done(cppi41_channel);
- spin_unlock_irqrestore(&musb->lock, flags);
- } else {
- schedule_work(&cppi41_channel->dma_completion);
- }
- }
- }
-
static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
{
struct cppi41_dma_controller *controller;
if (!list_empty(&controller->early_tx_list)) {
ret = HRTIMER_RESTART;
hrtimer_forward_now(&controller->early_tx,
- ktime_set(0, 150 * NSEC_PER_USEC));
+ ktime_set(0, 50 * NSEC_PER_USEC));
}
spin_unlock_irqrestore(&musb->lock, flags);
transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0;
- if (!cppi41_channel->is_tx) {
- if (is_isoc(hw_ep, 1))
- schedule_work(&cppi41_channel->dma_completion);
- else
- cppi41_trans_done(cppi41_channel);
- goto out;
- }
-
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
cppi41_trans_done(cppi41_channel);
goto out;
}
}
- if (is_isoc(hw_ep, 0)) {
- schedule_work(&cppi41_channel->dma_completion);
- goto out;
- }
list_add_tail(&cppi41_channel->tx_check,
&controller->early_tx_list);
- if (!hrtimer_active(&controller->early_tx)) {
+ if (!hrtimer_is_queued(&controller->early_tx)) {
+ unsigned long usecs = cppi41_channel->total_len / 10;
+
hrtimer_start_range_ns(&controller->early_tx,
- ktime_set(0, 140 * NSEC_PER_USEC),
+ ktime_set(0, usecs * NSEC_PER_USEC),
40 * NSEC_PER_USEC,
HRTIMER_MODE_REL);
}
dma_desc->callback = cppi41_dma_callback;
dma_desc->callback_param = channel;
cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
+ cppi41_channel->channel.rx_packet_done = false;
save_rx_toggle(cppi41_channel);
dma_async_issue_pending(dc);
cppi41_channel->port_num = port;
cppi41_channel->is_tx = is_tx;
INIT_LIST_HEAD(&cppi41_channel->tx_check);
- INIT_WORK(&cppi41_channel->dma_completion,
- cppi_trans_done_work);
musb_dma = &cppi41_channel->channel;
musb_dma->private_data = cppi41_channel;
* dependent on musb core layer symbols.
*/
static inline u8 dsps_readb(const void __iomem *addr, unsigned offset)
- { return __raw_readb(addr + offset); }
+ {
+ return __raw_readb(addr + offset);
+ }
static inline u32 dsps_readl(const void __iomem *addr, unsigned offset)
- { return __raw_readl(addr + offset); }
+ {
+ return __raw_readl(addr + offset);
+ }
static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data)
- { __raw_writeb(data, addr + offset); }
+ {
+ __raw_writeb(data, addr + offset);
+ }
static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data)
- { __raw_writel(data, addr + offset); }
+ {
+ __raw_writel(data, addr + offset);
+ }
/**
* DSPS musb wrapper register offset.
const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
struct timer_list timer; /* otg_workaround timer */
unsigned long last_timer; /* last timer data for each instance */
+ bool sw_babble_enabled;
struct dsps_context context;
struct debugfs_regset32 regset;
val &= ~(1 << wrp->otg_disable);
dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
+ /*
+ * Check whether the dsps version has babble control enabled.
+ * In latest silicon revision the babble control logic is enabled.
+ * If MUSB_BABBLE_CTL returns 0x4 then we have the babble control
+ * logic enabled.
+ */
+ val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ if (val == MUSB_BABBLE_RCV_DISABLE) {
+ glue->sw_babble_enabled = true;
+ val |= MUSB_BABBLE_SW_SESSION_CTRL;
+ dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
+ }
+
ret = dsps_musb_dbg_init(musb, glue);
if (ret)
return ret;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
void __iomem *ctrl_base = musb->ctrl_base;
- void __iomem *base = musb->mregs;
u32 reg;
- reg = dsps_readl(base, wrp->mode);
+ reg = dsps_readl(ctrl_base, wrp->mode);
switch (mode) {
case MUSB_HOST:
*/
reg |= (1 << wrp->iddig_mux);
- dsps_writel(base, wrp->mode, reg);
+ dsps_writel(ctrl_base, wrp->mode, reg);
dsps_writel(ctrl_base, wrp->phy_utmi, 0x02);
break;
case MUSB_PERIPHERAL:
*/
reg |= (1 << wrp->iddig_mux);
- dsps_writel(base, wrp->mode, reg);
+ dsps_writel(ctrl_base, wrp->mode, reg);
break;
case MUSB_OTG:
- dsps_writel(base, wrp->phy_utmi, 0x02);
+ dsps_writel(ctrl_base, wrp->phy_utmi, 0x02);
break;
default:
dev_err(glue->dev, "unsupported mode %d\n", mode);
return 0;
}
- static void dsps_musb_reset(struct musb *musb)
+ static bool sw_babble_control(struct musb *musb)
+ {
+ u8 babble_ctl;
+ bool session_restart = false;
+
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ dev_dbg(musb->controller, "babble: MUSB_BABBLE_CTL value %x\n",
+ babble_ctl);
+ /*
+ * check line monitor flag to check whether babble is
+ * due to noise
+ */
+ dev_dbg(musb->controller, "STUCK_J is %s\n",
+ babble_ctl & MUSB_BABBLE_STUCK_J ? "set" : "reset");
+
+ if (babble_ctl & MUSB_BABBLE_STUCK_J) {
+ int timeout = 10;
+
+ /*
+ * babble is due to noise, then set transmit idle (d7 bit)
+ * to resume normal operation
+ */
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ babble_ctl |= MUSB_BABBLE_FORCE_TXIDLE;
+ dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl);
+
+ /* wait till line monitor flag cleared */
+ dev_dbg(musb->controller, "Set TXIDLE, wait J to clear\n");
+ do {
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ udelay(1);
+ } while ((babble_ctl & MUSB_BABBLE_STUCK_J) && timeout--);
+
+ /* check whether stuck_at_j bit cleared */
+ if (babble_ctl & MUSB_BABBLE_STUCK_J) {
+ /*
+ * real babble condition has occurred
+ * restart the controller to start the
+ * session again
+ */
+ dev_dbg(musb->controller, "J not cleared, misc (%x)\n",
+ babble_ctl);
+ session_restart = true;
+ }
+ } else {
+ session_restart = true;
+ }
+
+ return session_restart;
+ }
+
+ static int dsps_musb_reset(struct musb *musb)
{
struct device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
+ int session_restart = 0;
+
+ if (glue->sw_babble_enabled)
+ session_restart = sw_babble_control(musb);
+ /*
+ * In case of new silicon version babble condition can be recovered
+ * without resetting the MUSB. But for older silicon versions, MUSB
+ * reset is needed
+ */
+ if (session_restart || !glue->sw_babble_enabled) {
+ dev_info(musb->controller, "Restarting MUSB to recover from Babble\n");
+ dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
+ usleep_range(100, 200);
+ usb_phy_shutdown(musb->xceiv);
+ usleep_range(100, 200);
+ usb_phy_init(musb->xceiv);
+ session_restart = 1;
+ }
- dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
- udelay(100);
+ return !session_restart;
}
static struct musb_platform_ops dsps_ops = {
}
}
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err0;
}
- clk = clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err3;
+ goto err1;
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err4;
+ goto err1;
}
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &pdev->dev.coherent_dma_mask;
musb->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
- musb->dev.of_node = pdev->dev.of_node;
glue->dev = &pdev->dev;
glue->musb = musb;
ARRAY_SIZE(musb_resources));
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err5;
+ goto err2;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err5;
+ goto err2;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err5;
+ goto err2;
}
return 0;
- err5:
+ err2:
clk_disable_unprepare(clk);
- err4:
- clk_put(clk);
-
- err3:
- platform_device_put(musb);
-
err1:
- kfree(glue);
+ platform_device_put(musb);
err0:
return ret;
platform_device_unregister(glue->musb);
clk_disable_unprepare(glue->clk);
- clk_put(glue->clk);
- kfree(glue);
return 0;
}
static int msm_otg_phy_clk_reset(struct msm_otg *motg)
{
- int ret;
+ int ret = 0;
- if (motg->pdata->phy_clk_reset)
+ if (motg->pdata->phy_clk_reset && motg->phy_reset_clk)
ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk);
- else
+ else if (motg->phy_rst)
ret = reset_control_reset(motg->phy_rst);
if (ret)
motg->chg_state = USB_CHG_STATE_UNDEFINED;
motg->chg_type = USB_INVALID_CHARGER;
}
- pm_runtime_put_sync(otg->phy->dev);
+
+ if (otg->phy->state == OTG_STATE_B_IDLE)
+ pm_runtime_put_sync(otg->phy->dev);
break;
case OTG_STATE_B_PERIPHERAL:
dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n");
debugfs_remove(msm_otg_dbg_root);
}
- static struct of_device_id msm_otg_dt_match[] = {
+ static const struct of_device_id msm_otg_dt_match[] = {
{
.compatible = "qcom,usb-otg-ci",
.data = (void *) CI_45NM_INTEGRATED_PHY
motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy");
if (IS_ERR(motg->phy_rst))
- return PTR_ERR(motg->phy_rst);
+ motg->phy_rst = NULL;
pdata->mode = of_usb_get_dr_mode(node);
if (pdata->mode == USB_DR_MODE_UNKNOWN)
np ? "phy" : "usb_phy_clk");
if (IS_ERR(motg->phy_reset_clk)) {
dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
- return PTR_ERR(motg->phy_reset_clk);
+ motg->phy_reset_clk = NULL;
}
motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk");
return gpio_direction_output(phy->reset_gpio, 0);
}
-static void tegra_usb_phy_close(struct usb_phy *x)
+static void tegra_usb_phy_close(struct tegra_usb_phy *phy)
{
- struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
-
if (!IS_ERR(phy->vbus))
regulator_disable(phy->vbus);
.requires_extra_tuning_parameters = true,
};
- static struct of_device_id tegra_usb_phy_id_table[] = {
+ static const struct of_device_id tegra_usb_phy_id_table[] = {
{ .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config },
{ .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config },
{ },
if (err < 0)
return err;
- tegra_phy->u_phy.shutdown = tegra_usb_phy_close;
tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend;
platform_set_drvdata(pdev, tegra_phy);
err = usb_add_phy_dev(&tegra_phy->u_phy);
if (err < 0) {
- tegra_usb_phy_close(&tegra_phy->u_phy);
+ tegra_usb_phy_close(tegra_phy);
return err;
}
struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev);
usb_remove_phy(&tegra_phy->u_phy);
+ tegra_usb_phy_close(tegra_phy);
return 0;
}
FUNCTIONFS_HAS_FS_DESC = 1,
FUNCTIONFS_HAS_HS_DESC = 2,
FUNCTIONFS_HAS_SS_DESC = 4,
+ FUNCTIONFS_HAS_MS_OS_DESC = 8,
};
- #ifndef __KERNEL__
-
/* Descriptor of an non-audio endpoint */
struct usb_endpoint_descriptor_no_audio {
__u8 bLength;
__u8 bInterval;
} __attribute__((packed));
+/* Legacy format, deprecated as of 3.14. */
+struct usb_functionfs_descs_head {
+ __le32 magic;
+ __le32 length;
+ __le32 fs_count;
+ __le32 hs_count;
+} __attribute__((packed, deprecated));
+
+ /* MS OS Descriptor header */
+ struct usb_os_desc_header {
+ __u8 interface;
+ __le32 dwLength;
+ __le16 bcdVersion;
+ __le16 wIndex;
+ union {
+ struct {
+ __u8 bCount;
+ __u8 Reserved;
+ };
+ __le16 wCount;
+ };
+ } __attribute__((packed));
+
+ struct usb_ext_compat_desc {
+ __u8 bFirstInterfaceNumber;
+ __u8 Reserved1;
+ __u8 CompatibleID[8];
+ __u8 SubCompatibleID[8];
+ __u8 Reserved2[6];
+ };
+
+ struct usb_ext_prop_desc {
+ __le32 dwSize;
+ __le32 dwPropertyDataType;
+ __le16 wPropertyNameLength;
+ } __attribute__((packed));
+
+ #ifndef __KERNEL__
+
/*
* Descriptors format:
*
* | | fs_count | LE32 | number of full-speed descriptors |
* | | hs_count | LE32 | number of high-speed descriptors |
* | | ss_count | LE32 | number of super-speed descriptors |
+ * | | os_count | LE32 | number of MS OS descriptors |
* | | fs_descrs | Descriptor[] | list of full-speed descriptors |
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
* | | ss_descrs | Descriptor[] | list of super-speed descriptors |
+ * | | os_descrs | OSDesc[] | list of MS OS descriptors |
*
* Depending on which flags are set, various fields may be missing in the
* structure. Any flags that are not recognised cause the whole block to be
* | 0 | bLength | U8 | length of the descriptor |
* | 1 | bDescriptorType | U8 | descriptor type |
* | 2 | payload | | descriptor's payload |
+ *
+ * OSDesc[] is an array of valid MS OS Feature Descriptors which have one of
+ * the following formats:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------+------+--------------------------|
+ * | 0 | inteface | U8 | related interface number |
+ * | 1 | dwLength | U32 | length of the descriptor |
+ * | 5 | bcdVersion | U16 | currently supported: 1 |
+ * | 7 | wIndex | U16 | currently supported: 4 |
+ * | 9 | bCount | U8 | number of ext. compat. |
+ * | 10 | Reserved | U8 | 0 |
+ * | 11 | ExtCompat[] | | list of ext. compat. d. |
+ *
+ * | off | name | type | description |
+ * |-----+-----------------+------+--------------------------|
+ * | 0 | inteface | U8 | related interface number |
+ * | 1 | dwLength | U32 | length of the descriptor |
+ * | 5 | bcdVersion | U16 | currently supported: 1 |
+ * | 7 | wIndex | U16 | currently supported: 5 |
+ * | 9 | wCount | U16 | number of ext. compat. |
+ * | 11 | ExtProp[] | | list of ext. prop. d. |
+ *
+ * ExtCompat[] is an array of valid Extended Compatiblity descriptors
+ * which have the following format:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------------+------+-------------------------------------|
+ * | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st|
+ * | | | | interface in an IAD group |
+ * | 1 | Reserved | U8 | 0 |
+ * | 2 | CompatibleID | U8[8]| compatible ID string |
+ * | 10 | SubCompatibleID | U8[8]| subcompatible ID string |
+ * | 18 | Reserved | U8[6]| 0 |
+ *
+ * ExtProp[] is an array of valid Extended Properties descriptors
+ * which have the following format:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------------+------+-------------------------------------|
+ * | 0 | dwSize | U32 | length of the descriptor |
+ * | 4 | dwPropertyDataType | U32 | 1..7 |
+ * | 8 | wPropertyNameLength | U16 | bPropertyName length (NL) |
+ * | 10 | bPropertyName |U8[NL]| name of this property |
+ * |10+NL| dwPropertyDataLength | U32 | bPropertyData length (DL) |
+ * |14+NL| bProperty |U8[DL]| payload of this property |
*/
struct usb_functionfs_strings_head {