提交 1c41a957 编写于 作者: G Greg Kroah-Hartman

Merge tag 'usb-for-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next

Felipe writes:

usb: patches for v4.1 merge window

As usual, a big pile of commits. This time a total
of 111 non-merge commits.

Other than the usual set of cleanups and non-critical
fixes, we have some interesting work for AM335x's MUSB
babble recovery. Now that takes a lot less time and we
don't have to Reset MUSB all the time.

The printer gadget has been converted to configfs interface
and the atmel udc has learned suspend/resume with wakeup.
Signed-off-by: NFelipe Balbi <balbi@ti.com>
What: /config/usb-gadget/gadget/functions/printer.name
Date: Apr 2015
KernelVersion: 4.1
Description:
The attributes:
pnp_string - Data to be passed to the host in pnp string
q_len - Number of requests per endpoint
...@@ -14,6 +14,7 @@ Optional properties: ...@@ -14,6 +14,7 @@ Optional properties:
- phys: from the *Generic PHY* bindings - phys: from the *Generic PHY* bindings
- phy-names: from the *Generic PHY* bindings - phy-names: from the *Generic PHY* bindings
- tx-fifo-resize: determines if the FIFO *has* to be reallocated. - tx-fifo-resize: determines if the FIFO *has* to be reallocated.
- snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
- snps,disable_scramble_quirk: true when SW should disable data scrambling. - snps,disable_scramble_quirk: true when SW should disable data scrambling.
Only really useful for FPGA builds. Only really useful for FPGA builds.
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled - snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
......
...@@ -15,7 +15,10 @@ Optional properties: ...@@ -15,7 +15,10 @@ Optional properties:
- phys: phandle + phy specifier pair - phys: phandle + phy specifier pair
- phy-names: must be "usb" - phy-names: must be "usb"
- dmas: Must contain a list of references to DMA specifiers. - dmas: Must contain a list of references to DMA specifiers.
- dma-names : Must contain a list of DMA names, "tx" or "rx". - dma-names : Must contain a list of DMA names:
- tx0 ... tx<n>
- rx0 ... rx<n>
- This <n> means DnFIFO in USBHS module.
Example: Example:
usbhs: usb@e6590000 { usbhs: usb@e6590000 {
......
...@@ -19,6 +19,7 @@ provided by gadgets. ...@@ -19,6 +19,7 @@ provided by gadgets.
16. UAC1 function 16. UAC1 function
17. UAC2 function 17. UAC2 function
18. UVC function 18. UVC function
19. PRINTER function
1. ACM function 1. ACM function
...@@ -726,3 +727,49 @@ with these patches: ...@@ -726,3 +727,49 @@ with these patches:
http://www.spinics.net/lists/linux-usb/msg99220.html http://www.spinics.net/lists/linux-usb/msg99220.html
host: luvcview -f yuv host: luvcview -f yuv
19. PRINTER function
====================
The function is provided by usb_f_printer.ko module.
Function-specific configfs interface
------------------------------------
The function name to use when creating the function directory is "printer".
The printer function provides these attributes in its function directory:
pnp_string - Data to be passed to the host in pnp string
q_len - Number of requests per endpoint
Testing the PRINTER function
----------------------------
The most basic testing:
device: run the gadget
# ls -l /devices/virtual/usb_printer_gadget/
should show g_printer<number>.
If udev is active, then /dev/g_printer<number> should appear automatically.
host:
If udev is active, then e.g. /dev/usb/lp0 should appear.
host->device transmission:
device:
# cat /dev/g_printer<number>
host:
# cat > /dev/usb/lp0
device->host transmission:
# cat > /dev/g_printer<number>
host:
# cat /dev/usb/lp0
More advanced testing can be done with the prn_example
described in Documentation/usb/gadget-printer.txt.
...@@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma) ...@@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
/* interrupt, error, port change, reset, sleep/suspend */ /* interrupt, error, port change, reset, sleep/suspend */
hw_write(ci, OP_USBINTR, ~0, hw_write(ci, OP_USBINTR, ~0,
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
} else { } else {
hw_write(ci, OP_USBINTR, ~0, 0); hw_write(ci, OP_USBINTR, ~0, 0);
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
} }
return 0; return 0;
} }
...@@ -1508,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) ...@@ -1508,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
hw_device_reset(ci); hw_device_reset(ci);
hw_device_state(ci, ci->ep0out->qh.dma); hw_device_state(ci, ci->ep0out->qh.dma);
usb_gadget_set_state(_gadget, USB_STATE_POWERED); usb_gadget_set_state(_gadget, USB_STATE_POWERED);
usb_udc_vbus_handler(_gadget, true);
} else { } else {
usb_udc_vbus_handler(_gadget, false);
if (ci->driver) if (ci->driver)
ci->driver->disconnect(&ci->gadget); ci->driver->disconnect(&ci->gadget);
hw_device_state(ci, 0); hw_device_state(ci, 0);
...@@ -1574,13 +1574,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) ...@@ -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); struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
if (!ci->vbus_active) pm_runtime_get_sync(&ci->gadget.dev);
return -EOPNOTSUPP;
if (is_on) if (is_on)
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
else else
hw_write(ci, OP_USBCMD, USBCMD_RS, 0); hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
pm_runtime_put_sync(&ci->gadget.dev);
return 0; return 0;
} }
...@@ -1710,6 +1709,7 @@ static int ci_udc_start(struct usb_gadget *gadget, ...@@ -1710,6 +1709,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
spin_lock_irqsave(&ci->lock, flags); spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci); hw_device_reset(ci);
} else { } else {
usb_udc_vbus_handler(&ci->gadget, false);
pm_runtime_put_sync(&ci->gadget.dev); pm_runtime_put_sync(&ci->gadget.dev);
return retval; return retval;
} }
......
...@@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM ...@@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM
config USB_DWC2_PCI config USB_DWC2_PCI
tristate "DWC2 PCI" tristate "DWC2 PCI"
depends on USB_DWC2_HOST && PCI depends on PCI
default USB_DWC2_HOST default n
select USB_DWC2_PLATFORM
select NOP_USB_XCEIV
help help
The Designware USB2.0 PCI interface module for controllers 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 config USB_DWC2_DEBUG
bool "Enable Debugging Messages" bool "Enable Debugging Messages"
......
...@@ -19,10 +19,8 @@ endif ...@@ -19,10 +19,8 @@ endif
# mode. The PCI bus interface module will called dwc2_pci.ko and the platform # mode. The PCI bus interface module will called dwc2_pci.ko and the platform
# interface module will be called dwc2_platform.ko. # interface module will be called dwc2_platform.ko.
ifneq ($(CONFIG_USB_DWC2_PCI),) obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o
obj-$(CONFIG_USB_DWC2) += dwc2_pci.o dwc2_pci-y := pci.o
dwc2_pci-y := pci.o
endif
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
dwc2_platform-y := platform.o dwc2_platform-y := platform.o
...@@ -593,6 +593,8 @@ struct dwc2_hsotg { ...@@ -593,6 +593,8 @@ struct dwc2_hsotg {
struct dwc2_core_params *core_params; struct dwc2_core_params *core_params;
enum usb_otg_state op_state; enum usb_otg_state op_state;
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1;
struct phy *phy; struct phy *phy;
struct usb_phy *uphy; struct usb_phy *uphy;
......
...@@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) ...@@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
*/ */
channel->qh = NULL; 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;
}
} }
/** /**
......
...@@ -50,113 +50,97 @@ ...@@ -50,113 +50,97 @@
#include <linux/usb/hcd.h> #include <linux/usb/hcd.h>
#include <linux/usb/ch11.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 #define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0
static const char dwc2_driver_name[] = "dwc2"; static const char dwc2_driver_name[] = "dwc2-pci";
static const struct dwc2_core_params dwc2_module_params = { struct dwc2_pci_glue {
.otg_cap = -1, struct platform_device *dwc2;
.otg_ver = -1, struct platform_device *phy;
.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 void dwc2_pci_remove(struct pci_dev *pci)
* 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)
{ {
struct dwc2_hsotg *hsotg = pci_get_drvdata(dev); struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
dwc2_hcd_remove(hsotg); platform_device_unregister(glue->dwc2);
pci_disable_device(dev); usb_phy_generic_unregister(glue->phy);
kfree(glue);
pci_set_drvdata(pci, NULL);
} }
/** static int dwc2_pci_probe(struct pci_dev *pci,
* 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) const struct pci_device_id *id)
{ {
struct dwc2_hsotg *hsotg; struct resource res[2];
int retval; 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); dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
if (!hsotg) if (!dwc2) {
dev_err(dev, "couldn't allocate dwc2 device\n");
return -ENOMEM; return -ENOMEM;
}
hsotg->dev = &dev->dev; memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
hsotg->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]);
if (IS_ERR(hsotg->regs))
return PTR_ERR(hsotg->regs);
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", res[0].start = pci_resource_start(pci, 0);
(unsigned long)pci_resource_start(dev, 0), hsotg->regs); res[0].end = pci_resource_end(pci, 0);
res[0].name = "dwc2";
res[0].flags = IORESOURCE_MEM;
if (pci_enable_device(dev) < 0) res[1].start = pci->irq;
return -ENODEV; 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->dev.parent = dev;
dwc2_handle_common_intr, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (retval)
return retval;
spin_lock_init(&hsotg->lock); phy = usb_phy_generic_register();
retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params); if (IS_ERR(phy)) {
if (retval) { dev_err(dev, "error registering generic PHY (%ld)\n",
pci_disable_device(dev); PTR_ERR(phy));
return retval; 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[] = { static const struct pci_device_id dwc2_pci_ids[] = {
...@@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids); ...@@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
static struct pci_driver dwc2_pci_driver = { static struct pci_driver dwc2_pci_driver = {
.name = dwc2_driver_name, .name = dwc2_driver_name,
.id_table = dwc2_pci_ids, .id_table = dwc2_pci_ids,
.probe = dwc2_driver_probe, .probe = dwc2_pci_probe,
.remove = dwc2_driver_remove, .remove = dwc2_pci_remove,
}; };
module_pci_driver(dwc2_pci_driver); module_pci_driver(dwc2_pci_driver);
......
...@@ -121,7 +121,9 @@ static int dwc2_driver_remove(struct platform_device *dev) ...@@ -121,7 +121,9 @@ static int dwc2_driver_remove(struct platform_device *dev)
{ {
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
if (hsotg->hcd_enabled)
dwc2_hcd_remove(hsotg); dwc2_hcd_remove(hsotg);
if (hsotg->gadget_enabled)
s3c_hsotg_remove(hsotg); s3c_hsotg_remove(hsotg);
return 0; return 0;
...@@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev) ...@@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev)
spin_lock_init(&hsotg->lock); spin_lock_init(&hsotg->lock);
mutex_init(&hsotg->init_mutex); mutex_init(&hsotg->init_mutex);
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg, irq); retval = dwc2_gadget_init(hsotg, irq);
if (retval) if (retval)
return retval; return retval;
hsotg->gadget_enabled = 1;
}
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
retval = dwc2_hcd_init(hsotg, irq, params); retval = dwc2_hcd_init(hsotg, irq, params);
if (retval) if (retval) {
if (hsotg->gadget_enabled)
s3c_hsotg_remove(hsotg);
return retval; return retval;
}
hsotg->hcd_enabled = 1;
}
platform_set_drvdata(dev, hsotg); platform_set_drvdata(dev, hsotg);
......
...@@ -104,11 +104,4 @@ config USB_DWC3_DEBUG ...@@ -104,11 +104,4 @@ config USB_DWC3_DEBUG
help help
Say Y here to enable debugging messages on DWC3 Driver. 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 endif
...@@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev)
* since it will be requested by the xhci-plat driver. * since it will be requested by the xhci-plat driver.
*/ */
regs = devm_ioremap_resource(dev, res); regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs)) if (IS_ERR(regs)) {
return PTR_ERR(regs); ret = PTR_ERR(regs);
goto err0;
}
dwc->regs = regs; dwc->regs = regs;
dwc->regs_size = resource_size(res); 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 */ /* default to highest possible threshold */
lpm_nyet_threshold = 0xff; lpm_nyet_threshold = 0xff;
...@@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,is-utmi-l1-suspend"); "snps,is-utmi-l1-suspend");
of_property_read_u8(node, "snps,hird-threshold", of_property_read_u8(node, "snps,hird-threshold",
&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, dwc->needs_fifo_resize = of_property_read_bool(node,
"tx-fifo-resize"); "tx-fifo-resize");
...@@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev)
hird_threshold = pdata->hird_threshold; hird_threshold = pdata->hird_threshold;
dwc->needs_fifo_resize = pdata->tx_fifo_resize; dwc->needs_fifo_resize = pdata->tx_fifo_resize;
dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
dwc->dr_mode = pdata->dr_mode; dwc->dr_mode = pdata->dr_mode;
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk; dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
...@@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_get_phy(dwc); ret = dwc3_core_get_phy(dwc);
if (ret) if (ret)
return ret; goto err0;
spin_lock_init(&dwc->lock); spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc); platform_set_drvdata(pdev, dwc);
...@@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n"); dev_err(dwc->dev, "failed to allocate event buffers\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err0; goto err1;
} }
if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
...@@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev) ...@@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev)
ret = dwc3_core_init(dwc); ret = dwc3_core_init(dwc);
if (ret) { if (ret) {
dev_err(dev, "failed to initialize core\n"); 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->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy); ret = phy_power_on(dwc->usb2_generic_phy);
if (ret < 0) if (ret < 0)
goto err1; goto err2;
ret = phy_power_on(dwc->usb3_generic_phy); ret = phy_power_on(dwc->usb3_generic_phy);
if (ret < 0) if (ret < 0)
goto err_usb2phy_power; goto err3;
ret = dwc3_event_buffers_setup(dwc); ret = dwc3_event_buffers_setup(dwc);
if (ret) { if (ret) {
dev_err(dwc->dev, "failed to setup event buffers\n"); dev_err(dwc->dev, "failed to setup event buffers\n");
goto err_usb3phy_power; goto err4;
} }
ret = dwc3_core_init_mode(dwc); ret = dwc3_core_init_mode(dwc);
if (ret) if (ret)
goto err2; goto err5;
ret = dwc3_debugfs_init(dwc); ret = dwc3_debugfs_init(dwc);
if (ret) { if (ret) {
dev_err(dev, "failed to initialize debugfs\n"); dev_err(dev, "failed to initialize debugfs\n");
goto err3; goto err6;
} }
pm_runtime_allow(dev); pm_runtime_allow(dev);
return 0; return 0;
err3: err6:
dwc3_core_exit_mode(dwc); dwc3_core_exit_mode(dwc);
err2: err5:
dwc3_event_buffers_cleanup(dwc); dwc3_event_buffers_cleanup(dwc);
err_usb3phy_power: err4:
phy_power_off(dwc->usb3_generic_phy); phy_power_off(dwc->usb3_generic_phy);
err_usb2phy_power: err3:
phy_power_off(dwc->usb2_generic_phy); phy_power_off(dwc->usb2_generic_phy);
err1: err2:
usb_phy_set_suspend(dwc->usb2_phy, 1); usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1); usb_phy_set_suspend(dwc->usb3_phy, 1);
dwc3_core_exit(dwc); dwc3_core_exit(dwc);
err0: err1:
dwc3_free_event_buffers(dwc); 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; return ret;
} }
static int dwc3_remove(struct platform_device *pdev) static int dwc3_remove(struct platform_device *pdev)
{ {
struct dwc3 *dwc = platform_get_drvdata(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_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc); dwc3_core_exit_mode(dwc);
......
...@@ -689,6 +689,7 @@ struct dwc3_scratchpad_array { ...@@ -689,6 +689,7 @@ struct dwc3_scratchpad_array {
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued * @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup * @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 * @disable_scramble_quirk: set if we enable the disable scramble quirk
* @u2exit_lfps_quirk: set if we enable u2exit lfps 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 * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
...@@ -812,6 +813,7 @@ struct dwc3 { ...@@ -812,6 +813,7 @@ struct dwc3 {
unsigned setup_packet_pending:1; unsigned setup_packet_pending:1;
unsigned start_config_issued:1; unsigned start_config_issued:1;
unsigned three_stage_setup:1; unsigned three_stage_setup:1;
unsigned usb3_lpm_capable:1;
unsigned disable_scramble_quirk:1; unsigned disable_scramble_quirk:1;
unsigned u2exit_lfps_quirk:1; unsigned u2exit_lfps_quirk:1;
......
...@@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) ...@@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
return IRQ_HANDLED; 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) static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
{ {
u32 reg; u32 reg;
...@@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev) ...@@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev)
if (omap->extcon_id_dev.edev) if (omap->extcon_id_dev.edev)
extcon_unregister_interest(&omap->extcon_id_dev); extcon_unregister_interest(&omap->extcon_id_dev);
dwc3_omap_disable_irqs(omap); 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_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
......
...@@ -24,8 +24,6 @@ ...@@ -24,8 +24,6 @@
#include "platform_data.h" #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_SYNOPSYS_HAPSUSB3 0xabcd
#define PCI_DEVICE_ID_INTEL_BYT 0x0f37 #define PCI_DEVICE_ID_INTEL_BYT 0x0f37
#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
......
...@@ -1855,7 +1855,6 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, ...@@ -1855,7 +1855,6 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int i; unsigned int i;
int ret; int ret;
do {
req = next_request(&dep->req_queued); req = next_request(&dep->req_queued);
if (!req) { if (!req) {
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
...@@ -1874,14 +1873,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, ...@@ -1874,14 +1873,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
event, status); event, status);
if (ret) if (ret)
break; break;
}while (++i < req->request.num_mapped_sgs); } while (++i < req->request.num_mapped_sgs);
dwc3_gadget_giveback(dep, req, status); dwc3_gadget_giveback(dep, req, status);
if (ret)
break;
} while (1);
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->req_queued)) { list_empty(&dep->req_queued)) {
if (list_empty(&dep->request_list)) { if (list_empty(&dep->request_list)) {
......
...@@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc) ...@@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(&pdata, 0, sizeof(pdata)); memset(&pdata, 0, sizeof(pdata));
#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
pdata.usb3_lpm_capable = 1;
#endif
ret = platform_device_add_data(xhci, &pdata, sizeof(pdata)); ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
if (ret) { if (ret) {
......
...@@ -24,6 +24,7 @@ struct dwc3_platform_data { ...@@ -24,6 +24,7 @@ struct dwc3_platform_data {
enum usb_device_speed maximum_speed; enum usb_device_speed maximum_speed;
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
bool tx_fifo_resize; bool tx_fifo_resize;
bool usb3_lpm_capable;
unsigned is_utmi_l1_suspend:1; unsigned is_utmi_l1_suspend:1;
u8 hird_threshold; u8 hird_threshold;
......
...@@ -196,6 +196,9 @@ config USB_F_MIDI ...@@ -196,6 +196,9 @@ config USB_F_MIDI
config USB_F_HID config USB_F_HID
tristate tristate
config USB_F_PRINTER
tristate
choice choice
tristate "USB Gadget Drivers" tristate "USB Gadget Drivers"
default USB_ETH default USB_ETH
...@@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC ...@@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC
device. It provides a userspace API to process UVC control requests device. It provides a userspace API to process UVC control requests
and stream video data to the host. 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" source "drivers/usb/gadget/legacy/Kconfig"
endchoice endchoice
......
...@@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings( ...@@ -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 * 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 * 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 * 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 * The ->language pointer of each struct usb_gadget_strings has to contain the
* same amount of entries. * same amount of entries.
* For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first * 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. * entry of en-US. Therefore both entries become the same id assign.
*/ */
struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev, 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) ...@@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
req->length = 0; req->length = 0;
gadget->ep0->driver_data = cdev; 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) { switch (ctrl->bRequest) {
/* we handle all standard USB descriptors */ /* we handle all standard USB descriptors */
...@@ -1751,6 +1758,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1751,6 +1758,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
* take such requests too, if that's ever needed: to work * take such requests too, if that's ever needed: to work
* in config 0, etc. * 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) { switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE: case USB_RECIP_INTERFACE:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES) if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
...@@ -1768,7 +1779,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ...@@ -1768,7 +1779,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
f = NULL; f = NULL;
break; break;
} }
try_fun_setup:
if (f && f->setup) if (f && f->setup)
value = f->setup(f, ctrl); value = f->setup(f, ctrl);
else { else {
......
...@@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o ...@@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
usb_f_hid-y := f_hid.o usb_f_hid-y := f_hid.o
obj-$(CONFIG_USB_F_HID) += usb_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
...@@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) ...@@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
/* disable/free request and end point */ /* disable/free request and end point */
usb_ep_disable(hidg->in_ep); usb_ep_disable(hidg->in_ep);
usb_ep_dequeue(hidg->in_ep, hidg->req);
kfree(hidg->req->buf); kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req); usb_ep_free_request(hidg->in_ep, hidg->req);
......
...@@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) ...@@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
if (!curlun) { /* Unsupported LUNs are okay */ if (!curlun) { /* Unsupported LUNs are okay */
common->bad_lun_okay = 1; common->bad_lun_okay = 1;
memset(buf, 0, 36); 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 */ buf[4] = 31; /* Additional length */
return 36; return 36;
} }
......
/*
* 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(&current_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(&current_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;
}
/*
* 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 */
...@@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch) ...@@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
unsigned long flags; unsigned long flags;
int status; 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)); port->port_num, tty, ch, __builtin_return_address(0));
spin_lock_irqsave(&port->port_lock, flags); spin_lock_irqsave(&port->port_lock, flags);
......
...@@ -301,6 +301,7 @@ config USB_MIDI_GADGET ...@@ -301,6 +301,7 @@ config USB_MIDI_GADGET
config USB_G_PRINTER config USB_G_PRINTER
tristate "Printer Gadget" tristate "Printer Gadget"
select USB_LIBCOMPOSITE select USB_LIBCOMPOSITE
select USB_F_PRINTER
help help
The Printer Gadget channels data between the USB host and a The Printer Gadget channels data between the USB host and a
userspace program driving the print engine. The user space userspace program driving the print engine. The user space
......
...@@ -12,29 +12,7 @@ ...@@ -12,29 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.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 <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/ch9.h>
#include <linux/usb/composite.h> #include <linux/usb/composite.h>
...@@ -46,50 +24,12 @@ ...@@ -46,50 +24,12 @@
USB_GADGET_COMPOSITE_OPTIONS(); USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_DESC "Printer Gadget" #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 shortname [] = "printer";
static const char driver_desc [] = DRIVER_DESC; static const char driver_desc [] = DRIVER_DESC;
static dev_t g_printer_devno; #include "u_printer.h"
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;
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -120,6 +60,9 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR); ...@@ -120,6 +60,9 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
#define QLEN qlen #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); ...@@ -127,10 +70,6 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
* descriptors are built on demand. * 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 = { static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc, .bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE, .bDescriptorType = USB_DT_DEVICE,
...@@ -143,108 +82,6 @@ static struct usb_device_descriptor device_desc = { ...@@ -143,108 +82,6 @@ static struct usb_device_descriptor device_desc = {
.bNumConfigurations = 1 .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 = { static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor, .bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG, .bDescriptorType = USB_DT_OTG,
...@@ -256,29 +93,13 @@ static const struct usb_descriptor_header *otg_desc[] = { ...@@ -256,29 +93,13 @@ static const struct usb_descriptor_header *otg_desc[] = {
NULL, 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 */ /* descriptors that are built on-demand */
static char product_desc [40] = DRIVER_DESC; static char product_desc [40] = DRIVER_DESC;
static char serial_num [40] = "1"; 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;"; "XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
/* static strings, in UTF-8 */ /* static strings, in UTF-8 */
...@@ -299,921 +120,19 @@ static struct usb_gadget_strings *dev_strings[] = { ...@@ -299,921 +120,19 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL, 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(&current_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(&current_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 = { static struct usb_configuration printer_cfg_driver = {
.label = "printer", .label = "printer",
.unbind = printer_cfg_unbind,
.bConfigurationValue = 1, .bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .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 usb_gadget *gadget = c->cdev->gadget;
struct printer_dev *dev; int status = 0;
int status = -ENOMEM;
size_t len;
u32 i;
struct usb_request *req;
usb_ep_autoconfig_reset(gadget); 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); usb_gadget_set_selfpowered(gadget);
if (gadget_is_otg(gadget)) { if (gadget_is_otg(gadget)) {
...@@ -1222,86 +141,64 @@ static int __init printer_bind_config(struct usb_configuration *c) ...@@ -1222,86 +141,64 @@ static int __init printer_bind_config(struct usb_configuration *c)
printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
} }
spin_lock_init(&dev->lock); f_printer = usb_get_function(fi_printer);
mutex_init(&dev->lock_printer_io); if (IS_ERR(f_printer))
INIT_LIST_HEAD(&dev->tx_reqs); return PTR_ERR(f_printer);
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;
INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); status = usb_add_function(c, f_printer);
return 0; if (status < 0)
usb_put_function(f_printer);
fail:
printer_cfg_unbind(c);
return status; return status;
} }
static int printer_unbind(struct usb_composite_dev *cdev)
{
return 0;
}
static int __init printer_bind(struct usb_composite_dev *cdev) 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); ret = usb_string_ids_tab(cdev, strings);
if (ret < 0) if (ret < 0) {
usb_put_function_instance(fi_printer);
return ret; return ret;
}
device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id; device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id; device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id; device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config); ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
if (ret) if (ret) {
usb_put_function_instance(fi_printer);
return ret; return ret;
}
usb_composite_overwrite_options(cdev, &coverwrite); usb_composite_overwrite_options(cdev, &coverwrite);
return ret; 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 = { static __refdata struct usb_composite_driver printer_driver = {
.name = shortname, .name = shortname,
.dev = &device_desc, .dev = &device_desc,
...@@ -1311,47 +208,7 @@ static __refdata struct usb_composite_driver printer_driver = { ...@@ -1311,47 +208,7 @@ static __refdata struct usb_composite_driver printer_driver = {
.unbind = printer_unbind, .unbind = printer_unbind,
}; };
static int __init module_usb_composite_driver(printer_driver);
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_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Craig Nadler"); MODULE_AUTHOR("Craig Nadler");
......
...@@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file) ...@@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file)
spin_lock_irq(&udc->lock); spin_lock_irq(&udc->lock);
for (i = 0; i < inode->i_size / 4; i++) 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); spin_unlock_irq(&udc->lock);
file->private_data = data; file->private_data = data;
...@@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep, ...@@ -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))) if (crq->wLength != cpu_to_le16(sizeof(status)))
goto stall; goto stall;
ep->state = DATA_STAGE_IN; 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); usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
break; break;
} }
...@@ -1739,43 +1739,95 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) ...@@ -1739,43 +1739,95 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t usba_vbus_irq(int irq, void *devid) static int start_clock(struct usba_udc *udc)
{ {
struct usba_udc *udc = devid; int ret;
int vbus;
/* debounce */ if (udc->clocked)
udelay(10); return 0;
spin_lock(&udc->lock); 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;
}
/* May happen if Vbus pin toggles during probe() */ udc->clocked = true;
if (!udc->driver) return 0;
goto out; }
vbus = vbus_is_present(udc); static void stop_clock(struct usba_udc *udc)
if (vbus != udc->vbus_prev) { {
if (vbus) { 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); toggle_bias(udc, 1);
usba_writel(udc, CTRL, USBA_ENABLE_MASK); usba_writel(udc, CTRL, USBA_ENABLE_MASK);
usba_int_enb_set(udc, USBA_END_OF_RESET); usba_int_enb_set(udc, USBA_END_OF_RESET);
} else { 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; udc->gadget.speed = USB_SPEED_UNKNOWN;
reset_all_endpoints(udc); reset_all_endpoints(udc);
/* This will also disable the DP pullup */
toggle_bias(udc, 0); toggle_bias(udc, 0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK); usba_writel(udc, CTRL, USBA_DISABLE_MASK);
if (udc->driver->disconnect) { spin_unlock_irqrestore(&udc->lock, flags);
spin_unlock(&udc->lock);
stop_clock(udc);
}
static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
{
struct usba_udc *udc = devid;
int vbus;
/* debounce */
udelay(10);
mutex_lock(&udc->vbus_mutex);
vbus = vbus_is_present(udc);
if (vbus != udc->vbus_prev) {
if (vbus) {
usba_start(udc);
} else {
usba_stop(udc);
if (udc->driver->disconnect)
udc->driver->disconnect(&udc->gadget); udc->driver->disconnect(&udc->gadget);
spin_lock(&udc->lock);
}
} }
udc->vbus_prev = vbus; udc->vbus_prev = vbus;
} }
out: mutex_unlock(&udc->vbus_mutex);
spin_unlock(&udc->lock);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget, ...@@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget,
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&udc->lock, flags); spin_lock_irqsave(&udc->lock, flags);
udc->devstatus = 1 << USB_DEVICE_SELF_POWERED; udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
udc->driver = driver; udc->driver = driver;
spin_unlock_irqrestore(&udc->lock, flags); spin_unlock_irqrestore(&udc->lock, flags);
ret = clk_prepare_enable(udc->pclk); mutex_lock(&udc->vbus_mutex);
if (ret)
return ret;
ret = clk_prepare_enable(udc->hclk);
if (ret) {
clk_disable_unprepare(udc->pclk);
return ret;
}
udc->vbus_prev = 0;
if (gpio_is_valid(udc->vbus_pin)) if (gpio_is_valid(udc->vbus_pin))
enable_irq(gpio_to_irq(udc->vbus_pin)); enable_irq(gpio_to_irq(udc->vbus_pin));
/* If Vbus is present, enable the controller and wait for reset */ /* If Vbus is present, enable the controller and wait for reset */
spin_lock_irqsave(&udc->lock, flags); udc->vbus_prev = vbus_is_present(udc);
if (vbus_is_present(udc) && udc->vbus_prev == 0) { if (udc->vbus_prev) {
toggle_bias(udc, 1); ret = usba_start(udc);
usba_writel(udc, CTRL, USBA_ENABLE_MASK); if (ret)
usba_int_enb_set(udc, USBA_END_OF_RESET); goto err;
} }
spin_unlock_irqrestore(&udc->lock, flags);
mutex_unlock(&udc->vbus_mutex);
return 0; 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) static int atmel_usba_stop(struct usb_gadget *gadget)
{ {
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
unsigned long flags;
if (gpio_is_valid(udc->vbus_pin)) if (gpio_is_valid(udc->vbus_pin))
disable_irq(gpio_to_irq(udc->vbus_pin)); disable_irq(gpio_to_irq(udc->vbus_pin));
spin_lock_irqsave(&udc->lock, flags); usba_stop(udc);
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);
udc->driver = NULL; udc->driver = NULL;
...@@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev) ...@@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev)
return PTR_ERR(hclk); return PTR_ERR(hclk);
spin_lock_init(&udc->lock); spin_lock_init(&udc->lock);
mutex_init(&udc->vbus_mutex);
udc->pdev = pdev; udc->pdev = pdev;
udc->pclk = pclk; udc->pclk = pclk;
udc->hclk = hclk; udc->hclk = hclk;
...@@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev) ...@@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev)
if (gpio_is_valid(udc->vbus_pin)) { if (gpio_is_valid(udc->vbus_pin)) {
if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
ret = devm_request_irq(&pdev->dev, irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
gpio_to_irq(udc->vbus_pin), IRQ_NOAUTOEN);
usba_vbus_irq, 0, ret = devm_request_threaded_irq(&pdev->dev,
gpio_to_irq(udc->vbus_pin), NULL,
usba_vbus_irq_thread, IRQF_ONESHOT,
"atmel_usba_udc", udc); "atmel_usba_udc", udc);
if (ret) { if (ret) {
udc->vbus_pin = -ENODEV; udc->vbus_pin = -ENODEV;
dev_warn(&udc->pdev->dev, dev_warn(&udc->pdev->dev,
"failed to request vbus irq; " "failed to request vbus irq; "
"assuming always on\n"); "assuming always on\n");
} else {
disable_irq(gpio_to_irq(udc->vbus_pin));
} }
} else { } else {
/* gpio_request fail so use -EINVAL for gpio_is_valid */ /* gpio_request fail so use -EINVAL for gpio_is_valid */
...@@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev) ...@@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev)
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (ret) if (ret)
return ret; return ret;
device_init_wakeup(&pdev->dev, 1);
usba_init_debugfs(udc); usba_init_debugfs(udc);
for (i = 1; i < udc->num_ep; i++) for (i = 1; i < udc->num_ep; i++)
...@@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev) ...@@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
udc = platform_get_drvdata(pdev); udc = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
usb_del_gadget_udc(&udc->gadget); usb_del_gadget_udc(&udc->gadget);
for (i = 1; i < udc->num_ep; i++) for (i = 1; i < udc->num_ep; i++)
...@@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev) ...@@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver udc_driver = {
.remove = __exit_p(usba_udc_remove), .remove = __exit_p(usba_udc_remove),
.driver = { .driver = {
.name = "atmel_usba_udc", .name = "atmel_usba_udc",
.pm = &usba_udc_pm_ops,
.of_match_table = of_match_ptr(atmel_udc_dt_ids), .of_match_table = of_match_ptr(atmel_udc_dt_ids),
}, },
}; };
......
...@@ -191,18 +191,28 @@ ...@@ -191,18 +191,28 @@
| USBA_BF(name, value)) | USBA_BF(name, value))
/* Register access macros */ /* 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) \ #define usba_readl(udc, reg) \
__raw_readl((udc)->regs + USBA_##reg) usba_io_readl((udc)->regs + USBA_##reg)
#define usba_writel(udc, reg, value) \ #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) \ #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) \ #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) \ #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) \ #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 */ /* Calculate base address for a given endpoint or DMA controller */
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20) #define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
...@@ -313,6 +323,9 @@ struct usba_udc { ...@@ -313,6 +323,9 @@ struct usba_udc {
/* Protect hw registers from concurrent modifications */ /* Protect hw registers from concurrent modifications */
spinlock_t lock; spinlock_t lock;
/* Mutex to prevent concurrent start or stop */
struct mutex vbus_mutex;
void __iomem *regs; void __iomem *regs;
void __iomem *fifo; void __iomem *fifo;
...@@ -328,6 +341,7 @@ struct usba_udc { ...@@ -328,6 +341,7 @@ struct usba_udc {
struct clk *hclk; struct clk *hclk;
struct usba_ep *usba_ep; struct usba_ep *usba_ep;
bool bias_pulse_needed; bool bias_pulse_needed;
bool clocked;
u16 devstatus; u16 devstatus;
......
...@@ -2631,7 +2631,7 @@ static int __init init(void) ...@@ -2631,7 +2631,7 @@ static int __init init(void)
return -EINVAL; return -EINVAL;
if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) { 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); MAX_NUM_UDC);
return -EINVAL; return -EINVAL;
} }
......
...@@ -1024,8 +1024,7 @@ static const char proc_node_name [] = "driver/udc"; ...@@ -1024,8 +1024,7 @@ static const char proc_node_name [] = "driver/udc";
static void dump_intmask(struct seq_file *m, const char *label, u32 mask) static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
{ {
/* int_status is the same format ... */ /* int_status is the same format ... */
seq_printf(m, seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
"%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
label, mask, label, mask,
(mask & INT_PWRDETECT) ? " power" : "", (mask & INT_PWRDETECT) ? " power" : "",
(mask & INT_SYSERROR) ? " sys" : "", (mask & INT_SYSERROR) ? " sys" : "",
...@@ -1053,6 +1052,51 @@ static void dump_intmask(struct seq_file *m, const char *label, u32 mask) ...@@ -1053,6 +1052,51 @@ static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
(mask & INT_SUSPEND) ? " suspend" : ""); (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) static int udc_proc_read(struct seq_file *m, void *v)
{ {
...@@ -1079,18 +1123,7 @@ static int udc_proc_read(struct seq_file *m, void *v) ...@@ -1079,18 +1123,7 @@ static int udc_proc_read(struct seq_file *m, void *v)
is_usb_connected is_usb_connected
? ((tmp & PW_PULLUP) ? "full speed" : "powered") ? ((tmp & PW_PULLUP) ? "full speed" : "powered")
: "disconnected", : "disconnected",
({const char *state; udc_ep_state(dev->ep0state));
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; })
);
dump_intmask(m, "int_status", readl(&regs->int_status)); dump_intmask(m, "int_status", readl(&regs->int_status));
dump_intmask(m, "int_enable", readl(&regs->int_enable)); dump_intmask(m, "int_enable", readl(&regs->int_enable));
...@@ -1099,17 +1132,17 @@ static int udc_proc_read(struct seq_file *m, void *v) ...@@ -1099,17 +1132,17 @@ static int udc_proc_read(struct seq_file *m, void *v)
goto done; goto done;
/* registers for (active) device and ep0 */ /* registers for (active) device and ep0 */
if (seq_printf(m, "\nirqs %lu\ndataset %02x " seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n",
"single.bcs %02x.%02x state %x addr %u\n",
dev->irqs, readl(&regs->DataSet), dev->irqs, readl(&regs->DataSet),
readl(&regs->EPxSingle), readl(&regs->EPxBCS), readl(&regs->EPxSingle), readl(&regs->EPxBCS),
readl(&regs->UsbState), readl(&regs->UsbState),
readl(&regs->address)) < 0) readl(&regs->address));
if (seq_has_overflowed(m))
goto done; goto done;
tmp = readl(&regs->dma_master); tmp = readl(&regs->dma_master);
if (seq_printf(m, seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n",
"dma %03X =" EIGHTBITS "%s %s\n", tmp, tmp,
(tmp & MST_EOPB_DIS) ? " eopb-" : "", (tmp & MST_EOPB_DIS) ? " eopb-" : "",
(tmp & MST_EOPB_ENA) ? " eopb+" : "", (tmp & MST_EOPB_ENA) ? " eopb+" : "",
(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "", (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
...@@ -1121,9 +1154,8 @@ static int udc_proc_read(struct seq_file *m, void *v) ...@@ -1121,9 +1154,8 @@ static int udc_proc_read(struct seq_file *m, void *v)
(tmp & MST_RD_ENA) ? " IN" : "", (tmp & MST_RD_ENA) ? " IN" : "",
(tmp & MST_WR_ENA) ? " OUT" : "", (tmp & MST_WR_ENA) ? " OUT" : "",
(tmp & MST_CONNECTION) (tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in");
? "ep1in/ep2out" if (seq_has_overflowed(m))
: "ep1out/ep2in") < 0)
goto done; goto done;
/* dump endpoint queues */ /* dump endpoint queues */
...@@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v) ...@@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v)
continue; continue;
tmp = readl(ep->reg_status); tmp = readl(ep->reg_status);
if (seq_printf(m, seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n",
"%s %s max %u %s, irqs %lu, "
"status %02x (%s) " FOURBITS "\n",
ep->ep.name, ep->ep.name,
ep->is_in ? "in" : "out", ep->is_in ? "in" : "out",
ep->ep.maxpacket, ep->ep.maxpacket,
ep->dma ? "dma" : "pio", ep->dma ? "dma" : "pio",
ep->irqs, ep->irqs,
tmp, ({ char *s; tmp, udc_ep_status(tmp),
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_TOGGLE) ? "data1" : "data0",
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "", (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "", (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "" (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "");
) < 0) if (seq_has_overflowed(m))
goto done; goto done;
if (list_empty(&ep->queue)) { 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; goto done;
continue; continue;
} }
...@@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v) ...@@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v)
} else } else
tmp = req->req.actual; tmp = req->req.actual;
if (seq_printf(m, seq_printf(m, "\treq %p len %u/%u buf %p\n",
"\treq %p len %u/%u buf %p\n",
&req->req, tmp, req->req.length, &req->req, tmp, req->req.length,
req->req.buf) < 0) req->req.buf);
if (seq_has_overflowed(m))
goto done; goto done;
} }
} }
......
...@@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep, ...@@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
req = container_of(_req, struct lpc32xx_request, req); req = container_of(_req, struct lpc32xx_request, req);
ep = container_of(_ep, struct lpc32xx_ep, ep); 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)) !list_empty(&req->queue))
return -EINVAL; return -EINVAL;
udc = ep->udc; udc = ep->udc;
if (!_ep) { if (udc->gadget.speed == USB_SPEED_UNKNOWN)
dev_dbg(udc->dev, "invalid ep\n"); return -EPIPE;
return -EINVAL;
}
if ((!udc) || (!udc->driver) ||
(udc->gadget.speed == USB_SPEED_UNKNOWN)) {
dev_dbg(udc->dev, "invalid device\n");
return -EINVAL;
}
if (ep->lep) { if (ep->lep) {
struct lpc32xx_usbd_dd_gad *dd; struct lpc32xx_usbd_dd_gad *dd;
......
...@@ -80,6 +80,13 @@ static const char *const ep_name[] = { ...@@ -80,6 +80,13 @@ static const char *const ep_name[] = {
"ep-e", "ep-f", "ep-g", "ep-h", "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 0 == ep-{a,b,c,d} 1K fifo each
* mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable * 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 * 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) ...@@ -138,31 +145,44 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
u32 max, tmp; u32 max, tmp;
unsigned long flags; unsigned long flags;
static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; 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); ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || !desc || ep->desc || _ep->name == ep0name || 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; return -EINVAL;
}
dev = ep->dev; dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
return -ESHUTDOWN; ret = -ESHUTDOWN;
goto print_err;
}
/* erratum 0119 workaround ties up an endpoint number */ /* erratum 0119 workaround ties up an endpoint number */
if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) {
return -EDOM; ret = -EDOM;
goto print_err;
}
if (dev->quirks & PLX_SUPERSPEED) { if (dev->quirks & PLX_SUPERSPEED) {
if ((desc->bEndpointAddress & 0x0f) >= 0x0c) if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
return -EDOM; ret = -EDOM;
goto print_err;
}
ep->is_in = !!usb_endpoint_dir_in(desc); ep->is_in = !!usb_endpoint_dir_in(desc);
if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) {
return -EINVAL; ret = -EINVAL;
goto print_err;
}
} }
/* sanity check ep-e/ep-f since their fifos are small */ /* sanity check ep-e/ep-f since their fifos are small */
max = usb_endpoint_maxp(desc) & 0x1fff; max = usb_endpoint_maxp(desc) & 0x1fff;
if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) {
return -ERANGE; ret = -ERANGE;
goto print_err;
}
spin_lock_irqsave(&dev->lock, flags); spin_lock_irqsave(&dev->lock, flags);
_ep->maxpacket = max & 0x7ff; _ep->maxpacket = max & 0x7ff;
...@@ -192,7 +212,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) ...@@ -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_HIGH && max != 512) ||
(dev->gadget.speed == USB_SPEED_FULL && max > 64)) { (dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
spin_unlock_irqrestore(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
return -ERANGE; ret = -ERANGE;
goto print_err;
} }
} }
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
...@@ -271,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) ...@@ -271,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
/* pci writes may still be posted */ /* pci writes may still be posted */
spin_unlock_irqrestore(&dev->lock, flags); 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) static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
...@@ -426,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep) ...@@ -426,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep)
unsigned long flags; unsigned long flags;
ep = container_of(_ep, struct net2280_ep, ep); 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; return -EINVAL;
}
spin_lock_irqsave(&ep->dev->lock, flags); spin_lock_irqsave(&ep->dev->lock, flags);
nuke(ep); nuke(ep);
...@@ -458,8 +484,10 @@ static struct usb_request ...@@ -458,8 +484,10 @@ static struct usb_request
struct net2280_ep *ep; struct net2280_ep *ep;
struct net2280_request *req; struct net2280_request *req;
if (!_ep) if (!_ep) {
pr_err("%s: Invalid ep\n", __func__);
return NULL; return NULL;
}
ep = container_of(_ep, struct net2280_ep, ep); ep = container_of(_ep, struct net2280_ep, ep);
req = kzalloc(sizeof(*req), gfp_flags); req = kzalloc(sizeof(*req), gfp_flags);
...@@ -491,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) ...@@ -491,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
struct net2280_request *req; struct net2280_request *req;
ep = container_of(_ep, struct net2280_ep, ep); 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; return;
}
req = container_of(_req, struct net2280_request, req); req = container_of(_req, struct net2280_request, req);
WARN_ON(!list_empty(&req->queue)); WARN_ON(!list_empty(&req->queue));
...@@ -896,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) ...@@ -896,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
struct net2280_ep *ep; struct net2280_ep *ep;
struct net2280 *dev; struct net2280 *dev;
unsigned long flags; unsigned long flags;
int ret = 0;
/* we always require a cpu-view buffer, so that we can /* we always require a cpu-view buffer, so that we can
* always use pio (as fallback or whatever). * 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); 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; 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; dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
return -ESHUTDOWN; ret = -ESHUTDOWN;
goto print_err;
}
/* FIXME implement PIO fallback for ZLPs with DMA */ /* FIXME implement PIO fallback for ZLPs with DMA */
if (ep->dma && _req->length == 0) if (ep->dma && _req->length == 0) {
return -EOPNOTSUPP; ret = -EOPNOTSUPP;
goto print_err;
}
/* set up dma mapping in case the caller didn't */ /* set up dma mapping in case the caller didn't */
if (ep->dma) { if (ep->dma) {
int ret;
ret = usb_gadget_map_request(&dev->gadget, _req, ret = usb_gadget_map_request(&dev->gadget, _req,
ep->is_in); ep->is_in);
if (ret) if (ret)
return ret; goto print_err;
} }
ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
...@@ -1013,7 +1053,11 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) ...@@ -1013,7 +1053,11 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
spin_unlock_irqrestore(&dev->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
/* pci writes may still be posted */ /* 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 static inline void
...@@ -1134,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) ...@@ -1134,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
int stopped; int stopped;
ep = container_of(_ep, struct net2280_ep, ep); 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; return -EINVAL;
}
spin_lock_irqsave(&ep->dev->lock, flags); spin_lock_irqsave(&ep->dev->lock, flags);
stopped = ep->stopped; stopped = ep->stopped;
...@@ -1157,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) ...@@ -1157,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
} }
if (&req->req != _req) { if (&req->req != _req) {
spin_unlock_irqrestore(&ep->dev->lock, flags); spin_unlock_irqrestore(&ep->dev->lock, flags);
dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n",
__func__);
return -EINVAL; return -EINVAL;
} }
...@@ -1214,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) ...@@ -1214,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
int retval = 0; int retval = 0;
ep = container_of(_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 -EINVAL; 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) if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
== USB_ENDPOINT_XFER_ISOC) == USB_ENDPOINT_XFER_ISOC) {
return -EINVAL; retval = -EINVAL;
goto print_err;
}
spin_lock_irqsave(&ep->dev->lock, flags); spin_lock_irqsave(&ep->dev->lock, flags);
if (!list_empty(&ep->queue)) if (!list_empty(&ep->queue)) {
retval = -EAGAIN; 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; retval = -EAGAIN;
else { goto print_unlock;
} else {
ep_vdbg(ep->dev, "%s %s %s\n", _ep->name, ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
value ? "set" : "clear", value ? "set" : "clear",
wedged ? "wedge" : "halt"); wedged ? "wedge" : "halt");
...@@ -1251,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) ...@@ -1251,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
spin_unlock_irqrestore(&ep->dev->lock, flags); spin_unlock_irqrestore(&ep->dev->lock, flags);
return retval; 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) 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) ...@@ -1260,8 +1323,10 @@ static int net2280_set_halt(struct usb_ep *_ep, int value)
static int net2280_set_wedge(struct usb_ep *_ep) 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 -EINVAL;
}
return net2280_set_halt_and_wedge(_ep, 1, 1); return net2280_set_halt_and_wedge(_ep, 1, 1);
} }
...@@ -1271,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep) ...@@ -1271,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep)
u32 avail; u32 avail;
ep = container_of(_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 -ENODEV; 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; return -ESHUTDOWN;
}
avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1); 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; return -EOVERFLOW;
}
if (ep->is_in) if (ep->is_in)
avail = ep->fifo_size - avail; avail = ep->fifo_size - avail;
return avail; return avail;
...@@ -1289,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep) ...@@ -1289,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep)
struct net2280_ep *ep; struct net2280_ep *ep;
ep = container_of(_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; 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; return;
}
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
(void) readl(&ep->regs->ep_rsp); (void) readl(&ep->regs->ep_rsp);
...@@ -1977,7 +2056,7 @@ static void usb_reinit_338x(struct net2280 *dev) ...@@ -1977,7 +2056,7 @@ static void usb_reinit_338x(struct net2280 *dev)
for (i = 0; i < dev->n_ep; i++) { for (i = 0; i < dev->n_ep; i++) {
struct net2280_ep *ep = &dev->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->dev = dev;
ep->num = i; ep->num = i;
...@@ -1989,11 +2068,9 @@ static void usb_reinit_338x(struct net2280 *dev) ...@@ -1989,11 +2068,9 @@ static void usb_reinit_338x(struct net2280 *dev)
ep->regs = (struct net2280_ep_regs __iomem *) ep->regs = (struct net2280_ep_regs __iomem *)
(((void __iomem *)&dev->epregs[ne[i]]) + (((void __iomem *)&dev->epregs[ne[i]]) +
ep_reg_addr[i]); ep_reg_addr[i]);
ep->fiforegs = &dev->fiforegs[i];
} else { } else {
ep->cfg = &dev->epregs[i]; ep->cfg = &dev->epregs[i];
ep->regs = &dev->epregs[i]; ep->regs = &dev->epregs[i];
ep->fiforegs = &dev->fiforegs[i];
} }
ep->fifo_size = (i != 0) ? 2048 : 512; ep->fifo_size = (i != 0) ? 2048 : 512;
...@@ -2186,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget, ...@@ -2186,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget,
dev->ep[i].irqs = 0; dev->ep[i].irqs = 0;
/* hook up the driver ... */ /* hook up the driver ... */
dev->softconnect = 1;
driver->driver.bus = NULL; driver->driver.bus = NULL;
dev->driver = driver; dev->driver = driver;
...@@ -3052,6 +3128,8 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat) ...@@ -3052,6 +3128,8 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
BIT(PCI_RETRY_ABORT_INTERRUPT)) BIT(PCI_RETRY_ABORT_INTERRUPT))
static void handle_stat1_irqs(struct net2280 *dev, u32 stat) static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
__releases(dev->lock)
__acquires(dev->lock)
{ {
struct net2280_ep *ep; struct net2280_ep *ep;
u32 tmp, num, mask, scratch; u32 tmp, num, mask, scratch;
...@@ -3373,8 +3451,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -3373,8 +3451,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
u32 usbstat; u32 usbstat;
dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
(base + 0x00b4); (base + 0x00b4);
dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
(base + 0x0500);
dev->llregs = (struct usb338x_ll_regs __iomem *) dev->llregs = (struct usb338x_ll_regs __iomem *)
(base + 0x0700); (base + 0x0700);
dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
......
...@@ -96,7 +96,6 @@ struct net2280_ep { ...@@ -96,7 +96,6 @@ struct net2280_ep {
struct net2280_ep_regs __iomem *regs; struct net2280_ep_regs __iomem *regs;
struct net2280_dma_regs __iomem *dma; struct net2280_dma_regs __iomem *dma;
struct net2280_dma *dummy; struct net2280_dma *dummy;
struct usb338x_fifo_regs __iomem *fiforegs;
dma_addr_t td_dma; /* of dummy */ dma_addr_t td_dma; /* of dummy */
struct net2280 *dev; struct net2280 *dev;
unsigned long irqs; unsigned long irqs;
...@@ -181,7 +180,6 @@ struct net2280 { ...@@ -181,7 +180,6 @@ struct net2280 {
struct net2280_dma_regs __iomem *dma; struct net2280_dma_regs __iomem *dma;
struct net2280_dep_regs __iomem *dep; struct net2280_dep_regs __iomem *dep;
struct net2280_ep_regs __iomem *epregs; struct net2280_ep_regs __iomem *epregs;
struct usb338x_fifo_regs __iomem *fiforegs;
struct usb338x_ll_regs __iomem *llregs; struct usb338x_ll_regs __iomem *llregs;
struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs; struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs; struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
......
...@@ -93,23 +93,22 @@ static void handle_ep(struct pxa_ep *ep); ...@@ -93,23 +93,22 @@ static void handle_ep(struct pxa_ep *ep);
static int state_dbg_show(struct seq_file *s, void *p) static int state_dbg_show(struct seq_file *s, void *p)
{ {
struct pxa_udc *udc = s->private; struct pxa_udc *udc = s->private;
int pos = 0, ret;
u32 tmp; u32 tmp;
ret = -ENODEV;
if (!udc->driver) if (!udc->driver)
goto out; return -ENODEV;
/* basic device status */ /* basic device status */
pos += seq_printf(s, DRIVER_DESC "\n" seq_printf(s, DRIVER_DESC "\n"
"%s version: %s\nGadget driver: %s\n", "%s version: %s\n"
"Gadget driver: %s\n",
driver_name, DRIVER_VERSION, driver_name, DRIVER_VERSION,
udc->driver ? udc->driver->driver.name : "(none)"); udc->driver ? udc->driver->driver.name : "(none)");
tmp = udc_readl(udc, UDCCR); tmp = udc_readl(udc, UDCCR);
pos += seq_printf(s, seq_printf(s,
"udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), " "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n",
"con=%d,inter=%d,altinter=%d\n", tmp, tmp,
(tmp & UDCCR_OEN) ? " oen":"", (tmp & UDCCR_OEN) ? " oen":"",
(tmp & UDCCR_AALTHNP) ? " aalthnp":"", (tmp & UDCCR_AALTHNP) ? " aalthnp":"",
(tmp & UDCCR_AHNP) ? " rem" : "", (tmp & UDCCR_AHNP) ? " rem" : "",
...@@ -124,19 +123,16 @@ static int state_dbg_show(struct seq_file *s, void *p) ...@@ -124,19 +123,16 @@ static int state_dbg_show(struct seq_file *s, void *p)
(tmp & UDCCR_AIN) >> UDCCR_AIN_S, (tmp & UDCCR_AIN) >> UDCCR_AIN_S,
(tmp & UDCCR_AAISN) >> UDCCR_AAISN_S); (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
/* registers for device and ep0 */ /* registers for device and ep0 */
pos += seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n", seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1)); udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
pos += seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n", seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1)); udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
pos += seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR)); seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
pos += seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, " seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n",
"reconfig=%lu\n",
udc->stats.irqs_reset, udc->stats.irqs_suspend, udc->stats.irqs_reset, udc->stats.irqs_suspend,
udc->stats.irqs_resume, udc->stats.irqs_reconfig); udc->stats.irqs_resume, udc->stats.irqs_reconfig);
ret = 0; return 0;
out:
return ret;
} }
static int queues_dbg_show(struct seq_file *s, void *p) static int queues_dbg_show(struct seq_file *s, void *p)
...@@ -144,50 +140,47 @@ static int queues_dbg_show(struct seq_file *s, void *p) ...@@ -144,50 +140,47 @@ static int queues_dbg_show(struct seq_file *s, void *p)
struct pxa_udc *udc = s->private; struct pxa_udc *udc = s->private;
struct pxa_ep *ep; struct pxa_ep *ep;
struct pxa27x_request *req; struct pxa27x_request *req;
int pos = 0, i, maxpkt, ret; int i, maxpkt;
ret = -ENODEV;
if (!udc->driver) if (!udc->driver)
goto out; return -ENODEV;
/* dump endpoint queues */ /* dump endpoint queues */
for (i = 0; i < NR_PXA_ENDPOINTS; i++) { for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
ep = &udc->pxa_ep[i]; ep = &udc->pxa_ep[i];
maxpkt = ep->fifo_size; maxpkt = ep->fifo_size;
pos += seq_printf(s, "%-12s max_pkt=%d %s\n", seq_printf(s, "%-12s max_pkt=%d %s\n",
EPNAME(ep), maxpkt, "pio"); EPNAME(ep), maxpkt, "pio");
if (list_empty(&ep->queue)) { if (list_empty(&ep->queue)) {
pos += seq_printf(s, "\t(nothing queued)\n"); seq_puts(s, "\t(nothing queued)\n");
continue; continue;
} }
list_for_each_entry(req, &ep->queue, queue) { list_for_each_entry(req, &ep->queue, queue) {
pos += seq_printf(s, "\treq %p len %d/%d buf %p\n", seq_printf(s, "\treq %p len %d/%d buf %p\n",
&req->req, req->req.actual, &req->req, req->req.actual,
req->req.length, req->req.buf); req->req.length, req->req.buf);
} }
} }
ret = 0; return 0;
out:
return ret;
} }
static int eps_dbg_show(struct seq_file *s, void *p) static int eps_dbg_show(struct seq_file *s, void *p)
{ {
struct pxa_udc *udc = s->private; struct pxa_udc *udc = s->private;
struct pxa_ep *ep; struct pxa_ep *ep;
int pos = 0, i, ret; int i;
u32 tmp; u32 tmp;
ret = -ENODEV;
if (!udc->driver) if (!udc->driver)
goto out; return -ENODEV;
ep = &udc->pxa_ep[0]; ep = &udc->pxa_ep[0];
tmp = udc_ep_readl(ep, UDCCSR); tmp = udc_ep_readl(ep, UDCCSR);
pos += seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", tmp, seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n",
tmp,
(tmp & UDCCSR0_SA) ? " sa" : "", (tmp & UDCCSR0_SA) ? " sa" : "",
(tmp & UDCCSR0_RNE) ? " rne" : "", (tmp & UDCCSR0_RNE) ? " rne" : "",
(tmp & UDCCSR0_FST) ? " fst" : "", (tmp & UDCCSR0_FST) ? " fst" : "",
...@@ -198,10 +191,7 @@ static int eps_dbg_show(struct seq_file *s, void *p) ...@@ -198,10 +191,7 @@ static int eps_dbg_show(struct seq_file *s, void *p)
for (i = 0; i < NR_PXA_ENDPOINTS; i++) { for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
ep = &udc->pxa_ep[i]; ep = &udc->pxa_ep[i];
tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR); tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR);
pos += seq_printf(s, "%-12s: " seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n",
"IN %lu(%lu reqs), OUT %lu(%lu reqs), "
"irqs=%lu, udccr=0x%08x, udccsr=0x%03x, "
"udcbcr=%d\n",
EPNAME(ep), EPNAME(ep),
ep->stats.in_bytes, ep->stats.in_ops, ep->stats.in_bytes, ep->stats.in_ops,
ep->stats.out_bytes, ep->stats.out_ops, ep->stats.out_bytes, ep->stats.out_ops,
...@@ -210,9 +200,7 @@ static int eps_dbg_show(struct seq_file *s, void *p) ...@@ -210,9 +200,7 @@ static int eps_dbg_show(struct seq_file *s, void *p)
udc_ep_readl(ep, UDCBCR)); udc_ep_readl(ep, UDCBCR));
} }
ret = 0; return 0;
out:
return ret;
} }
static int eps_dbg_open(struct inode *inode, struct file *file) static int eps_dbg_open(struct inode *inode, struct file *file)
......
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
* @dev - the child device to the actual controller * @dev - the child device to the actual controller
* @gadget - the gadget. For use by the class code * @gadget - the gadget. For use by the class code
* @list - for use by the udc class driver * @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 * This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together. * to hold information about udc driver and gadget together.
...@@ -44,6 +46,7 @@ struct usb_udc { ...@@ -44,6 +46,7 @@ struct usb_udc {
struct usb_gadget *gadget; struct usb_gadget *gadget;
struct device dev; struct device dev;
struct list_head list; struct list_head list;
bool vbus;
}; };
static struct class *udc_class; static struct class *udc_class;
...@@ -129,19 +132,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); ...@@ -129,19 +132,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
static void usb_gadget_state_work(struct work_struct *work) static void usb_gadget_state_work(struct work_struct *work)
{ {
struct usb_gadget *gadget = work_to_gadget(work); struct usb_gadget *gadget = work_to_gadget(work);
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;
mutex_unlock(&udc_lock);
return;
found:
mutex_unlock(&udc_lock);
if (udc)
sysfs_notify(&udc->dev.kobj, NULL, "state"); sysfs_notify(&udc->dev.kobj, NULL, "state");
} }
...@@ -155,6 +148,34 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state); ...@@ -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 * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
* @gadget: The gadget which 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, ...@@ -278,6 +299,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
goto err3; goto err3;
udc->gadget = gadget; udc->gadget = gadget;
gadget->udc = udc;
mutex_lock(&udc_lock); mutex_lock(&udc_lock);
list_add_tail(&udc->list, &udc_list); list_add_tail(&udc->list, &udc_list);
...@@ -287,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, ...@@ -287,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
goto err4; goto err4;
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
udc->vbus = true;
mutex_unlock(&udc_lock); mutex_unlock(&udc_lock);
...@@ -348,21 +371,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc) ...@@ -348,21 +371,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
*/ */
void usb_del_gadget_udc(struct usb_gadget *gadget) 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);
if (!udc)
return; return;
found:
dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
mutex_lock(&udc_lock);
list_del(&udc->list); list_del(&udc->list);
mutex_unlock(&udc_lock); mutex_unlock(&udc_lock);
...@@ -397,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri ...@@ -397,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
driver->unbind(udc->gadget); driver->unbind(udc->gadget);
goto err1; goto err1;
} }
usb_gadget_connect(udc->gadget); usb_udc_connect_control(udc);
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
return 0; return 0;
......
...@@ -507,7 +507,8 @@ void musb_hnp_stop(struct musb *musb) ...@@ -507,7 +507,8 @@ void musb_hnp_stop(struct musb *musb)
musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16); 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. * Interrupt Service Routine to record USB "global" interrupts.
* Since these do not happen often and signify things of * 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, ...@@ -534,30 +535,16 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
*/ */
if (int_usb & MUSB_INTR_RESUME) { if (int_usb & MUSB_INTR_RESUME) {
handled = IRQ_HANDLED; 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) { if (devctl & MUSB_DEVCTL_HM) {
void __iomem *mbase = musb->mregs;
u8 power;
switch (musb->xceiv->otg->state) { switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND: case OTG_STATE_A_SUSPEND:
/* remote wakeup? later, GetPortStatus /* remote wakeup? later, GetPortStatus
* will stop RESUME signaling * 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 |= musb->port1_status |=
(USB_PORT_STAT_C_SUSPEND << 16) (USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME; | MUSB_PORT_STAT_RESUME;
...@@ -775,10 +762,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, ...@@ -775,10 +762,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->ep0_stage = MUSB_EP0_START; 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->intrtxe = musb->epmask;
musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe); musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
musb->intrrxe = musb->epmask & 0xfffe; musb->intrrxe = musb->epmask & 0xfffe;
...@@ -879,20 +862,19 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, ...@@ -879,20 +862,19 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
*/ */
if (int_usb & MUSB_INTR_RESET) { if (int_usb & MUSB_INTR_RESET) {
handled = IRQ_HANDLED; handled = IRQ_HANDLED;
if ((devctl & MUSB_DEVCTL_HM) != 0) { if (devctl & MUSB_DEVCTL_HM) {
/* /*
* Looks like non-HS BABBLE can be ignored, but * When BABBLE happens what we can depends on which
* HS BABBLE is an error condition. For HS the solution * platform MUSB is running, because some platforms
* is to avoid babble in the first place and fix what * implemented proprietary means for 'recovering' from
* caused BABBLE. When HS BABBLE happens we can only * Babble conditions. One such platform is AM335x. In
* stop the session. * most cases, however, the only thing we can do is
* drop the session.
*/ */
if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) dev_err(musb->controller, "Babble\n");
dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl);
else { if (is_host_active(musb))
ERR("Stopping host session -- babble\n"); musb_recover_from_babble(musb);
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
}
} else { } else {
dev_dbg(musb->controller, "BUS RESET as %s\n", dev_dbg(musb->controller, "BUS RESET as %s\n",
usb_otg_state_string(musb->xceiv->otg->state)); usb_otg_state_string(musb->xceiv->otg->state));
...@@ -931,13 +913,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, ...@@ -931,13 +913,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
} }
} }
/* 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 #if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or /* REVISIT ... this would be for multiplexing periodic endpoints, or
* supporting transfer phasing to prevent exceeding ISO bandwidth * supporting transfer phasing to prevent exceeding ISO bandwidth
...@@ -990,7 +965,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, ...@@ -990,7 +965,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void musb_generic_disable(struct musb *musb) static void musb_disable_interrupts(struct musb *musb)
{ {
void __iomem *mbase = musb->mregs; void __iomem *mbase = musb->mregs;
u16 temp; u16 temp;
...@@ -1002,16 +977,35 @@ static void musb_generic_disable(struct musb *musb) ...@@ -1002,16 +977,35 @@ static void musb_generic_disable(struct musb *musb)
musb->intrrxe = 0; musb->intrrxe = 0;
musb_writew(mbase, MUSB_INTRRXE, 0); musb_writew(mbase, MUSB_INTRRXE, 0);
/* off */
musb_writeb(mbase, MUSB_DEVCTL, 0);
/* flush pending interrupts */ /* flush pending interrupts */
temp = musb_readb(mbase, MUSB_INTRUSB); temp = musb_readb(mbase, MUSB_INTRUSB);
temp = musb_readw(mbase, MUSB_INTRTX); temp = musb_readw(mbase, MUSB_INTRTX);
temp = musb_readw(mbase, MUSB_INTRRX); 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);
}
/* /*
* Program the HDRC to start (enable interrupts, dma, etc.). * Program the HDRC to start (enable interrupts, dma, etc.).
*/ */
...@@ -1022,13 +1016,7 @@ void musb_start(struct musb *musb) ...@@ -1022,13 +1016,7 @@ void musb_start(struct musb *musb)
dev_dbg(musb->controller, "<== devctl %02x\n", devctl); dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
/* Set INT enable registers, enable interrupts */ musb_enable_interrupts(musb);
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_writeb(regs, MUSB_TESTMODE, 0); musb_writeb(regs, MUSB_TESTMODE, 0);
/* put into basic highspeed mode and start session */ /* put into basic highspeed mode and start session */
...@@ -1587,9 +1575,12 @@ static int musb_core_init(u16 musb_type, struct musb *musb) ...@@ -1587,9 +1575,12 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
irqreturn_t musb_interrupt(struct musb *musb) irqreturn_t musb_interrupt(struct musb *musb)
{ {
irqreturn_t retval = IRQ_NONE; irqreturn_t retval = IRQ_NONE;
unsigned long status;
unsigned long epnum;
u8 devctl; 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); devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
...@@ -1597,56 +1588,57 @@ irqreturn_t musb_interrupt(struct musb *musb) ...@@ -1597,56 +1588,57 @@ irqreturn_t musb_interrupt(struct musb *musb)
is_host_active(musb) ? "host" : "peripheral", is_host_active(musb) ? "host" : "peripheral",
musb->int_usb, musb->int_tx, musb->int_rx); 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 (musb->int_tx & 1) {
if (is_host_active(musb)) if (is_host_active(musb))
retval |= musb_h_ep0_irq(musb); retval |= musb_h_ep0_irq(musb);
else else
retval |= musb_g_ep0_irq(musb); 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 */ status = musb->int_tx;
reg = musb->int_rx >> 1;
ep_num = 1; for_each_set_bit(epnum, &status, 16) {
while (reg) {
if (reg & 1) {
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval = ep->rx_irq(...) */
retval = IRQ_HANDLED; retval = IRQ_HANDLED;
if (is_host_active(musb)) if (is_host_active(musb))
musb_host_rx(musb, ep_num); musb_host_tx(musb, epnum);
else else
musb_g_rx(musb, ep_num); musb_g_tx(musb, epnum);
} }
reg >>= 1; status = musb->int_rx;
ep_num++;
}
/* TX on endpoints 1-15 */ for_each_set_bit(epnum, &status, 16) {
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; retval = IRQ_HANDLED;
if (is_host_active(musb)) if (is_host_active(musb))
musb_host_tx(musb, ep_num); musb_host_rx(musb, epnum);
else else
musb_g_tx(musb, ep_num); musb_g_rx(musb, epnum);
}
reg >>= 1;
ep_num++;
} }
return retval; return retval;
...@@ -1825,33 +1817,44 @@ static void musb_irq_work(struct work_struct *data) ...@@ -1825,33 +1817,44 @@ static void musb_irq_work(struct work_struct *data)
} }
} }
/* Recover from babble interrupt conditions */ static void musb_recover_from_babble(struct musb *musb)
static void musb_recover_work(struct work_struct *data)
{ {
struct musb *musb = container_of(data, struct musb, recover_work.work); int ret;
int status, ret; u8 devctl;
musb_disable_interrupts(musb);
ret = musb_platform_reset(musb); /*
if (ret) * 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; return;
}
usb_phy_vbus_off(musb->xceiv); /* drop session bit */
usleep_range(100, 200); devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
usb_phy_vbus_on(musb->xceiv); /* tell usbcore about it */
usleep_range(100, 200); musb_root_disconnect(musb);
/* /*
* When a babble condition occurs, the musb controller * When a babble condition occurs, the musb controller
* removes the session bit and the endpoint config is lost. * removes the session bit and the endpoint config is lost.
*/ */
if (musb->dyn_fifo) if (musb->dyn_fifo)
status = ep_config_from_table(musb); ret = ep_config_from_table(musb);
else else
status = ep_config_from_hw(musb); ret = ep_config_from_hw(musb);
/* start the session again */ /* restart session */
if (status == 0) if (ret == 0)
musb_start(musb); musb_start(musb);
} }
...@@ -2087,7 +2090,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) ...@@ -2087,7 +2090,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* Init IRQ workqueue before request_irq */ /* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work); 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->deassert_reset_work, musb_deassert_reset);
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume); INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
...@@ -2183,7 +2185,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) ...@@ -2183,7 +2185,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
fail3: fail3:
cancel_work_sync(&musb->irq_work); 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->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work); cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller) if (musb->dma_controller)
...@@ -2249,7 +2250,6 @@ static int musb_remove(struct platform_device *pdev) ...@@ -2249,7 +2250,6 @@ static int musb_remove(struct platform_device *pdev)
dma_controller_destroy(musb->dma_controller); dma_controller_destroy(musb->dma_controller);
cancel_work_sync(&musb->irq_work); 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->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work); cancel_delayed_work_sync(&musb->deassert_reset_work);
musb_free(musb); musb_free(musb);
......
...@@ -160,7 +160,8 @@ struct musb_io; ...@@ -160,7 +160,8 @@ struct musb_io;
* @init: turns on clocks, sets up platform-specific registers, etc * @init: turns on clocks, sets up platform-specific registers, etc
* @exit: undoes @init * @exit: undoes @init
* @set_mode: forcefully changes operating mode * @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 * @vbus_status: returns vbus status if possible
* @set_vbus: forces vbus status * @set_vbus: forces vbus status
* @adjust_channel_params: pre check for standard dma channel_program func * @adjust_channel_params: pre check for standard dma channel_program func
...@@ -196,7 +197,7 @@ struct musb_platform_ops { ...@@ -196,7 +197,7 @@ struct musb_platform_ops {
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
int (*set_mode)(struct musb *musb, u8 mode); int (*set_mode)(struct musb *musb, u8 mode);
void (*try_idle)(struct musb *musb, unsigned long timeout); 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); int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on); void (*set_vbus)(struct musb *musb, int on);
...@@ -300,7 +301,6 @@ struct musb { ...@@ -300,7 +301,6 @@ struct musb {
irqreturn_t (*isr)(int, void *); irqreturn_t (*isr)(int, void *);
struct work_struct irq_work; struct work_struct irq_work;
struct delayed_work recover_work;
struct delayed_work deassert_reset_work; struct delayed_work deassert_reset_work;
struct delayed_work finish_resume_work; struct delayed_work finish_resume_work;
u16 hwvers; u16 hwvers;
...@@ -558,12 +558,12 @@ static inline void musb_platform_try_idle(struct musb *musb, ...@@ -558,12 +558,12 @@ static inline void musb_platform_try_idle(struct musb *musb,
musb->ops->try_idle(musb, timeout); 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) if (!musb->ops->recover)
return -EINVAL; return 0;
return musb->ops->reset(musb); return musb->ops->recover(musb);
} }
static inline int musb_platform_get_vbus_status(struct musb *musb) static inline int musb_platform_get_vbus_status(struct musb *musb)
......
...@@ -225,10 +225,12 @@ static void cppi41_dma_callback(void *private_data) ...@@ -225,10 +225,12 @@ static void cppi41_dma_callback(void *private_data)
struct dma_channel *channel = private_data; struct dma_channel *channel = private_data;
struct cppi41_dma_channel *cppi41_channel = channel->private_data; struct cppi41_dma_channel *cppi41_channel = channel->private_data;
struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep; struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
struct cppi41_dma_controller *controller;
struct musb *musb = hw_ep->musb; struct musb *musb = hw_ep->musb;
unsigned long flags; unsigned long flags;
struct dma_tx_state txstate; struct dma_tx_state txstate;
u32 transferred; u32 transferred;
int is_hs = 0;
bool empty; bool empty;
spin_lock_irqsave(&musb->lock, flags); spin_lock_irqsave(&musb->lock, flags);
...@@ -248,12 +250,14 @@ static void cppi41_dma_callback(void *private_data) ...@@ -248,12 +250,14 @@ static void cppi41_dma_callback(void *private_data)
transferred < cppi41_channel->packet_sz) transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0; cppi41_channel->prog_len = 0;
if (cppi41_channel->is_tx)
empty = musb_is_tx_fifo_empty(hw_ep); empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
if (!cppi41_channel->is_tx || empty) {
cppi41_trans_done(cppi41_channel); cppi41_trans_done(cppi41_channel);
} else { goto out;
struct cppi41_dma_controller *controller; }
int is_hs = 0;
/* /*
* On AM335x it has been observed that the TX interrupt fires * On AM335x it has been observed that the TX interrupt fires
* too early that means the TXFIFO is not yet empty but the DMA * too early that means the TXFIFO is not yet empty but the DMA
...@@ -277,20 +281,16 @@ static void cppi41_dma_callback(void *private_data) ...@@ -277,20 +281,16 @@ static void cppi41_dma_callback(void *private_data)
unsigned wait = 25; unsigned wait = 25;
do { do {
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty)
break;
wait--;
if (!wait)
break;
udelay(1);
} while (1);
empty = musb_is_tx_fifo_empty(hw_ep); empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) { if (empty) {
cppi41_trans_done(cppi41_channel); cppi41_trans_done(cppi41_channel);
goto out; goto out;
} }
wait--;
if (!wait)
break;
cpu_relax();
} while (1);
} }
list_add_tail(&cppi41_channel->tx_check, list_add_tail(&cppi41_channel->tx_check,
&controller->early_tx_list); &controller->early_tx_list);
...@@ -302,7 +302,7 @@ static void cppi41_dma_callback(void *private_data) ...@@ -302,7 +302,7 @@ static void cppi41_dma_callback(void *private_data)
20 * NSEC_PER_USEC, 20 * NSEC_PER_USEC,
HRTIMER_MODE_REL); HRTIMER_MODE_REL);
} }
}
out: out:
spin_unlock_irqrestore(&musb->lock, flags); spin_unlock_irqrestore(&musb->lock, flags);
} }
......
...@@ -119,7 +119,7 @@ struct dsps_musb_wrapper { ...@@ -119,7 +119,7 @@ struct dsps_musb_wrapper {
unsigned iddig:5; unsigned iddig:5;
unsigned iddig_mux:5; unsigned iddig_mux:5;
/* miscellaneous stuff */ /* miscellaneous stuff */
u8 poll_seconds; unsigned poll_timeout;
}; };
/* /*
...@@ -225,9 +225,8 @@ static void dsps_musb_enable(struct musb *musb) ...@@ -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->epintr_set, epmask);
dsps_writel(reg_base, wrp->coreintr_set, coremask); dsps_writel(reg_base, wrp->coreintr_set, coremask);
/* Force the DRVVBUS IRQ so we can start polling for ID change. */ /* start polling for ID change. */
dsps_writel(reg_base, wrp->coreintr_set, mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout));
(1 << wrp->drvvbus) << wrp->usb_shift);
dsps_musb_try_idle(musb, 0); dsps_musb_try_idle(musb, 0);
} }
...@@ -285,7 +284,8 @@ static void otg_timer(unsigned long _musb) ...@@ -285,7 +284,8 @@ static void otg_timer(unsigned long _musb)
} }
if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session) if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_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; break;
case OTG_STATE_A_WAIT_VFALL: case OTG_STATE_A_WAIT_VFALL:
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
...@@ -330,28 +330,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) ...@@ -330,28 +330,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n", dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
usbintr, epintr); 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)) { if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
int drvvbus = dsps_readl(reg_base, wrp->status); int drvvbus = dsps_readl(reg_base, wrp->status);
...@@ -374,8 +352,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) ...@@ -374,8 +352,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
*/ */
musb->int_usb &= ~MUSB_INTR_VBUSERROR; musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL; musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&glue->timer, mod_timer(&glue->timer, jiffies +
jiffies + wrp->poll_seconds * HZ); msecs_to_jiffies(wrp->poll_timeout));
WARNING("VBUS error workaround (delay coming)\n"); WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) { } else if (drvvbus) {
MUSB_HST_MODE(musb); MUSB_HST_MODE(musb);
...@@ -404,7 +382,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) ...@@ -404,7 +382,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
/* Poll for ID change in OTG port mode */ /* Poll for ID change in OTG port mode */
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) 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: out:
spin_unlock_irqrestore(&musb->lock, flags); spin_unlock_irqrestore(&musb->lock, flags);
...@@ -453,7 +432,7 @@ static int dsps_musb_init(struct musb *musb) ...@@ -453,7 +432,7 @@ static int dsps_musb_init(struct musb *musb)
musb->ctrl_base = reg_base; musb->ctrl_base = reg_base;
/* NOP driver needs change if supporting dual instance */ /* 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)) if (IS_ERR(musb->xceiv))
return PTR_ERR(musb->xceiv); return PTR_ERR(musb->xceiv);
...@@ -497,7 +476,7 @@ static int dsps_musb_init(struct musb *musb) ...@@ -497,7 +476,7 @@ static int dsps_musb_init(struct musb *musb)
* logic enabled. * logic enabled.
*/ */
val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL); 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; glue->sw_babble_enabled = true;
val |= MUSB_BABBLE_SW_SESSION_CTRL; val |= MUSB_BABBLE_SW_SESSION_CTRL;
dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val); dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
...@@ -571,7 +550,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode) ...@@ -571,7 +550,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
return 0; return 0;
} }
static bool sw_babble_control(struct musb *musb) static bool dsps_sw_babble_control(struct musb *musb)
{ {
u8 babble_ctl; u8 babble_ctl;
bool session_restart = false; bool session_restart = false;
...@@ -622,37 +601,36 @@ static bool sw_babble_control(struct musb *musb) ...@@ -622,37 +601,36 @@ static bool sw_babble_control(struct musb *musb)
return session_restart; 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 device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent); struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp; int session_restart = 0;
int session_restart = 0, error;
if (glue->sw_babble_enabled) if (glue->sw_babble_enabled)
session_restart = sw_babble_control(musb); session_restart = dsps_sw_babble_control(musb);
/* else
* 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 = 1; 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 = { static struct musb_platform_ops dsps_ops = {
...@@ -665,7 +643,7 @@ static struct musb_platform_ops dsps_ops = { ...@@ -665,7 +643,7 @@ static struct musb_platform_ops dsps_ops = {
.try_idle = dsps_musb_try_idle, .try_idle = dsps_musb_try_idle,
.set_mode = dsps_musb_set_mode, .set_mode = dsps_musb_set_mode,
.reset = dsps_musb_reset, .recover = dsps_musb_recover,
}; };
static u64 musb_dmamask = DMA_BIT_MASK(32); static u64 musb_dmamask = DMA_BIT_MASK(32);
...@@ -737,7 +715,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, ...@@ -737,7 +715,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
musb->dev.parent = dev; musb->dev.parent = dev;
musb->dev.dma_mask = &musb_dmamask; musb->dev.dma_mask = &musb_dmamask;
musb->dev.coherent_dma_mask = musb_dmamask; musb->dev.coherent_dma_mask = musb_dmamask;
musb->dev.of_node = of_node_get(dn);
glue->musb = musb; glue->musb = musb;
...@@ -802,6 +779,9 @@ static int dsps_probe(struct platform_device *pdev) ...@@ -802,6 +779,9 @@ static int dsps_probe(struct platform_device *pdev)
} }
wrp = match->data; wrp = match->data;
if (of_device_is_compatible(pdev->dev.of_node, "ti,musb-dm816"))
dsps_ops.read_fifo = dsps_read_fifo32;
/* allocate glue */ /* allocate glue */
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) if (!glue)
...@@ -873,12 +853,14 @@ static const struct dsps_musb_wrapper am33xx_driver_data = { ...@@ -873,12 +853,14 @@ static const struct dsps_musb_wrapper am33xx_driver_data = {
.rxep_shift = 16, .rxep_shift = 16,
.rxep_mask = 0xfffe, .rxep_mask = 0xfffe,
.rxep_bitmap = (0xfffe << 16), .rxep_bitmap = (0xfffe << 16),
.poll_seconds = 2, .poll_timeout = 2000, /* ms */
}; };
static const struct of_device_id musb_dsps_of_match[] = { static const struct of_device_id musb_dsps_of_match[] = {
{ .compatible = "ti,musb-am33xx", { .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); MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
...@@ -929,7 +911,8 @@ static int dsps_resume(struct device *dev) ...@@ -929,7 +911,8 @@ static int dsps_resume(struct device *dev)
dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode); dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) 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; return 0;
} }
......
...@@ -1876,44 +1876,6 @@ static int musb_gadget_start(struct usb_gadget *g, ...@@ -1876,44 +1876,6 @@ static int musb_gadget_start(struct usb_gadget *g,
return retval; 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 * Unregister the gadget driver. Used by gadget drivers when
* unregistering themselves from the controller. * unregistering themselves from the controller.
...@@ -1940,7 +1902,7 @@ static int musb_gadget_stop(struct usb_gadget *g) ...@@ -1940,7 +1902,7 @@ static int musb_gadget_stop(struct usb_gadget *g)
(void) musb_gadget_vbus_draw(&musb->g, 0); (void) musb_gadget_vbus_draw(&musb->g, 0);
musb->xceiv->otg->state = OTG_STATE_UNDEFINED; musb->xceiv->otg->state = OTG_STATE_UNDEFINED;
stop_activity(musb, NULL); musb_stop(musb);
otg_set_peripheral(musb->xceiv->otg, NULL); otg_set_peripheral(musb->xceiv->otg, NULL);
musb->is_active = 0; musb->is_active = 0;
......
...@@ -202,13 +202,13 @@ config USB_RCAR_GEN2_PHY ...@@ -202,13 +202,13 @@ config USB_RCAR_GEN2_PHY
config USB_ULPI config USB_ULPI
bool "Generic ULPI Transceiver Driver" bool "Generic ULPI Transceiver Driver"
depends on ARM || ARM64 depends on ARM || ARM64
select USB_ULPI_VIEWPORT
help help
Enable this to support ULPI connected USB OTG transceivers which Enable this to support ULPI connected USB OTG transceivers which
are likely found on embedded boards. are likely found on embedded boards.
config USB_ULPI_VIEWPORT config USB_ULPI_VIEWPORT
bool bool
depends on USB_ULPI
help help
Provides read/write operations to the ULPI phy register set for Provides read/write operations to the ULPI phy register set for
controllers with a viewport register (e.g. Chipidea/ARC controllers). controllers with a viewport register (e.g. Chipidea/ARC controllers).
......
...@@ -27,7 +27,7 @@ static const char *const usbphy_modes[] = { ...@@ -27,7 +27,7 @@ static const char *const usbphy_modes[] = {
* @np: Pointer to the given device_node * @np: Pointer to the given device_node
* *
* The function gets phy interface string from property 'phy_type', * 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) enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np)
{ {
......
...@@ -893,7 +893,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab) ...@@ -893,7 +893,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
/* /*
* Disconnection Sequence: * Disconnection Sequence:
* 1. Disconect Interrupt * 1. Disconnect Interrupt
* 2. Disable regulators * 2. Disable regulators
* 3. Disable AB clock * 3. Disable AB clock
* 4. Disable the Phy * 4. Disable the Phy
......
...@@ -62,14 +62,14 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) ...@@ -62,14 +62,14 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
return 0; 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) if (!nop->gpiod_reset)
return; return;
gpiod_direction_output(nop->gpiod_reset, !asserted); gpiod_set_value(nop->gpiod_reset, 1);
usleep_range(10000, 20000); usleep_range(10000, 20000);
gpiod_set_value(nop->gpiod_reset, asserted); gpiod_set_value(nop->gpiod_reset, 0);
} }
/* interface to regulator framework */ /* interface to regulator framework */
...@@ -151,8 +151,7 @@ int usb_gen_phy_init(struct usb_phy *phy) ...@@ -151,8 +151,7 @@ int usb_gen_phy_init(struct usb_phy *phy)
if (!IS_ERR(nop->clk)) if (!IS_ERR(nop->clk))
clk_prepare_enable(nop->clk); clk_prepare_enable(nop->clk);
/* De-assert RESET */ nop_reset(nop);
nop_reset_set(nop, 0);
return 0; return 0;
} }
...@@ -162,8 +161,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy) ...@@ -162,8 +161,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
{ {
struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
/* Assert RESET */ gpiod_set_value(nop->gpiod_reset, 1);
nop_reset_set(nop, 1);
if (!IS_ERR(nop->clk)) if (!IS_ERR(nop->clk))
clk_disable_unprepare(nop->clk); clk_disable_unprepare(nop->clk);
......
...@@ -81,7 +81,9 @@ static void devm_usb_phy_release(struct device *dev, void *res) ...@@ -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) 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;
} }
/** /**
......
...@@ -275,6 +275,16 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, ...@@ -275,6 +275,16 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum,
return 0; return 0;
} }
/*
* 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 * local functions
*/ */
...@@ -487,6 +497,15 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) ...@@ -487,6 +497,15 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
if (gpio > 0) if (gpio > 0)
dparam->enable_gpio = gpio; 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; return info;
} }
......
...@@ -193,6 +193,7 @@ struct usbhs_priv; ...@@ -193,6 +193,7 @@ struct usbhs_priv;
#define TYPE_BULK (1 << 14) #define TYPE_BULK (1 << 14)
#define TYPE_INT (2 << 14) #define TYPE_INT (2 << 14)
#define TYPE_ISO (3 << 14) #define TYPE_ISO (3 << 14)
#define BFRE (1 << 10) /* BRDY Interrupt Operation Spec. */
#define DBLB (1 << 9) /* Double Buffer Mode */ #define DBLB (1 << 9) /* Double Buffer Mode */
#define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */ #define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */
#define DIR_OUT (1 << 4) /* Transfer Direction */ #define DIR_OUT (1 << 4) /* Transfer Direction */
...@@ -216,6 +217,7 @@ struct usbhs_priv; ...@@ -216,6 +217,7 @@ struct usbhs_priv;
#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */
#define SQCLR (1 << 8) /* Toggle Bit Clear */ #define SQCLR (1 << 8) /* Toggle Bit Clear */
#define SQSET (1 << 7) /* Toggle Bit Set */ #define SQSET (1 << 7) /* Toggle Bit Set */
#define SQMON (1 << 6) /* Toggle Bit Check */
#define PBUSY (1 << 5) /* Pipe Busy */ #define PBUSY (1 << 5) /* Pipe Busy */
#define PID_MASK (0x3) /* Response PID */ #define PID_MASK (0x3) /* Response PID */
#define PID_NAK 0 #define PID_NAK 0
...@@ -323,6 +325,11 @@ int usbhs_frame_get_num(struct usbhs_priv *priv); ...@@ -323,6 +325,11 @@ int usbhs_frame_get_num(struct usbhs_priv *priv);
int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub, int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub,
u16 hubport, u16 speed); u16 hubport, u16 speed);
/*
* interrupt functions
*/
void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit);
/* /*
* data * data
*/ */
......
...@@ -813,7 +813,8 @@ static void xfer_work(struct work_struct *work) ...@@ -813,7 +813,8 @@ static void xfer_work(struct work_struct *work)
desc->callback = usbhsf_dma_complete; desc->callback = usbhsf_dma_complete;
desc->callback_param = pipe; 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"); dev_err(dev, "Failed to submit dma descriptor\n");
return; return;
} }
...@@ -822,10 +823,10 @@ static void xfer_work(struct work_struct *work) ...@@ -822,10 +823,10 @@ static void xfer_work(struct work_struct *work)
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
usbhs_pipe_running(pipe, 1); usbhs_pipe_running(pipe, 1);
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
usbhs_pipe_enable(pipe);
usbhsf_dma_start(pipe, fifo); usbhsf_dma_start(pipe, fifo);
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
dma_async_issue_pending(chan); 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) ...@@ -838,6 +839,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
struct usbhs_fifo *fifo; struct usbhs_fifo *fifo;
int len = pkt->length - pkt->actual; int len = pkt->length - pkt->actual;
int ret; int ret;
uintptr_t align_mask;
if (usbhs_pipe_is_busy(pipe)) if (usbhs_pipe_is_busy(pipe))
return 0; return 0;
...@@ -847,10 +849,14 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) ...@@ -847,10 +849,14 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
usbhs_pipe_is_dcp(pipe)) usbhs_pipe_is_dcp(pipe))
goto usbhsf_pio_prepare_push; 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; 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; goto usbhsf_pio_prepare_push;
/* return at this time if the pipe is running */ /* return at this time if the pipe is running */
...@@ -924,7 +930,85 @@ struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = { ...@@ -924,7 +930,85 @@ struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
/* /*
* DMA pop 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_pipe *pipe = pkt->pipe;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
...@@ -993,7 +1077,16 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) ...@@ -993,7 +1077,16 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
return pkt->handler->try_run(pkt, is_done); 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; struct usbhs_pipe *pipe = pkt->pipe;
int maxp = usbhs_pipe_get_maxpacket(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) ...@@ -1017,8 +1110,68 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
return 0; 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 = { struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
.prepare = usbhsf_prepare_pop, .prepare = usbhsf_dma_prepare_pop,
.try_run = usbhsf_dma_try_pop, .try_run = usbhsf_dma_try_pop,
.dma_done = usbhsf_dma_pop_done .dma_done = usbhsf_dma_pop_done
}; };
...@@ -1069,23 +1222,29 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo) ...@@ -1069,23 +1222,29 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo)
&fifo->rx_slave); &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)) if (IS_ERR(fifo->tx_chan))
fifo->tx_chan = NULL; 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)) if (IS_ERR(fifo->rx_chan))
fifo->rx_chan = NULL; fifo->rx_chan = NULL;
} }
static void usbhsf_dma_init(struct usbhs_priv *priv, static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,
struct usbhs_fifo *fifo) int channel)
{ {
struct device *dev = usbhs_priv_to_dev(priv); struct device *dev = usbhs_priv_to_dev(priv);
if (dev->of_node) if (dev->of_node)
usbhsf_dma_init_dt(dev, fifo); usbhsf_dma_init_dt(dev, fifo, channel);
else else
usbhsf_dma_init_pdev(fifo); usbhsf_dma_init_pdev(fifo);
...@@ -1231,7 +1390,7 @@ do { \ ...@@ -1231,7 +1390,7 @@ do { \
usbhs_get_dparam(priv, d##channel##_tx_id); \ usbhs_get_dparam(priv, d##channel##_tx_id); \
fifo->rx_slave.shdma_slave.slave_id = \ fifo->rx_slave.shdma_slave.slave_id = \
usbhs_get_dparam(priv, d##channel##_rx_id); \ usbhs_get_dparam(priv, d##channel##_rx_id); \
usbhsf_dma_init(priv, fifo); \ usbhsf_dma_init(priv, fifo, channel); \
} while (0) } while (0)
#define USBHS_DFIFO_INIT(priv, fifo, channel) \ #define USBHS_DFIFO_INIT(priv, fifo, channel) \
......
...@@ -58,6 +58,7 @@ struct usbhs_pkt { ...@@ -58,6 +58,7 @@ struct usbhs_pkt {
struct usbhs_pkt *pkt); struct usbhs_pkt *pkt);
struct work_struct work; struct work_struct work;
dma_addr_t dma; dma_addr_t dma;
dma_cookie_t cookie;
void *buf; void *buf;
int length; int length;
int trans; int trans;
......
...@@ -119,18 +119,34 @@ struct usbhsg_recip_handle { ...@@ -119,18 +119,34 @@ struct usbhsg_recip_handle {
/* /*
* queue push/pop * queue push/pop
*/ */
static void usbhsg_queue_pop(struct usbhsg_uep *uep, static void __usbhsg_queue_pop(struct usbhsg_uep *uep,
struct usbhsg_request *ureq, struct usbhsg_request *ureq,
int status) int status)
{ {
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
struct device *dev = usbhsg_gpriv_to_dev(gpriv); 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)); dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
ureq->req.status = status; ureq->req.status = status;
spin_unlock(usbhs_priv_to_lock(priv));
usb_gadget_giveback_request(&uep->ep, &ureq->req); 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) static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
......
...@@ -84,6 +84,17 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe, ...@@ -84,6 +84,17 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
usbhs_bset(priv, pipe_reg, mask, val); 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 * DCPCFG/PIPECFG functions
*/ */
...@@ -92,6 +103,11 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val) ...@@ -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); __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 * PIPEnTRN/PIPEnTRE functions
*/ */
...@@ -616,6 +632,11 @@ void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence) ...@@ -616,6 +632,11 @@ void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
usbhsp_pipectrl_set(pipe, mask, val); 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) void usbhs_pipe_clear(struct usbhs_pipe *pipe)
{ {
if (usbhs_pipe_is_dcp(pipe)) { if (usbhs_pipe_is_dcp(pipe)) {
...@@ -626,6 +647,24 @@ void usbhs_pipe_clear(struct usbhs_pipe *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) static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
{ {
struct usbhs_pipe *pos, *pipe; struct usbhs_pipe *pos, *pipe;
......
...@@ -97,6 +97,7 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len); ...@@ -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_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
u16 epnum, u16 maxp); 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_data0(pipe) usbhs_pipe_data_sequence(pipe, 0)
#define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1) #define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1)
......
...@@ -2315,6 +2315,8 @@ ...@@ -2315,6 +2315,8 @@
#define PCI_VENDOR_ID_CENATEK 0x16CA #define PCI_VENDOR_ID_CENATEK 0x16CA
#define PCI_DEVICE_ID_CENATEK_IDE 0x0001 #define PCI_DEVICE_ID_CENATEK_IDE 0x0001
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_VENDOR_ID_VITESSE 0x1725 #define PCI_VENDOR_ID_VITESSE 0x1725
#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174 #define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174
......
...@@ -148,6 +148,7 @@ struct usb_os_desc_table { ...@@ -148,6 +148,7 @@ struct usb_os_desc_table {
* @disable: (REQUIRED) Indicates the function should be disabled. Reasons * @disable: (REQUIRED) Indicates the function should be disabled. Reasons
* include host resetting or reconfiguring the gadget, and disconnection. * include host resetting or reconfiguring the gadget, and disconnection.
* @setup: Used for interface-specific control requests. * @setup: Used for interface-specific control requests.
* @req_match: Tests if a given class request can be handled by this function.
* @suspend: Notifies functions when the host stops sending USB traffic. * @suspend: Notifies functions when the host stops sending USB traffic.
* @resume: Notifies functions when the host restarts USB traffic. * @resume: Notifies functions when the host restarts USB traffic.
* @get_status: Returns function status as a reply to * @get_status: Returns function status as a reply to
...@@ -213,6 +214,8 @@ struct usb_function { ...@@ -213,6 +214,8 @@ struct usb_function {
void (*disable)(struct usb_function *); void (*disable)(struct usb_function *);
int (*setup)(struct usb_function *, int (*setup)(struct usb_function *,
const struct usb_ctrlrequest *); const struct usb_ctrlrequest *);
bool (*req_match)(struct usb_function *,
const struct usb_ctrlrequest *);
void (*suspend)(struct usb_function *); void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *); void (*resume)(struct usb_function *);
......
...@@ -190,7 +190,7 @@ struct usb_ep { ...@@ -190,7 +190,7 @@ struct usb_ep {
* @ep:the endpoint being configured * @ep:the endpoint being configured
* @maxpacket_limit:value of maximum packet size limit * @maxpacket_limit:value of maximum packet size limit
* *
* This function shoud be used only in UDC drivers to initialize endpoint * This function should be used only in UDC drivers to initialize endpoint
* (usually in probe function). * (usually in probe function).
*/ */
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep, static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
...@@ -474,6 +474,7 @@ struct usb_dcd_config_params { ...@@ -474,6 +474,7 @@ struct usb_dcd_config_params {
struct usb_gadget; struct usb_gadget;
struct usb_gadget_driver; struct usb_gadget_driver;
struct usb_udc;
/* the rest of the api to the controller hardware: device operations, /* the rest of the api to the controller hardware: device operations,
* which don't involve endpoints (or i/o). * which don't involve endpoints (or i/o).
...@@ -496,6 +497,7 @@ struct usb_gadget_ops { ...@@ -496,6 +497,7 @@ struct usb_gadget_ops {
/** /**
* struct usb_gadget - represents a usb slave device * struct usb_gadget - represents a usb slave device
* @work: (internal use) Workqueue to be used for sysfs_notify() * @work: (internal use) Workqueue to be used for sysfs_notify()
* @udc: struct usb_udc pointer for this gadget
* @ops: Function pointers used to access hardware-specific operations. * @ops: Function pointers used to access hardware-specific operations.
* @ep0: Endpoint zero, used when reading or writing responses to * @ep0: Endpoint zero, used when reading or writing responses to
* driver setup() requests * driver setup() requests
...@@ -545,6 +547,7 @@ struct usb_gadget_ops { ...@@ -545,6 +547,7 @@ struct usb_gadget_ops {
*/ */
struct usb_gadget { struct usb_gadget {
struct work_struct work; struct work_struct work;
struct usb_udc *udc;
/* readonly to gadget driver */ /* readonly to gadget driver */
const struct usb_gadget_ops *ops; const struct usb_gadget_ops *ops;
struct usb_ep *ep0; struct usb_ep *ep0;
...@@ -1029,6 +1032,10 @@ extern void usb_gadget_udc_reset(struct usb_gadget *gadget, ...@@ -1029,6 +1032,10 @@ extern void usb_gadget_udc_reset(struct usb_gadget *gadget,
extern void usb_gadget_giveback_request(struct usb_ep *ep, extern void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req); struct usb_request *req);
/*-------------------------------------------------------------------------*/
/* utility to update vbus status for udc core, it may be scheduled */
extern void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
...@@ -165,6 +165,8 @@ struct renesas_usbhs_driver_param { ...@@ -165,6 +165,8 @@ struct renesas_usbhs_driver_param {
*/ */
u32 has_otg:1; /* for controlling PWEN/EXTLP */ u32 has_otg:1; /* for controlling PWEN/EXTLP */
u32 has_sudmac:1; /* for SUDMAC */ u32 has_sudmac:1; /* for SUDMAC */
u32 has_usb_dmac:1; /* for USB-DMAC */
#define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */
}; };
#define USBHS_TYPE_R8A7790 1 #define USBHS_TYPE_R8A7790 1
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册