diff options
Diffstat (limited to 'drivers')
51 files changed, 2771 insertions, 1993 deletions
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 228654c94843..41914a55055d 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma) /* interrupt, error, port change, reset, sleep/suspend */ hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); - hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { hw_write(ci, OP_USBINTR, ~0, 0); - hw_write(ci, OP_USBCMD, USBCMD_RS, 0); } return 0; } @@ -1508,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) hw_device_reset(ci); hw_device_state(ci, ci->ep0out->qh.dma); usb_gadget_set_state(_gadget, USB_STATE_POWERED); + usb_udc_vbus_handler(_gadget, true); } else { + usb_udc_vbus_handler(_gadget, false); if (ci->driver) ci->driver->disconnect(&ci->gadget); hw_device_state(ci, 0); @@ -1574,13 +1574,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); - if (!ci->vbus_active) - return -EOPNOTSUPP; - + pm_runtime_get_sync(&ci->gadget.dev); if (is_on) hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); else hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + pm_runtime_put_sync(&ci->gadget.dev); return 0; } @@ -1710,6 +1709,7 @@ static int ci_udc_start(struct usb_gadget *gadget, spin_lock_irqsave(&ci->lock, flags); hw_device_reset(ci); } else { + usb_udc_vbus_handler(&ci->gadget, false); pm_runtime_put_sync(&ci->gadget.dev); return retval; } diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 76b9ba4dc925..1bcb36ae6505 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM config USB_DWC2_PCI tristate "DWC2 PCI" - depends on USB_DWC2_HOST && PCI - default USB_DWC2_HOST + depends on PCI + default n + select USB_DWC2_PLATFORM + select NOP_USB_XCEIV help The Designware USB2.0 PCI interface module for controllers - connected to a PCI bus. This is only used for host mode. + connected to a PCI bus. config USB_DWC2_DEBUG bool "Enable Debugging Messages" diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index 8f752679752a..f07b425eaff3 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -19,10 +19,8 @@ endif # mode. The PCI bus interface module will called dwc2_pci.ko and the platform # interface module will be called dwc2_platform.ko. -ifneq ($(CONFIG_USB_DWC2_PCI),) - obj-$(CONFIG_USB_DWC2) += dwc2_pci.o - dwc2_pci-y := pci.o -endif +obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o +dwc2_pci-y := pci.o obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o dwc2_platform-y := platform.o diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index f74304b12652..836c012c7707 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -593,6 +593,8 @@ struct dwc2_hsotg { struct dwc2_core_params *core_params; enum usb_otg_state op_state; enum usb_dr_mode dr_mode; + unsigned int hcd_enabled:1; + unsigned int gadget_enabled:1; struct phy *phy; struct usb_phy *uphy; diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index c78c8740db1d..559b55e5debb 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) */ channel->qh = NULL; } + /* All channels have been freed, mark them available */ + if (hsotg->core_params->uframe_sched > 0) { + hsotg->available_host_channels = + hsotg->core_params->host_channels; + } else { + hsotg->non_periodic_channels = 0; + hsotg->periodic_channels = 0; + } } /** diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index a4e724b0a62e..ae419615a176 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -50,113 +50,97 @@ #include <linux/usb/hcd.h> #include <linux/usb/ch11.h> +#include <linux/platform_device.h> +#include <linux/usb/usb_phy_generic.h> -#include "core.h" -#include "hcd.h" - -#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 #define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0 -static const char dwc2_driver_name[] = "dwc2"; - -static const struct dwc2_core_params dwc2_module_params = { - .otg_cap = -1, - .otg_ver = -1, - .dma_enable = -1, - .dma_desc_enable = 0, - .speed = -1, - .enable_dynamic_fifo = -1, - .en_multiple_tx_fifo = -1, - .host_rx_fifo_size = 1024, - .host_nperio_tx_fifo_size = 256, - .host_perio_tx_fifo_size = 1024, - .max_transfer_size = 65535, - .max_packet_count = 511, - .host_channels = -1, - .phy_type = -1, - .phy_utmi_width = -1, - .phy_ulpi_ddr = -1, - .phy_ulpi_ext_vbus = -1, - .i2c_enable = -1, - .ulpi_fs_ls = -1, - .host_support_fs_ls_low_power = -1, - .host_ls_low_power_phy_clk = -1, - .ts_dline = -1, - .reload_ctl = -1, - .ahbcfg = -1, - .uframe_sched = -1, +static const char dwc2_driver_name[] = "dwc2-pci"; + +struct dwc2_pci_glue { + struct platform_device *dwc2; + struct platform_device *phy; }; -/** - * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the - * DWC_otg driver - * - * @dev: Bus device - * - * This routine is called, for example, when the rmmod command is executed. The - * device may or may not be electrically present. If it is present, the driver - * stops device processing. Any resources used on behalf of this device are - * freed. - */ -static void dwc2_driver_remove(struct pci_dev *dev) +static void dwc2_pci_remove(struct pci_dev *pci) { - struct dwc2_hsotg *hsotg = pci_get_drvdata(dev); + struct dwc2_pci_glue *glue = pci_get_drvdata(pci); - dwc2_hcd_remove(hsotg); - pci_disable_device(dev); + platform_device_unregister(glue->dwc2); + usb_phy_generic_unregister(glue->phy); + kfree(glue); + pci_set_drvdata(pci, NULL); } -/** - * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg - * driver - * - * @dev: Bus device - * - * This routine creates the driver components required to control the device - * (core, HCD, and PCD) and initializes the device. The driver components are - * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved - * in the device private data. This allows the driver to access the dwc2_hsotg - * structure on subsequent calls to driver methods for this device. - */ -static int dwc2_driver_probe(struct pci_dev *dev, - const struct pci_device_id *id) +static int dwc2_pci_probe(struct pci_dev *pci, + const struct pci_device_id *id) { - struct dwc2_hsotg *hsotg; - int retval; + struct resource res[2]; + struct platform_device *dwc2; + struct platform_device *phy; + int ret; + struct device *dev = &pci->dev; + struct dwc2_pci_glue *glue; + + ret = pcim_enable_device(pci); + if (ret) { + dev_err(dev, "failed to enable pci device\n"); + return -ENODEV; + } + + pci_set_master(pci); - hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL); - if (!hsotg) + dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO); + if (!dwc2) { + dev_err(dev, "couldn't allocate dwc2 device\n"); return -ENOMEM; + } - hsotg->dev = &dev->dev; - hsotg->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]); - if (IS_ERR(hsotg->regs)) - return PTR_ERR(hsotg->regs); + memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); - dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", - (unsigned long)pci_resource_start(dev, 0), hsotg->regs); + res[0].start = pci_resource_start(pci, 0); + res[0].end = pci_resource_end(pci, 0); + res[0].name = "dwc2"; + res[0].flags = IORESOURCE_MEM; - if (pci_enable_device(dev) < 0) - return -ENODEV; + res[1].start = pci->irq; + res[1].name = "dwc2"; + res[1].flags = IORESOURCE_IRQ; - pci_set_master(dev); + ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res)); + if (ret) { + dev_err(dev, "couldn't add resources to dwc2 device\n"); + return ret; + } - retval = devm_request_irq(hsotg->dev, dev->irq, - dwc2_handle_common_intr, IRQF_SHARED, - dev_name(hsotg->dev), hsotg); - if (retval) - return retval; + dwc2->dev.parent = dev; - spin_lock_init(&hsotg->lock); - retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params); - if (retval) { - pci_disable_device(dev); - return retval; + phy = usb_phy_generic_register(); + if (IS_ERR(phy)) { + dev_err(dev, "error registering generic PHY (%ld)\n", + PTR_ERR(phy)); + return PTR_ERR(phy); } - pci_set_drvdata(dev, hsotg); + ret = platform_device_add(dwc2); + if (ret) { + dev_err(dev, "failed to register dwc2 device\n"); + goto err; + } + + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) + return -ENOMEM; + + glue->phy = phy; + glue->dwc2 = dwc2; + pci_set_drvdata(pci, glue); - return retval; + return 0; +err: + usb_phy_generic_unregister(phy); + platform_device_put(dwc2); + return ret; } static const struct pci_device_id dwc2_pci_ids[] = { @@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids); static struct pci_driver dwc2_pci_driver = { .name = dwc2_driver_name, .id_table = dwc2_pci_ids, - .probe = dwc2_driver_probe, - .remove = dwc2_driver_remove, + .probe = dwc2_pci_probe, + .remove = dwc2_pci_remove, }; module_pci_driver(dwc2_pci_driver); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index ae095f009b4f..185663e0b5f4 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -121,8 +121,10 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); - dwc2_hcd_remove(hsotg); - s3c_hsotg_remove(hsotg); + if (hsotg->hcd_enabled) + dwc2_hcd_remove(hsotg); + if (hsotg->gadget_enabled) + s3c_hsotg_remove(hsotg); return 0; } @@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); mutex_init(&hsotg->init_mutex); - retval = dwc2_gadget_init(hsotg, irq); - if (retval) - return retval; - retval = dwc2_hcd_init(hsotg, irq, params); - if (retval) - return retval; + + if (hsotg->dr_mode != USB_DR_MODE_HOST) { + retval = dwc2_gadget_init(hsotg, irq); + if (retval) + return retval; + hsotg->gadget_enabled = 1; + } + + if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { + retval = dwc2_hcd_init(hsotg, irq, params); + if (retval) { + if (hsotg->gadget_enabled) + s3c_hsotg_remove(hsotg); + return retval; + } + hsotg->hcd_enabled = 1; + } platform_set_drvdata(dev, hsotg); diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index edbf9c85af7e..827c4f80379f 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -104,11 +104,4 @@ config USB_DWC3_DEBUG help Say Y here to enable debugging messages on DWC3 Driver. -config DWC3_HOST_USB3_LPM_ENABLE - bool "Enable USB3 LPM Capability" - depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y - default n - help - Select this when you want to enable USB3 LPM with dwc3 xhci host. - endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9f0e209b8f6c..2bbab3d86fff 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev) * since it will be requested by the xhci-plat driver. */ regs = devm_ioremap_resource(dev, res); - if (IS_ERR(regs)) - return PTR_ERR(regs); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err0; + } dwc->regs = regs; dwc->regs_size = resource_size(res); - /* - * restore res->start back to its original value so that, - * in case the probe is deferred, we don't end up getting error in - * request the memory region the next time probe is called. - */ - res->start -= DWC3_GLOBALS_REGS_START; /* default to highest possible threshold */ lpm_nyet_threshold = 0xff; @@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev) "snps,is-utmi-l1-suspend"); of_property_read_u8(node, "snps,hird-threshold", &hird_threshold); + dwc->usb3_lpm_capable = of_property_read_bool(node, + "snps,usb3_lpm_capable"); dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); @@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev) hird_threshold = pdata->hird_threshold; dwc->needs_fifo_resize = pdata->tx_fifo_resize; + dwc->usb3_lpm_capable = pdata->usb3_lpm_capable; dwc->dr_mode = pdata->dr_mode; dwc->disable_scramble_quirk = pdata->disable_scramble_quirk; @@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_core_get_phy(dwc); if (ret) - return ret; + goto err0; spin_lock_init(&dwc->lock); platform_set_drvdata(pdev, dwc); @@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); ret = -ENOMEM; - goto err0; + goto err1; } if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) @@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); - goto err0; + goto err1; } usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); ret = phy_power_on(dwc->usb2_generic_phy); if (ret < 0) - goto err1; + goto err2; ret = phy_power_on(dwc->usb3_generic_phy); if (ret < 0) - goto err_usb2phy_power; + goto err3; ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err_usb3phy_power; + goto err4; } ret = dwc3_core_init_mode(dwc); if (ret) - goto err2; + goto err5; ret = dwc3_debugfs_init(dwc); if (ret) { dev_err(dev, "failed to initialize debugfs\n"); - goto err3; + goto err6; } pm_runtime_allow(dev); return 0; -err3: +err6: dwc3_core_exit_mode(dwc); -err2: +err5: dwc3_event_buffers_cleanup(dwc); -err_usb3phy_power: +err4: phy_power_off(dwc->usb3_generic_phy); -err_usb2phy_power: +err3: phy_power_off(dwc->usb2_generic_phy); -err1: +err2: usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1); dwc3_core_exit(dwc); -err0: +err1: dwc3_free_event_buffers(dwc); +err0: + /* + * restore res->start back to its original value so that, in case the + * probe is deferred, we don't end up getting error in request the + * memory region the next time probe is called. + */ + res->start -= DWC3_GLOBALS_REGS_START; + return ret; } static int dwc3_remove(struct platform_device *pdev) { struct dwc3 *dwc = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + /* + * restore res->start back to its original value so that, in case the + * probe is deferred, we don't end up getting error in request the + * memory region the next time probe is called. + */ + res->start -= DWC3_GLOBALS_REGS_START; dwc3_debugfs_exit(dwc); dwc3_core_exit_mode(dwc); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index d201910b892f..fdab715a0631 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -689,6 +689,7 @@ struct dwc3_scratchpad_array { * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @start_config_issued: true when StartConfig command has been issued * @three_stage_setup: set if we perform a three phase setup + * @usb3_lpm_capable: set if hadrware supports Link Power Management * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -812,6 +813,7 @@ struct dwc3 { unsigned setup_packet_pending:1; unsigned start_config_issued:1; unsigned three_stage_setup:1; + unsigned usb3_lpm_capable:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 52e0c4e5e48e..edba5348be18 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) return IRQ_HANDLED; } -static int dwc3_omap_remove_core(struct device *dev, void *c) -{ - struct platform_device *pdev = to_platform_device(dev); - - of_device_unregister(pdev); - - return 0; -} - static void dwc3_omap_enable_irqs(struct dwc3_omap *omap) { u32 reg; @@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev) if (omap->extcon_id_dev.edev) extcon_unregister_interest(&omap->extcon_id_dev); dwc3_omap_disable_irqs(omap); - device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core); + of_platform_depopulate(omap->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 8d950569d557..b773fb53d6a7 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -24,8 +24,6 @@ #include "platform_data.h" -/* FIXME define these in <linux/pci_ids.h> */ -#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd #define PCI_DEVICE_ID_INTEL_BYT 0x0f37 #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a03a485205c7..8946c32047e9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1855,32 +1855,27 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int i; int ret; + req = next_request(&dep->req_queued); + if (!req) { + WARN_ON_ONCE(1); + return 1; + } + i = 0; do { - req = next_request(&dep->req_queued); - if (!req) { - WARN_ON_ONCE(1); - return 1; - } - i = 0; - do { - slot = req->start_slot + i; - if ((slot == DWC3_TRB_NUM - 1) && + slot = req->start_slot + i; + if ((slot == DWC3_TRB_NUM - 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc)) - slot++; - slot %= DWC3_TRB_NUM; - trb = &dep->trb_pool[slot]; - - ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, - event, status); - if (ret) - break; - }while (++i < req->request.num_mapped_sgs); - - dwc3_gadget_giveback(dep, req, status); + slot++; + slot %= DWC3_TRB_NUM; + trb = &dep->trb_pool[slot]; + ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, + event, status); if (ret) break; - } while (1); + } while (++i < req->request.num_mapped_sgs); + + dwc3_gadget_giveback(dep, req, status); if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->req_queued)) { diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 12bfd3c5405e..c679f63783ae 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc) memset(&pdata, 0, sizeof(pdata)); -#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE - pdata.usb3_lpm_capable = 1; -#endif + pdata.usb3_lpm_capable = dwc->usb3_lpm_capable; ret = platform_device_add_data(xhci, &pdata, sizeof(pdata)); if (ret) { diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index a3a3b6d5668c..a2bd464be828 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -24,6 +24,7 @@ struct dwc3_platform_data { enum usb_device_speed maximum_speed; enum usb_dr_mode dr_mode; bool tx_fifo_resize; + bool usb3_lpm_capable; unsigned is_utmi_l1_suspend:1; u8 hird_threshold; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b454d05be583..bcf83c0a6e62 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -196,6 +196,9 @@ config USB_F_MIDI config USB_F_HID tristate +config USB_F_PRINTER + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC device. It provides a userspace API to process UVC control requests and stream video data to the host. +config USB_CONFIGFS_F_PRINTER + bool "Printer function" + select USB_F_PRINTER + depends on USB_CONFIGFS + help + The Printer function channels data between the USB host and a + userspace program driving the print engine. The user space + program reads and writes the device file /dev/g_printer<X> to + receive or send printer data. It can use ioctl calls to + the device file to get or set printer status. + + For more information, see Documentation/usb/gadget_printer.txt + which includes sample code for accessing the device file. + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 13adfd1a3f54..4e3447bbd097 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings( * This function will create a deep copy of usb_gadget_strings and usb_string * and attach it to the cdev. The actual string (usb_string.s) will not be * copied but only a referenced will be made. The struct usb_gadget_strings - * array may contain multiple languges and should be NULL terminated. + * array may contain multiple languages and should be NULL terminated. * The ->language pointer of each struct usb_gadget_strings has to contain the * same amount of entries. * For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first - * usb_string entry of es-ES containts the translation of the first usb_string + * usb_string entry of es-ES contains the translation of the first usb_string * entry of en-US. Therefore both entries become the same id assign. */ struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, @@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) req->length = 0; gadget->ep0->driver_data = cdev; + /* + * Don't let non-standard requests match any of the cases below + * by accident. + */ + if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + goto unknown; + switch (ctrl->bRequest) { /* we handle all standard USB descriptors */ @@ -1751,6 +1758,10 @@ unknown: * take such requests too, if that's ever needed: to work * in config 0, etc. */ + list_for_each_entry(f, &cdev->config->functions, list) + if (f->req_match && f->req_match(f, ctrl)) + goto try_fun_setup; + f = NULL; switch (ctrl->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) @@ -1768,7 +1779,7 @@ unknown: f = NULL; break; } - +try_fun_setup: if (f && f->setup) value = f->setup(f, ctrl); else { diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index f71b1aaa0edf..bd7def576955 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o usb_f_hid-y := f_hid.o obj-$(CONFIG_USB_F_HID) += usb_f_hid.o +usb_f_printer-y := f_printer.o +obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index a2612fb79eff..13dfc9915b1d 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) /* disable/free request and end point */ usb_ep_disable(hidg->in_ep); - usb_ep_dequeue(hidg->in_ep, hidg->req); kfree(hidg->req->buf); usb_ep_free_request(hidg->in_ep, hidg->req); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 49a2f19c2b87..3cc109f3c9c8 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) if (!curlun) { /* Unsupported LUNs are okay */ common->bad_lun_okay = 1; memset(buf, 0, 36); - buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[0] = TYPE_NO_LUN; /* Unsupported, no device-type */ buf[4] = 31; /* Additional length */ return 36; } diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c new file mode 100644 index 000000000000..44173df27273 --- /dev/null +++ b/drivers/usb/gadget/function/f_printer.c @@ -0,0 +1,1471 @@ +/* + * f_printer.c - USB printer function driver + * + * Copied from drivers/usb/gadget/legacy/printer.c, + * which was: + * + * printer.c -- Printer gadget driver + * + * Copyright (C) 2003-2005 David Brownell + * Copyright (C) 2006 Craig W. Nadler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/idr.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/moduleparam.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/types.h> +#include <linux/ctype.h> +#include <linux/cdev.h> + +#include <asm/byteorder.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/uaccess.h> +#include <asm/unaligned.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/composite.h> +#include <linux/usb/gadget.h> +#include <linux/usb/g_printer.h> + +#include "u_printer.h" + +#define PNP_STRING_LEN 1024 +#define PRINTER_MINORS 4 +#define GET_DEVICE_ID 0 +#define GET_PORT_STATUS 1 +#define SOFT_RESET 2 + +static int major, minors; +static struct class *usb_gadget_class; +static DEFINE_IDA(printer_ida); +static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */ + +/*-------------------------------------------------------------------------*/ + +struct printer_dev { + spinlock_t lock; /* lock this structure */ + /* lock buffer lists during read/write calls */ + struct mutex lock_printer_io; + struct usb_gadget *gadget; + s8 interface; + struct usb_ep *in_ep, *out_ep; + + struct list_head rx_reqs; /* List of free RX structs */ + struct list_head rx_reqs_active; /* List of Active RX xfers */ + struct list_head rx_buffers; /* List of completed xfers */ + /* wait until there is data to be read. */ + wait_queue_head_t rx_wait; + struct list_head tx_reqs; /* List of free TX structs */ + struct list_head tx_reqs_active; /* List of Active TX xfers */ + /* Wait until there are write buffers available to use. */ + wait_queue_head_t tx_wait; + /* Wait until all write buffers have been sent. */ + wait_queue_head_t tx_flush_wait; + struct usb_request *current_rx_req; + size_t current_rx_bytes; + u8 *current_rx_buf; + u8 printer_status; + u8 reset_printer; + int minor; + struct cdev printer_cdev; + u8 printer_cdev_open; + wait_queue_head_t wait; + unsigned q_len; + char *pnp_string; /* We don't own memory! */ + struct usb_function function; +}; + +static inline struct printer_dev *func_to_printer(struct usb_function *f) +{ + return container_of(f, struct printer_dev, function); +} + +/*-------------------------------------------------------------------------*/ + +/* + * DESCRIPTORS ... most are static, but strings and (full) configuration + * descriptors are built on demand. + */ + +/* holds our biggest descriptor */ +#define USB_DESC_BUFSIZE 256 +#define USB_BUFSIZE 8192 + +static struct usb_interface_descriptor intf_desc = { + .bLength = sizeof(intf_desc), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_PRINTER, + .bInterfaceSubClass = 1, /* Printer Sub-Class */ + .bInterfaceProtocol = 2, /* Bi-Directional */ + .iInterface = 0 +}; + +static struct usb_endpoint_descriptor fs_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK +}; + +static struct usb_endpoint_descriptor fs_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK +}; + +static struct usb_descriptor_header *fs_printer_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &fs_ep_in_desc, + (struct usb_descriptor_header *) &fs_ep_out_desc, + NULL +}; + +/* + * usb 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + */ + +static struct usb_endpoint_descriptor hs_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512) +}; + +static struct usb_endpoint_descriptor hs_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512) +}; + +static struct usb_qualifier_descriptor dev_qualifier = { + .bLength = sizeof(dev_qualifier), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PRINTER, + .bNumConfigurations = 1 +}; + +static struct usb_descriptor_header *hs_printer_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &hs_ep_in_desc, + (struct usb_descriptor_header *) &hs_ep_out_desc, + NULL +}; + +/* + * Added endpoint descriptors for 3.0 devices + */ + +static struct usb_endpoint_descriptor ss_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = { + .bLength = sizeof(ss_ep_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_endpoint_descriptor ss_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = { + .bLength = sizeof(ss_ep_out_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, +}; + +static struct usb_descriptor_header *ss_printer_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &ss_ep_in_desc, + (struct usb_descriptor_header *) &ss_ep_in_comp_desc, + (struct usb_descriptor_header *) &ss_ep_out_desc, + (struct usb_descriptor_header *) &ss_ep_out_comp_desc, + NULL +}; + +/* maxpacket and other transfer characteristics vary by speed. */ +static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget, + struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *ss) +{ + switch (gadget->speed) { + case USB_SPEED_SUPER: + return ss; + case USB_SPEED_HIGH: + return hs; + default: + return fs; + } +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request * +printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, gfp_flags); + + if (req != NULL) { + req->length = len; + req->buf = kmalloc(len, gfp_flags); + if (req->buf == NULL) { + usb_ep_free_request(ep, req); + return NULL; + } + } + + return req; +} + +static void +printer_req_free(struct usb_ep *ep, struct usb_request *req) +{ + if (ep != NULL && req != NULL) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +/*-------------------------------------------------------------------------*/ + +static void rx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct printer_dev *dev = ep->driver_data; + int status = req->status; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + list_del_init(&req->list); /* Remode from Active List */ + + switch (status) { + + /* normal completion */ + case 0: + if (req->actual > 0) { + list_add_tail(&req->list, &dev->rx_buffers); + DBG(dev, "G_Printer : rx length %d\n", req->actual); + } else { + list_add(&req->list, &dev->rx_reqs); + } + break; + + /* software-driven interface shutdown */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + VDBG(dev, "rx shutdown, code %d\n", status); + list_add(&req->list, &dev->rx_reqs); + break; + + /* for hardware automagic (such as pxa) */ + case -ECONNABORTED: /* endpoint reset */ + DBG(dev, "rx %s reset\n", ep->name); + list_add(&req->list, &dev->rx_reqs); + break; + + /* data overrun */ + case -EOVERFLOW: + /* FALLTHROUGH */ + + default: + DBG(dev, "rx status %d\n", status); + list_add(&req->list, &dev->rx_reqs); + break; + } + + wake_up_interruptible(&dev->rx_wait); + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct printer_dev *dev = ep->driver_data; + + switch (req->status) { + default: + VDBG(dev, "tx err %d\n", req->status); + /* FALLTHROUGH */ + case -ECONNRESET: /* unlink */ + case -ESHUTDOWN: /* disconnect etc */ + break; + case 0: + break; + } + + spin_lock(&dev->lock); + /* Take the request struct off the active list and put it on the + * free list. + */ + list_del_init(&req->list); + list_add(&req->list, &dev->tx_reqs); + wake_up_interruptible(&dev->tx_wait); + if (likely(list_empty(&dev->tx_reqs_active))) + wake_up_interruptible(&dev->tx_flush_wait); + + spin_unlock(&dev->lock); +} + +/*-------------------------------------------------------------------------*/ + +static int +printer_open(struct inode *inode, struct file *fd) +{ + struct printer_dev *dev; + unsigned long flags; + int ret = -EBUSY; + + dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); + + spin_lock_irqsave(&dev->lock, flags); + + if (!dev->printer_cdev_open) { + dev->printer_cdev_open = 1; + fd->private_data = dev; + ret = 0; + /* Change the printer status to show that it's on-line. */ + dev->printer_status |= PRINTER_SELECTED; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + DBG(dev, "printer_open returned %x\n", ret); + return ret; +} + +static int +printer_close(struct inode *inode, struct file *fd) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + dev->printer_cdev_open = 0; + fd->private_data = NULL; + /* Change printer status to show that the printer is off-line. */ + dev->printer_status &= ~PRINTER_SELECTED; + spin_unlock_irqrestore(&dev->lock, flags); + + DBG(dev, "printer_close\n"); + + return 0; +} + +/* This function must be called with interrupts turned off. */ +static void +setup_rx_reqs(struct printer_dev *dev) +{ + struct usb_request *req; + + while (likely(!list_empty(&dev->rx_reqs))) { + int error; + + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del_init(&req->list); + + /* The USB Host sends us whatever amount of data it wants to + * so we always set the length field to the full USB_BUFSIZE. + * If the amount of data is more than the read() caller asked + * for it will be stored in the request buffer until it is + * asked for by read(). + */ + req->length = USB_BUFSIZE; + req->complete = rx_complete; + + /* here, we unlock, and only unlock, to avoid deadlock. */ + spin_unlock(&dev->lock); + error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); + spin_lock(&dev->lock); + if (error) { + DBG(dev, "rx submit --> %d\n", error); + list_add(&req->list, &dev->rx_reqs); + break; + } + /* if the req is empty, then add it into dev->rx_reqs_active. */ + else if (list_empty(&req->list)) + list_add(&req->list, &dev->rx_reqs_active); + } +} + +static ssize_t +printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + size_t size; + size_t bytes_copied; + struct usb_request *req; + /* This is a pointer to the current USB rx request. */ + struct usb_request *current_rx_req; + /* This is the number of bytes in the current rx buffer. */ + size_t current_rx_bytes; + /* This is a pointer to the current rx buffer. */ + u8 *current_rx_buf; + + if (len == 0) + return -EINVAL; + + DBG(dev, "printer_read trying to read %d bytes\n", (int)len); + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + + /* We will use this flag later to check if a printer reset happened + * after we turn interrupts back on. + */ + dev->reset_printer = 0; + + setup_rx_reqs(dev); + + bytes_copied = 0; + current_rx_req = dev->current_rx_req; + current_rx_bytes = dev->current_rx_bytes; + current_rx_buf = dev->current_rx_buf; + dev->current_rx_req = NULL; + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + + /* Check if there is any data in the read buffers. Please note that + * current_rx_bytes is the number of bytes in the current rx buffer. + * If it is zero then check if there are any other rx_buffers that + * are on the completed list. We are only out of data if all rx + * buffers are empty. + */ + if ((current_rx_bytes == 0) && + (likely(list_empty(&dev->rx_buffers)))) { + /* Turn interrupts back on before sleeping. */ + spin_unlock_irqrestore(&dev->lock, flags); + + /* + * If no data is available check if this is a NON-Blocking + * call or not. + */ + if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* Sleep until data is available */ + wait_event_interruptible(dev->rx_wait, + (likely(!list_empty(&dev->rx_buffers)))); + spin_lock_irqsave(&dev->lock, flags); + } + + /* We have data to return then copy it to the caller's buffer.*/ + while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers))) + && len) { + if (current_rx_bytes == 0) { + req = container_of(dev->rx_buffers.next, + struct usb_request, list); + list_del_init(&req->list); + + if (req->actual && req->buf) { + current_rx_req = req; + current_rx_bytes = req->actual; + current_rx_buf = req->buf; + } else { + list_add(&req->list, &dev->rx_reqs); + continue; + } + } + + /* Don't leave irqs off while doing memory copies */ + spin_unlock_irqrestore(&dev->lock, flags); + + if (len > current_rx_bytes) + size = current_rx_bytes; + else + size = len; + + size -= copy_to_user(buf, current_rx_buf, size); + bytes_copied += size; + len -= size; + buf += size; + + spin_lock_irqsave(&dev->lock, flags); + + /* We've disconnected or reset so return. */ + if (dev->reset_printer) { + list_add(¤t_rx_req->list, &dev->rx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* If we not returning all the data left in this RX request + * buffer then adjust the amount of data left in the buffer. + * Othewise if we are done with this RX request buffer then + * requeue it to get any incoming data from the USB host. + */ + if (size < current_rx_bytes) { + current_rx_bytes -= size; + current_rx_buf += size; + } else { + list_add(¤t_rx_req->list, &dev->rx_reqs); + current_rx_bytes = 0; + current_rx_buf = NULL; + current_rx_req = NULL; + } + } + + dev->current_rx_req = current_rx_req; + dev->current_rx_bytes = current_rx_bytes; + dev->current_rx_buf = current_rx_buf; + + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied); + + if (bytes_copied) + return bytes_copied; + else + return -EAGAIN; +} + +static ssize_t +printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + size_t size; /* Amount of data in a TX request. */ + size_t bytes_copied = 0; + struct usb_request *req; + + DBG(dev, "printer_write trying to send %d bytes\n", (int)len); + + if (len == 0) + return -EINVAL; + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + + /* Check if a printer reset happens while we have interrupts on */ + dev->reset_printer = 0; + + /* Check if there is any available write buffers */ + if (likely(list_empty(&dev->tx_reqs))) { + /* Turn interrupts back on before sleeping. */ + spin_unlock_irqrestore(&dev->lock, flags); + + /* + * If write buffers are available check if this is + * a NON-Blocking call or not. + */ + if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + /* Sleep until a write buffer is available */ + wait_event_interruptible(dev->tx_wait, + (likely(!list_empty(&dev->tx_reqs)))); + spin_lock_irqsave(&dev->lock, flags); + } + + while (likely(!list_empty(&dev->tx_reqs)) && len) { + + if (len > USB_BUFSIZE) + size = USB_BUFSIZE; + else + size = len; + + req = container_of(dev->tx_reqs.next, struct usb_request, + list); + list_del_init(&req->list); + + req->complete = tx_complete; + req->length = size; + + /* Check if we need to send a zero length packet. */ + if (len > size) + /* They will be more TX requests so no yet. */ + req->zero = 0; + else + /* If the data amount is not a multiple of the + * maxpacket size then send a zero length packet. + */ + req->zero = ((len % dev->in_ep->maxpacket) == 0); + + /* Don't leave irqs off while doing memory copies */ + spin_unlock_irqrestore(&dev->lock, flags); + + if (copy_from_user(req->buf, buf, size)) { + list_add(&req->list, &dev->tx_reqs); + mutex_unlock(&dev->lock_printer_io); + return bytes_copied; + } + + bytes_copied += size; + len -= size; + buf += size; + + spin_lock_irqsave(&dev->lock, flags); + + /* We've disconnected or reset so free the req and buffer */ + if (dev->reset_printer) { + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) { + list_add(&req->list, &dev->tx_reqs); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + return -EAGAIN; + } + + list_add(&req->list, &dev->tx_reqs_active); + + } + + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied); + + if (bytes_copied) + return bytes_copied; + else + return -EAGAIN; +} + +static int +printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) +{ + struct printer_dev *dev = fd->private_data; + struct inode *inode = file_inode(fd); + unsigned long flags; + int tx_list_empty; + + mutex_lock(&inode->i_mutex); + spin_lock_irqsave(&dev->lock, flags); + tx_list_empty = (likely(list_empty(&dev->tx_reqs))); + spin_unlock_irqrestore(&dev->lock, flags); + + if (!tx_list_empty) { + /* Sleep until all data has been sent */ + wait_event_interruptible(dev->tx_flush_wait, + (likely(list_empty(&dev->tx_reqs_active)))); + } + mutex_unlock(&inode->i_mutex); + + return 0; +} + +static unsigned int +printer_poll(struct file *fd, poll_table *wait) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + int status = 0; + + mutex_lock(&dev->lock_printer_io); + spin_lock_irqsave(&dev->lock, flags); + setup_rx_reqs(dev); + spin_unlock_irqrestore(&dev->lock, flags); + mutex_unlock(&dev->lock_printer_io); + + poll_wait(fd, &dev->rx_wait, wait); + poll_wait(fd, &dev->tx_wait, wait); + + spin_lock_irqsave(&dev->lock, flags); + if (likely(!list_empty(&dev->tx_reqs))) + status |= POLLOUT | POLLWRNORM; + + if (likely(dev->current_rx_bytes) || + likely(!list_empty(&dev->rx_buffers))) + status |= POLLIN | POLLRDNORM; + + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +static long +printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) +{ + struct printer_dev *dev = fd->private_data; + unsigned long flags; + int status = 0; + + DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg); + + /* handle ioctls */ + + spin_lock_irqsave(&dev->lock, flags); + + switch (code) { + case GADGET_GET_PRINTER_STATUS: + status = (int)dev->printer_status; + break; + case GADGET_SET_PRINTER_STATUS: + dev->printer_status = (u8)arg; + break; + default: + /* could not handle ioctl */ + DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n", + code); + status = -ENOTTY; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return status; +} + +/* used after endpoint configuration */ +static const struct file_operations printer_io_operations = { + .owner = THIS_MODULE, + .open = printer_open, + .read = printer_read, + .write = printer_write, + .fsync = printer_fsync, + .poll = printer_poll, + .unlocked_ioctl = printer_ioctl, + .release = printer_close, + .llseek = noop_llseek, +}; + +/*-------------------------------------------------------------------------*/ + +static int +set_printer_interface(struct printer_dev *dev) +{ + int result = 0; + + dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc, + &ss_ep_in_desc); + dev->in_ep->driver_data = dev; + + dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc, + &hs_ep_out_desc, &ss_ep_out_desc); + dev->out_ep->driver_data = dev; + + result = usb_ep_enable(dev->in_ep); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); + goto done; + } + + result = usb_ep_enable(dev->out_ep); + if (result != 0) { + DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); + goto done; + } + +done: + /* on error, disable any endpoints */ + if (result != 0) { + (void) usb_ep_disable(dev->in_ep); + (void) usb_ep_disable(dev->out_ep); + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; + } + + /* caller is responsible for cleanup on error */ + return result; +} + +static void printer_reset_interface(struct printer_dev *dev) +{ + if (dev->interface < 0) + return; + + DBG(dev, "%s\n", __func__); + + if (dev->in_ep->desc) + usb_ep_disable(dev->in_ep); + + if (dev->out_ep->desc) + usb_ep_disable(dev->out_ep); + + dev->in_ep->desc = NULL; + dev->out_ep->desc = NULL; + dev->interface = -1; +} + +/* Change our operational Interface. */ +static int set_interface(struct printer_dev *dev, unsigned number) +{ + int result = 0; + + /* Free the current interface */ + printer_reset_interface(dev); + + result = set_printer_interface(dev); + if (result) + printer_reset_interface(dev); + else + dev->interface = number; + + if (!result) + INFO(dev, "Using interface %x\n", number); + + return result; +} + +static void printer_soft_reset(struct printer_dev *dev) +{ + struct usb_request *req; + + INFO(dev, "Received Printer Reset Request\n"); + + if (usb_ep_disable(dev->in_ep)) + DBG(dev, "Failed to disable USB in_ep\n"); + if (usb_ep_disable(dev->out_ep)) + DBG(dev, "Failed to disable USB out_ep\n"); + + if (dev->current_rx_req != NULL) { + list_add(&dev->current_rx_req->list, &dev->rx_reqs); + dev->current_rx_req = NULL; + } + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + dev->reset_printer = 1; + + while (likely(!(list_empty(&dev->rx_buffers)))) { + req = container_of(dev->rx_buffers.next, struct usb_request, + list); + list_del_init(&req->list); + list_add(&req->list, &dev->rx_reqs); + } + + while (likely(!(list_empty(&dev->rx_reqs_active)))) { + req = container_of(dev->rx_buffers.next, struct usb_request, + list); + list_del_init(&req->list); + list_add(&req->list, &dev->rx_reqs); + } + + while (likely(!(list_empty(&dev->tx_reqs_active)))) { + req = container_of(dev->tx_reqs_active.next, + struct usb_request, list); + list_del_init(&req->list); + list_add(&req->list, &dev->tx_reqs); + } + + if (usb_ep_enable(dev->in_ep)) + DBG(dev, "Failed to enable USB in_ep\n"); + if (usb_ep_enable(dev->out_ep)) + DBG(dev, "Failed to enable USB out_ep\n"); + + wake_up_interruptible(&dev->rx_wait); + wake_up_interruptible(&dev->tx_wait); + wake_up_interruptible(&dev->tx_flush_wait); +} + +/*-------------------------------------------------------------------------*/ + +static bool gprinter_req_match(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct printer_dev *dev = func_to_printer(f); + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE || + (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) + return false; + + switch (ctrl->bRequest) { + case GET_DEVICE_ID: + w_index >>= 8; + if (w_length <= PNP_STRING_LEN && + (USB_DIR_IN & ctrl->bRequestType)) + break; + return false; + case GET_PORT_STATUS: + if (!w_value && w_length == 1 && + (USB_DIR_IN & ctrl->bRequestType)) + break; + return false; + case SOFT_RESET: + if (!w_value && !w_length && + !(USB_DIR_IN & ctrl->bRequestType)) + break; + /* fall through */ + default: + return false; + } + return w_index == dev->interface; +} + +/* + * The setup() callback implements all the ep0 functionality that's not + * handled lower down. + */ +static int printer_func_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct printer_dev *dev = func_to_printer(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 wIndex = le16_to_cpu(ctrl->wIndex); + u16 wValue = le16_to_cpu(ctrl->wValue); + u16 wLength = le16_to_cpu(ctrl->wLength); + + DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); + + switch (ctrl->bRequestType&USB_TYPE_MASK) { + case USB_TYPE_CLASS: + switch (ctrl->bRequest) { + case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */ + /* Only one printer interface is supported. */ + if ((wIndex>>8) != dev->interface) + break; + + value = (dev->pnp_string[0] << 8) | dev->pnp_string[1]; + memcpy(req->buf, dev->pnp_string, value); + DBG(dev, "1284 PNP String: %x %s\n", value, + &dev->pnp_string[2]); + break; + + case GET_PORT_STATUS: /* Get Port Status */ + /* Only one printer interface is supported. */ + if (wIndex != dev->interface) + break; + + *(u8 *)req->buf = dev->printer_status; + value = min_t(u16, wLength, 1); + break; + + case SOFT_RESET: /* Soft Reset */ + /* Only one printer interface is supported. */ + if (wIndex != dev->interface) + break; + + printer_soft_reset(dev); + + value = 0; + break; + + default: + goto unknown; + } + break; + + default: +unknown: + VDBG(dev, + "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); + break; + } + /* host either stalls (value < 0) or reports success */ + if (value >= 0) { + req->length = value; + req->zero = value < wLength; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + ERROR(dev, "%s:%d Error!\n", __func__, __LINE__); + req->status = 0; + } + } + return value; +} + +static int printer_func_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct usb_gadget *gadget = c->cdev->gadget; + struct printer_dev *dev = func_to_printer(f); + struct device *pdev; + struct usb_composite_dev *cdev = c->cdev; + struct usb_ep *in_ep; + struct usb_ep *out_ep = NULL; + struct usb_request *req; + dev_t devt; + int id; + int ret; + u32 i; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + intf_desc.bInterfaceNumber = id; + + /* finish hookup to lower layer ... */ + dev->gadget = gadget; + + /* all we really need is bulk IN/OUT */ + in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); + if (!in_ep) { +autoconf_fail: + dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n", + cdev->gadget->name); + return -ENODEV; + } + in_ep->driver_data = in_ep; /* claim */ + + out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); + if (!out_ep) + goto autoconf_fail; + out_ep->driver_data = out_ep; /* claim */ + + /* assumes that all endpoints are dual-speed */ + hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; + hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; + ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; + ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; + + ret = usb_assign_descriptors(f, fs_printer_function, + hs_printer_function, ss_printer_function); + if (ret) + return ret; + + dev->in_ep = in_ep; + dev->out_ep = out_ep; + + ret = -ENOMEM; + for (i = 0; i < dev->q_len; i++) { + req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); + if (!req) + goto fail_tx_reqs; + list_add(&req->list, &dev->tx_reqs); + } + + for (i = 0; i < dev->q_len; i++) { + req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); + if (!req) + goto fail_rx_reqs; + list_add(&req->list, &dev->rx_reqs); + } + + /* Setup the sysfs files for the printer gadget. */ + devt = MKDEV(major, dev->minor); + pdev = device_create(usb_gadget_class, NULL, devt, + NULL, "g_printer%d", dev->minor); + if (IS_ERR(pdev)) { + ERROR(dev, "Failed to create device: g_printer\n"); + ret = PTR_ERR(pdev); + goto fail_rx_reqs; + } + + /* + * Register a character device as an interface to a user mode + * program that handles the printer specific functionality. + */ + cdev_init(&dev->printer_cdev, &printer_io_operations); + dev->printer_cdev.owner = THIS_MODULE; + ret = cdev_add(&dev->printer_cdev, devt, 1); + if (ret) { + ERROR(dev, "Failed to open char device\n"); + goto fail_cdev_add; + } + + return 0; + +fail_cdev_add: + device_destroy(usb_gadget_class, devt); + +fail_rx_reqs: + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + +fail_tx_reqs: + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->in_ep, req); + } + + return ret; + +} + +static int printer_func_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct printer_dev *dev = func_to_printer(f); + int ret = -ENOTSUPP; + + if (!alt) + ret = set_interface(dev, intf); + + return ret; +} + +static void printer_func_disable(struct usb_function *f) +{ + struct printer_dev *dev = func_to_printer(f); + unsigned long flags; + + DBG(dev, "%s\n", __func__); + + spin_lock_irqsave(&dev->lock, flags); + printer_reset_interface(dev); + spin_unlock_irqrestore(&dev->lock, flags); +} + +static inline struct f_printer_opts +*to_f_printer_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_printer_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_printer_opts); +CONFIGFS_ATTR_OPS(f_printer_opts); + +static void printer_attr_release(struct config_item *item) +{ + struct f_printer_opts *opts = to_f_printer_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations printer_item_ops = { + .release = printer_attr_release, + .show_attribute = f_printer_opts_attr_show, + .store_attribute = f_printer_opts_attr_store, +}; + +static ssize_t f_printer_opts_pnp_string_show(struct f_printer_opts *opts, + char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = strlcpy(page, opts->pnp_string + 2, PNP_STRING_LEN - 2); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_printer_opts_pnp_string_store(struct f_printer_opts *opts, + const char *page, size_t len) +{ + int result, l; + + mutex_lock(&opts->lock); + result = strlcpy(opts->pnp_string + 2, page, PNP_STRING_LEN - 2); + l = strlen(opts->pnp_string + 2) + 2; + opts->pnp_string[0] = (l >> 8) & 0xFF; + opts->pnp_string[1] = l & 0xFF; + mutex_unlock(&opts->lock); + + return result; +} + +static struct f_printer_opts_attribute f_printer_opts_pnp_string = + __CONFIGFS_ATTR(pnp_string, S_IRUGO | S_IWUSR, + f_printer_opts_pnp_string_show, + f_printer_opts_pnp_string_store); + +static ssize_t f_printer_opts_q_len_show(struct f_printer_opts *opts, + char *page) +{ + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d\n", opts->q_len); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_printer_opts_q_len_store(struct f_printer_opts *opts, + const char *page, size_t len) +{ + int ret; + u16 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou16(page, 0, &num); + if (ret) + goto end; + + opts->q_len = (unsigned)num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +static struct f_printer_opts_attribute f_printer_opts_q_len = + __CONFIGFS_ATTR(q_len, S_IRUGO | S_IWUSR, f_printer_opts_q_len_show, + f_printer_opts_q_len_store); + +static struct configfs_attribute *printer_attrs[] = { + &f_printer_opts_pnp_string.attr, + &f_printer_opts_q_len.attr, + NULL, +}; + +static struct config_item_type printer_func_type = { + .ct_item_ops = &printer_item_ops, + .ct_attrs = printer_attrs, + .ct_owner = THIS_MODULE, +}; + +static inline int gprinter_get_minor(void) +{ + return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL); +} + +static inline void gprinter_put_minor(int minor) +{ + ida_simple_remove(&printer_ida, minor); +} + +static int gprinter_setup(int); +static void gprinter_cleanup(void); + +static void gprinter_free_inst(struct usb_function_instance *f) +{ + struct f_printer_opts *opts; + + opts = container_of(f, struct f_printer_opts, func_inst); + + mutex_lock(&printer_ida_lock); + + gprinter_put_minor(opts->minor); + if (idr_is_empty(&printer_ida.idr)) + gprinter_cleanup(); + + mutex_unlock(&printer_ida_lock); + + kfree(opts); +} + +static struct usb_function_instance *gprinter_alloc_inst(void) +{ + struct f_printer_opts *opts; + struct usb_function_instance *ret; + int status = 0; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = gprinter_free_inst; + ret = &opts->func_inst; + + mutex_lock(&printer_ida_lock); + + if (idr_is_empty(&printer_ida.idr)) { + status = gprinter_setup(PRINTER_MINORS); + if (status) { + ret = ERR_PTR(status); + kfree(opts); + goto unlock; + } + } + + opts->minor = gprinter_get_minor(); + if (opts->minor < 0) { + ret = ERR_PTR(opts->minor); + kfree(opts); + if (idr_is_empty(&printer_ida.idr)) + gprinter_cleanup(); + goto unlock; + } + config_group_init_type_name(&opts->func_inst.group, "", + &printer_func_type); + +unlock: + mutex_unlock(&printer_ida_lock); + return ret; +} + +static void gprinter_free(struct usb_function *f) +{ + struct printer_dev *dev = func_to_printer(f); + struct f_printer_opts *opts; + + opts = container_of(f->fi, struct f_printer_opts, func_inst); + kfree(dev); + mutex_lock(&opts->lock); + --opts->refcnt; + mutex_unlock(&opts->lock); +} + +static void printer_func_unbind(struct usb_configuration *c, + struct usb_function *f) +{ + struct printer_dev *dev; + struct usb_request *req; + + dev = func_to_printer(f); + + device_destroy(usb_gadget_class, MKDEV(major, dev->minor)); + + /* Remove Character Device */ + cdev_del(&dev->printer_cdev); + + /* we must already have been disconnected ... no i/o may be active */ + WARN_ON(!list_empty(&dev->tx_reqs_active)); + WARN_ON(!list_empty(&dev->rx_reqs_active)); + + /* Free all memory for this driver. */ + while (!list_empty(&dev->tx_reqs)) { + req = container_of(dev->tx_reqs.next, struct usb_request, + list); + list_del(&req->list); + printer_req_free(dev->in_ep, req); + } + + if (dev->current_rx_req != NULL) + printer_req_free(dev->out_ep, dev->current_rx_req); + + while (!list_empty(&dev->rx_reqs)) { + req = container_of(dev->rx_reqs.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + + while (!list_empty(&dev->rx_buffers)) { + req = container_of(dev->rx_buffers.next, + struct usb_request, list); + list_del(&req->list); + printer_req_free(dev->out_ep, req); + } + usb_free_all_descriptors(f); +} + +static struct usb_function *gprinter_alloc(struct usb_function_instance *fi) +{ + struct printer_dev *dev; + struct f_printer_opts *opts; + + opts = container_of(fi, struct f_printer_opts, func_inst); + + mutex_lock(&opts->lock); + if (opts->minor >= minors) { + mutex_unlock(&opts->lock); + return ERR_PTR(-ENOENT); + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + mutex_unlock(&opts->lock); + return ERR_PTR(-ENOMEM); + } + + ++opts->refcnt; + dev->minor = opts->minor; + dev->pnp_string = opts->pnp_string; + dev->q_len = opts->q_len; + mutex_unlock(&opts->lock); + + dev->function.name = "printer"; + dev->function.bind = printer_func_bind; + dev->function.setup = printer_func_setup; + dev->function.unbind = printer_func_unbind; + dev->function.set_alt = printer_func_set_alt; + dev->function.disable = printer_func_disable; + dev->function.req_match = gprinter_req_match; + dev->function.free_func = gprinter_free; + + INIT_LIST_HEAD(&dev->tx_reqs); + INIT_LIST_HEAD(&dev->rx_reqs); + INIT_LIST_HEAD(&dev->rx_buffers); + INIT_LIST_HEAD(&dev->tx_reqs_active); + INIT_LIST_HEAD(&dev->rx_reqs_active); + + spin_lock_init(&dev->lock); + mutex_init(&dev->lock_printer_io); + init_waitqueue_head(&dev->rx_wait); + init_waitqueue_head(&dev->tx_wait); + init_waitqueue_head(&dev->tx_flush_wait); + + dev->interface = -1; + dev->printer_cdev_open = 0; + dev->printer_status = PRINTER_NOT_ERROR; + dev->current_rx_req = NULL; + dev->current_rx_bytes = 0; + dev->current_rx_buf = NULL; + + return &dev->function; +} + +DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Craig Nadler"); + +static int gprinter_setup(int count) +{ + int status; + dev_t devt; + + usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); + if (IS_ERR(usb_gadget_class)) { + status = PTR_ERR(usb_gadget_class); + usb_gadget_class = NULL; + pr_err("unable to create usb_gadget class %d\n", status); + return status; + } + + status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget"); + if (status) { + pr_err("alloc_chrdev_region %d\n", status); + class_destroy(usb_gadget_class); + usb_gadget_class = NULL; + return status; + } + + major = MAJOR(devt); + minors = count; + + return status; +} + +static void gprinter_cleanup(void) +{ + if (major) { + unregister_chrdev_region(MKDEV(major, 0), minors); + major = minors = 0; + } + class_destroy(usb_gadget_class); + usb_gadget_class = NULL; +} diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h new file mode 100644 index 000000000000..0e2c49d4274e --- /dev/null +++ b/drivers/usb/gadget/function/u_printer.h @@ -0,0 +1,37 @@ +/* + * u_printer.h + * + * Utility definitions for the printer function + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef U_PRINTER_H +#define U_PRINTER_H + +#include <linux/usb/composite.h> + +#define PNP_STRING_LEN 1024 + +struct f_printer_opts { + struct usb_function_instance func_inst; + int minor; + char pnp_string[PNP_STRING_LEN]; + unsigned q_len; + + /* + * Protect the data from concurrent access by read/write + * and create symlink/remove symlink + */ + struct mutex lock; + int refcnt; +}; + +#endif /* U_PRINTER_H */ diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 491082aaf103..89179ab20c10 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch) unsigned long flags; int status; - pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n", + pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n", port->port_num, tty, ch, __builtin_return_address(0)); spin_lock_irqsave(&port->port_lock, flags); diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 113c87e22117..d5a7102de696 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -301,6 +301,7 @@ config USB_MIDI_GADGET config USB_G_PRINTER tristate "Printer Gadget" select USB_LIBCOMPOSITE + select USB_F_PRINTER help The Printer Gadget channels data between the USB host and a userspace program driving the print engine. The user space diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 90545980542f..d5b6ee725a2a 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -12,29 +12,7 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/device.h> -#include <linux/moduleparam.h> -#include <linux/fs.h> -#include <linux/poll.h> -#include <linux/types.h> -#include <linux/ctype.h> -#include <linux/cdev.h> - #include <asm/byteorder.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> #include <linux/usb/ch9.h> #include <linux/usb/composite.h> @@ -46,50 +24,12 @@ USB_GADGET_COMPOSITE_OPTIONS(); #define DRIVER_DESC "Printer Gadget" -#define DRIVER_VERSION "2007 OCT 06" +#define DRIVER_VERSION "2015 FEB 17" -static DEFINE_MUTEX(printer_mutex); static const char shortname [] = "printer"; static const char driver_desc [] = DRIVER_DESC; -static dev_t g_printer_devno; - -static struct class *usb_gadget_class; - -/*-------------------------------------------------------------------------*/ - -struct printer_dev { - spinlock_t lock; /* lock this structure */ - /* lock buffer lists during read/write calls */ - struct mutex lock_printer_io; - struct usb_gadget *gadget; - s8 interface; - struct usb_ep *in_ep, *out_ep; - - struct list_head rx_reqs; /* List of free RX structs */ - struct list_head rx_reqs_active; /* List of Active RX xfers */ - struct list_head rx_buffers; /* List of completed xfers */ - /* wait until there is data to be read. */ - wait_queue_head_t rx_wait; - struct list_head tx_reqs; /* List of free TX structs */ - struct list_head tx_reqs_active; /* List of Active TX xfers */ - /* Wait until there are write buffers available to use. */ - wait_queue_head_t tx_wait; - /* Wait until all write buffers have been sent. */ - wait_queue_head_t tx_flush_wait; - struct usb_request *current_rx_req; - size_t current_rx_bytes; - u8 *current_rx_buf; - u8 printer_status; - u8 reset_printer; - struct cdev printer_cdev; - struct device *pdev; - u8 printer_cdev_open; - wait_queue_head_t wait; - struct usb_function function; -}; - -static struct printer_dev usb_printer_gadget; +#include "u_printer.h" /*-------------------------------------------------------------------------*/ @@ -120,6 +60,9 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); #define QLEN qlen +static struct usb_function_instance *fi_printer; +static struct usb_function *f_printer; + /*-------------------------------------------------------------------------*/ /* @@ -127,10 +70,6 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); * descriptors are built on demand. */ -/* holds our biggest descriptor */ -#define USB_DESC_BUFSIZE 256 -#define USB_BUFSIZE 8192 - static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -143,108 +82,6 @@ static struct usb_device_descriptor device_desc = { .bNumConfigurations = 1 }; -static struct usb_interface_descriptor intf_desc = { - .bLength = sizeof intf_desc, - .bDescriptorType = USB_DT_INTERFACE, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_PRINTER, - .bInterfaceSubClass = 1, /* Printer Sub-Class */ - .bInterfaceProtocol = 2, /* Bi-Directional */ - .iInterface = 0 -}; - -static struct usb_endpoint_descriptor fs_ep_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK -}; - -static struct usb_endpoint_descriptor fs_ep_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK -}; - -static struct usb_descriptor_header *fs_printer_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &fs_ep_in_desc, - (struct usb_descriptor_header *) &fs_ep_out_desc, - NULL -}; - -/* - * usb 2.0 devices need to expose both high speed and full speed - * descriptors, unless they only run at full speed. - */ - -static struct usb_endpoint_descriptor hs_ep_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512) -}; - -static struct usb_endpoint_descriptor hs_ep_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(512) -}; - -static struct usb_qualifier_descriptor dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - .bcdUSB = cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PRINTER, - .bNumConfigurations = 1 -}; - -static struct usb_descriptor_header *hs_printer_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &hs_ep_in_desc, - (struct usb_descriptor_header *) &hs_ep_out_desc, - NULL -}; - -/* - * Added endpoint descriptors for 3.0 devices - */ - -static struct usb_endpoint_descriptor ss_ep_in_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(1024), -}; - -static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = { - .bLength = sizeof(ss_ep_in_comp_desc), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, -}; - -static struct usb_endpoint_descriptor ss_ep_out_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = cpu_to_le16(1024), -}; - -static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = { - .bLength = sizeof(ss_ep_out_comp_desc), - .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, -}; - -static struct usb_descriptor_header *ss_printer_function[] = { - (struct usb_descriptor_header *) &intf_desc, - (struct usb_descriptor_header *) &ss_ep_in_desc, - (struct usb_descriptor_header *) &ss_ep_in_comp_desc, - (struct usb_descriptor_header *) &ss_ep_out_desc, - (struct usb_descriptor_header *) &ss_ep_out_comp_desc, - NULL -}; - static struct usb_otg_descriptor otg_descriptor = { .bLength = sizeof otg_descriptor, .bDescriptorType = USB_DT_OTG, @@ -256,29 +93,13 @@ static const struct usb_descriptor_header *otg_desc[] = { NULL, }; -/* maxpacket and other transfer characteristics vary by speed. */ -static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget, - struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs, - struct usb_endpoint_descriptor *ss) -{ - switch (gadget->speed) { - case USB_SPEED_SUPER: - return ss; - case USB_SPEED_HIGH: - return hs; - default: - return fs; - } -} - /*-------------------------------------------------------------------------*/ /* descriptors that are built on-demand */ static char product_desc [40] = DRIVER_DESC; static char serial_num [40] = "1"; -static char pnp_string [1024] = +static char pnp_string[PNP_STRING_LEN] = "XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;"; /* static strings, in UTF-8 */ @@ -299,921 +120,19 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -/*-------------------------------------------------------------------------*/ - -static struct usb_request * -printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, gfp_flags); - - if (req != NULL) { - req->length = len; - req->buf = kmalloc(len, gfp_flags); - if (req->buf == NULL) { - usb_ep_free_request(ep, req); - return NULL; - } - } - - return req; -} - -static void -printer_req_free(struct usb_ep *ep, struct usb_request *req) -{ - if (ep != NULL && req != NULL) { - kfree(req->buf); - usb_ep_free_request(ep, req); - } -} - -/*-------------------------------------------------------------------------*/ - -static void rx_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct printer_dev *dev = ep->driver_data; - int status = req->status; - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - - list_del_init(&req->list); /* Remode from Active List */ - - switch (status) { - - /* normal completion */ - case 0: - if (req->actual > 0) { - list_add_tail(&req->list, &dev->rx_buffers); - DBG(dev, "G_Printer : rx length %d\n", req->actual); - } else { - list_add(&req->list, &dev->rx_reqs); - } - break; - - /* software-driven interface shutdown */ - case -ECONNRESET: /* unlink */ - case -ESHUTDOWN: /* disconnect etc */ - VDBG(dev, "rx shutdown, code %d\n", status); - list_add(&req->list, &dev->rx_reqs); - break; - - /* for hardware automagic (such as pxa) */ - case -ECONNABORTED: /* endpoint reset */ - DBG(dev, "rx %s reset\n", ep->name); - list_add(&req->list, &dev->rx_reqs); - break; - - /* data overrun */ - case -EOVERFLOW: - /* FALLTHROUGH */ - - default: - DBG(dev, "rx status %d\n", status); - list_add(&req->list, &dev->rx_reqs); - break; - } - - wake_up_interruptible(&dev->rx_wait); - spin_unlock_irqrestore(&dev->lock, flags); -} - -static void tx_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct printer_dev *dev = ep->driver_data; - - switch (req->status) { - default: - VDBG(dev, "tx err %d\n", req->status); - /* FALLTHROUGH */ - case -ECONNRESET: /* unlink */ - case -ESHUTDOWN: /* disconnect etc */ - break; - case 0: - break; - } - - spin_lock(&dev->lock); - /* Take the request struct off the active list and put it on the - * free list. - */ - list_del_init(&req->list); - list_add(&req->list, &dev->tx_reqs); - wake_up_interruptible(&dev->tx_wait); - if (likely(list_empty(&dev->tx_reqs_active))) - wake_up_interruptible(&dev->tx_flush_wait); - - spin_unlock(&dev->lock); -} - -/*-------------------------------------------------------------------------*/ - -static int -printer_open(struct inode *inode, struct file *fd) -{ - struct printer_dev *dev; - unsigned long flags; - int ret = -EBUSY; - - mutex_lock(&printer_mutex); - dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev); - - spin_lock_irqsave(&dev->lock, flags); - - if (!dev->printer_cdev_open) { - dev->printer_cdev_open = 1; - fd->private_data = dev; - ret = 0; - /* Change the printer status to show that it's on-line. */ - dev->printer_status |= PRINTER_SELECTED; - } - - spin_unlock_irqrestore(&dev->lock, flags); - - DBG(dev, "printer_open returned %x\n", ret); - mutex_unlock(&printer_mutex); - return ret; -} - -static int -printer_close(struct inode *inode, struct file *fd) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - dev->printer_cdev_open = 0; - fd->private_data = NULL; - /* Change printer status to show that the printer is off-line. */ - dev->printer_status &= ~PRINTER_SELECTED; - spin_unlock_irqrestore(&dev->lock, flags); - - DBG(dev, "printer_close\n"); - - return 0; -} - -/* This function must be called with interrupts turned off. */ -static void -setup_rx_reqs(struct printer_dev *dev) -{ - struct usb_request *req; - - while (likely(!list_empty(&dev->rx_reqs))) { - int error; - - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del_init(&req->list); - - /* The USB Host sends us whatever amount of data it wants to - * so we always set the length field to the full USB_BUFSIZE. - * If the amount of data is more than the read() caller asked - * for it will be stored in the request buffer until it is - * asked for by read(). - */ - req->length = USB_BUFSIZE; - req->complete = rx_complete; - - /* here, we unlock, and only unlock, to avoid deadlock. */ - spin_unlock(&dev->lock); - error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC); - spin_lock(&dev->lock); - if (error) { - DBG(dev, "rx submit --> %d\n", error); - list_add(&req->list, &dev->rx_reqs); - break; - } - /* if the req is empty, then add it into dev->rx_reqs_active. */ - else if (list_empty(&req->list)) { - list_add(&req->list, &dev->rx_reqs_active); - } - } -} - -static ssize_t -printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - size_t size; - size_t bytes_copied; - struct usb_request *req; - /* This is a pointer to the current USB rx request. */ - struct usb_request *current_rx_req; - /* This is the number of bytes in the current rx buffer. */ - size_t current_rx_bytes; - /* This is a pointer to the current rx buffer. */ - u8 *current_rx_buf; - - if (len == 0) - return -EINVAL; - - DBG(dev, "printer_read trying to read %d bytes\n", (int)len); - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - - /* We will use this flag later to check if a printer reset happened - * after we turn interrupts back on. - */ - dev->reset_printer = 0; - - setup_rx_reqs(dev); - - bytes_copied = 0; - current_rx_req = dev->current_rx_req; - current_rx_bytes = dev->current_rx_bytes; - current_rx_buf = dev->current_rx_buf; - dev->current_rx_req = NULL; - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - - /* Check if there is any data in the read buffers. Please note that - * current_rx_bytes is the number of bytes in the current rx buffer. - * If it is zero then check if there are any other rx_buffers that - * are on the completed list. We are only out of data if all rx - * buffers are empty. - */ - if ((current_rx_bytes == 0) && - (likely(list_empty(&dev->rx_buffers)))) { - /* Turn interrupts back on before sleeping. */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* - * If no data is available check if this is a NON-Blocking - * call or not. - */ - if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* Sleep until data is available */ - wait_event_interruptible(dev->rx_wait, - (likely(!list_empty(&dev->rx_buffers)))); - spin_lock_irqsave(&dev->lock, flags); - } - - /* We have data to return then copy it to the caller's buffer.*/ - while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers))) - && len) { - if (current_rx_bytes == 0) { - req = container_of(dev->rx_buffers.next, - struct usb_request, list); - list_del_init(&req->list); - - if (req->actual && req->buf) { - current_rx_req = req; - current_rx_bytes = req->actual; - current_rx_buf = req->buf; - } else { - list_add(&req->list, &dev->rx_reqs); - continue; - } - } - - /* Don't leave irqs off while doing memory copies */ - spin_unlock_irqrestore(&dev->lock, flags); - - if (len > current_rx_bytes) - size = current_rx_bytes; - else - size = len; - - size -= copy_to_user(buf, current_rx_buf, size); - bytes_copied += size; - len -= size; - buf += size; - - spin_lock_irqsave(&dev->lock, flags); - - /* We've disconnected or reset so return. */ - if (dev->reset_printer) { - list_add(¤t_rx_req->list, &dev->rx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* If we not returning all the data left in this RX request - * buffer then adjust the amount of data left in the buffer. - * Othewise if we are done with this RX request buffer then - * requeue it to get any incoming data from the USB host. - */ - if (size < current_rx_bytes) { - current_rx_bytes -= size; - current_rx_buf += size; - } else { - list_add(¤t_rx_req->list, &dev->rx_reqs); - current_rx_bytes = 0; - current_rx_buf = NULL; - current_rx_req = NULL; - } - } - - dev->current_rx_req = current_rx_req; - dev->current_rx_bytes = current_rx_bytes; - dev->current_rx_buf = current_rx_buf; - - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied); - - if (bytes_copied) - return bytes_copied; - else - return -EAGAIN; -} - -static ssize_t -printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - size_t size; /* Amount of data in a TX request. */ - size_t bytes_copied = 0; - struct usb_request *req; - - DBG(dev, "printer_write trying to send %d bytes\n", (int)len); - - if (len == 0) - return -EINVAL; - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - - /* Check if a printer reset happens while we have interrupts on */ - dev->reset_printer = 0; - - /* Check if there is any available write buffers */ - if (likely(list_empty(&dev->tx_reqs))) { - /* Turn interrupts back on before sleeping. */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* - * If write buffers are available check if this is - * a NON-Blocking call or not. - */ - if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) { - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - /* Sleep until a write buffer is available */ - wait_event_interruptible(dev->tx_wait, - (likely(!list_empty(&dev->tx_reqs)))); - spin_lock_irqsave(&dev->lock, flags); - } - - while (likely(!list_empty(&dev->tx_reqs)) && len) { - - if (len > USB_BUFSIZE) - size = USB_BUFSIZE; - else - size = len; - - req = container_of(dev->tx_reqs.next, struct usb_request, - list); - list_del_init(&req->list); - - req->complete = tx_complete; - req->length = size; - - /* Check if we need to send a zero length packet. */ - if (len > size) - /* They will be more TX requests so no yet. */ - req->zero = 0; - else - /* If the data amount is not a multple of the - * maxpacket size then send a zero length packet. - */ - req->zero = ((len % dev->in_ep->maxpacket) == 0); - - /* Don't leave irqs off while doing memory copies */ - spin_unlock_irqrestore(&dev->lock, flags); - - if (copy_from_user(req->buf, buf, size)) { - list_add(&req->list, &dev->tx_reqs); - mutex_unlock(&dev->lock_printer_io); - return bytes_copied; - } - - bytes_copied += size; - len -= size; - buf += size; - - spin_lock_irqsave(&dev->lock, flags); - - /* We've disconnected or reset so free the req and buffer */ - if (dev->reset_printer) { - list_add(&req->list, &dev->tx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) { - list_add(&req->list, &dev->tx_reqs); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - return -EAGAIN; - } - - list_add(&req->list, &dev->tx_reqs_active); - - } - - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied); - - if (bytes_copied) { - return bytes_copied; - } else { - return -EAGAIN; - } -} - -static int -printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync) -{ - struct printer_dev *dev = fd->private_data; - struct inode *inode = file_inode(fd); - unsigned long flags; - int tx_list_empty; - - mutex_lock(&inode->i_mutex); - spin_lock_irqsave(&dev->lock, flags); - tx_list_empty = (likely(list_empty(&dev->tx_reqs))); - spin_unlock_irqrestore(&dev->lock, flags); - - if (!tx_list_empty) { - /* Sleep until all data has been sent */ - wait_event_interruptible(dev->tx_flush_wait, - (likely(list_empty(&dev->tx_reqs_active)))); - } - mutex_unlock(&inode->i_mutex); - - return 0; -} - -static unsigned int -printer_poll(struct file *fd, poll_table *wait) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - int status = 0; - - mutex_lock(&dev->lock_printer_io); - spin_lock_irqsave(&dev->lock, flags); - setup_rx_reqs(dev); - spin_unlock_irqrestore(&dev->lock, flags); - mutex_unlock(&dev->lock_printer_io); - - poll_wait(fd, &dev->rx_wait, wait); - poll_wait(fd, &dev->tx_wait, wait); - - spin_lock_irqsave(&dev->lock, flags); - if (likely(!list_empty(&dev->tx_reqs))) - status |= POLLOUT | POLLWRNORM; - - if (likely(dev->current_rx_bytes) || - likely(!list_empty(&dev->rx_buffers))) - status |= POLLIN | POLLRDNORM; - - spin_unlock_irqrestore(&dev->lock, flags); - - return status; -} - -static long -printer_ioctl(struct file *fd, unsigned int code, unsigned long arg) -{ - struct printer_dev *dev = fd->private_data; - unsigned long flags; - int status = 0; - - DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg); - - /* handle ioctls */ - - spin_lock_irqsave(&dev->lock, flags); - - switch (code) { - case GADGET_GET_PRINTER_STATUS: - status = (int)dev->printer_status; - break; - case GADGET_SET_PRINTER_STATUS: - dev->printer_status = (u8)arg; - break; - default: - /* could not handle ioctl */ - DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n", - code); - status = -ENOTTY; - } - - spin_unlock_irqrestore(&dev->lock, flags); - - return status; -} - -/* used after endpoint configuration */ -static const struct file_operations printer_io_operations = { - .owner = THIS_MODULE, - .open = printer_open, - .read = printer_read, - .write = printer_write, - .fsync = printer_fsync, - .poll = printer_poll, - .unlocked_ioctl = printer_ioctl, - .release = printer_close, - .llseek = noop_llseek, -}; - -/*-------------------------------------------------------------------------*/ - -static int -set_printer_interface(struct printer_dev *dev) -{ - int result = 0; - - dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc, - &ss_ep_in_desc); - dev->in_ep->driver_data = dev; - - dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc, - &hs_ep_out_desc, &ss_ep_out_desc); - dev->out_ep->driver_data = dev; - - result = usb_ep_enable(dev->in_ep); - if (result != 0) { - DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); - goto done; - } - - result = usb_ep_enable(dev->out_ep); - if (result != 0) { - DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result); - goto done; - } - -done: - /* on error, disable any endpoints */ - if (result != 0) { - (void) usb_ep_disable(dev->in_ep); - (void) usb_ep_disable(dev->out_ep); - dev->in_ep->desc = NULL; - dev->out_ep->desc = NULL; - } - - /* caller is responsible for cleanup on error */ - return result; -} - -static void printer_reset_interface(struct printer_dev *dev) -{ - if (dev->interface < 0) - return; - - DBG(dev, "%s\n", __func__); - - if (dev->in_ep->desc) - usb_ep_disable(dev->in_ep); - - if (dev->out_ep->desc) - usb_ep_disable(dev->out_ep); - - dev->in_ep->desc = NULL; - dev->out_ep->desc = NULL; - dev->interface = -1; -} - -/* Change our operational Interface. */ -static int set_interface(struct printer_dev *dev, unsigned number) -{ - int result = 0; - - /* Free the current interface */ - printer_reset_interface(dev); - - result = set_printer_interface(dev); - if (result) - printer_reset_interface(dev); - else - dev->interface = number; - - if (!result) - INFO(dev, "Using interface %x\n", number); - - return result; -} - -static void printer_soft_reset(struct printer_dev *dev) -{ - struct usb_request *req; - - INFO(dev, "Received Printer Reset Request\n"); - - if (usb_ep_disable(dev->in_ep)) - DBG(dev, "Failed to disable USB in_ep\n"); - if (usb_ep_disable(dev->out_ep)) - DBG(dev, "Failed to disable USB out_ep\n"); - - if (dev->current_rx_req != NULL) { - list_add(&dev->current_rx_req->list, &dev->rx_reqs); - dev->current_rx_req = NULL; - } - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - dev->reset_printer = 1; - - while (likely(!(list_empty(&dev->rx_buffers)))) { - req = container_of(dev->rx_buffers.next, struct usb_request, - list); - list_del_init(&req->list); - list_add(&req->list, &dev->rx_reqs); - } - - while (likely(!(list_empty(&dev->rx_reqs_active)))) { - req = container_of(dev->rx_buffers.next, struct usb_request, - list); - list_del_init(&req->list); - list_add(&req->list, &dev->rx_reqs); - } - - while (likely(!(list_empty(&dev->tx_reqs_active)))) { - req = container_of(dev->tx_reqs_active.next, - struct usb_request, list); - list_del_init(&req->list); - list_add(&req->list, &dev->tx_reqs); - } - - if (usb_ep_enable(dev->in_ep)) - DBG(dev, "Failed to enable USB in_ep\n"); - if (usb_ep_enable(dev->out_ep)) - DBG(dev, "Failed to enable USB out_ep\n"); - - wake_up_interruptible(&dev->rx_wait); - wake_up_interruptible(&dev->tx_wait); - wake_up_interruptible(&dev->tx_flush_wait); -} - -/*-------------------------------------------------------------------------*/ - -/* - * The setup() callback implements all the ep0 functionality that's not - * handled lower down. - */ -static int printer_func_setup(struct usb_function *f, - const struct usb_ctrlrequest *ctrl) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - struct usb_composite_dev *cdev = f->config->cdev; - struct usb_request *req = cdev->req; - int value = -EOPNOTSUPP; - u16 wIndex = le16_to_cpu(ctrl->wIndex); - u16 wValue = le16_to_cpu(ctrl->wValue); - u16 wLength = le16_to_cpu(ctrl->wLength); - - DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength); - - switch (ctrl->bRequestType&USB_TYPE_MASK) { - case USB_TYPE_CLASS: - switch (ctrl->bRequest) { - case 0: /* Get the IEEE-1284 PNP String */ - /* Only one printer interface is supported. */ - if ((wIndex>>8) != dev->interface) - break; - - value = (pnp_string[0]<<8)|pnp_string[1]; - memcpy(req->buf, pnp_string, value); - DBG(dev, "1284 PNP String: %x %s\n", value, - &pnp_string[2]); - break; - - case 1: /* Get Port Status */ - /* Only one printer interface is supported. */ - if (wIndex != dev->interface) - break; - - *(u8 *)req->buf = dev->printer_status; - value = min(wLength, (u16) 1); - break; - - case 2: /* Soft Reset */ - /* Only one printer interface is supported. */ - if (wIndex != dev->interface) - break; - - printer_soft_reset(dev); - - value = 0; - break; - - default: - goto unknown; - } - break; - - default: -unknown: - VDBG(dev, - "unknown ctrl req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - wValue, wIndex, wLength); - break; - } - /* host either stalls (value < 0) or reports success */ - return value; -} - -static int __init printer_func_bind(struct usb_configuration *c, - struct usb_function *f) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - struct usb_composite_dev *cdev = c->cdev; - struct usb_ep *in_ep; - struct usb_ep *out_ep = NULL; - int id; - int ret; - - id = usb_interface_id(c, f); - if (id < 0) - return id; - intf_desc.bInterfaceNumber = id; - - /* all we really need is bulk IN/OUT */ - in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc); - if (!in_ep) { -autoconf_fail: - dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n", - cdev->gadget->name); - return -ENODEV; - } - in_ep->driver_data = in_ep; /* claim */ - - out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); - if (!out_ep) - goto autoconf_fail; - out_ep->driver_data = out_ep; /* claim */ - - /* assumes that all endpoints are dual-speed */ - hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; - hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; - ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; - ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress; - - ret = usb_assign_descriptors(f, fs_printer_function, - hs_printer_function, ss_printer_function); - if (ret) - return ret; - - dev->in_ep = in_ep; - dev->out_ep = out_ep; - return 0; -} - -static void printer_func_unbind(struct usb_configuration *c, - struct usb_function *f) -{ - usb_free_all_descriptors(f); -} - -static int printer_func_set_alt(struct usb_function *f, - unsigned intf, unsigned alt) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - int ret = -ENOTSUPP; - - if (!alt) - ret = set_interface(dev, intf); - - return ret; -} - -static void printer_func_disable(struct usb_function *f) -{ - struct printer_dev *dev = container_of(f, struct printer_dev, function); - unsigned long flags; - - DBG(dev, "%s\n", __func__); - - spin_lock_irqsave(&dev->lock, flags); - printer_reset_interface(dev); - spin_unlock_irqrestore(&dev->lock, flags); -} - -static void printer_cfg_unbind(struct usb_configuration *c) -{ - struct printer_dev *dev; - struct usb_request *req; - - dev = &usb_printer_gadget; - - DBG(dev, "%s\n", __func__); - - /* Remove sysfs files */ - device_destroy(usb_gadget_class, g_printer_devno); - - /* Remove Character Device */ - cdev_del(&dev->printer_cdev); - - /* we must already have been disconnected ... no i/o may be active */ - WARN_ON(!list_empty(&dev->tx_reqs_active)); - WARN_ON(!list_empty(&dev->rx_reqs_active)); - - /* Free all memory for this driver. */ - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, struct usb_request, - list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - - if (dev->current_rx_req != NULL) - printer_req_free(dev->out_ep, dev->current_rx_req); - - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - - while (!list_empty(&dev->rx_buffers)) { - req = container_of(dev->rx_buffers.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } -} - static struct usb_configuration printer_cfg_driver = { .label = "printer", - .unbind = printer_cfg_unbind, .bConfigurationValue = 1, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, }; -static int __init printer_bind_config(struct usb_configuration *c) +static int __init printer_do_config(struct usb_configuration *c) { struct usb_gadget *gadget = c->cdev->gadget; - struct printer_dev *dev; - int status = -ENOMEM; - size_t len; - u32 i; - struct usb_request *req; + int status = 0; usb_ep_autoconfig_reset(gadget); - dev = &usb_printer_gadget; - - dev->function.name = shortname; - dev->function.bind = printer_func_bind; - dev->function.setup = printer_func_setup; - dev->function.unbind = printer_func_unbind; - dev->function.set_alt = printer_func_set_alt; - dev->function.disable = printer_func_disable; - - status = usb_add_function(c, &dev->function); - if (status) - return status; - - /* Setup the sysfs files for the printer gadget. */ - dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, - NULL, "g_printer"); - if (IS_ERR(dev->pdev)) { - ERROR(dev, "Failed to create device: g_printer\n"); - status = PTR_ERR(dev->pdev); - goto fail; - } - - /* - * Register a character device as an interface to a user mode - * program that handles the printer specific functionality. - */ - cdev_init(&dev->printer_cdev, &printer_io_operations); - dev->printer_cdev.owner = THIS_MODULE; - status = cdev_add(&dev->printer_cdev, g_printer_devno, 1); - if (status) { - ERROR(dev, "Failed to open char device\n"); - goto fail; - } - - if (iPNPstring) - strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2); - - len = strlen(pnp_string); - pnp_string[0] = (len >> 8) & 0xFF; - pnp_string[1] = len & 0xFF; - usb_gadget_set_selfpowered(gadget); if (gadget_is_otg(gadget)) { @@ -1222,86 +141,64 @@ static int __init printer_bind_config(struct usb_configuration *c) printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - spin_lock_init(&dev->lock); - mutex_init(&dev->lock_printer_io); - INIT_LIST_HEAD(&dev->tx_reqs); - INIT_LIST_HEAD(&dev->tx_reqs_active); - INIT_LIST_HEAD(&dev->rx_reqs); - INIT_LIST_HEAD(&dev->rx_reqs_active); - INIT_LIST_HEAD(&dev->rx_buffers); - init_waitqueue_head(&dev->rx_wait); - init_waitqueue_head(&dev->tx_wait); - init_waitqueue_head(&dev->tx_flush_wait); - - dev->interface = -1; - dev->printer_cdev_open = 0; - dev->printer_status = PRINTER_NOT_ERROR; - dev->current_rx_req = NULL; - dev->current_rx_bytes = 0; - dev->current_rx_buf = NULL; - - for (i = 0; i < QLEN; i++) { - req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) { - while (!list_empty(&dev->tx_reqs)) { - req = container_of(dev->tx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->in_ep, req); - } - return -ENOMEM; - } - list_add(&req->list, &dev->tx_reqs); - } - - for (i = 0; i < QLEN; i++) { - req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL); - if (!req) { - while (!list_empty(&dev->rx_reqs)) { - req = container_of(dev->rx_reqs.next, - struct usb_request, list); - list_del(&req->list); - printer_req_free(dev->out_ep, req); - } - return -ENOMEM; - } - list_add(&req->list, &dev->rx_reqs); - } - - /* finish hookup to lower layer ... */ - dev->gadget = gadget; + f_printer = usb_get_function(fi_printer); + if (IS_ERR(f_printer)) + return PTR_ERR(f_printer); - INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); - return 0; + status = usb_add_function(c, f_printer); + if (status < 0) + usb_put_function(f_printer); -fail: - printer_cfg_unbind(c); return status; } -static int printer_unbind(struct usb_composite_dev *cdev) -{ - return 0; -} - static int __init printer_bind(struct usb_composite_dev *cdev) { - int ret; + struct f_printer_opts *opts; + int ret, len; + + fi_printer = usb_get_function_instance("printer"); + if (IS_ERR(fi_printer)) + return PTR_ERR(fi_printer); + + if (iPNPstring) + strlcpy(&pnp_string[2], iPNPstring, PNP_STRING_LEN - 2); + + len = strlen(pnp_string); + pnp_string[0] = (len >> 8) & 0xFF; + pnp_string[1] = len & 0xFF; + + opts = container_of(fi_printer, struct f_printer_opts, func_inst); + opts->minor = 0; + memcpy(opts->pnp_string, pnp_string, PNP_STRING_LEN); + opts->q_len = QLEN; ret = usb_string_ids_tab(cdev, strings); - if (ret < 0) + if (ret < 0) { + usb_put_function_instance(fi_printer); return ret; + } device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id; device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id; device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id; - ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config); - if (ret) + ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config); + if (ret) { + usb_put_function_instance(fi_printer); return ret; + } usb_composite_overwrite_options(cdev, &coverwrite); return ret; } +static int __exit printer_unbind(struct usb_composite_dev *cdev) +{ + usb_put_function(f_printer); + usb_put_function_instance(fi_printer); + + return 0; +} + static __refdata struct usb_composite_driver printer_driver = { .name = shortname, .dev = &device_desc, @@ -1311,47 +208,7 @@ static __refdata struct usb_composite_driver printer_driver = { .unbind = printer_unbind, }; -static int __init -init(void) -{ - int status; - - usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget"); - if (IS_ERR(usb_gadget_class)) { - status = PTR_ERR(usb_gadget_class); - pr_err("unable to create usb_gadget class %d\n", status); - return status; - } - - status = alloc_chrdev_region(&g_printer_devno, 0, 1, - "USB printer gadget"); - if (status) { - pr_err("alloc_chrdev_region %d\n", status); - class_destroy(usb_gadget_class); - return status; - } - - status = usb_composite_probe(&printer_driver); - if (status) { - class_destroy(usb_gadget_class); - unregister_chrdev_region(g_printer_devno, 1); - pr_err("usb_gadget_probe_driver %x\n", status); - } - - return status; -} -module_init(init); - -static void __exit -cleanup(void) -{ - mutex_lock(&usb_printer_gadget.lock_printer_io); - usb_composite_unregister(&printer_driver); - unregister_chrdev_region(g_printer_devno, 1); - class_destroy(usb_gadget_class); - mutex_unlock(&usb_printer_gadget.lock_printer_io); -} -module_exit(cleanup); +module_usb_composite_driver(printer_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Craig Nadler"); diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index d79cb35dbf8a..4c01953a0869 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file) spin_lock_irq(&udc->lock); for (i = 0; i < inode->i_size / 4; i++) - data[i] = __raw_readl(udc->regs + i * 4); + data[i] = usba_io_readl(udc->regs + i * 4); spin_unlock_irq(&udc->lock); file->private_data = data; @@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, if (crq->wLength != cpu_to_le16(sizeof(status))) goto stall; ep->state = DATA_STAGE_IN; - __raw_writew(status, ep->fifo); + usba_io_writew(status, ep->fifo); usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY); break; } @@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) return IRQ_HANDLED; } -static irqreturn_t usba_vbus_irq(int irq, void *devid) +static int start_clock(struct usba_udc *udc) +{ + int ret; + + if (udc->clocked) + return 0; + + ret = clk_prepare_enable(udc->pclk); + if (ret) + return ret; + ret = clk_prepare_enable(udc->hclk); + if (ret) { + clk_disable_unprepare(udc->pclk); + return ret; + } + + udc->clocked = true; + return 0; +} + +static void stop_clock(struct usba_udc *udc) +{ + if (!udc->clocked) + return; + + clk_disable_unprepare(udc->hclk); + clk_disable_unprepare(udc->pclk); + + udc->clocked = false; +} + +static int usba_start(struct usba_udc *udc) +{ + unsigned long flags; + int ret; + + ret = start_clock(udc); + if (ret) + return ret; + + spin_lock_irqsave(&udc->lock, flags); + toggle_bias(udc, 1); + usba_writel(udc, CTRL, USBA_ENABLE_MASK); + usba_int_enb_set(udc, USBA_END_OF_RESET); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static void usba_stop(struct usba_udc *udc) +{ + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + udc->gadget.speed = USB_SPEED_UNKNOWN; + reset_all_endpoints(udc); + + /* This will also disable the DP pullup */ + toggle_bias(udc, 0); + usba_writel(udc, CTRL, USBA_DISABLE_MASK); + spin_unlock_irqrestore(&udc->lock, flags); + + stop_clock(udc); +} + +static irqreturn_t usba_vbus_irq_thread(int irq, void *devid) { struct usba_udc *udc = devid; int vbus; @@ -1747,35 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid) /* debounce */ udelay(10); - spin_lock(&udc->lock); - - /* May happen if Vbus pin toggles during probe() */ - if (!udc->driver) - goto out; + mutex_lock(&udc->vbus_mutex); vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { - toggle_bias(udc, 1); - usba_writel(udc, CTRL, USBA_ENABLE_MASK); - usba_int_enb_set(udc, USBA_END_OF_RESET); + usba_start(udc); } else { - udc->gadget.speed = USB_SPEED_UNKNOWN; - reset_all_endpoints(udc); - toggle_bias(udc, 0); - usba_writel(udc, CTRL, USBA_DISABLE_MASK); - if (udc->driver->disconnect) { - spin_unlock(&udc->lock); + usba_stop(udc); + + if (udc->driver->disconnect) udc->driver->disconnect(&udc->gadget); - spin_lock(&udc->lock); - } } udc->vbus_prev = vbus; } -out: - spin_unlock(&udc->lock); - + mutex_unlock(&udc->vbus_mutex); return IRQ_HANDLED; } @@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget, unsigned long flags; spin_lock_irqsave(&udc->lock, flags); - udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->driver = driver; spin_unlock_irqrestore(&udc->lock, flags); - ret = clk_prepare_enable(udc->pclk); - if (ret) - return ret; - ret = clk_prepare_enable(udc->hclk); - if (ret) { - clk_disable_unprepare(udc->pclk); - return ret; - } + mutex_lock(&udc->vbus_mutex); - udc->vbus_prev = 0; if (gpio_is_valid(udc->vbus_pin)) enable_irq(gpio_to_irq(udc->vbus_pin)); /* If Vbus is present, enable the controller and wait for reset */ - spin_lock_irqsave(&udc->lock, flags); - if (vbus_is_present(udc) && udc->vbus_prev == 0) { - toggle_bias(udc, 1); - usba_writel(udc, CTRL, USBA_ENABLE_MASK); - usba_int_enb_set(udc, USBA_END_OF_RESET); + udc->vbus_prev = vbus_is_present(udc); + if (udc->vbus_prev) { + ret = usba_start(udc); + if (ret) + goto err; } - spin_unlock_irqrestore(&udc->lock, flags); + mutex_unlock(&udc->vbus_mutex); return 0; + +err: + if (gpio_is_valid(udc->vbus_pin)) + disable_irq(gpio_to_irq(udc->vbus_pin)); + + mutex_unlock(&udc->vbus_mutex); + + spin_lock_irqsave(&udc->lock, flags); + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + udc->driver = NULL; + spin_unlock_irqrestore(&udc->lock, flags); + return ret; } static int atmel_usba_stop(struct usb_gadget *gadget) { struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); - unsigned long flags; if (gpio_is_valid(udc->vbus_pin)) disable_irq(gpio_to_irq(udc->vbus_pin)); - spin_lock_irqsave(&udc->lock, flags); - udc->gadget.speed = USB_SPEED_UNKNOWN; - reset_all_endpoints(udc); - spin_unlock_irqrestore(&udc->lock, flags); - - /* This will also disable the DP pullup */ - toggle_bias(udc, 0); - usba_writel(udc, CTRL, USBA_DISABLE_MASK); - - clk_disable_unprepare(udc->hclk); - clk_disable_unprepare(udc->pclk); + usba_stop(udc); udc->driver = NULL; @@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev) return PTR_ERR(hclk); spin_lock_init(&udc->lock); + mutex_init(&udc->vbus_mutex); udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk; @@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev) if (gpio_is_valid(udc->vbus_pin)) { if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { - ret = devm_request_irq(&pdev->dev, - gpio_to_irq(udc->vbus_pin), - usba_vbus_irq, 0, + irq_set_status_flags(gpio_to_irq(udc->vbus_pin), + IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(&pdev->dev, + gpio_to_irq(udc->vbus_pin), NULL, + usba_vbus_irq_thread, IRQF_ONESHOT, "atmel_usba_udc", udc); if (ret) { udc->vbus_pin = -ENODEV; dev_warn(&udc->pdev->dev, "failed to request vbus irq; " "assuming always on\n"); - } else { - disable_irq(gpio_to_irq(udc->vbus_pin)); } } else { /* gpio_request fail so use -EINVAL for gpio_is_valid */ @@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev) ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (ret) return ret; + device_init_wakeup(&pdev->dev, 1); usba_init_debugfs(udc); for (i = 1; i < udc->num_ep; i++) @@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev) udc = platform_get_drvdata(pdev); + device_init_wakeup(&pdev->dev, 0); usb_del_gadget_udc(&udc->gadget); for (i = 1; i < udc->num_ep; i++) @@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int usba_udc_suspend(struct device *dev) +{ + struct usba_udc *udc = dev_get_drvdata(dev); + + /* Not started */ + if (!udc->driver) + return 0; + + mutex_lock(&udc->vbus_mutex); + + if (!device_may_wakeup(dev)) { + usba_stop(udc); + goto out; + } + + /* + * Device may wake up. We stay clocked if we failed + * to request vbus irq, assuming always on. + */ + if (gpio_is_valid(udc->vbus_pin)) { + usba_stop(udc); + enable_irq_wake(gpio_to_irq(udc->vbus_pin)); + } + +out: + mutex_unlock(&udc->vbus_mutex); + return 0; +} + +static int usba_udc_resume(struct device *dev) +{ + struct usba_udc *udc = dev_get_drvdata(dev); + + /* Not started */ + if (!udc->driver) + return 0; + + if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin)) + disable_irq_wake(gpio_to_irq(udc->vbus_pin)); + + /* If Vbus is present, enable the controller and wait for reset */ + mutex_lock(&udc->vbus_mutex); + udc->vbus_prev = vbus_is_present(udc); + if (udc->vbus_prev) + usba_start(udc); + mutex_unlock(&udc->vbus_mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); + static struct platform_driver udc_driver = { .remove = __exit_p(usba_udc_remove), .driver = { .name = "atmel_usba_udc", + .pm = &usba_udc_pm_ops, .of_match_table = of_match_ptr(atmel_udc_dt_ids), }, }; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 497cd18836f3..ea448a344767 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -191,18 +191,28 @@ | USBA_BF(name, value)) /* Register access macros */ +#ifdef CONFIG_AVR32 +#define usba_io_readl __raw_readl +#define usba_io_writel __raw_writel +#define usba_io_writew __raw_writew +#else +#define usba_io_readl readl_relaxed +#define usba_io_writel writel_relaxed +#define usba_io_writew writew_relaxed +#endif + #define usba_readl(udc, reg) \ - __raw_readl((udc)->regs + USBA_##reg) + usba_io_readl((udc)->regs + USBA_##reg) #define usba_writel(udc, reg, value) \ - __raw_writel((value), (udc)->regs + USBA_##reg) + usba_io_writel((value), (udc)->regs + USBA_##reg) #define usba_ep_readl(ep, reg) \ - __raw_readl((ep)->ep_regs + USBA_EPT_##reg) + usba_io_readl((ep)->ep_regs + USBA_EPT_##reg) #define usba_ep_writel(ep, reg, value) \ - __raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg) + usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg) #define usba_dma_readl(ep, reg) \ - __raw_readl((ep)->dma_regs + USBA_DMA_##reg) + usba_io_readl((ep)->dma_regs + USBA_DMA_##reg) #define usba_dma_writel(ep, reg, value) \ - __raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg) + usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg) /* Calculate base address for a given endpoint or DMA controller */ #define USBA_EPT_BASE(x) (0x100 + (x) * 0x20) @@ -313,6 +323,9 @@ struct usba_udc { /* Protect hw registers from concurrent modifications */ spinlock_t lock; + /* Mutex to prevent concurrent start or stop */ + struct mutex vbus_mutex; + void __iomem *regs; void __iomem *fifo; @@ -328,6 +341,7 @@ struct usba_udc { struct clk *hclk; struct usba_ep *usba_ep; bool bias_pulse_needed; + bool clocked; u16 devstatus; diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 8dda48445f6f..7592db7824c6 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2631,7 +2631,7 @@ static int __init init(void) return -EINVAL; if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) { - pr_err("Number of emulated UDC must be in range of 1…%d\n", + pr_err("Number of emulated UDC must be in range of 1...%d\n", MAX_NUM_UDC); return -EINVAL; } diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index 5b9176e7202a..9e8d842e8c08 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1024,35 +1024,79 @@ static const char proc_node_name [] = "driver/udc"; static void dump_intmask(struct seq_file *m, const char *label, u32 mask) { /* int_status is the same format ... */ - seq_printf(m, - "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n", - label, mask, - (mask & INT_PWRDETECT) ? " power" : "", - (mask & INT_SYSERROR) ? " sys" : "", - (mask & INT_MSTRDEND) ? " in-dma" : "", - (mask & INT_MSTWRTMOUT) ? " wrtmo" : "", - - (mask & INT_MSTWREND) ? " out-dma" : "", - (mask & INT_MSTWRSET) ? " wrset" : "", - (mask & INT_ERR) ? " err" : "", - (mask & INT_SOF) ? " sof" : "", - - (mask & INT_EP3NAK) ? " ep3nak" : "", - (mask & INT_EP2NAK) ? " ep2nak" : "", - (mask & INT_EP1NAK) ? " ep1nak" : "", - (mask & INT_EP3DATASET) ? " ep3" : "", - - (mask & INT_EP2DATASET) ? " ep2" : "", - (mask & INT_EP1DATASET) ? " ep1" : "", - (mask & INT_STATUSNAK) ? " ep0snak" : "", - (mask & INT_STATUS) ? " ep0status" : "", - - (mask & INT_SETUP) ? " setup" : "", - (mask & INT_ENDPOINT0) ? " ep0" : "", - (mask & INT_USBRESET) ? " reset" : "", - (mask & INT_SUSPEND) ? " suspend" : ""); + seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n", + label, mask, + (mask & INT_PWRDETECT) ? " power" : "", + (mask & INT_SYSERROR) ? " sys" : "", + (mask & INT_MSTRDEND) ? " in-dma" : "", + (mask & INT_MSTWRTMOUT) ? " wrtmo" : "", + + (mask & INT_MSTWREND) ? " out-dma" : "", + (mask & INT_MSTWRSET) ? " wrset" : "", + (mask & INT_ERR) ? " err" : "", + (mask & INT_SOF) ? " sof" : "", + + (mask & INT_EP3NAK) ? " ep3nak" : "", + (mask & INT_EP2NAK) ? " ep2nak" : "", + (mask & INT_EP1NAK) ? " ep1nak" : "", + (mask & INT_EP3DATASET) ? " ep3" : "", + + (mask & INT_EP2DATASET) ? " ep2" : "", + (mask & INT_EP1DATASET) ? " ep1" : "", + (mask & INT_STATUSNAK) ? " ep0snak" : "", + (mask & INT_STATUS) ? " ep0status" : "", + + (mask & INT_SETUP) ? " setup" : "", + (mask & INT_ENDPOINT0) ? " ep0" : "", + (mask & INT_USBRESET) ? " reset" : "", + (mask & INT_SUSPEND) ? " suspend" : ""); +} + +static const char *udc_ep_state(enum ep0state state) +{ + switch (state) { + case EP0_DISCONNECT: + return "ep0_disconnect"; + case EP0_IDLE: + return "ep0_idle"; + case EP0_IN: + return "ep0_in"; + case EP0_OUT: + return "ep0_out"; + case EP0_STATUS: + return "ep0_status"; + case EP0_STALL: + return "ep0_stall"; + case EP0_SUSPEND: + return "ep0_suspend"; + } + + return "ep0_?"; } +static const char *udc_ep_status(u32 status) +{ + switch (status & EPxSTATUS_EP_MASK) { + case EPxSTATUS_EP_READY: + return "ready"; + case EPxSTATUS_EP_DATAIN: + return "packet"; + case EPxSTATUS_EP_FULL: + return "full"; + case EPxSTATUS_EP_TX_ERR: /* host will retry */ + return "tx_err"; + case EPxSTATUS_EP_RX_ERR: + return "rx_err"; + case EPxSTATUS_EP_BUSY: /* ep0 only */ + return "busy"; + case EPxSTATUS_EP_STALL: + return "stall"; + case EPxSTATUS_EP_INVALID: /* these "can't happen" */ + return "invalid"; + } + + return "?"; +} static int udc_proc_read(struct seq_file *m, void *v) { @@ -1068,29 +1112,18 @@ static int udc_proc_read(struct seq_file *m, void *v) tmp = readl(®s->power_detect); is_usb_connected = tmp & PW_DETECT; seq_printf(m, - "%s - %s\n" - "%s version: %s %s\n" - "Gadget driver: %s\n" - "Host %s, %s\n" - "\n", - pci_name(dev->pdev), driver_desc, - driver_name, DRIVER_VERSION, dmastr(), - dev->driver ? dev->driver->driver.name : "(none)", - is_usb_connected - ? ((tmp & PW_PULLUP) ? "full speed" : "powered") - : "disconnected", - ({const char *state; - switch(dev->ep0state){ - case EP0_DISCONNECT: state = "ep0_disconnect"; break; - case EP0_IDLE: state = "ep0_idle"; break; - case EP0_IN: state = "ep0_in"; break; - case EP0_OUT: state = "ep0_out"; break; - case EP0_STATUS: state = "ep0_status"; break; - case EP0_STALL: state = "ep0_stall"; break; - case EP0_SUSPEND: state = "ep0_suspend"; break; - default: state = "ep0_?"; break; - } state; }) - ); + "%s - %s\n" + "%s version: %s %s\n" + "Gadget driver: %s\n" + "Host %s, %s\n" + "\n", + pci_name(dev->pdev), driver_desc, + driver_name, DRIVER_VERSION, dmastr(), + dev->driver ? dev->driver->driver.name : "(none)", + is_usb_connected + ? ((tmp & PW_PULLUP) ? "full speed" : "powered") + : "disconnected", + udc_ep_state(dev->ep0state)); dump_intmask(m, "int_status", readl(®s->int_status)); dump_intmask(m, "int_enable", readl(®s->int_enable)); @@ -1099,31 +1132,30 @@ static int udc_proc_read(struct seq_file *m, void *v) goto done; /* registers for (active) device and ep0 */ - if (seq_printf(m, "\nirqs %lu\ndataset %02x " - "single.bcs %02x.%02x state %x addr %u\n", - dev->irqs, readl(®s->DataSet), - readl(®s->EPxSingle), readl(®s->EPxBCS), - readl(®s->UsbState), - readl(®s->address)) < 0) + seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n", + dev->irqs, readl(®s->DataSet), + readl(®s->EPxSingle), readl(®s->EPxBCS), + readl(®s->UsbState), + readl(®s->address)); + if (seq_has_overflowed(m)) goto done; tmp = readl(®s->dma_master); - if (seq_printf(m, - "dma %03X =" EIGHTBITS "%s %s\n", tmp, - (tmp & MST_EOPB_DIS) ? " eopb-" : "", - (tmp & MST_EOPB_ENA) ? " eopb+" : "", - (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "", - (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "", - - (tmp & MST_RD_EOPB) ? " eopb" : "", - (tmp & MST_RD_RESET) ? " in_reset" : "", - (tmp & MST_WR_RESET) ? " out_reset" : "", - (tmp & MST_RD_ENA) ? " IN" : "", - - (tmp & MST_WR_ENA) ? " OUT" : "", - (tmp & MST_CONNECTION) - ? "ep1in/ep2out" - : "ep1out/ep2in") < 0) + seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n", + tmp, + (tmp & MST_EOPB_DIS) ? " eopb-" : "", + (tmp & MST_EOPB_ENA) ? " eopb+" : "", + (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "", + (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "", + + (tmp & MST_RD_EOPB) ? " eopb" : "", + (tmp & MST_RD_RESET) ? " in_reset" : "", + (tmp & MST_WR_RESET) ? " out_reset" : "", + (tmp & MST_RD_ENA) ? " IN" : "", + + (tmp & MST_WR_ENA) ? " OUT" : "", + (tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in"); + if (seq_has_overflowed(m)) goto done; /* dump endpoint queues */ @@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v) continue; tmp = readl(ep->reg_status); - if (seq_printf(m, - "%s %s max %u %s, irqs %lu, " - "status %02x (%s) " FOURBITS "\n", - ep->ep.name, - ep->is_in ? "in" : "out", - ep->ep.maxpacket, - ep->dma ? "dma" : "pio", - ep->irqs, - tmp, ({ char *s; - switch (tmp & EPxSTATUS_EP_MASK) { - case EPxSTATUS_EP_READY: - s = "ready"; break; - case EPxSTATUS_EP_DATAIN: - s = "packet"; break; - case EPxSTATUS_EP_FULL: - s = "full"; break; - case EPxSTATUS_EP_TX_ERR: // host will retry - s = "tx_err"; break; - case EPxSTATUS_EP_RX_ERR: - s = "rx_err"; break; - case EPxSTATUS_EP_BUSY: /* ep0 only */ - s = "busy"; break; - case EPxSTATUS_EP_STALL: - s = "stall"; break; - case EPxSTATUS_EP_INVALID: // these "can't happen" - s = "invalid"; break; - default: - s = "?"; break; - } s; }), - (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0", - (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "", - (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "", - (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "" - ) < 0) + seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n", + ep->ep.name, + ep->is_in ? "in" : "out", + ep->ep.maxpacket, + ep->dma ? "dma" : "pio", + ep->irqs, + tmp, udc_ep_status(tmp), + (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0", + (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "", + (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "", + (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""); + if (seq_has_overflowed(m)) goto done; if (list_empty(&ep->queue)) { - if (seq_puts(m, "\t(nothing queued)\n") < 0) + seq_puts(m, "\t(nothing queued)\n"); + if (seq_has_overflowed(m)) goto done; continue; } @@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v) } else tmp = req->req.actual; - if (seq_printf(m, - "\treq %p len %u/%u buf %p\n", - &req->req, tmp, req->req.length, - req->req.buf) < 0) + seq_printf(m, "\treq %p len %u/%u buf %p\n", + &req->req, tmp, req->req.length, + req->req.buf); + if (seq_has_overflowed(m)) goto done; } } diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 27fd41333f71..3b6a7852822d 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep, req = container_of(_req, struct lpc32xx_request, req); ep = container_of(_ep, struct lpc32xx_ep, ep); - if (!_req || !_req->complete || !_req->buf || + if (!_ep || !_req || !_req->complete || !_req->buf || !list_empty(&req->queue)) return -EINVAL; udc = ep->udc; - if (!_ep) { - dev_dbg(udc->dev, "invalid ep\n"); - return -EINVAL; - } - - - if ((!udc) || (!udc->driver) || - (udc->gadget.speed == USB_SPEED_UNKNOWN)) { - dev_dbg(udc->dev, "invalid device\n"); - return -EINVAL; - } + if (udc->gadget.speed == USB_SPEED_UNKNOWN) + return -EPIPE; if (ep->lep) { struct lpc32xx_usbd_dd_gad *dd; diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d2c0bf65e345..9871b90195ad 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -80,6 +80,13 @@ static const char *const ep_name[] = { "ep-e", "ep-f", "ep-g", "ep-h", }; +/* Endpoint names for usb3380 advance mode */ +static const char *const ep_name_adv[] = { + ep0name, + "ep1in", "ep2out", "ep3in", "ep4out", + "ep1out", "ep2in", "ep3out", "ep4in", +}; + /* mode 0 == ep-{a,b,c,d} 1K fifo each * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable @@ -138,31 +145,44 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) u32 max, tmp; unsigned long flags; static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; + int ret = 0; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || - desc->bDescriptorType != USB_DT_ENDPOINT) + desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_err("%s: failed at line=%d\n", __func__, __LINE__); return -EINVAL; + } dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + ret = -ESHUTDOWN; + goto print_err; + } /* erratum 0119 workaround ties up an endpoint number */ - if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) - return -EDOM; + if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) { + ret = -EDOM; + goto print_err; + } if (dev->quirks & PLX_SUPERSPEED) { - if ((desc->bEndpointAddress & 0x0f) >= 0x0c) - return -EDOM; + if ((desc->bEndpointAddress & 0x0f) >= 0x0c) { + ret = -EDOM; + goto print_err; + } ep->is_in = !!usb_endpoint_dir_in(desc); - if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) - return -EINVAL; + if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) { + ret = -EINVAL; + goto print_err; + } } /* sanity check ep-e/ep-f since their fifos are small */ max = usb_endpoint_maxp(desc) & 0x1fff; - if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) - return -ERANGE; + if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) { + ret = -ERANGE; + goto print_err; + } spin_lock_irqsave(&dev->lock, flags); _ep->maxpacket = max & 0x7ff; @@ -192,7 +212,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { spin_unlock_irqrestore(&dev->lock, flags); - return -ERANGE; + ret = -ERANGE; + goto print_err; } } ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); @@ -271,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* pci writes may still be posted */ spin_unlock_irqrestore(&dev->lock, flags); - return 0; + return ret; + +print_err: + dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret); + return ret; } static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec) @@ -426,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep) unsigned long flags; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || !ep->desc || _ep->name == ep0name) + if (!_ep || !ep->desc || _ep->name == ep0name) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; - + } spin_lock_irqsave(&ep->dev->lock, flags); nuke(ep); @@ -458,8 +484,10 @@ static struct usb_request struct net2280_ep *ep; struct net2280_request *req; - if (!_ep) + if (!_ep) { + pr_err("%s: Invalid ep\n", __func__); return NULL; + } ep = container_of(_ep, struct net2280_ep, ep); req = kzalloc(sizeof(*req), gfp_flags); @@ -491,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) struct net2280_request *req; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || !_req) + if (!_ep || !_req) { + dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n", + __func__, _ep, _req); return; + } req = container_of(_req, struct net2280_request, req); WARN_ON(!list_empty(&req->queue)); @@ -896,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) struct net2280_ep *ep; struct net2280 *dev; unsigned long flags; + int ret = 0; /* we always require a cpu-view buffer, so that we can * always use pio (as fallback or whatever). */ - req = container_of(_req, struct net2280_request, req); - if (!_req || !_req->complete || !_req->buf || - !list_empty(&req->queue)) - return -EINVAL; - if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) - return -EDOM; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) + if (!_ep || (!ep->desc && ep->num != 0)) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; + } + req = container_of(_req, struct net2280_request, req); + if (!_req || !_req->complete || !_req->buf || + !list_empty(&req->queue)) { + ret = -EINVAL; + goto print_err; + } + if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) { + ret = -EDOM; + goto print_err; + } dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + ret = -ESHUTDOWN; + goto print_err; + } /* FIXME implement PIO fallback for ZLPs with DMA */ - if (ep->dma && _req->length == 0) - return -EOPNOTSUPP; + if (ep->dma && _req->length == 0) { + ret = -EOPNOTSUPP; + goto print_err; + } /* set up dma mapping in case the caller didn't */ if (ep->dma) { - int ret; - ret = usb_gadget_map_request(&dev->gadget, _req, ep->is_in); if (ret) - return ret; + goto print_err; } ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", @@ -1013,7 +1053,11 @@ done: spin_unlock_irqrestore(&dev->lock, flags); /* pci writes may still be posted */ - return 0; + return ret; + +print_err: + dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret); + return ret; } static inline void @@ -1134,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) int stopped; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0) || !_req) + if (!_ep || (!ep->desc && ep->num != 0) || !_req) { + pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n", + __func__, _ep, _req); return -EINVAL; + } spin_lock_irqsave(&ep->dev->lock, flags); stopped = ep->stopped; @@ -1157,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) } if (&req->req != _req) { spin_unlock_irqrestore(&ep->dev->lock, flags); + dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n", + __func__); return -EINVAL; } @@ -1214,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) int retval = 0; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) + if (!_ep || (!ep->desc && ep->num != 0)) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; + } + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { + retval = -ESHUTDOWN; + goto print_err; + } if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) - == USB_ENDPOINT_XFER_ISOC) - return -EINVAL; + == USB_ENDPOINT_XFER_ISOC) { + retval = -EINVAL; + goto print_err; + } spin_lock_irqsave(&ep->dev->lock, flags); - if (!list_empty(&ep->queue)) + if (!list_empty(&ep->queue)) { retval = -EAGAIN; - else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) + goto print_unlock; + } else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) { retval = -EAGAIN; - else { + goto print_unlock; + } else { ep_vdbg(ep->dev, "%s %s %s\n", _ep->name, value ? "set" : "clear", wedged ? "wedge" : "halt"); @@ -1251,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) spin_unlock_irqrestore(&ep->dev->lock, flags); return retval; + +print_unlock: + spin_unlock_irqrestore(&ep->dev->lock, flags); +print_err: + dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval); + return retval; } static int net2280_set_halt(struct usb_ep *_ep, int value) @@ -1260,8 +1323,10 @@ static int net2280_set_halt(struct usb_ep *_ep, int value) static int net2280_set_wedge(struct usb_ep *_ep) { - if (!_ep || _ep->name == ep0name) + if (!_ep || _ep->name == ep0name) { + pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep); return -EINVAL; + } return net2280_set_halt_and_wedge(_ep, 1, 1); } @@ -1271,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep) u32 avail; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) + if (!_ep || (!ep->desc && ep->num != 0)) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -ENODEV; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + } + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { + dev_err(&ep->dev->pdev->dev, + "%s: Invalid driver=%p or speed=%d\n", + __func__, ep->dev->driver, ep->dev->gadget.speed); return -ESHUTDOWN; + } avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1); - if (avail > ep->fifo_size) + if (avail > ep->fifo_size) { + dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__); return -EOVERFLOW; + } if (ep->is_in) avail = ep->fifo_size - avail; return avail; @@ -1289,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep) struct net2280_ep *ep; ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) + if (!_ep || (!ep->desc && ep->num != 0)) { + pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) + } + if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { + dev_err(&ep->dev->pdev->dev, + "%s: Invalid driver=%p or speed=%d\n", + __func__, ep->dev->driver, ep->dev->gadget.speed); return; + } writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); (void) readl(&ep->regs->ep_rsp); @@ -1977,7 +2056,7 @@ static void usb_reinit_338x(struct net2280 *dev) for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep = &dev->ep[i]; - ep->ep.name = ep_name[i]; + ep->ep.name = dev->enhanced_mode ? ep_name_adv[i] : ep_name[i]; ep->dev = dev; ep->num = i; @@ -1989,11 +2068,9 @@ static void usb_reinit_338x(struct net2280 *dev) ep->regs = (struct net2280_ep_regs __iomem *) (((void __iomem *)&dev->epregs[ne[i]]) + ep_reg_addr[i]); - ep->fiforegs = &dev->fiforegs[i]; } else { ep->cfg = &dev->epregs[i]; ep->regs = &dev->epregs[i]; - ep->fiforegs = &dev->fiforegs[i]; } ep->fifo_size = (i != 0) ? 2048 : 512; @@ -2186,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget, dev->ep[i].irqs = 0; /* hook up the driver ... */ - dev->softconnect = 1; driver->driver.bus = NULL; dev->driver = driver; @@ -3052,6 +3128,8 @@ next_endpoints: BIT(PCI_RETRY_ABORT_INTERRUPT)) static void handle_stat1_irqs(struct net2280 *dev, u32 stat) +__releases(dev->lock) +__acquires(dev->lock) { struct net2280_ep *ep; u32 tmp, num, mask, scratch; @@ -3373,8 +3451,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) u32 usbstat; dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) (base + 0x00b4); - dev->fiforegs = (struct usb338x_fifo_regs __iomem *) - (base + 0x0500); dev->llregs = (struct usb338x_ll_regs __iomem *) (base + 0x0700); dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index ac8d5a20a378..4dff60d34f73 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -96,7 +96,6 @@ struct net2280_ep { struct net2280_ep_regs __iomem *regs; struct net2280_dma_regs __iomem *dma; struct net2280_dma *dummy; - struct usb338x_fifo_regs __iomem *fiforegs; dma_addr_t td_dma; /* of dummy */ struct net2280 *dev; unsigned long irqs; @@ -181,7 +180,6 @@ struct net2280 { struct net2280_dma_regs __iomem *dma; struct net2280_dep_regs __iomem *dep; struct net2280_ep_regs __iomem *epregs; - struct usb338x_fifo_regs __iomem *fiforegs; struct usb338x_ll_regs __iomem *llregs; struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index f0ae143dab6d..b51226abade6 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -93,50 +93,46 @@ static void handle_ep(struct pxa_ep *ep); static int state_dbg_show(struct seq_file *s, void *p) { struct pxa_udc *udc = s->private; - int pos = 0, ret; u32 tmp; - ret = -ENODEV; if (!udc->driver) - goto out; + return -ENODEV; /* basic device status */ - pos += seq_printf(s, DRIVER_DESC "\n" - "%s version: %s\nGadget driver: %s\n", - driver_name, DRIVER_VERSION, - udc->driver ? udc->driver->driver.name : "(none)"); + seq_printf(s, DRIVER_DESC "\n" + "%s version: %s\n" + "Gadget driver: %s\n", + driver_name, DRIVER_VERSION, + udc->driver ? udc->driver->driver.name : "(none)"); tmp = udc_readl(udc, UDCCR); - pos += seq_printf(s, - "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), " - "con=%d,inter=%d,altinter=%d\n", tmp, - (tmp & UDCCR_OEN) ? " oen":"", - (tmp & UDCCR_AALTHNP) ? " aalthnp":"", - (tmp & UDCCR_AHNP) ? " rem" : "", - (tmp & UDCCR_BHNP) ? " rstir" : "", - (tmp & UDCCR_DWRE) ? " dwre" : "", - (tmp & UDCCR_SMAC) ? " smac" : "", - (tmp & UDCCR_EMCE) ? " emce" : "", - (tmp & UDCCR_UDR) ? " udr" : "", - (tmp & UDCCR_UDA) ? " uda" : "", - (tmp & UDCCR_UDE) ? " ude" : "", - (tmp & UDCCR_ACN) >> UDCCR_ACN_S, - (tmp & UDCCR_AIN) >> UDCCR_AIN_S, - (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S); + seq_printf(s, + "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n", + tmp, + (tmp & UDCCR_OEN) ? " oen":"", + (tmp & UDCCR_AALTHNP) ? " aalthnp":"", + (tmp & UDCCR_AHNP) ? " rem" : "", + (tmp & UDCCR_BHNP) ? " rstir" : "", + (tmp & UDCCR_DWRE) ? " dwre" : "", + (tmp & UDCCR_SMAC) ? " smac" : "", + (tmp & UDCCR_EMCE) ? " emce" : "", + (tmp & UDCCR_UDR) ? " udr" : "", + (tmp & UDCCR_UDA) ? " uda" : "", + (tmp & UDCCR_UDE) ? " ude" : "", + (tmp & UDCCR_ACN) >> UDCCR_ACN_S, + (tmp & UDCCR_AIN) >> UDCCR_AIN_S, + (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S); /* registers for device and ep0 */ - pos += seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n", - udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1)); - pos += seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n", - udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1)); - pos += seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR)); - pos += seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, " - "reconfig=%lu\n", - udc->stats.irqs_reset, udc->stats.irqs_suspend, - udc->stats.irqs_resume, udc->stats.irqs_reconfig); - - ret = 0; -out: - return ret; + seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n", + udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1)); + seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n", + udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1)); + seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR)); + seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n", + udc->stats.irqs_reset, udc->stats.irqs_suspend, + udc->stats.irqs_resume, udc->stats.irqs_reconfig); + + return 0; } static int queues_dbg_show(struct seq_file *s, void *p) @@ -144,75 +140,67 @@ static int queues_dbg_show(struct seq_file *s, void *p) struct pxa_udc *udc = s->private; struct pxa_ep *ep; struct pxa27x_request *req; - int pos = 0, i, maxpkt, ret; + int i, maxpkt; - ret = -ENODEV; if (!udc->driver) - goto out; + return -ENODEV; /* dump endpoint queues */ for (i = 0; i < NR_PXA_ENDPOINTS; i++) { ep = &udc->pxa_ep[i]; maxpkt = ep->fifo_size; - pos += seq_printf(s, "%-12s max_pkt=%d %s\n", - EPNAME(ep), maxpkt, "pio"); + seq_printf(s, "%-12s max_pkt=%d %s\n", + EPNAME(ep), maxpkt, "pio"); if (list_empty(&ep->queue)) { - pos += seq_printf(s, "\t(nothing queued)\n"); + seq_puts(s, "\t(nothing queued)\n"); continue; } list_for_each_entry(req, &ep->queue, queue) { - pos += seq_printf(s, "\treq %p len %d/%d buf %p\n", - &req->req, req->req.actual, - req->req.length, req->req.buf); + seq_printf(s, "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); } } - ret = 0; -out: - return ret; + return 0; } static int eps_dbg_show(struct seq_file *s, void *p) { struct pxa_udc *udc = s->private; struct pxa_ep *ep; - int pos = 0, i, ret; + int i; u32 tmp; - ret = -ENODEV; if (!udc->driver) - goto out; + return -ENODEV; ep = &udc->pxa_ep[0]; tmp = udc_ep_readl(ep, UDCCSR); - pos += seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", tmp, - (tmp & UDCCSR0_SA) ? " sa" : "", - (tmp & UDCCSR0_RNE) ? " rne" : "", - (tmp & UDCCSR0_FST) ? " fst" : "", - (tmp & UDCCSR0_SST) ? " sst" : "", - (tmp & UDCCSR0_DME) ? " dme" : "", - (tmp & UDCCSR0_IPR) ? " ipr" : "", - (tmp & UDCCSR0_OPC) ? " opc" : ""); + seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", + tmp, + (tmp & UDCCSR0_SA) ? " sa" : "", + (tmp & UDCCSR0_RNE) ? " rne" : "", + (tmp & UDCCSR0_FST) ? " fst" : "", + (tmp & UDCCSR0_SST) ? " sst" : "", + (tmp & UDCCSR0_DME) ? " dme" : "", + (tmp & UDCCSR0_IPR) ? " ipr" : "", + (tmp & UDCCSR0_OPC) ? " opc" : ""); for (i = 0; i < NR_PXA_ENDPOINTS; i++) { ep = &udc->pxa_ep[i]; tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR); - pos += seq_printf(s, "%-12s: " - "IN %lu(%lu reqs), OUT %lu(%lu reqs), " - "irqs=%lu, udccr=0x%08x, udccsr=0x%03x, " - "udcbcr=%d\n", - EPNAME(ep), - ep->stats.in_bytes, ep->stats.in_ops, - ep->stats.out_bytes, ep->stats.out_ops, - ep->stats.irqs, - tmp, udc_ep_readl(ep, UDCCSR), - udc_ep_readl(ep, UDCBCR)); + seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n", + EPNAME(ep), + ep->stats.in_bytes, ep->stats.in_ops, + ep->stats.out_bytes, ep->stats.out_ops, + ep->stats.irqs, + tmp, udc_ep_readl(ep, UDCCSR), + udc_ep_readl(ep, UDCBCR)); } - ret = 0; -out: - return ret; + return 0; } static int eps_dbg_open(struct inode *inode, struct file *file) diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index 5a81cb086b99..d69c35558f68 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -35,6 +35,8 @@ * @dev - the child device to the actual controller * @gadget - the gadget. For use by the class code * @list - for use by the udc class driver + * @vbus - for udcs who care about vbus status, this value is real vbus status; + * for udcs who do not care about vbus status, this value is always true * * This represents the internal data structure which is used by the UDC-class * to hold information about udc driver and gadget together. @@ -44,6 +46,7 @@ struct usb_udc { struct usb_gadget *gadget; struct device dev; struct list_head list; + bool vbus; }; static struct class *udc_class; @@ -128,21 +131,11 @@ EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); static void usb_gadget_state_work(struct work_struct *work) { - struct usb_gadget *gadget = work_to_gadget(work); - struct usb_udc *udc = NULL; - - mutex_lock(&udc_lock); - list_for_each_entry(udc, &udc_list, list) - if (udc->gadget == gadget) - goto found; - mutex_unlock(&udc_lock); - - return; - -found: - mutex_unlock(&udc_lock); + struct usb_gadget *gadget = work_to_gadget(work); + struct usb_udc *udc = gadget->udc; - sysfs_notify(&udc->dev.kobj, NULL, "state"); + if (udc) + sysfs_notify(&udc->dev.kobj, NULL, "state"); } void usb_gadget_set_state(struct usb_gadget *gadget, @@ -155,6 +148,34 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state); /* ------------------------------------------------------------------------- */ +static void usb_udc_connect_control(struct usb_udc *udc) +{ + if (udc->vbus) + usb_gadget_connect(udc->gadget); + else + usb_gadget_disconnect(udc->gadget); +} + +/** + * usb_udc_vbus_handler - updates the udc core vbus status, and try to + * connect or disconnect gadget + * @gadget: The gadget which vbus change occurs + * @status: The vbus status + * + * The udc driver calls it when it wants to connect or disconnect gadget + * according to vbus status. + */ +void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) +{ + struct usb_udc *udc = gadget->udc; + + if (udc) { + udc->vbus = status; + usb_udc_connect_control(udc); + } +} +EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); + /** * usb_gadget_udc_reset - notifies the udc core that bus reset occurs * @gadget: The gadget which bus reset occurs @@ -278,6 +299,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, goto err3; udc->gadget = gadget; + gadget->udc = udc; mutex_lock(&udc_lock); list_add_tail(&udc->list, &udc_list); @@ -287,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, goto err4; usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + udc->vbus = true; mutex_unlock(&udc_lock); @@ -348,21 +371,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc) */ void usb_del_gadget_udc(struct usb_gadget *gadget) { - struct usb_udc *udc = NULL; + struct usb_udc *udc = gadget->udc; - mutex_lock(&udc_lock); - list_for_each_entry(udc, &udc_list, list) - if (udc->gadget == gadget) - goto found; - - dev_err(gadget->dev.parent, "gadget not registered.\n"); - mutex_unlock(&udc_lock); - - return; + if (!udc) + return; -found: dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); + mutex_lock(&udc_lock); list_del(&udc->list); mutex_unlock(&udc_lock); @@ -397,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri driver->unbind(udc->gadget); goto err1; } - usb_gadget_connect(udc->gadget); + usb_udc_connect_control(udc); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 067920f2d570..a48b5a9c6c47 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -507,7 +507,8 @@ void musb_hnp_stop(struct musb *musb) musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16); } -static void musb_generic_disable(struct musb *musb); +static void musb_recover_from_babble(struct musb *musb); + /* * Interrupt Service Routine to record USB "global" interrupts. * Since these do not happen often and signify things of @@ -534,30 +535,16 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ if (int_usb & MUSB_INTR_RESUME) { handled = IRQ_HANDLED; - dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->otg->state)); + dev_dbg(musb->controller, "RESUME (%s)\n", + usb_otg_state_string(musb->xceiv->otg->state)); if (devctl & MUSB_DEVCTL_HM) { - void __iomem *mbase = musb->mregs; - u8 power; - switch (musb->xceiv->otg->state) { case OTG_STATE_A_SUSPEND: /* remote wakeup? later, GetPortStatus * will stop RESUME signaling */ - power = musb_readb(musb->mregs, MUSB_POWER); - if (power & MUSB_POWER_SUSPENDM) { - /* spurious */ - musb->int_usb &= ~MUSB_INTR_SUSPEND; - dev_dbg(musb->controller, "Spurious SUSPENDM\n"); - break; - } - - power &= ~MUSB_POWER_SUSPENDM; - musb_writeb(mbase, MUSB_POWER, - power | MUSB_POWER_RESUME); - musb->port1_status |= (USB_PORT_STAT_C_SUSPEND << 16) | MUSB_PORT_STAT_RESUME; @@ -775,10 +762,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, musb->ep0_stage = MUSB_EP0_START; - /* flush endpoints when transitioning from Device Mode */ - if (is_peripheral_active(musb)) { - /* REVISIT HNP; just force disconnect */ - } musb->intrtxe = musb->epmask; musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe); musb->intrrxe = musb->epmask & 0xfffe; @@ -879,20 +862,19 @@ b_host: */ if (int_usb & MUSB_INTR_RESET) { handled = IRQ_HANDLED; - if ((devctl & MUSB_DEVCTL_HM) != 0) { + if (devctl & MUSB_DEVCTL_HM) { /* - * Looks like non-HS BABBLE can be ignored, but - * HS BABBLE is an error condition. For HS the solution - * is to avoid babble in the first place and fix what - * caused BABBLE. When HS BABBLE happens we can only - * stop the session. + * When BABBLE happens what we can depends on which + * platform MUSB is running, because some platforms + * implemented proprietary means for 'recovering' from + * Babble conditions. One such platform is AM335x. In + * most cases, however, the only thing we can do is + * drop the session. */ - if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) - dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl); - else { - ERR("Stopping host session -- babble\n"); - musb_writeb(musb->mregs, MUSB_DEVCTL, 0); - } + dev_err(musb->controller, "Babble\n"); + + if (is_host_active(musb)) + musb_recover_from_babble(musb); } else { dev_dbg(musb->controller, "BUS RESET as %s\n", usb_otg_state_string(musb->xceiv->otg->state)); @@ -931,13 +913,6 @@ b_host: } } - /* handle babble condition */ - if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) { - musb_generic_disable(musb); - schedule_delayed_work(&musb->recover_work, - msecs_to_jiffies(100)); - } - #if 0 /* REVISIT ... this would be for multiplexing periodic endpoints, or * supporting transfer phasing to prevent exceeding ISO bandwidth @@ -990,7 +965,7 @@ b_host: /*-------------------------------------------------------------------------*/ -static void musb_generic_disable(struct musb *musb) +static void musb_disable_interrupts(struct musb *musb) { void __iomem *mbase = musb->mregs; u16 temp; @@ -1002,14 +977,33 @@ static void musb_generic_disable(struct musb *musb) musb->intrrxe = 0; musb_writew(mbase, MUSB_INTRRXE, 0); - /* off */ - musb_writeb(mbase, MUSB_DEVCTL, 0); - /* flush pending interrupts */ temp = musb_readb(mbase, MUSB_INTRUSB); temp = musb_readw(mbase, MUSB_INTRTX); temp = musb_readw(mbase, MUSB_INTRRX); +} + +static void musb_enable_interrupts(struct musb *musb) +{ + void __iomem *regs = musb->mregs; + + /* Set INT enable registers, enable interrupts */ + musb->intrtxe = musb->epmask; + musb_writew(regs, MUSB_INTRTXE, musb->intrtxe); + musb->intrrxe = musb->epmask & 0xfffe; + musb_writew(regs, MUSB_INTRRXE, musb->intrrxe); + musb_writeb(regs, MUSB_INTRUSBE, 0xf7); + +} + +static void musb_generic_disable(struct musb *musb) +{ + void __iomem *mbase = musb->mregs; + musb_disable_interrupts(musb); + + /* off */ + musb_writeb(mbase, MUSB_DEVCTL, 0); } /* @@ -1022,13 +1016,7 @@ void musb_start(struct musb *musb) dev_dbg(musb->controller, "<== devctl %02x\n", devctl); - /* Set INT enable registers, enable interrupts */ - musb->intrtxe = musb->epmask; - musb_writew(regs, MUSB_INTRTXE, musb->intrtxe); - musb->intrrxe = musb->epmask & 0xfffe; - musb_writew(regs, MUSB_INTRRXE, musb->intrrxe); - musb_writeb(regs, MUSB_INTRUSBE, 0xf7); - + musb_enable_interrupts(musb); musb_writeb(regs, MUSB_TESTMODE, 0); /* put into basic highspeed mode and start session */ @@ -1587,9 +1575,12 @@ static int musb_core_init(u16 musb_type, struct musb *musb) irqreturn_t musb_interrupt(struct musb *musb) { irqreturn_t retval = IRQ_NONE; + unsigned long status; + unsigned long epnum; u8 devctl; - int ep_num; - u32 reg; + + if (!musb->int_usb && !musb->int_tx && !musb->int_rx) + return IRQ_NONE; devctl = musb_readb(musb->mregs, MUSB_DEVCTL); @@ -1597,56 +1588,57 @@ irqreturn_t musb_interrupt(struct musb *musb) is_host_active(musb) ? "host" : "peripheral", musb->int_usb, musb->int_tx, musb->int_rx); - /* the core can interrupt us for multiple reasons; docs have - * a generic interrupt flowchart to follow + /** + * According to Mentor Graphics' documentation, flowchart on page 98, + * IRQ should be handled as follows: + * + * . Resume IRQ + * . Session Request IRQ + * . VBUS Error IRQ + * . Suspend IRQ + * . Connect IRQ + * . Disconnect IRQ + * . Reset/Babble IRQ + * . SOF IRQ (we're not using this one) + * . Endpoint 0 IRQ + * . TX Endpoints + * . RX Endpoints + * + * We will be following that flowchart in order to avoid any problems + * that might arise with internal Finite State Machine. */ - if (musb->int_usb) - retval |= musb_stage0_irq(musb, musb->int_usb, - devctl); - /* "stage 1" is handling endpoint irqs */ + if (musb->int_usb) + retval |= musb_stage0_irq(musb, musb->int_usb, devctl); - /* handle endpoint 0 first */ if (musb->int_tx & 1) { if (is_host_active(musb)) retval |= musb_h_ep0_irq(musb); else retval |= musb_g_ep0_irq(musb); + + /* we have just handled endpoint 0 IRQ, clear it */ + musb->int_tx &= ~BIT(0); } - /* RX on endpoints 1-15 */ - reg = musb->int_rx >> 1; - ep_num = 1; - while (reg) { - if (reg & 1) { - /* musb_ep_select(musb->mregs, ep_num); */ - /* REVISIT just retval = ep->rx_irq(...) */ - retval = IRQ_HANDLED; - if (is_host_active(musb)) - musb_host_rx(musb, ep_num); - else - musb_g_rx(musb, ep_num); - } + status = musb->int_tx; - reg >>= 1; - ep_num++; + for_each_set_bit(epnum, &status, 16) { + retval = IRQ_HANDLED; + if (is_host_active(musb)) + musb_host_tx(musb, epnum); + else + musb_g_tx(musb, epnum); } - /* TX on endpoints 1-15 */ - reg = musb->int_tx >> 1; - ep_num = 1; - while (reg) { - if (reg & 1) { - /* musb_ep_select(musb->mregs, ep_num); */ - /* REVISIT just retval |= ep->tx_irq(...) */ - retval = IRQ_HANDLED; - if (is_host_active(musb)) - musb_host_tx(musb, ep_num); - else - musb_g_tx(musb, ep_num); - } - reg >>= 1; - ep_num++; + status = musb->int_rx; + + for_each_set_bit(epnum, &status, 16) { + retval = IRQ_HANDLED; + if (is_host_active(musb)) + musb_host_rx(musb, epnum); + else + musb_g_rx(musb, epnum); } return retval; @@ -1825,33 +1817,44 @@ static void musb_irq_work(struct work_struct *data) } } -/* Recover from babble interrupt conditions */ -static void musb_recover_work(struct work_struct *data) +static void musb_recover_from_babble(struct musb *musb) { - struct musb *musb = container_of(data, struct musb, recover_work.work); - int status, ret; + int ret; + u8 devctl; - ret = musb_platform_reset(musb); - if (ret) + musb_disable_interrupts(musb); + + /* + * wait at least 320 cycles of 60MHz clock. That's 5.3us, we will give + * it some slack and wait for 10us. + */ + udelay(10); + + ret = musb_platform_recover(musb); + if (ret) { + musb_enable_interrupts(musb); return; + } - usb_phy_vbus_off(musb->xceiv); - usleep_range(100, 200); + /* drop session bit */ + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + devctl &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - usb_phy_vbus_on(musb->xceiv); - usleep_range(100, 200); + /* tell usbcore about it */ + musb_root_disconnect(musb); /* * When a babble condition occurs, the musb controller * removes the session bit and the endpoint config is lost. */ if (musb->dyn_fifo) - status = ep_config_from_table(musb); + ret = ep_config_from_table(musb); else - status = ep_config_from_hw(musb); + ret = ep_config_from_hw(musb); - /* start the session again */ - if (status == 0) + /* restart session */ + if (ret == 0) musb_start(musb); } @@ -2087,7 +2090,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) /* Init IRQ workqueue before request_irq */ INIT_WORK(&musb->irq_work, musb_irq_work); - INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work); INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset); INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); @@ -2183,7 +2185,6 @@ fail4: fail3: cancel_work_sync(&musb->irq_work); - cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) @@ -2249,7 +2250,6 @@ static int musb_remove(struct platform_device *pdev) dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); - cancel_delayed_work_sync(&musb->recover_work); cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); musb_free(musb); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 5e65958f7915..3877249a8b2d 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -160,7 +160,8 @@ struct musb_io; * @init: turns on clocks, sets up platform-specific registers, etc * @exit: undoes @init * @set_mode: forcefully changes operating mode - * @try_ilde: tries to idle the IP + * @try_idle: tries to idle the IP + * @recover: platform-specific babble recovery * @vbus_status: returns vbus status if possible * @set_vbus: forces vbus status * @adjust_channel_params: pre check for standard dma channel_program func @@ -196,7 +197,7 @@ struct musb_platform_ops { void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); - int (*reset)(struct musb *musb); + int (*recover)(struct musb *musb); int (*vbus_status)(struct musb *musb); void (*set_vbus)(struct musb *musb, int on); @@ -300,7 +301,6 @@ struct musb { irqreturn_t (*isr)(int, void *); struct work_struct irq_work; - struct delayed_work recover_work; struct delayed_work deassert_reset_work; struct delayed_work finish_resume_work; u16 hwvers; @@ -558,12 +558,12 @@ static inline void musb_platform_try_idle(struct musb *musb, musb->ops->try_idle(musb, timeout); } -static inline int musb_platform_reset(struct musb *musb) +static inline int musb_platform_recover(struct musb *musb) { - if (!musb->ops->reset) - return -EINVAL; + if (!musb->ops->recover) + return 0; - return musb->ops->reset(musb); + return musb->ops->recover(musb); } static inline int musb_platform_get_vbus_status(struct musb *musb) diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index be84562d021b..8bd8c5e26921 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -225,10 +225,12 @@ static void cppi41_dma_callback(void *private_data) struct dma_channel *channel = private_data; struct cppi41_dma_channel *cppi41_channel = channel->private_data; struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; + struct cppi41_dma_controller *controller; struct musb *musb = hw_ep->musb; unsigned long flags; struct dma_tx_state txstate; u32 transferred; + int is_hs = 0; bool empty; spin_lock_irqsave(&musb->lock, flags); @@ -248,61 +250,59 @@ static void cppi41_dma_callback(void *private_data) transferred < cppi41_channel->packet_sz) cppi41_channel->prog_len = 0; - empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) { + if (cppi41_channel->is_tx) + empty = musb_is_tx_fifo_empty(hw_ep); + + if (!cppi41_channel->is_tx || empty) { cppi41_trans_done(cppi41_channel); - } else { - struct cppi41_dma_controller *controller; - int is_hs = 0; - /* - * On AM335x it has been observed that the TX interrupt fires - * too early that means the TXFIFO is not yet empty but the DMA - * engine says that it is done with the transfer. We don't - * receive a FIFO empty interrupt so the only thing we can do is - * to poll for the bit. On HS it usually takes 2us, on FS around - * 110us - 150us depending on the transfer size. - * We spin on HS (no longer than than 25us and setup a timer on - * FS to check for the bit and complete the transfer. - */ - controller = cppi41_channel->controller; + goto out; + } - if (is_host_active(musb)) { - if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED) - is_hs = 1; - } else { - if (musb->g.speed == USB_SPEED_HIGH) - is_hs = 1; - } - if (is_hs) { - unsigned wait = 25; - - do { - empty = musb_is_tx_fifo_empty(hw_ep); - if (empty) - break; - wait--; - if (!wait) - break; - udelay(1); - } while (1); + /* + * On AM335x it has been observed that the TX interrupt fires + * too early that means the TXFIFO is not yet empty but the DMA + * engine says that it is done with the transfer. We don't + * receive a FIFO empty interrupt so the only thing we can do is + * to poll for the bit. On HS it usually takes 2us, on FS around + * 110us - 150us depending on the transfer size. + * We spin on HS (no longer than than 25us and setup a timer on + * FS to check for the bit and complete the transfer. + */ + controller = cppi41_channel->controller; + + if (is_host_active(musb)) { + if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED) + is_hs = 1; + } else { + if (musb->g.speed == USB_SPEED_HIGH) + is_hs = 1; + } + if (is_hs) { + unsigned wait = 25; + do { empty = musb_is_tx_fifo_empty(hw_ep); if (empty) { cppi41_trans_done(cppi41_channel); goto out; } - } - list_add_tail(&cppi41_channel->tx_check, - &controller->early_tx_list); - if (!hrtimer_is_queued(&controller->early_tx)) { - unsigned long usecs = cppi41_channel->total_len / 10; + wait--; + if (!wait) + break; + cpu_relax(); + } while (1); + } + list_add_tail(&cppi41_channel->tx_check, + &controller->early_tx_list); + if (!hrtimer_is_queued(&controller->early_tx)) { + unsigned long usecs = cppi41_channel->total_len / 10; - hrtimer_start_range_ns(&controller->early_tx, + hrtimer_start_range_ns(&controller->early_tx, ktime_set(0, usecs * NSEC_PER_USEC), 20 * NSEC_PER_USEC, HRTIMER_MODE_REL); - } } + out: spin_unlock_irqrestore(&musb->lock, flags); } diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index a900c9877195..b23ad150a165 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -119,7 +119,7 @@ struct dsps_musb_wrapper { unsigned iddig:5; unsigned iddig_mux:5; /* miscellaneous stuff */ - u8 poll_seconds; + unsigned poll_timeout; }; /* @@ -225,9 +225,8 @@ static void dsps_musb_enable(struct musb *musb) dsps_writel(reg_base, wrp->epintr_set, epmask); dsps_writel(reg_base, wrp->coreintr_set, coremask); - /* Force the DRVVBUS IRQ so we can start polling for ID change. */ - dsps_writel(reg_base, wrp->coreintr_set, - (1 << wrp->drvvbus) << wrp->usb_shift); + /* start polling for ID change. */ + mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout)); dsps_musb_try_idle(musb, 0); } @@ -285,7 +284,8 @@ static void otg_timer(unsigned long _musb) } if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION); - mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); break; case OTG_STATE_A_WAIT_VFALL: musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; @@ -330,28 +330,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n", usbintr, epintr); - /* - * DRVVBUS IRQs are the only proxy we have (a very poor one!) for - * DSPS IP's missing ID change IRQ. We need an ID change IRQ to - * switch appropriately between halves of the OTG state machine. - * Managing DEVCTL.SESSION per Mentor docs requires that we know its - * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set. - * Also, DRVVBUS pulses for SRP (but not at 5V) ... - */ - if (is_host_active(musb) && usbintr & MUSB_INTR_BABBLE) { - pr_info("CAUTION: musb: Babble Interrupt Occurred\n"); - - /* - * When a babble condition occurs, the musb controller removes - * the session and is no longer in host mode. Hence, all - * devices connected to its root hub get disconnected. - * - * Hand this error down to the musb core isr, so it can - * recover. - */ - musb->int_usb = MUSB_INTR_BABBLE | MUSB_INTR_DISCONNECT; - musb->int_tx = musb->int_rx = 0; - } if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) { int drvvbus = dsps_readl(reg_base, wrp->status); @@ -374,8 +352,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) */ musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; - mod_timer(&glue->timer, - jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); WARNING("VBUS error workaround (delay coming)\n"); } else if (drvvbus) { MUSB_HST_MODE(musb); @@ -404,7 +382,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) /* Poll for ID change in OTG port mode */ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) - mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); out: spin_unlock_irqrestore(&musb->lock, flags); @@ -453,7 +432,7 @@ static int dsps_musb_init(struct musb *musb) musb->ctrl_base = reg_base; /* NOP driver needs change if supporting dual instance */ - musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0); + musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "phys", 0); if (IS_ERR(musb->xceiv)) return PTR_ERR(musb->xceiv); @@ -497,7 +476,7 @@ static int dsps_musb_init(struct musb *musb) * logic enabled. */ val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); - if (val == MUSB_BABBLE_RCV_DISABLE) { + 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); @@ -571,7 +550,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode) return 0; } -static bool sw_babble_control(struct musb *musb) +static bool dsps_sw_babble_control(struct musb *musb) { u8 babble_ctl; bool session_restart = false; @@ -622,37 +601,36 @@ static bool sw_babble_control(struct musb *musb) return session_restart; } -static int dsps_musb_reset(struct musb *musb) +static int dsps_musb_recover(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, error; + 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); - error = phy_power_off(musb->phy); - if (error) - dev_err(dev, "phy shutdown failed: %i\n", error); - usleep_range(100, 200); - usb_phy_init(musb->xceiv); - error = phy_power_on(musb->phy); - if (error) - dev_err(dev, "phy powerup failed: %i\n", error); + session_restart = dsps_sw_babble_control(musb); + else session_restart = 1; + + return session_restart ? 0 : -EPIPE; +} + +/* Similar to am35x, dm81xx support only 32-bit read operation */ +static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) +{ + void __iomem *fifo = hw_ep->fifo; + + if (len >= 4) { + readsl(fifo, dst, len >> 2); + dst += len & ~0x03; + len &= 0x03; } - return !session_restart; + /* Read any remaining 1 to 3 bytes */ + if (len > 0) { + u32 val = musb_readl(fifo, 0); + memcpy(dst, &val, len); + } } static struct musb_platform_ops dsps_ops = { @@ -665,7 +643,7 @@ static struct musb_platform_ops dsps_ops = { .try_idle = dsps_musb_try_idle, .set_mode = dsps_musb_set_mode, - .reset = dsps_musb_reset, + .recover = dsps_musb_recover, }; static u64 musb_dmamask = DMA_BIT_MASK(32); @@ -737,7 +715,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, musb->dev.parent = dev; musb->dev.dma_mask = &musb_dmamask; musb->dev.coherent_dma_mask = musb_dmamask; - musb->dev.of_node = of_node_get(dn); glue->musb = musb; @@ -802,6 +779,9 @@ static int dsps_probe(struct platform_device *pdev) } wrp = match->data; + if (of_device_is_compatible(pdev->dev.of_node, "ti,musb-dm816")) + dsps_ops.read_fifo = dsps_read_fifo32; + /* allocate glue */ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) @@ -873,12 +853,14 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { .rxep_shift = 16, .rxep_mask = 0xfffe, .rxep_bitmap = (0xfffe << 16), - .poll_seconds = 2, + .poll_timeout = 2000, /* ms */ }; static const struct of_device_id musb_dsps_of_match[] = { { .compatible = "ti,musb-am33xx", - .data = (void *) &am33xx_driver_data, }, + .data = &am33xx_driver_data, }, + { .compatible = "ti,musb-dm816", + .data = &am33xx_driver_data, }, { }, }; MODULE_DEVICE_TABLE(of, musb_dsps_of_match); @@ -929,7 +911,8 @@ static int dsps_resume(struct device *dev) dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode); if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) - mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ); + mod_timer(&glue->timer, jiffies + + msecs_to_jiffies(wrp->poll_timeout)); return 0; } diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index b2d9040c7685..4c481cd66c77 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1876,44 +1876,6 @@ err: return retval; } -static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver) -{ - int i; - struct musb_hw_ep *hw_ep; - - /* don't disconnect if it's not connected */ - if (musb->g.speed == USB_SPEED_UNKNOWN) - driver = NULL; - else - musb->g.speed = USB_SPEED_UNKNOWN; - - /* deactivate the hardware */ - if (musb->softconnect) { - musb->softconnect = 0; - musb_pullup(musb, 0); - } - musb_stop(musb); - - /* killing any outstanding requests will quiesce the driver; - * then report disconnect - */ - if (driver) { - for (i = 0, hw_ep = musb->endpoints; - i < musb->nr_endpoints; - i++, hw_ep++) { - musb_ep_select(musb->mregs, i); - if (hw_ep->is_shared_fifo /* || !epnum */) { - nuke(&hw_ep->ep_in, -ESHUTDOWN); - } else { - if (hw_ep->max_packet_sz_tx) - nuke(&hw_ep->ep_in, -ESHUTDOWN); - if (hw_ep->max_packet_sz_rx) - nuke(&hw_ep->ep_out, -ESHUTDOWN); - } - } - } -} - /* * Unregister the gadget driver. Used by gadget drivers when * unregistering themselves from the controller. @@ -1940,7 +1902,7 @@ static int musb_gadget_stop(struct usb_gadget *g) (void) musb_gadget_vbus_draw(&musb->g, 0); musb->xceiv->otg->state = OTG_STATE_UNDEFINED; - stop_activity(musb, NULL); + musb_stop(musb); otg_set_peripheral(musb->xceiv->otg, NULL); musb->is_active = 0; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 2fb3828b5089..2175678e674e 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -202,13 +202,13 @@ config USB_RCAR_GEN2_PHY config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM || ARM64 + select USB_ULPI_VIEWPORT help Enable this to support ULPI connected USB OTG transceivers which are likely found on embedded boards. config USB_ULPI_VIEWPORT bool - depends on USB_ULPI help Provides read/write operations to the ULPI phy register set for controllers with a viewport register (e.g. Chipidea/ARC controllers). diff --git a/drivers/usb/phy/of.c b/drivers/usb/phy/of.c index 7ea0154da9d5..66ffa82457a8 100644 --- a/drivers/usb/phy/of.c +++ b/drivers/usb/phy/of.c @@ -27,7 +27,7 @@ static const char *const usbphy_modes[] = { * @np: Pointer to the given device_node * * The function gets phy interface string from property 'phy_type', - * and returns the correspondig enum usb_phy_interface + * and returns the corresponding enum usb_phy_interface */ enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np) { diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 0b1bd2369293..59cccfadae96 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -893,7 +893,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab) /* * Disconnection Sequence: - * 1. Disconect Interrupt + * 1. Disconnect Interrupt * 2. Disable regulators * 3. Disable AB clock * 4. Disable the Phy diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 70be50b734b2..deee68eafb72 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -62,14 +62,14 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) return 0; } -static void nop_reset_set(struct usb_phy_generic *nop, int asserted) +static void nop_reset(struct usb_phy_generic *nop) { if (!nop->gpiod_reset) return; - gpiod_direction_output(nop->gpiod_reset, !asserted); + gpiod_set_value(nop->gpiod_reset, 1); usleep_range(10000, 20000); - gpiod_set_value(nop->gpiod_reset, asserted); + gpiod_set_value(nop->gpiod_reset, 0); } /* interface to regulator framework */ @@ -151,8 +151,7 @@ int usb_gen_phy_init(struct usb_phy *phy) if (!IS_ERR(nop->clk)) clk_prepare_enable(nop->clk); - /* De-assert RESET */ - nop_reset_set(nop, 0); + nop_reset(nop); return 0; } @@ -162,8 +161,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy) { struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); - /* Assert RESET */ - nop_reset_set(nop, 1); + gpiod_set_value(nop->gpiod_reset, 1); if (!IS_ERR(nop->clk)) clk_disable_unprepare(nop->clk); diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 2f9735b35338..d1cd6b50f520 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -81,7 +81,9 @@ static void devm_usb_phy_release(struct device *dev, void *res) static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) { - return res == match_data; + struct usb_phy **phy = res; + + return *phy == match_data; } /** diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 4cf77d3c3bd2..0f7e850fd4aa 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -276,6 +276,16 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, } /* + * interrupt functions + */ +void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit) +{ + u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0); + + usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask); +} + +/* * local functions */ static void usbhsc_set_buswait(struct usbhs_priv *priv) @@ -487,6 +497,15 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) if (gpio > 0) dparam->enable_gpio = gpio; + switch (dparam->type) { + case USBHS_TYPE_R8A7790: + case USBHS_TYPE_R8A7791: + dparam->has_usb_dmac = 1; + break; + default: + break; + } + return info; } diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index fc96e924edc4..8c5fc12ad778 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -193,6 +193,7 @@ struct usbhs_priv; #define TYPE_BULK (1 << 14) #define TYPE_INT (2 << 14) #define TYPE_ISO (3 << 14) +#define BFRE (1 << 10) /* BRDY Interrupt Operation Spec. */ #define DBLB (1 << 9) /* Double Buffer Mode */ #define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */ #define DIR_OUT (1 << 4) /* Transfer Direction */ @@ -216,6 +217,7 @@ struct usbhs_priv; #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ #define SQCLR (1 << 8) /* Toggle Bit Clear */ #define SQSET (1 << 7) /* Toggle Bit Set */ +#define SQMON (1 << 6) /* Toggle Bit Check */ #define PBUSY (1 << 5) /* Pipe Busy */ #define PID_MASK (0x3) /* Response PID */ #define PID_NAK 0 @@ -324,6 +326,11 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub, u16 hubport, u16 speed); /* + * interrupt functions + */ +void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit); + +/* * data */ struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev); diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index d891bff39d66..8597cf9cfceb 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -813,7 +813,8 @@ static void xfer_work(struct work_struct *work) desc->callback = usbhsf_dma_complete; desc->callback_param = pipe; - if (dmaengine_submit(desc) < 0) { + pkt->cookie = dmaengine_submit(desc); + if (pkt->cookie < 0) { dev_err(dev, "Failed to submit dma descriptor\n"); return; } @@ -822,10 +823,10 @@ static void xfer_work(struct work_struct *work) fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); usbhs_pipe_running(pipe, 1); - usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); - usbhs_pipe_enable(pipe); usbhsf_dma_start(pipe, fifo); + usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); dma_async_issue_pending(chan); + usbhs_pipe_enable(pipe); } /* @@ -838,6 +839,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) struct usbhs_fifo *fifo; int len = pkt->length - pkt->actual; int ret; + uintptr_t align_mask; if (usbhs_pipe_is_busy(pipe)) return 0; @@ -847,10 +849,14 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) usbhs_pipe_is_dcp(pipe)) goto usbhsf_pio_prepare_push; - if (len & 0x7) /* 8byte alignment */ + /* check data length if this driver don't use USB-DMAC */ + if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7) goto usbhsf_pio_prepare_push; - if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ + /* check buffer alignment */ + align_mask = usbhs_get_dparam(priv, has_usb_dmac) ? + USBHS_USB_DMAC_XFER_SIZE - 1 : 0x7; + if ((uintptr_t)(pkt->buf + pkt->actual) & align_mask) goto usbhsf_pio_prepare_push; /* return at this time if the pipe is running */ @@ -924,7 +930,85 @@ struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = { /* * DMA pop handler */ -static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) + +static int usbhsf_dma_prepare_pop_with_rx_irq(struct usbhs_pkt *pkt, + int *is_done) +{ + return usbhsf_prepare_pop(pkt, is_done); +} + +static int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt, + int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo; + int ret; + + if (usbhs_pipe_is_busy(pipe)) + return 0; + + /* use PIO if packet is less than pio_dma_border or pipe is DCP */ + if ((pkt->length < usbhs_get_dparam(priv, pio_dma_border)) || + usbhs_pipe_is_dcp(pipe)) + goto usbhsf_pio_prepare_pop; + + fifo = usbhsf_get_dma_fifo(priv, pkt); + if (!fifo) + goto usbhsf_pio_prepare_pop; + + if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1)) + goto usbhsf_pio_prepare_pop; + + usbhs_pipe_config_change_bfre(pipe, 1); + + ret = usbhsf_fifo_select(pipe, fifo, 0); + if (ret < 0) + goto usbhsf_pio_prepare_pop; + + if (usbhsf_dma_map(pkt) < 0) + goto usbhsf_pio_prepare_pop_unselect; + + /* DMA */ + + /* + * usbhs_fifo_dma_pop_handler :: prepare + * enabled irq to come here. + * but it is no longer needed for DMA. disable it. + */ + usbhsf_rx_irq_ctrl(pipe, 0); + + pkt->trans = pkt->length; + + INIT_WORK(&pkt->work, xfer_work); + schedule_work(&pkt->work); + + return 0; + +usbhsf_pio_prepare_pop_unselect: + usbhsf_fifo_unselect(pipe, fifo); +usbhsf_pio_prepare_pop: + + /* + * change handler to PIO + */ + pkt->handler = &usbhs_fifo_pio_pop_handler; + usbhs_pipe_config_change_bfre(pipe, 0); + + return pkt->handler->prepare(pkt, is_done); +} + +static int usbhsf_dma_prepare_pop(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe); + + if (usbhs_get_dparam(priv, has_usb_dmac)) + return usbhsf_dma_prepare_pop_with_usb_dmac(pkt, is_done); + else + return usbhsf_dma_prepare_pop_with_rx_irq(pkt, is_done); +} + +static int usbhsf_dma_try_pop_with_rx_irq(struct usbhs_pkt *pkt, int *is_done) { struct usbhs_pipe *pipe = pkt->pipe; struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); @@ -993,7 +1077,16 @@ usbhsf_pio_prepare_pop: return pkt->handler->try_run(pkt, is_done); } -static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) +static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe); + + BUG_ON(usbhs_get_dparam(priv, has_usb_dmac)); + + return usbhsf_dma_try_pop_with_rx_irq(pkt, is_done); +} + +static int usbhsf_dma_pop_done_with_rx_irq(struct usbhs_pkt *pkt, int *is_done) { struct usbhs_pipe *pipe = pkt->pipe; int maxp = usbhs_pipe_get_maxpacket(pipe); @@ -1017,8 +1110,68 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) return 0; } +static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt, + struct dma_chan *chan, int dtln) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct dma_tx_state state; + size_t received_size; + int maxp = usbhs_pipe_get_maxpacket(pipe); + + dmaengine_tx_status(chan, pkt->cookie, &state); + received_size = pkt->length - state.residue; + + if (dtln) { + received_size -= USBHS_USB_DMAC_XFER_SIZE; + received_size &= ~(maxp - 1); + received_size += dtln; + } + + return received_size; +} + +static int usbhsf_dma_pop_done_with_usb_dmac(struct usbhs_pkt *pkt, + int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); + struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt); + int rcv_len; + + /* + * Since the driver disables rx_irq in DMA mode, the interrupt handler + * cannot the BRDYSTS. So, the function clears it here because the + * driver may use PIO mode next time. + */ + usbhs_xxxsts_clear(priv, BRDYSTS, usbhs_pipe_number(pipe)); + + rcv_len = usbhsf_fifo_rcv_len(priv, fifo); + usbhsf_fifo_clear(pipe, fifo); + pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len); + + usbhsf_dma_stop(pipe, fifo); + usbhsf_dma_unmap(pkt); + usbhsf_fifo_unselect(pipe, pipe->fifo); + + /* The driver can assume the rx transaction is always "done" */ + *is_done = 1; + + return 0; +} + +static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe); + + if (usbhs_get_dparam(priv, has_usb_dmac)) + return usbhsf_dma_pop_done_with_usb_dmac(pkt, is_done); + else + return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done); +} + struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = { - .prepare = usbhsf_prepare_pop, + .prepare = usbhsf_dma_prepare_pop, .try_run = usbhsf_dma_try_pop, .dma_done = usbhsf_dma_pop_done }; @@ -1069,23 +1222,29 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo) &fifo->rx_slave); } -static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo) +static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo, + int channel) { - fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx"); + char name[16]; + + snprintf(name, sizeof(name), "tx%d", channel); + fifo->tx_chan = dma_request_slave_channel_reason(dev, name); if (IS_ERR(fifo->tx_chan)) fifo->tx_chan = NULL; - fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx"); + + snprintf(name, sizeof(name), "rx%d", channel); + fifo->rx_chan = dma_request_slave_channel_reason(dev, name); if (IS_ERR(fifo->rx_chan)) fifo->rx_chan = NULL; } -static void usbhsf_dma_init(struct usbhs_priv *priv, - struct usbhs_fifo *fifo) +static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo, + int channel) { struct device *dev = usbhs_priv_to_dev(priv); if (dev->of_node) - usbhsf_dma_init_dt(dev, fifo); + usbhsf_dma_init_dt(dev, fifo, channel); else usbhsf_dma_init_pdev(fifo); @@ -1231,7 +1390,7 @@ do { \ usbhs_get_dparam(priv, d##channel##_tx_id); \ fifo->rx_slave.shdma_slave.slave_id = \ usbhs_get_dparam(priv, d##channel##_rx_id); \ - usbhsf_dma_init(priv, fifo); \ + usbhsf_dma_init(priv, fifo, channel); \ } while (0) #define USBHS_DFIFO_INIT(priv, fifo, channel) \ diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index f07037c1185f..04d3f8abad9e 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -58,6 +58,7 @@ struct usbhs_pkt { struct usbhs_pkt *pkt); struct work_struct work; dma_addr_t dma; + dma_cookie_t cookie; void *buf; int length; int trans; diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index e0384af77e56..dc2aa3261202 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -119,18 +119,34 @@ struct usbhsg_recip_handle { /* * queue push/pop */ -static void usbhsg_queue_pop(struct usbhsg_uep *uep, - struct usbhsg_request *ureq, - int status) +static void __usbhsg_queue_pop(struct usbhsg_uep *uep, + struct usbhsg_request *ureq, + int status) { struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe)); ureq->req.status = status; + spin_unlock(usbhs_priv_to_lock(priv)); usb_gadget_giveback_request(&uep->ep, &ureq->req); + spin_lock(usbhs_priv_to_lock(priv)); +} + +static void usbhsg_queue_pop(struct usbhsg_uep *uep, + struct usbhsg_request *ureq, + int status) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); + unsigned long flags; + + usbhs_lock(priv, flags); + __usbhsg_queue_pop(uep, ureq, status); + usbhs_unlock(priv, flags); } static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 007f45abe96c..4f9c3356127a 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -84,6 +84,17 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe, usbhs_bset(priv, pipe_reg, mask, val); } +static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe, + u16 dcp_reg, u16 pipe_reg) +{ + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + + if (usbhs_pipe_is_dcp(pipe)) + return usbhs_read(priv, dcp_reg); + else + return usbhs_read(priv, pipe_reg); +} + /* * DCPCFG/PIPECFG functions */ @@ -92,6 +103,11 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val) __usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val); } +static u16 usbhsp_pipe_cfg_get(struct usbhs_pipe *pipe) +{ + return __usbhsp_pipe_xxx_get(pipe, DCPCFG, PIPECFG); +} + /* * PIPEnTRN/PIPEnTRE functions */ @@ -616,6 +632,11 @@ void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) usbhsp_pipectrl_set(pipe, mask, val); } +static int usbhs_pipe_get_data_sequence(struct usbhs_pipe *pipe) +{ + return !!(usbhsp_pipectrl_get(pipe) & SQMON); +} + void usbhs_pipe_clear(struct usbhs_pipe *pipe) { if (usbhs_pipe_is_dcp(pipe)) { @@ -626,6 +647,24 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe) } } +void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable) +{ + int sequence; + + if (usbhs_pipe_is_dcp(pipe)) + return; + + usbhsp_pipe_select(pipe); + /* check if the driver needs to change the BFRE value */ + if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE))) + return; + + sequence = usbhs_pipe_get_data_sequence(pipe); + usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0); + usbhs_pipe_clear(pipe); + usbhs_pipe_data_sequence(pipe, sequence); +} + static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) { struct usbhs_pipe *pos, *pipe; diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index d24a05972370..b0bc7b603016 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -97,6 +97,7 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len); void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, u16 epnum, u16 maxp); +void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable); #define usbhs_pipe_sequence_data0(pipe) usbhs_pipe_data_sequence(pipe, 0) #define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1) |