forked from Minki/linux
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: Felipe Balbi <balbi@ti.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVEZlvAAoJEIaOsuA1yqREvPMP/1dxQ6ufjkfmBmeZWiDWmwQs uoLskjNqVlnmcjWdasEwUSKMJMYfxNAmabYCx7ZWdmcDi0FNvmrjSfXqmM5uG5bF UKgP/7KbiK9WSRAcAuFEqYCMcAoH2CcLhlZ7BF7Peo+WxeQrh0A4ZPxh4VVJI2D4 47/bpXFqPydVZyZurpfB0QA1r5jjsG+tJdP/LJKv6/g38XSBs4cgFUzzBWDN8212 YVKlCiRWRwnCOrYFP1pf/gR4SM8UF4Vd+lDZ28JnwcxCmTKKMhAAoHjgckV6UctW Ur+hshsuvPwFWm9Rqp77TnO37LranaFUR2W2cBUGiPSZaUeXsdOhORpT+S64nuZi ZEw4qp9QlRwH8wCBT0m/Uozv+CZ7VP41hjYdFpcUevd/XLPQjx1ddn032jGYsrtC o+drLGtuULimusf/14zs1X4ejLdeYy6f0hLzqyDS8zTHAjToAmrM3Uw9nQWxWauB rHdTYMrgMGoolnIFVSLgR6xbGMu9wCYexylKFwVWEV6xrj4ZgpoVitUGg5DphCQy FdEepkeWgAUTcYC3wcIUHHC9wVS3YgPWRmyKjnh65kIFDGjPjHMK1eW06RGuhld/ A92UJNIga34RtUXSUrcuwRBtWY+pDE/6XxppqzxTKAMjT9HV8JNPZGHu+2nKP0kp qu9PqszUGOyCuTG9+A+E =U+M2 -----END PGP SIGNATURE----- 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: Felipe Balbi <balbi@ti.com>
This commit is contained in:
commit
1c41a9570a
9
Documentation/ABI/testing/configfs-usb-gadget-printer
Normal file
9
Documentation/ABI/testing/configfs-usb-gadget-printer
Normal file
@ -0,0 +1,9 @@
|
||||
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:
|
||||
- phys: from the *Generic PHY* bindings
|
||||
- phy-names: from the *Generic PHY* bindings
|
||||
- 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.
|
||||
Only really useful for FPGA builds.
|
||||
- snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
|
||||
|
@ -15,7 +15,10 @@ Optional properties:
|
||||
- phys: phandle + phy specifier pair
|
||||
- phy-names: must be "usb"
|
||||
- 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:
|
||||
usbhs: usb@e6590000 {
|
||||
|
@ -19,6 +19,7 @@ provided by gadgets.
|
||||
16. UAC1 function
|
||||
17. UAC2 function
|
||||
18. UVC function
|
||||
19. PRINTER function
|
||||
|
||||
|
||||
1. ACM function
|
||||
@ -726,3 +727,49 @@ with these patches:
|
||||
http://www.spinics.net/lists/linux-usb/msg99220.html
|
||||
|
||||
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)
|
||||
/* interrupt, error, port change, reset, sleep/suspend */
|
||||
hw_write(ci, OP_USBINTR, ~0,
|
||||
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
|
||||
} else {
|
||||
hw_write(ci, OP_USBINTR, ~0, 0);
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1508,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
hw_device_reset(ci);
|
||||
hw_device_state(ci, ci->ep0out->qh.dma);
|
||||
usb_gadget_set_state(_gadget, USB_STATE_POWERED);
|
||||
usb_udc_vbus_handler(_gadget, true);
|
||||
} else {
|
||||
usb_udc_vbus_handler(_gadget, false);
|
||||
if (ci->driver)
|
||||
ci->driver->disconnect(&ci->gadget);
|
||||
hw_device_state(ci, 0);
|
||||
@ -1574,13 +1574,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
|
||||
|
||||
if (!ci->vbus_active)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
pm_runtime_get_sync(&ci->gadget.dev);
|
||||
if (is_on)
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
|
||||
else
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
|
||||
pm_runtime_put_sync(&ci->gadget.dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1710,6 +1709,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
|
||||
spin_lock_irqsave(&ci->lock, flags);
|
||||
hw_device_reset(ci);
|
||||
} else {
|
||||
usb_udc_vbus_handler(&ci->gadget, false);
|
||||
pm_runtime_put_sync(&ci->gadget.dev);
|
||||
return retval;
|
||||
}
|
||||
|
@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM
|
||||
|
||||
config USB_DWC2_PCI
|
||||
tristate "DWC2 PCI"
|
||||
depends on USB_DWC2_HOST && PCI
|
||||
default USB_DWC2_HOST
|
||||
depends on PCI
|
||||
default n
|
||||
select USB_DWC2_PLATFORM
|
||||
select NOP_USB_XCEIV
|
||||
help
|
||||
The Designware USB2.0 PCI interface module for controllers
|
||||
connected to a PCI bus. This is only used for host mode.
|
||||
connected to a PCI bus.
|
||||
|
||||
config USB_DWC2_DEBUG
|
||||
bool "Enable Debugging Messages"
|
||||
|
@ -19,10 +19,8 @@ endif
|
||||
# mode. The PCI bus interface module will called dwc2_pci.ko and the platform
|
||||
# interface module will be called dwc2_platform.ko.
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC2_PCI),)
|
||||
obj-$(CONFIG_USB_DWC2) += dwc2_pci.o
|
||||
dwc2_pci-y := pci.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o
|
||||
dwc2_pci-y := pci.o
|
||||
|
||||
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
|
||||
dwc2_platform-y := platform.o
|
||||
|
@ -593,6 +593,8 @@ struct dwc2_hsotg {
|
||||
struct dwc2_core_params *core_params;
|
||||
enum usb_otg_state op_state;
|
||||
enum usb_dr_mode dr_mode;
|
||||
unsigned int hcd_enabled:1;
|
||||
unsigned int gadget_enabled:1;
|
||||
|
||||
struct phy *phy;
|
||||
struct usb_phy *uphy;
|
||||
|
@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
|
||||
*/
|
||||
channel->qh = NULL;
|
||||
}
|
||||
/* All channels have been freed, mark them available */
|
||||
if (hsotg->core_params->uframe_sched > 0) {
|
||||
hsotg->available_host_channels =
|
||||
hsotg->core_params->host_channels;
|
||||
} else {
|
||||
hsotg->non_periodic_channels = 0;
|
||||
hsotg->periodic_channels = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,113 +50,97 @@
|
||||
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/ch11.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/usb_phy_generic.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "hcd.h"
|
||||
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
|
||||
#define PCI_PRODUCT_ID_HAPS_HSOTG 0xabc0
|
||||
|
||||
static const char dwc2_driver_name[] = "dwc2";
|
||||
static const char dwc2_driver_name[] = "dwc2-pci";
|
||||
|
||||
static const struct dwc2_core_params dwc2_module_params = {
|
||||
.otg_cap = -1,
|
||||
.otg_ver = -1,
|
||||
.dma_enable = -1,
|
||||
.dma_desc_enable = 0,
|
||||
.speed = -1,
|
||||
.enable_dynamic_fifo = -1,
|
||||
.en_multiple_tx_fifo = -1,
|
||||
.host_rx_fifo_size = 1024,
|
||||
.host_nperio_tx_fifo_size = 256,
|
||||
.host_perio_tx_fifo_size = 1024,
|
||||
.max_transfer_size = 65535,
|
||||
.max_packet_count = 511,
|
||||
.host_channels = -1,
|
||||
.phy_type = -1,
|
||||
.phy_utmi_width = -1,
|
||||
.phy_ulpi_ddr = -1,
|
||||
.phy_ulpi_ext_vbus = -1,
|
||||
.i2c_enable = -1,
|
||||
.ulpi_fs_ls = -1,
|
||||
.host_support_fs_ls_low_power = -1,
|
||||
.host_ls_low_power_phy_clk = -1,
|
||||
.ts_dline = -1,
|
||||
.reload_ctl = -1,
|
||||
.ahbcfg = -1,
|
||||
.uframe_sched = -1,
|
||||
struct dwc2_pci_glue {
|
||||
struct platform_device *dwc2;
|
||||
struct platform_device *phy;
|
||||
};
|
||||
|
||||
/**
|
||||
* dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
|
||||
* DWC_otg driver
|
||||
*
|
||||
* @dev: Bus device
|
||||
*
|
||||
* This routine is called, for example, when the rmmod command is executed. The
|
||||
* device may or may not be electrically present. If it is present, the driver
|
||||
* stops device processing. Any resources used on behalf of this device are
|
||||
* freed.
|
||||
*/
|
||||
static void dwc2_driver_remove(struct pci_dev *dev)
|
||||
static void dwc2_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = pci_get_drvdata(dev);
|
||||
struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
|
||||
|
||||
dwc2_hcd_remove(hsotg);
|
||||
pci_disable_device(dev);
|
||||
platform_device_unregister(glue->dwc2);
|
||||
usb_phy_generic_unregister(glue->phy);
|
||||
kfree(glue);
|
||||
pci_set_drvdata(pci, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
|
||||
* driver
|
||||
*
|
||||
* @dev: Bus device
|
||||
*
|
||||
* This routine creates the driver components required to control the device
|
||||
* (core, HCD, and PCD) and initializes the device. The driver components are
|
||||
* stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved
|
||||
* in the device private data. This allows the driver to access the dwc2_hsotg
|
||||
* structure on subsequent calls to driver methods for this device.
|
||||
*/
|
||||
static int dwc2_driver_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
static int dwc2_pci_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg;
|
||||
int retval;
|
||||
struct resource res[2];
|
||||
struct platform_device *dwc2;
|
||||
struct platform_device *phy;
|
||||
int ret;
|
||||
struct device *dev = &pci->dev;
|
||||
struct dwc2_pci_glue *glue;
|
||||
|
||||
hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
|
||||
if (!hsotg)
|
||||
return -ENOMEM;
|
||||
|
||||
hsotg->dev = &dev->dev;
|
||||
hsotg->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]);
|
||||
if (IS_ERR(hsotg->regs))
|
||||
return PTR_ERR(hsotg->regs);
|
||||
|
||||
dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
|
||||
(unsigned long)pci_resource_start(dev, 0), hsotg->regs);
|
||||
|
||||
if (pci_enable_device(dev) < 0)
|
||||
ret = pcim_enable_device(pci);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable pci device\n");
|
||||
return -ENODEV;
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
retval = devm_request_irq(hsotg->dev, dev->irq,
|
||||
dwc2_handle_common_intr, IRQF_SHARED,
|
||||
dev_name(hsotg->dev), hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params);
|
||||
if (retval) {
|
||||
pci_disable_device(dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pci_set_drvdata(dev, hsotg);
|
||||
pci_set_master(pci);
|
||||
|
||||
return retval;
|
||||
dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
|
||||
if (!dwc2) {
|
||||
dev_err(dev, "couldn't allocate dwc2 device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
|
||||
|
||||
res[0].start = pci_resource_start(pci, 0);
|
||||
res[0].end = pci_resource_end(pci, 0);
|
||||
res[0].name = "dwc2";
|
||||
res[0].flags = IORESOURCE_MEM;
|
||||
|
||||
res[1].start = pci->irq;
|
||||
res[1].name = "dwc2";
|
||||
res[1].flags = IORESOURCE_IRQ;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
dwc2->dev.parent = dev;
|
||||
|
||||
phy = usb_phy_generic_register();
|
||||
if (IS_ERR(phy)) {
|
||||
dev_err(dev, "error registering generic PHY (%ld)\n",
|
||||
PTR_ERR(phy));
|
||||
return PTR_ERR(phy);
|
||||
}
|
||||
|
||||
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 0;
|
||||
err:
|
||||
usb_phy_generic_unregister(phy);
|
||||
platform_device_put(dwc2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pci_device_id dwc2_pci_ids[] = {
|
||||
@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
|
||||
static struct pci_driver dwc2_pci_driver = {
|
||||
.name = dwc2_driver_name,
|
||||
.id_table = dwc2_pci_ids,
|
||||
.probe = dwc2_driver_probe,
|
||||
.remove = dwc2_driver_remove,
|
||||
.probe = dwc2_pci_probe,
|
||||
.remove = dwc2_pci_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(dwc2_pci_driver);
|
||||
|
@ -121,8 +121,10 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
|
||||
|
||||
dwc2_hcd_remove(hsotg);
|
||||
s3c_hsotg_remove(hsotg);
|
||||
if (hsotg->hcd_enabled)
|
||||
dwc2_hcd_remove(hsotg);
|
||||
if (hsotg->gadget_enabled)
|
||||
s3c_hsotg_remove(hsotg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
|
||||
spin_lock_init(&hsotg->lock);
|
||||
mutex_init(&hsotg->init_mutex);
|
||||
retval = dwc2_gadget_init(hsotg, irq);
|
||||
if (retval)
|
||||
return retval;
|
||||
retval = dwc2_hcd_init(hsotg, irq, params);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
retval = dwc2_gadget_init(hsotg, irq);
|
||||
if (retval)
|
||||
return retval;
|
||||
hsotg->gadget_enabled = 1;
|
||||
}
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
|
||||
retval = dwc2_hcd_init(hsotg, irq, params);
|
||||
if (retval) {
|
||||
if (hsotg->gadget_enabled)
|
||||
s3c_hsotg_remove(hsotg);
|
||||
return retval;
|
||||
}
|
||||
hsotg->hcd_enabled = 1;
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, hsotg);
|
||||
|
||||
|
@ -104,11 +104,4 @@ config USB_DWC3_DEBUG
|
||||
help
|
||||
Say Y here to enable debugging messages on DWC3 Driver.
|
||||
|
||||
config DWC3_HOST_USB3_LPM_ENABLE
|
||||
bool "Enable USB3 LPM Capability"
|
||||
depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
|
||||
default n
|
||||
help
|
||||
Select this when you want to enable USB3 LPM with dwc3 xhci host.
|
||||
|
||||
endif
|
||||
|
@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
* since it will be requested by the xhci-plat driver.
|
||||
*/
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
if (IS_ERR(regs)) {
|
||||
ret = PTR_ERR(regs);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(res);
|
||||
/*
|
||||
* restore res->start back to its original value so that,
|
||||
* in case the probe is deferred, we don't end up getting error in
|
||||
* request the memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/* default to highest possible threshold */
|
||||
lpm_nyet_threshold = 0xff;
|
||||
@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
"snps,is-utmi-l1-suspend");
|
||||
of_property_read_u8(node, "snps,hird-threshold",
|
||||
&hird_threshold);
|
||||
dwc->usb3_lpm_capable = of_property_read_bool(node,
|
||||
"snps,usb3_lpm_capable");
|
||||
|
||||
dwc->needs_fifo_resize = of_property_read_bool(node,
|
||||
"tx-fifo-resize");
|
||||
@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
hird_threshold = pdata->hird_threshold;
|
||||
|
||||
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
|
||||
dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
|
||||
dwc->dr_mode = pdata->dr_mode;
|
||||
|
||||
dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
|
||||
@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
|
||||
ret = dwc3_core_get_phy(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err0;
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to allocate event buffers\n");
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
|
||||
@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
goto err0;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
goto err2;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_power;
|
||||
goto err3;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err_usb3phy_power;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err2;
|
||||
goto err5;
|
||||
|
||||
ret = dwc3_debugfs_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize debugfs\n");
|
||||
goto err3;
|
||||
goto err6;
|
||||
}
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
err6:
|
||||
dwc3_core_exit_mode(dwc);
|
||||
|
||||
err2:
|
||||
err5:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
|
||||
err_usb3phy_power:
|
||||
err4:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
err_usb2phy_power:
|
||||
err3:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
||||
err1:
|
||||
err2:
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
dwc3_core_exit(dwc);
|
||||
|
||||
err0:
|
||||
err1:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
err0:
|
||||
/*
|
||||
* restore res->start back to its original value so that, in case the
|
||||
* probe is deferred, we don't end up getting error in request the
|
||||
* memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
/*
|
||||
* restore res->start back to its original value so that, in case the
|
||||
* probe is deferred, we don't end up getting error in request the
|
||||
* memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
dwc3_debugfs_exit(dwc);
|
||||
dwc3_core_exit_mode(dwc);
|
||||
|
@ -689,6 +689,7 @@ struct dwc3_scratchpad_array {
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
* @three_stage_setup: set if we perform a three phase setup
|
||||
* @usb3_lpm_capable: set if hadrware supports Link Power Management
|
||||
* @disable_scramble_quirk: set if we enable the disable scramble quirk
|
||||
* @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
|
||||
* @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
|
||||
@ -812,6 +813,7 @@ struct dwc3 {
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned start_config_issued:1;
|
||||
unsigned three_stage_setup:1;
|
||||
unsigned usb3_lpm_capable:1;
|
||||
|
||||
unsigned disable_scramble_quirk:1;
|
||||
unsigned u2exit_lfps_quirk:1;
|
||||
|
@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dwc3_omap_remove_core(struct device *dev, void *c)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
of_device_unregister(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
|
||||
{
|
||||
u32 reg;
|
||||
@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev)
|
||||
if (omap->extcon_id_dev.edev)
|
||||
extcon_unregister_interest(&omap->extcon_id_dev);
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
|
||||
of_platform_depopulate(omap->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
|
@ -24,8 +24,6 @@
|
||||
|
||||
#include "platform_data.h"
|
||||
|
||||
/* FIXME define these in <linux/pci_ids.h> */
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
|
||||
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
|
||||
#define PCI_DEVICE_ID_INTEL_BYT 0x0f37
|
||||
#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e
|
||||
|
@ -1855,32 +1855,27 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
return 1;
|
||||
}
|
||||
i = 0;
|
||||
do {
|
||||
req = next_request(&dep->req_queued);
|
||||
if (!req) {
|
||||
WARN_ON_ONCE(1);
|
||||
return 1;
|
||||
}
|
||||
i = 0;
|
||||
do {
|
||||
slot = req->start_slot + i;
|
||||
if ((slot == DWC3_TRB_NUM - 1) &&
|
||||
slot = req->start_slot + i;
|
||||
if ((slot == DWC3_TRB_NUM - 1) &&
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc))
|
||||
slot++;
|
||||
slot %= DWC3_TRB_NUM;
|
||||
trb = &dep->trb_pool[slot];
|
||||
|
||||
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
|
||||
event, status);
|
||||
if (ret)
|
||||
break;
|
||||
}while (++i < req->request.num_mapped_sgs);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
slot++;
|
||||
slot %= DWC3_TRB_NUM;
|
||||
trb = &dep->trb_pool[slot];
|
||||
|
||||
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
|
||||
event, status);
|
||||
if (ret)
|
||||
break;
|
||||
} while (1);
|
||||
} while (++i < req->request.num_mapped_sgs);
|
||||
|
||||
dwc3_gadget_giveback(dep, req, status);
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
||||
list_empty(&dep->req_queued)) {
|
||||
|
@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc)
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
|
||||
#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
|
||||
pdata.usb3_lpm_capable = 1;
|
||||
#endif
|
||||
pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
|
||||
|
||||
ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
|
||||
if (ret) {
|
||||
|
@ -24,6 +24,7 @@ struct dwc3_platform_data {
|
||||
enum usb_device_speed maximum_speed;
|
||||
enum usb_dr_mode dr_mode;
|
||||
bool tx_fifo_resize;
|
||||
bool usb3_lpm_capable;
|
||||
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
u8 hird_threshold;
|
||||
|
@ -196,6 +196,9 @@ config USB_F_MIDI
|
||||
config USB_F_HID
|
||||
tristate
|
||||
|
||||
config USB_F_PRINTER
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC
|
||||
device. It provides a userspace API to process UVC control requests
|
||||
and stream video data to the host.
|
||||
|
||||
config USB_CONFIGFS_F_PRINTER
|
||||
bool "Printer function"
|
||||
select USB_F_PRINTER
|
||||
depends on USB_CONFIGFS
|
||||
help
|
||||
The Printer function channels data between the USB host and a
|
||||
userspace program driving the print engine. The user space
|
||||
program reads and writes the device file /dev/g_printer<X> to
|
||||
receive or send printer data. It can use ioctl calls to
|
||||
the device file to get or set printer status.
|
||||
|
||||
For more information, see Documentation/usb/gadget_printer.txt
|
||||
which includes sample code for accessing the device file.
|
||||
|
||||
source "drivers/usb/gadget/legacy/Kconfig"
|
||||
|
||||
endchoice
|
||||
|
@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings(
|
||||
* This function will create a deep copy of usb_gadget_strings and usb_string
|
||||
* and attach it to the cdev. The actual string (usb_string.s) will not be
|
||||
* copied but only a referenced will be made. The struct usb_gadget_strings
|
||||
* array may contain multiple languges and should be NULL terminated.
|
||||
* array may contain multiple languages and should be NULL terminated.
|
||||
* The ->language pointer of each struct usb_gadget_strings has to contain the
|
||||
* same amount of entries.
|
||||
* For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
|
||||
* usb_string entry of es-ES containts the translation of the first usb_string
|
||||
* usb_string entry of es-ES contains the translation of the first usb_string
|
||||
* entry of en-US. Therefore both entries become the same id assign.
|
||||
*/
|
||||
struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
|
||||
@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
||||
req->length = 0;
|
||||
gadget->ep0->driver_data = cdev;
|
||||
|
||||
/*
|
||||
* Don't let non-standard requests match any of the cases below
|
||||
* by accident.
|
||||
*/
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
|
||||
goto unknown;
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
|
||||
/* we handle all standard USB descriptors */
|
||||
@ -1751,6 +1758,10 @@ unknown:
|
||||
* take such requests too, if that's ever needed: to work
|
||||
* in config 0, etc.
|
||||
*/
|
||||
list_for_each_entry(f, &cdev->config->functions, list)
|
||||
if (f->req_match && f->req_match(f, ctrl))
|
||||
goto try_fun_setup;
|
||||
f = NULL;
|
||||
switch (ctrl->bRequestType & USB_RECIP_MASK) {
|
||||
case USB_RECIP_INTERFACE:
|
||||
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
|
||||
@ -1768,7 +1779,7 @@ unknown:
|
||||
f = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
try_fun_setup:
|
||||
if (f && f->setup)
|
||||
value = f->setup(f, ctrl);
|
||||
else {
|
||||
|
@ -42,3 +42,5 @@ usb_f_midi-y := f_midi.o
|
||||
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
|
||||
usb_f_hid-y := f_hid.o
|
||||
obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
|
||||
usb_f_printer-y := f_printer.o
|
||||
obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o
|
||||
|
@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
|
||||
/* disable/free request and end point */
|
||||
usb_ep_disable(hidg->in_ep);
|
||||
usb_ep_dequeue(hidg->in_ep, hidg->req);
|
||||
kfree(hidg->req->buf);
|
||||
usb_ep_free_request(hidg->in_ep, hidg->req);
|
||||
|
||||
|
@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
|
||||
if (!curlun) { /* Unsupported LUNs are okay */
|
||||
common->bad_lun_okay = 1;
|
||||
memset(buf, 0, 36);
|
||||
buf[0] = 0x7f; /* Unsupported, no device-type */
|
||||
buf[0] = TYPE_NO_LUN; /* Unsupported, no device-type */
|
||||
buf[4] = 31; /* Additional length */
|
||||
return 36;
|
||||
}
|
||||
|
1471
drivers/usb/gadget/function/f_printer.c
Normal file
1471
drivers/usb/gadget/function/f_printer.c
Normal file
File diff suppressed because it is too large
Load Diff
37
drivers/usb/gadget/function/u_printer.h
Normal file
37
drivers/usb/gadget/function/u_printer.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* u_printer.h
|
||||
*
|
||||
* Utility definitions for the printer function
|
||||
*
|
||||
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef U_PRINTER_H
|
||||
#define U_PRINTER_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#define PNP_STRING_LEN 1024
|
||||
|
||||
struct f_printer_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
int minor;
|
||||
char pnp_string[PNP_STRING_LEN];
|
||||
unsigned q_len;
|
||||
|
||||
/*
|
||||
* Protect the data from concurrent access by read/write
|
||||
* and create symlink/remove symlink
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
#endif /* U_PRINTER_H */
|
@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
|
||||
pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n",
|
||||
port->port_num, tty, ch, __builtin_return_address(0));
|
||||
|
||||
spin_lock_irqsave(&port->port_lock, flags);
|
||||
|
@ -301,6 +301,7 @@ config USB_MIDI_GADGET
|
||||
config USB_G_PRINTER
|
||||
tristate "Printer Gadget"
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_PRINTER
|
||||
help
|
||||
The Printer Gadget channels data between the USB host and a
|
||||
userspace program driving the print engine. The user space
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file)
|
||||
|
||||
spin_lock_irq(&udc->lock);
|
||||
for (i = 0; i < inode->i_size / 4; i++)
|
||||
data[i] = __raw_readl(udc->regs + i * 4);
|
||||
data[i] = usba_io_readl(udc->regs + i * 4);
|
||||
spin_unlock_irq(&udc->lock);
|
||||
|
||||
file->private_data = data;
|
||||
@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
|
||||
if (crq->wLength != cpu_to_le16(sizeof(status)))
|
||||
goto stall;
|
||||
ep->state = DATA_STAGE_IN;
|
||||
__raw_writew(status, ep->fifo);
|
||||
usba_io_writew(status, ep->fifo);
|
||||
usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
|
||||
break;
|
||||
}
|
||||
@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t usba_vbus_irq(int irq, void *devid)
|
||||
static int start_clock(struct usba_udc *udc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (udc->clocked)
|
||||
return 0;
|
||||
|
||||
ret = clk_prepare_enable(udc->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clk_prepare_enable(udc->hclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
udc->clocked = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stop_clock(struct usba_udc *udc)
|
||||
{
|
||||
if (!udc->clocked)
|
||||
return;
|
||||
|
||||
clk_disable_unprepare(udc->hclk);
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
|
||||
udc->clocked = false;
|
||||
}
|
||||
|
||||
static int usba_start(struct usba_udc *udc)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
ret = start_clock(udc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
toggle_bias(udc, 1);
|
||||
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
|
||||
usba_int_enb_set(udc, USBA_END_OF_RESET);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usba_stop(struct usba_udc *udc)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
reset_all_endpoints(udc);
|
||||
|
||||
/* This will also disable the DP pullup */
|
||||
toggle_bias(udc, 0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
stop_clock(udc);
|
||||
}
|
||||
|
||||
static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
|
||||
{
|
||||
struct usba_udc *udc = devid;
|
||||
int vbus;
|
||||
@ -1747,35 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
|
||||
/* debounce */
|
||||
udelay(10);
|
||||
|
||||
spin_lock(&udc->lock);
|
||||
|
||||
/* May happen if Vbus pin toggles during probe() */
|
||||
if (!udc->driver)
|
||||
goto out;
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
|
||||
vbus = vbus_is_present(udc);
|
||||
if (vbus != udc->vbus_prev) {
|
||||
if (vbus) {
|
||||
toggle_bias(udc, 1);
|
||||
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
|
||||
usba_int_enb_set(udc, USBA_END_OF_RESET);
|
||||
usba_start(udc);
|
||||
} else {
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
reset_all_endpoints(udc);
|
||||
toggle_bias(udc, 0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
if (udc->driver->disconnect) {
|
||||
spin_unlock(&udc->lock);
|
||||
usba_stop(udc);
|
||||
|
||||
if (udc->driver->disconnect)
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
spin_lock(&udc->lock);
|
||||
}
|
||||
}
|
||||
udc->vbus_prev = vbus;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&udc->lock);
|
||||
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget,
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
|
||||
udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
|
||||
udc->driver = driver;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
ret = clk_prepare_enable(udc->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clk_prepare_enable(udc->hclk);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
return ret;
|
||||
}
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
|
||||
udc->vbus_prev = 0;
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
enable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
/* If Vbus is present, enable the controller and wait for reset */
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
if (vbus_is_present(udc) && udc->vbus_prev == 0) {
|
||||
toggle_bias(udc, 1);
|
||||
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
|
||||
usba_int_enb_set(udc, USBA_END_OF_RESET);
|
||||
udc->vbus_prev = vbus_is_present(udc);
|
||||
if (udc->vbus_prev) {
|
||||
ret = usba_start(udc);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
disable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
|
||||
udc->driver = NULL;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atmel_usba_stop(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
|
||||
unsigned long flags;
|
||||
|
||||
if (gpio_is_valid(udc->vbus_pin))
|
||||
disable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
udc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
reset_all_endpoints(udc);
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
/* This will also disable the DP pullup */
|
||||
toggle_bias(udc, 0);
|
||||
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
|
||||
|
||||
clk_disable_unprepare(udc->hclk);
|
||||
clk_disable_unprepare(udc->pclk);
|
||||
usba_stop(udc);
|
||||
|
||||
udc->driver = NULL;
|
||||
|
||||
@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(hclk);
|
||||
|
||||
spin_lock_init(&udc->lock);
|
||||
mutex_init(&udc->vbus_mutex);
|
||||
udc->pdev = pdev;
|
||||
udc->pclk = pclk;
|
||||
udc->hclk = hclk;
|
||||
@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev)
|
||||
|
||||
if (gpio_is_valid(udc->vbus_pin)) {
|
||||
if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
|
||||
ret = devm_request_irq(&pdev->dev,
|
||||
gpio_to_irq(udc->vbus_pin),
|
||||
usba_vbus_irq, 0,
|
||||
irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
|
||||
IRQ_NOAUTOEN);
|
||||
ret = devm_request_threaded_irq(&pdev->dev,
|
||||
gpio_to_irq(udc->vbus_pin), NULL,
|
||||
usba_vbus_irq_thread, IRQF_ONESHOT,
|
||||
"atmel_usba_udc", udc);
|
||||
if (ret) {
|
||||
udc->vbus_pin = -ENODEV;
|
||||
dev_warn(&udc->pdev->dev,
|
||||
"failed to request vbus irq; "
|
||||
"assuming always on\n");
|
||||
} else {
|
||||
disable_irq(gpio_to_irq(udc->vbus_pin));
|
||||
}
|
||||
} else {
|
||||
/* gpio_request fail so use -EINVAL for gpio_is_valid */
|
||||
@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev)
|
||||
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
|
||||
if (ret)
|
||||
return ret;
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
usba_init_debugfs(udc);
|
||||
for (i = 1; i < udc->num_ep; i++)
|
||||
@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
|
||||
|
||||
udc = platform_get_drvdata(pdev);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
usb_del_gadget_udc(&udc->gadget);
|
||||
|
||||
for (i = 1; i < udc->num_ep; i++)
|
||||
@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int usba_udc_suspend(struct device *dev)
|
||||
{
|
||||
struct usba_udc *udc = dev_get_drvdata(dev);
|
||||
|
||||
/* Not started */
|
||||
if (!udc->driver)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
|
||||
if (!device_may_wakeup(dev)) {
|
||||
usba_stop(udc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Device may wake up. We stay clocked if we failed
|
||||
* to request vbus irq, assuming always on.
|
||||
*/
|
||||
if (gpio_is_valid(udc->vbus_pin)) {
|
||||
usba_stop(udc);
|
||||
enable_irq_wake(gpio_to_irq(udc->vbus_pin));
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usba_udc_resume(struct device *dev)
|
||||
{
|
||||
struct usba_udc *udc = dev_get_drvdata(dev);
|
||||
|
||||
/* Not started */
|
||||
if (!udc->driver)
|
||||
return 0;
|
||||
|
||||
if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin))
|
||||
disable_irq_wake(gpio_to_irq(udc->vbus_pin));
|
||||
|
||||
/* If Vbus is present, enable the controller and wait for reset */
|
||||
mutex_lock(&udc->vbus_mutex);
|
||||
udc->vbus_prev = vbus_is_present(udc);
|
||||
if (udc->vbus_prev)
|
||||
usba_start(udc);
|
||||
mutex_unlock(&udc->vbus_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume);
|
||||
|
||||
static struct platform_driver udc_driver = {
|
||||
.remove = __exit_p(usba_udc_remove),
|
||||
.driver = {
|
||||
.name = "atmel_usba_udc",
|
||||
.pm = &usba_udc_pm_ops,
|
||||
.of_match_table = of_match_ptr(atmel_udc_dt_ids),
|
||||
},
|
||||
};
|
||||
|
@ -191,18 +191,28 @@
|
||||
| USBA_BF(name, value))
|
||||
|
||||
/* Register access macros */
|
||||
#ifdef CONFIG_AVR32
|
||||
#define usba_io_readl __raw_readl
|
||||
#define usba_io_writel __raw_writel
|
||||
#define usba_io_writew __raw_writew
|
||||
#else
|
||||
#define usba_io_readl readl_relaxed
|
||||
#define usba_io_writel writel_relaxed
|
||||
#define usba_io_writew writew_relaxed
|
||||
#endif
|
||||
|
||||
#define usba_readl(udc, reg) \
|
||||
__raw_readl((udc)->regs + USBA_##reg)
|
||||
usba_io_readl((udc)->regs + USBA_##reg)
|
||||
#define usba_writel(udc, reg, value) \
|
||||
__raw_writel((value), (udc)->regs + USBA_##reg)
|
||||
usba_io_writel((value), (udc)->regs + USBA_##reg)
|
||||
#define usba_ep_readl(ep, reg) \
|
||||
__raw_readl((ep)->ep_regs + USBA_EPT_##reg)
|
||||
usba_io_readl((ep)->ep_regs + USBA_EPT_##reg)
|
||||
#define usba_ep_writel(ep, reg, value) \
|
||||
__raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
|
||||
usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
|
||||
#define usba_dma_readl(ep, reg) \
|
||||
__raw_readl((ep)->dma_regs + USBA_DMA_##reg)
|
||||
usba_io_readl((ep)->dma_regs + USBA_DMA_##reg)
|
||||
#define usba_dma_writel(ep, reg, value) \
|
||||
__raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
|
||||
usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
|
||||
|
||||
/* Calculate base address for a given endpoint or DMA controller */
|
||||
#define USBA_EPT_BASE(x) (0x100 + (x) * 0x20)
|
||||
@ -313,6 +323,9 @@ struct usba_udc {
|
||||
/* Protect hw registers from concurrent modifications */
|
||||
spinlock_t lock;
|
||||
|
||||
/* Mutex to prevent concurrent start or stop */
|
||||
struct mutex vbus_mutex;
|
||||
|
||||
void __iomem *regs;
|
||||
void __iomem *fifo;
|
||||
|
||||
@ -328,6 +341,7 @@ struct usba_udc {
|
||||
struct clk *hclk;
|
||||
struct usba_ep *usba_ep;
|
||||
bool bias_pulse_needed;
|
||||
bool clocked;
|
||||
|
||||
u16 devstatus;
|
||||
|
||||
|
@ -2631,7 +2631,7 @@ static int __init init(void)
|
||||
return -EINVAL;
|
||||
|
||||
if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) {
|
||||
pr_err("Number of emulated UDC must be in range of 1…%d\n",
|
||||
pr_err("Number of emulated UDC must be in range of 1...%d\n",
|
||||
MAX_NUM_UDC);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1024,35 +1024,79 @@ static const char proc_node_name [] = "driver/udc";
|
||||
static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
|
||||
{
|
||||
/* int_status is the same format ... */
|
||||
seq_printf(m,
|
||||
"%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
|
||||
label, mask,
|
||||
(mask & INT_PWRDETECT) ? " power" : "",
|
||||
(mask & INT_SYSERROR) ? " sys" : "",
|
||||
(mask & INT_MSTRDEND) ? " in-dma" : "",
|
||||
(mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
|
||||
seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
|
||||
label, mask,
|
||||
(mask & INT_PWRDETECT) ? " power" : "",
|
||||
(mask & INT_SYSERROR) ? " sys" : "",
|
||||
(mask & INT_MSTRDEND) ? " in-dma" : "",
|
||||
(mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
|
||||
|
||||
(mask & INT_MSTWREND) ? " out-dma" : "",
|
||||
(mask & INT_MSTWRSET) ? " wrset" : "",
|
||||
(mask & INT_ERR) ? " err" : "",
|
||||
(mask & INT_SOF) ? " sof" : "",
|
||||
(mask & INT_MSTWREND) ? " out-dma" : "",
|
||||
(mask & INT_MSTWRSET) ? " wrset" : "",
|
||||
(mask & INT_ERR) ? " err" : "",
|
||||
(mask & INT_SOF) ? " sof" : "",
|
||||
|
||||
(mask & INT_EP3NAK) ? " ep3nak" : "",
|
||||
(mask & INT_EP2NAK) ? " ep2nak" : "",
|
||||
(mask & INT_EP1NAK) ? " ep1nak" : "",
|
||||
(mask & INT_EP3DATASET) ? " ep3" : "",
|
||||
(mask & INT_EP3NAK) ? " ep3nak" : "",
|
||||
(mask & INT_EP2NAK) ? " ep2nak" : "",
|
||||
(mask & INT_EP1NAK) ? " ep1nak" : "",
|
||||
(mask & INT_EP3DATASET) ? " ep3" : "",
|
||||
|
||||
(mask & INT_EP2DATASET) ? " ep2" : "",
|
||||
(mask & INT_EP1DATASET) ? " ep1" : "",
|
||||
(mask & INT_STATUSNAK) ? " ep0snak" : "",
|
||||
(mask & INT_STATUS) ? " ep0status" : "",
|
||||
(mask & INT_EP2DATASET) ? " ep2" : "",
|
||||
(mask & INT_EP1DATASET) ? " ep1" : "",
|
||||
(mask & INT_STATUSNAK) ? " ep0snak" : "",
|
||||
(mask & INT_STATUS) ? " ep0status" : "",
|
||||
|
||||
(mask & INT_SETUP) ? " setup" : "",
|
||||
(mask & INT_ENDPOINT0) ? " ep0" : "",
|
||||
(mask & INT_USBRESET) ? " reset" : "",
|
||||
(mask & INT_SUSPEND) ? " suspend" : "");
|
||||
(mask & INT_SETUP) ? " setup" : "",
|
||||
(mask & INT_ENDPOINT0) ? " ep0" : "",
|
||||
(mask & INT_USBRESET) ? " reset" : "",
|
||||
(mask & INT_SUSPEND) ? " suspend" : "");
|
||||
}
|
||||
|
||||
static const char *udc_ep_state(enum ep0state state)
|
||||
{
|
||||
switch (state) {
|
||||
case EP0_DISCONNECT:
|
||||
return "ep0_disconnect";
|
||||
case EP0_IDLE:
|
||||
return "ep0_idle";
|
||||
case EP0_IN:
|
||||
return "ep0_in";
|
||||
case EP0_OUT:
|
||||
return "ep0_out";
|
||||
case EP0_STATUS:
|
||||
return "ep0_status";
|
||||
case EP0_STALL:
|
||||
return "ep0_stall";
|
||||
case EP0_SUSPEND:
|
||||
return "ep0_suspend";
|
||||
}
|
||||
|
||||
return "ep0_?";
|
||||
}
|
||||
|
||||
static const char *udc_ep_status(u32 status)
|
||||
{
|
||||
switch (status & EPxSTATUS_EP_MASK) {
|
||||
case EPxSTATUS_EP_READY:
|
||||
return "ready";
|
||||
case EPxSTATUS_EP_DATAIN:
|
||||
return "packet";
|
||||
case EPxSTATUS_EP_FULL:
|
||||
return "full";
|
||||
case EPxSTATUS_EP_TX_ERR: /* host will retry */
|
||||
return "tx_err";
|
||||
case EPxSTATUS_EP_RX_ERR:
|
||||
return "rx_err";
|
||||
case EPxSTATUS_EP_BUSY: /* ep0 only */
|
||||
return "busy";
|
||||
case EPxSTATUS_EP_STALL:
|
||||
return "stall";
|
||||
case EPxSTATUS_EP_INVALID: /* these "can't happen" */
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
static int udc_proc_read(struct seq_file *m, void *v)
|
||||
{
|
||||
@ -1068,29 +1112,18 @@ static int udc_proc_read(struct seq_file *m, void *v)
|
||||
tmp = readl(®s->power_detect);
|
||||
is_usb_connected = tmp & PW_DETECT;
|
||||
seq_printf(m,
|
||||
"%s - %s\n"
|
||||
"%s version: %s %s\n"
|
||||
"Gadget driver: %s\n"
|
||||
"Host %s, %s\n"
|
||||
"\n",
|
||||
pci_name(dev->pdev), driver_desc,
|
||||
driver_name, DRIVER_VERSION, dmastr(),
|
||||
dev->driver ? dev->driver->driver.name : "(none)",
|
||||
is_usb_connected
|
||||
? ((tmp & PW_PULLUP) ? "full speed" : "powered")
|
||||
: "disconnected",
|
||||
({const char *state;
|
||||
switch(dev->ep0state){
|
||||
case EP0_DISCONNECT: state = "ep0_disconnect"; break;
|
||||
case EP0_IDLE: state = "ep0_idle"; break;
|
||||
case EP0_IN: state = "ep0_in"; break;
|
||||
case EP0_OUT: state = "ep0_out"; break;
|
||||
case EP0_STATUS: state = "ep0_status"; break;
|
||||
case EP0_STALL: state = "ep0_stall"; break;
|
||||
case EP0_SUSPEND: state = "ep0_suspend"; break;
|
||||
default: state = "ep0_?"; break;
|
||||
} state; })
|
||||
);
|
||||
"%s - %s\n"
|
||||
"%s version: %s %s\n"
|
||||
"Gadget driver: %s\n"
|
||||
"Host %s, %s\n"
|
||||
"\n",
|
||||
pci_name(dev->pdev), driver_desc,
|
||||
driver_name, DRIVER_VERSION, dmastr(),
|
||||
dev->driver ? dev->driver->driver.name : "(none)",
|
||||
is_usb_connected
|
||||
? ((tmp & PW_PULLUP) ? "full speed" : "powered")
|
||||
: "disconnected",
|
||||
udc_ep_state(dev->ep0state));
|
||||
|
||||
dump_intmask(m, "int_status", readl(®s->int_status));
|
||||
dump_intmask(m, "int_enable", readl(®s->int_enable));
|
||||
@ -1099,31 +1132,30 @@ static int udc_proc_read(struct seq_file *m, void *v)
|
||||
goto done;
|
||||
|
||||
/* registers for (active) device and ep0 */
|
||||
if (seq_printf(m, "\nirqs %lu\ndataset %02x "
|
||||
"single.bcs %02x.%02x state %x addr %u\n",
|
||||
dev->irqs, readl(®s->DataSet),
|
||||
readl(®s->EPxSingle), readl(®s->EPxBCS),
|
||||
readl(®s->UsbState),
|
||||
readl(®s->address)) < 0)
|
||||
seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n",
|
||||
dev->irqs, readl(®s->DataSet),
|
||||
readl(®s->EPxSingle), readl(®s->EPxBCS),
|
||||
readl(®s->UsbState),
|
||||
readl(®s->address));
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
|
||||
tmp = readl(®s->dma_master);
|
||||
if (seq_printf(m,
|
||||
"dma %03X =" EIGHTBITS "%s %s\n", tmp,
|
||||
(tmp & MST_EOPB_DIS) ? " eopb-" : "",
|
||||
(tmp & MST_EOPB_ENA) ? " eopb+" : "",
|
||||
(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
|
||||
(tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
|
||||
seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n",
|
||||
tmp,
|
||||
(tmp & MST_EOPB_DIS) ? " eopb-" : "",
|
||||
(tmp & MST_EOPB_ENA) ? " eopb+" : "",
|
||||
(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
|
||||
(tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
|
||||
|
||||
(tmp & MST_RD_EOPB) ? " eopb" : "",
|
||||
(tmp & MST_RD_RESET) ? " in_reset" : "",
|
||||
(tmp & MST_WR_RESET) ? " out_reset" : "",
|
||||
(tmp & MST_RD_ENA) ? " IN" : "",
|
||||
(tmp & MST_RD_EOPB) ? " eopb" : "",
|
||||
(tmp & MST_RD_RESET) ? " in_reset" : "",
|
||||
(tmp & MST_WR_RESET) ? " out_reset" : "",
|
||||
(tmp & MST_RD_ENA) ? " IN" : "",
|
||||
|
||||
(tmp & MST_WR_ENA) ? " OUT" : "",
|
||||
(tmp & MST_CONNECTION)
|
||||
? "ep1in/ep2out"
|
||||
: "ep1out/ep2in") < 0)
|
||||
(tmp & MST_WR_ENA) ? " OUT" : "",
|
||||
(tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in");
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
|
||||
/* dump endpoint queues */
|
||||
@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v)
|
||||
continue;
|
||||
|
||||
tmp = readl(ep->reg_status);
|
||||
if (seq_printf(m,
|
||||
"%s %s max %u %s, irqs %lu, "
|
||||
"status %02x (%s) " FOURBITS "\n",
|
||||
ep->ep.name,
|
||||
ep->is_in ? "in" : "out",
|
||||
ep->ep.maxpacket,
|
||||
ep->dma ? "dma" : "pio",
|
||||
ep->irqs,
|
||||
tmp, ({ char *s;
|
||||
switch (tmp & EPxSTATUS_EP_MASK) {
|
||||
case EPxSTATUS_EP_READY:
|
||||
s = "ready"; break;
|
||||
case EPxSTATUS_EP_DATAIN:
|
||||
s = "packet"; break;
|
||||
case EPxSTATUS_EP_FULL:
|
||||
s = "full"; break;
|
||||
case EPxSTATUS_EP_TX_ERR: // host will retry
|
||||
s = "tx_err"; break;
|
||||
case EPxSTATUS_EP_RX_ERR:
|
||||
s = "rx_err"; break;
|
||||
case EPxSTATUS_EP_BUSY: /* ep0 only */
|
||||
s = "busy"; break;
|
||||
case EPxSTATUS_EP_STALL:
|
||||
s = "stall"; break;
|
||||
case EPxSTATUS_EP_INVALID: // these "can't happen"
|
||||
s = "invalid"; break;
|
||||
default:
|
||||
s = "?"; break;
|
||||
} s; }),
|
||||
(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
|
||||
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
|
||||
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
|
||||
(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""
|
||||
) < 0)
|
||||
seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n",
|
||||
ep->ep.name,
|
||||
ep->is_in ? "in" : "out",
|
||||
ep->ep.maxpacket,
|
||||
ep->dma ? "dma" : "pio",
|
||||
ep->irqs,
|
||||
tmp, udc_ep_status(tmp),
|
||||
(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
|
||||
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
|
||||
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
|
||||
(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "");
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
|
||||
if (list_empty(&ep->queue)) {
|
||||
if (seq_puts(m, "\t(nothing queued)\n") < 0)
|
||||
seq_puts(m, "\t(nothing queued)\n");
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
continue;
|
||||
}
|
||||
@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v)
|
||||
} else
|
||||
tmp = req->req.actual;
|
||||
|
||||
if (seq_printf(m,
|
||||
"\treq %p len %u/%u buf %p\n",
|
||||
&req->req, tmp, req->req.length,
|
||||
req->req.buf) < 0)
|
||||
seq_printf(m, "\treq %p len %u/%u buf %p\n",
|
||||
&req->req, tmp, req->req.length,
|
||||
req->req.buf);
|
||||
if (seq_has_overflowed(m))
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
|
||||
req = container_of(_req, struct lpc32xx_request, req);
|
||||
ep = container_of(_ep, struct lpc32xx_ep, ep);
|
||||
|
||||
if (!_req || !_req->complete || !_req->buf ||
|
||||
if (!_ep || !_req || !_req->complete || !_req->buf ||
|
||||
!list_empty(&req->queue))
|
||||
return -EINVAL;
|
||||
|
||||
udc = ep->udc;
|
||||
|
||||
if (!_ep) {
|
||||
dev_dbg(udc->dev, "invalid ep\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
if ((!udc) || (!udc->driver) ||
|
||||
(udc->gadget.speed == USB_SPEED_UNKNOWN)) {
|
||||
dev_dbg(udc->dev, "invalid device\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (udc->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -EPIPE;
|
||||
|
||||
if (ep->lep) {
|
||||
struct lpc32xx_usbd_dd_gad *dd;
|
||||
|
@ -80,6 +80,13 @@ static const char *const ep_name[] = {
|
||||
"ep-e", "ep-f", "ep-g", "ep-h",
|
||||
};
|
||||
|
||||
/* Endpoint names for usb3380 advance mode */
|
||||
static const char *const ep_name_adv[] = {
|
||||
ep0name,
|
||||
"ep1in", "ep2out", "ep3in", "ep4out",
|
||||
"ep1out", "ep2in", "ep3out", "ep4in",
|
||||
};
|
||||
|
||||
/* mode 0 == ep-{a,b,c,d} 1K fifo each
|
||||
* mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
|
||||
* mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
|
||||
@ -138,31 +145,44 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
u32 max, tmp;
|
||||
unsigned long flags;
|
||||
static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
|
||||
int ret = 0;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
|
||||
desc->bDescriptorType != USB_DT_ENDPOINT)
|
||||
desc->bDescriptorType != USB_DT_ENDPOINT) {
|
||||
pr_err("%s: failed at line=%d\n", __func__, __LINE__);
|
||||
return -EINVAL;
|
||||
}
|
||||
dev = ep->dev;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
ret = -ESHUTDOWN;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
/* erratum 0119 workaround ties up an endpoint number */
|
||||
if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
|
||||
return -EDOM;
|
||||
if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) {
|
||||
ret = -EDOM;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
if (dev->quirks & PLX_SUPERSPEED) {
|
||||
if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
|
||||
return -EDOM;
|
||||
if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
|
||||
ret = -EDOM;
|
||||
goto print_err;
|
||||
}
|
||||
ep->is_in = !!usb_endpoint_dir_in(desc);
|
||||
if (dev->enhanced_mode && ep->is_in && ep_key[ep->num])
|
||||
return -EINVAL;
|
||||
if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) {
|
||||
ret = -EINVAL;
|
||||
goto print_err;
|
||||
}
|
||||
}
|
||||
|
||||
/* sanity check ep-e/ep-f since their fifos are small */
|
||||
max = usb_endpoint_maxp(desc) & 0x1fff;
|
||||
if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY))
|
||||
return -ERANGE;
|
||||
if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) {
|
||||
ret = -ERANGE;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
_ep->maxpacket = max & 0x7ff;
|
||||
@ -192,7 +212,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
(dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
|
||||
(dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return -ERANGE;
|
||||
ret = -ERANGE;
|
||||
goto print_err;
|
||||
}
|
||||
}
|
||||
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
|
||||
@ -271,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
|
||||
/* pci writes may still be posted */
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
print_err:
|
||||
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
|
||||
@ -426,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep)
|
||||
unsigned long flags;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || !ep->desc || _ep->name == ep0name)
|
||||
if (!_ep || !ep->desc || _ep->name == ep0name) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
spin_lock_irqsave(&ep->dev->lock, flags);
|
||||
nuke(ep);
|
||||
|
||||
@ -458,8 +484,10 @@ static struct usb_request
|
||||
struct net2280_ep *ep;
|
||||
struct net2280_request *req;
|
||||
|
||||
if (!_ep)
|
||||
if (!_ep) {
|
||||
pr_err("%s: Invalid ep\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
|
||||
req = kzalloc(sizeof(*req), gfp_flags);
|
||||
@ -491,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
||||
struct net2280_request *req;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || !_req)
|
||||
if (!_ep || !_req) {
|
||||
dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n",
|
||||
__func__, _ep, _req);
|
||||
return;
|
||||
}
|
||||
|
||||
req = container_of(_req, struct net2280_request, req);
|
||||
WARN_ON(!list_empty(&req->queue));
|
||||
@ -896,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
||||
struct net2280_ep *ep;
|
||||
struct net2280 *dev;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
/* we always require a cpu-view buffer, so that we can
|
||||
* always use pio (as fallback or whatever).
|
||||
*/
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0)) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return -EINVAL;
|
||||
}
|
||||
req = container_of(_req, struct net2280_request, req);
|
||||
if (!_req || !_req->complete || !_req->buf ||
|
||||
!list_empty(&req->queue))
|
||||
return -EINVAL;
|
||||
if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
|
||||
return -EDOM;
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0))
|
||||
return -EINVAL;
|
||||
!list_empty(&req->queue)) {
|
||||
ret = -EINVAL;
|
||||
goto print_err;
|
||||
}
|
||||
if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) {
|
||||
ret = -EDOM;
|
||||
goto print_err;
|
||||
}
|
||||
dev = ep->dev;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
ret = -ESHUTDOWN;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
/* FIXME implement PIO fallback for ZLPs with DMA */
|
||||
if (ep->dma && _req->length == 0)
|
||||
return -EOPNOTSUPP;
|
||||
if (ep->dma && _req->length == 0) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
/* set up dma mapping in case the caller didn't */
|
||||
if (ep->dma) {
|
||||
int ret;
|
||||
|
||||
ret = usb_gadget_map_request(&dev->gadget, _req,
|
||||
ep->is_in);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
|
||||
@ -1013,7 +1053,11 @@ done:
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
/* pci writes may still be posted */
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
print_err:
|
||||
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -1134,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
int stopped;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0) || !_req)
|
||||
if (!_ep || (!ep->desc && ep->num != 0) || !_req) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n",
|
||||
__func__, _ep, _req);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ep->dev->lock, flags);
|
||||
stopped = ep->stopped;
|
||||
@ -1157,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
||||
}
|
||||
if (&req->req != _req) {
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1214,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
|
||||
int retval = 0;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0))
|
||||
if (!_ep || (!ep->desc && ep->num != 0)) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return -EINVAL;
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
retval = -ESHUTDOWN;
|
||||
goto print_err;
|
||||
}
|
||||
if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
|
||||
== USB_ENDPOINT_XFER_ISOC)
|
||||
return -EINVAL;
|
||||
== USB_ENDPOINT_XFER_ISOC) {
|
||||
retval = -EINVAL;
|
||||
goto print_err;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ep->dev->lock, flags);
|
||||
if (!list_empty(&ep->queue))
|
||||
if (!list_empty(&ep->queue)) {
|
||||
retval = -EAGAIN;
|
||||
else if (ep->is_in && value && net2280_fifo_status(_ep) != 0)
|
||||
goto print_unlock;
|
||||
} else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) {
|
||||
retval = -EAGAIN;
|
||||
else {
|
||||
goto print_unlock;
|
||||
} else {
|
||||
ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
|
||||
value ? "set" : "clear",
|
||||
wedged ? "wedge" : "halt");
|
||||
@ -1251,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
|
||||
return retval;
|
||||
|
||||
print_unlock:
|
||||
spin_unlock_irqrestore(&ep->dev->lock, flags);
|
||||
print_err:
|
||||
dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int net2280_set_halt(struct usb_ep *_ep, int value)
|
||||
@ -1260,8 +1323,10 @@ static int net2280_set_halt(struct usb_ep *_ep, int value)
|
||||
|
||||
static int net2280_set_wedge(struct usb_ep *_ep)
|
||||
{
|
||||
if (!_ep || _ep->name == ep0name)
|
||||
if (!_ep || _ep->name == ep0name) {
|
||||
pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep);
|
||||
return -EINVAL;
|
||||
}
|
||||
return net2280_set_halt_and_wedge(_ep, 1, 1);
|
||||
}
|
||||
|
||||
@ -1271,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep)
|
||||
u32 avail;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0))
|
||||
if (!_ep || (!ep->desc && ep->num != 0)) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return -ENODEV;
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
}
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
dev_err(&ep->dev->pdev->dev,
|
||||
"%s: Invalid driver=%p or speed=%d\n",
|
||||
__func__, ep->dev->driver, ep->dev->gadget.speed);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
|
||||
if (avail > ep->fifo_size)
|
||||
if (avail > ep->fifo_size) {
|
||||
dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__);
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
if (ep->is_in)
|
||||
avail = ep->fifo_size - avail;
|
||||
return avail;
|
||||
@ -1289,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep)
|
||||
struct net2280_ep *ep;
|
||||
|
||||
ep = container_of(_ep, struct net2280_ep, ep);
|
||||
if (!_ep || (!ep->desc && ep->num != 0))
|
||||
if (!_ep || (!ep->desc && ep->num != 0)) {
|
||||
pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
|
||||
return;
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
|
||||
}
|
||||
if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
|
||||
dev_err(&ep->dev->pdev->dev,
|
||||
"%s: Invalid driver=%p or speed=%d\n",
|
||||
__func__, ep->dev->driver, ep->dev->gadget.speed);
|
||||
return;
|
||||
}
|
||||
|
||||
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
|
||||
(void) readl(&ep->regs->ep_rsp);
|
||||
@ -1977,7 +2056,7 @@ static void usb_reinit_338x(struct net2280 *dev)
|
||||
for (i = 0; i < dev->n_ep; i++) {
|
||||
struct net2280_ep *ep = &dev->ep[i];
|
||||
|
||||
ep->ep.name = ep_name[i];
|
||||
ep->ep.name = dev->enhanced_mode ? ep_name_adv[i] : ep_name[i];
|
||||
ep->dev = dev;
|
||||
ep->num = i;
|
||||
|
||||
@ -1989,11 +2068,9 @@ static void usb_reinit_338x(struct net2280 *dev)
|
||||
ep->regs = (struct net2280_ep_regs __iomem *)
|
||||
(((void __iomem *)&dev->epregs[ne[i]]) +
|
||||
ep_reg_addr[i]);
|
||||
ep->fiforegs = &dev->fiforegs[i];
|
||||
} else {
|
||||
ep->cfg = &dev->epregs[i];
|
||||
ep->regs = &dev->epregs[i];
|
||||
ep->fiforegs = &dev->fiforegs[i];
|
||||
}
|
||||
|
||||
ep->fifo_size = (i != 0) ? 2048 : 512;
|
||||
@ -2186,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget,
|
||||
dev->ep[i].irqs = 0;
|
||||
|
||||
/* hook up the driver ... */
|
||||
dev->softconnect = 1;
|
||||
driver->driver.bus = NULL;
|
||||
dev->driver = driver;
|
||||
|
||||
@ -3052,6 +3128,8 @@ next_endpoints:
|
||||
BIT(PCI_RETRY_ABORT_INTERRUPT))
|
||||
|
||||
static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
|
||||
__releases(dev->lock)
|
||||
__acquires(dev->lock)
|
||||
{
|
||||
struct net2280_ep *ep;
|
||||
u32 tmp, num, mask, scratch;
|
||||
@ -3373,8 +3451,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
u32 usbstat;
|
||||
dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
|
||||
(base + 0x00b4);
|
||||
dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
|
||||
(base + 0x0500);
|
||||
dev->llregs = (struct usb338x_ll_regs __iomem *)
|
||||
(base + 0x0700);
|
||||
dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
|
||||
|
@ -96,7 +96,6 @@ struct net2280_ep {
|
||||
struct net2280_ep_regs __iomem *regs;
|
||||
struct net2280_dma_regs __iomem *dma;
|
||||
struct net2280_dma *dummy;
|
||||
struct usb338x_fifo_regs __iomem *fiforegs;
|
||||
dma_addr_t td_dma; /* of dummy */
|
||||
struct net2280 *dev;
|
||||
unsigned long irqs;
|
||||
@ -181,7 +180,6 @@ struct net2280 {
|
||||
struct net2280_dma_regs __iomem *dma;
|
||||
struct net2280_dep_regs __iomem *dep;
|
||||
struct net2280_ep_regs __iomem *epregs;
|
||||
struct usb338x_fifo_regs __iomem *fiforegs;
|
||||
struct usb338x_ll_regs __iomem *llregs;
|
||||
struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
|
||||
struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
|
||||
|
@ -93,50 +93,46 @@ static void handle_ep(struct pxa_ep *ep);
|
||||
static int state_dbg_show(struct seq_file *s, void *p)
|
||||
{
|
||||
struct pxa_udc *udc = s->private;
|
||||
int pos = 0, ret;
|
||||
u32 tmp;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!udc->driver)
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
|
||||
/* basic device status */
|
||||
pos += seq_printf(s, DRIVER_DESC "\n"
|
||||
"%s version: %s\nGadget driver: %s\n",
|
||||
driver_name, DRIVER_VERSION,
|
||||
udc->driver ? udc->driver->driver.name : "(none)");
|
||||
seq_printf(s, DRIVER_DESC "\n"
|
||||
"%s version: %s\n"
|
||||
"Gadget driver: %s\n",
|
||||
driver_name, DRIVER_VERSION,
|
||||
udc->driver ? udc->driver->driver.name : "(none)");
|
||||
|
||||
tmp = udc_readl(udc, UDCCR);
|
||||
pos += seq_printf(s,
|
||||
"udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), "
|
||||
"con=%d,inter=%d,altinter=%d\n", tmp,
|
||||
(tmp & UDCCR_OEN) ? " oen":"",
|
||||
(tmp & UDCCR_AALTHNP) ? " aalthnp":"",
|
||||
(tmp & UDCCR_AHNP) ? " rem" : "",
|
||||
(tmp & UDCCR_BHNP) ? " rstir" : "",
|
||||
(tmp & UDCCR_DWRE) ? " dwre" : "",
|
||||
(tmp & UDCCR_SMAC) ? " smac" : "",
|
||||
(tmp & UDCCR_EMCE) ? " emce" : "",
|
||||
(tmp & UDCCR_UDR) ? " udr" : "",
|
||||
(tmp & UDCCR_UDA) ? " uda" : "",
|
||||
(tmp & UDCCR_UDE) ? " ude" : "",
|
||||
(tmp & UDCCR_ACN) >> UDCCR_ACN_S,
|
||||
(tmp & UDCCR_AIN) >> UDCCR_AIN_S,
|
||||
(tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
|
||||
seq_printf(s,
|
||||
"udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n",
|
||||
tmp,
|
||||
(tmp & UDCCR_OEN) ? " oen":"",
|
||||
(tmp & UDCCR_AALTHNP) ? " aalthnp":"",
|
||||
(tmp & UDCCR_AHNP) ? " rem" : "",
|
||||
(tmp & UDCCR_BHNP) ? " rstir" : "",
|
||||
(tmp & UDCCR_DWRE) ? " dwre" : "",
|
||||
(tmp & UDCCR_SMAC) ? " smac" : "",
|
||||
(tmp & UDCCR_EMCE) ? " emce" : "",
|
||||
(tmp & UDCCR_UDR) ? " udr" : "",
|
||||
(tmp & UDCCR_UDA) ? " uda" : "",
|
||||
(tmp & UDCCR_UDE) ? " ude" : "",
|
||||
(tmp & UDCCR_ACN) >> UDCCR_ACN_S,
|
||||
(tmp & UDCCR_AIN) >> UDCCR_AIN_S,
|
||||
(tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
|
||||
/* registers for device and ep0 */
|
||||
pos += seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
|
||||
udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
|
||||
pos += seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
|
||||
udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
|
||||
pos += seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
|
||||
pos += seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, "
|
||||
"reconfig=%lu\n",
|
||||
udc->stats.irqs_reset, udc->stats.irqs_suspend,
|
||||
udc->stats.irqs_resume, udc->stats.irqs_reconfig);
|
||||
seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
|
||||
udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
|
||||
seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
|
||||
udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
|
||||
seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
|
||||
seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n",
|
||||
udc->stats.irqs_reset, udc->stats.irqs_suspend,
|
||||
udc->stats.irqs_resume, udc->stats.irqs_reconfig);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queues_dbg_show(struct seq_file *s, void *p)
|
||||
@ -144,75 +140,67 @@ static int queues_dbg_show(struct seq_file *s, void *p)
|
||||
struct pxa_udc *udc = s->private;
|
||||
struct pxa_ep *ep;
|
||||
struct pxa27x_request *req;
|
||||
int pos = 0, i, maxpkt, ret;
|
||||
int i, maxpkt;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!udc->driver)
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
|
||||
/* dump endpoint queues */
|
||||
for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
|
||||
ep = &udc->pxa_ep[i];
|
||||
maxpkt = ep->fifo_size;
|
||||
pos += seq_printf(s, "%-12s max_pkt=%d %s\n",
|
||||
EPNAME(ep), maxpkt, "pio");
|
||||
seq_printf(s, "%-12s max_pkt=%d %s\n",
|
||||
EPNAME(ep), maxpkt, "pio");
|
||||
|
||||
if (list_empty(&ep->queue)) {
|
||||
pos += seq_printf(s, "\t(nothing queued)\n");
|
||||
seq_puts(s, "\t(nothing queued)\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
pos += seq_printf(s, "\treq %p len %d/%d buf %p\n",
|
||||
&req->req, req->req.actual,
|
||||
req->req.length, req->req.buf);
|
||||
seq_printf(s, "\treq %p len %d/%d buf %p\n",
|
||||
&req->req, req->req.actual,
|
||||
req->req.length, req->req.buf);
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eps_dbg_show(struct seq_file *s, void *p)
|
||||
{
|
||||
struct pxa_udc *udc = s->private;
|
||||
struct pxa_ep *ep;
|
||||
int pos = 0, i, ret;
|
||||
int i;
|
||||
u32 tmp;
|
||||
|
||||
ret = -ENODEV;
|
||||
if (!udc->driver)
|
||||
goto out;
|
||||
return -ENODEV;
|
||||
|
||||
ep = &udc->pxa_ep[0];
|
||||
tmp = udc_ep_readl(ep, UDCCSR);
|
||||
pos += seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", tmp,
|
||||
(tmp & UDCCSR0_SA) ? " sa" : "",
|
||||
(tmp & UDCCSR0_RNE) ? " rne" : "",
|
||||
(tmp & UDCCSR0_FST) ? " fst" : "",
|
||||
(tmp & UDCCSR0_SST) ? " sst" : "",
|
||||
(tmp & UDCCSR0_DME) ? " dme" : "",
|
||||
(tmp & UDCCSR0_IPR) ? " ipr" : "",
|
||||
(tmp & UDCCSR0_OPC) ? " opc" : "");
|
||||
seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n",
|
||||
tmp,
|
||||
(tmp & UDCCSR0_SA) ? " sa" : "",
|
||||
(tmp & UDCCSR0_RNE) ? " rne" : "",
|
||||
(tmp & UDCCSR0_FST) ? " fst" : "",
|
||||
(tmp & UDCCSR0_SST) ? " sst" : "",
|
||||
(tmp & UDCCSR0_DME) ? " dme" : "",
|
||||
(tmp & UDCCSR0_IPR) ? " ipr" : "",
|
||||
(tmp & UDCCSR0_OPC) ? " opc" : "");
|
||||
for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
|
||||
ep = &udc->pxa_ep[i];
|
||||
tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR);
|
||||
pos += seq_printf(s, "%-12s: "
|
||||
"IN %lu(%lu reqs), OUT %lu(%lu reqs), "
|
||||
"irqs=%lu, udccr=0x%08x, udccsr=0x%03x, "
|
||||
"udcbcr=%d\n",
|
||||
EPNAME(ep),
|
||||
ep->stats.in_bytes, ep->stats.in_ops,
|
||||
ep->stats.out_bytes, ep->stats.out_ops,
|
||||
ep->stats.irqs,
|
||||
tmp, udc_ep_readl(ep, UDCCSR),
|
||||
udc_ep_readl(ep, UDCBCR));
|
||||
seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n",
|
||||
EPNAME(ep),
|
||||
ep->stats.in_bytes, ep->stats.in_ops,
|
||||
ep->stats.out_bytes, ep->stats.out_ops,
|
||||
ep->stats.irqs,
|
||||
tmp, udc_ep_readl(ep, UDCCSR),
|
||||
udc_ep_readl(ep, UDCBCR));
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eps_dbg_open(struct inode *inode, struct file *file)
|
||||
|
@ -35,6 +35,8 @@
|
||||
* @dev - the child device to the actual controller
|
||||
* @gadget - the gadget. For use by the class code
|
||||
* @list - for use by the udc class driver
|
||||
* @vbus - for udcs who care about vbus status, this value is real vbus status;
|
||||
* for udcs who do not care about vbus status, this value is always true
|
||||
*
|
||||
* This represents the internal data structure which is used by the UDC-class
|
||||
* to hold information about udc driver and gadget together.
|
||||
@ -44,6 +46,7 @@ struct usb_udc {
|
||||
struct usb_gadget *gadget;
|
||||
struct device dev;
|
||||
struct list_head list;
|
||||
bool vbus;
|
||||
};
|
||||
|
||||
static struct class *udc_class;
|
||||
@ -128,21 +131,11 @@ EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
|
||||
|
||||
static void usb_gadget_state_work(struct work_struct *work)
|
||||
{
|
||||
struct usb_gadget *gadget = work_to_gadget(work);
|
||||
struct usb_udc *udc = NULL;
|
||||
struct usb_gadget *gadget = work_to_gadget(work);
|
||||
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);
|
||||
|
||||
sysfs_notify(&udc->dev.kobj, NULL, "state");
|
||||
if (udc)
|
||||
sysfs_notify(&udc->dev.kobj, NULL, "state");
|
||||
}
|
||||
|
||||
void usb_gadget_set_state(struct usb_gadget *gadget,
|
||||
@ -155,6 +148,34 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static void usb_udc_connect_control(struct usb_udc *udc)
|
||||
{
|
||||
if (udc->vbus)
|
||||
usb_gadget_connect(udc->gadget);
|
||||
else
|
||||
usb_gadget_disconnect(udc->gadget);
|
||||
}
|
||||
|
||||
/**
|
||||
* usb_udc_vbus_handler - updates the udc core vbus status, and try to
|
||||
* connect or disconnect gadget
|
||||
* @gadget: The gadget which vbus change occurs
|
||||
* @status: The vbus status
|
||||
*
|
||||
* The udc driver calls it when it wants to connect or disconnect gadget
|
||||
* according to vbus status.
|
||||
*/
|
||||
void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
|
||||
{
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
if (udc) {
|
||||
udc->vbus = status;
|
||||
usb_udc_connect_control(udc);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
|
||||
|
||||
/**
|
||||
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
|
||||
* @gadget: The gadget which bus reset occurs
|
||||
@ -278,6 +299,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
goto err3;
|
||||
|
||||
udc->gadget = gadget;
|
||||
gadget->udc = udc;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_add_tail(&udc->list, &udc_list);
|
||||
@ -287,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
|
||||
goto err4;
|
||||
|
||||
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
|
||||
udc->vbus = true;
|
||||
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
@ -348,21 +371,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
|
||||
*/
|
||||
void usb_del_gadget_udc(struct usb_gadget *gadget)
|
||||
{
|
||||
struct usb_udc *udc = NULL;
|
||||
struct usb_udc *udc = gadget->udc;
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_for_each_entry(udc, &udc_list, list)
|
||||
if (udc->gadget == gadget)
|
||||
goto found;
|
||||
if (!udc)
|
||||
return;
|
||||
|
||||
dev_err(gadget->dev.parent, "gadget not registered.\n");
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
return;
|
||||
|
||||
found:
|
||||
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
|
||||
|
||||
mutex_lock(&udc_lock);
|
||||
list_del(&udc->list);
|
||||
mutex_unlock(&udc_lock);
|
||||
|
||||
@ -397,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
|
||||
driver->unbind(udc->gadget);
|
||||
goto err1;
|
||||
}
|
||||
usb_gadget_connect(udc->gadget);
|
||||
usb_udc_connect_control(udc);
|
||||
|
||||
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
|
||||
return 0;
|
||||
|
@ -507,7 +507,8 @@ void musb_hnp_stop(struct musb *musb)
|
||||
musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
|
||||
}
|
||||
|
||||
static void musb_generic_disable(struct musb *musb);
|
||||
static void musb_recover_from_babble(struct musb *musb);
|
||||
|
||||
/*
|
||||
* Interrupt Service Routine to record USB "global" interrupts.
|
||||
* Since these do not happen often and signify things of
|
||||
@ -534,30 +535,16 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
*/
|
||||
if (int_usb & MUSB_INTR_RESUME) {
|
||||
handled = IRQ_HANDLED;
|
||||
dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->otg->state));
|
||||
dev_dbg(musb->controller, "RESUME (%s)\n",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
|
||||
if (devctl & MUSB_DEVCTL_HM) {
|
||||
void __iomem *mbase = musb->mregs;
|
||||
u8 power;
|
||||
|
||||
switch (musb->xceiv->otg->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* remote wakeup? later, GetPortStatus
|
||||
* will stop RESUME signaling
|
||||
*/
|
||||
|
||||
power = musb_readb(musb->mregs, MUSB_POWER);
|
||||
if (power & MUSB_POWER_SUSPENDM) {
|
||||
/* spurious */
|
||||
musb->int_usb &= ~MUSB_INTR_SUSPEND;
|
||||
dev_dbg(musb->controller, "Spurious SUSPENDM\n");
|
||||
break;
|
||||
}
|
||||
|
||||
power &= ~MUSB_POWER_SUSPENDM;
|
||||
musb_writeb(mbase, MUSB_POWER,
|
||||
power | MUSB_POWER_RESUME);
|
||||
|
||||
musb->port1_status |=
|
||||
(USB_PORT_STAT_C_SUSPEND << 16)
|
||||
| MUSB_PORT_STAT_RESUME;
|
||||
@ -775,10 +762,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
|
||||
musb->ep0_stage = MUSB_EP0_START;
|
||||
|
||||
/* flush endpoints when transitioning from Device Mode */
|
||||
if (is_peripheral_active(musb)) {
|
||||
/* REVISIT HNP; just force disconnect */
|
||||
}
|
||||
musb->intrtxe = musb->epmask;
|
||||
musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
|
||||
musb->intrrxe = musb->epmask & 0xfffe;
|
||||
@ -879,20 +862,19 @@ b_host:
|
||||
*/
|
||||
if (int_usb & MUSB_INTR_RESET) {
|
||||
handled = IRQ_HANDLED;
|
||||
if ((devctl & MUSB_DEVCTL_HM) != 0) {
|
||||
if (devctl & MUSB_DEVCTL_HM) {
|
||||
/*
|
||||
* Looks like non-HS BABBLE can be ignored, but
|
||||
* HS BABBLE is an error condition. For HS the solution
|
||||
* is to avoid babble in the first place and fix what
|
||||
* caused BABBLE. When HS BABBLE happens we can only
|
||||
* stop the session.
|
||||
* When BABBLE happens what we can depends on which
|
||||
* platform MUSB is running, because some platforms
|
||||
* implemented proprietary means for 'recovering' from
|
||||
* Babble conditions. One such platform is AM335x. In
|
||||
* most cases, however, the only thing we can do is
|
||||
* drop the session.
|
||||
*/
|
||||
if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV))
|
||||
dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl);
|
||||
else {
|
||||
ERR("Stopping host session -- babble\n");
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
|
||||
}
|
||||
dev_err(musb->controller, "Babble\n");
|
||||
|
||||
if (is_host_active(musb))
|
||||
musb_recover_from_babble(musb);
|
||||
} else {
|
||||
dev_dbg(musb->controller, "BUS RESET as %s\n",
|
||||
usb_otg_state_string(musb->xceiv->otg->state));
|
||||
@ -931,13 +913,6 @@ b_host:
|
||||
}
|
||||
}
|
||||
|
||||
/* handle babble condition */
|
||||
if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) {
|
||||
musb_generic_disable(musb);
|
||||
schedule_delayed_work(&musb->recover_work,
|
||||
msecs_to_jiffies(100));
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* REVISIT ... this would be for multiplexing periodic endpoints, or
|
||||
* supporting transfer phasing to prevent exceeding ISO bandwidth
|
||||
@ -990,7 +965,7 @@ b_host:
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static void musb_generic_disable(struct musb *musb)
|
||||
static void musb_disable_interrupts(struct musb *musb)
|
||||
{
|
||||
void __iomem *mbase = musb->mregs;
|
||||
u16 temp;
|
||||
@ -1002,14 +977,33 @@ static void musb_generic_disable(struct musb *musb)
|
||||
musb->intrrxe = 0;
|
||||
musb_writew(mbase, MUSB_INTRRXE, 0);
|
||||
|
||||
/* off */
|
||||
musb_writeb(mbase, MUSB_DEVCTL, 0);
|
||||
|
||||
/* flush pending interrupts */
|
||||
temp = musb_readb(mbase, MUSB_INTRUSB);
|
||||
temp = musb_readw(mbase, MUSB_INTRTX);
|
||||
temp = musb_readw(mbase, MUSB_INTRRX);
|
||||
}
|
||||
|
||||
static void musb_enable_interrupts(struct musb *musb)
|
||||
{
|
||||
void __iomem *regs = musb->mregs;
|
||||
|
||||
/* Set INT enable registers, enable interrupts */
|
||||
musb->intrtxe = musb->epmask;
|
||||
musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
|
||||
musb->intrrxe = musb->epmask & 0xfffe;
|
||||
musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
|
||||
musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
|
||||
|
||||
}
|
||||
|
||||
static void musb_generic_disable(struct musb *musb)
|
||||
{
|
||||
void __iomem *mbase = musb->mregs;
|
||||
|
||||
musb_disable_interrupts(musb);
|
||||
|
||||
/* off */
|
||||
musb_writeb(mbase, MUSB_DEVCTL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1022,13 +1016,7 @@ void musb_start(struct musb *musb)
|
||||
|
||||
dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
|
||||
|
||||
/* Set INT enable registers, enable interrupts */
|
||||
musb->intrtxe = musb->epmask;
|
||||
musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
|
||||
musb->intrrxe = musb->epmask & 0xfffe;
|
||||
musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
|
||||
musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
|
||||
|
||||
musb_enable_interrupts(musb);
|
||||
musb_writeb(regs, MUSB_TESTMODE, 0);
|
||||
|
||||
/* put into basic highspeed mode and start session */
|
||||
@ -1587,9 +1575,12 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
|
||||
irqreturn_t musb_interrupt(struct musb *musb)
|
||||
{
|
||||
irqreturn_t retval = IRQ_NONE;
|
||||
unsigned long status;
|
||||
unsigned long epnum;
|
||||
u8 devctl;
|
||||
int ep_num;
|
||||
u32 reg;
|
||||
|
||||
if (!musb->int_usb && !musb->int_tx && !musb->int_rx)
|
||||
return IRQ_NONE;
|
||||
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
|
||||
@ -1597,56 +1588,57 @@ irqreturn_t musb_interrupt(struct musb *musb)
|
||||
is_host_active(musb) ? "host" : "peripheral",
|
||||
musb->int_usb, musb->int_tx, musb->int_rx);
|
||||
|
||||
/* the core can interrupt us for multiple reasons; docs have
|
||||
* a generic interrupt flowchart to follow
|
||||
/**
|
||||
* According to Mentor Graphics' documentation, flowchart on page 98,
|
||||
* IRQ should be handled as follows:
|
||||
*
|
||||
* . Resume IRQ
|
||||
* . Session Request IRQ
|
||||
* . VBUS Error IRQ
|
||||
* . Suspend IRQ
|
||||
* . Connect IRQ
|
||||
* . Disconnect IRQ
|
||||
* . Reset/Babble IRQ
|
||||
* . SOF IRQ (we're not using this one)
|
||||
* . Endpoint 0 IRQ
|
||||
* . TX Endpoints
|
||||
* . RX Endpoints
|
||||
*
|
||||
* We will be following that flowchart in order to avoid any problems
|
||||
* that might arise with internal Finite State Machine.
|
||||
*/
|
||||
|
||||
if (musb->int_usb)
|
||||
retval |= musb_stage0_irq(musb, musb->int_usb,
|
||||
devctl);
|
||||
retval |= musb_stage0_irq(musb, musb->int_usb, devctl);
|
||||
|
||||
/* "stage 1" is handling endpoint irqs */
|
||||
|
||||
/* handle endpoint 0 first */
|
||||
if (musb->int_tx & 1) {
|
||||
if (is_host_active(musb))
|
||||
retval |= musb_h_ep0_irq(musb);
|
||||
else
|
||||
retval |= musb_g_ep0_irq(musb);
|
||||
|
||||
/* we have just handled endpoint 0 IRQ, clear it */
|
||||
musb->int_tx &= ~BIT(0);
|
||||
}
|
||||
|
||||
/* RX on endpoints 1-15 */
|
||||
reg = musb->int_rx >> 1;
|
||||
ep_num = 1;
|
||||
while (reg) {
|
||||
if (reg & 1) {
|
||||
/* musb_ep_select(musb->mregs, ep_num); */
|
||||
/* REVISIT just retval = ep->rx_irq(...) */
|
||||
retval = IRQ_HANDLED;
|
||||
if (is_host_active(musb))
|
||||
musb_host_rx(musb, ep_num);
|
||||
else
|
||||
musb_g_rx(musb, ep_num);
|
||||
}
|
||||
status = musb->int_tx;
|
||||
|
||||
reg >>= 1;
|
||||
ep_num++;
|
||||
for_each_set_bit(epnum, &status, 16) {
|
||||
retval = IRQ_HANDLED;
|
||||
if (is_host_active(musb))
|
||||
musb_host_tx(musb, epnum);
|
||||
else
|
||||
musb_g_tx(musb, epnum);
|
||||
}
|
||||
|
||||
/* TX on endpoints 1-15 */
|
||||
reg = musb->int_tx >> 1;
|
||||
ep_num = 1;
|
||||
while (reg) {
|
||||
if (reg & 1) {
|
||||
/* musb_ep_select(musb->mregs, ep_num); */
|
||||
/* REVISIT just retval |= ep->tx_irq(...) */
|
||||
retval = IRQ_HANDLED;
|
||||
if (is_host_active(musb))
|
||||
musb_host_tx(musb, ep_num);
|
||||
else
|
||||
musb_g_tx(musb, ep_num);
|
||||
}
|
||||
reg >>= 1;
|
||||
ep_num++;
|
||||
status = musb->int_rx;
|
||||
|
||||
for_each_set_bit(epnum, &status, 16) {
|
||||
retval = IRQ_HANDLED;
|
||||
if (is_host_active(musb))
|
||||
musb_host_rx(musb, epnum);
|
||||
else
|
||||
musb_g_rx(musb, epnum);
|
||||
}
|
||||
|
||||
return retval;
|
||||
@ -1825,33 +1817,44 @@ static void musb_irq_work(struct work_struct *data)
|
||||
}
|
||||
}
|
||||
|
||||
/* Recover from babble interrupt conditions */
|
||||
static void musb_recover_work(struct work_struct *data)
|
||||
static void musb_recover_from_babble(struct musb *musb)
|
||||
{
|
||||
struct musb *musb = container_of(data, struct musb, recover_work.work);
|
||||
int status, ret;
|
||||
int ret;
|
||||
u8 devctl;
|
||||
|
||||
ret = musb_platform_reset(musb);
|
||||
if (ret)
|
||||
musb_disable_interrupts(musb);
|
||||
|
||||
/*
|
||||
* wait at least 320 cycles of 60MHz clock. That's 5.3us, we will give
|
||||
* it some slack and wait for 10us.
|
||||
*/
|
||||
udelay(10);
|
||||
|
||||
ret = musb_platform_recover(musb);
|
||||
if (ret) {
|
||||
musb_enable_interrupts(musb);
|
||||
return;
|
||||
}
|
||||
|
||||
usb_phy_vbus_off(musb->xceiv);
|
||||
usleep_range(100, 200);
|
||||
/* drop session bit */
|
||||
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
|
||||
|
||||
usb_phy_vbus_on(musb->xceiv);
|
||||
usleep_range(100, 200);
|
||||
/* tell usbcore about it */
|
||||
musb_root_disconnect(musb);
|
||||
|
||||
/*
|
||||
* When a babble condition occurs, the musb controller
|
||||
* removes the session bit and the endpoint config is lost.
|
||||
*/
|
||||
if (musb->dyn_fifo)
|
||||
status = ep_config_from_table(musb);
|
||||
ret = ep_config_from_table(musb);
|
||||
else
|
||||
status = ep_config_from_hw(musb);
|
||||
ret = ep_config_from_hw(musb);
|
||||
|
||||
/* start the session again */
|
||||
if (status == 0)
|
||||
/* restart session */
|
||||
if (ret == 0)
|
||||
musb_start(musb);
|
||||
}
|
||||
|
||||
@ -2087,7 +2090,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
||||
|
||||
/* Init IRQ workqueue before request_irq */
|
||||
INIT_WORK(&musb->irq_work, musb_irq_work);
|
||||
INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work);
|
||||
INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
|
||||
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
|
||||
|
||||
@ -2183,7 +2185,6 @@ fail4:
|
||||
|
||||
fail3:
|
||||
cancel_work_sync(&musb->irq_work);
|
||||
cancel_delayed_work_sync(&musb->recover_work);
|
||||
cancel_delayed_work_sync(&musb->finish_resume_work);
|
||||
cancel_delayed_work_sync(&musb->deassert_reset_work);
|
||||
if (musb->dma_controller)
|
||||
@ -2249,7 +2250,6 @@ static int musb_remove(struct platform_device *pdev)
|
||||
dma_controller_destroy(musb->dma_controller);
|
||||
|
||||
cancel_work_sync(&musb->irq_work);
|
||||
cancel_delayed_work_sync(&musb->recover_work);
|
||||
cancel_delayed_work_sync(&musb->finish_resume_work);
|
||||
cancel_delayed_work_sync(&musb->deassert_reset_work);
|
||||
musb_free(musb);
|
||||
|
@ -160,7 +160,8 @@ struct musb_io;
|
||||
* @init: turns on clocks, sets up platform-specific registers, etc
|
||||
* @exit: undoes @init
|
||||
* @set_mode: forcefully changes operating mode
|
||||
* @try_ilde: tries to idle the IP
|
||||
* @try_idle: tries to idle the IP
|
||||
* @recover: platform-specific babble recovery
|
||||
* @vbus_status: returns vbus status if possible
|
||||
* @set_vbus: forces vbus status
|
||||
* @adjust_channel_params: pre check for standard dma channel_program func
|
||||
@ -196,7 +197,7 @@ struct musb_platform_ops {
|
||||
void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
|
||||
int (*set_mode)(struct musb *musb, u8 mode);
|
||||
void (*try_idle)(struct musb *musb, unsigned long timeout);
|
||||
int (*reset)(struct musb *musb);
|
||||
int (*recover)(struct musb *musb);
|
||||
|
||||
int (*vbus_status)(struct musb *musb);
|
||||
void (*set_vbus)(struct musb *musb, int on);
|
||||
@ -300,7 +301,6 @@ struct musb {
|
||||
|
||||
irqreturn_t (*isr)(int, void *);
|
||||
struct work_struct irq_work;
|
||||
struct delayed_work recover_work;
|
||||
struct delayed_work deassert_reset_work;
|
||||
struct delayed_work finish_resume_work;
|
||||
u16 hwvers;
|
||||
@ -558,12 +558,12 @@ static inline void musb_platform_try_idle(struct musb *musb,
|
||||
musb->ops->try_idle(musb, timeout);
|
||||
}
|
||||
|
||||
static inline int musb_platform_reset(struct musb *musb)
|
||||
static inline int musb_platform_recover(struct musb *musb)
|
||||
{
|
||||
if (!musb->ops->reset)
|
||||
return -EINVAL;
|
||||
if (!musb->ops->recover)
|
||||
return 0;
|
||||
|
||||
return musb->ops->reset(musb);
|
||||
return musb->ops->recover(musb);
|
||||
}
|
||||
|
||||
static inline int musb_platform_get_vbus_status(struct musb *musb)
|
||||
|
@ -225,10 +225,12 @@ static void cppi41_dma_callback(void *private_data)
|
||||
struct dma_channel *channel = private_data;
|
||||
struct cppi41_dma_channel *cppi41_channel = channel->private_data;
|
||||
struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
|
||||
struct cppi41_dma_controller *controller;
|
||||
struct musb *musb = hw_ep->musb;
|
||||
unsigned long flags;
|
||||
struct dma_tx_state txstate;
|
||||
u32 transferred;
|
||||
int is_hs = 0;
|
||||
bool empty;
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
@ -248,61 +250,59 @@ static void cppi41_dma_callback(void *private_data)
|
||||
transferred < cppi41_channel->packet_sz)
|
||||
cppi41_channel->prog_len = 0;
|
||||
|
||||
empty = musb_is_tx_fifo_empty(hw_ep);
|
||||
if (empty) {
|
||||
if (cppi41_channel->is_tx)
|
||||
empty = musb_is_tx_fifo_empty(hw_ep);
|
||||
|
||||
if (!cppi41_channel->is_tx || empty) {
|
||||
cppi41_trans_done(cppi41_channel);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* On AM335x it has been observed that the TX interrupt fires
|
||||
* too early that means the TXFIFO is not yet empty but the DMA
|
||||
* engine says that it is done with the transfer. We don't
|
||||
* receive a FIFO empty interrupt so the only thing we can do is
|
||||
* to poll for the bit. On HS it usually takes 2us, on FS around
|
||||
* 110us - 150us depending on the transfer size.
|
||||
* We spin on HS (no longer than than 25us and setup a timer on
|
||||
* FS to check for the bit and complete the transfer.
|
||||
*/
|
||||
controller = cppi41_channel->controller;
|
||||
|
||||
if (is_host_active(musb)) {
|
||||
if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
|
||||
is_hs = 1;
|
||||
} else {
|
||||
struct cppi41_dma_controller *controller;
|
||||
int is_hs = 0;
|
||||
/*
|
||||
* On AM335x it has been observed that the TX interrupt fires
|
||||
* too early that means the TXFIFO is not yet empty but the DMA
|
||||
* engine says that it is done with the transfer. We don't
|
||||
* receive a FIFO empty interrupt so the only thing we can do is
|
||||
* to poll for the bit. On HS it usually takes 2us, on FS around
|
||||
* 110us - 150us depending on the transfer size.
|
||||
* We spin on HS (no longer than than 25us and setup a timer on
|
||||
* FS to check for the bit and complete the transfer.
|
||||
*/
|
||||
controller = cppi41_channel->controller;
|
||||
|
||||
if (is_host_active(musb)) {
|
||||
if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
|
||||
is_hs = 1;
|
||||
} else {
|
||||
if (musb->g.speed == USB_SPEED_HIGH)
|
||||
is_hs = 1;
|
||||
}
|
||||
if (is_hs) {
|
||||
unsigned wait = 25;
|
||||
|
||||
do {
|
||||
empty = musb_is_tx_fifo_empty(hw_ep);
|
||||
if (empty)
|
||||
break;
|
||||
wait--;
|
||||
if (!wait)
|
||||
break;
|
||||
udelay(1);
|
||||
} while (1);
|
||||
if (musb->g.speed == USB_SPEED_HIGH)
|
||||
is_hs = 1;
|
||||
}
|
||||
if (is_hs) {
|
||||
unsigned wait = 25;
|
||||
|
||||
do {
|
||||
empty = musb_is_tx_fifo_empty(hw_ep);
|
||||
if (empty) {
|
||||
cppi41_trans_done(cppi41_channel);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
list_add_tail(&cppi41_channel->tx_check,
|
||||
&controller->early_tx_list);
|
||||
if (!hrtimer_is_queued(&controller->early_tx)) {
|
||||
unsigned long usecs = cppi41_channel->total_len / 10;
|
||||
wait--;
|
||||
if (!wait)
|
||||
break;
|
||||
cpu_relax();
|
||||
} while (1);
|
||||
}
|
||||
list_add_tail(&cppi41_channel->tx_check,
|
||||
&controller->early_tx_list);
|
||||
if (!hrtimer_is_queued(&controller->early_tx)) {
|
||||
unsigned long usecs = cppi41_channel->total_len / 10;
|
||||
|
||||
hrtimer_start_range_ns(&controller->early_tx,
|
||||
hrtimer_start_range_ns(&controller->early_tx,
|
||||
ktime_set(0, usecs * NSEC_PER_USEC),
|
||||
20 * NSEC_PER_USEC,
|
||||
HRTIMER_MODE_REL);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ struct dsps_musb_wrapper {
|
||||
unsigned iddig:5;
|
||||
unsigned iddig_mux:5;
|
||||
/* miscellaneous stuff */
|
||||
u8 poll_seconds;
|
||||
unsigned poll_timeout;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -225,9 +225,8 @@ static void dsps_musb_enable(struct musb *musb)
|
||||
|
||||
dsps_writel(reg_base, wrp->epintr_set, epmask);
|
||||
dsps_writel(reg_base, wrp->coreintr_set, coremask);
|
||||
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
|
||||
dsps_writel(reg_base, wrp->coreintr_set,
|
||||
(1 << wrp->drvvbus) << wrp->usb_shift);
|
||||
/* start polling for ID change. */
|
||||
mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout));
|
||||
dsps_musb_try_idle(musb, 0);
|
||||
}
|
||||
|
||||
@ -285,7 +284,8 @@ static void otg_timer(unsigned long _musb)
|
||||
}
|
||||
if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
|
||||
dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
|
||||
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
|
||||
mod_timer(&glue->timer, jiffies +
|
||||
msecs_to_jiffies(wrp->poll_timeout));
|
||||
break;
|
||||
case OTG_STATE_A_WAIT_VFALL:
|
||||
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
|
||||
@ -330,28 +330,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
|
||||
|
||||
dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
|
||||
usbintr, epintr);
|
||||
/*
|
||||
* DRVVBUS IRQs are the only proxy we have (a very poor one!) for
|
||||
* DSPS IP's missing ID change IRQ. We need an ID change IRQ to
|
||||
* switch appropriately between halves of the OTG state machine.
|
||||
* Managing DEVCTL.SESSION per Mentor docs requires that we know its
|
||||
* value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
|
||||
* Also, DRVVBUS pulses for SRP (but not at 5V) ...
|
||||
*/
|
||||
if (is_host_active(musb) && usbintr & MUSB_INTR_BABBLE) {
|
||||
pr_info("CAUTION: musb: Babble Interrupt Occurred\n");
|
||||
|
||||
/*
|
||||
* When a babble condition occurs, the musb controller removes
|
||||
* the session and is no longer in host mode. Hence, all
|
||||
* devices connected to its root hub get disconnected.
|
||||
*
|
||||
* Hand this error down to the musb core isr, so it can
|
||||
* recover.
|
||||
*/
|
||||
musb->int_usb = MUSB_INTR_BABBLE | MUSB_INTR_DISCONNECT;
|
||||
musb->int_tx = musb->int_rx = 0;
|
||||
}
|
||||
|
||||
if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
|
||||
int drvvbus = dsps_readl(reg_base, wrp->status);
|
||||
@ -374,8 +352,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
|
||||
*/
|
||||
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
|
||||
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
|
||||
mod_timer(&glue->timer,
|
||||
jiffies + wrp->poll_seconds * HZ);
|
||||
mod_timer(&glue->timer, jiffies +
|
||||
msecs_to_jiffies(wrp->poll_timeout));
|
||||
WARNING("VBUS error workaround (delay coming)\n");
|
||||
} else if (drvvbus) {
|
||||
MUSB_HST_MODE(musb);
|
||||
@ -404,7 +382,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
|
||||
/* Poll for ID change in OTG port mode */
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
|
||||
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
|
||||
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
|
||||
mod_timer(&glue->timer, jiffies +
|
||||
msecs_to_jiffies(wrp->poll_timeout));
|
||||
out:
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
@ -453,7 +432,7 @@ static int dsps_musb_init(struct musb *musb)
|
||||
musb->ctrl_base = reg_base;
|
||||
|
||||
/* NOP driver needs change if supporting dual instance */
|
||||
musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0);
|
||||
musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "phys", 0);
|
||||
if (IS_ERR(musb->xceiv))
|
||||
return PTR_ERR(musb->xceiv);
|
||||
|
||||
@ -497,7 +476,7 @@ static int dsps_musb_init(struct musb *musb)
|
||||
* logic enabled.
|
||||
*/
|
||||
val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
|
||||
if (val == MUSB_BABBLE_RCV_DISABLE) {
|
||||
if (val & MUSB_BABBLE_RCV_DISABLE) {
|
||||
glue->sw_babble_enabled = true;
|
||||
val |= MUSB_BABBLE_SW_SESSION_CTRL;
|
||||
dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
|
||||
@ -571,7 +550,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sw_babble_control(struct musb *musb)
|
||||
static bool dsps_sw_babble_control(struct musb *musb)
|
||||
{
|
||||
u8 babble_ctl;
|
||||
bool session_restart = false;
|
||||
@ -622,37 +601,36 @@ static bool sw_babble_control(struct musb *musb)
|
||||
return session_restart;
|
||||
}
|
||||
|
||||
static int dsps_musb_reset(struct musb *musb)
|
||||
static int dsps_musb_recover(struct musb *musb)
|
||||
{
|
||||
struct device *dev = musb->controller;
|
||||
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
|
||||
const struct dsps_musb_wrapper *wrp = glue->wrp;
|
||||
int session_restart = 0, error;
|
||||
int session_restart = 0;
|
||||
|
||||
if (glue->sw_babble_enabled)
|
||||
session_restart = sw_babble_control(musb);
|
||||
/*
|
||||
* In case of new silicon version babble condition can be recovered
|
||||
* without resetting the MUSB. But for older silicon versions, MUSB
|
||||
* reset is needed
|
||||
*/
|
||||
if (session_restart || !glue->sw_babble_enabled) {
|
||||
dev_info(musb->controller, "Restarting MUSB to recover from Babble\n");
|
||||
dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
|
||||
usleep_range(100, 200);
|
||||
usb_phy_shutdown(musb->xceiv);
|
||||
error = phy_power_off(musb->phy);
|
||||
if (error)
|
||||
dev_err(dev, "phy shutdown failed: %i\n", error);
|
||||
usleep_range(100, 200);
|
||||
usb_phy_init(musb->xceiv);
|
||||
error = phy_power_on(musb->phy);
|
||||
if (error)
|
||||
dev_err(dev, "phy powerup failed: %i\n", error);
|
||||
session_restart = dsps_sw_babble_control(musb);
|
||||
else
|
||||
session_restart = 1;
|
||||
|
||||
return session_restart ? 0 : -EPIPE;
|
||||
}
|
||||
|
||||
/* Similar to am35x, dm81xx support only 32-bit read operation */
|
||||
static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
|
||||
{
|
||||
void __iomem *fifo = hw_ep->fifo;
|
||||
|
||||
if (len >= 4) {
|
||||
readsl(fifo, dst, len >> 2);
|
||||
dst += len & ~0x03;
|
||||
len &= 0x03;
|
||||
}
|
||||
|
||||
return !session_restart;
|
||||
/* Read any remaining 1 to 3 bytes */
|
||||
if (len > 0) {
|
||||
u32 val = musb_readl(fifo, 0);
|
||||
memcpy(dst, &val, len);
|
||||
}
|
||||
}
|
||||
|
||||
static struct musb_platform_ops dsps_ops = {
|
||||
@ -665,7 +643,7 @@ static struct musb_platform_ops dsps_ops = {
|
||||
|
||||
.try_idle = dsps_musb_try_idle,
|
||||
.set_mode = dsps_musb_set_mode,
|
||||
.reset = dsps_musb_reset,
|
||||
.recover = dsps_musb_recover,
|
||||
};
|
||||
|
||||
static u64 musb_dmamask = DMA_BIT_MASK(32);
|
||||
@ -737,7 +715,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
|
||||
musb->dev.parent = dev;
|
||||
musb->dev.dma_mask = &musb_dmamask;
|
||||
musb->dev.coherent_dma_mask = musb_dmamask;
|
||||
musb->dev.of_node = of_node_get(dn);
|
||||
|
||||
glue->musb = musb;
|
||||
|
||||
@ -802,6 +779,9 @@ static int dsps_probe(struct platform_device *pdev)
|
||||
}
|
||||
wrp = match->data;
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "ti,musb-dm816"))
|
||||
dsps_ops.read_fifo = dsps_read_fifo32;
|
||||
|
||||
/* allocate glue */
|
||||
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
|
||||
if (!glue)
|
||||
@ -873,12 +853,14 @@ static const struct dsps_musb_wrapper am33xx_driver_data = {
|
||||
.rxep_shift = 16,
|
||||
.rxep_mask = 0xfffe,
|
||||
.rxep_bitmap = (0xfffe << 16),
|
||||
.poll_seconds = 2,
|
||||
.poll_timeout = 2000, /* ms */
|
||||
};
|
||||
|
||||
static const struct of_device_id musb_dsps_of_match[] = {
|
||||
{ .compatible = "ti,musb-am33xx",
|
||||
.data = (void *) &am33xx_driver_data, },
|
||||
.data = &am33xx_driver_data, },
|
||||
{ .compatible = "ti,musb-dm816",
|
||||
.data = &am33xx_driver_data, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
|
||||
@ -929,7 +911,8 @@ static int dsps_resume(struct device *dev)
|
||||
dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
|
||||
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
|
||||
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
|
||||
mod_timer(&glue->timer, jiffies +
|
||||
msecs_to_jiffies(wrp->poll_timeout));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1876,44 +1876,6 @@ err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
|
||||
{
|
||||
int i;
|
||||
struct musb_hw_ep *hw_ep;
|
||||
|
||||
/* don't disconnect if it's not connected */
|
||||
if (musb->g.speed == USB_SPEED_UNKNOWN)
|
||||
driver = NULL;
|
||||
else
|
||||
musb->g.speed = USB_SPEED_UNKNOWN;
|
||||
|
||||
/* deactivate the hardware */
|
||||
if (musb->softconnect) {
|
||||
musb->softconnect = 0;
|
||||
musb_pullup(musb, 0);
|
||||
}
|
||||
musb_stop(musb);
|
||||
|
||||
/* killing any outstanding requests will quiesce the driver;
|
||||
* then report disconnect
|
||||
*/
|
||||
if (driver) {
|
||||
for (i = 0, hw_ep = musb->endpoints;
|
||||
i < musb->nr_endpoints;
|
||||
i++, hw_ep++) {
|
||||
musb_ep_select(musb->mregs, i);
|
||||
if (hw_ep->is_shared_fifo /* || !epnum */) {
|
||||
nuke(&hw_ep->ep_in, -ESHUTDOWN);
|
||||
} else {
|
||||
if (hw_ep->max_packet_sz_tx)
|
||||
nuke(&hw_ep->ep_in, -ESHUTDOWN);
|
||||
if (hw_ep->max_packet_sz_rx)
|
||||
nuke(&hw_ep->ep_out, -ESHUTDOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unregister the gadget driver. Used by gadget drivers when
|
||||
* unregistering themselves from the controller.
|
||||
@ -1940,7 +1902,7 @@ static int musb_gadget_stop(struct usb_gadget *g)
|
||||
(void) musb_gadget_vbus_draw(&musb->g, 0);
|
||||
|
||||
musb->xceiv->otg->state = OTG_STATE_UNDEFINED;
|
||||
stop_activity(musb, NULL);
|
||||
musb_stop(musb);
|
||||
otg_set_peripheral(musb->xceiv->otg, NULL);
|
||||
|
||||
musb->is_active = 0;
|
||||
|
@ -202,13 +202,13 @@ config USB_RCAR_GEN2_PHY
|
||||
config USB_ULPI
|
||||
bool "Generic ULPI Transceiver Driver"
|
||||
depends on ARM || ARM64
|
||||
select USB_ULPI_VIEWPORT
|
||||
help
|
||||
Enable this to support ULPI connected USB OTG transceivers which
|
||||
are likely found on embedded boards.
|
||||
|
||||
config USB_ULPI_VIEWPORT
|
||||
bool
|
||||
depends on USB_ULPI
|
||||
help
|
||||
Provides read/write operations to the ULPI phy register set for
|
||||
controllers with a viewport register (e.g. Chipidea/ARC controllers).
|
||||
|
@ -27,7 +27,7 @@ static const char *const usbphy_modes[] = {
|
||||
* @np: Pointer to the given device_node
|
||||
*
|
||||
* The function gets phy interface string from property 'phy_type',
|
||||
* and returns the correspondig enum usb_phy_interface
|
||||
* and returns the corresponding enum usb_phy_interface
|
||||
*/
|
||||
enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np)
|
||||
{
|
||||
|
@ -893,7 +893,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
|
||||
|
||||
/*
|
||||
* Disconnection Sequence:
|
||||
* 1. Disconect Interrupt
|
||||
* 1. Disconnect Interrupt
|
||||
* 2. Disable regulators
|
||||
* 3. Disable AB clock
|
||||
* 4. Disable the Phy
|
||||
|
@ -62,14 +62,14 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
|
||||
static void nop_reset(struct usb_phy_generic *nop)
|
||||
{
|
||||
if (!nop->gpiod_reset)
|
||||
return;
|
||||
|
||||
gpiod_direction_output(nop->gpiod_reset, !asserted);
|
||||
gpiod_set_value(nop->gpiod_reset, 1);
|
||||
usleep_range(10000, 20000);
|
||||
gpiod_set_value(nop->gpiod_reset, asserted);
|
||||
gpiod_set_value(nop->gpiod_reset, 0);
|
||||
}
|
||||
|
||||
/* interface to regulator framework */
|
||||
@ -151,8 +151,7 @@ int usb_gen_phy_init(struct usb_phy *phy)
|
||||
if (!IS_ERR(nop->clk))
|
||||
clk_prepare_enable(nop->clk);
|
||||
|
||||
/* De-assert RESET */
|
||||
nop_reset_set(nop, 0);
|
||||
nop_reset(nop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -162,8 +161,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
|
||||
{
|
||||
struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
|
||||
|
||||
/* Assert RESET */
|
||||
nop_reset_set(nop, 1);
|
||||
gpiod_set_value(nop->gpiod_reset, 1);
|
||||
|
||||
if (!IS_ERR(nop->clk))
|
||||
clk_disable_unprepare(nop->clk);
|
||||
|
@ -81,7 +81,9 @@ static void devm_usb_phy_release(struct device *dev, void *res)
|
||||
|
||||
static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
|
||||
{
|
||||
return res == match_data;
|
||||
struct usb_phy **phy = res;
|
||||
|
||||
return *phy == match_data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -275,6 +275,16 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum,
|
||||
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
|
||||
*/
|
||||
@ -487,6 +497,15 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
|
||||
if (gpio > 0)
|
||||
dparam->enable_gpio = gpio;
|
||||
|
||||
switch (dparam->type) {
|
||||
case USBHS_TYPE_R8A7790:
|
||||
case USBHS_TYPE_R8A7791:
|
||||
dparam->has_usb_dmac = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
@ -193,6 +193,7 @@ struct usbhs_priv;
|
||||
#define TYPE_BULK (1 << 14)
|
||||
#define TYPE_INT (2 << 14)
|
||||
#define TYPE_ISO (3 << 14)
|
||||
#define BFRE (1 << 10) /* BRDY Interrupt Operation Spec. */
|
||||
#define DBLB (1 << 9) /* Double Buffer Mode */
|
||||
#define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */
|
||||
#define DIR_OUT (1 << 4) /* Transfer Direction */
|
||||
@ -216,6 +217,7 @@ struct usbhs_priv;
|
||||
#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */
|
||||
#define SQCLR (1 << 8) /* Toggle Bit Clear */
|
||||
#define SQSET (1 << 7) /* Toggle Bit Set */
|
||||
#define SQMON (1 << 6) /* Toggle Bit Check */
|
||||
#define PBUSY (1 << 5) /* Pipe Busy */
|
||||
#define PID_MASK (0x3) /* Response PID */
|
||||
#define PID_NAK 0
|
||||
@ -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,
|
||||
u16 hubport, u16 speed);
|
||||
|
||||
/*
|
||||
* interrupt functions
|
||||
*/
|
||||
void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit);
|
||||
|
||||
/*
|
||||
* data
|
||||
*/
|
||||
|
@ -813,7 +813,8 @@ static void xfer_work(struct work_struct *work)
|
||||
desc->callback = usbhsf_dma_complete;
|
||||
desc->callback_param = pipe;
|
||||
|
||||
if (dmaengine_submit(desc) < 0) {
|
||||
pkt->cookie = dmaengine_submit(desc);
|
||||
if (pkt->cookie < 0) {
|
||||
dev_err(dev, "Failed to submit dma descriptor\n");
|
||||
return;
|
||||
}
|
||||
@ -822,10 +823,10 @@ static void xfer_work(struct work_struct *work)
|
||||
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
|
||||
|
||||
usbhs_pipe_running(pipe, 1);
|
||||
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
|
||||
usbhs_pipe_enable(pipe);
|
||||
usbhsf_dma_start(pipe, fifo);
|
||||
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
|
||||
dma_async_issue_pending(chan);
|
||||
usbhs_pipe_enable(pipe);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -838,6 +839,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
||||
struct usbhs_fifo *fifo;
|
||||
int len = pkt->length - pkt->actual;
|
||||
int ret;
|
||||
uintptr_t align_mask;
|
||||
|
||||
if (usbhs_pipe_is_busy(pipe))
|
||||
return 0;
|
||||
@ -847,10 +849,14 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
|
||||
usbhs_pipe_is_dcp(pipe))
|
||||
goto usbhsf_pio_prepare_push;
|
||||
|
||||
if (len & 0x7) /* 8byte alignment */
|
||||
/* check data length if this driver don't use USB-DMAC */
|
||||
if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7)
|
||||
goto usbhsf_pio_prepare_push;
|
||||
|
||||
if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
|
||||
/* check buffer alignment */
|
||||
align_mask = usbhs_get_dparam(priv, has_usb_dmac) ?
|
||||
USBHS_USB_DMAC_XFER_SIZE - 1 : 0x7;
|
||||
if ((uintptr_t)(pkt->buf + pkt->actual) & align_mask)
|
||||
goto usbhsf_pio_prepare_push;
|
||||
|
||||
/* return at this time if the pipe is running */
|
||||
@ -924,7 +930,85 @@ struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
|
||||
/*
|
||||
* DMA pop handler
|
||||
*/
|
||||
static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
|
||||
|
||||
static int usbhsf_dma_prepare_pop_with_rx_irq(struct usbhs_pkt *pkt,
|
||||
int *is_done)
|
||||
{
|
||||
return usbhsf_prepare_pop(pkt, is_done);
|
||||
}
|
||||
|
||||
static int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt,
|
||||
int *is_done)
|
||||
{
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
struct usbhs_fifo *fifo;
|
||||
int ret;
|
||||
|
||||
if (usbhs_pipe_is_busy(pipe))
|
||||
return 0;
|
||||
|
||||
/* use PIO if packet is less than pio_dma_border or pipe is DCP */
|
||||
if ((pkt->length < usbhs_get_dparam(priv, pio_dma_border)) ||
|
||||
usbhs_pipe_is_dcp(pipe))
|
||||
goto usbhsf_pio_prepare_pop;
|
||||
|
||||
fifo = usbhsf_get_dma_fifo(priv, pkt);
|
||||
if (!fifo)
|
||||
goto usbhsf_pio_prepare_pop;
|
||||
|
||||
if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1))
|
||||
goto usbhsf_pio_prepare_pop;
|
||||
|
||||
usbhs_pipe_config_change_bfre(pipe, 1);
|
||||
|
||||
ret = usbhsf_fifo_select(pipe, fifo, 0);
|
||||
if (ret < 0)
|
||||
goto usbhsf_pio_prepare_pop;
|
||||
|
||||
if (usbhsf_dma_map(pkt) < 0)
|
||||
goto usbhsf_pio_prepare_pop_unselect;
|
||||
|
||||
/* DMA */
|
||||
|
||||
/*
|
||||
* usbhs_fifo_dma_pop_handler :: prepare
|
||||
* enabled irq to come here.
|
||||
* but it is no longer needed for DMA. disable it.
|
||||
*/
|
||||
usbhsf_rx_irq_ctrl(pipe, 0);
|
||||
|
||||
pkt->trans = pkt->length;
|
||||
|
||||
INIT_WORK(&pkt->work, xfer_work);
|
||||
schedule_work(&pkt->work);
|
||||
|
||||
return 0;
|
||||
|
||||
usbhsf_pio_prepare_pop_unselect:
|
||||
usbhsf_fifo_unselect(pipe, fifo);
|
||||
usbhsf_pio_prepare_pop:
|
||||
|
||||
/*
|
||||
* change handler to PIO
|
||||
*/
|
||||
pkt->handler = &usbhs_fifo_pio_pop_handler;
|
||||
usbhs_pipe_config_change_bfre(pipe, 0);
|
||||
|
||||
return pkt->handler->prepare(pkt, is_done);
|
||||
}
|
||||
|
||||
static int usbhsf_dma_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
|
||||
|
||||
if (usbhs_get_dparam(priv, has_usb_dmac))
|
||||
return usbhsf_dma_prepare_pop_with_usb_dmac(pkt, is_done);
|
||||
else
|
||||
return usbhsf_dma_prepare_pop_with_rx_irq(pkt, is_done);
|
||||
}
|
||||
|
||||
static int usbhsf_dma_try_pop_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
|
||||
{
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
@ -993,7 +1077,16 @@ usbhsf_pio_prepare_pop:
|
||||
return pkt->handler->try_run(pkt, is_done);
|
||||
}
|
||||
|
||||
static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
|
||||
static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
|
||||
|
||||
BUG_ON(usbhs_get_dparam(priv, has_usb_dmac));
|
||||
|
||||
return usbhsf_dma_try_pop_with_rx_irq(pkt, is_done);
|
||||
}
|
||||
|
||||
static int usbhsf_dma_pop_done_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
|
||||
{
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
int maxp = usbhs_pipe_get_maxpacket(pipe);
|
||||
@ -1017,8 +1110,68 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt,
|
||||
struct dma_chan *chan, int dtln)
|
||||
{
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct dma_tx_state state;
|
||||
size_t received_size;
|
||||
int maxp = usbhs_pipe_get_maxpacket(pipe);
|
||||
|
||||
dmaengine_tx_status(chan, pkt->cookie, &state);
|
||||
received_size = pkt->length - state.residue;
|
||||
|
||||
if (dtln) {
|
||||
received_size -= USBHS_USB_DMAC_XFER_SIZE;
|
||||
received_size &= ~(maxp - 1);
|
||||
received_size += dtln;
|
||||
}
|
||||
|
||||
return received_size;
|
||||
}
|
||||
|
||||
static int usbhsf_dma_pop_done_with_usb_dmac(struct usbhs_pkt *pkt,
|
||||
int *is_done)
|
||||
{
|
||||
struct usbhs_pipe *pipe = pkt->pipe;
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
|
||||
struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
|
||||
int rcv_len;
|
||||
|
||||
/*
|
||||
* Since the driver disables rx_irq in DMA mode, the interrupt handler
|
||||
* cannot the BRDYSTS. So, the function clears it here because the
|
||||
* driver may use PIO mode next time.
|
||||
*/
|
||||
usbhs_xxxsts_clear(priv, BRDYSTS, usbhs_pipe_number(pipe));
|
||||
|
||||
rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
|
||||
usbhsf_fifo_clear(pipe, fifo);
|
||||
pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len);
|
||||
|
||||
usbhsf_dma_stop(pipe, fifo);
|
||||
usbhsf_dma_unmap(pkt);
|
||||
usbhsf_fifo_unselect(pipe, pipe->fifo);
|
||||
|
||||
/* The driver can assume the rx transaction is always "done" */
|
||||
*is_done = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
|
||||
|
||||
if (usbhs_get_dparam(priv, has_usb_dmac))
|
||||
return usbhsf_dma_pop_done_with_usb_dmac(pkt, is_done);
|
||||
else
|
||||
return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done);
|
||||
}
|
||||
|
||||
struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
|
||||
.prepare = usbhsf_prepare_pop,
|
||||
.prepare = usbhsf_dma_prepare_pop,
|
||||
.try_run = usbhsf_dma_try_pop,
|
||||
.dma_done = usbhsf_dma_pop_done
|
||||
};
|
||||
@ -1069,23 +1222,29 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo)
|
||||
&fifo->rx_slave);
|
||||
}
|
||||
|
||||
static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo)
|
||||
static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo,
|
||||
int channel)
|
||||
{
|
||||
fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx");
|
||||
char name[16];
|
||||
|
||||
snprintf(name, sizeof(name), "tx%d", channel);
|
||||
fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
|
||||
if (IS_ERR(fifo->tx_chan))
|
||||
fifo->tx_chan = NULL;
|
||||
fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx");
|
||||
|
||||
snprintf(name, sizeof(name), "rx%d", channel);
|
||||
fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
|
||||
if (IS_ERR(fifo->rx_chan))
|
||||
fifo->rx_chan = NULL;
|
||||
}
|
||||
|
||||
static void usbhsf_dma_init(struct usbhs_priv *priv,
|
||||
struct usbhs_fifo *fifo)
|
||||
static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,
|
||||
int channel)
|
||||
{
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
|
||||
if (dev->of_node)
|
||||
usbhsf_dma_init_dt(dev, fifo);
|
||||
usbhsf_dma_init_dt(dev, fifo, channel);
|
||||
else
|
||||
usbhsf_dma_init_pdev(fifo);
|
||||
|
||||
@ -1231,7 +1390,7 @@ do { \
|
||||
usbhs_get_dparam(priv, d##channel##_tx_id); \
|
||||
fifo->rx_slave.shdma_slave.slave_id = \
|
||||
usbhs_get_dparam(priv, d##channel##_rx_id); \
|
||||
usbhsf_dma_init(priv, fifo); \
|
||||
usbhsf_dma_init(priv, fifo, channel); \
|
||||
} while (0)
|
||||
|
||||
#define USBHS_DFIFO_INIT(priv, fifo, channel) \
|
||||
|
@ -58,6 +58,7 @@ struct usbhs_pkt {
|
||||
struct usbhs_pkt *pkt);
|
||||
struct work_struct work;
|
||||
dma_addr_t dma;
|
||||
dma_cookie_t cookie;
|
||||
void *buf;
|
||||
int length;
|
||||
int trans;
|
||||
|
@ -119,18 +119,34 @@ struct usbhsg_recip_handle {
|
||||
/*
|
||||
* queue push/pop
|
||||
*/
|
||||
static void __usbhsg_queue_pop(struct usbhsg_uep *uep,
|
||||
struct usbhsg_request *ureq,
|
||||
int status)
|
||||
{
|
||||
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
|
||||
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
|
||||
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
|
||||
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
|
||||
|
||||
dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
|
||||
|
||||
ureq->req.status = status;
|
||||
spin_unlock(usbhs_priv_to_lock(priv));
|
||||
usb_gadget_giveback_request(&uep->ep, &ureq->req);
|
||||
spin_lock(usbhs_priv_to_lock(priv));
|
||||
}
|
||||
|
||||
static void usbhsg_queue_pop(struct usbhsg_uep *uep,
|
||||
struct usbhsg_request *ureq,
|
||||
int status)
|
||||
{
|
||||
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
|
||||
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
|
||||
struct device *dev = usbhsg_gpriv_to_dev(gpriv);
|
||||
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
|
||||
|
||||
ureq->req.status = status;
|
||||
usb_gadget_giveback_request(&uep->ep, &ureq->req);
|
||||
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)
|
||||
|
@ -84,6 +84,17 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
|
||||
usbhs_bset(priv, pipe_reg, mask, val);
|
||||
}
|
||||
|
||||
static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
|
||||
u16 dcp_reg, u16 pipe_reg)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
|
||||
|
||||
if (usbhs_pipe_is_dcp(pipe))
|
||||
return usbhs_read(priv, dcp_reg);
|
||||
else
|
||||
return usbhs_read(priv, pipe_reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* DCPCFG/PIPECFG functions
|
||||
*/
|
||||
@ -92,6 +103,11 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
|
||||
__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
|
||||
}
|
||||
|
||||
static u16 usbhsp_pipe_cfg_get(struct usbhs_pipe *pipe)
|
||||
{
|
||||
return __usbhsp_pipe_xxx_get(pipe, DCPCFG, PIPECFG);
|
||||
}
|
||||
|
||||
/*
|
||||
* PIPEnTRN/PIPEnTRE functions
|
||||
*/
|
||||
@ -616,6 +632,11 @@ void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
|
||||
usbhsp_pipectrl_set(pipe, mask, val);
|
||||
}
|
||||
|
||||
static int usbhs_pipe_get_data_sequence(struct usbhs_pipe *pipe)
|
||||
{
|
||||
return !!(usbhsp_pipectrl_get(pipe) & SQMON);
|
||||
}
|
||||
|
||||
void usbhs_pipe_clear(struct usbhs_pipe *pipe)
|
||||
{
|
||||
if (usbhs_pipe_is_dcp(pipe)) {
|
||||
@ -626,6 +647,24 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe)
|
||||
}
|
||||
}
|
||||
|
||||
void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable)
|
||||
{
|
||||
int sequence;
|
||||
|
||||
if (usbhs_pipe_is_dcp(pipe))
|
||||
return;
|
||||
|
||||
usbhsp_pipe_select(pipe);
|
||||
/* check if the driver needs to change the BFRE value */
|
||||
if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE)))
|
||||
return;
|
||||
|
||||
sequence = usbhs_pipe_get_data_sequence(pipe);
|
||||
usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0);
|
||||
usbhs_pipe_clear(pipe);
|
||||
usbhs_pipe_data_sequence(pipe, sequence);
|
||||
}
|
||||
|
||||
static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
|
||||
{
|
||||
struct usbhs_pipe *pos, *pipe;
|
||||
|
@ -97,6 +97,7 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len);
|
||||
void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
|
||||
void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
|
||||
u16 epnum, u16 maxp);
|
||||
void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable);
|
||||
|
||||
#define usbhs_pipe_sequence_data0(pipe) usbhs_pipe_data_sequence(pipe, 0)
|
||||
#define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1)
|
||||
|
@ -2315,6 +2315,8 @@
|
||||
#define PCI_VENDOR_ID_CENATEK 0x16CA
|
||||
#define PCI_DEVICE_ID_CENATEK_IDE 0x0001
|
||||
|
||||
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
|
||||
|
||||
#define PCI_VENDOR_ID_VITESSE 0x1725
|
||||
#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174
|
||||
|
||||
|
@ -148,6 +148,7 @@ struct usb_os_desc_table {
|
||||
* @disable: (REQUIRED) Indicates the function should be disabled. Reasons
|
||||
* include host resetting or reconfiguring the gadget, and disconnection.
|
||||
* @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.
|
||||
* @resume: Notifies functions when the host restarts USB traffic.
|
||||
* @get_status: Returns function status as a reply to
|
||||
@ -213,6 +214,8 @@ struct usb_function {
|
||||
void (*disable)(struct usb_function *);
|
||||
int (*setup)(struct usb_function *,
|
||||
const struct usb_ctrlrequest *);
|
||||
bool (*req_match)(struct usb_function *,
|
||||
const struct usb_ctrlrequest *);
|
||||
void (*suspend)(struct usb_function *);
|
||||
void (*resume)(struct usb_function *);
|
||||
|
||||
|
@ -190,7 +190,7 @@ struct usb_ep {
|
||||
* @ep:the endpoint being configured
|
||||
* @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).
|
||||
*/
|
||||
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
|
||||
@ -474,6 +474,7 @@ struct usb_dcd_config_params {
|
||||
|
||||
struct usb_gadget;
|
||||
struct usb_gadget_driver;
|
||||
struct usb_udc;
|
||||
|
||||
/* the rest of the api to the controller hardware: device operations,
|
||||
* which don't involve endpoints (or i/o).
|
||||
@ -496,6 +497,7 @@ struct usb_gadget_ops {
|
||||
/**
|
||||
* struct usb_gadget - represents a usb slave device
|
||||
* @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.
|
||||
* @ep0: Endpoint zero, used when reading or writing responses to
|
||||
* driver setup() requests
|
||||
@ -545,6 +547,7 @@ struct usb_gadget_ops {
|
||||
*/
|
||||
struct usb_gadget {
|
||||
struct work_struct work;
|
||||
struct usb_udc *udc;
|
||||
/* readonly to gadget driver */
|
||||
const struct usb_gadget_ops *ops;
|
||||
struct usb_ep *ep0;
|
||||
@ -1029,6 +1032,10 @@ extern void usb_gadget_udc_reset(struct usb_gadget *gadget,
|
||||
extern void usb_gadget_giveback_request(struct usb_ep *ep,
|
||||
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 {
|
||||
*/
|
||||
u32 has_otg:1; /* for controlling PWEN/EXTLP */
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user