summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-02-15 21:24:55 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-15 21:24:55 +0300
commite29876723f7cb7728f0d6a674d23f92673e9f112 (patch)
treeea1da8bf77139f6cc6de029988208a7eddaf2002 /drivers
parent8c988ae787af4900bec5410658e8a82844185c85 (diff)
parent4d4bac4499e9955521af80198063ef9c2f2bd634 (diff)
downloadlinux-e29876723f7cb7728f0d6a674d23f92673e9f112.tar.xz
Merge tag 'usb-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB patches from Greg KH: "Here's the big pull request for the USB driver tree for 3.20-rc1. Nothing major happening here, just lots of gadget driver updates, new device ids, and a bunch of cleanups. All of these have been in linux-next for a while with no reported issues" * tag 'usb-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (299 commits) usb: musb: fix device hotplug behind hub usb: dwc2: Fix a bug in reading the endpoint directions from reg. staging: emxx_udc: fix the build error usb: Retry port status check on resume to work around RH bugs Revert "usb: Reset USB-3 devices on USB-3 link bounce" uhci-hub: use HUB_CHAR_* usb: kconfig: replace PPC_OF with PPC ehci-pci: disable for Intel MID platforms (update) usb: gadget: Kconfig: use bool instead of boolean usb: musb: blackfin: remove incorrect __exit_p() USB: fix use-after-free bug in usb_hcd_unlink_urb() ehci-pci: disable for Intel MID platforms usb: host: pci_quirks: joing string literals USB: add flag for HCDs that can't receive wakeup requests (isp1760-hcd) USB: usbfs: allow URBs to be reaped after disconnection cdc-acm: kill unnecessary messages cdc-acm: add sanity checks usb: phy: phy-generic: Fix USB PHY gpio reset usb: dwc2: fix USB core dependencies usb: renesas_usbhs: fix NULL pointer dereference in dma_release_channel() ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/phy/Kconfig7
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-armada375-usb2.c4
-rw-r--r--drivers/phy/phy-exynos-mipi-video.c89
-rw-r--r--drivers/phy/phy-miphy28lp.c61
-rw-r--r--drivers/phy/phy-rockchip-usb.c158
-rw-r--r--drivers/phy/phy-ti-pipe3.c143
-rw-r--r--drivers/staging/emxx_udc/emxx_udc.c8
-rw-r--r--drivers/staging/emxx_udc/emxx_udc.h1
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/Makefile2
-rw-r--r--drivers/usb/chipidea/ci_hdrc_pci.c3
-rw-r--r--drivers/usb/chipidea/udc.c18
-rw-r--r--drivers/usb/class/cdc-acm.c54
-rw-r--r--drivers/usb/core/buffer.c26
-rw-r--r--drivers/usb/core/devio.c63
-rw-r--r--drivers/usb/core/driver.c29
-rw-r--r--drivers/usb/core/hcd.c16
-rw-r--r--drivers/usb/core/hub.c82
-rw-r--r--drivers/usb/core/message.c23
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/dwc2/Kconfig4
-rw-r--r--drivers/usb/dwc2/core.c2
-rw-r--r--drivers/usb/dwc2/core.h60
-rw-r--r--drivers/usb/dwc2/gadget.c1191
-rw-r--r--drivers/usb/dwc2/hcd.c100
-rw-r--r--drivers/usb/dwc2/hw.h2
-rw-r--r--drivers/usb/dwc2/platform.c36
-rw-r--r--drivers/usb/dwc3/Kconfig6
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/core.c2
-rw-r--r--drivers/usb/dwc3/core.h5
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c178
-rw-r--r--drivers/usb/dwc3/ep0.c2
-rw-r--r--drivers/usb/dwc3/gadget.c82
-rw-r--r--drivers/usb/dwc3/trace.h10
-rw-r--r--drivers/usb/gadget/Kconfig11
-rw-r--r--drivers/usb/gadget/composite.c2
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_fs.c119
-rw-r--r--drivers/usb/gadget/function/f_hid.c2
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c20
-rw-r--r--drivers/usb/gadget/function/f_uac1.c16
-rw-r--r--drivers/usb/gadget/function/f_uvc.c136
-rw-r--r--drivers/usb/gadget/function/u_ether.c4
-rw-r--r--drivers/usb/gadget/function/u_fs.h25
-rw-r--r--drivers/usb/gadget/function/u_uac1.c3
-rw-r--r--drivers/usb/gadget/function/u_uac1.h1
-rw-r--r--drivers/usb/gadget/function/u_uvc.h52
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c2468
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.h22
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c8
-rw-r--r--drivers/usb/gadget/udc/at91_udc.h1
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c147
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h9
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c1
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c10
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_udc.c1
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c9
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c2
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c5
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c12
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c3
-rw-r--r--drivers/usb/gadget/udc/net2272.c7
-rw-r--r--drivers/usb/gadget/udc/net2272.h1
-rw-r--r--drivers/usb/gadget/udc/net2280.c533
-rw-r--r--drivers/usb/gadget/udc/net2280.h24
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c1
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c1
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c2
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c2
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c1
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c21
-rw-r--r--drivers/usb/gadget/udc/udc-core.c2
-rw-r--r--drivers/usb/host/Kconfig22
-rw-r--r--drivers/usb/host/Makefile3
-rw-r--r--drivers/usb/host/ehci-atmel.c102
-rw-r--r--drivers/usb/host/ehci-fsl.c1
-rw-r--r--drivers/usb/host/ehci-grlib.c1
-rw-r--r--drivers/usb/host/ehci-hcd.c6
-rw-r--r--drivers/usb/host/ehci-hub.c8
-rw-r--r--drivers/usb/host/ehci-pci.c27
-rw-r--r--drivers/usb/host/ehci-platform.c92
-rw-r--r--drivers/usb/host/ehci-pmcmsp.c1
-rw-r--r--drivers/usb/host/ehci-ppc-of.c1
-rw-r--r--drivers/usb/host/ehci-sead3.c1
-rw-r--r--drivers/usb/host/ehci-sh.c1
-rw-r--r--drivers/usb/host/ehci-tilegx.c1
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c1
-rw-r--r--drivers/usb/host/ehci.h2
-rw-r--r--drivers/usb/host/fhci-hub.c9
-rw-r--r--drivers/usb/host/fotg210-hcd.c4
-rw-r--r--drivers/usb/host/fusbh200-hcd.c4
-rw-r--r--drivers/usb/host/imx21-hcd.c5
-rw-r--r--drivers/usb/host/isp116x-hcd.c5
-rw-r--r--drivers/usb/host/isp1362-hcd.c8
-rw-r--r--drivers/usb/host/isp1760-hcd.h208
-rw-r--r--drivers/usb/host/isp1760-if.c477
-rw-r--r--drivers/usb/host/max3421-hcd.c6
-rw-r--r--drivers/usb/host/ohci-at91.c132
-rw-r--r--drivers/usb/host/ohci-da8xx.c1
-rw-r--r--drivers/usb/host/ohci-hub.c10
-rw-r--r--drivers/usb/host/ohci-jz4740.c1
-rw-r--r--drivers/usb/host/ohci-platform.c83
-rw-r--r--drivers/usb/host/ohci-ppc-of.c1
-rw-r--r--drivers/usb/host/ohci-s3c2410.c6
-rw-r--r--drivers/usb/host/ohci-sm501.c1
-rw-r--r--drivers/usb/host/ohci-tilegx.c1
-rw-r--r--drivers/usb/host/ohci-tmio.c1
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c10
-rw-r--r--drivers/usb/host/pci-quirks.c46
-rw-r--r--drivers/usb/host/r8a66597-hcd.c13
-rw-r--r--drivers/usb/host/sl811-hcd.c10
-rw-r--r--drivers/usb/host/u132-hcd.c10
-rw-r--r--drivers/usb/host/uhci-grlib.c1
-rw-r--r--drivers/usb/host/uhci-hub.c5
-rw-r--r--drivers/usb/host/uhci-platform.c1
-rw-r--r--drivers/usb/host/xhci-dbg.c2
-rw-r--r--drivers/usb/host/xhci-mem.c31
-rw-r--r--drivers/usb/host/xhci-ring.c12
-rw-r--r--drivers/usb/host/xhci.c78
-rw-r--r--drivers/usb/host/xhci.h12
-rw-r--r--drivers/usb/image/microtek.c4
-rw-r--r--drivers/usb/isp1760/Kconfig59
-rw-r--r--drivers/usb/isp1760/Makefile5
-rw-r--r--drivers/usb/isp1760/isp1760-core.c177
-rw-r--r--drivers/usb/isp1760/isp1760-core.h68
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.c (renamed from drivers/usb/host/isp1760-hcd.c)291
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.h102
-rw-r--r--drivers/usb/isp1760/isp1760-if.c309
-rw-r--r--drivers/usb/isp1760/isp1760-regs.h230
-rw-r--r--drivers/usb/isp1760/isp1760-udc.c1498
-rw-r--r--drivers/usb/isp1760/isp1760-udc.h106
-rw-r--r--drivers/usb/misc/uss720.c12
-rw-r--r--drivers/usb/musb/Kconfig7
-rw-r--r--drivers/usb/musb/blackfin.c2
-rw-r--r--drivers/usb/musb/musb_core.c7
-rw-r--r--drivers/usb/musb/musb_cppi41.c24
-rw-r--r--drivers/usb/musb/musb_debugfs.c2
-rw-r--r--drivers/usb/musb/musb_gadget.c4
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c2
-rw-r--r--drivers/usb/musb/musb_virthub.c7
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c35
-rw-r--r--drivers/usb/phy/phy-generic.c150
-rw-r--r--drivers/usb/phy/phy-generic.h10
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c86
-rw-r--r--drivers/usb/renesas_usbhs/common.c25
-rw-r--r--drivers/usb/renesas_usbhs/common.h3
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c25
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c2
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c3
-rw-r--r--drivers/usb/serial/cp210x.c1
-rw-r--r--drivers/usb/serial/mos7840.c60
-rw-r--r--drivers/usb/serial/option.c60
-rw-r--r--drivers/usb/usbip/vhci_hcd.c3
-rw-r--r--drivers/usb/wusbcore/rh.c4
-rw-r--r--drivers/uwb/lc-dev.c7
157 files changed, 8110 insertions, 2902 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 26a7623e551e..2962de205ba7 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -239,6 +239,13 @@ config PHY_QCOM_IPQ806X_SATA
depends on OF
select GENERIC_PHY
+config PHY_ROCKCHIP_USB
+ tristate "Rockchip USB2 PHY Driver"
+ depends on ARCH_ROCKCHIP && OF
+ select GENERIC_PHY
+ help
+ Enable this to support the Rockchip USB 2.0 PHY.
+
config PHY_ST_SPEAR1310_MIPHY
tristate "ST SPEAR1310-MIPHY driver"
select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index cfbb72064516..f080e1bb2a74 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -28,6 +28,7 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o
obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
+obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c
index ac7d99d01cb3..7c99ca256f05 100644
--- a/drivers/phy/phy-armada375-usb2.c
+++ b/drivers/phy/phy-armada375-usb2.c
@@ -118,8 +118,8 @@ static int armada375_usb_phy_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
- if (!usb_cluster_base)
- return -ENOMEM;
+ if (IS_ERR(usb_cluster_base))
+ return PTR_ERR(usb_cluster_base);
phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops);
if (IS_ERR(phy)) {
diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c
index 943e0f88a120..f017b2f2a54e 100644
--- a/drivers/phy/phy-exynos-mipi-video.c
+++ b/drivers/phy/phy-exynos-mipi-video.c
@@ -12,19 +12,18 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mfd/syscon/exynos4-pmu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/spinlock.h>
+#include <linux/mfd/syscon.h>
-/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
+/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */
#define EXYNOS_MIPI_PHY_CONTROL(n) ((n) * 4)
-#define EXYNOS_MIPI_PHY_ENABLE (1 << 0)
-#define EXYNOS_MIPI_PHY_SRESETN (1 << 1)
-#define EXYNOS_MIPI_PHY_MRESETN (1 << 2)
-#define EXYNOS_MIPI_PHY_RESET_MASK (3 << 1)
enum exynos_mipi_phy_id {
EXYNOS_MIPI_PHY_ID_CSIS0,
@@ -38,43 +37,62 @@ enum exynos_mipi_phy_id {
((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
struct exynos_mipi_video_phy {
- spinlock_t slock;
struct video_phy_desc {
struct phy *phy;
unsigned int index;
} phys[EXYNOS_MIPI_PHYS_NUM];
+ spinlock_t slock;
void __iomem *regs;
+ struct mutex mutex;
+ struct regmap *regmap;
};
static int __set_phy_state(struct exynos_mipi_video_phy *state,
enum exynos_mipi_phy_id id, unsigned int on)
{
+ const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2);
void __iomem *addr;
- u32 reg, reset;
-
- addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+ u32 val, reset;
if (is_mipi_dsim_phy_id(id))
- reset = EXYNOS_MIPI_PHY_MRESETN;
- else
- reset = EXYNOS_MIPI_PHY_SRESETN;
-
- spin_lock(&state->slock);
- reg = readl(addr);
- if (on)
- reg |= reset;
+ reset = EXYNOS4_MIPI_PHY_MRESETN;
else
- reg &= ~reset;
- writel(reg, addr);
-
- /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
- if (on)
- reg |= EXYNOS_MIPI_PHY_ENABLE;
- else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
- reg &= ~EXYNOS_MIPI_PHY_ENABLE;
+ reset = EXYNOS4_MIPI_PHY_SRESETN;
+
+ if (state->regmap) {
+ mutex_lock(&state->mutex);
+ regmap_read(state->regmap, offset, &val);
+ if (on)
+ val |= reset;
+ else
+ val &= ~reset;
+ regmap_write(state->regmap, offset, val);
+ if (on)
+ val |= EXYNOS4_MIPI_PHY_ENABLE;
+ else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
+ val &= ~EXYNOS4_MIPI_PHY_ENABLE;
+ regmap_write(state->regmap, offset, val);
+ mutex_unlock(&state->mutex);
+ } else {
+ addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+
+ spin_lock(&state->slock);
+ val = readl(addr);
+ if (on)
+ val |= reset;
+ else
+ val &= ~reset;
+ writel(val, addr);
+ /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */
+ if (on)
+ val |= EXYNOS4_MIPI_PHY_ENABLE;
+ else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
+ val &= ~EXYNOS4_MIPI_PHY_ENABLE;
+
+ writel(val, addr);
+ spin_unlock(&state->slock);
+ }
- writel(reg, addr);
- spin_unlock(&state->slock);
return 0;
}
@@ -118,7 +136,6 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
{
struct exynos_mipi_video_phy *state;
struct device *dev = &pdev->dev;
- struct resource *res;
struct phy_provider *phy_provider;
unsigned int i;
@@ -126,14 +143,22 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
if (!state)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
+ if (IS_ERR(state->regmap)) {
+ struct resource *res;
- state->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(state->regs))
- return PTR_ERR(state->regs);
+ dev_info(dev, "regmap lookup failed: %ld\n",
+ PTR_ERR(state->regmap));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ state->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
+ }
dev_set_drvdata(dev, state);
spin_lock_init(&state->slock);
+ mutex_init(&state->mutex);
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
struct phy *phy = devm_phy_create(dev, NULL,
diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c
index 27fa62ce6136..9b2848e6115d 100644
--- a/drivers/phy/phy-miphy28lp.c
+++ b/drivers/phy/phy-miphy28lp.c
@@ -194,6 +194,14 @@
#define MIPHY_SATA_BANK_NB 3
#define MIPHY_PCIE_BANK_NB 2
+enum {
+ SYSCFG_CTRL,
+ SYSCFG_STATUS,
+ SYSCFG_PCI,
+ SYSCFG_SATA,
+ SYSCFG_REG_MAX,
+};
+
struct miphy28lp_phy {
struct phy *phy;
struct miphy28lp_dev *phydev;
@@ -211,10 +219,7 @@ struct miphy28lp_phy {
u32 sata_gen;
/* Sysconfig registers offsets needed to configure the device */
- u32 syscfg_miphy_ctrl;
- u32 syscfg_miphy_status;
- u32 syscfg_pci;
- u32 syscfg_sata;
+ u32 syscfg_reg[SYSCFG_REG_MAX];
u8 type;
};
@@ -834,12 +839,12 @@ static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy)
if (!miphy_phy->osc_rdy)
return 0;
- if (!miphy_phy->syscfg_miphy_status)
+ if (!miphy_phy->syscfg_reg[SYSCFG_STATUS])
return -EINVAL;
do {
- regmap_read(miphy_dev->regmap, miphy_phy->syscfg_miphy_status,
- &val);
+ regmap_read(miphy_dev->regmap,
+ miphy_phy->syscfg_reg[SYSCFG_STATUS], &val);
if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY)
cpu_relax();
@@ -888,7 +893,7 @@ static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val)
int err;
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
- if (!miphy_phy->syscfg_miphy_ctrl)
+ if (!miphy_phy->syscfg_reg[SYSCFG_CTRL])
return -EINVAL;
err = reset_control_assert(miphy_phy->miphy_rst);
@@ -900,7 +905,8 @@ static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val)
if (miphy_phy->osc_force_ext)
miphy_val |= MIPHY_OSC_FORCE_EXT;
- regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_miphy_ctrl,
+ regmap_update_bits(miphy_dev->regmap,
+ miphy_phy->syscfg_reg[SYSCFG_CTRL],
MIPHY_CTRL_MASK, miphy_val);
err = reset_control_deassert(miphy_phy->miphy_rst);
@@ -917,8 +923,9 @@ static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy)
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
int err, sata_conf = SATA_CTRL_SELECT_SATA;
- if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci)
- || (!miphy_phy->base))
+ if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) ||
+ (!miphy_phy->syscfg_reg[SYSCFG_PCI]) ||
+ (!miphy_phy->base))
return -EINVAL;
dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base);
@@ -926,10 +933,11 @@ static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy)
/* Configure the glue-logic */
sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE);
- regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata,
+ regmap_update_bits(miphy_dev->regmap,
+ miphy_phy->syscfg_reg[SYSCFG_SATA],
SATA_CTRL_MASK, sata_conf);
- regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci,
+ regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI],
PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
/* MiPHY path and clocking init */
@@ -951,17 +959,19 @@ static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy)
struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
int err;
- if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci)
+ if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) ||
+ (!miphy_phy->syscfg_reg[SYSCFG_PCI])
|| (!miphy_phy->base) || (!miphy_phy->pipebase))
return -EINVAL;
dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base);
/* Configure the glue-logic */
- regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata,
+ regmap_update_bits(miphy_dev->regmap,
+ miphy_phy->syscfg_reg[SYSCFG_SATA],
SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
- regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci,
+ regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI],
PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL);
/* MiPHY path and clocking init */
@@ -1156,7 +1166,8 @@ static int miphy28lp_probe_resets(struct device_node *node,
static int miphy28lp_of_probe(struct device_node *np,
struct miphy28lp_phy *miphy_phy)
{
- struct resource res;
+ int i;
+ u32 ctrlreg;
miphy_phy->osc_force_ext =
of_property_read_bool(np, "st,osc-force-ext");
@@ -1175,18 +1186,10 @@ static int miphy28lp_of_probe(struct device_node *np,
if (!miphy_phy->sata_gen)
miphy_phy->sata_gen = SATA_GEN1;
- if (!miphy28lp_get_resource_byname(np, "miphy-ctrl-glue", &res))
- miphy_phy->syscfg_miphy_ctrl = res.start;
-
- if (!miphy28lp_get_resource_byname(np, "miphy-status-glue", &res))
- miphy_phy->syscfg_miphy_status = res.start;
-
- if (!miphy28lp_get_resource_byname(np, "pcie-glue", &res))
- miphy_phy->syscfg_pci = res.start;
-
- if (!miphy28lp_get_resource_byname(np, "sata-glue", &res))
- miphy_phy->syscfg_sata = res.start;
-
+ for (i = 0; i < SYSCFG_REG_MAX; i++) {
+ if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg))
+ miphy_phy->syscfg_reg[i] = ctrlreg;
+ }
return 0;
}
diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c
new file mode 100644
index 000000000000..22011c3b6a4b
--- /dev/null
+++ b/drivers/phy/phy-rockchip-usb.c
@@ -0,0 +1,158 @@
+/*
+ * Rockchip usb PHY driver
+ *
+ * Copyright (C) 2014 Yunzhi Li <lyz@rock-chips.com>
+ * Copyright (C) 2014 ROCKCHIP, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(13 + 16) set to 1 the BIT(13) can be written.
+ */
+#define SIDDQ_WRITE_ENA BIT(29)
+#define SIDDQ_ON BIT(13)
+#define SIDDQ_OFF (0 << 13)
+
+struct rockchip_usb_phy {
+ unsigned int reg_offset;
+ struct regmap *reg_base;
+ struct clk *clk;
+ struct phy *phy;
+};
+
+static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
+ bool siddq)
+{
+ return regmap_write(phy->reg_base, phy->reg_offset,
+ SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
+}
+
+static int rockchip_usb_phy_power_off(struct phy *_phy)
+{
+ struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+ int ret = 0;
+
+ /* Power down usb phy analog blocks by set siddq 1 */
+ ret = rockchip_usb_phy_power(phy, 1);
+ if (ret)
+ return ret;
+
+ clk_disable_unprepare(phy->clk);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rockchip_usb_phy_power_on(struct phy *_phy)
+{
+ struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+ int ret = 0;
+
+ ret = clk_prepare_enable(phy->clk);
+ if (ret)
+ return ret;
+
+ /* Power up usb phy analog blocks by set siddq 0 */
+ ret = rockchip_usb_phy_power(phy, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct phy_ops ops = {
+ .power_on = rockchip_usb_phy_power_on,
+ .power_off = rockchip_usb_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int rockchip_usb_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rockchip_usb_phy *rk_phy;
+ struct phy_provider *phy_provider;
+ struct device_node *child;
+ struct regmap *grf;
+ unsigned int reg_offset;
+
+ grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(&pdev->dev, "Missing rockchip,grf property\n");
+ return PTR_ERR(grf);
+ }
+
+ for_each_available_child_of_node(dev->of_node, child) {
+ rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
+ if (!rk_phy)
+ return -ENOMEM;
+
+ if (of_property_read_u32(child, "reg", &reg_offset)) {
+ dev_err(dev, "missing reg property in node %s\n",
+ child->name);
+ return -EINVAL;
+ }
+
+ rk_phy->reg_offset = reg_offset;
+ rk_phy->reg_base = grf;
+
+ rk_phy->clk = of_clk_get_by_name(child, "phyclk");
+ if (IS_ERR(rk_phy->clk))
+ rk_phy->clk = NULL;
+
+ rk_phy->phy = devm_phy_create(dev, child, &ops);
+ if (IS_ERR(rk_phy->phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(rk_phy->phy);
+ }
+ phy_set_drvdata(rk_phy->phy, rk_phy);
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
+ { .compatible = "rockchip,rk3288-usb-phy" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
+
+static struct platform_driver rockchip_usb_driver = {
+ .probe = rockchip_usb_phy_probe,
+ .driver = {
+ .name = "rockchip-usb-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = rockchip_usb_phy_dt_ids,
+ },
+};
+
+module_platform_driver(rockchip_usb_driver);
+
+MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c
index 465de2c800f2..95c88f929f27 100644
--- a/drivers/phy/phy-ti-pipe3.c
+++ b/drivers/phy/phy-ti-pipe3.c
@@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/phy/omap_control_phy.h>
#include <linux/of_platform.h>
+#include <linux/spinlock.h>
#define PLL_STATUS 0x00000004
#define PLL_GO 0x00000008
@@ -82,6 +83,10 @@ struct ti_pipe3 {
struct clk *refclk;
struct clk *div_clk;
struct pipe3_dpll_map *dpll_map;
+ bool enabled;
+ spinlock_t lock; /* serialize clock enable/disable */
+ /* the below flag is needed specifically for SATA */
+ bool refclk_enabled;
};
static struct pipe3_dpll_map dpll_map_usb[] = {
@@ -307,6 +312,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
return -ENOMEM;
phy->dev = &pdev->dev;
+ spin_lock_init(&phy->lock);
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
match = of_match_device(of_match_ptr(ti_pipe3_id_table),
@@ -333,21 +339,24 @@ static int ti_pipe3_probe(struct platform_device *pdev)
}
}
+ phy->refclk = devm_clk_get(phy->dev, "refclk");
+ if (IS_ERR(phy->refclk)) {
+ dev_err(&pdev->dev, "unable to get refclk\n");
+ /* older DTBs have missing refclk in SATA PHY
+ * so don't bail out in case of SATA PHY.
+ */
+ if (!of_device_is_compatible(node, "ti,phy-pipe3-sata"))
+ return PTR_ERR(phy->refclk);
+ }
+
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get wkupclk\n");
return PTR_ERR(phy->wkupclk);
}
-
- phy->refclk = devm_clk_get(phy->dev, "refclk");
- if (IS_ERR(phy->refclk)) {
- dev_err(&pdev->dev, "unable to get refclk\n");
- return PTR_ERR(phy->refclk);
- }
} else {
phy->wkupclk = ERR_PTR(-ENODEV);
- phy->refclk = ERR_PTR(-ENODEV);
}
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
@@ -426,33 +435,42 @@ static int ti_pipe3_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-
-static int ti_pipe3_runtime_suspend(struct device *dev)
+static int ti_pipe3_enable_refclk(struct ti_pipe3 *phy)
{
- struct ti_pipe3 *phy = dev_get_drvdata(dev);
+ if (!IS_ERR(phy->refclk) && !phy->refclk_enabled) {
+ int ret;
- if (!IS_ERR(phy->wkupclk))
- clk_disable_unprepare(phy->wkupclk);
+ ret = clk_prepare_enable(phy->refclk);
+ if (ret) {
+ dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
+ return ret;
+ }
+ phy->refclk_enabled = true;
+ }
+
+ return 0;
+}
+
+static void ti_pipe3_disable_refclk(struct ti_pipe3 *phy)
+{
if (!IS_ERR(phy->refclk))
clk_disable_unprepare(phy->refclk);
- if (!IS_ERR(phy->div_clk))
- clk_disable_unprepare(phy->div_clk);
- return 0;
+ phy->refclk_enabled = false;
}
-static int ti_pipe3_runtime_resume(struct device *dev)
+static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy)
{
- u32 ret = 0;
- struct ti_pipe3 *phy = dev_get_drvdata(dev);
+ int ret = 0;
+ unsigned long flags;
- if (!IS_ERR(phy->refclk)) {
- ret = clk_prepare_enable(phy->refclk);
- if (ret) {
- dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
- goto err1;
- }
- }
+ spin_lock_irqsave(&phy->lock, flags);
+ if (phy->enabled)
+ goto err1;
+
+ ret = ti_pipe3_enable_refclk(phy);
+ if (ret)
+ goto err1;
if (!IS_ERR(phy->wkupclk)) {
ret = clk_prepare_enable(phy->wkupclk);
@@ -469,6 +487,9 @@ static int ti_pipe3_runtime_resume(struct device *dev)
goto err3;
}
}
+
+ phy->enabled = true;
+ spin_unlock_irqrestore(&phy->lock, flags);
return 0;
err3:
@@ -479,20 +500,80 @@ err2:
if (!IS_ERR(phy->refclk))
clk_disable_unprepare(phy->refclk);
+ ti_pipe3_disable_refclk(phy);
err1:
+ spin_unlock_irqrestore(&phy->lock, flags);
return ret;
}
+static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&phy->lock, flags);
+ if (!phy->enabled) {
+ spin_unlock_irqrestore(&phy->lock, flags);
+ return;
+ }
+
+ if (!IS_ERR(phy->wkupclk))
+ clk_disable_unprepare(phy->wkupclk);
+ /* Don't disable refclk for SATA PHY due to Errata i783 */
+ if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
+ ti_pipe3_disable_refclk(phy);
+ if (!IS_ERR(phy->div_clk))
+ clk_disable_unprepare(phy->div_clk);
+ phy->enabled = false;
+ spin_unlock_irqrestore(&phy->lock, flags);
+}
+
+static int ti_pipe3_runtime_suspend(struct device *dev)
+{
+ struct ti_pipe3 *phy = dev_get_drvdata(dev);
+
+ ti_pipe3_disable_clocks(phy);
+ return 0;
+}
+
+static int ti_pipe3_runtime_resume(struct device *dev)
+{
+ struct ti_pipe3 *phy = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = ti_pipe3_enable_clocks(phy);
+ return ret;
+}
+
+static int ti_pipe3_suspend(struct device *dev)
+{
+ struct ti_pipe3 *phy = dev_get_drvdata(dev);
+
+ ti_pipe3_disable_clocks(phy);
+ return 0;
+}
+
+static int ti_pipe3_resume(struct device *dev)
+{
+ struct ti_pipe3 *phy = dev_get_drvdata(dev);
+ int ret;
+
+ ret = ti_pipe3_enable_clocks(phy);
+ if (ret)
+ return ret;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ return 0;
+}
+#endif
+
static const struct dev_pm_ops ti_pipe3_pm_ops = {
SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
ti_pipe3_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(ti_pipe3_suspend, ti_pipe3_resume)
};
-#define DEV_PM_OPS (&ti_pipe3_pm_ops)
-#else
-#define DEV_PM_OPS NULL
-#endif
-
#ifdef CONFIG_OF
static const struct of_device_id ti_pipe3_id_table[] = {
{
@@ -520,7 +601,7 @@ static struct platform_driver ti_pipe3_driver = {
.remove = ti_pipe3_remove,
.driver = {
.name = "ti-pipe3",
- .pm = DEV_PM_OPS,
+ .pm = &ti_pipe3_pm_ops,
.of_match_table = of_match_ptr(ti_pipe3_id_table),
},
};
diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c
index eb178fcb7954..bd70ea05708b 100644
--- a/drivers/staging/emxx_udc/emxx_udc.c
+++ b/drivers/staging/emxx_udc/emxx_udc.c
@@ -1608,7 +1608,7 @@ static int std_req_get_status(struct nbu2ss_udc *udc)
switch (recipient) {
case USB_RECIP_DEVICE:
if (udc->ctrl.wIndex == 0x0000) {
- if (udc->self_powered)
+ if (udc->gadget.is_selfpowered)
status_data |= (1 << USB_DEVICE_SELF_POWERED);
if (udc->remote_wakeup)
@@ -3117,7 +3117,7 @@ static int nbu2ss_gad_wakeup(struct usb_gadget *pgadget)
static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget,
int is_selfpowered)
{
- struct nbu2ss_udc *udc;
+ struct nbu2ss_udc *udc;
unsigned long flags;
/* INFO("=== %s()\n", __func__); */
@@ -3130,7 +3130,7 @@ static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget,
udc = container_of(pgadget, struct nbu2ss_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
- udc->self_powered = (is_selfpowered != 0);
+ pgadget->is_selfpowered = (is_selfpowered != 0);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
@@ -3308,7 +3308,7 @@ static int __init nbu2ss_drv_contest_init(
spin_lock_init(&udc->lock);
udc->dev = &pdev->dev;
- udc->self_powered = 1;
+ udc->gadget.is_selfpowered = 1;
udc->devstate = USB_STATE_NOTATTACHED;
udc->pdev = pdev;
udc->mA = 0;
diff --git a/drivers/staging/emxx_udc/emxx_udc.h b/drivers/staging/emxx_udc/emxx_udc.h
index ee1b80d705fa..202e2dc72bba 100644
--- a/drivers/staging/emxx_udc/emxx_udc.h
+++ b/drivers/staging/emxx_udc/emxx_udc.h
@@ -624,7 +624,6 @@ struct nbu2ss_udc {
unsigned linux_suspended:1;
unsigned linux_resume:1;
unsigned usb_suspended:1;
- unsigned self_powered:1;
unsigned remote_wakeup:1;
unsigned udc_enabled:1;
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index ae481c37a208..8ed451dd651e 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -104,6 +104,8 @@ source "drivers/usb/dwc2/Kconfig"
source "drivers/usb/chipidea/Kconfig"
+source "drivers/usb/isp1760/Kconfig"
+
comment "USB port drivers"
if USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index d7be71778059..2f1e2aa42b44 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB) += core/
obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_DWC2) += dwc2/
+obj-$(CONFIG_USB_ISP1760) += isp1760/
obj-$(CONFIG_USB_MON) += mon/
@@ -23,7 +24,6 @@ obj-$(CONFIG_USB_ISP1362_HCD) += host/
obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/
obj-$(CONFIG_USB_HWA_HCD) += host/
-obj-$(CONFIG_USB_ISP1760_HCD) += host/
obj-$(CONFIG_USB_IMX21_HCD) += host/
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/
obj-$(CONFIG_USB_FUSBH200_HCD) += host/
diff --git a/drivers/usb/chipidea/ci_hdrc_pci.c b/drivers/usb/chipidea/ci_hdrc_pci.c
index 241ae3444fde..4df669437211 100644
--- a/drivers/usb/chipidea/ci_hdrc_pci.c
+++ b/drivers/usb/chipidea/ci_hdrc_pci.c
@@ -111,6 +111,9 @@ static void ci_hdrc_pci_remove(struct pci_dev *pdev)
* PCI device structure
*
* Check "pci.h" for details
+ *
+ * Note: ehci-pci driver may try to probe the device first. You have to add an
+ * ID to the bypass_pci_id_table in ehci-pci driver to prevent this.
*/
static const struct pci_device_id ci_hdrc_pci_id_table[] = {
{
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 4fe18ce3bd5a..ff451048c1ac 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -819,8 +819,8 @@ __acquires(hwep->lock)
}
if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
- /* Assume that device is bus powered for now. */
- *(u16 *)req->buf = ci->remote_wakeup << 1;
+ *(u16 *)req->buf = (ci->remote_wakeup << 1) |
+ ci->gadget.is_selfpowered;
} else if ((setup->bRequestType & USB_RECIP_MASK) \
== USB_RECIP_ENDPOINT) {
dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ?
@@ -1520,6 +1520,19 @@ static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
return -ENOTSUPP;
}
+static int ci_udc_selfpowered(struct usb_gadget *_gadget, int is_on)
+{
+ struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
+ struct ci_hw_ep *hwep = ci->ep0in;
+ unsigned long flags;
+
+ spin_lock_irqsave(hwep->lock, flags);
+ _gadget->is_selfpowered = (is_on != 0);
+ spin_unlock_irqrestore(hwep->lock, flags);
+
+ return 0;
+}
+
/* Change Data+ pullup status
* this func is used by usb_gadget_connect/disconnet
*/
@@ -1549,6 +1562,7 @@ static int ci_udc_stop(struct usb_gadget *gadget);
static const struct usb_gadget_ops usb_gadget_ops = {
.vbus_session = ci_udc_vbus_session,
.wakeup = ci_udc_wakeup,
+ .set_selfpowered = ci_udc_selfpowered,
.pullup = ci_udc_pullup,
.vbus_draw = ci_udc_vbus_draw,
.udc_start = ci_udc_start,
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 546a17e8ad5b..e78720b59d67 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1091,6 +1091,7 @@ static int acm_probe(struct usb_interface *intf,
unsigned long quirks;
int num_rx_buf;
int i;
+ unsigned int elength = 0;
int combined_interfaces = 0;
struct device *tty_dev;
int rv = -ENOMEM;
@@ -1136,9 +1137,12 @@ static int acm_probe(struct usb_interface *intf,
dev_err(&intf->dev, "skipping garbage\n");
goto next_desc;
}
+ elength = buffer[0];
switch (buffer[2]) {
case USB_CDC_UNION_TYPE: /* we've found it */
+ if (elength < sizeof(struct usb_cdc_union_desc))
+ goto next_desc;
if (union_header) {
dev_err(&intf->dev, "More than one "
"union descriptor, skipping ...\n");
@@ -1147,29 +1151,36 @@ static int acm_probe(struct usb_interface *intf,
union_header = (struct usb_cdc_union_desc *)buffer;
break;
case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
+ if (elength < sizeof(struct usb_cdc_country_functional_desc))
+ goto next_desc;
cfd = (struct usb_cdc_country_functional_desc *)buffer;
break;
case USB_CDC_HEADER_TYPE: /* maybe check version */
break; /* for now we ignore it */
case USB_CDC_ACM_TYPE:
+ if (elength < 4)
+ goto next_desc;
ac_management_function = buffer[3];
break;
case USB_CDC_CALL_MANAGEMENT_TYPE:
+ if (elength < 5)
+ goto next_desc;
call_management_function = buffer[3];
call_interface_num = buffer[4];
break;
default:
- /* there are LOTS more CDC descriptors that
+ /*
+ * there are LOTS more CDC descriptors that
* could legitimately be found here.
*/
dev_dbg(&intf->dev, "Ignoring descriptor: "
- "type %02x, length %d\n",
- buffer[2], buffer[0]);
+ "type %02x, length %ud\n",
+ buffer[2], elength);
break;
}
next_desc:
- buflen -= buffer[0];
- buffer += buffer[0];
+ buflen -= elength;
+ buffer += elength;
}
if (!union_header) {
@@ -1287,10 +1298,8 @@ made_compressed_probe:
dev_dbg(&intf->dev, "interfaces are valid\n");
acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
- if (acm == NULL) {
- dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
+ if (acm == NULL)
goto alloc_fail;
- }
minor = acm_alloc_minor(acm);
if (minor == ACM_TTY_MINORS) {
@@ -1329,42 +1338,32 @@ made_compressed_probe:
acm->quirks = quirks;
buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
- if (!buf) {
- dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
+ if (!buf)
goto alloc_fail2;
- }
acm->ctrl_buffer = buf;
- if (acm_write_buffers_alloc(acm) < 0) {
- dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
+ if (acm_write_buffers_alloc(acm) < 0)
goto alloc_fail4;
- }
acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
- if (!acm->ctrlurb) {
- dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
+ if (!acm->ctrlurb)
goto alloc_fail5;
- }
+
for (i = 0; i < num_rx_buf; i++) {
struct acm_rb *rb = &(acm->read_buffers[i]);
struct urb *urb;
rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
&rb->dma);
- if (!rb->base) {
- dev_err(&intf->dev, "out of memory "
- "(read bufs usb_alloc_coherent)\n");
+ if (!rb->base)
goto alloc_fail6;
- }
rb->index = i;
rb->instance = acm;
urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- dev_err(&intf->dev,
- "out of memory (read urbs usb_alloc_urb)\n");
+ if (!urb)
goto alloc_fail6;
- }
+
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
urb->transfer_dma = rb->dma;
if (acm->is_int_ep) {
@@ -1389,11 +1388,8 @@ made_compressed_probe:
struct acm_wb *snd = &(acm->wb[i]);
snd->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (snd->urb == NULL) {
- dev_err(&intf->dev,
- "out of memory (write urbs usb_alloc_urb)\n");
+ if (snd->urb == NULL)
goto alloc_fail7;
- }
if (usb_endpoint_xfer_int(epwrite))
usb_fill_int_urb(snd->urb, usb_dev,
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index 684ef70dc09d..506b969ea7fd 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -22,17 +22,25 @@
*/
/* FIXME tune these based on pool statistics ... */
-static const size_t pool_max[HCD_BUFFER_POOLS] = {
- /* platforms without dma-friendly caches might need to
- * prevent cacheline sharing...
- */
- 32,
- 128,
- 512,
- PAGE_SIZE / 2
- /* bigger --> allocate pages */
+static size_t pool_max[HCD_BUFFER_POOLS] = {
+ 32, 128, 512, 2048,
};
+void __init usb_init_pool_max(void)
+{
+ /*
+ * The pool_max values must never be smaller than
+ * ARCH_KMALLOC_MINALIGN.
+ */
+ if (ARCH_KMALLOC_MINALIGN <= 32)
+ ; /* Original value is okay */
+ else if (ARCH_KMALLOC_MINALIGN <= 64)
+ pool_max[0] = 64;
+ else if (ARCH_KMALLOC_MINALIGN <= 128)
+ pool_max[0] = 0; /* Don't use this pool */
+ else
+ BUILD_BUG(); /* We don't allow this */
+}
/* SETUP primitives */
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 0b59731c3021..66abdbcfbfa5 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1689,7 +1689,7 @@ static struct async *reap_as(struct usb_dev_state *ps)
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
as = async_getcompleted(ps);
- if (as)
+ if (as || !connected(ps))
break;
if (signal_pending(current))
break;
@@ -1712,7 +1712,7 @@ static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
}
if (signal_pending(current))
return -EINTR;
- return -EIO;
+ return -ENODEV;
}
static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
@@ -1721,10 +1721,11 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
struct async *as;
as = async_getcompleted(ps);
- retval = -EAGAIN;
if (as) {
retval = processcompl(as, (void __user * __user *)arg);
free_async(as);
+ } else {
+ retval = (connected(ps) ? -EAGAIN : -ENODEV);
}
return retval;
}
@@ -1854,7 +1855,7 @@ static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
}
if (signal_pending(current))
return -EINTR;
- return -EIO;
+ return -ENODEV;
}
static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg)
@@ -1862,11 +1863,12 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar
int retval;
struct async *as;
- retval = -EAGAIN;
as = async_getcompleted(ps);
if (as) {
retval = processcompl_compat(as, (void __user * __user *)arg);
free_async(as);
+ } else {
+ retval = (connected(ps) ? -EAGAIN : -ENODEV);
}
return retval;
}
@@ -2038,7 +2040,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
{
__u32 caps;
- caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM;
+ caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
+ USBDEVFS_CAP_REAP_AFTER_DISCONNECT;
if (!ps->dev->bus->no_stop_on_short)
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
if (ps->dev->bus->sg_tablesize)
@@ -2138,6 +2141,32 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
return -EPERM;
usb_lock_device(dev);
+
+ /* Reap operations are allowed even after disconnection */
+ switch (cmd) {
+ case USBDEVFS_REAPURB:
+ snoop(&dev->dev, "%s: REAPURB\n", __func__);
+ ret = proc_reapurb(ps, p);
+ goto done;
+
+ case USBDEVFS_REAPURBNDELAY:
+ snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
+ ret = proc_reapurbnonblock(ps, p);
+ goto done;
+
+#ifdef CONFIG_COMPAT
+ case USBDEVFS_REAPURB32:
+ snoop(&dev->dev, "%s: REAPURB32\n", __func__);
+ ret = proc_reapurb_compat(ps, p);
+ goto done;
+
+ case USBDEVFS_REAPURBNDELAY32:
+ snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
+ ret = proc_reapurbnonblock_compat(ps, p);
+ goto done;
+#endif
+ }
+
if (!connected(ps)) {
usb_unlock_device(dev);
return -ENODEV;
@@ -2231,16 +2260,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
inode->i_mtime = CURRENT_TIME;
break;
- case USBDEVFS_REAPURB32:
- snoop(&dev->dev, "%s: REAPURB32\n", __func__);
- ret = proc_reapurb_compat(ps, p);
- break;
-
- case USBDEVFS_REAPURBNDELAY32:
- snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
- ret = proc_reapurbnonblock_compat(ps, p);
- break;
-
case USBDEVFS_IOCTL32:
snoop(&dev->dev, "%s: IOCTL32\n", __func__);
ret = proc_ioctl_compat(ps, ptr_to_compat(p));
@@ -2252,16 +2271,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
ret = proc_unlinkurb(ps, p);
break;
- case USBDEVFS_REAPURB:
- snoop(&dev->dev, "%s: REAPURB\n", __func__);
- ret = proc_reapurb(ps, p);
- break;
-
- case USBDEVFS_REAPURBNDELAY:
- snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
- ret = proc_reapurbnonblock(ps, p);
- break;
-
case USBDEVFS_DISCSIGNAL:
snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);
ret = proc_disconnectsignal(ps, p);
@@ -2304,6 +2313,8 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
ret = proc_free_streams(ps, p);
break;
}
+
+ done:
usb_unlock_device(dev);
if (ret >= 0)
inode->i_atime = CURRENT_TIME;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 874dec31a111..818369afff63 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -275,21 +275,6 @@ static int usb_unbind_device(struct device *dev)
return 0;
}
-/*
- * Cancel any pending scheduled resets
- *
- * [see usb_queue_reset_device()]
- *
- * Called after unconfiguring / when releasing interfaces. See
- * comments in __usb_queue_reset_device() regarding
- * udev->reset_running.
- */
-static void usb_cancel_queued_reset(struct usb_interface *iface)
-{
- if (iface->reset_running == 0)
- cancel_work_sync(&iface->reset_ws);
-}
-
/* called from driver core with dev locked */
static int usb_probe_interface(struct device *dev)
{
@@ -380,7 +365,6 @@ static int usb_probe_interface(struct device *dev)
usb_set_intfdata(intf, NULL);
intf->needs_remote_wakeup = 0;
intf->condition = USB_INTERFACE_UNBOUND;
- usb_cancel_queued_reset(intf);
/* If the LPM disable succeeded, balance the ref counts. */
if (!lpm_disable_error)
@@ -425,7 +409,6 @@ static int usb_unbind_interface(struct device *dev)
usb_disable_interface(udev, intf, false);
driver->disconnect(intf);
- usb_cancel_queued_reset(intf);
/* Free streams */
for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
@@ -1797,6 +1780,18 @@ static int autosuspend_check(struct usb_device *udev)
dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
return -EOPNOTSUPP;
}
+
+ /*
+ * If the device is a direct child of the root hub and the HCD
+ * doesn't handle wakeup requests, don't allow autosuspend when
+ * wakeup is needed.
+ */
+ if (w && udev->parent == udev->bus->root_hub &&
+ bus_to_hcd(udev->bus)->cant_recv_wakeups) {
+ dev_dbg(&udev->dev, "HCD doesn't handle wakeup requests\n");
+ return -EOPNOTSUPP;
+ }
+
udev->do_remote_wakeup = w;
return 0;
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 11cee55ae397..45a915ccd71c 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1618,6 +1618,7 @@ static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)
int usb_hcd_unlink_urb (struct urb *urb, int status)
{
struct usb_hcd *hcd;
+ struct usb_device *udev = urb->dev;
int retval = -EIDRM;
unsigned long flags;
@@ -1629,20 +1630,19 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
spin_lock_irqsave(&hcd_urb_unlink_lock, flags);
if (atomic_read(&urb->use_count) > 0) {
retval = 0;
- usb_get_dev(urb->dev);
+ usb_get_dev(udev);
}
spin_unlock_irqrestore(&hcd_urb_unlink_lock, flags);
if (retval == 0) {
hcd = bus_to_hcd(urb->dev->bus);
retval = unlink1(hcd, urb, status);
- usb_put_dev(urb->dev);
+ if (retval == 0)
+ retval = -EINPROGRESS;
+ else if (retval != -EIDRM && retval != -EBUSY)
+ dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n",
+ urb, retval);
+ usb_put_dev(udev);
}
-
- if (retval == 0)
- retval = -EINPROGRESS;
- else if (retval != -EIDRM && retval != -EBUSY)
- dev_dbg(&urb->dev->dev, "hcd_unlink_urb %p fail %d\n",
- urb, retval);
return retval;
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index eaffb0248de1..d7c3d5a35946 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2896,10 +2896,12 @@ static int port_is_suspended(struct usb_hub *hub, unsigned portstatus)
*/
static int check_port_resume_type(struct usb_device *udev,
struct usb_hub *hub, int port1,
- int status, unsigned portchange, unsigned portstatus)
+ int status, u16 portchange, u16 portstatus)
{
struct usb_port *port_dev = hub->ports[port1 - 1];
+ int retries = 3;
+ retry:
/* Is a warm reset needed to recover the connection? */
if (status == 0 && udev->reset_resume
&& hub_port_warm_reset_required(hub, port1, portstatus)) {
@@ -2907,10 +2909,17 @@ static int check_port_resume_type(struct usb_device *udev,
}
/* Is the device still present? */
else if (status || port_is_suspended(hub, portstatus) ||
- !port_is_power_on(hub, portstatus) ||
- !(portstatus & USB_PORT_STAT_CONNECTION)) {
+ !port_is_power_on(hub, portstatus)) {
if (status >= 0)
status = -ENODEV;
+ } else if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+ if (retries--) {
+ usleep_range(200, 300);
+ status = hub_port_status(hub, port1, &portstatus,
+ &portchange);
+ goto retry;
+ }
+ status = -ENODEV;
}
/* Can't do a normal resume if the port isn't enabled,
@@ -4643,9 +4652,13 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
test_bit(port1, hub->removed_bits)) {
- /* maybe switch power back on (e.g. root hub was reset) */
+ /*
+ * maybe switch power back on (e.g. root hub was reset)
+ * but only if the port isn't owned by someone else.
+ */
if (hub_is_port_power_switchable(hub)
- && !port_is_power_on(hub, portstatus))
+ && !port_is_power_on(hub, portstatus)
+ && !port_dev->port_owner)
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)
@@ -4870,7 +4883,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
static void port_event(struct usb_hub *hub, int port1)
__must_hold(&port_dev->status_lock)
{
- int connect_change, reset_device = 0;
+ int connect_change;
struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *udev = port_dev->child;
struct usb_device *hdev = hub->hdev;
@@ -4958,30 +4971,14 @@ static void port_event(struct usb_hub *hub, int port1)
if (hub_port_reset(hub, port1, NULL,
HUB_BH_RESET_TIME, true) < 0)
hub_port_disable(hub, port1, 1);
- } else
- reset_device = 1;
- }
-
- /*
- * On disconnect USB3 protocol ports transit from U0 to
- * SS.Inactive to Rx.Detect. If this happens a warm-
- * reset is not needed, but a (re)connect may happen
- * before hub_wq runs and sees the disconnect, and the
- * device may be an unknown state.
- *
- * If the port went through SS.Inactive without hub_wq
- * seeing it the C_LINK_STATE change flag will be set,
- * and we reset the dev to put it in a known state.
- */
- if (reset_device || (udev && hub_is_superspeed(hub->hdev)
- && (portchange & USB_PORT_STAT_C_LINK_STATE)
- && (portstatus & USB_PORT_STAT_CONNECTION))) {
- usb_unlock_port(port_dev);
- usb_lock_device(udev);
- usb_reset_device(udev);
- usb_unlock_device(udev);
- usb_lock_port(port_dev);
- connect_change = 0;
+ } else {
+ usb_unlock_port(port_dev);
+ usb_lock_device(udev);
+ usb_reset_device(udev);
+ usb_unlock_device(udev);
+ usb_lock_port(port_dev);
+ connect_change = 0;
+ }
}
if (connect_change)
@@ -5577,26 +5574,19 @@ EXPORT_SYMBOL_GPL(usb_reset_device);
* possible; depending on how the driver attached to each interface
* handles ->pre_reset(), the second reset might happen or not.
*
- * - If a driver is unbound and it had a pending reset, the reset will
- * be cancelled.
- *
- * - This function can be called during .probe() or .disconnect()
- * times. On return from .disconnect(), any pending resets will be
- * cancelled.
- *
- * There is no no need to lock/unlock the @reset_ws as schedule_work()
- * does its own.
+ * - If the reset is delayed so long that the interface is unbound from
+ * its driver, the reset will be skipped.
*
- * NOTE: We don't do any reference count tracking because it is not
- * needed. The lifecycle of the work_struct is tied to the
- * usb_interface. Before destroying the interface we cancel the
- * work_struct, so the fact that work_struct is queued and or
- * running means the interface (and thus, the device) exist and
- * are referenced.
+ * - This function can be called during .probe(). It can also be called
+ * during .disconnect(), but doing so is pointless because the reset
+ * will not occur. If you really want to reset the device during
+ * .disconnect(), call usb_reset_device() directly -- but watch out
+ * for nested unbinding issues!
*/
void usb_queue_reset_device(struct usb_interface *iface)
{
- schedule_work(&iface->reset_ws);
+ if (schedule_work(&iface->reset_ws))
+ usb_get_intf(iface);
}
EXPORT_SYMBOL_GPL(usb_queue_reset_device);
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index f7b7713cfb2a..f368d2053da5 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1551,6 +1551,7 @@ static void usb_release_interface(struct device *dev)
altsetting_to_usb_interface_cache(intf->altsetting);
kref_put(&intfc->ref, usb_release_interface_cache);
+ usb_put_dev(interface_to_usbdev(intf));
kfree(intf);
}
@@ -1626,24 +1627,6 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
/*
* Internal function to queue a device reset
- *
- * This is initialized into the workstruct in 'struct
- * usb_device->reset_ws' that is launched by
- * message.c:usb_set_configuration() when initializing each 'struct
- * usb_interface'.
- *
- * It is safe to get the USB device without reference counts because
- * the life cycle of @iface is bound to the life cycle of @udev. Then,
- * this function will be ran only if @iface is alive (and before
- * freeing it any scheduled instances of it will have been cancelled).
- *
- * We need to set a flag (usb_dev->reset_running) because when we call
- * the reset, the interfaces might be unbound. The current interface
- * cannot try to remove the queued work as it would cause a deadlock
- * (you cannot remove your work from within your executing
- * workqueue). This flag lets it know, so that
- * usb_cancel_queued_reset() doesn't try to do it.
- *
* See usb_queue_reset_device() for more details
*/
static void __usb_queue_reset_device(struct work_struct *ws)
@@ -1655,11 +1638,10 @@ static void __usb_queue_reset_device(struct work_struct *ws)
rc = usb_lock_device_for_reset(udev, iface);
if (rc >= 0) {
- iface->reset_running = 1;
usb_reset_device(udev);
- iface->reset_running = 0;
usb_unlock_device(udev);
}
+ usb_put_intf(iface); /* Undo _get_ in usb_queue_reset_device() */
}
@@ -1854,6 +1836,7 @@ free_interfaces:
dev_set_name(&intf->dev, "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration, alt->desc.bInterfaceNumber);
+ usb_get_dev(dev);
}
kfree(new_interfaces);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 2a92b97f0144..b1fb9aef0f5b 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -1049,6 +1049,7 @@ static int __init usb_init(void)
pr_info("%s: USB support disabled\n", usbcore_name);
return 0;
}
+ usb_init_pool_max();
retval = usb_debugfs_init();
if (retval)
diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig
index b323c4c11b0a..76b9ba4dc925 100644
--- a/drivers/usb/dwc2/Kconfig
+++ b/drivers/usb/dwc2/Kconfig
@@ -23,7 +23,7 @@ choice
config USB_DWC2_HOST
bool "Host only mode"
- depends on USB
+ depends on USB=y || (USB_DWC2=m && USB)
help
The Designware USB2.0 high-speed host controller
integrated into many SoCs. Select this option if you want the
@@ -42,7 +42,7 @@ config USB_DWC2_PERIPHERAL
config USB_DWC2_DUAL_ROLE
bool "Dual Role mode"
- depends on (USB=y || USB=USB_DWC2) && (USB_GADGET=y || USB_GADGET=USB_DWC2)
+ depends on (USB=y && USB_GADGET=y) || (USB_DWC2=m && USB && USB_GADGET)
help
Select this option if you want the driver to work in a dual-role
mode. In this mode both host and gadget features are enabled, and
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 7605850b7a9c..d5197d492e21 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -462,7 +462,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
dwc2_enable_common_interrupts(hsotg);
/*
- * Do device or host intialization based on mode during PCD and
+ * Do device or host initialization based on mode during PCD and
* HCD initialization
*/
if (dwc2_is_host_mode(hsotg)) {
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 7a70a1349334..f74304b12652 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -108,7 +108,7 @@ struct s3c_hsotg_req;
* @halted: Set if the endpoint has been halted.
* @periodic: Set if this is a periodic ep, such as Interrupt
* @isochronous: Set if this is a isochronous ep
- * @sent_zlp: Set if we've sent a zero-length packet.
+ * @send_zlp: Set if we need to send a zero-length packet.
* @total_data: The total number of data bytes done.
* @fifo_size: The size of the FIFO (for periodic IN endpoints)
* @fifo_load: The amount of data loaded into the FIFO (periodic IN)
@@ -149,7 +149,7 @@ struct s3c_hsotg_ep {
unsigned int halted:1;
unsigned int periodic:1;
unsigned int isochronous:1;
- unsigned int sent_zlp:1;
+ unsigned int send_zlp:1;
char name[10];
};
@@ -158,14 +158,12 @@ struct s3c_hsotg_ep {
* struct s3c_hsotg_req - data transfer request
* @req: The USB gadget request
* @queue: The list of requests for the endpoint this is queued for.
- * @in_progress: Has already had size/packets written to core
- * @mapped: DMA buffer for this request has been mapped via dma_map_single().
+ * @saved_req_buf: variable to save req.buf when bounce buffers are used.
*/
struct s3c_hsotg_req {
struct usb_request req;
struct list_head queue;
- unsigned char in_progress;
- unsigned char mapped;
+ void *saved_req_buf;
};
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
@@ -193,6 +191,22 @@ enum dwc2_lx_state {
DWC2_L3, /* Off state */
};
+/*
+ * Gadget periodic tx fifo sizes as used by legacy driver
+ * EP0 is not included
+ */
+#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
+ 768, 0, 0, 0, 0, 0, 0, 0}
+
+/* Gadget ep0 states */
+enum dwc2_ep0_state {
+ DWC2_EP0_SETUP,
+ DWC2_EP0_DATA_IN,
+ DWC2_EP0_DATA_OUT,
+ DWC2_EP0_STATUS_IN,
+ DWC2_EP0_STATUS_OUT,
+};
+
/**
* struct dwc2_core_params - Parameters for configuring the core
*
@@ -381,7 +395,7 @@ struct dwc2_core_params {
* @power_optimized Are power optimizations enabled?
* @num_dev_ep Number of device endpoints available
* @num_dev_perio_in_ep Number of device periodic IN endpoints
- * avaialable
+ * available
* @dev_token_q_depth Device Mode IN Token Sequence Learning Queue
* Depth
* 0 to 30
@@ -434,6 +448,9 @@ struct dwc2_hw_params {
u32 snpsid;
};
+/* Size of control and EP0 buffers */
+#define DWC2_CTRL_BUFF_SIZE 8
+
/**
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
* and periodic schedules
@@ -552,14 +569,20 @@ struct dwc2_hw_params {
* @num_of_eps: Number of available EPs (excluding EP0)
* @debug_root: Root directrory for debugfs.
* @debug_file: Main status file for debugfs.
+ * @debug_testmode: Testmode status file for debugfs.
* @debug_fifo: FIFO status file for debugfs.
* @ep0_reply: Request used for ep0 reply.
* @ep0_buff: Buffer for EP0 reply data, if needed.
* @ctrl_buff: Buffer for EP0 control requests.
* @ctrl_req: Request for EP0 control packets.
- * @setup: NAK management for EP0 SETUP
+ * @ep0_state: EP0 control transfers state
+ * @test_mode: USB test mode requested by the host
* @last_rst: Time of last reset
* @eps: The endpoints being supplied to the gadget framework
+ * @g_using_dma: Indicate if dma usage is enabled
+ * @g_rx_fifo_sz: Contains rx fifo size value
+ * @g_np_g_tx_fifo_sz: Contains Non-Periodic tx fifo size value
+ * @g_tx_fifo_sz: Contains tx fifo size value per endpoints
*/
struct dwc2_hsotg {
struct device *dev;
@@ -591,6 +614,7 @@ struct dwc2_hsotg {
struct dentry *debug_root;
struct dentry *debug_file;
+ struct dentry *debug_testmode;
struct dentry *debug_fifo;
/* DWC OTG HW Release versions */
@@ -684,15 +708,21 @@ struct dwc2_hsotg {
struct usb_request *ep0_reply;
struct usb_request *ctrl_req;
- u8 ep0_buff[8];
- u8 ctrl_buff[8];
+ void *ep0_buff;
+ void *ctrl_buff;
+ enum dwc2_ep0_state ep0_state;
+ u8 test_mode;
struct usb_gadget gadget;
unsigned int enabled:1;
unsigned int connected:1;
- unsigned int setup:1;
unsigned long last_rst;
- struct s3c_hsotg_ep *eps;
+ struct s3c_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
+ struct s3c_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
+ u32 g_using_dma;
+ u32 g_rx_fifo_sz;
+ u32 g_np_g_tx_fifo_sz;
+ u32 g_tx_fifo_sz[MAX_EPS_CHANNELS];
#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
};
@@ -969,7 +999,8 @@ extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg);
extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2);
extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2);
extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
-extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2);
+extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+ bool reset);
extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
#else
@@ -981,7 +1012,8 @@ static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2)
{ return 0; }
static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
{ return 0; }
-static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2) {}
+static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+ bool reset) {}
static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
#endif
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 79242008085b..6a30887082cd 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -35,6 +35,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/phy.h>
#include <linux/platform_data/s3c-hsotg.h>
+#include <linux/uaccess.h>
#include "core.h"
#include "hw.h"
@@ -65,7 +66,16 @@ static inline void __bic32(void __iomem *ptr, u32 val)
writel(readl(ptr) & ~val, ptr);
}
-/* forward decleration of functions */
+static inline struct s3c_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg,
+ u32 ep_index, u32 dir_in)
+{
+ if (dir_in)
+ return hsotg->eps_in[ep_index];
+ else
+ return hsotg->eps_out[ep_index];
+}
+
+/* forward declaration of functions */
static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
/**
@@ -85,11 +95,11 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
* a core reset. This means we either need to fix the gadgets to take
* account of DMA alignment, or add bounce buffers (yuerk).
*
- * Until this issue is sorted out, we always return 'false'.
+ * g_using_dma is set depending on dts flag.
*/
static inline bool using_dma(struct dwc2_hsotg *hsotg)
{
- return false; /* support is not complete */
+ return hsotg->g_using_dma;
}
/**
@@ -165,15 +175,18 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
{
unsigned int ep;
unsigned int addr;
- unsigned int size;
int timeout;
u32 val;
- /* set FIFO sizes to 2048/1024 */
+ /* Reset fifo map if not correctly cleared during previous session */
+ WARN_ON(hsotg->fifo_map);
+ hsotg->fifo_map = 0;
- writel(2048, hsotg->regs + GRXFSIZ);
- writel((2048 << FIFOSIZE_STARTADDR_SHIFT) |
- (1024 << FIFOSIZE_DEPTH_SHIFT), hsotg->regs + GNPTXFSIZ);
+ /* set RX/NPTX FIFO sizes */
+ writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ);
+ writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) |
+ (hsotg->g_np_g_tx_fifo_sz << FIFOSIZE_DEPTH_SHIFT),
+ hsotg->regs + GNPTXFSIZ);
/*
* arange all the rest of the TX FIFOs, as some versions of this
@@ -183,35 +196,21 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
*/
/* start at the end of the GNPTXFSIZ, rounded up */
- addr = 2048 + 1024;
+ addr = hsotg->g_rx_fifo_sz + hsotg->g_np_g_tx_fifo_sz;
/*
- * Because we have not enough memory to have each TX FIFO of size at
- * least 3072 bytes (the maximum single packet size), we create four
- * FIFOs of lenght 1024, and four of length 3072 bytes, and assing
+ * Configure fifos sizes from provided configuration and assign
* them to endpoints dynamically according to maxpacket size value of
* given endpoint.
*/
-
- /* 256*4=1024 bytes FIFO length */
- size = 256;
- for (ep = 1; ep <= 4; ep++) {
- val = addr;
- val |= size << FIFOSIZE_DEPTH_SHIFT;
- WARN_ONCE(addr + size > hsotg->fifo_mem,
- "insufficient fifo memory");
- addr += size;
-
- writel(val, hsotg->regs + DPTXFSIZN(ep));
- }
- /* 768*4=3072 bytes FIFO length */
- size = 768;
- for (ep = 5; ep <= 8; ep++) {
+ for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) {
+ if (!hsotg->g_tx_fifo_sz[ep])
+ continue;
val = addr;
- val |= size << FIFOSIZE_DEPTH_SHIFT;
- WARN_ONCE(addr + size > hsotg->fifo_mem,
+ val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT;
+ WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem,
"insufficient fifo memory");
- addr += size;
+ addr += hsotg->g_tx_fifo_sz[ep];
writel(val, hsotg->regs + DPTXFSIZN(ep));
}
@@ -236,6 +235,7 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
dev_err(hsotg->dev,
"%s: timeout flushing fifos (GRSTCTL=%08x)\n",
__func__, val);
+ break;
}
udelay(1);
@@ -566,11 +566,6 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
length = ureq->length - ureq->actual;
dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
ureq->length, ureq->actual);
- if (0)
- dev_dbg(hsotg->dev,
- "REQ buf %p len %d dma %pad noi=%d zp=%d snok=%d\n",
- ureq->buf, length, &ureq->dma,
- ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
maxreq = get_ep_limit(hs_ep);
if (length > maxreq) {
@@ -604,14 +599,15 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
else
epsize = 0;
- if (index != 0 && ureq->zero) {
- /*
- * test for the packets being exactly right for the
- * transfer
- */
-
- if (length == (packets * hs_ep->ep.maxpacket))
- packets++;
+ /*
+ * zero length packet should be programmed on its own and should not
+ * be counted in DIEPTSIZ.PktCnt with other packets.
+ */
+ if (dir_in && ureq->zero && !continuing) {
+ /* Test if zlp is actually required. */
+ if ((ureq->length >= hs_ep->ep.maxpacket) &&
+ !(ureq->length % hs_ep->ep.maxpacket))
+ hs_ep->send_zlp = 1;
}
epsize |= DXEPTSIZ_PKTCNT(packets);
@@ -644,15 +640,12 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
ctrl |= DXEPCTL_USBACTEP;
- dev_dbg(hsotg->dev, "setup req:%d\n", hsotg->setup);
+ dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state);
/* For Setup request do not clear NAK */
- if (hsotg->setup && index == 0)
- hsotg->setup = 0;
- else
+ if (!(index == 0 && hsotg->ep0_state == DWC2_EP0_SETUP))
ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */
-
dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
writel(ctrl, hsotg->regs + epctrl_reg);
@@ -686,7 +679,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
/* check ep is enabled */
if (!(readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA))
- dev_warn(hsotg->dev,
+ dev_dbg(hsotg->dev,
"ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n",
index, readl(hsotg->regs + epctrl_reg));
@@ -733,6 +726,59 @@ dma_error:
return -EIO;
}
+static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+ void *req_buf = hs_req->req.buf;
+
+ /* If dma is not being used or buffer is aligned */
+ if (!using_dma(hsotg) || !((long)req_buf & 3))
+ return 0;
+
+ WARN_ON(hs_req->saved_req_buf);
+
+ dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__,
+ hs_ep->ep.name, req_buf, hs_req->req.length);
+
+ hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC);
+ if (!hs_req->req.buf) {
+ hs_req->req.buf = req_buf;
+ dev_err(hsotg->dev,
+ "%s: unable to allocate memory for bounce buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* Save actual buffer */
+ hs_req->saved_req_buf = req_buf;
+
+ if (hs_ep->dir_in)
+ memcpy(hs_req->req.buf, req_buf, hs_req->req.length);
+ return 0;
+}
+
+static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+ /* If dma is not being used or buffer was aligned */
+ if (!using_dma(hsotg) || !hs_req->saved_req_buf)
+ return;
+
+ dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__,
+ hs_ep->ep.name, hs_req->req.status, hs_req->req.actual);
+
+ /* Copy data from bounce buffer on successful out transfer */
+ if (!hs_ep->dir_in && !hs_req->req.status)
+ memcpy(hs_req->saved_req_buf, hs_req->req.buf,
+ hs_req->req.actual);
+
+ /* Free bounce buffer */
+ kfree(hs_req->req.buf);
+
+ hs_req->req.buf = hs_req->saved_req_buf;
+ hs_req->saved_req_buf = NULL;
+}
+
static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags)
{
@@ -740,6 +786,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hs = hs_ep->parent;
bool first;
+ int ret;
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
ep->name, req, req->length, req->buf, req->no_interrupt,
@@ -750,9 +797,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
req->actual = 0;
req->status = -EINPROGRESS;
+ ret = s3c_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req);
+ if (ret)
+ return ret;
+
/* if we're using DMA, sync the buffers as necessary */
if (using_dma(hs)) {
- int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
+ ret = s3c_hsotg_map_dma(hs, hs_ep, req);
if (ret)
return ret;
}
@@ -819,7 +870,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
u32 windex)
{
- struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F];
+ struct s3c_hsotg_ep *ep;
int dir = (windex & USB_DIR_IN) ? 1 : 0;
int idx = windex & 0x7F;
@@ -829,6 +880,8 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
if (idx > hsotg->num_of_eps)
return NULL;
+ ep = index_to_ep(hsotg, idx, dir);
+
if (idx && ep->dir_in != dir)
return NULL;
@@ -836,6 +889,32 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
}
/**
+ * s3c_hsotg_set_test_mode - Enable usb Test Modes
+ * @hsotg: The driver state.
+ * @testmode: requested usb test mode
+ * Enable usb Test Mode requested by the Host.
+ */
+static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
+{
+ int dctl = readl(hsotg->regs + DCTL);
+
+ dctl &= ~DCTL_TSTCTL_MASK;
+ switch (testmode) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ dctl |= testmode << DCTL_TSTCTL_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(dctl, hsotg->regs + DCTL);
+ return 0;
+}
+
+/**
* s3c_hsotg_send_reply - send reply to control request
* @hsotg: The device state
* @ep: Endpoint 0
@@ -864,13 +943,15 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
req->buf = hsotg->ep0_buff;
req->length = length;
- req->zero = 1; /* always do zero-length final transfer */
+ /*
+ * zero flag is for sending zlp in DATA IN stage. It has no impact on
+ * STATUS stage.
+ */
+ req->zero = 0;
req->complete = s3c_hsotg_complete_oursetup;
if (length)
memcpy(req->buf, buff, length);
- else
- ep->sent_zlp = 1;
ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
if (ret) {
@@ -889,7 +970,7 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
struct usb_ctrlrequest *ctrl)
{
- struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+ struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
struct s3c_hsotg_ep *ep;
__le16 reply;
int ret;
@@ -953,33 +1034,62 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
}
/**
- * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE
+ * s3c_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE
* @hsotg: The device state
* @ctrl: USB control request
*/
static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
struct usb_ctrlrequest *ctrl)
{
- struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+ struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
struct s3c_hsotg_req *hs_req;
bool restart;
bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
struct s3c_hsotg_ep *ep;
int ret;
bool halted;
+ u32 recip;
+ u32 wValue;
+ u32 wIndex;
dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
__func__, set ? "SET" : "CLEAR");
- if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
- ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
+ wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
+ recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ switch (wValue) {
+ case USB_DEVICE_TEST_MODE:
+ if ((wIndex & 0xff) != 0)
+ return -EINVAL;
+ if (!set)
+ return -EINVAL;
+
+ hsotg->test_mode = wIndex >> 8;
+ ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
+ if (ret) {
+ dev_err(hsotg->dev,
+ "%s: failed to send reply\n", __func__);
+ return ret;
+ }
+ break;
+ default:
+ return -ENOENT;
+ }
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ ep = ep_from_windex(hsotg, wIndex);
if (!ep) {
dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
- __func__, le16_to_cpu(ctrl->wIndex));
+ __func__, wIndex);
return -ENOENT;
}
- switch (le16_to_cpu(ctrl->wValue)) {
+ switch (wValue) {
case USB_ENDPOINT_HALT:
halted = ep->halted;
@@ -1006,16 +1116,22 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
hs_req = ep->req;
ep->req = NULL;
list_del_init(&hs_req->queue);
- usb_gadget_giveback_request(&ep->ep,
- &hs_req->req);
+ if (hs_req->req.complete) {
+ spin_unlock(&hsotg->lock);
+ usb_gadget_giveback_request(
+ &ep->ep, &hs_req->req);
+ spin_lock(&hsotg->lock);
+ }
}
/* If we have pending request, then start it */
- restart = !list_empty(&ep->queue);
- if (restart) {
- hs_req = get_ep_head(ep);
- s3c_hsotg_start_req(hsotg, ep,
- hs_req, false);
+ if (!ep->req) {
+ restart = !list_empty(&ep->queue);
+ if (restart) {
+ hs_req = get_ep_head(ep);
+ s3c_hsotg_start_req(hsotg, ep,
+ hs_req, false);
+ }
}
}
@@ -1024,9 +1140,10 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
default:
return -ENOENT;
}
- } else
- return -ENOENT; /* currently only deal with endpoint */
-
+ break;
+ default:
+ return -ENOENT;
+ }
return 1;
}
@@ -1040,7 +1157,7 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg);
*/
static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
{
- struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+ struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
u32 reg;
u32 ctrl;
@@ -1080,34 +1197,29 @@ static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg,
struct usb_ctrlrequest *ctrl)
{
- struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+ struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
int ret = 0;
u32 dcfg;
- ep0->sent_zlp = 0;
-
dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n",
ctrl->bRequest, ctrl->bRequestType,
ctrl->wValue, ctrl->wLength);
- /*
- * record the direction of the request, for later use when enquing
- * packets onto EP0.
- */
-
- ep0->dir_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0;
- dev_dbg(hsotg->dev, "ctrl: dir_in=%d\n", ep0->dir_in);
-
- /*
- * if we've no data with this request, then the last part of the
- * transaction is going to implicitly be IN.
- */
- if (ctrl->wLength == 0)
+ if (ctrl->wLength == 0) {
ep0->dir_in = 1;
+ hsotg->ep0_state = DWC2_EP0_STATUS_IN;
+ } else if (ctrl->bRequestType & USB_DIR_IN) {
+ ep0->dir_in = 1;
+ hsotg->ep0_state = DWC2_EP0_DATA_IN;
+ } else {
+ ep0->dir_in = 0;
+ hsotg->ep0_state = DWC2_EP0_DATA_OUT;
+ }
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
switch (ctrl->bRequest) {
case USB_REQ_SET_ADDRESS:
+ hsotg->connected = 1;
dcfg = readl(hsotg->regs + DCFG);
dcfg &= ~DCFG_DEVADDR_MASK;
dcfg |= (le16_to_cpu(ctrl->wValue) <<
@@ -1201,9 +1313,11 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
return;
}
- hsotg->eps[0].dir_in = 0;
+ hsotg->eps_out[0]->dir_in = 0;
+ hsotg->eps_out[0]->send_zlp = 0;
+ hsotg->ep0_state = DWC2_EP0_SETUP;
- ret = s3c_hsotg_ep_queue(&hsotg->eps[0].ep, req, GFP_ATOMIC);
+ ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC);
if (ret < 0) {
dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret);
/*
@@ -1213,6 +1327,32 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
}
}
+static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
+ struct s3c_hsotg_ep *hs_ep)
+{
+ u32 ctrl;
+ u8 index = hs_ep->index;
+ u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
+ u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
+
+ if (hs_ep->dir_in)
+ dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n",
+ index);
+ else
+ dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n",
+ index);
+
+ writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
+ DXEPTSIZ_XFERSIZE(0), hsotg->regs +
+ epsiz_reg);
+
+ ctrl = readl(hsotg->regs + epctl_reg);
+ ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */
+ ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
+ ctrl |= DXEPCTL_USBACTEP;
+ writel(ctrl, hsotg->regs + epctl_reg);
+}
+
/**
* s3c_hsotg_complete_request - complete a request given to us
* @hsotg: The device state.
@@ -1249,6 +1389,8 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
if (hs_req->req.status == -EINPROGRESS)
hs_req->req.status = result;
+ s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);
+
hs_ep->req = NULL;
list_del_init(&hs_req->queue);
@@ -1293,7 +1435,7 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
*/
static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
{
- struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx];
+ struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx];
struct s3c_hsotg_req *hs_req = hs_ep->req;
void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx);
int to_read;
@@ -1305,7 +1447,7 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx));
int ptr;
- dev_warn(hsotg->dev,
+ dev_dbg(hsotg->dev,
"%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n",
__func__, size, ep_idx, epctl);
@@ -1345,9 +1487,9 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
}
/**
- * s3c_hsotg_send_zlp - send zero-length packet on control endpoint
+ * s3c_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint
* @hsotg: The device instance
- * @req: The request currently on this endpoint
+ * @dir_in: If IN zlp
*
* Generate a zero-length IN packet request for terminating a SETUP
* transaction.
@@ -1356,53 +1498,28 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
* currently believed that we do not need to wait for any space in
* the TxFIFO.
*/
-static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg,
- struct s3c_hsotg_req *req)
+static void s3c_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in)
{
- u32 ctrl;
+ /* eps_out[0] is used in both directions */
+ hsotg->eps_out[0]->dir_in = dir_in;
+ hsotg->ep0_state = dir_in ? DWC2_EP0_STATUS_IN : DWC2_EP0_STATUS_OUT;
- if (!req) {
- dev_warn(hsotg->dev, "%s: no request?\n", __func__);
- return;
- }
-
- if (req->req.length == 0) {
- hsotg->eps[0].sent_zlp = 1;
- s3c_hsotg_enqueue_setup(hsotg);
- return;
- }
-
- hsotg->eps[0].dir_in = 1;
- hsotg->eps[0].sent_zlp = 1;
-
- dev_dbg(hsotg->dev, "sending zero-length packet\n");
-
- /* issue a zero-sized packet to terminate this */
- writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
- DXEPTSIZ_XFERSIZE(0), hsotg->regs + DIEPTSIZ(0));
-
- ctrl = readl(hsotg->regs + DIEPCTL0);
- ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */
- ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
- ctrl |= DXEPCTL_USBACTEP;
- writel(ctrl, hsotg->regs + DIEPCTL0);
+ s3c_hsotg_program_zlp(hsotg, hsotg->eps_out[0]);
}
/**
* s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
* @hsotg: The device instance
* @epnum: The endpoint received from
- * @was_setup: Set if processing a SetupDone event.
*
* The RXFIFO has delivered an OutDone event, which means that the data
* transfer for an OUT endpoint has been completed, either by a short
* packet or by the finish of a transfer.
*/
-static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
- int epnum, bool was_setup)
+static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
{
u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum));
- struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum];
+ struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[epnum];
struct s3c_hsotg_req *hs_req = hs_ep->req;
struct usb_request *req = &hs_req->req;
unsigned size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
@@ -1413,6 +1530,13 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
return;
}
+ if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_OUT) {
+ dev_dbg(hsotg->dev, "zlp packet received\n");
+ s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+ s3c_hsotg_enqueue_setup(hsotg);
+ return;
+ }
+
if (using_dma(hsotg)) {
unsigned size_done;
@@ -1435,12 +1559,6 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
if (req->actual < req->length && size_left == 0) {
s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
return;
- } else if (epnum == 0) {
- /*
- * After was_setup = 1 =>
- * set CNAK for non Setup requests
- */
- hsotg->setup = was_setup ? 0 : 1;
}
if (req->actual < req->length && req->short_not_ok) {
@@ -1453,13 +1571,10 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
*/
}
- if (epnum == 0) {
- /*
- * Condition req->complete != s3c_hsotg_complete_setup says:
- * send ZLP when we have an asynchronous request from gadget
- */
- if (!was_setup && req->complete != s3c_hsotg_complete_setup)
- s3c_hsotg_send_zlp(hsotg, hs_req);
+ if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
+ /* Move to STATUS IN */
+ s3c_hsotg_ep0_zlp(hsotg, true);
+ return;
}
s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
@@ -1511,8 +1626,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
size = grxstsr & GRXSTS_BYTECNT_MASK;
size >>= GRXSTS_BYTECNT_SHIFT;
- if (1)
- dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
+ dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
__func__, grxstsr, size, epnum);
switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) {
@@ -1525,7 +1639,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
s3c_hsotg_read_frameno(hsotg));
if (!using_dma(hsotg))
- s3c_hsotg_handle_outdone(hsotg, epnum, false);
+ s3c_hsotg_handle_outdone(hsotg, epnum);
break;
case GRXSTS_PKTSTS_SETUPDONE:
@@ -1533,8 +1647,13 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
"SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
s3c_hsotg_read_frameno(hsotg),
readl(hsotg->regs + DOEPCTL(0)));
-
- s3c_hsotg_handle_outdone(hsotg, epnum, true);
+ /*
+ * Call s3c_hsotg_handle_outdone here if it was not called from
+ * GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't
+ * generate GRXSTS_PKTSTS_OUTDONE for setup packet.
+ */
+ if (hsotg->ep0_state == DWC2_EP0_SETUP)
+ s3c_hsotg_handle_outdone(hsotg, epnum);
break;
case GRXSTS_PKTSTS_OUTRX:
@@ -1547,6 +1666,8 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
s3c_hsotg_read_frameno(hsotg),
readl(hsotg->regs + DOEPCTL(0)));
+ WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP);
+
s3c_hsotg_rx_data(hsotg, epnum, size);
break;
@@ -1591,14 +1712,18 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps)
* the hardware control registers to reflect this.
*/
static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
- unsigned int ep, unsigned int mps)
+ unsigned int ep, unsigned int mps, unsigned int dir_in)
{
- struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
+ struct s3c_hsotg_ep *hs_ep;
void __iomem *regs = hsotg->regs;
u32 mpsval;
u32 mcval;
u32 reg;
+ hs_ep = index_to_ep(hsotg, ep, dir_in);
+ if (!hs_ep)
+ return;
+
if (ep == 0) {
/* EP0 is a special case */
mpsval = s3c_hsotg_ep0_mps(mps);
@@ -1617,17 +1742,12 @@ static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
hs_ep->ep.maxpacket = mpsval;
}
- /*
- * update both the in and out endpoint controldir_ registers, even
- * if one of the directions may not be in use.
- */
-
- reg = readl(regs + DIEPCTL(ep));
- reg &= ~DXEPCTL_MPS_MASK;
- reg |= mpsval;
- writel(reg, regs + DIEPCTL(ep));
-
- if (ep) {
+ if (dir_in) {
+ reg = readl(regs + DIEPCTL(ep));
+ reg &= ~DXEPCTL_MPS_MASK;
+ reg |= mpsval;
+ writel(reg, regs + DIEPCTL(ep));
+ } else {
reg = readl(regs + DOEPCTL(ep));
reg &= ~DXEPCTL_MPS_MASK;
reg |= mpsval;
@@ -1727,9 +1847,21 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
}
/* Finish ZLP handling for IN EP0 transactions */
- if (hsotg->eps[0].sent_zlp) {
- dev_dbg(hsotg->dev, "zlp packet received\n");
+ if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) {
+ dev_dbg(hsotg->dev, "zlp packet sent\n");
s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+ if (hsotg->test_mode) {
+ int ret;
+
+ ret = s3c_hsotg_set_test_mode(hsotg, hsotg->test_mode);
+ if (ret < 0) {
+ dev_dbg(hsotg->dev, "Invalid Test #%d\n",
+ hsotg->test_mode);
+ s3c_hsotg_stall_ep0(hsotg);
+ return;
+ }
+ }
+ s3c_hsotg_enqueue_setup(hsotg);
return;
}
@@ -1756,31 +1888,27 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n",
hs_req->req.length, hs_req->req.actual, hs_req->req.zero);
- /*
- * Check if dealing with Maximum Packet Size(MPS) IN transfer at EP0
- * When sent data is a multiple MPS size (e.g. 64B ,128B ,192B
- * ,256B ... ), after last MPS sized packet send IN ZLP packet to
- * inform the host that no more data is available.
- * The state of req.zero member is checked to be sure that the value to
- * send is smaller than wValue expected from host.
- * Check req.length to NOT send another ZLP when the current one is
- * under completion (the one for which this completion has been called).
- */
- if (hs_req->req.length && hs_ep->index == 0 && hs_req->req.zero &&
- hs_req->req.length == hs_req->req.actual &&
- !(hs_req->req.length % hs_ep->ep.maxpacket)) {
+ if (!size_left && hs_req->req.actual < hs_req->req.length) {
+ dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
+ s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
+ return;
+ }
- dev_dbg(hsotg->dev, "ep0 zlp IN packet sent\n");
- s3c_hsotg_send_zlp(hsotg, hs_req);
+ /* Zlp for all endpoints, for ep0 only in DATA IN stage */
+ if (hs_ep->send_zlp) {
+ s3c_hsotg_program_zlp(hsotg, hs_ep);
+ hs_ep->send_zlp = 0;
+ /* transfer will be completed on next complete interrupt */
+ return;
+ }
+ if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) {
+ /* Move to STATUS OUT */
+ s3c_hsotg_ep0_zlp(hsotg, false);
return;
}
- if (!size_left && hs_req->req.actual < hs_req->req.length) {
- dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
- s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
- } else
- s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+ s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
}
/**
@@ -1794,7 +1922,7 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
int dir_in)
{
- struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx];
+ struct s3c_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in);
u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx);
u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
@@ -1807,9 +1935,19 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
/* Clear endpoint interrupts */
writel(ints, hsotg->regs + epint_reg);
+ if (!hs_ep) {
+ dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n",
+ __func__, idx, dir_in ? "in" : "out");
+ return;
+ }
+
dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n",
__func__, idx, dir_in ? "in" : "out", ints);
+ /* Don't process XferCompl interrupt if it is a setup packet */
+ if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD)))
+ ints &= ~DXEPINT_XFERCOMPL;
+
if (ints & DXEPINT_XFERCOMPL) {
if (hs_ep->isochronous && hs_ep->interval == 1) {
if (ctrl & DXEPCTL_EOFRNUM)
@@ -1839,7 +1977,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
* as we ignore the RXFIFO.
*/
- s3c_hsotg_handle_outdone(hsotg, idx, false);
+ s3c_hsotg_handle_outdone(hsotg, idx);
}
}
@@ -1878,7 +2016,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
if (dir_in)
WARN_ON_ONCE(1);
else
- s3c_hsotg_handle_outdone(hsotg, 0, true);
+ s3c_hsotg_handle_outdone(hsotg, 0);
}
}
@@ -1969,9 +2107,15 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
if (ep0_mps) {
int i;
- s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps);
- for (i = 1; i < hsotg->num_of_eps; i++)
- s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps);
+ /* Initialize ep0 for both in and out directions */
+ s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 1);
+ s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0);
+ for (i = 1; i < hsotg->num_of_eps; i++) {
+ if (hsotg->eps_in[i])
+ s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 1);
+ if (hsotg->eps_out[i])
+ s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 0);
+ }
}
/* ensure after enumeration our EP0 is active */
@@ -1988,30 +2132,23 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
* @hsotg: The device state.
* @ep: The endpoint the requests may be on.
* @result: The result code to use.
- * @force: Force removal of any current requests
*
* Go through the requests on the given endpoint and mark them
* completed with the given result code.
*/
static void kill_all_requests(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *ep,
- int result, bool force)
+ int result)
{
struct s3c_hsotg_req *req, *treq;
unsigned size;
- list_for_each_entry_safe(req, treq, &ep->queue, queue) {
- /*
- * currently, we can't do much about an already
- * running request on an in endpoint
- */
-
- if (ep->req == req && ep->dir_in && !force)
- continue;
+ ep->req = NULL;
+ list_for_each_entry_safe(req, treq, &ep->queue, queue)
s3c_hsotg_complete_request(hsotg, ep, req,
result);
- }
+
if (!hsotg->dedicated_fifos)
return;
size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4;
@@ -2035,8 +2172,16 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
return;
hsotg->connected = 0;
- for (ep = 0; ep < hsotg->num_of_eps; ep++)
- kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true);
+ hsotg->test_mode = 0;
+
+ for (ep = 0; ep < hsotg->num_of_eps; ep++) {
+ if (hsotg->eps_in[ep])
+ kill_all_requests(hsotg, hsotg->eps_in[ep],
+ -ESHUTDOWN);
+ if (hsotg->eps_out[ep])
+ kill_all_requests(hsotg, hsotg->eps_out[ep],
+ -ESHUTDOWN);
+ }
call_gadget(hsotg, disconnect);
}
@@ -2053,9 +2198,11 @@ static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
int epno, ret;
/* look through for any more data to transmit */
-
for (epno = 0; epno < hsotg->num_of_eps; epno++) {
- ep = &hsotg->eps[epno];
+ ep = index_to_ep(hsotg, epno, 1);
+
+ if (!ep)
+ continue;
if (!ep->dir_in)
continue;
@@ -2129,9 +2276,13 @@ static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg)
*
* Issue a soft reset to the core, and await the core finishing it.
*/
-void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
+void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
+ bool is_usb_reset)
{
- s3c_hsotg_corereset(hsotg);
+ u32 val;
+
+ if (!is_usb_reset)
+ s3c_hsotg_corereset(hsotg);
/*
* we must now enable ep0 ready for host detection and then
@@ -2139,14 +2290,16 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
*/
/* set the PLL on, remove the HNP/SRP and set the PHY */
+ val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
- (0x5 << 10), hsotg->regs + GUSBCFG);
+ (val << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG);
s3c_hsotg_init_fifo(hsotg);
- __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+ if (!is_usb_reset)
+ __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
- writel(1 << 18 | DCFG_DEVSPD_HS, hsotg->regs + DCFG);
+ writel(DCFG_EPMISCNT(1) | DCFG_DEVSPD_HS, hsotg->regs + DCFG);
/* Clear any pending OTG interrupts */
writel(0xffffffff, hsotg->regs + GOTGINT);
@@ -2163,7 +2316,7 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
if (using_dma(hsotg))
writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
- GAHBCFG_HBSTLEN_INCR4,
+ (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT),
hsotg->regs + GAHBCFG);
else
writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NP_TXF_EMP_LVL |
@@ -2177,8 +2330,8 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
* interrupts.
*/
- writel(((hsotg->dedicated_fifos) ? DIEPMSK_TXFIFOEMPTY |
- DIEPMSK_INTKNTXFEMPMSK : 0) |
+ writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ?
+ DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) |
DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
DIEPMSK_INTKNEPMISMSK,
@@ -2215,9 +2368,11 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1);
s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1);
- __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
- udelay(10); /* see openiboot */
- __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ if (!is_usb_reset) {
+ __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ udelay(10); /* see openiboot */
+ __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+ }
dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + DCTL));
@@ -2230,13 +2385,13 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
DXEPTSIZ_XFERSIZE(8), hsotg->regs + DOEPTSIZ0);
- writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
+ writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
DXEPCTL_CNAK | DXEPCTL_EPENA |
DXEPCTL_USBACTEP,
hsotg->regs + DOEPCTL0);
/* enable, but don't activate EP0in */
- writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
+ writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0);
s3c_hsotg_enqueue_setup(hsotg);
@@ -2246,8 +2401,10 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
readl(hsotg->regs + DOEPCTL0));
/* clear global NAKs */
- writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON,
- hsotg->regs + DCTL);
+ val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
+ if (!is_usb_reset)
+ val |= DCTL_SFTDISCON;
+ __orr32(hsotg->regs + DCTL, val);
/* must be at-least 3ms to allow bus to see disconnect */
mdelay(3);
@@ -2293,7 +2450,6 @@ irq_retry:
writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
s3c_hsotg_irq_enumdone(hsotg);
- hsotg->connected = 1;
}
if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
@@ -2308,12 +2464,14 @@ irq_retry:
dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
- for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) {
+ for (ep = 0; ep < hsotg->num_of_eps && daint_out;
+ ep++, daint_out >>= 1) {
if (daint_out & 1)
s3c_hsotg_epint(hsotg, ep, 0);
}
- for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) {
+ for (ep = 0; ep < hsotg->num_of_eps && daint_in;
+ ep++, daint_in >>= 1) {
if (daint_in & 1)
s3c_hsotg_epint(hsotg, ep, 1);
}
@@ -2329,15 +2487,17 @@ irq_retry:
writel(GINTSTS_USBRST, hsotg->regs + GINTSTS);
+ /* Report disconnection if it is not already done. */
+ s3c_hsotg_disconnect(hsotg);
+
if (usb_status & GOTGCTL_BSESVLD) {
if (time_after(jiffies, hsotg->last_rst +
msecs_to_jiffies(200))) {
- kill_all_requests(hsotg, &hsotg->eps[0],
- -ECONNRESET, true);
+ kill_all_requests(hsotg, hsotg->eps_out[0],
+ -ECONNRESET);
- s3c_hsotg_core_init_disconnected(hsotg);
- s3c_hsotg_core_connect(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg, true);
}
}
}
@@ -2429,12 +2589,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
struct dwc2_hsotg *hsotg = hs_ep->parent;
unsigned long flags;
- int index = hs_ep->index;
+ unsigned int index = hs_ep->index;
u32 epctrl_reg;
u32 epctrl;
u32 mps;
- int dir_in;
- int i, val, size;
+ unsigned int dir_in;
+ unsigned int i, val, size;
int ret = 0;
dev_dbg(hsotg->dev,
@@ -2482,7 +2642,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
epctrl |= DXEPCTL_SNAK;
/* update the endpoint state */
- s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps);
+ s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in);
/* default, set to non-periodic */
hs_ep->isochronous = 0;
@@ -2518,30 +2678,48 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
break;
}
+ /* If fifo is already allocated for this ep */
+ if (hs_ep->fifo_index) {
+ size = hs_ep->ep.maxpacket * hs_ep->mc;
+ /* If bigger fifo is required deallocate current one */
+ if (size > hs_ep->fifo_size) {
+ hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
+ hs_ep->fifo_index = 0;
+ hs_ep->fifo_size = 0;
+ }
+ }
+
/*
* if the hardware has dedicated fifos, we must give each IN EP
* a unique tx-fifo even if it is non-periodic.
*/
- if (dir_in && hsotg->dedicated_fifos) {
+ if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) {
+ u32 fifo_index = 0;
+ u32 fifo_size = UINT_MAX;
size = hs_ep->ep.maxpacket*hs_ep->mc;
- for (i = 1; i <= 8; ++i) {
+ for (i = 1; i < hsotg->num_of_eps; ++i) {
if (hsotg->fifo_map & (1<<i))
continue;
val = readl(hsotg->regs + DPTXFSIZN(i));
val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
if (val < size)
continue;
- hsotg->fifo_map |= 1<<i;
-
- epctrl |= DXEPCTL_TXFNUM(i);
- hs_ep->fifo_index = i;
- hs_ep->fifo_size = val;
- break;
+ /* Search for smallest acceptable fifo */
+ if (val < fifo_size) {
+ fifo_size = val;
+ fifo_index = i;
+ }
}
- if (i == 8) {
+ if (!fifo_index) {
+ dev_err(hsotg->dev,
+ "%s: No suitable fifo found\n", __func__);
ret = -ENOMEM;
goto error;
}
+ hsotg->fifo_map |= 1 << fifo_index;
+ epctrl |= DXEPCTL_TXFNUM(fifo_index);
+ hs_ep->fifo_index = fifo_index;
+ hs_ep->fifo_size = fifo_size;
}
/* for non control endpoints, set PID to D0 */
@@ -2579,7 +2757,7 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
- if (ep == &hsotg->eps[0].ep) {
+ if (ep == &hsotg->eps_out[0]->ep) {
dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
return -EINVAL;
}
@@ -2587,8 +2765,6 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
spin_lock_irqsave(&hsotg->lock, flags);
- /* terminate all requests with shutdown */
- kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, force);
hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
hs_ep->fifo_index = 0;
@@ -2605,6 +2781,9 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
/* disable endpoint interrupts */
s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
+ /* terminate all requests with shutdown */
+ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
+
spin_unlock_irqrestore(&hsotg->lock, flags);
return 0;
}
@@ -2682,40 +2861,39 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
return 0;
}
- /* write both IN and OUT control registers */
-
- epreg = DIEPCTL(index);
- epctl = readl(hs->regs + epreg);
-
- if (value) {
- epctl |= DXEPCTL_STALL + DXEPCTL_SNAK;
- if (epctl & DXEPCTL_EPENA)
- epctl |= DXEPCTL_EPDIS;
+ if (hs_ep->dir_in) {
+ epreg = DIEPCTL(index);
+ epctl = readl(hs->regs + epreg);
+
+ if (value) {
+ epctl |= DXEPCTL_STALL + DXEPCTL_SNAK;
+ if (epctl & DXEPCTL_EPENA)
+ epctl |= DXEPCTL_EPDIS;
+ } else {
+ epctl &= ~DXEPCTL_STALL;
+ xfertype = epctl & DXEPCTL_EPTYPE_MASK;
+ if (xfertype == DXEPCTL_EPTYPE_BULK ||
+ xfertype == DXEPCTL_EPTYPE_INTERRUPT)
+ epctl |= DXEPCTL_SETD0PID;
+ }
+ writel(epctl, hs->regs + epreg);
} else {
- epctl &= ~DXEPCTL_STALL;
- xfertype = epctl & DXEPCTL_EPTYPE_MASK;
- if (xfertype == DXEPCTL_EPTYPE_BULK ||
- xfertype == DXEPCTL_EPTYPE_INTERRUPT)
- epctl |= DXEPCTL_SETD0PID;
- }
-
- writel(epctl, hs->regs + epreg);
- epreg = DOEPCTL(index);
- epctl = readl(hs->regs + epreg);
+ epreg = DOEPCTL(index);
+ epctl = readl(hs->regs + epreg);
- if (value)
- epctl |= DXEPCTL_STALL;
- else {
- epctl &= ~DXEPCTL_STALL;
- xfertype = epctl & DXEPCTL_EPTYPE_MASK;
- if (xfertype == DXEPCTL_EPTYPE_BULK ||
- xfertype == DXEPCTL_EPTYPE_INTERRUPT)
- epctl |= DXEPCTL_SETD0PID;
+ if (value)
+ epctl |= DXEPCTL_STALL;
+ else {
+ epctl &= ~DXEPCTL_STALL;
+ xfertype = epctl & DXEPCTL_EPTYPE_MASK;
+ if (xfertype == DXEPCTL_EPTYPE_BULK ||
+ xfertype == DXEPCTL_EPTYPE_INTERRUPT)
+ epctl |= DXEPCTL_SETD0PID;
+ }
+ writel(epctl, hs->regs + epreg);
}
- writel(epctl, hs->regs + epreg);
-
hs_ep->halted = value;
return 0;
@@ -2801,6 +2979,7 @@ static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg)
*/
static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
{
+ u32 trdtim;
/* unmask subset of endpoint interrupts */
writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
@@ -2816,12 +2995,6 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
/* Be in disconnected state until gadget is registered */
__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
- if (0) {
- /* post global nak until we're ready */
- writel(DCTL_SGNPINNAK | DCTL_SGOUTNAK,
- hsotg->regs + DCTL);
- }
-
/* setup fifos */
dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
@@ -2831,11 +3004,13 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
s3c_hsotg_init_fifo(hsotg);
/* set the PLL on, remove the HNP/SRP and set the PHY */
- writel(GUSBCFG_PHYIF16 | GUSBCFG_TOUTCAL(7) | (0x5 << 10),
- hsotg->regs + GUSBCFG);
+ trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
+ writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
+ (trdtim << GUSBCFG_USBTRDTIM_SHIFT),
+ hsotg->regs + GUSBCFG);
- writel(using_dma(hsotg) ? GAHBCFG_DMA_EN : 0x0,
- hsotg->regs + GAHBCFG);
+ if (using_dma(hsotg))
+ __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
}
/**
@@ -2889,10 +3064,12 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
}
s3c_hsotg_phy_enable(hsotg);
+ if (!IS_ERR_OR_NULL(hsotg->uphy))
+ otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
spin_lock_irqsave(&hsotg->lock, flags);
s3c_hsotg_init(hsotg);
- s3c_hsotg_core_init_disconnected(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg, false);
hsotg->enabled = 0;
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -2927,8 +3104,12 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
mutex_lock(&hsotg->init_mutex);
/* all endpoints should be shutdown */
- for (ep = 1; ep < hsotg->num_of_eps; ep++)
- s3c_hsotg_ep_disable_force(&hsotg->eps[ep].ep, true);
+ for (ep = 1; ep < hsotg->num_of_eps; ep++) {
+ if (hsotg->eps_in[ep])
+ s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+ if (hsotg->eps_out[ep])
+ s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+ }
spin_lock_irqsave(&hsotg->lock, flags);
@@ -2938,6 +3119,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
spin_unlock_irqrestore(&hsotg->lock, flags);
+ if (!IS_ERR_OR_NULL(hsotg->uphy))
+ otg_set_peripheral(hsotg->uphy->otg, NULL);
s3c_hsotg_phy_disable(hsotg);
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
@@ -2979,9 +3162,11 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
if (is_on) {
clk_enable(hsotg->clk);
hsotg->enabled = 1;
+ s3c_hsotg_core_init_disconnected(hsotg, false);
s3c_hsotg_core_connect(hsotg);
} else {
s3c_hsotg_core_disconnect(hsotg);
+ s3c_hsotg_disconnect(hsotg);
hsotg->enabled = 0;
clk_disable(hsotg->clk);
}
@@ -2993,11 +3178,52 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
+static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+ unsigned long flags;
+
+ dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active);
+ spin_lock_irqsave(&hsotg->lock, flags);
+
+ if (is_active) {
+ /* Kill any ep0 requests as controller will be reinitialized */
+ kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
+ s3c_hsotg_core_init_disconnected(hsotg, false);
+ if (hsotg->enabled)
+ s3c_hsotg_core_connect(hsotg);
+ } else {
+ s3c_hsotg_core_disconnect(hsotg);
+ s3c_hsotg_disconnect(hsotg);
+ }
+
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ return 0;
+}
+
+/**
+ * s3c_hsotg_vbus_draw - report bMaxPower field
+ * @gadget: The usb gadget state
+ * @mA: Amount of current
+ *
+ * Report how much power the device may consume to the phy.
+ */
+static int s3c_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+ struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+
+ if (IS_ERR_OR_NULL(hsotg->uphy))
+ return -ENOTSUPP;
+ return usb_phy_set_power(hsotg->uphy, mA);
+}
+
static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
.get_frame = s3c_hsotg_gadget_getframe,
.udc_start = s3c_hsotg_udc_start,
.udc_stop = s3c_hsotg_udc_stop,
.pullup = s3c_hsotg_pullup,
+ .vbus_session = s3c_hsotg_vbus_session,
+ .vbus_draw = s3c_hsotg_vbus_draw,
};
/**
@@ -3012,19 +3238,19 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
*/
static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep,
- int epnum)
+ int epnum,
+ bool dir_in)
{
char *dir;
if (epnum == 0)
dir = "";
- else if ((epnum % 2) == 0) {
- dir = "out";
- } else {
+ else if (dir_in)
dir = "in";
- hs_ep->dir_in = 1;
- }
+ else
+ dir = "out";
+ hs_ep->dir_in = dir_in;
hs_ep->index = epnum;
snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir);
@@ -3048,8 +3274,10 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
if (using_dma(hsotg)) {
u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15);
- writel(next, hsotg->regs + DIEPCTL(epnum));
- writel(next, hsotg->regs + DOEPCTL(epnum));
+ if (dir_in)
+ writel(next, hsotg->regs + DIEPCTL(epnum));
+ else
+ writel(next, hsotg->regs + DOEPCTL(epnum));
}
}
@@ -3059,24 +3287,56 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
*
* Read the USB core HW configuration registers
*/
-static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
+static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
{
- u32 cfg2, cfg3, cfg4;
+ u32 cfg;
+ u32 ep_type;
+ u32 i;
+
/* check hardware configuration */
- cfg2 = readl(hsotg->regs + 0x48);
- hsotg->num_of_eps = (cfg2 >> 10) & 0xF;
+ cfg = readl(hsotg->regs + GHWCFG2);
+ hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF;
+ /* Add ep0 */
+ hsotg->num_of_eps++;
+
+ hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, sizeof(struct s3c_hsotg_ep),
+ GFP_KERNEL);
+ if (!hsotg->eps_in[0])
+ return -ENOMEM;
+ /* Same s3c_hsotg_ep is used in both directions for ep0 */
+ hsotg->eps_out[0] = hsotg->eps_in[0];
+
+ cfg = readl(hsotg->regs + GHWCFG1);
+ for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
+ ep_type = cfg & 3;
+ /* Direction in or both */
+ if (!(ep_type & 2)) {
+ hsotg->eps_in[i] = devm_kzalloc(hsotg->dev,
+ sizeof(struct s3c_hsotg_ep), GFP_KERNEL);
+ if (!hsotg->eps_in[i])
+ return -ENOMEM;
+ }
+ /* Direction out or both */
+ if (!(ep_type & 1)) {
+ hsotg->eps_out[i] = devm_kzalloc(hsotg->dev,
+ sizeof(struct s3c_hsotg_ep), GFP_KERNEL);
+ if (!hsotg->eps_out[i])
+ return -ENOMEM;
+ }
+ }
- cfg3 = readl(hsotg->regs + 0x4C);
- hsotg->fifo_mem = (cfg3 >> 16);
+ cfg = readl(hsotg->regs + GHWCFG3);
+ hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT);
- cfg4 = readl(hsotg->regs + 0x50);
- hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
+ cfg = readl(hsotg->regs + GHWCFG4);
+ hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1;
dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
hsotg->num_of_eps,
hsotg->dedicated_fifos ? "dedicated" : "shared",
hsotg->fifo_mem);
+ return 0;
}
/**
@@ -3095,22 +3355,22 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
readl(regs + DCFG), readl(regs + DCTL),
readl(regs + DIEPMSK));
- dev_info(dev, "GAHBCFG=0x%08x, 0x44=0x%08x\n",
- readl(regs + GAHBCFG), readl(regs + 0x44));
+ dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n",
+ readl(regs + GAHBCFG), readl(regs + GHWCFG1));
dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ));
/* show periodic fifo settings */
- for (idx = 1; idx <= 15; idx++) {
+ for (idx = 1; idx < hsotg->num_of_eps; idx++) {
val = readl(regs + DPTXFSIZN(idx));
dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
val >> FIFOSIZE_DEPTH_SHIFT,
val & FIFOSIZE_STARTADDR_MASK);
}
- for (idx = 0; idx < 15; idx++) {
+ for (idx = 0; idx < hsotg->num_of_eps; idx++) {
dev_info(dev,
"ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
readl(regs + DIEPCTL(idx)),
@@ -3132,6 +3392,103 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
}
/**
+ * testmode_write - debugfs: change usb test mode
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry modify the current usb test mode.
+ */
+static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
+ count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct dwc2_hsotg *hsotg = s->private;
+ unsigned long flags;
+ u32 testmode = 0;
+ char buf[32];
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (!strncmp(buf, "test_j", 6))
+ testmode = TEST_J;
+ else if (!strncmp(buf, "test_k", 6))
+ testmode = TEST_K;
+ else if (!strncmp(buf, "test_se0_nak", 12))
+ testmode = TEST_SE0_NAK;
+ else if (!strncmp(buf, "test_packet", 11))
+ testmode = TEST_PACKET;
+ else if (!strncmp(buf, "test_force_enable", 17))
+ testmode = TEST_FORCE_EN;
+ else
+ testmode = 0;
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+ s3c_hsotg_set_test_mode(hsotg, testmode);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ return count;
+}
+
+/**
+ * testmode_show - debugfs: show usb test mode state
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry shows which usb test mode is currently enabled.
+ */
+static int testmode_show(struct seq_file *s, void *unused)
+{
+ struct dwc2_hsotg *hsotg = s->private;
+ unsigned long flags;
+ int dctl;
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+ dctl = readl(hsotg->regs + DCTL);
+ dctl &= DCTL_TSTCTL_MASK;
+ dctl >>= DCTL_TSTCTL_SHIFT;
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ switch (dctl) {
+ case 0:
+ seq_puts(s, "no test\n");
+ break;
+ case TEST_J:
+ seq_puts(s, "test_j\n");
+ break;
+ case TEST_K:
+ seq_puts(s, "test_k\n");
+ break;
+ case TEST_SE0_NAK:
+ seq_puts(s, "test_se0_nak\n");
+ break;
+ case TEST_PACKET:
+ seq_puts(s, "test_packet\n");
+ break;
+ case TEST_FORCE_EN:
+ seq_puts(s, "test_force_enable\n");
+ break;
+ default:
+ seq_printf(s, "UNKNOWN %d\n", dctl);
+ }
+
+ return 0;
+}
+
+static int testmode_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, testmode_show, inode->i_private);
+}
+
+static const struct file_operations testmode_fops = {
+ .owner = THIS_MODULE,
+ .open = testmode_open,
+ .write = testmode_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/**
* state_show - debugfs: show overall driver and device state.
* @seq: The seq file to write to.
* @v: Unused parameter.
@@ -3168,7 +3525,7 @@ static int state_show(struct seq_file *seq, void *v)
seq_puts(seq, "\nEndpoint status:\n");
- for (idx = 0; idx < 15; idx++) {
+ for (idx = 0; idx < hsotg->num_of_eps; idx++) {
u32 in, out;
in = readl(regs + DIEPCTL(idx));
@@ -3227,7 +3584,7 @@ static int fifo_show(struct seq_file *seq, void *v)
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
- for (idx = 1; idx <= 15; idx++) {
+ for (idx = 1; idx < hsotg->num_of_eps; idx++) {
val = readl(regs + DPTXFSIZN(idx));
seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
@@ -3359,29 +3716,53 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
/* create general state file */
- hsotg->debug_file = debugfs_create_file("state", 0444, root,
+ hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root,
hsotg, &state_fops);
if (IS_ERR(hsotg->debug_file))
dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
- hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root,
+ hsotg->debug_testmode = debugfs_create_file("testmode",
+ S_IRUGO | S_IWUSR, root,
+ hsotg, &testmode_fops);
+
+ if (IS_ERR(hsotg->debug_testmode))
+ dev_err(hsotg->dev, "%s: failed to create testmode\n",
+ __func__);
+
+ hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root,
hsotg, &fifo_fops);
if (IS_ERR(hsotg->debug_fifo))
dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
- /* create one file for each endpoint */
-
+ /* Create one file for each out endpoint */
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
- struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
+ struct s3c_hsotg_ep *ep;
- ep->debugfs = debugfs_create_file(ep->name, 0444,
- root, ep, &ep_fops);
+ ep = hsotg->eps_out[epidx];
+ if (ep) {
+ ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
+ root, ep, &ep_fops);
- if (IS_ERR(ep->debugfs))
- dev_err(hsotg->dev, "failed to create %s debug file\n",
- ep->name);
+ if (IS_ERR(ep->debugfs))
+ dev_err(hsotg->dev, "failed to create %s debug file\n",
+ ep->name);
+ }
+ }
+ /* Create one file for each in endpoint. EP0 is handled with out eps */
+ for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) {
+ struct s3c_hsotg_ep *ep;
+
+ ep = hsotg->eps_in[epidx];
+ if (ep) {
+ ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
+ root, ep, &ep_fops);
+
+ if (IS_ERR(ep->debugfs))
+ dev_err(hsotg->dev, "failed to create %s debug file\n",
+ ep->name);
+ }
}
}
@@ -3396,15 +3777,63 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
unsigned epidx;
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
- struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
- debugfs_remove(ep->debugfs);
+ if (hsotg->eps_in[epidx])
+ debugfs_remove(hsotg->eps_in[epidx]->debugfs);
+ if (hsotg->eps_out[epidx])
+ debugfs_remove(hsotg->eps_out[epidx]->debugfs);
}
debugfs_remove(hsotg->debug_file);
+ debugfs_remove(hsotg->debug_testmode);
debugfs_remove(hsotg->debug_fifo);
debugfs_remove(hsotg->debug_root);
}
+#ifdef CONFIG_OF
+static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg)
+{
+ struct device_node *np = hsotg->dev->of_node;
+ u32 len = 0;
+ u32 i = 0;
+
+ /* Enable dma if requested in device tree */
+ hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
+
+ /*
+ * Register TX periodic fifo size per endpoint.
+ * EP0 is excluded since it has no fifo configuration.
+ */
+ if (!of_find_property(np, "g-tx-fifo-size", &len))
+ goto rx_fifo;
+
+ len /= sizeof(u32);
+
+ /* Read tx fifo sizes other than ep0 */
+ if (of_property_read_u32_array(np, "g-tx-fifo-size",
+ &hsotg->g_tx_fifo_sz[1], len))
+ goto rx_fifo;
+
+ /* Add ep0 */
+ len++;
+
+ /* Make remaining TX fifos unavailable */
+ if (len < MAX_EPS_CHANNELS) {
+ for (i = len; i < MAX_EPS_CHANNELS; i++)
+ hsotg->g_tx_fifo_sz[i] = 0;
+ }
+
+rx_fifo:
+ /* Register RX fifo size */
+ of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz);
+
+ /* Register NPTX fifo size */
+ of_property_read_u32(np, "g-np-tx-fifo-size",
+ &hsotg->g_np_g_tx_fifo_sz);
+}
+#else
+static inline void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { }
+#endif
+
/**
* dwc2_gadget_init - init function for gadget
* @dwc2: The data structure for the DWC2 driver.
@@ -3414,41 +3843,47 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
{
struct device *dev = hsotg->dev;
struct s3c_hsotg_plat *plat = dev->platform_data;
- struct phy *phy;
- struct usb_phy *uphy;
- struct s3c_hsotg_ep *eps;
int epnum;
int ret;
int i;
+ u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE;
/* Set default UTMI width */
hsotg->phyif = GUSBCFG_PHYIF16;
+ s3c_hsotg_of_probe(hsotg);
+
+ /* Initialize to legacy fifo configuration values */
+ hsotg->g_rx_fifo_sz = 2048;
+ hsotg->g_np_g_tx_fifo_sz = 1024;
+ memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
+ /* Device tree specific probe */
+ s3c_hsotg_of_probe(hsotg);
+ /* Dump fifo information */
+ dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
+ hsotg->g_np_g_tx_fifo_sz);
+ dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz);
+ for (i = 0; i < MAX_EPS_CHANNELS; i++)
+ dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i,
+ hsotg->g_tx_fifo_sz[i]);
/*
- * Attempt to find a generic PHY, then look for an old style
- * USB PHY, finally fall back to pdata
+ * If platform probe couldn't find a generic PHY or an old style
+ * USB PHY, fall back to pdata
*/
- phy = devm_phy_get(dev, "usb2-phy");
- if (IS_ERR(phy)) {
- uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
- if (IS_ERR(uphy)) {
- /* Fallback for pdata */
- plat = dev_get_platdata(dev);
- if (!plat) {
- dev_err(dev,
- "no platform data or transceiver defined\n");
- return -EPROBE_DEFER;
- }
- hsotg->plat = plat;
- } else
- hsotg->uphy = uphy;
- } else {
- hsotg->phy = phy;
+ if (IS_ERR_OR_NULL(hsotg->phy) && IS_ERR_OR_NULL(hsotg->uphy)) {
+ plat = dev_get_platdata(dev);
+ if (!plat) {
+ dev_err(dev,
+ "no platform data or transceiver defined\n");
+ return -EPROBE_DEFER;
+ }
+ hsotg->plat = plat;
+ } else if (hsotg->phy) {
/*
* If using the generic PHY framework, check if the PHY bus
* width is 8-bit and set the phyif appropriately.
*/
- if (phy_get_bus_width(phy) == 8)
+ if (phy_get_bus_width(hsotg->phy) == 8)
hsotg->phyif = GUSBCFG_PHYIF8;
}
@@ -3488,16 +3923,53 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
if (ret) {
dev_err(dev, "failed to enable supplies: %d\n", ret);
- goto err_supplies;
+ goto err_clk;
}
/* usb phy enable */
s3c_hsotg_phy_enable(hsotg);
+ /*
+ * Force Device mode before initialization.
+ * This allows correctly configuring fifo for device mode.
+ */
+ __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE);
+ __orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
+
+ /*
+ * According to Synopsys databook, this sleep is needed for the force
+ * device mode to take effect.
+ */
+ msleep(25);
+
s3c_hsotg_corereset(hsotg);
- s3c_hsotg_hw_cfg(hsotg);
+ ret = s3c_hsotg_hw_cfg(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
+ goto err_clk;
+ }
+
s3c_hsotg_init(hsotg);
+ /* Switch back to default configuration */
+ __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
+
+ hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
+ DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
+ if (!hsotg->ctrl_buff) {
+ dev_err(dev, "failed to allocate ctrl request buff\n");
+ ret = -ENOMEM;
+ goto err_supplies;
+ }
+
+ hsotg->ep0_buff = devm_kzalloc(hsotg->dev,
+ DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
+ if (!hsotg->ep0_buff) {
+ dev_err(dev, "failed to allocate ctrl reply buff\n");
+ ret = -ENOMEM;
+ goto err_supplies;
+ }
+
ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
if (ret < 0) {
@@ -3506,7 +3978,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
dev_err(dev, "cannot claim IRQ for gadget\n");
- goto err_clk;
+ goto err_supplies;
}
/* hsotg->num_of_eps holds number of EPs other than ep0 */
@@ -3517,33 +3989,30 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
goto err_supplies;
}
- eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep),
- GFP_KERNEL);
- if (!eps) {
- ret = -ENOMEM;
- goto err_supplies;
- }
-
- hsotg->eps = eps;
-
/* setup endpoint information */
INIT_LIST_HEAD(&hsotg->gadget.ep_list);
- hsotg->gadget.ep0 = &hsotg->eps[0].ep;
+ hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep;
/* allocate EP0 request */
- hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps[0].ep,
+ hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep,
GFP_KERNEL);
if (!hsotg->ctrl_req) {
dev_err(dev, "failed to allocate ctrl req\n");
ret = -ENOMEM;
- goto err_ep_mem;
+ goto err_supplies;
}
/* initialise the endpoints now the core has been initialised */
- for (epnum = 0; epnum < hsotg->num_of_eps; epnum++)
- s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum);
+ for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) {
+ if (hsotg->eps_in[epnum])
+ s3c_hsotg_initep(hsotg, hsotg->eps_in[epnum],
+ epnum, 1);
+ if (hsotg->eps_out[epnum])
+ s3c_hsotg_initep(hsotg, hsotg->eps_out[epnum],
+ epnum, 0);
+ }
/* disable power and clock */
s3c_hsotg_phy_disable(hsotg);
@@ -3552,12 +4021,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
hsotg->supplies);
if (ret) {
dev_err(dev, "failed to disable supplies: %d\n", ret);
- goto err_ep_mem;
+ goto err_supplies;
}
ret = usb_add_gadget_udc(dev, &hsotg->gadget);
if (ret)
- goto err_ep_mem;
+ goto err_supplies;
s3c_hsotg_create_debug(hsotg);
@@ -3565,8 +4034,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
return 0;
-err_ep_mem:
- kfree(eps);
err_supplies:
s3c_hsotg_phy_disable(hsotg);
err_clk:
@@ -3612,8 +4079,12 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
s3c_hsotg_phy_disable(hsotg);
- for (ep = 0; ep < hsotg->num_of_eps; ep++)
- s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
+ for (ep = 0; ep < hsotg->num_of_eps; ep++) {
+ if (hsotg->eps_in[ep])
+ s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+ if (hsotg->eps_out[ep])
+ s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+ }
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
@@ -3644,7 +4115,7 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
s3c_hsotg_phy_enable(hsotg);
spin_lock_irqsave(&hsotg->lock, flags);
- s3c_hsotg_core_init_disconnected(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg, false);
if (hsotg->enabled)
s3c_hsotg_core_connect(hsotg);
spin_unlock_irqrestore(&hsotg->lock, flags);
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index a0cd9db6f4cd..c78c8740db1d 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -316,10 +316,12 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
*/
static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
{
- if (hsotg->lx_state == DWC2_L2)
+ if (hsotg->lx_state == DWC2_L2) {
hsotg->flags.b.port_suspend_change = 1;
- else
+ usb_hcd_resume_root_hub(hsotg->priv);
+ } else {
hsotg->flags.b.port_l1_change = 1;
+ }
}
/**
@@ -1371,7 +1373,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
dwc2_core_init(hsotg, false, -1);
dwc2_enable_global_interrupts(hsotg);
- s3c_hsotg_core_init_disconnected(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg, false);
s3c_hsotg_core_connect(hsotg);
} else {
/* A-Device connector (Host Mode) */
@@ -1473,30 +1475,6 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
}
}
-static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
-{
- u32 hprt0;
-
- /* After clear the Stop PHY clock bit, we should wait for a moment
- * for PLL work stable with clock output.
- */
- writel(0, hsotg->regs + PCGCTL);
- usleep_range(2000, 4000);
-
- hprt0 = dwc2_read_hprt0(hsotg);
- hprt0 |= HPRT0_RES;
- writel(hprt0, hsotg->regs + HPRT0);
- hprt0 &= ~HPRT0_SUSP;
- /* according to USB2.0 Spec 7.1.7.7, the host must send the resume
- * signal for at least 20ms
- */
- usleep_range(20000, 25000);
-
- hprt0 &= ~HPRT0_RES;
- writel(hprt0, hsotg->regs + HPRT0);
- hsotg->lx_state = DWC2_L0;
-}
-
/* Handles hub class-specific requests */
static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u16 wvalue, u16 windex, char *buf, u16 wlength)
@@ -1542,7 +1520,17 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
case USB_PORT_FEAT_SUSPEND:
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
- dwc2_port_resume(hsotg);
+ writel(0, hsotg->regs + PCGCTL);
+ usleep_range(20000, 40000);
+
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 |= HPRT0_RES;
+ writel(hprt0, hsotg->regs + HPRT0);
+ hprt0 &= ~HPRT0_SUSP;
+ usleep_range(100000, 150000);
+
+ hprt0 &= ~HPRT0_RES;
+ writel(hprt0, hsotg->regs + HPRT0);
break;
case USB_PORT_FEAT_POWER:
@@ -1622,7 +1610,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
hub_desc->bDescLength = 9;
hub_desc->bDescriptorType = 0x29;
hub_desc->bNbrPorts = 1;
- hub_desc->wHubCharacteristics = cpu_to_le16(0x08);
+ hub_desc->wHubCharacteristics =
+ cpu_to_le16(HUB_CHAR_COMMON_LPSM |
+ HUB_CHAR_INDV_PORT_OCPM);
hub_desc->bPwrOn2PwrGood = 1;
hub_desc->bHubContrCurrent = 0;
hub_desc->u.hs.DeviceRemovable[0] = 0;
@@ -2315,55 +2305,6 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
usleep_range(1000, 3000);
}
-static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
-{
- struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
- u32 hprt0;
-
- if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
- (hsotg->op_state == OTG_STATE_A_HOST)))
- return 0;
-
- /* TODO: We get into suspend from 'on' state, maybe we need to do
- * something if we get here from DWC2_L1(LPM sleep) state one day.
- */
- if (hsotg->lx_state != DWC2_L0)
- return 0;
-
- hprt0 = dwc2_read_hprt0(hsotg);
- if (hprt0 & HPRT0_CONNSTS) {
- dwc2_port_suspend(hsotg, 1);
- } else {
- u32 pcgctl = readl(hsotg->regs + PCGCTL);
-
- pcgctl |= PCGCTL_STOPPCLK;
- writel(pcgctl, hsotg->regs + PCGCTL);
- }
-
- return 0;
-}
-
-static int _dwc2_hcd_resume(struct usb_hcd *hcd)
-{
- struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
- u32 hprt0;
-
- if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
- (hsotg->op_state == OTG_STATE_A_HOST)))
- return 0;
-
- if (hsotg->lx_state != DWC2_L2)
- return 0;
-
- hprt0 = dwc2_read_hprt0(hsotg);
- if ((hprt0 & HPRT0_CONNSTS) && (hprt0 & HPRT0_SUSP))
- dwc2_port_resume(hsotg);
- else
- writel(0, hsotg->regs + PCGCTL);
-
- return 0;
-}
-
/* Returns the current frame number */
static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
{
@@ -2734,9 +2675,6 @@ static struct hc_driver dwc2_hc_driver = {
.hub_status_data = _dwc2_hcd_hub_status_data,
.hub_control = _dwc2_hcd_hub_control,
.clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
-
- .bus_suspend = _dwc2_hcd_suspend,
- .bus_resume = _dwc2_hcd_resume,
};
/*
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index 51248b935493..d0a5ed8fa15a 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -294,6 +294,7 @@
#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26)
#define GHWCFG4_NUM_IN_EPS_SHIFT 26
#define GHWCFG4_DED_FIFO_EN (1 << 25)
+#define GHWCFG4_DED_FIFO_SHIFT 25
#define GHWCFG4_SESSION_END_FILT_EN (1 << 24)
#define GHWCFG4_B_VALID_FILT_EN (1 << 23)
#define GHWCFG4_A_VALID_FILT_EN (1 << 22)
@@ -541,6 +542,7 @@
#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20))
#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20))
+#define DXEPINT_SETUP_RCVD (1 << 15)
#define DXEPINT_INEPNAKEFF (1 << 6)
#define DXEPINT_BACK2BACKSETUP (1 << 6)
#define DXEPINT_INTKNEPMIS (1 << 5)
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 6a795aa2ff05..ae095f009b4f 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -155,6 +155,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
struct dwc2_core_params defparams;
struct dwc2_hsotg *hsotg;
struct resource *res;
+ struct phy *phy;
+ struct usb_phy *uphy;
int retval;
int irq;
@@ -212,6 +214,24 @@ static int dwc2_driver_probe(struct platform_device *dev)
hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
+ /*
+ * Attempt to find a generic PHY, then look for an old style
+ * USB PHY
+ */
+ phy = devm_phy_get(&dev->dev, "usb2-phy");
+ if (IS_ERR(phy)) {
+ hsotg->phy = NULL;
+ uphy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2);
+ if (IS_ERR(uphy))
+ hsotg->uphy = NULL;
+ else
+ hsotg->uphy = uphy;
+ } else {
+ hsotg->phy = phy;
+ phy_power_on(hsotg->phy);
+ phy_init(hsotg->phy);
+ }
+
spin_lock_init(&hsotg->lock);
mutex_init(&hsotg->init_mutex);
retval = dwc2_gadget_init(hsotg, irq);
@@ -231,8 +251,15 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
int ret = 0;
- if (dwc2_is_device_mode(dwc2))
+ if (dwc2_is_device_mode(dwc2)) {
ret = s3c_hsotg_suspend(dwc2);
+ } else {
+ if (dwc2->lx_state == DWC2_L0)
+ return 0;
+ phy_exit(dwc2->phy);
+ phy_power_off(dwc2->phy);
+
+ }
return ret;
}
@@ -241,8 +268,13 @@ static int __maybe_unused dwc2_resume(struct device *dev)
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
int ret = 0;
- if (dwc2_is_device_mode(dwc2))
+ if (dwc2_is_device_mode(dwc2)) {
ret = s3c_hsotg_resume(dwc2);
+ } else {
+ phy_power_on(dwc2->phy);
+ phy_init(dwc2->phy);
+
+ }
return ret;
}
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 58b5b2cde4c5..edbf9c85af7e 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -104,12 +104,6 @@ config USB_DWC3_DEBUG
help
Say Y here to enable debugging messages on DWC3 Driver.
-config USB_DWC3_VERBOSE
- bool "Enable Verbose Debugging Messages"
- depends on USB_DWC3_DEBUG
- help
- Say Y here to enable verbose 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
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index bb34fbcfeab3..46172f47f02d 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -2,7 +2,6 @@
CFLAGS_trace.o := -I$(src)
ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG
-ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC3) += dwc3.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 25ddc39efad8..9f0e209b8f6c 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -345,7 +345,7 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
- dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n",
+ dwc3_trace(trace_dwc3_core, "found %d IN and %d OUT endpoints",
dwc->num_in_eps, dwc->num_out_eps);
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 4bb9aa696ede..d201910b892f 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -431,7 +431,6 @@ struct dwc3_event_buffer {
* @dwc: pointer to DWC controller
* @saved_state: ep state saved during hibernation
* @flags: endpoint flags (wedged, stalled, ...)
- * @current_trb: index of current used trb
* @number: endpoint number (1 - 15)
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
* @resource_index: Resource transfer index
@@ -464,8 +463,6 @@ struct dwc3_ep {
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN (1 << 31)
- unsigned current_trb;
-
u8 number;
u8 type;
u8 resource_index;
@@ -685,7 +682,6 @@ struct dwc3_scratchpad_array {
* @is_utmi_l1_suspend: the core asserts output signal
* 0 - utmi_sleep_n
* 1 - utmi_l1_suspend_n
- * @is_selfpowered: true when we are selfpowered
* @is_fpga: true when we are using the FPGA board
* @needs_fifo_resize: not all users might want fifo resizing, flag it
* @pullups_connected: true when Run/Stop bit is set
@@ -809,7 +805,6 @@ struct dwc3 {
unsigned has_hibernation:1;
unsigned has_lpm_erratum:1;
unsigned is_utmi_l1_suspend:1;
- unsigned is_selfpowered:1;
unsigned is_fpga:1;
unsigned needs_fifo_resize:1;
unsigned pullups_connected:1;
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index b642a2f998f9..8d950569d557 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -22,9 +22,6 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
-#include <linux/usb/otg.h>
-#include <linux/usb/usb_phy_generic.h>
-
#include "platform_data.h"
/* FIXME define these in <linux/pci_ids.h> */
@@ -36,66 +33,41 @@
#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30
#define PCI_DEVICE_ID_INTEL_SPTH 0xa130
-struct dwc3_pci {
- struct device *dev;
- struct platform_device *dwc3;
- struct platform_device *usb2_phy;
- struct platform_device *usb3_phy;
-};
-
-static int dwc3_pci_register_phys(struct dwc3_pci *glue)
+static int dwc3_pci_quirks(struct pci_dev *pdev)
{
- struct usb_phy_generic_platform_data pdata;
- struct platform_device *pdev;
- int ret;
+ if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+ pdev->device == PCI_DEVICE_ID_AMD_NL_USB) {
+ struct dwc3_platform_data pdata;
- memset(&pdata, 0x00, sizeof(pdata));
+ memset(&pdata, 0, sizeof(pdata));
- pdev = platform_device_alloc("usb_phy_generic", 0);
- if (!pdev)
- return -ENOMEM;
-
- glue->usb2_phy = pdev;
- pdata.type = USB_PHY_TYPE_USB2;
- pdata.gpio_reset = -1;
+ pdata.has_lpm_erratum = true;
+ pdata.lpm_nyet_threshold = 0xf;
- ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata));
- if (ret)
- goto err1;
+ pdata.u2exit_lfps_quirk = true;
+ pdata.u2ss_inp3_quirk = true;
+ pdata.req_p1p2p3_quirk = true;
+ pdata.del_p1p2p3_quirk = true;
+ pdata.del_phy_power_chg_quirk = true;
+ pdata.lfps_filter_quirk = true;
+ pdata.rx_detect_poll_quirk = true;
- pdev = platform_device_alloc("usb_phy_generic", 1);
- if (!pdev) {
- ret = -ENOMEM;
- goto err1;
- }
+ pdata.tx_de_emphasis_quirk = true;
+ pdata.tx_de_emphasis = 1;
- glue->usb3_phy = pdev;
- pdata.type = USB_PHY_TYPE_USB3;
-
- ret = platform_device_add_data(glue->usb3_phy, &pdata, sizeof(pdata));
- if (ret)
- goto err2;
-
- ret = platform_device_add(glue->usb2_phy);
- if (ret)
- goto err2;
+ /*
+ * FIXME these quirks should be removed when AMD NL
+ * taps out
+ */
+ pdata.disable_scramble_quirk = true;
+ pdata.dis_u3_susphy_quirk = true;
+ pdata.dis_u2_susphy_quirk = true;
- ret = platform_device_add(glue->usb3_phy);
- if (ret)
- goto err3;
+ return platform_device_add_data(pci_get_drvdata(pdev), &pdata,
+ sizeof(pdata));
+ }
return 0;
-
-err3:
- platform_device_del(glue->usb2_phy);
-
-err2:
- platform_device_put(glue->usb3_phy);
-
-err1:
- platform_device_put(glue->usb2_phy);
-
- return ret;
}
static int dwc3_pci_probe(struct pci_dev *pci,
@@ -103,18 +75,8 @@ static int dwc3_pci_probe(struct pci_dev *pci,
{
struct resource res[2];
struct platform_device *dwc3;
- struct dwc3_pci *glue;
int ret;
struct device *dev = &pci->dev;
- struct dwc3_platform_data dwc3_pdata;
-
- memset(&dwc3_pdata, 0x00, sizeof(dwc3_pdata));
-
- glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
- if (!glue)
- return -ENOMEM;
-
- glue->dev = dev;
ret = pcim_enable_device(pci);
if (ret) {
@@ -124,12 +86,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
pci_set_master(pci);
- ret = dwc3_pci_register_phys(glue);
- if (ret) {
- dev_err(dev, "couldn't register PHYs\n");
- return ret;
- }
-
dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
if (!dwc3) {
dev_err(dev, "couldn't allocate dwc3 device\n");
@@ -147,70 +103,34 @@ static int dwc3_pci_probe(struct pci_dev *pci,
res[1].name = "dwc_usb3";
res[1].flags = IORESOURCE_IRQ;
- if (pci->vendor == PCI_VENDOR_ID_AMD &&
- pci->device == PCI_DEVICE_ID_AMD_NL_USB) {
- dwc3_pdata.has_lpm_erratum = true;
- dwc3_pdata.lpm_nyet_threshold = 0xf;
-
- dwc3_pdata.u2exit_lfps_quirk = true;
- dwc3_pdata.u2ss_inp3_quirk = true;
- dwc3_pdata.req_p1p2p3_quirk = true;
- dwc3_pdata.del_p1p2p3_quirk = true;
- dwc3_pdata.del_phy_power_chg_quirk = true;
- dwc3_pdata.lfps_filter_quirk = true;
- dwc3_pdata.rx_detect_poll_quirk = true;
-
- dwc3_pdata.tx_de_emphasis_quirk = true;
- dwc3_pdata.tx_de_emphasis = 1;
-
- /*
- * FIXME these quirks should be removed when AMD NL
- * taps out
- */
- dwc3_pdata.disable_scramble_quirk = true;
- dwc3_pdata.dis_u3_susphy_quirk = true;
- dwc3_pdata.dis_u2_susphy_quirk = true;
- }
-
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc3 device\n");
return ret;
}
- pci_set_drvdata(pci, glue);
-
- ret = platform_device_add_data(dwc3, &dwc3_pdata, sizeof(dwc3_pdata));
+ pci_set_drvdata(pci, dwc3);
+ ret = dwc3_pci_quirks(pci);
if (ret)
- goto err3;
-
- dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
+ goto err;
- dwc3->dev.dma_mask = dev->dma_mask;
- dwc3->dev.dma_parms = dev->dma_parms;
dwc3->dev.parent = dev;
- glue->dwc3 = dwc3;
ret = platform_device_add(dwc3);
if (ret) {
dev_err(dev, "failed to register dwc3 device\n");
- goto err3;
+ goto err;
}
return 0;
-
-err3:
+err:
platform_device_put(dwc3);
return ret;
}
static void dwc3_pci_remove(struct pci_dev *pci)
{
- struct dwc3_pci *glue = pci_get_drvdata(pci);
-
- platform_device_unregister(glue->dwc3);
- platform_device_unregister(glue->usb2_phy);
- platform_device_unregister(glue->usb3_phy);
+ platform_device_unregister(pci_get_drvdata(pci));
}
static const struct pci_device_id dwc3_pci_id_table[] = {
@@ -228,45 +148,11 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
-#ifdef CONFIG_PM_SLEEP
-static int dwc3_pci_suspend(struct device *dev)
-{
- struct pci_dev *pci = to_pci_dev(dev);
-
- pci_disable_device(pci);
-
- return 0;
-}
-
-static int dwc3_pci_resume(struct device *dev)
-{
- struct pci_dev *pci = to_pci_dev(dev);
- int ret;
-
- ret = pci_enable_device(pci);
- if (ret) {
- dev_err(dev, "can't re-enable device --> %d\n", ret);
- return ret;
- }
-
- pci_set_master(pci);
-
- return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static const struct dev_pm_ops dwc3_pci_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
-};
-
static struct pci_driver dwc3_pci_driver = {
.name = "dwc3-pci",
.id_table = dwc3_pci_id_table,
.probe = dwc3_pci_probe,
.remove = dwc3_pci_remove,
- .driver = {
- .pm = &dwc3_pci_dev_pm_ops,
- },
};
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 1bc77a3b4997..2ef3c8d6a9db 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -344,7 +344,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
/*
* LTM will be set once we know how to set this in HW.
*/
- usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+ usb_status |= dwc->gadget.is_selfpowered;
if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 8f65ab3a3b92..a03a485205c7 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -139,7 +139,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
udelay(5);
}
- dev_vdbg(dwc->dev, "link state change request timed out\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "link state change request timed out");
return -ETIMEDOUT;
}
@@ -219,7 +220,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
fifo_size |= (last_fifo_depth << 16);
- dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
+ dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d",
dep->name, last_fifo_depth, fifo_size & 0xffff);
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
@@ -287,7 +288,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
do {
reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
if (!(reg & DWC3_DGCMD_CMDACT)) {
- dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "Command Complete --> %d",
DWC3_DGCMD_STATUS(reg));
return 0;
}
@@ -297,8 +299,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
* interrupt context.
*/
timeout--;
- if (!timeout)
+ if (!timeout) {
+ dwc3_trace(trace_dwc3_gadget,
+ "Command Timed Out");
return -ETIMEDOUT;
+ }
udelay(1);
} while (1);
}
@@ -320,7 +325,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
do {
reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
if (!(reg & DWC3_DEPCMD_CMDACT)) {
- dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "Command Complete --> %d",
DWC3_DEPCMD_STATUS(reg));
return 0;
}
@@ -330,8 +336,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
* interrupt context.
*/
timeout--;
- if (!timeout)
+ if (!timeout) {
+ dwc3_trace(trace_dwc3_gadget,
+ "Command Timed Out");
return -ETIMEDOUT;
+ }
udelay(1);
} while (1);
@@ -352,9 +361,6 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
if (dep->trb_pool)
return 0;
- if (dep->number == 0 || dep->number == 1)
- return 0;
-
dep->trb_pool = dma_alloc_coherent(dwc->dev,
sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
&dep->trb_pool_dma, GFP_KERNEL);
@@ -492,7 +498,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
u32 reg;
int ret;
- dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name);
if (!(dep->flags & DWC3_EP_ENABLED)) {
ret = dwc3_gadget_start_config(dwc, dep);
@@ -729,10 +735,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, dma_addr_t dma,
unsigned length, unsigned last, unsigned chain, unsigned node)
{
- struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
- dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
+ dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s",
dep->name, req, (unsigned long long) dma,
length, last ? " last" : "",
chain ? " chain" : "");
@@ -934,7 +939,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
u32 cmd;
if (start_new && (dep->flags & DWC3_EP_BUSY)) {
- dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name);
return -EBUSY;
}
dep->flags &= ~DWC3_EP_PENDING_REQUEST;
@@ -1005,8 +1010,9 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
u32 uf;
if (list_empty(&dep->request_list)) {
- dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
- dep->name);
+ dwc3_trace(trace_dwc3_gadget,
+ "ISOC ep %s run out for requests",
+ dep->name);
dep->flags |= DWC3_EP_PENDING_REQUEST;
return;
}
@@ -1113,15 +1119,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* handled.
*/
if (dep->stream_capable) {
- int ret;
-
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
- if (ret && ret != -EBUSY) {
- struct dwc3 *dwc = dep->dwc;
-
+ if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
- }
}
return 0;
@@ -1152,8 +1153,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
goto out;
}
- dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
- request, ep->name, request->length);
trace_dwc3_ep_queue(req);
ret = __dwc3_gadget_ep_queue(dep, req);
@@ -1416,7 +1415,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
unsigned long flags;
spin_lock_irqsave(&dwc->lock, flags);
- dwc->is_selfpowered = !!is_selfpowered;
+ g->is_selfpowered = !!is_selfpowered;
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
@@ -1468,7 +1467,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
udelay(1);
} while (1);
- dev_vdbg(dwc->dev, "gadget %s data soft-%s\n",
+ dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
dwc->gadget_driver
? dwc->gadget_driver->function : "no-function",
is_on ? "connect" : "disconnect");
@@ -1688,7 +1687,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dep->endpoint.name = dep->name;
- dev_vdbg(dwc->dev, "initializing %s\n", dep->name);
+ dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
if (epnum == 0 || epnum == 1) {
usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
@@ -1725,13 +1724,15 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
if (ret < 0) {
- dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "failed to allocate OUT endpoints");
return ret;
}
ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
if (ret < 0) {
- dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "failed to allocate IN endpoints");
return ret;
}
@@ -1977,7 +1978,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
} else {
int ret;
- dev_vdbg(dwc->dev, "%s: reason %s\n",
+ dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
dep->name, event->status &
DEPEVT_STATUS_TRANSFER_ACTIVE
? "Transfer Active"
@@ -2001,7 +2002,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
switch (event->status) {
case DEPEVT_STREAMEVT_FOUND:
- dev_vdbg(dwc->dev, "Stream %d found and started\n",
+ dwc3_trace(trace_dwc3_gadget,
+ "Stream %d found and started",
event->parameters);
break;
@@ -2015,7 +2017,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
break;
case DWC3_DEPEVT_EPCMDCMPLT:
- dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
+ dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
break;
}
}
@@ -2043,6 +2045,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->resume(&dwc->gadget);
+ spin_lock(&dwc->lock);
}
}
@@ -2079,7 +2082,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
* We have discussed this with the IP Provider and it was
* suggested to giveback all requests here, but give HW some
* extra time to synchronize with the interconnect. We're using
- * an arbitraty 100us delay for that.
+ * an arbitrary 100us delay for that.
*
* Note also that a similar handling was tested by Synopsys
* (thanks a lot Paul) and nothing bad has come out of it.
@@ -2389,7 +2392,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
(next == DWC3_LINK_STATE_RESUME)) {
- dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n");
+ dwc3_trace(trace_dwc3_gadget,
+ "ignoring transition U3 -> Resume");
return;
}
}
@@ -2511,22 +2515,22 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
break;
case DWC3_DEVICE_EVENT_EOPF:
- dev_vdbg(dwc->dev, "End of Periodic Frame\n");
+ dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
break;
case DWC3_DEVICE_EVENT_SOF:
- dev_vdbg(dwc->dev, "Start of Periodic Frame\n");
+ dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
break;
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
- dev_vdbg(dwc->dev, "Erratic Error\n");
+ dwc3_trace(trace_dwc3_gadget, "Erratic Error");
break;
case DWC3_DEVICE_EVENT_CMD_CMPL:
- dev_vdbg(dwc->dev, "Command Complete\n");
+ dwc3_trace(trace_dwc3_gadget, "Command Complete");
break;
case DWC3_DEVICE_EVENT_OVERFLOW:
- dev_vdbg(dwc->dev, "Overflow\n");
+ dwc3_trace(trace_dwc3_gadget, "Overflow");
break;
default:
- dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+ dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
}
}
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index 9fc20b33dd8e..9c10669ab91f 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -47,6 +47,16 @@ DEFINE_EVENT(dwc3_log_msg, dwc3_writel,
TP_ARGS(vaf)
);
+DEFINE_EVENT(dwc3_log_msg, dwc3_gadget,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(dwc3_log_msg, dwc3_core,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 747ef53bda14..96539038c03a 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -423,6 +423,17 @@ config USB_CONFIGFS_F_HID
For more information, see Documentation/usb/gadget_hid.txt.
+config USB_CONFIGFS_F_UVC
+ bool "USB Webcam function"
+ depends on USB_CONFIGFS
+ depends on VIDEO_DEV
+ select VIDEOBUF2_VMALLOC
+ select USB_F_UVC
+ help
+ The Webcam function acts as a composite USB Audio and Video Class
+ device. It provides a userspace API to process UVC control requests
+ and stream video data to the host.
+
source "drivers/usb/gadget/legacy/Kconfig"
endchoice
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 617835348569..13adfd1a3f54 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1655,7 +1655,7 @@ unknown:
* OS descriptors handling
*/
if (cdev->use_os_string && cdev->os_desc_config &&
- (ctrl->bRequest & USB_TYPE_VENDOR) &&
+ (ctrl->bRequestType & USB_TYPE_VENDOR) &&
ctrl->bRequest == cdev->b_vendor_code) {
struct usb_request *req;
struct usb_configuration *os_desc_cfg;
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index dd68091d92f0..f71b1aaa0edf 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -36,7 +36,7 @@ usb_f_uac1-y := f_uac1.o u_uac1.o
obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o
usb_f_uac2-y := f_uac2.o
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
-usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
+usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_midi-y := f_midi.o
obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 63314ede7ba6..af98b096af2f 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -31,6 +31,7 @@
#include <linux/aio.h>
#include <linux/mmu_context.h>
#include <linux/poll.h>
+#include <linux/eventfd.h>
#include "u_fs.h"
#include "u_f.h"
@@ -153,6 +154,8 @@ struct ffs_io_data {
struct usb_ep *ep;
struct usb_request *req;
+
+ struct ffs_data *ffs;
};
struct ffs_desc_helper {
@@ -390,17 +393,20 @@ done_spin:
return ret;
}
+/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
size_t n)
{
/*
- * We are holding ffs->ev.waitq.lock and ffs->mutex and we need
- * to release them.
+ * n cannot be bigger than ffs->ev.count, which cannot be bigger than
+ * size of ffs->ev.types array (which is four) so that's how much space
+ * we reserve.
*/
- struct usb_functionfs_event events[n];
+ struct usb_functionfs_event events[ARRAY_SIZE(ffs->ev.types)];
+ const size_t size = n * sizeof *events;
unsigned i = 0;
- memset(events, 0, sizeof events);
+ memset(events, 0, size);
do {
events[i].type = ffs->ev.types[i];
@@ -410,19 +416,15 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
}
} while (++i < n);
- if (n < ffs->ev.count) {
- ffs->ev.count -= n;
+ ffs->ev.count -= n;
+ if (ffs->ev.count)
memmove(ffs->ev.types, ffs->ev.types + n,
ffs->ev.count * sizeof *ffs->ev.types);
- } else {
- ffs->ev.count = 0;
- }
spin_unlock_irq(&ffs->ev.waitq.lock);
mutex_unlock(&ffs->mutex);
- return unlikely(__copy_to_user(buf, events, sizeof events))
- ? -EFAULT : sizeof events;
+ return unlikely(__copy_to_user(buf, events, size)) ? -EFAULT : size;
}
static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
@@ -606,6 +608,8 @@ static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
}
case FFS_CLOSING:
break;
+ case FFS_DEACTIVATED:
+ break;
}
mutex_unlock(&ffs->mutex);
@@ -673,6 +677,9 @@ static void ffs_user_copy_worker(struct work_struct *work)
aio_complete(io_data->kiocb, ret, ret);
+ if (io_data->ffs->ffs_eventfd && !io_data->kiocb->ki_eventfd)
+ eventfd_signal(io_data->ffs->ffs_eventfd, 1);
+
usb_ep_free_request(io_data->ep, io_data->req);
io_data->kiocb->private = NULL;
@@ -826,6 +833,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
io_data->buf = data;
io_data->ep = ep->ep;
io_data->req = req;
+ io_data->ffs = epfile->ffs;
req->context = io_data;
req->complete = ffs_epfile_async_io_complete;
@@ -1180,6 +1188,7 @@ struct ffs_sb_fill_data {
struct ffs_file_perms perms;
umode_t root_mode;
const char *dev_name;
+ bool no_disconnect;
struct ffs_data *ffs_data;
};
@@ -1250,6 +1259,12 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
/* Interpret option */
switch (eq - opts) {
+ case 13:
+ if (!memcmp(opts, "no_disconnect", 13))
+ data->no_disconnect = !!value;
+ else
+ goto invalid;
+ break;
case 5:
if (!memcmp(opts, "rmode", 5))
data->root_mode = (value & 0555) | S_IFDIR;
@@ -1314,6 +1329,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
.gid = GLOBAL_ROOT_GID,
},
.root_mode = S_IFDIR | 0500,
+ .no_disconnect = false,
};
struct dentry *rv;
int ret;
@@ -1330,6 +1346,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
if (unlikely(!ffs))
return ERR_PTR(-ENOMEM);
ffs->file_perms = data.perms;
+ ffs->no_disconnect = data.no_disconnect;
ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
if (unlikely(!ffs->dev_name)) {
@@ -1361,6 +1378,7 @@ ffs_fs_kill_sb(struct super_block *sb)
kill_litter_super(sb);
if (sb->s_fs_info) {
ffs_release_dev(sb->s_fs_info);
+ ffs_data_closed(sb->s_fs_info);
ffs_data_put(sb->s_fs_info);
}
}
@@ -1417,7 +1435,11 @@ static void ffs_data_opened(struct ffs_data *ffs)
ENTER();
atomic_inc(&ffs->ref);
- atomic_inc(&ffs->opened);
+ if (atomic_add_return(1, &ffs->opened) == 1 &&
+ ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
}
static void ffs_data_put(struct ffs_data *ffs)
@@ -1439,6 +1461,21 @@ static void ffs_data_closed(struct ffs_data *ffs)
ENTER();
if (atomic_dec_and_test(&ffs->opened)) {
+ if (ffs->no_disconnect) {
+ ffs->state = FFS_DEACTIVATED;
+ if (ffs->epfiles) {
+ ffs_epfiles_destroy(ffs->epfiles,
+ ffs->eps_count);
+ ffs->epfiles = NULL;
+ }
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ __ffs_ep0_stall(ffs);
+ } else {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
+ }
+ if (atomic_read(&ffs->opened) < 0) {
ffs->state = FFS_CLOSING;
ffs_data_reset(ffs);
}
@@ -1480,6 +1517,9 @@ static void ffs_data_clear(struct ffs_data *ffs)
if (ffs->epfiles)
ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+ if (ffs->ffs_eventfd)
+ eventfd_ctx_put(ffs->ffs_eventfd);
+
kfree(ffs->raw_descs_data);
kfree(ffs->raw_strings);
kfree(ffs->stringtabs);
@@ -1581,10 +1621,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
mutex_init(&epfile->mutex);
init_waitqueue_head(&epfile->wait);
if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
- sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]);
+ sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
else
- sprintf(epfiles->name, "ep%u", i);
- epfile->dentry = ffs_sb_create_file(ffs->sb, epfiles->name,
+ sprintf(epfile->name, "ep%u", i);
+ epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
epfile,
&ffs_epfile_operations);
if (unlikely(!epfile->dentry)) {
@@ -1616,7 +1656,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
kfree(epfiles);
}
-
static void ffs_func_eps_disable(struct ffs_function *func)
{
struct ffs_ep *ep = func->eps;
@@ -1629,10 +1668,12 @@ static void ffs_func_eps_disable(struct ffs_function *func)
/* pending requests get nuked */
if (likely(ep->ep))
usb_ep_disable(ep->ep);
- epfile->ep = NULL;
-
++ep;
- ++epfile;
+
+ if (epfile) {
+ epfile->ep = NULL;
+ ++epfile;
+ }
} while (--count);
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
}
@@ -2138,7 +2179,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
FUNCTIONFS_HAS_HS_DESC |
FUNCTIONFS_HAS_SS_DESC |
FUNCTIONFS_HAS_MS_OS_DESC |
- FUNCTIONFS_VIRTUAL_ADDR)) {
+ FUNCTIONFS_VIRTUAL_ADDR |
+ FUNCTIONFS_EVENTFD)) {
ret = -ENOSYS;
goto error;
}
@@ -2149,6 +2191,20 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
goto error;
}
+ if (flags & FUNCTIONFS_EVENTFD) {
+ if (len < 4)
+ goto error;
+ ffs->ffs_eventfd =
+ eventfd_ctx_fdget((int)get_unaligned_le32(data));
+ if (IS_ERR(ffs->ffs_eventfd)) {
+ ret = PTR_ERR(ffs->ffs_eventfd);
+ ffs->ffs_eventfd = NULL;
+ goto error;
+ }
+ data += 4;
+ len -= 4;
+ }
+
/* Read fs_count, hs_count and ss_count (if present) */
for (i = 0; i < 3; ++i) {
if (!(flags & (1 << i))) {
@@ -2377,6 +2433,13 @@ static void __ffs_event_add(struct ffs_data *ffs,
if (ffs->setup_state == FFS_SETUP_PENDING)
ffs->setup_state = FFS_SETUP_CANCELLED;
+ /*
+ * Logic of this function guarantees that there are at most four pending
+ * evens on ffs->ev.types queue. This is important because the queue
+ * has space for four elements only and __ffs_ep0_read_events function
+ * depends on that limit as well. If more event types are added, those
+ * limits have to be revisited or guaranteed to still hold.
+ */
switch (type) {
case FUNCTIONFS_RESUME:
rem_type2 = FUNCTIONFS_SUSPEND;
@@ -2416,6 +2479,8 @@ static void __ffs_event_add(struct ffs_data *ffs,
pr_vdebug("adding event %d\n", type);
ffs->ev.types[ffs->ev.count++] = type;
wake_up_locked(&ffs->ev.waitq);
+ if (ffs->ffs_eventfd)
+ eventfd_signal(ffs->ffs_eventfd, 1);
}
static void ffs_event_add(struct ffs_data *ffs,
@@ -2888,6 +2953,13 @@ static int ffs_func_bind(struct usb_configuration *c,
/* Other USB function hooks *************************************************/
+static void ffs_reset_work(struct work_struct *work)
+{
+ struct ffs_data *ffs = container_of(work,
+ struct ffs_data, reset_work);
+ ffs_data_reset(ffs);
+}
+
static int ffs_func_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
{
@@ -2904,6 +2976,13 @@ static int ffs_func_set_alt(struct usb_function *f,
if (ffs->func)
ffs_func_eps_disable(ffs->func);
+ if (ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ INIT_WORK(&ffs->reset_work, ffs_reset_work);
+ schedule_work(&ffs->reset_work);
+ return -ENODEV;
+ }
+
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index a1bc3e3a0b09..426d69a9c018 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -759,7 +759,7 @@ static struct f_hid_opts_attribute f_hid_opts_##name = \
F_HID_OPT(subclass, 8, 255);
F_HID_OPT(protocol, 8, 255);
-F_HID_OPT(report_length, 16, 65536);
+F_HID_OPT(report_length, 16, 65535);
static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page)
{
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index 80be25b32cd7..e07c50ced64d 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -1214,7 +1214,7 @@ static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->pattern);
+ result = sprintf(page, "%u", opts->pattern);
mutex_unlock(&opts->lock);
return result;
@@ -1258,7 +1258,7 @@ static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->isoc_interval);
+ result = sprintf(page, "%u", opts->isoc_interval);
mutex_unlock(&opts->lock);
return result;
@@ -1302,7 +1302,7 @@ static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->isoc_maxpacket);
+ result = sprintf(page, "%u", opts->isoc_maxpacket);
mutex_unlock(&opts->lock);
return result;
@@ -1346,7 +1346,7 @@ static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->isoc_mult);
+ result = sprintf(page, "%u", opts->isoc_mult);
mutex_unlock(&opts->lock);
return result;
@@ -1390,7 +1390,7 @@ static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->isoc_maxburst);
+ result = sprintf(page, "%u", opts->isoc_maxburst);
mutex_unlock(&opts->lock);
return result;
@@ -1434,7 +1434,7 @@ static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->bulk_buflen);
+ result = sprintf(page, "%u", opts->bulk_buflen);
mutex_unlock(&opts->lock);
return result;
@@ -1473,7 +1473,7 @@ static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->int_interval);
+ result = sprintf(page, "%u", opts->int_interval);
mutex_unlock(&opts->lock);
return result;
@@ -1517,7 +1517,7 @@ static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->int_maxpacket);
+ result = sprintf(page, "%u", opts->int_maxpacket);
mutex_unlock(&opts->lock);
return result;
@@ -1561,7 +1561,7 @@ static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->int_mult);
+ result = sprintf(page, "%u", opts->int_mult);
mutex_unlock(&opts->lock);
return result;
@@ -1605,7 +1605,7 @@ static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page)
int result;
mutex_lock(&opts->lock);
- result = sprintf(page, "%d", opts->int_maxburst);
+ result = sprintf(page, "%u", opts->int_maxburst);
mutex_unlock(&opts->lock);
return result;
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index e9715845f82e..9719abfb6145 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -31,7 +31,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
*/
#define F_AUDIO_AC_INTERFACE 0
#define F_AUDIO_AS_INTERFACE 1
-#define F_AUDIO_NUM_INTERFACES 2
+#define F_AUDIO_NUM_INTERFACES 1
/* B.3.1 Standard AC Interface Descriptor */
static struct usb_interface_descriptor ac_interface_desc = {
@@ -42,14 +42,18 @@ static struct usb_interface_descriptor ac_interface_desc = {
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
};
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
+/*
+ * The number of AudioStreaming and MIDIStreaming interfaces
+ * in the Audio Interface Collection
+ */
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
/* 1 input terminal, 1 output terminal and 1 feature unit */
#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
/* B.3.2 Class-Specific AC Interface Descriptor */
-static struct uac1_ac_header_descriptor_2 ac_header_desc = {
+static struct uac1_ac_header_descriptor_1 ac_header_desc = {
.bLength = UAC_DT_AC_HEADER_LENGTH,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_HEADER,
@@ -57,8 +61,8 @@ static struct uac1_ac_header_descriptor_2 ac_header_desc = {
.wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
.bInCollection = F_AUDIO_NUM_INTERFACES,
.baInterfaceNr = {
- [0] = F_AUDIO_AC_INTERFACE,
- [1] = F_AUDIO_AS_INTERFACE,
+ /* Interface number of the first AudioStream interface */
+ [0] = 1,
}
};
@@ -584,6 +588,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (intf == 1) {
if (alt == 1) {
+ config_ep_by_speed(cdev->gadget, f, out_ep);
usb_ep_enable(out_ep);
out_ep->driver_data = audio;
audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
@@ -669,7 +674,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
audio->card.gadget = c->cdev->gadget;
- audio_opts->card = &audio->card;
/* set up ASLA audio devices */
if (!audio_opts->bound) {
status = gaudio_setup(&audio->card);
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 945b3bd2ca98..76891adfba7a 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -27,10 +27,11 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-event.h>
+#include "u_uvc.h"
#include "uvc.h"
+#include "uvc_configfs.h"
#include "uvc_v4l2.h"
#include "uvc_video.h"
-#include "u_uvc.h"
unsigned int uvc_gadget_trace_param;
@@ -509,6 +510,9 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
break;
}
+ if (!uvc_control_desc || !uvc_streaming_cls)
+ return ERR_PTR(-ENODEV);
+
/* Descriptors layout
*
* uvc_iad
@@ -605,7 +609,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
INFO(cdev, "uvc_function_bind\n");
- opts = to_f_uvc_opts(f->fi);
+ opts = fi_to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters.
*/
opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
@@ -700,10 +704,27 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
/* Copy descriptors */
f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
- if (gadget_is_dualspeed(cdev->gadget))
+ if (IS_ERR(f->fs_descriptors)) {
+ ret = PTR_ERR(f->fs_descriptors);
+ f->fs_descriptors = NULL;
+ goto error;
+ }
+ if (gadget_is_dualspeed(cdev->gadget)) {
f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
- if (gadget_is_superspeed(c->cdev->gadget))
+ if (IS_ERR(f->hs_descriptors)) {
+ ret = PTR_ERR(f->hs_descriptors);
+ f->hs_descriptors = NULL;
+ goto error;
+ }
+ }
+ if (gadget_is_superspeed(c->cdev->gadget)) {
f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
+ if (IS_ERR(f->ss_descriptors)) {
+ ret = PTR_ERR(f->ss_descriptors);
+ f->ss_descriptors = NULL;
+ goto error;
+ }
+ }
/* Preallocate control endpoint request. */
uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
@@ -766,27 +787,106 @@ error:
static void uvc_free_inst(struct usb_function_instance *f)
{
- struct f_uvc_opts *opts = to_f_uvc_opts(f);
+ struct f_uvc_opts *opts = fi_to_f_uvc_opts(f);
+ mutex_destroy(&opts->lock);
kfree(opts);
}
static struct usb_function_instance *uvc_alloc_inst(void)
{
struct f_uvc_opts *opts;
+ struct uvc_camera_terminal_descriptor *cd;
+ struct uvc_processing_unit_descriptor *pd;
+ struct uvc_output_terminal_descriptor *od;
+ struct uvc_color_matching_descriptor *md;
+ struct uvc_descriptor_header **ctl_cls;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = uvc_free_inst;
-
+ mutex_init(&opts->lock);
+
+ cd = &opts->uvc_camera_terminal;
+ cd->bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3);
+ cd->bDescriptorType = USB_DT_CS_INTERFACE;
+ cd->bDescriptorSubType = UVC_VC_INPUT_TERMINAL;
+ cd->bTerminalID = 1;
+ cd->wTerminalType = cpu_to_le16(0x0201);
+ cd->bAssocTerminal = 0;
+ cd->iTerminal = 0;
+ cd->wObjectiveFocalLengthMin = cpu_to_le16(0);
+ cd->wObjectiveFocalLengthMax = cpu_to_le16(0);
+ cd->wOcularFocalLength = cpu_to_le16(0);
+ cd->bControlSize = 3;
+ cd->bmControls[0] = 2;
+ cd->bmControls[1] = 0;
+ cd->bmControls[2] = 0;
+
+ pd = &opts->uvc_processing;
+ pd->bLength = UVC_DT_PROCESSING_UNIT_SIZE(2);
+ pd->bDescriptorType = USB_DT_CS_INTERFACE;
+ pd->bDescriptorSubType = UVC_VC_PROCESSING_UNIT;
+ pd->bUnitID = 2;
+ pd->bSourceID = 1;
+ pd->wMaxMultiplier = cpu_to_le16(16*1024);
+ pd->bControlSize = 2;
+ pd->bmControls[0] = 1;
+ pd->bmControls[1] = 0;
+ pd->iProcessing = 0;
+
+ od = &opts->uvc_output_terminal;
+ od->bLength = UVC_DT_OUTPUT_TERMINAL_SIZE;
+ od->bDescriptorType = USB_DT_CS_INTERFACE;
+ od->bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL;
+ od->bTerminalID = 3;
+ od->wTerminalType = cpu_to_le16(0x0101);
+ od->bAssocTerminal = 0;
+ od->bSourceID = 2;
+ od->iTerminal = 0;
+
+ md = &opts->uvc_color_matching;
+ md->bLength = UVC_DT_COLOR_MATCHING_SIZE;
+ md->bDescriptorType = USB_DT_CS_INTERFACE;
+ md->bDescriptorSubType = UVC_VS_COLORFORMAT;
+ md->bColorPrimaries = 1;
+ md->bTransferCharacteristics = 1;
+ md->bMatrixCoefficients = 4;
+
+ /* Prepare fs control class descriptors for configfs-based gadgets */
+ ctl_cls = opts->uvc_fs_control_cls;
+ ctl_cls[0] = NULL; /* assigned elsewhere by configfs */
+ ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+ ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+ ctl_cls[3] = (struct uvc_descriptor_header *)od;
+ ctl_cls[4] = NULL; /* NULL-terminate */
+ opts->fs_control =
+ (const struct uvc_descriptor_header * const *)ctl_cls;
+
+ /* Prepare hs control class descriptors for configfs-based gadgets */
+ ctl_cls = opts->uvc_ss_control_cls;
+ ctl_cls[0] = NULL; /* assigned elsewhere by configfs */
+ ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+ ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+ ctl_cls[3] = (struct uvc_descriptor_header *)od;
+ ctl_cls[4] = NULL; /* NULL-terminate */
+ opts->ss_control =
+ (const struct uvc_descriptor_header * const *)ctl_cls;
+
+ opts->streaming_interval = 1;
+ opts->streaming_maxpacket = 1024;
+
+ uvcg_attach_configfs(opts);
return &opts->func_inst;
}
static void uvc_free(struct usb_function *f)
{
struct uvc_device *uvc = to_uvc(f);
-
+ struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
+ func_inst);
+ --opts->refcnt;
kfree(uvc);
}
@@ -812,19 +912,39 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
{
struct uvc_device *uvc;
struct f_uvc_opts *opts;
+ struct uvc_descriptor_header **strm_cls;
uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
if (uvc == NULL)
return ERR_PTR(-ENOMEM);
uvc->state = UVC_STATE_DISCONNECTED;
- opts = to_f_uvc_opts(fi);
+ opts = fi_to_f_uvc_opts(fi);
+
+ mutex_lock(&opts->lock);
+ if (opts->uvc_fs_streaming_cls) {
+ strm_cls = opts->uvc_fs_streaming_cls;
+ opts->fs_streaming =
+ (const struct uvc_descriptor_header * const *)strm_cls;
+ }
+ if (opts->uvc_hs_streaming_cls) {
+ strm_cls = opts->uvc_hs_streaming_cls;
+ opts->hs_streaming =
+ (const struct uvc_descriptor_header * const *)strm_cls;
+ }
+ if (opts->uvc_ss_streaming_cls) {
+ strm_cls = opts->uvc_ss_streaming_cls;
+ opts->ss_streaming =
+ (const struct uvc_descriptor_header * const *)strm_cls;
+ }
uvc->desc.fs_control = opts->fs_control;
uvc->desc.ss_control = opts->ss_control;
uvc->desc.fs_streaming = opts->fs_streaming;
uvc->desc.hs_streaming = opts->hs_streaming;
uvc->desc.ss_streaming = opts->ss_streaming;
+ ++opts->refcnt;
+ mutex_unlock(&opts->lock);
/* Register the function. */
uvc->func.name = "uvc";
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 6e6f87656e7b..f1fd777ef4ec 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -729,9 +729,7 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
if (len < 18)
return -EINVAL;
- snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
- dev_addr[0], dev_addr[1], dev_addr[2],
- dev_addr[3], dev_addr[4], dev_addr[5]);
+ snprintf(str, len, "%pM", dev_addr);
return 18;
}
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index cd128e31f808..60139854e0b1 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -19,6 +19,7 @@
#include <linux/usb/composite.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#ifdef VERBOSE_DEBUG
#ifndef pr_vdebug
@@ -93,6 +94,26 @@ enum ffs_state {
FFS_ACTIVE,
/*
+ * Function is visible to host, but it's not functional. All
+ * setup requests are stalled and transfers on another endpoints
+ * are refused. All epfiles, except ep0, are deleted so there
+ * is no way to perform any operations on them.
+ *
+ * This state is set after closing all functionfs files, when
+ * mount parameter "no_disconnect=1" has been set. Function will
+ * remain in deactivated state until filesystem is umounted or
+ * ep0 is opened again. In the second case functionfs state will
+ * be reset, and it will be ready for descriptors and strings
+ * writing.
+ *
+ * This is useful only when functionfs is composed to gadget
+ * with another function which can perform some critical
+ * operations, and it's strongly desired to have this operations
+ * completed, even after functionfs files closure.
+ */
+ FFS_DEACTIVATED,
+
+ /*
* All endpoints have been closed. This state is also set if
* we encounter an unrecoverable error. The only
* unrecoverable error is situation when after reading strings
@@ -251,6 +272,10 @@ struct ffs_data {
kgid_t gid;
} file_perms;
+ struct eventfd_ctx *ffs_eventfd;
+ bool no_disconnect;
+ struct work_struct reset_work;
+
/*
* The endpoint files, filled by ffs_epfiles_create(),
* destroyed by ffs_epfiles_destroy().
diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
index 53842a1b947f..c78c84138a28 100644
--- a/drivers/usb/gadget/function/u_uac1.c
+++ b/drivers/usb/gadget/function/u_uac1.c
@@ -308,8 +308,7 @@ int gaudio_setup(struct gaudio *card)
*/
void gaudio_cleanup(struct gaudio *the_card)
{
- if (the_card) {
+ if (the_card)
gaudio_close_snd_dev(the_card);
- }
}
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index f8b17fe82efe..fe386df6dd3e 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -70,7 +70,6 @@ struct f_uac1_opts {
unsigned fn_play_alloc:1;
unsigned fn_cap_alloc:1;
unsigned fn_cntl_alloc:1;
- struct gaudio *card;
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index 2a8dfdff0332..4676b60a5063 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -17,8 +17,9 @@
#define U_UVC_H
#include <linux/usb/composite.h>
+#include <linux/usb/video.h>
-#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst)
+#define fi_to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst)
struct f_uvc_opts {
struct usb_function_instance func_inst;
@@ -26,11 +27,60 @@ struct f_uvc_opts {
unsigned int streaming_interval;
unsigned int streaming_maxpacket;
unsigned int streaming_maxburst;
+
+ /*
+ * Control descriptors array pointers for full-/high-speed and
+ * super-speed. They point by default to the uvc_fs_control_cls and
+ * uvc_ss_control_cls arrays respectively. Legacy gadgets must
+ * override them in their gadget bind callback.
+ */
const struct uvc_descriptor_header * const *fs_control;
const struct uvc_descriptor_header * const *ss_control;
+
+ /*
+ * Streaming descriptors array pointers for full-speed, high-speed and
+ * super-speed. They will point to the uvc_[fhs]s_streaming_cls arrays
+ * for configfs-based gadgets. Legacy gadgets must initialize them in
+ * their gadget bind callback.
+ */
const struct uvc_descriptor_header * const *fs_streaming;
const struct uvc_descriptor_header * const *hs_streaming;
const struct uvc_descriptor_header * const *ss_streaming;
+
+ /* Default control descriptors for configfs-based gadgets. */
+ struct uvc_camera_terminal_descriptor uvc_camera_terminal;
+ struct uvc_processing_unit_descriptor uvc_processing;
+ struct uvc_output_terminal_descriptor uvc_output_terminal;
+ struct uvc_color_matching_descriptor uvc_color_matching;
+
+ /*
+ * Control descriptors pointers arrays for full-/high-speed and
+ * super-speed. The first element is a configurable control header
+ * descriptor, the other elements point to the fixed default control
+ * descriptors. Used by configfs only, must not be touched by legacy
+ * gadgets.
+ */
+ struct uvc_descriptor_header *uvc_fs_control_cls[5];
+ struct uvc_descriptor_header *uvc_ss_control_cls[5];
+
+ /*
+ * Streaming descriptors for full-speed, high-speed and super-speed.
+ * Used by configfs only, must not be touched by legacy gadgets. The
+ * arrays are allocated at runtime as the number of descriptors isn't
+ * known in advance.
+ */
+ struct uvc_descriptor_header **uvc_fs_streaming_cls;
+ struct uvc_descriptor_header **uvc_hs_streaming_cls;
+ struct uvc_descriptor_header **uvc_ss_streaming_cls;
+
+ /*
+ * Read/write access to configfs attributes is handled by configfs.
+ *
+ * This lock protects the descriptors from concurrent access by
+ * read/write and symlink creation/removal.
+ */
+ struct mutex lock;
+ int refcnt;
};
void uvc_set_trace_param(unsigned int trace);
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
new file mode 100644
index 000000000000..3c0467bcb14f
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -0,0 +1,2468 @@
+/*
+ * uvc_configfs.c
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 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.
+ */
+#include "u_uvc.h"
+#include "uvc_configfs.h"
+
+#define UVCG_STREAMING_CONTROL_SIZE 1
+
+#define CONFIGFS_ATTR_OPS_RO(_item) \
+static ssize_t _item##_attr_show(struct config_item *item, \
+ struct configfs_attribute *attr, \
+ char *page) \
+{ \
+ struct _item *_item = to_##_item(item); \
+ struct _item##_attribute *_item##_attr = \
+ container_of(attr, struct _item##_attribute, attr); \
+ ssize_t ret = 0; \
+ \
+ if (_item##_attr->show) \
+ ret = _item##_attr->show(_item, page); \
+ return ret; \
+}
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item);
+
+/* control/header/<NAME> */
+DECLARE_UVC_HEADER_DESCRIPTOR(1);
+
+struct uvcg_control_header {
+ struct config_item item;
+ struct UVC_HEADER_DESCRIPTOR(1) desc;
+ unsigned linked;
+};
+
+static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item)
+{
+ return container_of(item, struct uvcg_control_header, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_control_header);
+CONFIGFS_ATTR_OPS(uvcg_control_header);
+
+static struct configfs_item_operations uvcg_control_header_item_ops = {
+ .show_attribute = uvcg_control_header_attr_show,
+ .store_attribute = uvcg_control_header_attr_store,
+};
+
+#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \
+static ssize_t uvcg_control_header_##cname##_show( \
+ struct uvcg_control_header *ch, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = ch->item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(ch->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static ssize_t \
+uvcg_control_header_##cname##_store(struct uvcg_control_header *ch, \
+ const char *page, size_t len) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+ int ret; \
+ uxx num; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = ch->item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ if (ch->linked || opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = str2u(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > limit) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ ch->desc.aname = vnoc(num); \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ mutex_unlock(su_mutex); \
+ return ret; \
+} \
+ \
+static struct uvcg_control_header_attribute \
+ uvcg_control_header_##cname = \
+ __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \
+ uvcg_control_header_##cname##_show, \
+ uvcg_control_header_##cname##_store)
+
+UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
+ 0xffff);
+
+UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32,
+ u32, cpu_to_le32, 0x7fffffff);
+
+#undef UVCG_CTRL_HDR_ATTR
+
+static struct configfs_attribute *uvcg_control_header_attrs[] = {
+ &uvcg_control_header_bcd_uvc.attr,
+ &uvcg_control_header_dw_clock_frequency.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_control_header_type = {
+ .ct_item_ops = &uvcg_control_header_item_ops,
+ .ct_attrs = uvcg_control_header_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *uvcg_control_header_make(struct config_group *group,
+ const char *name)
+{
+ struct uvcg_control_header *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ h->desc.bLength = UVC_DT_HEADER_SIZE(1);
+ h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ h->desc.bDescriptorSubType = UVC_VC_HEADER;
+ h->desc.bcdUVC = cpu_to_le16(0x0100);
+ h->desc.dwClockFrequency = cpu_to_le32(48000000);
+
+ config_item_init_type_name(&h->item, name, &uvcg_control_header_type);
+
+ return &h->item;
+}
+
+static void uvcg_control_header_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct uvcg_control_header *h = to_uvcg_control_header(item);
+
+ kfree(h);
+}
+
+/* control/header */
+static struct uvcg_control_header_grp {
+ struct config_group group;
+} uvcg_control_header_grp;
+
+static struct configfs_group_operations uvcg_control_header_grp_ops = {
+ .make_item = uvcg_control_header_make,
+ .drop_item = uvcg_control_header_drop,
+};
+
+static struct config_item_type uvcg_control_header_grp_type = {
+ .ct_group_ops = &uvcg_control_header_grp_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* control/processing/default */
+static struct uvcg_default_processing {
+ struct config_group group;
+} uvcg_default_processing;
+
+static inline struct uvcg_default_processing
+*to_uvcg_default_processing(struct config_item *item)
+{
+ return container_of(to_config_group(item),
+ struct uvcg_default_processing, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_processing);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_processing);
+
+static struct configfs_item_operations uvcg_default_processing_item_ops = {
+ .show_attribute = uvcg_default_processing_attr_show,
+};
+
+#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \
+static ssize_t uvcg_default_processing_##cname##_show( \
+ struct uvcg_default_processing *dp, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; \
+ struct uvc_processing_unit_descriptor *pd; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ pd = &opts->uvc_processing; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(pd->aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_default_processing_attribute \
+ uvcg_default_processing_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_default_processing_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv);
+UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv);
+UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu);
+UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_PROCESSING_ATTR
+
+static ssize_t uvcg_default_processing_bm_controls_show(
+ struct uvcg_default_processing *dp, char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;
+ struct uvc_processing_unit_descriptor *pd;
+ int result, i;
+ char *pg = page;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+ pd = &opts->uvc_processing;
+
+ mutex_lock(&opts->lock);
+ for (result = 0, i = 0; i < pd->bControlSize; ++i) {
+ result += sprintf(pg, "%d\n", pd->bmControls[i]);
+ pg = page + result;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return result;
+}
+
+static struct uvcg_default_processing_attribute
+ uvcg_default_processing_bm_controls =
+ __CONFIGFS_ATTR_RO(bmControls,
+ uvcg_default_processing_bm_controls_show);
+
+static struct configfs_attribute *uvcg_default_processing_attrs[] = {
+ &uvcg_default_processing_b_unit_id.attr,
+ &uvcg_default_processing_b_source_id.attr,
+ &uvcg_default_processing_w_max_multiplier.attr,
+ &uvcg_default_processing_bm_controls.attr,
+ &uvcg_default_processing_i_processing.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_default_processing_type = {
+ .ct_item_ops = &uvcg_default_processing_item_ops,
+ .ct_attrs = uvcg_default_processing_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* struct uvcg_processing {}; */
+
+static struct config_group *uvcg_processing_default_groups[] = {
+ &uvcg_default_processing.group,
+ NULL,
+};
+
+/* control/processing */
+static struct uvcg_processing_grp {
+ struct config_group group;
+} uvcg_processing_grp;
+
+static struct config_item_type uvcg_processing_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* control/terminal/camera/default */
+static struct uvcg_default_camera {
+ struct config_group group;
+} uvcg_default_camera;
+
+static inline struct uvcg_default_camera
+*to_uvcg_default_camera(struct config_item *item)
+{
+ return container_of(to_config_group(item),
+ struct uvcg_default_camera, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_camera);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_camera);
+
+static struct configfs_item_operations uvcg_default_camera_item_ops = {
+ .show_attribute = uvcg_default_camera_attr_show,
+};
+
+#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \
+static ssize_t uvcg_default_camera_##cname##_show( \
+ struct uvcg_default_camera *dc, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \
+ struct uvc_camera_terminal_descriptor *cd; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> \
+ ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ cd = &opts->uvc_camera_terminal; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(cd->aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ \
+ return result; \
+} \
+ \
+static struct uvcg_default_camera_attribute \
+ uvcg_default_camera_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_default_camera_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin,
+ le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax,
+ le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength,
+ le16_to_cpu);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_CAMERA_ATTR
+
+static ssize_t uvcg_default_camera_bm_controls_show(
+ struct uvcg_default_camera *dc, char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;
+ struct uvc_camera_terminal_descriptor *cd;
+ int result, i;
+ char *pg = page;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent->
+ ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+ cd = &opts->uvc_camera_terminal;
+
+ mutex_lock(&opts->lock);
+ for (result = 0, i = 0; i < cd->bControlSize; ++i) {
+ result += sprintf(pg, "%d\n", cd->bmControls[i]);
+ pg = page + result;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+ return result;
+}
+
+static struct uvcg_default_camera_attribute
+ uvcg_default_camera_bm_controls =
+ __CONFIGFS_ATTR_RO(bmControls, uvcg_default_camera_bm_controls_show);
+
+static struct configfs_attribute *uvcg_default_camera_attrs[] = {
+ &uvcg_default_camera_b_terminal_id.attr,
+ &uvcg_default_camera_w_terminal_type.attr,
+ &uvcg_default_camera_b_assoc_terminal.attr,
+ &uvcg_default_camera_i_terminal.attr,
+ &uvcg_default_camera_w_objective_focal_length_min.attr,
+ &uvcg_default_camera_w_objective_focal_length_max.attr,
+ &uvcg_default_camera_w_ocular_focal_length.attr,
+ &uvcg_default_camera_bm_controls.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_default_camera_type = {
+ .ct_item_ops = &uvcg_default_camera_item_ops,
+ .ct_attrs = uvcg_default_camera_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* struct uvcg_camera {}; */
+
+static struct config_group *uvcg_camera_default_groups[] = {
+ &uvcg_default_camera.group,
+ NULL,
+};
+
+/* control/terminal/camera */
+static struct uvcg_camera_grp {
+ struct config_group group;
+} uvcg_camera_grp;
+
+static struct config_item_type uvcg_camera_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* control/terminal/output/default */
+static struct uvcg_default_output {
+ struct config_group group;
+} uvcg_default_output;
+
+static inline struct uvcg_default_output
+*to_uvcg_default_output(struct config_item *item)
+{
+ return container_of(to_config_group(item),
+ struct uvcg_default_output, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_output);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_output);
+
+static struct configfs_item_operations uvcg_default_output_item_ops = {
+ .show_attribute = uvcg_default_output_attr_show,
+};
+
+#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \
+static ssize_t uvcg_default_output_##cname##_show( \
+ struct uvcg_default_output *dout, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex; \
+ struct uvc_output_terminal_descriptor *cd; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = dout->group.cg_item.ci_parent->ci_parent-> \
+ ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ cd = &opts->uvc_output_terminal; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(cd->aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ \
+ return result; \
+} \
+ \
+static struct uvcg_default_output_attribute \
+ uvcg_default_output_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_default_output_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
+UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_OUTPUT_ATTR
+
+static struct configfs_attribute *uvcg_default_output_attrs[] = {
+ &uvcg_default_output_b_terminal_id.attr,
+ &uvcg_default_output_w_terminal_type.attr,
+ &uvcg_default_output_b_assoc_terminal.attr,
+ &uvcg_default_output_b_source_id.attr,
+ &uvcg_default_output_i_terminal.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_default_output_type = {
+ .ct_item_ops = &uvcg_default_output_item_ops,
+ .ct_attrs = uvcg_default_output_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* struct uvcg_output {}; */
+
+static struct config_group *uvcg_output_default_groups[] = {
+ &uvcg_default_output.group,
+ NULL,
+};
+
+/* control/terminal/output */
+static struct uvcg_output_grp {
+ struct config_group group;
+} uvcg_output_grp;
+
+static struct config_item_type uvcg_output_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_terminal_default_groups[] = {
+ &uvcg_camera_grp.group,
+ &uvcg_output_grp.group,
+ NULL,
+};
+
+/* control/terminal */
+static struct uvcg_terminal_grp {
+ struct config_group group;
+} uvcg_terminal_grp;
+
+static struct config_item_type uvcg_terminal_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* control/class/{fs} */
+static struct uvcg_control_class {
+ struct config_group group;
+} uvcg_control_class_fs, uvcg_control_class_ss;
+
+
+static inline struct uvc_descriptor_header
+**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o)
+{
+ struct uvcg_control_class *cl = container_of(to_config_group(i),
+ struct uvcg_control_class, group);
+
+ if (cl == &uvcg_control_class_fs)
+ return o->uvc_fs_control_cls;
+
+ if (cl == &uvcg_control_class_ss)
+ return o->uvc_ss_control_cls;
+
+ return NULL;
+}
+
+static int uvcg_control_class_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_item *control, *header;
+ struct f_uvc_opts *opts;
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvc_descriptor_header **class_array;
+ struct uvcg_control_header *target_hdr;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ control = src->ci_parent->ci_parent;
+ header = config_group_find_item(to_config_group(control), "header");
+ if (!header || target->ci_parent != header)
+ goto out;
+
+ opts = to_f_uvc_opts(control->ci_parent);
+
+ mutex_lock(&opts->lock);
+
+ class_array = uvcg_get_ctl_class_arr(src, opts);
+ if (!class_array)
+ goto unlock;
+ if (opts->refcnt || class_array[0]) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ target_hdr = to_uvcg_control_header(target);
+ ++target_hdr->linked;
+ class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&opts->lock);
+out:
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static int uvcg_control_class_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_item *control, *header;
+ struct f_uvc_opts *opts;
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvc_descriptor_header **class_array;
+ struct uvcg_control_header *target_hdr;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ control = src->ci_parent->ci_parent;
+ header = config_group_find_item(to_config_group(control), "header");
+ if (!header || target->ci_parent != header)
+ goto out;
+
+ opts = to_f_uvc_opts(control->ci_parent);
+
+ mutex_lock(&opts->lock);
+
+ class_array = uvcg_get_ctl_class_arr(src, opts);
+ if (!class_array)
+ goto unlock;
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ target_hdr = to_uvcg_control_header(target);
+ --target_hdr->linked;
+ class_array[0] = NULL;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&opts->lock);
+out:
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static struct configfs_item_operations uvcg_control_class_item_ops = {
+ .allow_link = uvcg_control_class_allow_link,
+ .drop_link = uvcg_control_class_drop_link,
+};
+
+static struct config_item_type uvcg_control_class_type = {
+ .ct_item_ops = &uvcg_control_class_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_control_class_default_groups[] = {
+ &uvcg_control_class_fs.group,
+ &uvcg_control_class_ss.group,
+ NULL,
+};
+
+/* control/class */
+static struct uvcg_control_class_grp {
+ struct config_group group;
+} uvcg_control_class_grp;
+
+static struct config_item_type uvcg_control_class_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_control_default_groups[] = {
+ &uvcg_control_header_grp.group,
+ &uvcg_processing_grp.group,
+ &uvcg_terminal_grp.group,
+ &uvcg_control_class_grp.group,
+ NULL,
+};
+
+/* control */
+static struct uvcg_control_grp {
+ struct config_group group;
+} uvcg_control_grp;
+
+static struct config_item_type uvcg_control_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/uncompressed */
+static struct uvcg_uncompressed_grp {
+ struct config_group group;
+} uvcg_uncompressed_grp;
+
+/* streaming/mjpeg */
+static struct uvcg_mjpeg_grp {
+ struct config_group group;
+} uvcg_mjpeg_grp;
+
+static struct config_item *fmt_parent[] = {
+ &uvcg_uncompressed_grp.group.cg_item,
+ &uvcg_mjpeg_grp.group.cg_item,
+};
+
+enum uvcg_format_type {
+ UVCG_UNCOMPRESSED = 0,
+ UVCG_MJPEG,
+};
+
+struct uvcg_format {
+ struct config_group group;
+ enum uvcg_format_type type;
+ unsigned linked;
+ unsigned num_frames;
+ __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE];
+};
+
+static struct uvcg_format *to_uvcg_format(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct uvcg_format, group);
+}
+
+static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &f->group.cg_subsys->su_mutex;
+ int result, i;
+ char *pg = page;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ result = sprintf(pg, "0x");
+ pg += result;
+ for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) {
+ result += sprintf(pg, "%x\n", f->bmaControls[i]);
+ pg = page + result;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+ return result;
+}
+
+static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch,
+ const char *page, size_t len)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ if (ch->linked || opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ if (len < 4 || *page != '0' ||
+ (*(page + 1) != 'x' && *(page + 1) != 'X'))
+ goto end;
+ ret = hex2bin(ch->bmaControls, page + 2, 1);
+ if (ret < 0)
+ goto end;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+struct uvcg_format_ptr {
+ struct uvcg_format *fmt;
+ struct list_head entry;
+};
+
+/* streaming/header/<NAME> */
+struct uvcg_streaming_header {
+ struct config_item item;
+ struct uvc_input_header_descriptor desc;
+ unsigned linked;
+ struct list_head formats;
+ unsigned num_fmt;
+};
+
+static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
+{
+ return container_of(item, struct uvcg_streaming_header, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_streaming_header);
+CONFIGFS_ATTR_OPS(uvcg_streaming_header);
+
+static int uvcg_streaming_header_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ struct uvcg_streaming_header *src_hdr;
+ struct uvcg_format *target_fmt = NULL;
+ struct uvcg_format_ptr *format_ptr;
+ int i, ret = -EINVAL;
+
+ src_hdr = to_uvcg_streaming_header(src);
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = src->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+
+ if (src_hdr->linked) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i)
+ if (target->ci_parent == fmt_parent[i])
+ break;
+ if (i == ARRAY_SIZE(fmt_parent))
+ goto out;
+
+ target_fmt = container_of(to_config_group(target), struct uvcg_format,
+ group);
+ if (!target_fmt)
+ goto out;
+
+ format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL);
+ if (!format_ptr) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = 0;
+ format_ptr->fmt = target_fmt;
+ list_add_tail(&format_ptr->entry, &src_hdr->formats);
+ ++src_hdr->num_fmt;
+
+out:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static int uvcg_streaming_header_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct config_item *opts_item;
+ struct f_uvc_opts *opts;
+ struct uvcg_streaming_header *src_hdr;
+ struct uvcg_format *target_fmt = NULL;
+ struct uvcg_format_ptr *format_ptr, *tmp;
+ int ret = -EINVAL;
+
+ src_hdr = to_uvcg_streaming_header(src);
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = src->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ target_fmt = container_of(to_config_group(target), struct uvcg_format,
+ group);
+ if (!target_fmt)
+ goto out;
+
+ list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry)
+ if (format_ptr->fmt == target_fmt) {
+ list_del(&format_ptr->entry);
+ kfree(format_ptr);
+ --src_hdr->num_fmt;
+ break;
+ }
+
+out:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+
+}
+
+static struct configfs_item_operations uvcg_streaming_header_item_ops = {
+ .show_attribute = uvcg_streaming_header_attr_show,
+ .store_attribute = uvcg_streaming_header_attr_store,
+ .allow_link = uvcg_streaming_header_allow_link,
+ .drop_link = uvcg_streaming_header_drop_link,
+};
+
+#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv) \
+static ssize_t uvcg_streaming_header_##cname##_show( \
+ struct uvcg_streaming_header *sh, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = sh->item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(sh->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_streaming_header_attribute \
+ uvcg_streaming_header_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_streaming_header_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod,
+ identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_STREAMING_HEADER_ATTR
+
+static struct configfs_attribute *uvcg_streaming_header_attrs[] = {
+ &uvcg_streaming_header_bm_info.attr,
+ &uvcg_streaming_header_b_terminal_link.attr,
+ &uvcg_streaming_header_b_still_capture_method.attr,
+ &uvcg_streaming_header_b_trigger_support.attr,
+ &uvcg_streaming_header_b_trigger_usage.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_streaming_header_type = {
+ .ct_item_ops = &uvcg_streaming_header_item_ops,
+ .ct_attrs = uvcg_streaming_header_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item
+*uvcg_streaming_header_make(struct config_group *group, const char *name)
+{
+ struct uvcg_streaming_header *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&h->formats);
+ h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ h->desc.bDescriptorSubType = UVC_VS_INPUT_HEADER;
+ h->desc.bTerminalLink = 3;
+ h->desc.bControlSize = UVCG_STREAMING_CONTROL_SIZE;
+
+ config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type);
+
+ return &h->item;
+}
+
+static void uvcg_streaming_header_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct uvcg_streaming_header *h = to_uvcg_streaming_header(item);
+
+ kfree(h);
+}
+
+/* streaming/header */
+static struct uvcg_streaming_header_grp {
+ struct config_group group;
+} uvcg_streaming_header_grp;
+
+static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
+ .make_item = uvcg_streaming_header_make,
+ .drop_item = uvcg_streaming_header_drop,
+};
+
+static struct config_item_type uvcg_streaming_header_grp_type = {
+ .ct_group_ops = &uvcg_streaming_header_grp_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/<mode>/<format>/<NAME> */
+struct uvcg_frame {
+ struct {
+ u8 b_length;
+ u8 b_descriptor_type;
+ u8 b_descriptor_subtype;
+ u8 b_frame_index;
+ u8 bm_capabilities;
+ u16 w_width;
+ u16 w_height;
+ u32 dw_min_bit_rate;
+ u32 dw_max_bit_rate;
+ u32 dw_max_video_frame_buffer_size;
+ u32 dw_default_frame_interval;
+ u8 b_frame_interval_type;
+ } __attribute__((packed)) frame;
+ u32 *dw_frame_interval;
+ enum uvcg_format_type fmt_type;
+ struct config_item item;
+};
+
+static struct uvcg_frame *to_uvcg_frame(struct config_item *item)
+{
+ return container_of(item, struct uvcg_frame, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_frame);
+CONFIGFS_ATTR_OPS(uvcg_frame);
+
+static struct configfs_item_operations uvcg_frame_item_ops = {
+ .show_attribute = uvcg_frame_attr_show,
+ .store_attribute = uvcg_frame_attr_store,
+};
+
+#define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \
+static ssize_t uvcg_frame_##cname##_show(struct uvcg_frame *f, char *page)\
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static ssize_t uvcg_frame_##cname##_store(struct uvcg_frame *f, \
+ const char *page, size_t len)\
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct uvcg_format *fmt; \
+ struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+ int ret; \
+ u##bits num; \
+ \
+ ret = kstrtou##bits(page, 0, &num); \
+ if (ret) \
+ return ret; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ fmt = to_uvcg_format(f->item.ci_parent); \
+ \
+ mutex_lock(&opts->lock); \
+ if (fmt->linked || opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ f->frame.cname = to_little_endian(num); \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ mutex_unlock(su_mutex); \
+ return ret; \
+} \
+ \
+static struct uvcg_frame_attribute \
+ uvcg_frame_##cname = \
+ __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \
+ uvcg_frame_##cname##_show, \
+ uvcg_frame_##cname##_store)
+
+#define noop_conversion(x) (x)
+
+UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion,
+ noop_conversion, 8);
+UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize,
+ le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval,
+ le32_to_cpu, cpu_to_le32, 32);
+
+#undef noop_conversion
+
+#undef UVCG_FRAME_ATTR
+
+static ssize_t uvcg_frame_dw_frame_interval_show(struct uvcg_frame *frm,
+ char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex;
+ int result, i;
+ char *pg = page;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) {
+ result += sprintf(pg, "%d\n",
+ le32_to_cpu(frm->dw_frame_interval[i]));
+ pg = page + result;
+ }
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+ return result;
+}
+
+static inline int __uvcg_count_frm_intrv(char *buf, void *priv)
+{
+ ++*((int *)priv);
+ return 0;
+}
+
+static inline int __uvcg_fill_frm_intrv(char *buf, void *priv)
+{
+ u32 num, **interv;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &num);
+ if (ret)
+ return ret;
+
+ interv = priv;
+ **interv = cpu_to_le32(num);
+ ++*interv;
+
+ return 0;
+}
+
+static int __uvcg_iter_frm_intrv(const char *page, size_t len,
+ int (*fun)(char *, void *), void *priv)
+{
+ /* sign, base 2 representation, newline, terminator */
+ char buf[1 + sizeof(u32) * 8 + 1 + 1];
+ const char *pg = page;
+ int i, ret;
+
+ if (!fun)
+ return -EINVAL;
+
+ while (pg - page < len) {
+ i = 0;
+ while (i < sizeof(buf) && (pg - page < len) &&
+ *pg != '\0' && *pg != '\n')
+ buf[i++] = *pg++;
+ if (i == sizeof(buf))
+ return -EINVAL;
+ while ((pg - page < len) && (*pg == '\0' || *pg == '\n'))
+ ++pg;
+ buf[i] = '\0';
+ ret = fun(buf, priv);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t uvcg_frame_dw_frame_interval_store(struct uvcg_frame *ch,
+ const char *page, size_t len)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct uvcg_format *fmt;
+ struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;
+ int ret = 0, n = 0;
+ u32 *frm_intrv, *tmp;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+ fmt = to_uvcg_format(ch->item.ci_parent);
+
+ mutex_lock(&opts->lock);
+ if (fmt->linked || opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n);
+ if (ret)
+ goto end;
+
+ tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL);
+ if (!frm_intrv) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp);
+ if (ret) {
+ kfree(frm_intrv);
+ goto end;
+ }
+
+ kfree(ch->dw_frame_interval);
+ ch->dw_frame_interval = frm_intrv;
+ ch->frame.b_frame_interval_type = n;
+ ret = len;
+
+end:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static struct uvcg_frame_attribute
+ uvcg_frame_dw_frame_interval =
+ __CONFIGFS_ATTR(dwFrameInterval, S_IRUGO | S_IWUSR,
+ uvcg_frame_dw_frame_interval_show,
+ uvcg_frame_dw_frame_interval_store);
+
+static struct configfs_attribute *uvcg_frame_attrs[] = {
+ &uvcg_frame_bm_capabilities.attr,
+ &uvcg_frame_w_width.attr,
+ &uvcg_frame_w_height.attr,
+ &uvcg_frame_dw_min_bit_rate.attr,
+ &uvcg_frame_dw_max_bit_rate.attr,
+ &uvcg_frame_dw_max_video_frame_buffer_size.attr,
+ &uvcg_frame_dw_default_frame_interval.attr,
+ &uvcg_frame_dw_frame_interval.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_frame_type = {
+ .ct_item_ops = &uvcg_frame_item_ops,
+ .ct_attrs = uvcg_frame_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *uvcg_frame_make(struct config_group *group,
+ const char *name)
+{
+ struct uvcg_frame *h;
+ struct uvcg_format *fmt;
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ h->frame.b_descriptor_type = USB_DT_CS_INTERFACE;
+ h->frame.b_frame_index = 1;
+ h->frame.w_width = cpu_to_le16(640);
+ h->frame.w_height = cpu_to_le16(360);
+ h->frame.dw_min_bit_rate = cpu_to_le32(18432000);
+ h->frame.dw_max_bit_rate = cpu_to_le32(55296000);
+ h->frame.dw_max_video_frame_buffer_size = cpu_to_le32(460800);
+ h->frame.dw_default_frame_interval = cpu_to_le32(666666);
+
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ fmt = to_uvcg_format(&group->cg_item);
+ if (fmt->type == UVCG_UNCOMPRESSED) {
+ h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED;
+ h->fmt_type = UVCG_UNCOMPRESSED;
+ } else if (fmt->type == UVCG_MJPEG) {
+ h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG;
+ h->fmt_type = UVCG_MJPEG;
+ } else {
+ mutex_unlock(&opts->lock);
+ kfree(h);
+ return ERR_PTR(-EINVAL);
+ }
+ ++fmt->num_frames;
+ mutex_unlock(&opts->lock);
+
+ config_item_init_type_name(&h->item, name, &uvcg_frame_type);
+
+ return &h->item;
+}
+
+static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
+{
+ struct uvcg_frame *h = to_uvcg_frame(item);
+ struct uvcg_format *fmt;
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+
+ opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ fmt = to_uvcg_format(&group->cg_item);
+ --fmt->num_frames;
+ kfree(h);
+ mutex_unlock(&opts->lock);
+}
+
+/* streaming/uncompressed/<NAME> */
+struct uvcg_uncompressed {
+ struct uvcg_format fmt;
+ struct uvc_format_uncompressed desc;
+};
+
+static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item)
+{
+ return container_of(
+ container_of(to_config_group(item), struct uvcg_format, group),
+ struct uvcg_uncompressed, fmt);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_uncompressed);
+CONFIGFS_ATTR_OPS(uvcg_uncompressed);
+
+static struct configfs_item_operations uvcg_uncompressed_item_ops = {
+ .show_attribute = uvcg_uncompressed_attr_show,
+ .store_attribute = uvcg_uncompressed_attr_store,
+};
+
+static struct configfs_group_operations uvcg_uncompressed_group_ops = {
+ .make_item = uvcg_frame_make,
+ .drop_item = uvcg_frame_drop,
+};
+
+static ssize_t uvcg_uncompressed_guid_format_show(struct uvcg_uncompressed *ch,
+ char *page)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat));
+ mutex_unlock(&opts->lock);
+
+ mutex_unlock(su_mutex);
+
+ return sizeof(ch->desc.guidFormat);
+}
+
+static ssize_t uvcg_uncompressed_guid_format_store(struct uvcg_uncompressed *ch,
+ const char *page, size_t len)
+{
+ struct f_uvc_opts *opts;
+ struct config_item *opts_item;
+ struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+ int ret;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+ opts = to_f_uvc_opts(opts_item);
+
+ mutex_lock(&opts->lock);
+ if (ch->fmt.linked || opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ memcpy(ch->desc.guidFormat, page,
+ min(sizeof(ch->desc.guidFormat), len));
+ ret = sizeof(ch->desc.guidFormat);
+
+end:
+ mutex_unlock(&opts->lock);
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static struct uvcg_uncompressed_attribute uvcg_uncompressed_guid_format =
+ __CONFIGFS_ATTR(guidFormat, S_IRUGO | S_IWUSR,
+ uvcg_uncompressed_guid_format_show,
+ uvcg_uncompressed_guid_format_store);
+
+
+#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv) \
+static ssize_t uvcg_uncompressed_##cname##_show( \
+ struct uvcg_uncompressed *u, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_uncompressed_attribute \
+ uvcg_uncompressed_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_uncompressed_##cname##_show)
+
+#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv) \
+static ssize_t uvcg_uncompressed_##cname##_show( \
+ struct uvcg_uncompressed *u, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static ssize_t \
+uvcg_uncompressed_##cname##_store(struct uvcg_uncompressed *u, \
+ const char *page, size_t len) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int ret; \
+ u8 num; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ if (u->fmt.linked || opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = kstrtou8(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > 255) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ u->desc.aname = num; \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ mutex_unlock(su_mutex); \
+ return ret; \
+} \
+ \
+static struct uvcg_uncompressed_attribute \
+ uvcg_uncompressed_##cname = \
+ __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \
+ uvcg_uncompressed_##cname##_show, \
+ uvcg_uncompressed_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv);
+UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex,
+ identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_UNCOMPRESSED_ATTR
+#undef UVCG_UNCOMPRESSED_ATTR_RO
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_show(struct uvcg_uncompressed *unc, char *page)
+{
+ return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_store(struct uvcg_uncompressed *ch,
+ const char *page, size_t len)
+{
+ return uvcg_format_bma_controls_store(&ch->fmt, page, len);
+}
+
+static struct uvcg_uncompressed_attribute uvcg_uncompressed_bma_controls =
+ __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR,
+ uvcg_uncompressed_bma_controls_show,
+ uvcg_uncompressed_bma_controls_store);
+
+static struct configfs_attribute *uvcg_uncompressed_attrs[] = {
+ &uvcg_uncompressed_guid_format.attr,
+ &uvcg_uncompressed_b_bits_per_pixel.attr,
+ &uvcg_uncompressed_b_default_frame_index.attr,
+ &uvcg_uncompressed_b_aspect_ratio_x.attr,
+ &uvcg_uncompressed_b_aspect_ratio_y.attr,
+ &uvcg_uncompressed_bm_interface_flags.attr,
+ &uvcg_uncompressed_bma_controls.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_uncompressed_type = {
+ .ct_item_ops = &uvcg_uncompressed_item_ops,
+ .ct_group_ops = &uvcg_uncompressed_group_ops,
+ .ct_attrs = uvcg_uncompressed_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_uncompressed_make(struct config_group *group,
+ const char *name)
+{
+ static char guid[] = {
+ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+ };
+ struct uvcg_uncompressed *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ h->desc.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE;
+ h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ h->desc.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED;
+ memcpy(h->desc.guidFormat, guid, sizeof(guid));
+ h->desc.bBitsPerPixel = 16;
+ h->desc.bDefaultFrameIndex = 1;
+ h->desc.bAspectRatioX = 0;
+ h->desc.bAspectRatioY = 0;
+ h->desc.bmInterfaceFlags = 0;
+ h->desc.bCopyProtect = 0;
+
+ h->fmt.type = UVCG_UNCOMPRESSED;
+ config_group_init_type_name(&h->fmt.group, name,
+ &uvcg_uncompressed_type);
+
+ return &h->fmt.group;
+}
+
+static void uvcg_uncompressed_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct uvcg_uncompressed *h = to_uvcg_uncompressed(item);
+
+ kfree(h);
+}
+
+static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
+ .make_group = uvcg_uncompressed_make,
+ .drop_item = uvcg_uncompressed_drop,
+};
+
+static struct config_item_type uvcg_uncompressed_grp_type = {
+ .ct_group_ops = &uvcg_uncompressed_grp_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/mjpeg/<NAME> */
+struct uvcg_mjpeg {
+ struct uvcg_format fmt;
+ struct uvc_format_mjpeg desc;
+};
+
+static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item)
+{
+ return container_of(
+ container_of(to_config_group(item), struct uvcg_format, group),
+ struct uvcg_mjpeg, fmt);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_mjpeg);
+CONFIGFS_ATTR_OPS(uvcg_mjpeg);
+
+static struct configfs_item_operations uvcg_mjpeg_item_ops = {
+ .show_attribute = uvcg_mjpeg_attr_show,
+ .store_attribute = uvcg_mjpeg_attr_store,
+};
+
+static struct configfs_group_operations uvcg_mjpeg_group_ops = {
+ .make_item = uvcg_frame_make,
+ .drop_item = uvcg_frame_drop,
+};
+
+#define UVCG_MJPEG_ATTR_RO(cname, aname, conv) \
+static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_mjpeg_attribute \
+ uvcg_mjpeg_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_mjpeg_##cname##_show)
+
+#define UVCG_MJPEG_ATTR(cname, aname, conv) \
+static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(u->desc.aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static ssize_t \
+uvcg_mjpeg_##cname##_store(struct uvcg_mjpeg *u, \
+ const char *page, size_t len) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \
+ int ret; \
+ u8 num; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+ opts = to_f_uvc_opts(opts_item); \
+ \
+ mutex_lock(&opts->lock); \
+ if (u->fmt.linked || opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = kstrtou8(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > 255) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ u->desc.aname = num; \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ mutex_unlock(su_mutex); \
+ return ret; \
+} \
+ \
+static struct uvcg_mjpeg_attribute \
+ uvcg_mjpeg_##cname = \
+ __CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR, \
+ uvcg_mjpeg_##cname##_show, \
+ uvcg_mjpeg_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex,
+ identity_conv);
+UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
+UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_MJPEG_ATTR
+#undef UVCG_MJPEG_ATTR_RO
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_show(struct uvcg_mjpeg *unc, char *page)
+{
+ return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_store(struct uvcg_mjpeg *ch,
+ const char *page, size_t len)
+{
+ return uvcg_format_bma_controls_store(&ch->fmt, page, len);
+}
+
+static struct uvcg_mjpeg_attribute uvcg_mjpeg_bma_controls =
+ __CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR,
+ uvcg_mjpeg_bma_controls_show,
+ uvcg_mjpeg_bma_controls_store);
+
+static struct configfs_attribute *uvcg_mjpeg_attrs[] = {
+ &uvcg_mjpeg_b_default_frame_index.attr,
+ &uvcg_mjpeg_bm_flags.attr,
+ &uvcg_mjpeg_b_aspect_ratio_x.attr,
+ &uvcg_mjpeg_b_aspect_ratio_y.attr,
+ &uvcg_mjpeg_bm_interface_flags.attr,
+ &uvcg_mjpeg_bma_controls.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_mjpeg_type = {
+ .ct_item_ops = &uvcg_mjpeg_item_ops,
+ .ct_group_ops = &uvcg_mjpeg_group_ops,
+ .ct_attrs = uvcg_mjpeg_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_mjpeg_make(struct config_group *group,
+ const char *name)
+{
+ struct uvcg_mjpeg *h;
+
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
+
+ h->desc.bLength = UVC_DT_FORMAT_MJPEG_SIZE;
+ h->desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ h->desc.bDescriptorSubType = UVC_VS_FORMAT_MJPEG;
+ h->desc.bDefaultFrameIndex = 1;
+ h->desc.bAspectRatioX = 0;
+ h->desc.bAspectRatioY = 0;
+ h->desc.bmInterfaceFlags = 0;
+ h->desc.bCopyProtect = 0;
+
+ h->fmt.type = UVCG_MJPEG;
+ config_group_init_type_name(&h->fmt.group, name,
+ &uvcg_mjpeg_type);
+
+ return &h->fmt.group;
+}
+
+static void uvcg_mjpeg_drop(struct config_group *group,
+ struct config_item *item)
+{
+ struct uvcg_mjpeg *h = to_uvcg_mjpeg(item);
+
+ kfree(h);
+}
+
+static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
+ .make_group = uvcg_mjpeg_make,
+ .drop_item = uvcg_mjpeg_drop,
+};
+
+static struct config_item_type uvcg_mjpeg_grp_type = {
+ .ct_group_ops = &uvcg_mjpeg_grp_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/color_matching/default */
+static struct uvcg_default_color_matching {
+ struct config_group group;
+} uvcg_default_color_matching;
+
+static inline struct uvcg_default_color_matching
+*to_uvcg_default_color_matching(struct config_item *item)
+{
+ return container_of(to_config_group(item),
+ struct uvcg_default_color_matching, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_color_matching);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_color_matching);
+
+static struct configfs_item_operations uvcg_default_color_matching_item_ops = {
+ .show_attribute = uvcg_default_color_matching_attr_show,
+};
+
+#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \
+static ssize_t uvcg_default_color_matching_##cname##_show( \
+ struct uvcg_default_color_matching *dc, char *page) \
+{ \
+ struct f_uvc_opts *opts; \
+ struct config_item *opts_item; \
+ struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \
+ struct uvc_color_matching_descriptor *cd; \
+ int result; \
+ \
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \
+ \
+ opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent; \
+ opts = to_f_uvc_opts(opts_item); \
+ cd = &opts->uvc_color_matching; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(cd->aname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ mutex_unlock(su_mutex); \
+ return result; \
+} \
+ \
+static struct uvcg_default_color_matching_attribute \
+ uvcg_default_color_matching_##cname = \
+ __CONFIGFS_ATTR_RO(aname, uvcg_default_color_matching_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries,
+ identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics,
+ bTransferCharacteristics, identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients,
+ identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_COLOR_MATCHING_ATTR
+
+static struct configfs_attribute *uvcg_default_color_matching_attrs[] = {
+ &uvcg_default_color_matching_b_color_primaries.attr,
+ &uvcg_default_color_matching_b_transfer_characteristics.attr,
+ &uvcg_default_color_matching_b_matrix_coefficients.attr,
+ NULL,
+};
+
+static struct config_item_type uvcg_default_color_matching_type = {
+ .ct_item_ops = &uvcg_default_color_matching_item_ops,
+ .ct_attrs = uvcg_default_color_matching_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/* struct uvcg_color_matching {}; */
+
+static struct config_group *uvcg_color_matching_default_groups[] = {
+ &uvcg_default_color_matching.group,
+ NULL,
+};
+
+/* streaming/color_matching */
+static struct uvcg_color_matching_grp {
+ struct config_group group;
+} uvcg_color_matching_grp;
+
+static struct config_item_type uvcg_color_matching_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+/* streaming/class/{fs|hs|ss} */
+static struct uvcg_streaming_class {
+ struct config_group group;
+} uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss;
+
+
+static inline struct uvc_descriptor_header
+***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o)
+{
+ struct uvcg_streaming_class *cl = container_of(to_config_group(i),
+ struct uvcg_streaming_class, group);
+
+ if (cl == &uvcg_streaming_class_fs)
+ return &o->uvc_fs_streaming_cls;
+
+ if (cl == &uvcg_streaming_class_hs)
+ return &o->uvc_hs_streaming_cls;
+
+ if (cl == &uvcg_streaming_class_ss)
+ return &o->uvc_ss_streaming_cls;
+
+ return NULL;
+}
+
+enum uvcg_strm_type {
+ UVCG_HEADER = 0,
+ UVCG_FORMAT,
+ UVCG_FRAME
+};
+
+/*
+ * Iterate over a hierarchy of streaming descriptors' config items.
+ * The items are created by the user with configfs.
+ *
+ * It "processes" the header pointed to by @priv1, then for each format
+ * that follows the header "processes" the format itself and then for
+ * each frame inside a format "processes" the frame.
+ *
+ * As a "processing" function the @fun is used.
+ *
+ * __uvcg_iter_strm_cls() is used in two context: first, to calculate
+ * the amount of memory needed for an array of streaming descriptors
+ * and second, to actually fill the array.
+ *
+ * @h: streaming header pointer
+ * @priv2: an "inout" parameter (the caller might want to see the changes to it)
+ * @priv3: an "inout" parameter (the caller might want to see the changes to it)
+ * @fun: callback function for processing each level of the hierarchy
+ */
+static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h,
+ void *priv2, void *priv3,
+ int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type))
+{
+ struct uvcg_format_ptr *f;
+ struct config_group *grp;
+ struct config_item *item;
+ struct uvcg_frame *frm;
+ int ret, i, j;
+
+ if (!fun)
+ return -EINVAL;
+
+ i = j = 0;
+ ret = fun(h, priv2, priv3, 0, UVCG_HEADER);
+ if (ret)
+ return ret;
+ list_for_each_entry(f, &h->formats, entry) {
+ ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT);
+ if (ret)
+ return ret;
+ grp = &f->fmt->group;
+ list_for_each_entry(item, &grp->cg_children, ci_entry) {
+ frm = to_uvcg_frame(item);
+ ret = fun(frm, priv2, priv3, j++, UVCG_FRAME);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Count how many bytes are needed for an array of streaming descriptors.
+ *
+ * @priv1: pointer to a header, format or frame
+ * @priv2: inout parameter, accumulated size of the array
+ * @priv3: inout parameter, accumulated number of the array elements
+ * @n: unused, this function's prototype must match @fun in __uvcg_iter_strm_cls
+ */
+static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n,
+ enum uvcg_strm_type type)
+{
+ size_t *size = priv2;
+ size_t *count = priv3;
+
+ switch (type) {
+ case UVCG_HEADER: {
+ struct uvcg_streaming_header *h = priv1;
+
+ *size += sizeof(h->desc);
+ /* bmaControls */
+ *size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE;
+ }
+ break;
+ case UVCG_FORMAT: {
+ struct uvcg_format *fmt = priv1;
+
+ if (fmt->type == UVCG_UNCOMPRESSED) {
+ struct uvcg_uncompressed *u =
+ container_of(fmt, struct uvcg_uncompressed,
+ fmt);
+
+ *size += sizeof(u->desc);
+ } else if (fmt->type == UVCG_MJPEG) {
+ struct uvcg_mjpeg *m =
+ container_of(fmt, struct uvcg_mjpeg, fmt);
+
+ *size += sizeof(m->desc);
+ } else {
+ return -EINVAL;
+ }
+ }
+ break;
+ case UVCG_FRAME: {
+ struct uvcg_frame *frm = priv1;
+ int sz = sizeof(frm->dw_frame_interval);
+
+ *size += sizeof(frm->frame);
+ *size += frm->frame.b_frame_interval_type * sz;
+ }
+ break;
+ }
+
+ ++*count;
+
+ return 0;
+}
+
+/*
+ * Fill an array of streaming descriptors.
+ *
+ * @priv1: pointer to a header, format or frame
+ * @priv2: inout parameter, pointer into a block of memory
+ * @priv3: inout parameter, pointer to a 2-dimensional array
+ */
+static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
+ enum uvcg_strm_type type)
+{
+ void **dest = priv2;
+ struct uvc_descriptor_header ***array = priv3;
+ size_t sz;
+
+ **array = *dest;
+ ++*array;
+
+ switch (type) {
+ case UVCG_HEADER: {
+ struct uvc_input_header_descriptor *ihdr = *dest;
+ struct uvcg_streaming_header *h = priv1;
+ struct uvcg_format_ptr *f;
+
+ memcpy(*dest, &h->desc, sizeof(h->desc));
+ *dest += sizeof(h->desc);
+ sz = UVCG_STREAMING_CONTROL_SIZE;
+ list_for_each_entry(f, &h->formats, entry) {
+ memcpy(*dest, f->fmt->bmaControls, sz);
+ *dest += sz;
+ }
+ ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz;
+ ihdr->bNumFormats = h->num_fmt;
+ }
+ break;
+ case UVCG_FORMAT: {
+ struct uvcg_format *fmt = priv1;
+
+ if (fmt->type == UVCG_UNCOMPRESSED) {
+ struct uvc_format_uncompressed *unc = *dest;
+ struct uvcg_uncompressed *u =
+ container_of(fmt, struct uvcg_uncompressed,
+ fmt);
+
+ memcpy(*dest, &u->desc, sizeof(u->desc));
+ *dest += sizeof(u->desc);
+ unc->bNumFrameDescriptors = fmt->num_frames;
+ unc->bFormatIndex = n + 1;
+ } else if (fmt->type == UVCG_MJPEG) {
+ struct uvc_format_mjpeg *mjp = *dest;
+ struct uvcg_mjpeg *m =
+ container_of(fmt, struct uvcg_mjpeg, fmt);
+
+ memcpy(*dest, &m->desc, sizeof(m->desc));
+ *dest += sizeof(m->desc);
+ mjp->bNumFrameDescriptors = fmt->num_frames;
+ mjp->bFormatIndex = n + 1;
+ } else {
+ return -EINVAL;
+ }
+ }
+ break;
+ case UVCG_FRAME: {
+ struct uvcg_frame *frm = priv1;
+ struct uvc_descriptor_header *h = *dest;
+
+ sz = sizeof(frm->frame);
+ memcpy(*dest, &frm->frame, sz);
+ *dest += sz;
+ sz = frm->frame.b_frame_interval_type *
+ sizeof(*frm->dw_frame_interval);
+ memcpy(*dest, frm->dw_frame_interval, sz);
+ *dest += sz;
+ if (frm->fmt_type == UVCG_UNCOMPRESSED)
+ h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(
+ frm->frame.b_frame_interval_type);
+ else if (frm->fmt_type == UVCG_MJPEG)
+ h->bLength = UVC_DT_FRAME_MJPEG_SIZE(
+ frm->frame.b_frame_interval_type);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int uvcg_streaming_class_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_item *streaming, *header;
+ struct f_uvc_opts *opts;
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvc_descriptor_header ***class_array, **cl_arr;
+ struct uvcg_streaming_header *target_hdr;
+ void *data, *data_save;
+ size_t size = 0, count = 0;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ streaming = src->ci_parent->ci_parent;
+ header = config_group_find_item(to_config_group(streaming), "header");
+ if (!header || target->ci_parent != header)
+ goto out;
+
+ opts = to_f_uvc_opts(streaming->ci_parent);
+
+ mutex_lock(&opts->lock);
+
+ class_array = __uvcg_get_stream_class_arr(src, opts);
+ if (!class_array || *class_array || opts->refcnt) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ target_hdr = to_uvcg_streaming_header(target);
+ ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm);
+ if (ret)
+ goto unlock;
+
+ count += 2; /* color_matching, NULL */
+ *class_array = kcalloc(count, sizeof(void *), GFP_KERNEL);
+ if (!*class_array) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ data = data_save = kzalloc(size, GFP_KERNEL);
+ if (!data) {
+ kfree(*class_array);
+ *class_array = NULL;
+ ret = PTR_ERR(data);
+ goto unlock;
+ }
+ cl_arr = *class_array;
+ ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr,
+ __uvcg_fill_strm);
+ if (ret) {
+ kfree(*class_array);
+ *class_array = NULL;
+ /*
+ * __uvcg_fill_strm() called from __uvcg_iter_stream_cls()
+ * might have advanced the "data", so use a backup copy
+ */
+ kfree(data_save);
+ goto unlock;
+ }
+ *cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching;
+
+ ++target_hdr->linked;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&opts->lock);
+out:
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static int uvcg_streaming_class_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct config_item *streaming, *header;
+ struct f_uvc_opts *opts;
+ struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+ struct uvc_descriptor_header ***class_array;
+ struct uvcg_streaming_header *target_hdr;
+ int ret = -EINVAL;
+
+ mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+ streaming = src->ci_parent->ci_parent;
+ header = config_group_find_item(to_config_group(streaming), "header");
+ if (!header || target->ci_parent != header)
+ goto out;
+
+ opts = to_f_uvc_opts(streaming->ci_parent);
+
+ mutex_lock(&opts->lock);
+
+ class_array = __uvcg_get_stream_class_arr(src, opts);
+ if (!class_array || !*class_array)
+ goto unlock;
+
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ target_hdr = to_uvcg_streaming_header(target);
+ --target_hdr->linked;
+ kfree(**class_array);
+ kfree(*class_array);
+ *class_array = NULL;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&opts->lock);
+out:
+ mutex_unlock(su_mutex);
+ return ret;
+}
+
+static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+ .allow_link = uvcg_streaming_class_allow_link,
+ .drop_link = uvcg_streaming_class_drop_link,
+};
+
+static struct config_item_type uvcg_streaming_class_type = {
+ .ct_item_ops = &uvcg_streaming_class_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_streaming_class_default_groups[] = {
+ &uvcg_streaming_class_fs.group,
+ &uvcg_streaming_class_hs.group,
+ &uvcg_streaming_class_ss.group,
+ NULL,
+};
+
+/* streaming/class */
+static struct uvcg_streaming_class_grp {
+ struct config_group group;
+} uvcg_streaming_class_grp;
+
+static struct config_item_type uvcg_streaming_class_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_streaming_default_groups[] = {
+ &uvcg_streaming_header_grp.group,
+ &uvcg_uncompressed_grp.group,
+ &uvcg_mjpeg_grp.group,
+ &uvcg_color_matching_grp.group,
+ &uvcg_streaming_class_grp.group,
+ NULL,
+};
+
+/* streaming */
+static struct uvcg_streaming_grp {
+ struct config_group group;
+} uvcg_streaming_grp;
+
+static struct config_item_type uvcg_streaming_grp_type = {
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_default_groups[] = {
+ &uvcg_control_grp.group,
+ &uvcg_streaming_grp.group,
+ NULL,
+};
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_uvc_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_uvc_opts);
+CONFIGFS_ATTR_OPS(f_uvc_opts);
+
+static void uvc_attr_release(struct config_item *item)
+{
+ struct f_uvc_opts *opts = to_f_uvc_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations uvc_item_ops = {
+ .release = uvc_attr_release,
+ .show_attribute = f_uvc_opts_attr_show,
+ .store_attribute = f_uvc_opts_attr_store,
+};
+
+#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit) \
+static ssize_t f_uvc_opts_##cname##_show( \
+ struct f_uvc_opts *opts, char *page) \
+{ \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", conv(opts->cname)); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t \
+f_uvc_opts_##cname##_store(struct f_uvc_opts *opts, \
+ const char *page, size_t len) \
+{ \
+ int ret; \
+ uxx num; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = str2u(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > limit) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ opts->cname = vnoc(num); \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+static struct f_uvc_opts_attribute \
+ f_uvc_opts_attribute_##cname = \
+ __CONFIGFS_ATTR(cname, S_IRUGO | S_IWUSR, \
+ f_uvc_opts_##cname##_show, \
+ f_uvc_opts_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv,
+ 16);
+UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu,
+ 3072);
+UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv,
+ 15);
+
+#undef identity_conv
+
+#undef UVCG_OPTS_ATTR
+
+static struct configfs_attribute *uvc_attrs[] = {
+ &f_uvc_opts_attribute_streaming_interval.attr,
+ &f_uvc_opts_attribute_streaming_maxpacket.attr,
+ &f_uvc_opts_attribute_streaming_maxburst.attr,
+ NULL,
+};
+
+static struct config_item_type uvc_func_type = {
+ .ct_item_ops = &uvc_item_ops,
+ .ct_attrs = uvc_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static inline void uvcg_init_group(struct config_group *g,
+ struct config_group **default_groups,
+ const char *name,
+ struct config_item_type *type)
+{
+ g->default_groups = default_groups;
+ config_group_init_type_name(g, name, type);
+}
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts)
+{
+ config_group_init_type_name(&uvcg_control_header_grp.group,
+ "header",
+ &uvcg_control_header_grp_type);
+ config_group_init_type_name(&uvcg_default_processing.group,
+ "default",
+ &uvcg_default_processing_type);
+ uvcg_init_group(&uvcg_processing_grp.group,
+ uvcg_processing_default_groups,
+ "processing",
+ &uvcg_processing_grp_type);
+ config_group_init_type_name(&uvcg_default_camera.group,
+ "default",
+ &uvcg_default_camera_type);
+ uvcg_init_group(&uvcg_camera_grp.group,
+ uvcg_camera_default_groups,
+ "camera",
+ &uvcg_camera_grp_type);
+ config_group_init_type_name(&uvcg_default_output.group,
+ "default",
+ &uvcg_default_output_type);
+ uvcg_init_group(&uvcg_output_grp.group,
+ uvcg_output_default_groups,
+ "output",
+ &uvcg_output_grp_type);
+ uvcg_init_group(&uvcg_terminal_grp.group,
+ uvcg_terminal_default_groups,
+ "terminal",
+ &uvcg_terminal_grp_type);
+ config_group_init_type_name(&uvcg_control_class_fs.group,
+ "fs",
+ &uvcg_control_class_type);
+ config_group_init_type_name(&uvcg_control_class_ss.group,
+ "ss",
+ &uvcg_control_class_type);
+ uvcg_init_group(&uvcg_control_class_grp.group,
+ uvcg_control_class_default_groups,
+ "class",
+ &uvcg_control_class_grp_type);
+ uvcg_init_group(&uvcg_control_grp.group,
+ uvcg_control_default_groups,
+ "control",
+ &uvcg_control_grp_type);
+ config_group_init_type_name(&uvcg_streaming_header_grp.group,
+ "header",
+ &uvcg_streaming_header_grp_type);
+ config_group_init_type_name(&uvcg_uncompressed_grp.group,
+ "uncompressed",
+ &uvcg_uncompressed_grp_type);
+ config_group_init_type_name(&uvcg_mjpeg_grp.group,
+ "mjpeg",
+ &uvcg_mjpeg_grp_type);
+ config_group_init_type_name(&uvcg_default_color_matching.group,
+ "default",
+ &uvcg_default_color_matching_type);
+ uvcg_init_group(&uvcg_color_matching_grp.group,
+ uvcg_color_matching_default_groups,
+ "color_matching",
+ &uvcg_color_matching_grp_type);
+ config_group_init_type_name(&uvcg_streaming_class_fs.group,
+ "fs",
+ &uvcg_streaming_class_type);
+ config_group_init_type_name(&uvcg_streaming_class_hs.group,
+ "hs",
+ &uvcg_streaming_class_type);
+ config_group_init_type_name(&uvcg_streaming_class_ss.group,
+ "ss",
+ &uvcg_streaming_class_type);
+ uvcg_init_group(&uvcg_streaming_class_grp.group,
+ uvcg_streaming_class_default_groups,
+ "class",
+ &uvcg_streaming_class_grp_type);
+ uvcg_init_group(&uvcg_streaming_grp.group,
+ uvcg_streaming_default_groups,
+ "streaming",
+ &uvcg_streaming_grp_type);
+ uvcg_init_group(&opts->func_inst.group,
+ uvcg_default_groups,
+ "",
+ &uvc_func_type);
+ return 0;
+}
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
new file mode 100644
index 000000000000..085e67be7c71
--- /dev/null
+++ b/drivers/usb/gadget/function/uvc_configfs.h
@@ -0,0 +1,22 @@
+/*
+ * uvc_configfs.h
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 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 UVC_CONFIGFS_H
+#define UVC_CONFIGFS_H
+
+struct f_uvc_opts;
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts);
+
+#endif /* UVC_CONFIGFS_H */
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index c862656d18b8..c0ec5f71f16f 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -176,7 +176,7 @@ static int proc_udc_show(struct seq_file *s, void *unused)
udc->enabled
? (udc->vbus ? "active" : "enabled")
: "disabled",
- udc->selfpowered ? "self" : "VBUS",
+ udc->gadget.is_selfpowered ? "self" : "VBUS",
udc->suspended ? ", suspended" : "",
udc->driver ? udc->driver->driver.name : "(none)");
@@ -1000,7 +1000,7 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
unsigned long flags;
spin_lock_irqsave(&udc->lock, flags);
- udc->selfpowered = (is_on != 0);
+ gadget->is_selfpowered = (is_on != 0);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
@@ -1149,7 +1149,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
*/
case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
| USB_REQ_GET_STATUS:
- tmp = (udc->selfpowered << USB_DEVICE_SELF_POWERED);
+ tmp = (udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED);
if (at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_ESR)
tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);
PACKET("get device status\n");
@@ -1653,7 +1653,7 @@ static int at91_start(struct usb_gadget *gadget,
udc->driver = driver;
udc->gadget.dev.of_node = udc->pdev->dev.of_node;
udc->enabled = 1;
- udc->selfpowered = 1;
+ udc->gadget.is_selfpowered = 1;
return 0;
}
diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h
index 017524663381..467dcfb74a51 100644
--- a/drivers/usb/gadget/udc/at91_udc.h
+++ b/drivers/usb/gadget/udc/at91_udc.h
@@ -122,7 +122,6 @@ struct at91_udc {
unsigned req_pending:1;
unsigned wait_for_addr_ack:1;
unsigned wait_for_config_ack:1;
- unsigned selfpowered:1;
unsigned active_suspend:1;
u8 addr;
struct at91_udc_data board;
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 9f93bed42052..c0410862c2a1 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -315,6 +316,17 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc)
}
#endif
+static inline u32 usba_int_enb_get(struct usba_udc *udc)
+{
+ return udc->int_enb_cache;
+}
+
+static inline void usba_int_enb_set(struct usba_udc *udc, u32 val)
+{
+ usba_writel(udc, INT_ENB, val);
+ udc->int_enb_cache = val;
+}
+
static int vbus_is_present(struct usba_udc *udc)
{
if (gpio_is_valid(udc->vbus_pin))
@@ -324,27 +336,22 @@ static int vbus_is_present(struct usba_udc *udc)
return 1;
}
-#if defined(CONFIG_ARCH_AT91SAM9RL)
-
-#include <linux/clk/at91_pmc.h>
-
-static void toggle_bias(int is_on)
+static void toggle_bias(struct usba_udc *udc, int is_on)
{
- unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
-
- if (is_on)
- at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
- else
- at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+ if (udc->errata && udc->errata->toggle_bias)
+ udc->errata->toggle_bias(udc, is_on);
}
-#else
-
-static void toggle_bias(int is_on)
+static void generate_bias_pulse(struct usba_udc *udc)
{
-}
+ if (!udc->bias_pulse_needed)
+ return;
+
+ if (udc->errata && udc->errata->pulse_bias)
+ udc->errata->pulse_bias(udc);
-#endif /* CONFIG_ARCH_AT91SAM9RL */
+ udc->bias_pulse_needed = false;
+}
static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
{
@@ -601,16 +608,14 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
if (ep->can_dma) {
u32 ctrl;
- usba_writel(udc, INT_ENB,
- (usba_readl(udc, INT_ENB)
- | USBA_BF(EPT_INT, 1 << ep->index)
- | USBA_BF(DMA_INT, 1 << ep->index)));
+ usba_int_enb_set(udc, usba_int_enb_get(udc) |
+ USBA_BF(EPT_INT, 1 << ep->index) |
+ USBA_BF(DMA_INT, 1 << ep->index));
ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA;
usba_ep_writel(ep, CTL_ENB, ctrl);
} else {
- usba_writel(udc, INT_ENB,
- (usba_readl(udc, INT_ENB)
- | USBA_BF(EPT_INT, 1 << ep->index)));
+ usba_int_enb_set(udc, usba_int_enb_get(udc) |
+ USBA_BF(EPT_INT, 1 << ep->index));
}
spin_unlock_irqrestore(&udc->lock, flags);
@@ -618,7 +623,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index,
(unsigned long)usba_ep_readl(ep, CFG));
DBG(DBG_HW, "INT_ENB after init: %#08lx\n",
- (unsigned long)usba_readl(udc, INT_ENB));
+ (unsigned long)usba_int_enb_get(udc));
return 0;
}
@@ -654,9 +659,8 @@ static int usba_ep_disable(struct usb_ep *_ep)
usba_dma_readl(ep, STATUS);
}
usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
- usba_writel(udc, INT_ENB,
- usba_readl(udc, INT_ENB)
- & ~USBA_BF(EPT_INT, 1 << ep->index));
+ usba_int_enb_set(udc, usba_int_enb_get(udc) &
+ ~USBA_BF(EPT_INT, 1 << ep->index));
request_complete_list(ep, &req_list, -ESHUTDOWN);
@@ -985,6 +989,7 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
struct usba_udc *udc = to_usba_udc(gadget);
unsigned long flags;
+ gadget->is_selfpowered = (is_selfpowered != 0);
spin_lock_irqsave(&udc->lock, flags);
if (is_selfpowered)
udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
@@ -1619,18 +1624,21 @@ static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep)
static irqreturn_t usba_udc_irq(int irq, void *devid)
{
struct usba_udc *udc = devid;
- u32 status;
+ u32 status, int_enb;
u32 dma_status;
u32 ep_status;
spin_lock(&udc->lock);
- status = usba_readl(udc, INT_STA);
+ int_enb = usba_int_enb_get(udc);
+ status = usba_readl(udc, INT_STA) & int_enb;
DBG(DBG_INT, "irq, status=%#08x\n", status);
if (status & USBA_DET_SUSPEND) {
- toggle_bias(0);
+ toggle_bias(udc, 0);
usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
+ usba_int_enb_set(udc, int_enb | USBA_WAKE_UP);
+ udc->bias_pulse_needed = true;
DBG(DBG_BUS, "Suspend detected\n");
if (udc->gadget.speed != USB_SPEED_UNKNOWN
&& udc->driver && udc->driver->suspend) {
@@ -1641,13 +1649,15 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
}
if (status & USBA_WAKE_UP) {
- toggle_bias(1);
+ toggle_bias(udc, 1);
usba_writel(udc, INT_CLR, USBA_WAKE_UP);
+ usba_int_enb_set(udc, int_enb & ~USBA_WAKE_UP);
DBG(DBG_BUS, "Wake Up CPU detected\n");
}
if (status & USBA_END_OF_RESUME) {
usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
+ generate_bias_pulse(udc);
DBG(DBG_BUS, "Resume detected\n");
if (udc->gadget.speed != USB_SPEED_UNKNOWN
&& udc->driver && udc->driver->resume) {
@@ -1683,6 +1693,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
struct usba_ep *ep0;
usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
+ generate_bias_pulse(udc);
reset_all_endpoints(udc);
if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) {
@@ -1708,11 +1719,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
usba_ep_writel(ep0, CTL_ENB,
USBA_EPT_ENABLE | USBA_RX_SETUP);
- usba_writel(udc, INT_ENB,
- (usba_readl(udc, INT_ENB)
- | USBA_BF(EPT_INT, 1)
- | USBA_DET_SUSPEND
- | USBA_END_OF_RESUME));
+ usba_int_enb_set(udc, int_enb | USBA_BF(EPT_INT, 1) |
+ USBA_DET_SUSPEND | USBA_END_OF_RESUME);
/*
* Unclear why we hit this irregularly, e.g. in usbtest,
@@ -1745,13 +1753,13 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
vbus = vbus_is_present(udc);
if (vbus != udc->vbus_prev) {
if (vbus) {
- toggle_bias(1);
+ toggle_bias(udc, 1);
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
- usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+ usba_int_enb_set(udc, USBA_END_OF_RESET);
} else {
udc->gadget.speed = USB_SPEED_UNKNOWN;
reset_all_endpoints(udc);
- toggle_bias(0);
+ toggle_bias(udc, 0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
if (udc->driver->disconnect) {
spin_unlock(&udc->lock);
@@ -1797,9 +1805,9 @@ static int atmel_usba_start(struct usb_gadget *gadget,
/* 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(1);
+ toggle_bias(udc, 1);
usba_writel(udc, CTRL, USBA_ENABLE_MASK);
- usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+ usba_int_enb_set(udc, USBA_END_OF_RESET);
}
spin_unlock_irqrestore(&udc->lock, flags);
@@ -1820,7 +1828,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
spin_unlock_irqrestore(&udc->lock, flags);
/* This will also disable the DP pullup */
- toggle_bias(0);
+ toggle_bias(udc, 0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
clk_disable_unprepare(udc->hclk);
@@ -1832,6 +1840,41 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
}
#ifdef CONFIG_OF
+static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on)
+{
+ unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
+
+ if (is_on)
+ at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
+ else
+ at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+}
+
+static void at91sam9g45_pulse_bias(struct usba_udc *udc)
+{
+ unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
+
+ at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+ at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
+}
+
+static const struct usba_udc_errata at91sam9rl_errata = {
+ .toggle_bias = at91sam9rl_toggle_bias,
+};
+
+static const struct usba_udc_errata at91sam9g45_errata = {
+ .pulse_bias = at91sam9g45_pulse_bias,
+};
+
+static const struct of_device_id atmel_udc_dt_ids[] = {
+ { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
+ { .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
+ { .compatible = "atmel,sama5d3-udc" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
+
static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
struct usba_udc *udc)
{
@@ -1839,10 +1882,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
const char *name;
enum of_gpio_flags flags;
struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
struct device_node *pp;
int i, ret;
struct usba_ep *eps, *ep;
+ match = of_match_node(atmel_udc_dt_ids, np);
+ if (!match)
+ return ERR_PTR(-EINVAL);
+
+ udc->errata = match->data;
+
udc->num_ep = 0;
udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0,
@@ -2033,7 +2083,7 @@ static int usba_udc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n");
return ret;
}
- toggle_bias(0);
+
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
clk_disable_unprepare(pclk);
@@ -2042,6 +2092,8 @@ static int usba_udc_probe(struct platform_device *pdev)
else
udc->usba_ep = usba_udc_pdata(pdev, udc);
+ toggle_bias(udc, 0);
+
if (IS_ERR(udc->usba_ep))
return PTR_ERR(udc->usba_ep);
@@ -2101,15 +2153,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
return 0;
}
-#if defined(CONFIG_OF)
-static const struct of_device_id atmel_udc_dt_ids[] = {
- { .compatible = "atmel,at91sam9rl-udc" },
- { /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
-#endif
-
static struct platform_driver udc_driver = {
.remove = __exit_p(usba_udc_remove),
.driver = {
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index a70706e8cb02..497cd18836f3 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
@@ -304,6 +304,11 @@ struct usba_request {
unsigned int mapped:1;
};
+struct usba_udc_errata {
+ void (*toggle_bias)(struct usba_udc *udc, int is_on);
+ void (*pulse_bias)(struct usba_udc *udc);
+};
+
struct usba_udc {
/* Protect hw registers from concurrent modifications */
spinlock_t lock;
@@ -314,6 +319,7 @@ struct usba_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct platform_device *pdev;
+ const struct usba_udc_errata *errata;
int irq;
int vbus_pin;
int vbus_pin_inverted;
@@ -321,12 +327,15 @@ struct usba_udc {
struct clk *pclk;
struct clk *hclk;
struct usba_ep *usba_ep;
+ bool bias_pulse_needed;
u16 devstatus;
u16 test_mode;
int vbus_prev;
+ u32 int_enb_cache;
+
#ifdef CONFIG_USB_GADGET_DEBUG_FS
struct dentry *debugfs_root;
struct dentry *debugfs_regs;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index c6dfef8c7bbc..5c8f4effb62a 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -521,7 +521,6 @@ static int bdc_remove(struct platform_device *pdev)
static struct platform_driver bdc_driver = {
.driver = {
.name = BRCM_BDC_NAME,
- .owner = THIS_MODULE
},
.probe = bdc_probe,
.remove = bdc_remove,
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
index d4fe8d769bd6..b04980cf6dc4 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_ep.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -718,7 +718,7 @@ static int ep_queue(struct bdc_ep *ep, struct bdc_req *req)
struct bdc *bdc;
int ret = 0;
- if (!req || !ep || !ep->usb_ep.desc)
+ if (!req || !ep->usb_ep.desc)
return -EINVAL;
bdc = ep->bdc;
@@ -882,8 +882,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value)
ret = bdc_ep_set_stall(bdc, ep->ep_num);
if (ret)
- dev_err(bdc->dev, "failed to %s STALL on %s\n",
- value ? "set" : "clear", ep->name);
+ dev_err(bdc->dev, "failed to set STALL on %s\n",
+ ep->name);
else
ep->flags |= BDC_EP_STALL;
} else {
@@ -891,8 +891,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value)
dev_dbg(bdc->dev, "Before Clear\n");
ret = bdc_ep_clear_stall(bdc, ep->ep_num);
if (ret)
- dev_err(bdc->dev, "failed to %s STALL on %s\n",
- value ? "set" : "clear", ep->name);
+ dev_err(bdc->dev, "failed to clear STALL on %s\n",
+ ep->name);
else
ep->flags &= ~BDC_EP_STALL;
dev_dbg(bdc->dev, "After Clear\n");
diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c
index 3700ce70b0be..7f77db5d1278 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_udc.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
@@ -454,6 +454,7 @@ static int bdc_udc_set_selfpowered(struct usb_gadget *gadget,
unsigned long flags;
dev_dbg(bdc->dev, "%s()\n", __func__);
+ gadget->is_selfpowered = (is_self != 0);
spin_lock_irqsave(&bdc->lock, flags);
if (!is_self)
bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 9c598801404c..8dda48445f6f 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -802,6 +802,7 @@ static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value)
{
struct dummy *dum;
+ _gadget->is_selfpowered = (value != 0);
dum = gadget_to_dummy_hcd(_gadget)->dum;
if (value)
dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
@@ -1924,7 +1925,9 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
memset(desc, 0, sizeof *desc);
desc->bDescriptorType = 0x2a;
desc->bDescLength = 12;
- desc->wHubCharacteristics = cpu_to_le16(0x0001);
+ desc->wHubCharacteristics = cpu_to_le16(
+ HUB_CHAR_INDV_PORT_LPSM |
+ HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = 1;
desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
desc->u.ss.DeviceRemovable = 0xffff;
@@ -1935,7 +1938,9 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
memset(desc, 0, sizeof *desc);
desc->bDescriptorType = 0x29;
desc->bDescLength = 9;
- desc->wHubCharacteristics = cpu_to_le16(0x0001);
+ desc->wHubCharacteristics = cpu_to_le16(
+ HUB_CHAR_INDV_PORT_LPSM |
+ HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = 1;
desc->u.hs.DeviceRemovable[0] = 0xff;
desc->u.hs.DeviceRemovable[1] = 0xff;
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index 795c99c77697..e0822f1b6639 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -2630,7 +2630,7 @@ static int qe_udc_remove(struct platform_device *ofdev)
struct qe_udc *udc = platform_get_drvdata(ofdev);
struct qe_ep *ep;
unsigned int size;
- DECLARE_COMPLETION(done);
+ DECLARE_COMPLETION_ONSTACK(done);
usb_del_gadget_udc(&udc->gadget);
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 2df807403f22..55fcb930f92e 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1337,7 +1337,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
/* Get device status */
- tmp = 1 << USB_DEVICE_SELF_POWERED;
+ tmp = udc->gadget.is_selfpowered;
tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
/* Get interface status */
@@ -1948,6 +1948,7 @@ static int fsl_udc_start(struct usb_gadget *g,
/* hook up the driver */
udc_controller->driver = driver;
spin_unlock_irqrestore(&udc_controller->lock, flags);
+ g->is_selfpowered = 1;
if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
/* Suspend the controller until OTG enable it */
@@ -2529,7 +2530,7 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
- DECLARE_COMPLETION(done);
+ DECLARE_COMPLETION_ONSTACK(done);
if (!udc_controller)
return -ENODEV;
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index 34d9b7b831b3..27fd41333f71 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -191,7 +191,6 @@ struct lpc32xx_udc {
bool enabled;
bool clocked;
bool suspended;
- bool selfpowered;
int ep0state;
atomic_t enabled_ep_cnt;
wait_queue_head_t ep_disable_wait_queue;
@@ -547,7 +546,7 @@ static int proc_udc_show(struct seq_file *s, void *unused)
udc->vbus ? "present" : "off",
udc->enabled ? (udc->vbus ? "active" : "enabled") :
"disabled",
- udc->selfpowered ? "self" : "VBUS",
+ udc->gadget.is_selfpowered ? "self" : "VBUS",
udc->suspended ? ", suspended" : "",
udc->driver ? udc->driver->driver.name : "(none)");
@@ -2212,7 +2211,7 @@ static int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex)
break; /* Not supported */
case USB_RECIP_DEVICE:
- ep0buff = (udc->selfpowered << USB_DEVICE_SELF_POWERED);
+ ep0buff = udc->gadget.is_selfpowered;
if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP))
ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP);
break;
@@ -2498,10 +2497,7 @@ static int lpc32xx_wakeup(struct usb_gadget *gadget)
static int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on)
{
- struct lpc32xx_udc *udc = to_udc(gadget);
-
- /* Always self-powered */
- udc->selfpowered = (is_on != 0);
+ gadget->is_selfpowered = (is_on != 0);
return 0;
}
@@ -2946,7 +2942,7 @@ static int lpc32xx_start(struct usb_gadget *gadget,
udc->driver = driver;
udc->gadget.dev.of_node = udc->dev->of_node;
udc->enabled = 1;
- udc->selfpowered = 1;
+ udc->gadget.is_selfpowered = 1;
udc->vbus = 0;
/* Force VBUS process once to check for cable insertion */
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 253f3df8326a..d32160d6463f 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -1378,9 +1378,6 @@ static int mv_udc_start(struct usb_gadget *gadget,
}
}
- /* pullup is always on */
- mv_udc_pullup(&udc->gadget, 1);
-
/* When boot with cable attached, there will be no vbus irq occurred */
if (udc->qwork)
queue_work(udc->qwork, &udc->vbus_work);
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index d20de1fab08e..195baf3e1fcd 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -1132,13 +1132,10 @@ net2272_wakeup(struct usb_gadget *_gadget)
static int
net2272_set_selfpowered(struct usb_gadget *_gadget, int value)
{
- struct net2272 *dev;
-
if (!_gadget)
return -ENODEV;
- dev = container_of(_gadget, struct net2272, gadget);
- dev->is_selfpowered = value;
+ _gadget->is_selfpowered = (value != 0);
return 0;
}
@@ -1844,7 +1841,7 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
case USB_RECIP_DEVICE:
if (u.r.wLength > 2)
goto do_stall;
- if (dev->is_selfpowered)
+ if (dev->gadget.is_selfpowered)
status = (1 << USB_DEVICE_SELF_POWERED);
/* don't bother with a request object! */
diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h
index e59505789359..127ab03fcde3 100644
--- a/drivers/usb/gadget/udc/net2272.h
+++ b/drivers/usb/gadget/udc/net2272.h
@@ -458,7 +458,6 @@ struct net2272 {
struct usb_gadget_driver *driver;
unsigned protocol_stall:1,
softconnect:1,
- is_selfpowered:1,
wakeup:1,
dma_eot_polarity:1,
dma_dack_polarity:1,
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index d6411e0a8e03..d2c0bf65e345 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -12,11 +12,7 @@
* the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
* as well as Gadget Zero and Gadgetfs.
*
- * DMA is enabled by default. Drivers using transfer queues might use
- * DMA chaining to remove IRQ latencies between transfers. (Except when
- * short OUT transfers happen.) Drivers can use the req->no_interrupt
- * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
- * and DMA chaining is enabled.
+ * DMA is enabled by default.
*
* MSI is enabled by default. The legacy IRQ is used if MSI couldn't
* be enabled.
@@ -84,23 +80,6 @@ static const char *const ep_name[] = {
"ep-e", "ep-f", "ep-g", "ep-h",
};
-/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
- * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
- *
- * The net2280 DMA engines are not tightly integrated with their FIFOs;
- * not all cases are (yet) handled well in this driver or the silicon.
- * Some gadget drivers work better with the dma support here than others.
- * These two parameters let you use PIO or more aggressive DMA.
- */
-static bool use_dma = true;
-static bool use_dma_chaining;
-static bool use_msi = true;
-
-/* "modprobe net2280 use_dma=n" etc */
-module_param(use_dma, bool, 0444);
-module_param(use_dma_chaining, bool, 0444);
-module_param(use_msi, bool, 0444);
-
/* 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
@@ -120,11 +99,6 @@ static bool enable_suspend;
/* "modprobe net2280 enable_suspend=1" etc */
module_param(enable_suspend, bool, 0444);
-/* force full-speed operation */
-static bool full_speed;
-module_param(full_speed, bool, 0444);
-MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
-
#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
static char *type_string(u8 bmAttributes)
@@ -202,15 +176,6 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
/* set speed-dependent max packet; may kick in high bandwidth */
set_max_speed(ep, max);
- /* FIFO lines can't go to different packets. PIO is ok, so
- * use it instead of troublesome (non-bulk) multi-packet DMA.
- */
- if (ep->dma && (max % 4) != 0 && use_dma_chaining) {
- ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n",
- ep->ep.name, ep->ep.maxpacket);
- ep->dma = NULL;
- }
-
/* set type, direction, address; reset fifo counters */
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
@@ -478,7 +443,7 @@ static int net2280_disable(struct usb_ep *_ep)
/* synch memory views with the device */
(void)readl(&ep->cfg->ep_cfg);
- if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
+ if (!ep->dma && ep->num >= 1 && ep->num <= 4)
ep->dma = &ep->dev->dma[ep->num - 1];
spin_unlock_irqrestore(&ep->dev->lock, flags);
@@ -610,9 +575,15 @@ static void out_flush(struct net2280_ep *ep)
u32 __iomem *statp;
u32 tmp;
- ASSERT_OUT_NAKING(ep);
-
statp = &ep->regs->ep_stat;
+
+ tmp = readl(statp);
+ if (tmp & BIT(NAK_OUT_PACKETS)) {
+ ep_dbg(ep->dev, "%s %s %08x !NAK\n",
+ ep->ep.name, __func__, tmp);
+ writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+ }
+
writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
BIT(DATA_PACKET_RECEIVED_INTERRUPT),
statp);
@@ -747,8 +718,7 @@ static void fill_dma_desc(struct net2280_ep *ep,
req->valid = valid;
if (valid)
dmacount |= BIT(VALID_BIT);
- if (likely(!req->req.no_interrupt || !use_dma_chaining))
- dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
+ dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
/* td->dmadesc = previously set by caller */
td->dmaaddr = cpu_to_le32 (req->req.dma);
@@ -862,27 +832,11 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
req->td->dmadesc = cpu_to_le32 (ep->td_dma);
fill_dma_desc(ep, req, 1);
- if (!use_dma_chaining)
- req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
+ req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
start_queue(ep, tmp, req->td_dma);
}
-static inline void resume_dma(struct net2280_ep *ep)
-{
- writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl);
-
- ep->dma_started = true;
-}
-
-static inline void ep_stop_dma(struct net2280_ep *ep)
-{
- writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl);
- spin_stop_dma(ep->dma);
-
- ep->dma_started = false;
-}
-
static inline void
queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
{
@@ -973,10 +927,8 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
return ret;
}
-#if 0
ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
_ep->name, _req, _req->length, _req->buf);
-#endif
spin_lock_irqsave(&dev->lock, flags);
@@ -984,24 +936,12 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
_req->actual = 0;
/* kickstart this i/o queue? */
- if (list_empty(&ep->queue) && !ep->stopped) {
- /* DMA request while EP halted */
- if (ep->dma &&
- (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) &&
- (dev->quirks & PLX_SUPERSPEED)) {
- int valid = 1;
- if (ep->is_in) {
- int expect;
- expect = likely(req->req.zero ||
- ((req->req.length %
- ep->ep.maxpacket) != 0));
- if (expect != ep->in_fifo_validate)
- valid = 0;
- }
- queue_dma(ep, req, valid);
- }
+ if (list_empty(&ep->queue) && !ep->stopped &&
+ !((dev->quirks & PLX_SUPERSPEED) && ep->dma &&
+ (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) {
+
/* use DMA if the endpoint supports it, else pio */
- else if (ep->dma)
+ if (ep->dma)
start_dma(ep, req);
else {
/* maybe there's no control data, just status ack */
@@ -1084,8 +1024,6 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount,
done(ep, req, status);
}
-static void restart_dma(struct net2280_ep *ep);
-
static void scan_dma_completions(struct net2280_ep *ep)
{
/* only look at descriptors that were "naturally" retired,
@@ -1117,9 +1055,8 @@ static void scan_dma_completions(struct net2280_ep *ep)
dma_done(ep, req, tmp, 0);
break;
} else if (!ep->is_in &&
- (req->req.length % ep->ep.maxpacket) != 0) {
- if (ep->dev->quirks & PLX_SUPERSPEED)
- return dma_done(ep, req, tmp, 0);
+ (req->req.length % ep->ep.maxpacket) &&
+ !(ep->dev->quirks & PLX_SUPERSPEED)) {
tmp = readl(&ep->regs->ep_stat);
/* AVOID TROUBLE HERE by not issuing short reads from
@@ -1150,67 +1087,15 @@ static void scan_dma_completions(struct net2280_ep *ep)
static void restart_dma(struct net2280_ep *ep)
{
struct net2280_request *req;
- u32 dmactl = dmactl_default;
if (ep->stopped)
return;
req = list_entry(ep->queue.next, struct net2280_request, queue);
- if (!use_dma_chaining) {
- start_dma(ep, req);
- return;
- }
-
- /* the 2280 will be processing the queue unless queue hiccups after
- * the previous transfer:
- * IN: wanted automagic zlp, head doesn't (or vice versa)
- * DMA_FIFO_VALIDATE doesn't init from dma descriptors.
- * OUT: was "usb-short", we must restart.
- */
- if (ep->is_in && !req->valid) {
- struct net2280_request *entry, *prev = NULL;
- int reqmode, done = 0;
-
- ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
- ep->in_fifo_validate = likely(req->req.zero ||
- (req->req.length % ep->ep.maxpacket) != 0);
- if (ep->in_fifo_validate)
- dmactl |= BIT(DMA_FIFO_VALIDATE);
- list_for_each_entry(entry, &ep->queue, queue) {
- __le32 dmacount;
-
- if (entry == req)
- continue;
- dmacount = entry->td->dmacount;
- if (!done) {
- reqmode = likely(entry->req.zero ||
- (entry->req.length % ep->ep.maxpacket));
- if (reqmode == ep->in_fifo_validate) {
- entry->valid = 1;
- dmacount |= valid_bit;
- entry->td->dmacount = dmacount;
- prev = entry;
- continue;
- } else {
- /* force a hiccup */
- prev->td->dmacount |= dma_done_ie;
- done = 1;
- }
- }
-
- /* walk the rest of the queue so unlinks behave */
- entry->valid = 0;
- dmacount &= ~valid_bit;
- entry->td->dmacount = dmacount;
- prev = entry;
- }
- }
-
- writel(0, &ep->dma->dmactl);
- start_queue(ep, dmactl, req->td_dma);
+ start_dma(ep, req);
}
-static void abort_dma_228x(struct net2280_ep *ep)
+static void abort_dma(struct net2280_ep *ep)
{
/* abort the current transfer */
if (likely(!list_empty(&ep->queue))) {
@@ -1222,19 +1107,6 @@ static void abort_dma_228x(struct net2280_ep *ep)
scan_dma_completions(ep);
}
-static void abort_dma_338x(struct net2280_ep *ep)
-{
- writel(BIT(DMA_ABORT), &ep->dma->dmastat);
- spin_stop_dma(ep->dma);
-}
-
-static void abort_dma(struct net2280_ep *ep)
-{
- if (ep->dev->quirks & PLX_LEGACY)
- return abort_dma_228x(ep);
- return abort_dma_338x(ep);
-}
-
/* dequeue ALL requests */
static void nuke(struct net2280_ep *ep)
{
@@ -1306,25 +1178,6 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
done(ep, req, -ECONNRESET);
}
req = NULL;
-
- /* patch up hardware chaining data */
- } else if (ep->dma && use_dma_chaining) {
- if (req->queue.prev == ep->queue.next) {
- writel(le32_to_cpu(req->td->dmadesc),
- &ep->dma->dmadesc);
- if (req->td->dmacount & dma_done_ie)
- writel(readl(&ep->dma->dmacount) |
- le32_to_cpu(dma_done_ie),
- &ep->dma->dmacount);
- } else {
- struct net2280_request *prev;
-
- prev = list_entry(req->queue.prev,
- struct net2280_request, queue);
- prev->td->dmadesc = req->td->dmadesc;
- if (req->td->dmacount & dma_done_ie)
- prev->td->dmacount |= dma_done_ie;
- }
}
if (req)
@@ -1512,10 +1365,10 @@ static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value)
tmp = readl(&dev->usb->usbctl);
if (value) {
tmp |= BIT(SELF_POWERED_STATUS);
- dev->selfpowered = 1;
+ _gadget->is_selfpowered = 1;
} else {
tmp &= ~BIT(SELF_POWERED_STATUS);
- dev->selfpowered = 0;
+ _gadget->is_selfpowered = 0;
}
writel(tmp, &dev->usb->usbctl);
spin_unlock_irqrestore(&dev->lock, flags);
@@ -1604,14 +1457,11 @@ static ssize_t registers_show(struct device *_dev,
/* Main Control Registers */
t = scnprintf(next, size, "%s version " DRIVER_VERSION
- ", chiprev %04x, dma %s\n\n"
+ ", chiprev %04x\n\n"
"devinit %03x fifoctl %08x gadget '%s'\n"
"pci irqenb0 %02x irqenb1 %08x "
"irqstat0 %04x irqstat1 %08x\n",
driver_name, dev->chiprev,
- use_dma
- ? (use_dma_chaining ? "chaining" : "enabled")
- : "disabled",
readl(&dev->regs->devinit),
readl(&dev->regs->fifoctl),
s,
@@ -1913,76 +1763,73 @@ static void defect7374_disable_data_eps(struct net2280 *dev)
static void defect7374_enable_data_eps_zero(struct net2280 *dev)
{
u32 tmp = 0, tmp_reg;
- u32 fsmvalue, scratch;
+ u32 scratch;
int i;
unsigned char ep_sel;
scratch = get_idx_reg(dev->regs, SCRATCH);
- fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+
+ WARN_ON((scratch & (0xf << DEFECT7374_FSM_FIELD))
+ == DEFECT7374_FSM_SS_CONTROL_READ);
+
scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
- /*See if firmware needs to set up for workaround*/
- if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
- ep_warn(dev, "Operate Defect 7374 workaround soft this time");
- ep_warn(dev, "It will operate on cold-reboot and SS connect");
-
- /*GPEPs:*/
- tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
- (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
- ((dev->enhanced_mode) ?
- BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
- BIT(IN_ENDPOINT_ENABLE));
-
- for (i = 1; i < 5; i++)
- writel(tmp, &dev->ep[i].cfg->ep_cfg);
-
- /* CSRIN, PCIIN, STATIN, RCIN*/
- tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
- writel(tmp, &dev->dep[1].dep_cfg);
- writel(tmp, &dev->dep[3].dep_cfg);
- writel(tmp, &dev->dep[4].dep_cfg);
- writel(tmp, &dev->dep[5].dep_cfg);
-
- /*Implemented for development and debug.
- * Can be refined/tuned later.*/
- for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
- /* Select an endpoint for subsequent operations: */
- tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
- writel(((tmp_reg & ~0x1f) | ep_sel),
- &dev->plregs->pl_ep_ctrl);
-
- if (ep_sel == 1) {
- tmp =
- (readl(&dev->plregs->pl_ep_ctrl) |
- BIT(CLEAR_ACK_ERROR_CODE) | 0);
- writel(tmp, &dev->plregs->pl_ep_ctrl);
- continue;
- }
+ ep_warn(dev, "Operate Defect 7374 workaround soft this time");
+ ep_warn(dev, "It will operate on cold-reboot and SS connect");
- if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
- ep_sel == 18 || ep_sel == 20)
- continue;
+ /*GPEPs:*/
+ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
+ (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
+ ((dev->enhanced_mode) ?
+ BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
+ BIT(IN_ENDPOINT_ENABLE));
- tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
- BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
- writel(tmp, &dev->plregs->pl_ep_cfg_4);
+ for (i = 1; i < 5; i++)
+ writel(tmp, &dev->ep[i].cfg->ep_cfg);
- tmp = readl(&dev->plregs->pl_ep_ctrl) &
- ~BIT(EP_INITIALIZED);
- writel(tmp, &dev->plregs->pl_ep_ctrl);
+ /* CSRIN, PCIIN, STATIN, RCIN*/
+ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
+ writel(tmp, &dev->dep[1].dep_cfg);
+ writel(tmp, &dev->dep[3].dep_cfg);
+ writel(tmp, &dev->dep[4].dep_cfg);
+ writel(tmp, &dev->dep[5].dep_cfg);
+
+ /*Implemented for development and debug.
+ * Can be refined/tuned later.*/
+ for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+ /* Select an endpoint for subsequent operations: */
+ tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+ writel(((tmp_reg & ~0x1f) | ep_sel),
+ &dev->plregs->pl_ep_ctrl);
+ if (ep_sel == 1) {
+ tmp =
+ (readl(&dev->plregs->pl_ep_ctrl) |
+ BIT(CLEAR_ACK_ERROR_CODE) | 0);
+ writel(tmp, &dev->plregs->pl_ep_ctrl);
+ continue;
}
- /* Set FSM to focus on the first Control Read:
- * - Tip: Connection speed is known upon the first
- * setup request.*/
- scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
- set_idx_reg(dev->regs, SCRATCH, scratch);
+ if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
+ ep_sel == 18 || ep_sel == 20)
+ continue;
+
+ tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
+ BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
+ writel(tmp, &dev->plregs->pl_ep_cfg_4);
+
+ tmp = readl(&dev->plregs->pl_ep_ctrl) &
+ ~BIT(EP_INITIALIZED);
+ writel(tmp, &dev->plregs->pl_ep_ctrl);
- } else{
- ep_warn(dev, "Defect 7374 workaround soft will NOT operate");
- ep_warn(dev, "It will operate on cold-reboot and SS connect");
}
+
+ /* Set FSM to focus on the first Control Read:
+ * - Tip: Connection speed is known upon the first
+ * setup request.*/
+ scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
+ set_idx_reg(dev->regs, SCRATCH, scratch);
+
}
/* keeping it simple:
@@ -2033,21 +1880,13 @@ static void usb_reset_228x(struct net2280 *dev)
static void usb_reset_338x(struct net2280 *dev)
{
u32 tmp;
- u32 fsmvalue;
dev->gadget.speed = USB_SPEED_UNKNOWN;
(void)readl(&dev->usb->usbctl);
net2280_led_init(dev);
- fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
- (0xf << DEFECT7374_FSM_FIELD);
-
- /* See if firmware needs to set up for workaround: */
- if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
- ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__,
- fsmvalue);
- } else {
+ if (dev->bug7734_patched) {
/* disable automatic responses, and irqs */
writel(0, &dev->usb->stdrsp);
writel(0, &dev->regs->pciirqenb0);
@@ -2064,7 +1903,7 @@ static void usb_reset_338x(struct net2280 *dev)
writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
- if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+ if (dev->bug7734_patched) {
/* reset, and enable pci */
tmp = readl(&dev->regs->devinit) |
BIT(PCI_ENABLE) |
@@ -2093,10 +1932,6 @@ static void usb_reset(struct net2280 *dev)
static void usb_reinit_228x(struct net2280 *dev)
{
u32 tmp;
- int init_dma;
-
- /* use_dma changes are ignored till next device re-init */
- init_dma = use_dma;
/* basic endpoint init */
for (tmp = 0; tmp < 7; tmp++) {
@@ -2108,8 +1943,7 @@ static void usb_reinit_228x(struct net2280 *dev)
if (tmp > 0 && tmp <= 4) {
ep->fifo_size = 1024;
- if (init_dma)
- ep->dma = &dev->dma[tmp - 1];
+ ep->dma = &dev->dma[tmp - 1];
} else
ep->fifo_size = 64;
ep->regs = &dev->epregs[tmp];
@@ -2133,17 +1967,12 @@ static void usb_reinit_228x(struct net2280 *dev)
static void usb_reinit_338x(struct net2280 *dev)
{
- int init_dma;
int i;
u32 tmp, val;
- u32 fsmvalue;
static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
0x00, 0xC0, 0x00, 0xC0 };
- /* use_dma changes are ignored till next device re-init */
- init_dma = use_dma;
-
/* basic endpoint init */
for (i = 0; i < dev->n_ep; i++) {
struct net2280_ep *ep = &dev->ep[i];
@@ -2152,7 +1981,7 @@ static void usb_reinit_338x(struct net2280 *dev)
ep->dev = dev;
ep->num = i;
- if (i > 0 && i <= 4 && init_dma)
+ if (i > 0 && i <= 4)
ep->dma = &dev->dma[i - 1];
if (dev->enhanced_mode) {
@@ -2177,14 +2006,7 @@ static void usb_reinit_338x(struct net2280 *dev)
dev->ep[0].stopped = 0;
/* Link layer set up */
- fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
- (0xf << DEFECT7374_FSM_FIELD);
-
- /* See if driver needs to set up for workaround: */
- if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
- ep_info(dev, "%s: Defect 7374 FsmValue %08x\n",
- __func__, fsmvalue);
- else {
+ if (dev->bug7734_patched) {
tmp = readl(&dev->usb_ext->usbctl2) &
~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE));
writel(tmp, &dev->usb_ext->usbctl2);
@@ -2291,15 +2113,8 @@ static void ep0_start_228x(struct net2280 *dev)
static void ep0_start_338x(struct net2280 *dev)
{
- u32 fsmvalue;
-
- fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
- (0xf << DEFECT7374_FSM_FIELD);
- if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
- ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__,
- fsmvalue);
- else
+ if (dev->bug7734_patched)
writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
BIT(SET_EP_HIDE_STATUS_PHASE),
&dev->epregs[0].ep_rsp);
@@ -2382,16 +2197,12 @@ static int net2280_start(struct usb_gadget *_gadget,
if (retval)
goto err_func;
- /* Enable force-full-speed testing mode, if desired */
- if (full_speed && (dev->quirks & PLX_LEGACY))
- writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag);
-
- /* ... then enable host detection and ep0; and we're ready
+ /* enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
*/
net2280_led_active(dev, 1);
- if (dev->quirks & PLX_SUPERSPEED)
+ if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
defect7374_enable_data_eps_zero(dev);
ep0_start(dev);
@@ -2444,10 +2255,6 @@ static int net2280_stop(struct usb_gadget *_gadget)
net2280_led_active(dev, 0);
- /* Disable full-speed test mode */
- if (dev->quirks & PLX_LEGACY)
- writel(0, &dev->usb->xcvrdiag);
-
device_remove_file(&dev->pdev->dev, &dev_attr_function);
device_remove_file(&dev->pdev->dev, &dev_attr_queues);
@@ -2478,10 +2285,10 @@ static void handle_ep_small(struct net2280_ep *ep)
/* ack all, and handle what we care about */
t = readl(&ep->regs->ep_stat);
ep->irqs++;
-#if 0
+
ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n",
- ep->ep.name, t, req ? &req->req : 0);
-#endif
+ ep->ep.name, t, req ? &req->req : NULL);
+
if (!ep->is_in || (ep->dev->quirks & PLX_2280))
writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat);
else
@@ -2717,6 +2524,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
* run after the next USB connection.
*/
scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+ dev->bug7734_patched = 1;
goto restore_data_eps;
}
@@ -2730,6 +2538,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
if ((state >= (ACK_GOOD_NORMAL << STATE)) &&
(state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+ dev->bug7734_patched = 1;
break;
}
@@ -2766,80 +2575,19 @@ restore_data_eps:
return;
}
-static void ep_stall(struct net2280_ep *ep, int stall)
+static void ep_clear_seqnum(struct net2280_ep *ep)
{
struct net2280 *dev = ep->dev;
u32 val;
static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
- if (stall) {
- writel(BIT(SET_ENDPOINT_HALT) |
- /* BIT(SET_NAK_PACKETS) | */
- BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
- &ep->regs->ep_rsp);
- ep->is_halt = 1;
- } else {
- if (dev->gadget.speed == USB_SPEED_SUPER) {
- /*
- * Workaround for SS SeqNum not cleared via
- * Endpoint Halt (Clear) bit. select endpoint
- */
- val = readl(&dev->plregs->pl_ep_ctrl);
- val = (val & ~0x1f) | ep_pl[ep->num];
- writel(val, &dev->plregs->pl_ep_ctrl);
-
- val |= BIT(SEQUENCE_NUMBER_RESET);
- writel(val, &dev->plregs->pl_ep_ctrl);
- }
- val = readl(&ep->regs->ep_rsp);
- val |= BIT(CLEAR_ENDPOINT_HALT) |
- BIT(CLEAR_ENDPOINT_TOGGLE);
- writel(val,
- /* | BIT(CLEAR_NAK_PACKETS),*/
- &ep->regs->ep_rsp);
- ep->is_halt = 0;
- val = readl(&ep->regs->ep_rsp);
- }
-}
-
-static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged)
-{
- /* set/clear, then synch memory views with the device */
- if (value) {
- ep->stopped = 1;
- if (ep->num == 0)
- ep->dev->protocol_stall = 1;
- else {
- if (ep->dma)
- ep_stop_dma(ep);
- ep_stall(ep, true);
- }
-
- if (wedged)
- ep->wedged = 1;
- } else {
- ep->stopped = 0;
- ep->wedged = 0;
-
- ep_stall(ep, false);
+ val = readl(&dev->plregs->pl_ep_ctrl) & ~0x1f;
+ val |= ep_pl[ep->num];
+ writel(val, &dev->plregs->pl_ep_ctrl);
+ val |= BIT(SEQUENCE_NUMBER_RESET);
+ writel(val, &dev->plregs->pl_ep_ctrl);
- /* Flush the queue */
- if (!list_empty(&ep->queue)) {
- struct net2280_request *req =
- list_entry(ep->queue.next, struct net2280_request,
- queue);
- if (ep->dma)
- resume_dma(ep);
- else {
- if (ep->is_in)
- write_fifo(ep, &req->req);
- else {
- if (read_fifo(ep, req))
- done(ep, req, 0);
- }
- }
- }
- }
+ return;
}
static void handle_stat0_irqs_superspeed(struct net2280 *dev,
@@ -2863,7 +2611,7 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
switch (r.bRequestType) {
case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
status = dev->wakeup_enable ? 0x02 : 0x00;
- if (dev->selfpowered)
+ if (dev->gadget.is_selfpowered)
status |= BIT(0);
status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
dev->ltm_enable << 4);
@@ -2940,7 +2688,12 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
if (w_value != USB_ENDPOINT_HALT)
goto do_stall3;
ep_vdbg(dev, "%s clear halt\n", e->ep.name);
- ep_stall(e, false);
+ /*
+ * Workaround for SS SeqNum not cleared via
+ * Endpoint Halt (Clear) bit. select endpoint
+ */
+ ep_clear_seqnum(e);
+ clear_halt(e);
if (!list_empty(&e->queue) && e->td_dma)
restart_dma(e);
allow_status(ep);
@@ -2998,7 +2751,14 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
e = get_ep_by_addr(dev, w_index);
if (!e || (w_value != USB_ENDPOINT_HALT))
goto do_stall3;
- ep_stdrsp(e, true, false);
+ ep->stopped = 1;
+ if (ep->num == 0)
+ ep->dev->protocol_stall = 1;
+ else {
+ if (ep->dma)
+ abort_dma(ep);
+ set_halt(ep);
+ }
allow_status_338x(ep);
break;
@@ -3026,7 +2786,7 @@ do_stall3:
r.bRequestType, r.bRequest, tmp);
dev->protocol_stall = 1;
/* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
- ep_stall(ep, true);
+ set_halt(ep);
}
next_endpoints3:
@@ -3091,9 +2851,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
}
ep->stopped = 0;
dev->protocol_stall = 0;
- if (dev->quirks & PLX_SUPERSPEED)
- ep->is_halt = 0;
- else{
+ if (!(dev->quirks & PLX_SUPERSPEED)) {
if (ep->dev->quirks & PLX_2280)
tmp = BIT(FIFO_OVERFLOW) |
BIT(FIFO_UNDERFLOW);
@@ -3120,7 +2878,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
cpu_to_le32s(&u.raw[0]);
cpu_to_le32s(&u.raw[1]);
- if (dev->quirks & PLX_SUPERSPEED)
+ if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
defect7374_workaround(dev, u.r);
tmp = 0;
@@ -3423,17 +3181,12 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
continue;
}
- /* chaining should stop on abort, short OUT from fifo,
- * or (stat0 codepath) short OUT transfer.
- */
- if (!use_dma_chaining) {
- if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
- ep_dbg(ep->dev, "%s no xact done? %08x\n",
- ep->ep.name, tmp);
- continue;
- }
- stop_dma(ep->dma);
+ if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
+ ep_dbg(ep->dev, "%s no xact done? %08x\n",
+ ep->ep.name, tmp);
+ continue;
}
+ stop_dma(ep->dma);
/* OUT transfers terminate when the data from the
* host is in our memory. Process whatever's done.
@@ -3448,30 +3201,9 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
scan_dma_completions(ep);
/* disable dma on inactive queues; else maybe restart */
- if (list_empty(&ep->queue)) {
- if (use_dma_chaining)
- stop_dma(ep->dma);
- } else {
+ if (!list_empty(&ep->queue)) {
tmp = readl(&dma->dmactl);
- if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0)
- restart_dma(ep);
- else if (ep->is_in && use_dma_chaining) {
- struct net2280_request *req;
- __le32 dmacount;
-
- /* the descriptor at the head of the chain
- * may still have VALID_BIT clear; that's
- * used to trigger changing DMA_FIFO_VALIDATE
- * (affects automagic zlp writes).
- */
- req = list_entry(ep->queue.next,
- struct net2280_request, queue);
- dmacount = req->td->dmacount;
- dmacount &= cpu_to_le32(BIT(VALID_BIT) |
- DMA_BYTE_COUNT_MASK);
- if (dmacount && (dmacount & valid_bit) == 0)
- restart_dma(ep);
- }
+ restart_dma(ep);
}
ep->irqs++;
}
@@ -3556,7 +3288,7 @@ static void net2280_remove(struct pci_dev *pdev)
}
if (dev->got_irq)
free_irq(pdev->irq, dev);
- if (use_msi && dev->quirks & PLX_SUPERSPEED)
+ if (dev->quirks & PLX_SUPERSPEED)
pci_disable_msi(pdev);
if (dev->regs)
iounmap(dev->regs);
@@ -3581,9 +3313,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
void __iomem *base = NULL;
int retval, i;
- if (!use_dma)
- use_dma_chaining = 0;
-
/* alloc, and start init */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
@@ -3663,9 +3392,12 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
(0xf << DEFECT7374_FSM_FIELD);
/* See if firmware needs to set up for workaround: */
- if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ)
+ if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+ dev->bug7734_patched = 1;
writel(0, &dev->usb->usbctl);
- } else{
+ } else
+ dev->bug7734_patched = 0;
+ } else {
dev->enhanced_mode = 0;
dev->n_ep = 7;
/* put into initial config, link up all endpoints */
@@ -3682,7 +3414,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto done;
}
- if (use_msi && (dev->quirks & PLX_SUPERSPEED))
+ if (dev->quirks & PLX_SUPERSPEED)
if (pci_enable_msi(pdev))
ep_err(dev, "Failed to enable MSI mode\n");
@@ -3741,9 +3473,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ep_info(dev, "%s\n", driver_desc);
ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n",
pdev->irq, base, dev->chiprev);
- ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n",
- use_dma ? (use_dma_chaining ? "chaining" : "enabled")
- : "disabled",
+ ep_info(dev, "version: " DRIVER_VERSION "; %s\n",
dev->enhanced_mode ? "enhanced mode" : "legacy mode");
retval = device_create_file(&pdev->dev, &dev_attr_registers);
if (retval)
@@ -3776,9 +3506,6 @@ static void net2280_shutdown(struct pci_dev *pdev)
/* disable the pullup so the host will think we're gone */
writel(0, &dev->usb->usbctl);
- /* Disable full-speed test mode */
- if (dev->quirks & PLX_LEGACY)
- writel(0, &dev->usb->xcvrdiag);
}
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
index 03f15242d794..ac8d5a20a378 100644
--- a/drivers/usb/gadget/udc/net2280.h
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -100,7 +100,6 @@ struct net2280_ep {
dma_addr_t td_dma; /* of dummy */
struct net2280 *dev;
unsigned long irqs;
- unsigned is_halt:1, dma_started:1;
/* analogous to a host-side qh */
struct list_head queue;
@@ -126,7 +125,7 @@ static inline void allow_status(struct net2280_ep *ep)
ep->stopped = 1;
}
-static void allow_status_338x(struct net2280_ep *ep)
+static inline void allow_status_338x(struct net2280_ep *ep)
{
/*
* Control Status Phase Handshake was set by the chip when the setup
@@ -165,8 +164,8 @@ struct net2280 {
u2_enable:1,
ltm_enable:1,
wakeup_enable:1,
- selfpowered:1,
- addressed_state:1;
+ addressed_state:1,
+ bug7734_patched:1;
u16 chiprev;
int enhanced_mode;
int n_ep;
@@ -356,23 +355,6 @@ static inline void start_out_naking(struct net2280_ep *ep)
readl(&ep->regs->ep_rsp);
}
-#ifdef DEBUG
-static inline void assert_out_naking(struct net2280_ep *ep, const char *where)
-{
- u32 tmp = readl(&ep->regs->ep_stat);
-
- if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
- ep_dbg(ep->dev, "%s %s %08x !NAK\n",
- ep->ep.name, where, tmp);
- writel(BIT(SET_NAK_OUT_PACKETS),
- &ep->regs->ep_rsp);
- }
-}
-#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__)
-#else
-#define ASSERT_OUT_NAKING(ep) do {} while (0)
-#endif
-
static inline void stop_out_naking(struct net2280_ep *ep)
{
u32 tmp;
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 288c087220a8..e2fcdb8e5596 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -1171,6 +1171,7 @@ omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
unsigned long flags;
u16 syscon1;
+ gadget->is_selfpowered = (is_selfpowered != 0);
udc = container_of(gadget, struct omap_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
syscon1 = omap_readw(UDC_SYSCON1);
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index 1c7379ac2379..613547f07828 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -1161,6 +1161,7 @@ static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value)
if (!gadget)
return -EINVAL;
+ gadget->is_selfpowered = (value != 0);
dev = container_of(gadget, struct pch_udc_dev, gadget);
if (value)
pch_udc_set_selfpowered(dev);
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 8550b2d5db32..f6cbe667ce39 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -1272,7 +1272,6 @@ static int pxa25x_udc_start(struct usb_gadget *g,
goto bind_fail;
}
- pullup(dev);
dump_state(dev);
return 0;
bind_fail:
@@ -1339,7 +1338,6 @@ static int pxa25x_udc_stop(struct usb_gadget*g)
local_irq_disable();
dev->pullup = 0;
- pullup(dev);
stop_activity(dev, NULL);
local_irq_enable();
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index c61a896061fa..6a855fc9bd84 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -1809,7 +1809,6 @@ static int pxa27x_udc_start(struct usb_gadget *g,
/* first hook up the driver ... */
udc->driver = driver;
- dplus_pullup(udc, 1);
if (!IS_ERR_OR_NULL(udc->transceiver)) {
retval = otg_set_peripheral(udc->transceiver->otg,
@@ -1862,7 +1861,6 @@ static int pxa27x_udc_stop(struct usb_gadget *g)
stop_activity(udc, NULL);
udc_disable(udc);
- dplus_pullup(udc, 0);
udc->driver = NULL;
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index 06870da0b988..2495fe9c95c5 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1803,6 +1803,7 @@ static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self)
{
struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
+ gadget->is_selfpowered = (is_self != 0);
if (is_self)
r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED;
else
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index 824cf12e9add..b808951491cc 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -238,14 +238,6 @@ static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
S3C2410_UDC_EP0_CSR_REG);
}
-static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base)
-{
- udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
- udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
- | S3C2410_UDC_EP0_CSR_SSE),
- S3C2410_UDC_EP0_CSR_REG);
-}
-
static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
{
udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
@@ -291,18 +283,6 @@ static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
}
}
-static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev)
-{
- unsigned i;
-
- /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
- * fifos, and pending transactions mustn't be continued in any case.
- */
-
- for (i = 1; i < S3C2410_ENDPOINTS; i++)
- s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED);
-}
-
static inline int s3c2410_udc_fifo_count_out(void)
{
int tmp;
@@ -1454,6 +1434,7 @@ static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
+ gadget->is_selfpowered = (value != 0);
if (value)
udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
else
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index e31d574d8860..5a81cb086b99 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -564,6 +564,7 @@ static USB_UDC_ATTR(is_a_peripheral);
static USB_UDC_ATTR(b_hnp_enable);
static USB_UDC_ATTR(a_hnp_support);
static USB_UDC_ATTR(a_alt_hnp_support);
+static USB_UDC_ATTR(is_selfpowered);
static struct attribute *usb_udc_attrs[] = {
&dev_attr_srp.attr,
@@ -577,6 +578,7 @@ static struct attribute *usb_udc_attrs[] = {
&dev_attr_b_hnp_enable.attr,
&dev_attr_a_hnp_support.attr,
&dev_attr_a_alt_hnp_support.attr,
+ &dev_attr_is_selfpowered.attr,
NULL,
};
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index fafc628480e0..5ad60e46dc2b 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -219,7 +219,7 @@ config USB_EHCI_TEGRA
config USB_EHCI_HCD_PPC_OF
bool "EHCI support for PPC USB controller on OF platform bus"
- depends on PPC_OF
+ depends on PPC
default y
---help---
Enables support for the USB controller present on the PowerPC
@@ -331,20 +331,6 @@ config USB_ISP116X_HCD
To compile this driver as a module, choose M here: the
module will be called isp116x-hcd.
-config USB_ISP1760_HCD
- tristate "ISP 1760 HCD support"
- ---help---
- The ISP1760 chip is a USB 2.0 host controller.
-
- This driver does not support isochronous transfers or OTG.
- This USB controller is usually attached to a non-DMA-Master
- capable bus. NXP's eval kit brings this chip on PCI card
- where the chip itself is behind a PLB to simulate such
- a bus.
-
- To compile this driver as a module, choose M here: the
- module will be called isp1760.
-
config USB_ISP1362_HCD
tristate "ISP1362 HCD support"
---help---
@@ -494,7 +480,7 @@ config USB_OHCI_ATH79
config USB_OHCI_HCD_PPC_OF_BE
bool "OHCI support for OF platform bus (big endian)"
- depends on PPC_OF
+ depends on PPC
select USB_OHCI_BIG_ENDIAN_DESC
select USB_OHCI_BIG_ENDIAN_MMIO
---help---
@@ -503,7 +489,7 @@ config USB_OHCI_HCD_PPC_OF_BE
config USB_OHCI_HCD_PPC_OF_LE
bool "OHCI support for OF platform bus (little endian)"
- depends on PPC_OF
+ depends on PPC
select USB_OHCI_LITTLE_ENDIAN
---help---
Enables support for little-endian USB controllers present on the
@@ -511,7 +497,7 @@ config USB_OHCI_HCD_PPC_OF_LE
config USB_OHCI_HCD_PPC_OF
bool
- depends on PPC_OF
+ depends on PPC
default USB_OHCI_HCD_PPC_OF_BE || USB_OHCI_HCD_PPC_OF_LE
config USB_OHCI_HCD_PCI
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index d6216a493bab..65b0b6a58599 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -5,8 +5,6 @@
# tell define_trace.h where to find the xhci trace header
CFLAGS_xhci-trace.o := -I$(src)
-isp1760-y := isp1760-hcd.o isp1760-if.o
-
fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o
fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o
@@ -69,7 +67,6 @@ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
-obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o
obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index 56a88506febe..663f7908b15c 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -27,44 +27,66 @@
#define DRIVER_DESC "EHCI Atmel driver"
static const char hcd_name[] = "ehci-atmel";
-static struct hc_driver __read_mostly ehci_atmel_hc_driver;
/* interface and function clocks */
-static struct clk *iclk, *fclk, *uclk;
-static int clocked;
+#define hcd_to_atmel_ehci_priv(h) \
+ ((struct atmel_ehci_priv *)hcd_to_ehci(h)->priv)
+
+struct atmel_ehci_priv {
+ struct clk *iclk;
+ struct clk *fclk;
+ struct clk *uclk;
+ bool clocked;
+};
+
+static struct hc_driver __read_mostly ehci_atmel_hc_driver;
+
+static const struct ehci_driver_overrides ehci_atmel_drv_overrides __initconst = {
+ .extra_priv_size = sizeof(struct atmel_ehci_priv),
+};
/*-------------------------------------------------------------------------*/
-static void atmel_start_clock(void)
+static void atmel_start_clock(struct atmel_ehci_priv *atmel_ehci)
{
+ if (atmel_ehci->clocked)
+ return;
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
- clk_set_rate(uclk, 48000000);
- clk_prepare_enable(uclk);
+ clk_set_rate(atmel_ehci->uclk, 48000000);
+ clk_prepare_enable(atmel_ehci->uclk);
}
- clk_prepare_enable(iclk);
- clk_prepare_enable(fclk);
- clocked = 1;
+ clk_prepare_enable(atmel_ehci->iclk);
+ clk_prepare_enable(atmel_ehci->fclk);
+ atmel_ehci->clocked = true;
}
-static void atmel_stop_clock(void)
+static void atmel_stop_clock(struct atmel_ehci_priv *atmel_ehci)
{
- clk_disable_unprepare(fclk);
- clk_disable_unprepare(iclk);
+ if (!atmel_ehci->clocked)
+ return;
+ clk_disable_unprepare(atmel_ehci->fclk);
+ clk_disable_unprepare(atmel_ehci->iclk);
if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_disable_unprepare(uclk);
- clocked = 0;
+ clk_disable_unprepare(atmel_ehci->uclk);
+ atmel_ehci->clocked = false;
}
static void atmel_start_ehci(struct platform_device *pdev)
{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
dev_dbg(&pdev->dev, "start\n");
- atmel_start_clock();
+ atmel_start_clock(atmel_ehci);
}
static void atmel_stop_ehci(struct platform_device *pdev)
{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
dev_dbg(&pdev->dev, "stop\n");
- atmel_stop_clock();
+ atmel_stop_clock(atmel_ehci);
}
/*-------------------------------------------------------------------------*/
@@ -75,6 +97,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
const struct hc_driver *driver = &ehci_atmel_hc_driver;
struct resource *res;
struct ehci_hcd *ehci;
+ struct atmel_ehci_priv *atmel_ehci;
int irq;
int retval;
@@ -105,6 +128,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
retval = -ENOMEM;
goto fail_create_hcd;
}
+ atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->regs = devm_ioremap_resource(&pdev->dev, res);
@@ -116,23 +140,23 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- iclk = devm_clk_get(&pdev->dev, "ehci_clk");
- if (IS_ERR(iclk)) {
+ atmel_ehci->iclk = devm_clk_get(&pdev->dev, "ehci_clk");
+ if (IS_ERR(atmel_ehci->iclk)) {
dev_err(&pdev->dev, "Error getting interface clock\n");
retval = -ENOENT;
goto fail_request_resource;
}
- fclk = devm_clk_get(&pdev->dev, "uhpck");
- if (IS_ERR(fclk)) {
+ atmel_ehci->fclk = devm_clk_get(&pdev->dev, "uhpck");
+ if (IS_ERR(atmel_ehci->fclk)) {
dev_err(&pdev->dev, "Error getting function clock\n");
retval = -ENOENT;
goto fail_request_resource;
}
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
- uclk = devm_clk_get(&pdev->dev, "usb_clk");
- if (IS_ERR(uclk)) {
+ atmel_ehci->uclk = devm_clk_get(&pdev->dev, "usb_clk");
+ if (IS_ERR(atmel_ehci->uclk)) {
dev_err(&pdev->dev, "failed to get uclk\n");
- retval = PTR_ERR(uclk);
+ retval = PTR_ERR(atmel_ehci->uclk);
goto fail_request_resource;
}
}
@@ -169,11 +193,35 @@ static int ehci_atmel_drv_remove(struct platform_device *pdev)
usb_put_hcd(hcd);
atmel_stop_ehci(pdev);
- fclk = iclk = NULL;
return 0;
}
+#ifdef CONFIG_PM
+static int ehci_atmel_drv_suspend(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+ int ret;
+
+ ret = ehci_suspend(hcd, false);
+ if (ret)
+ return ret;
+
+ atmel_stop_clock(atmel_ehci);
+ return 0;
+}
+
+static int ehci_atmel_drv_resume(struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
+ atmel_start_clock(atmel_ehci);
+ return ehci_resume(hcd, false);
+}
+#endif
+
#ifdef CONFIG_OF
static const struct of_device_id atmel_ehci_dt_ids[] = {
{ .compatible = "atmel,at91sam9g45-ehci" },
@@ -183,12 +231,16 @@ static const struct of_device_id atmel_ehci_dt_ids[] = {
MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids);
#endif
+static SIMPLE_DEV_PM_OPS(ehci_atmel_pm_ops, ehci_atmel_drv_suspend,
+ ehci_atmel_drv_resume);
+
static struct platform_driver ehci_atmel_driver = {
.probe = ehci_atmel_drv_probe,
.remove = ehci_atmel_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "atmel-ehci",
+ .pm = &ehci_atmel_pm_ops,
.of_match_table = of_match_ptr(atmel_ehci_dt_ids),
},
};
@@ -199,7 +251,7 @@ static int __init ehci_atmel_init(void)
return -ENODEV;
pr_info("%s: " DRIVER_DESC "\n", hcd_name);
- ehci_init_driver(&ehci_atmel_hc_driver, NULL);
+ ehci_init_driver(&ehci_atmel_hc_driver, &ehci_atmel_drv_overrides);
return platform_driver_register(&ehci_atmel_driver);
}
module_init(ehci_atmel_init);
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index fb7bd0c7dc15..ab4eee3df97a 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -709,7 +709,6 @@ static struct platform_driver ehci_fsl_driver = {
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "fsl-ehci",
- .owner = THIS_MODULE,
.pm = EHCI_FSL_PM_OPS,
},
};
diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c
index 495b6fbcbcd9..21650044b09e 100644
--- a/drivers/usb/host/ehci-grlib.c
+++ b/drivers/usb/host/ehci-grlib.c
@@ -187,7 +187,6 @@ static struct platform_driver ehci_grlib_driver = {
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "grlib-ehci",
- .owner = THIS_MODULE,
.of_match_table = ehci_hcd_grlib_of_match,
},
};
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 38bfeedae1d0..85e56d1abd23 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1110,7 +1110,7 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
EXPORT_SYMBOL_GPL(ehci_suspend);
/* Returns 0 if power was preserved, 1 if power was lost */
-int ehci_resume(struct usb_hcd *hcd, bool hibernated)
+int ehci_resume(struct usb_hcd *hcd, bool force_reset)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
@@ -1124,12 +1124,12 @@ int ehci_resume(struct usb_hcd *hcd, bool hibernated)
return 0; /* Controller is dead */
/*
- * If CF is still set and we aren't resuming from hibernation
+ * If CF is still set and reset isn't forced
* then we maintained suspend power.
* Just undo the effect of ehci_suspend().
*/
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
- !hibernated) {
+ !force_reset) {
int mask = INTR_MASK;
ehci_prepare_ports_for_controller_resume(ehci);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 118edb7bdca2..87cf86f38b36 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -700,15 +700,15 @@ ehci_hub_descriptor (
memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
- temp = 0x0008; /* per-port overcurrent reporting */
+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
if (HCS_PPC (ehci->hcs_params))
- temp |= 0x0001; /* per-port power control */
+ temp |= HUB_CHAR_INDV_PORT_LPSM; /* per-port power control */
else
- temp |= 0x0002; /* no power switching */
+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */
#if 0
// re-enable when we support USB_PORT_FEAT_INDICATOR below.
if (HCS_INDICATOR (ehci->hcs_params))
- temp |= 0x0080; /* per-port indicators (LEDs) */
+ temp |= HUB_CHAR_PORTIND; /* per-port indicators (LEDs) */
#endif
desc->wHubCharacteristics = cpu_to_le16(temp);
}
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 851006a0d97b..2a5d2fd76040 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -43,6 +43,24 @@ static inline bool is_intel_quark_x1000(struct pci_dev *pdev)
}
/*
+ * This is the list of PCI IDs for the devices that have EHCI USB class and
+ * specific drivers for that. One of the example is a ChipIdea device installed
+ * on some Intel MID platforms.
+ */
+static const struct pci_device_id bypass_pci_id_table[] = {
+ /* ChipIdea on Intel MID platform */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe006), },
+ {}
+};
+
+static inline bool is_bypassed_id(struct pci_dev *pdev)
+{
+ return !!pci_match_id(bypass_pci_id_table, pdev);
+}
+
+/*
* 0x84 is the offset of in/out threshold register,
* and it is the same offset as the register of 'hostpc'.
*/
@@ -352,6 +370,13 @@ static const struct ehci_driver_overrides pci_overrides __initconst = {
/*-------------------------------------------------------------------------*/
+static int ehci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ if (is_bypassed_id(pdev))
+ return -ENODEV;
+ return usb_hcd_pci_probe(pdev, id);
+}
+
/* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids [] = { {
/* handle any USB 2.0 EHCI controller */
@@ -370,7 +395,7 @@ static struct pci_driver ehci_pci_driver = {
.name = (char *) hcd_name,
.id_table = pci_ids,
- .probe = usb_hcd_pci_probe,
+ .probe = ehci_pci_probe,
.remove = usb_hcd_pci_remove,
.shutdown = usb_hcd_pci_shutdown,
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 8557803e6154..d8a75a51d6d4 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -43,7 +43,8 @@
struct ehci_platform_priv {
struct clk *clks[EHCI_MAX_CLKS];
struct reset_control *rst;
- struct phy *phy;
+ struct phy **phys;
+ int num_phys;
};
static const char hcd_name[] = "ehci-platform";
@@ -78,7 +79,7 @@ static int ehci_platform_power_on(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
- int clk, ret;
+ int clk, ret, phy_num;
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
ret = clk_prepare_enable(priv->clks[clk]);
@@ -86,20 +87,28 @@ static int ehci_platform_power_on(struct platform_device *dev)
goto err_disable_clks;
}
- if (priv->phy) {
- ret = phy_init(priv->phy);
- if (ret)
- goto err_disable_clks;
-
- ret = phy_power_on(priv->phy);
- if (ret)
- goto err_exit_phy;
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ if (priv->phys[phy_num]) {
+ ret = phy_init(priv->phys[phy_num]);
+ if (ret)
+ goto err_exit_phy;
+ ret = phy_power_on(priv->phys[phy_num]);
+ if (ret) {
+ phy_exit(priv->phys[phy_num]);
+ goto err_exit_phy;
+ }
+ }
}
return 0;
err_exit_phy:
- phy_exit(priv->phy);
+ while (--phy_num >= 0) {
+ if (priv->phys[phy_num]) {
+ phy_power_off(priv->phys[phy_num]);
+ phy_exit(priv->phys[phy_num]);
+ }
+ }
err_disable_clks:
while (--clk >= 0)
clk_disable_unprepare(priv->clks[clk]);
@@ -111,11 +120,13 @@ static void ehci_platform_power_off(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
- int clk;
+ int clk, phy_num;
- if (priv->phy) {
- phy_power_off(priv->phy);
- phy_exit(priv->phy);
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ if (priv->phys[phy_num]) {
+ phy_power_off(priv->phys[phy_num]);
+ phy_exit(priv->phys[phy_num]);
+ }
}
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
@@ -143,7 +154,8 @@ static int ehci_platform_probe(struct platform_device *dev)
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ehci_platform_priv *priv;
struct ehci_hcd *ehci;
- int err, irq, clk = 0;
+ const char *phy_name;
+ int err, irq, phy_num, clk = 0;
if (usb_disabled())
return -ENODEV;
@@ -155,7 +167,8 @@ static int ehci_platform_probe(struct platform_device *dev)
if (!pdata)
pdata = &ehci_platform_defaults;
- err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
+ err = dma_coerce_mask_and_coherent(&dev->dev,
+ pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
if (err)
return err;
@@ -185,12 +198,42 @@ static int ehci_platform_probe(struct platform_device *dev)
if (of_property_read_bool(dev->dev.of_node, "big-endian"))
ehci->big_endian_mmio = ehci->big_endian_desc = 1;
- priv->phy = devm_phy_get(&dev->dev, "usb");
- if (IS_ERR(priv->phy)) {
- err = PTR_ERR(priv->phy);
- if (err == -EPROBE_DEFER)
- goto err_put_hcd;
- priv->phy = NULL;
+ if (of_property_read_bool(dev->dev.of_node,
+ "needs-reset-on-resume"))
+ pdata->reset_on_resume = 1;
+
+ priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
+ "phys", "#phy-cells");
+ priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1;
+
+ priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
+ sizeof(struct phy *), GFP_KERNEL);
+ if (!priv->phys)
+ return -ENOMEM;
+
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ err = of_property_read_string_index(
+ dev->dev.of_node,
+ "phy-names", phy_num,
+ &phy_name);
+
+ if (err < 0) {
+ if (priv->num_phys > 1) {
+ dev_err(&dev->dev, "phy-names not provided");
+ goto err_put_hcd;
+ } else
+ phy_name = "usb";
+ }
+
+ priv->phys[phy_num] = devm_phy_get(&dev->dev,
+ phy_name);
+ if (IS_ERR(priv->phys[phy_num])) {
+ err = PTR_ERR(priv->phys[phy_num]);
+ if ((priv->num_phys > 1) ||
+ (err == -EPROBE_DEFER))
+ goto err_put_hcd;
+ priv->phys[phy_num] = NULL;
+ }
}
for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
@@ -340,7 +383,7 @@ static int ehci_platform_resume(struct device *dev)
return err;
}
- ehci_resume(hcd, false);
+ ehci_resume(hcd, pdata->reset_on_resume);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
@@ -349,6 +392,7 @@ static const struct of_device_id vt8500_ehci_ids[] = {
{ .compatible = "via,vt8500-ehci", },
{ .compatible = "wm,prizm-ehci", },
{ .compatible = "generic-ehci", },
+ { .compatible = "cavium,octeon-6335-ehci", },
{}
};
MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c
index 7d75465d97c7..342816a7f8b1 100644
--- a/drivers/usb/host/ehci-pmcmsp.c
+++ b/drivers/usb/host/ehci-pmcmsp.c
@@ -325,6 +325,5 @@ static struct platform_driver ehci_hcd_msp_driver = {
.remove = ehci_hcd_msp_drv_remove,
.driver = {
.name = "pmcmsp-ehci",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
index 547924796d29..1a10c8d542ca 100644
--- a/drivers/usb/host/ehci-ppc-of.c
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -234,7 +234,6 @@ static struct platform_driver ehci_hcd_ppc_of_driver = {
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ppc-of-ehci",
- .owner = THIS_MODULE,
.of_match_table = ehci_hcd_ppc_of_match,
},
};
diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c
index 9b6e8d0eac43..3d86cc2ffe68 100644
--- a/drivers/usb/host/ehci-sead3.c
+++ b/drivers/usb/host/ehci-sead3.c
@@ -178,7 +178,6 @@ static struct platform_driver ehci_hcd_sead3_driver = {
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "sead3-ehci",
- .owner = THIS_MODULE,
.pm = SEAD3_EHCI_PMOPS,
}
};
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
index 0e0ce684aff3..5caf88d679e4 100644
--- a/drivers/usb/host/ehci-sh.c
+++ b/drivers/usb/host/ehci-sh.c
@@ -189,7 +189,6 @@ static struct platform_driver ehci_hcd_sh_driver = {
.shutdown = ehci_hcd_sh_shutdown,
.driver = {
.name = "sh_ehci",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c
index 0d247673c3ca..bdb93b6a356f 100644
--- a/drivers/usb/host/ehci-tilegx.c
+++ b/drivers/usb/host/ehci-tilegx.c
@@ -210,7 +210,6 @@ static struct platform_driver ehci_hcd_tilegx_driver = {
.shutdown = ehci_hcd_tilegx_drv_shutdown,
.driver = {
.name = "tilegx-ehci",
- .owner = THIS_MODULE,
}
};
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
index a2328361dc80..f54480850bb8 100644
--- a/drivers/usb/host/ehci-xilinx-of.c
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -236,7 +236,6 @@ static struct platform_driver ehci_hcd_xilinx_of_driver = {
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "xilinx-of-ehci",
- .owner = THIS_MODULE,
.of_match_table = ehci_hcd_xilinx_of_match,
},
};
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 6f0577b0a5ae..52ef0844a180 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -871,7 +871,7 @@ extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr,
#ifdef CONFIG_PM
extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
-extern int ehci_resume(struct usb_hcd *hcd, bool hibernated);
+extern int ehci_resume(struct usb_hcd *hcd, bool force_reset);
#endif /* CONFIG_PM */
extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c
index 6af2512f8378..70116a65262c 100644
--- a/drivers/usb/host/fhci-hub.c
+++ b/drivers/usb/host/fhci-hub.c
@@ -32,8 +32,8 @@ static u8 root_hub_des[] = {
0x09, /* blength */
0x29, /* bDescriptorType;hub-descriptor */
0x01, /* bNbrPorts */
- 0x00, /* wHubCharacteristics */
- 0x00,
+ HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM, /* wHubCharacteristics */
+ 0x00, /* per-port power, no overcurrent */
0x01, /* bPwrOn2pwrGood;2ms */
0x00, /* bHubContrCurrent;0mA */
0x00, /* DeviceRemoveable */
@@ -208,7 +208,6 @@ int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
{
struct fhci_hcd *fhci = hcd_to_fhci(hcd);
int retval = 0;
- int len = 0;
struct usb_hub_status *hub_status;
struct usb_port_status *port_status;
unsigned long flags;
@@ -272,8 +271,6 @@ int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetHubDescriptor:
memcpy(buf, root_hub_des, sizeof(root_hub_des));
- buf[3] = 0x11; /* per-port power, no ovrcrnt */
- len = (buf[0] < wLength) ? buf[0] : wLength;
break;
case GetHubStatus:
hub_status = (struct usb_hub_status *)buf;
@@ -281,7 +278,6 @@ int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
cpu_to_le16(fhci->vroot_hub->hub.wHubStatus);
hub_status->wHubChange =
cpu_to_le16(fhci->vroot_hub->hub.wHubChange);
- len = 4;
break;
case GetPortStatus:
port_status = (struct usb_port_status *)buf;
@@ -289,7 +285,6 @@ int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
cpu_to_le16(fhci->vroot_hub->port.wPortStatus);
port_status->wPortChange =
cpu_to_le16(fhci->vroot_hub->port.wPortChange);
- len = 4;
break;
case SetHubFeature:
switch (wValue) {
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index ecf02b2623e8..475b21fd373b 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -1521,8 +1521,8 @@ fotg210_hub_descriptor(
memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
- temp = 0x0008; /* per-port overcurrent reporting */
- temp |= 0x0002; /* no power switching */
+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */
desc->wHubCharacteristics = cpu_to_le16(temp);
}
diff --git a/drivers/usb/host/fusbh200-hcd.c b/drivers/usb/host/fusbh200-hcd.c
index 664d2aa1239c..a83eefefffda 100644
--- a/drivers/usb/host/fusbh200-hcd.c
+++ b/drivers/usb/host/fusbh200-hcd.c
@@ -1479,8 +1479,8 @@ fusbh200_hub_descriptor (
memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
- temp = 0x0008; /* per-port overcurrent reporting */
- temp |= 0x0002; /* no power switching */
+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */
desc->wHubCharacteristics = cpu_to_le16(temp);
}
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index eb4efba9f1ad..6a2ad550b120 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -1482,9 +1482,8 @@ static int get_hub_descriptor(struct usb_hcd *hcd,
desc->bDescLength = 9;
desc->bPwrOn2PwrGood = 0;
desc->wHubCharacteristics = (__force __u16) cpu_to_le16(
- 0x0002 | /* No power switching */
- 0x0010 | /* No over current protection */
- 0);
+ HUB_CHAR_NO_LPSM | /* No power switching */
+ HUB_CHAR_NO_OCPM); /* No over current protection */
desc->u.hs.DeviceRemovable[0] = 1 << 1;
desc->u.hs.DeviceRemovable[1] = ~0;
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 31c9c4d0fa0b..113d0cc6cc43 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -948,7 +948,10 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x,
desc->bHubContrCurrent = 0;
desc->bNbrPorts = (u8) (reg & 0x3);
/* Power switching, device type, overcurrent. */
- desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f));
+ desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) &
+ (HUB_CHAR_LPSM |
+ HUB_CHAR_COMPOUND |
+ HUB_CHAR_OCPM)));
desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
/* ports removable, and legacy PortPwrCtrlMask */
desc->u.hs.DeviceRemovable[0] = 0;
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 75e5876f9d7c..b32ab60cad1e 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -1543,8 +1543,12 @@ static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
desc->bHubContrCurrent = 0;
desc->bNbrPorts = reg & 0x3;
/* Power switching, device type, overcurrent. */
- desc->wHubCharacteristics = cpu_to_le16((reg >> 8) & 0x1f);
- DBG(0, "%s: hubcharacteristics = %02x\n", __func__, cpu_to_le16((reg >> 8) & 0x1f));
+ desc->wHubCharacteristics = cpu_to_le16((reg >> 8) &
+ (HUB_CHAR_LPSM |
+ HUB_CHAR_COMPOUND |
+ HUB_CHAR_OCPM));
+ DBG(0, "%s: hubcharacteristics = %02x\n", __func__,
+ desc->wHubCharacteristics);
desc->bPwrOn2PwrGood = (reg >> 24) & 0xff;
/* ports removable, and legacy PortPwrCtrlMask */
desc->u.hs.DeviceRemovable[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h
deleted file mode 100644
index 33dc79ccaa6b..000000000000
--- a/drivers/usb/host/isp1760-hcd.h
+++ /dev/null
@@ -1,208 +0,0 @@
-#ifndef _ISP1760_HCD_H_
-#define _ISP1760_HCD_H_
-
-/* exports for if */
-struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
- int irq, unsigned long irqflags,
- int rst_gpio,
- struct device *dev, const char *busname,
- unsigned int devflags);
-int init_kmem_once(void);
-void deinit_kmem_cache(void);
-
-/* EHCI capability registers */
-#define HC_CAPLENGTH 0x00
-#define HC_HCSPARAMS 0x04
-#define HC_HCCPARAMS 0x08
-
-/* EHCI operational registers */
-#define HC_USBCMD 0x20
-#define HC_USBSTS 0x24
-#define HC_FRINDEX 0x2c
-#define HC_CONFIGFLAG 0x60
-#define HC_PORTSC1 0x64
-#define HC_ISO_PTD_DONEMAP_REG 0x130
-#define HC_ISO_PTD_SKIPMAP_REG 0x134
-#define HC_ISO_PTD_LASTPTD_REG 0x138
-#define HC_INT_PTD_DONEMAP_REG 0x140
-#define HC_INT_PTD_SKIPMAP_REG 0x144
-#define HC_INT_PTD_LASTPTD_REG 0x148
-#define HC_ATL_PTD_DONEMAP_REG 0x150
-#define HC_ATL_PTD_SKIPMAP_REG 0x154
-#define HC_ATL_PTD_LASTPTD_REG 0x158
-
-/* Configuration Register */
-#define HC_HW_MODE_CTRL 0x300
-#define ALL_ATX_RESET (1 << 31)
-#define HW_ANA_DIGI_OC (1 << 15)
-#define HW_DATA_BUS_32BIT (1 << 8)
-#define HW_DACK_POL_HIGH (1 << 6)
-#define HW_DREQ_POL_HIGH (1 << 5)
-#define HW_INTR_HIGH_ACT (1 << 2)
-#define HW_INTR_EDGE_TRIG (1 << 1)
-#define HW_GLOBAL_INTR_EN (1 << 0)
-
-#define HC_CHIP_ID_REG 0x304
-#define HC_SCRATCH_REG 0x308
-
-#define HC_RESET_REG 0x30c
-#define SW_RESET_RESET_HC (1 << 1)
-#define SW_RESET_RESET_ALL (1 << 0)
-
-#define HC_BUFFER_STATUS_REG 0x334
-#define ISO_BUF_FILL (1 << 2)
-#define INT_BUF_FILL (1 << 1)
-#define ATL_BUF_FILL (1 << 0)
-
-#define HC_MEMORY_REG 0x33c
-#define ISP_BANK(x) ((x) << 16)
-
-#define HC_PORT1_CTRL 0x374
-#define PORT1_POWER (3 << 3)
-#define PORT1_INIT1 (1 << 7)
-#define PORT1_INIT2 (1 << 23)
-#define HW_OTG_CTRL_SET 0x374
-#define HW_OTG_CTRL_CLR 0x376
-
-/* Interrupt Register */
-#define HC_INTERRUPT_REG 0x310
-
-#define HC_INTERRUPT_ENABLE 0x314
-#define HC_ISO_INT (1 << 9)
-#define HC_ATL_INT (1 << 8)
-#define HC_INTL_INT (1 << 7)
-#define HC_EOT_INT (1 << 3)
-#define HC_SOT_INT (1 << 1)
-#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT)
-
-#define HC_ISO_IRQ_MASK_OR_REG 0x318
-#define HC_INT_IRQ_MASK_OR_REG 0x31C
-#define HC_ATL_IRQ_MASK_OR_REG 0x320
-#define HC_ISO_IRQ_MASK_AND_REG 0x324
-#define HC_INT_IRQ_MASK_AND_REG 0x328
-#define HC_ATL_IRQ_MASK_AND_REG 0x32C
-
-/* urb state*/
-#define DELETE_URB (0x0008)
-#define NO_TRANSFER_ACTIVE (0xffffffff)
-
-/* Philips Proprietary Transfer Descriptor (PTD) */
-typedef __u32 __bitwise __dw;
-struct ptd {
- __dw dw0;
- __dw dw1;
- __dw dw2;
- __dw dw3;
- __dw dw4;
- __dw dw5;
- __dw dw6;
- __dw dw7;
-};
-#define PTD_OFFSET 0x0400
-#define ISO_PTD_OFFSET 0x0400
-#define INT_PTD_OFFSET 0x0800
-#define ATL_PTD_OFFSET 0x0c00
-#define PAYLOAD_OFFSET 0x1000
-
-struct slotinfo {
- struct isp1760_qh *qh;
- struct isp1760_qtd *qtd;
- unsigned long timestamp;
-};
-
-
-typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
- struct isp1760_qtd *qtd);
-
-/*
- * Device flags that can vary from board to board. All of these
- * indicate the most "atypical" case, so that a devflags of 0 is
- * a sane default configuration.
- */
-#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */
-#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */
-#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */
-#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */
-#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */
-#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */
-#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */
-#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */
-#define ISP1760_FLAG_RESET_ACTIVE_HIGH 0x80000000 /* RESET GPIO active high */
-
-/* chip memory management */
-struct memory_chunk {
- unsigned int start;
- unsigned int size;
- unsigned int free;
-};
-
-/*
- * 60kb divided in:
- * - 32 blocks @ 256 bytes
- * - 20 blocks @ 1024 bytes
- * - 4 blocks @ 8192 bytes
- */
-
-#define BLOCK_1_NUM 32
-#define BLOCK_2_NUM 20
-#define BLOCK_3_NUM 4
-
-#define BLOCK_1_SIZE 256
-#define BLOCK_2_SIZE 1024
-#define BLOCK_3_SIZE 8192
-#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
-#define MAX_PAYLOAD_SIZE BLOCK_3_SIZE
-#define PAYLOAD_AREA_SIZE 0xf000
-
-/* ATL */
-/* DW0 */
-#define DW0_VALID_BIT 1
-#define FROM_DW0_VALID(x) ((x) & 0x01)
-#define TO_DW0_LENGTH(x) (((u32) x) << 3)
-#define TO_DW0_MAXPACKET(x) (((u32) x) << 18)
-#define TO_DW0_MULTI(x) (((u32) x) << 29)
-#define TO_DW0_ENDPOINT(x) (((u32) x) << 31)
-/* DW1 */
-#define TO_DW1_DEVICE_ADDR(x) (((u32) x) << 3)
-#define TO_DW1_PID_TOKEN(x) (((u32) x) << 10)
-#define DW1_TRANS_BULK ((u32) 2 << 12)
-#define DW1_TRANS_INT ((u32) 3 << 12)
-#define DW1_TRANS_SPLIT ((u32) 1 << 14)
-#define DW1_SE_USB_LOSPEED ((u32) 2 << 16)
-#define TO_DW1_PORT_NUM(x) (((u32) x) << 18)
-#define TO_DW1_HUB_NUM(x) (((u32) x) << 25)
-/* DW2 */
-#define TO_DW2_DATA_START_ADDR(x) (((u32) x) << 8)
-#define TO_DW2_RL(x) ((x) << 25)
-#define FROM_DW2_RL(x) (((x) >> 25) & 0xf)
-/* DW3 */
-#define FROM_DW3_NRBYTESTRANSFERRED(x) ((x) & 0x7fff)
-#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) ((x) & 0x07ff)
-#define TO_DW3_NAKCOUNT(x) ((x) << 19)
-#define FROM_DW3_NAKCOUNT(x) (((x) >> 19) & 0xf)
-#define TO_DW3_CERR(x) ((x) << 23)
-#define FROM_DW3_CERR(x) (((x) >> 23) & 0x3)
-#define TO_DW3_DATA_TOGGLE(x) ((x) << 25)
-#define FROM_DW3_DATA_TOGGLE(x) (((x) >> 25) & 0x1)
-#define TO_DW3_PING(x) ((x) << 26)
-#define FROM_DW3_PING(x) (((x) >> 26) & 0x1)
-#define DW3_ERROR_BIT (1 << 28)
-#define DW3_BABBLE_BIT (1 << 29)
-#define DW3_HALT_BIT (1 << 30)
-#define DW3_ACTIVE_BIT (1 << 31)
-#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01)
-
-#define INT_UNDERRUN (1 << 2)
-#define INT_BABBLE (1 << 1)
-#define INT_EXACT (1 << 0)
-
-#define SETUP_PID (2)
-#define IN_PID (1)
-#define OUT_PID (0)
-
-/* Errata 1 */
-#define RL_COUNTER (0)
-#define NAK_COUNTER (0)
-#define ERR_COUNTER (2)
-
-#endif /* _ISP1760_HCD_H_ */
diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c
deleted file mode 100644
index 09254a43bc01..000000000000
--- a/drivers/usb/host/isp1760-if.c
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Glue code for the ISP1760 driver and bus
- * Currently there is support for
- * - OpenFirmware
- * - PCI
- * - PDEV (generic platform device centralized driver model)
- *
- * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
- *
- */
-
-#include <linux/usb.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/usb/isp1760.h>
-#include <linux/usb/hcd.h>
-
-#include "isp1760-hcd.h"
-
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
-#endif
-
-#ifdef CONFIG_PCI
-#include <linux/pci.h>
-#endif
-
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-struct isp1760 {
- struct usb_hcd *hcd;
- int rst_gpio;
-};
-
-static int of_isp1760_probe(struct platform_device *dev)
-{
- struct isp1760 *drvdata;
- struct device_node *dp = dev->dev.of_node;
- struct resource *res;
- struct resource memory;
- int virq;
- resource_size_t res_len;
- int ret;
- unsigned int devflags = 0;
- enum of_gpio_flags gpio_flags;
- u32 bus_width = 0;
-
- drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
- return -ENOMEM;
-
- ret = of_address_to_resource(dp, 0, &memory);
- if (ret) {
- ret = -ENXIO;
- goto free_data;
- }
-
- res_len = resource_size(&memory);
-
- res = request_mem_region(memory.start, res_len, dev_name(&dev->dev));
- if (!res) {
- ret = -EBUSY;
- goto free_data;
- }
-
- virq = irq_of_parse_and_map(dp, 0);
- if (!virq) {
- ret = -ENODEV;
- goto release_reg;
- }
-
- if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
- devflags |= ISP1760_FLAG_ISP1761;
-
- /* Some systems wire up only 16 of the 32 data lines */
- of_property_read_u32(dp, "bus-width", &bus_width);
- if (bus_width == 16)
- devflags |= ISP1760_FLAG_BUS_WIDTH_16;
-
- if (of_get_property(dp, "port1-otg", NULL) != NULL)
- devflags |= ISP1760_FLAG_OTG_EN;
-
- if (of_get_property(dp, "analog-oc", NULL) != NULL)
- devflags |= ISP1760_FLAG_ANALOG_OC;
-
- if (of_get_property(dp, "dack-polarity", NULL) != NULL)
- devflags |= ISP1760_FLAG_DACK_POL_HIGH;
-
- if (of_get_property(dp, "dreq-polarity", NULL) != NULL)
- devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
-
- drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags);
- if (gpio_is_valid(drvdata->rst_gpio)) {
- ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev));
- if (!ret) {
- if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) {
- devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH;
- gpio_direction_output(drvdata->rst_gpio, 0);
- } else {
- gpio_direction_output(drvdata->rst_gpio, 1);
- }
- } else {
- drvdata->rst_gpio = ret;
- }
- }
-
- drvdata->hcd = isp1760_register(memory.start, res_len, virq,
- IRQF_SHARED, drvdata->rst_gpio,
- &dev->dev, dev_name(&dev->dev),
- devflags);
- if (IS_ERR(drvdata->hcd)) {
- ret = PTR_ERR(drvdata->hcd);
- goto free_gpio;
- }
-
- platform_set_drvdata(dev, drvdata);
- return ret;
-
-free_gpio:
- if (gpio_is_valid(drvdata->rst_gpio))
- gpio_free(drvdata->rst_gpio);
-release_reg:
- release_mem_region(memory.start, res_len);
-free_data:
- kfree(drvdata);
- return ret;
-}
-
-static int of_isp1760_remove(struct platform_device *dev)
-{
- struct isp1760 *drvdata = platform_get_drvdata(dev);
-
- usb_remove_hcd(drvdata->hcd);
- iounmap(drvdata->hcd->regs);
- release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len);
- usb_put_hcd(drvdata->hcd);
-
- if (gpio_is_valid(drvdata->rst_gpio))
- gpio_free(drvdata->rst_gpio);
-
- kfree(drvdata);
- return 0;
-}
-
-static const struct of_device_id of_isp1760_match[] = {
- {
- .compatible = "nxp,usb-isp1760",
- },
- {
- .compatible = "nxp,usb-isp1761",
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, of_isp1760_match);
-
-static struct platform_driver isp1760_of_driver = {
- .driver = {
- .name = "nxp-isp1760",
- .of_match_table = of_isp1760_match,
- },
- .probe = of_isp1760_probe,
- .remove = of_isp1760_remove,
-};
-#endif
-
-#ifdef CONFIG_PCI
-static int isp1761_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
-{
- u8 latency, limit;
- __u32 reg_data;
- int retry_count;
- struct usb_hcd *hcd;
- unsigned int devflags = 0;
- int ret_status = 0;
-
- resource_size_t pci_mem_phy0;
- resource_size_t memlength;
-
- u8 __iomem *chip_addr;
- u8 __iomem *iobase;
- resource_size_t nxp_pci_io_base;
- resource_size_t iolength;
-
- if (usb_disabled())
- return -ENODEV;
-
- if (pci_enable_device(dev) < 0)
- return -ENODEV;
-
- if (!dev->irq)
- return -ENODEV;
-
- /* Grab the PLX PCI mem maped port start address we need */
- nxp_pci_io_base = pci_resource_start(dev, 0);
- iolength = pci_resource_len(dev, 0);
-
- if (!request_mem_region(nxp_pci_io_base, iolength, "ISP1761 IO MEM")) {
- printk(KERN_ERR "request region #1\n");
- return -EBUSY;
- }
-
- iobase = ioremap_nocache(nxp_pci_io_base, iolength);
- if (!iobase) {
- printk(KERN_ERR "ioremap #1\n");
- ret_status = -ENOMEM;
- goto cleanup1;
- }
- /* Grab the PLX PCI shared memory of the ISP 1761 we need */
- pci_mem_phy0 = pci_resource_start(dev, 3);
- memlength = pci_resource_len(dev, 3);
- if (memlength < 0xffff) {
- printk(KERN_ERR "memory length for this resource is wrong\n");
- ret_status = -ENOMEM;
- goto cleanup2;
- }
-
- if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) {
- printk(KERN_ERR "host controller already in use\n");
- ret_status = -EBUSY;
- goto cleanup2;
- }
-
- /* map available memory */
- chip_addr = ioremap_nocache(pci_mem_phy0,memlength);
- if (!chip_addr) {
- printk(KERN_ERR "Error ioremap failed\n");
- ret_status = -ENOMEM;
- goto cleanup3;
- }
-
- /* bad pci latencies can contribute to overruns */
- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
- if (latency) {
- pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
- if (limit && limit < latency)
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
- }
-
- /* Try to check whether we can access Scratch Register of
- * Host Controller or not. The initial PCI access is retried until
- * local init for the PCI bridge is completed
- */
- retry_count = 20;
- reg_data = 0;
- while ((reg_data != 0xFACE) && retry_count) {
- /*by default host is in 16bit mode, so
- * io operations at this stage must be 16 bit
- * */
- writel(0xface, chip_addr + HC_SCRATCH_REG);
- udelay(100);
- reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff;
- retry_count--;
- }
-
- iounmap(chip_addr);
-
- /* Host Controller presence is detected by writing to scratch register
- * and reading back and checking the contents are same or not
- */
- if (reg_data != 0xFACE) {
- dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
- ret_status = -ENOMEM;
- goto cleanup3;
- }
-
- pci_set_master(dev);
-
- /* configure PLX PCI chip to pass interrupts */
-#define PLX_INT_CSR_REG 0x68
- reg_data = readl(iobase + PLX_INT_CSR_REG);
- reg_data |= 0x900;
- writel(reg_data, iobase + PLX_INT_CSR_REG);
-
- dev->dev.dma_mask = NULL;
- hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq,
- IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev),
- devflags);
- if (IS_ERR(hcd)) {
- ret_status = -ENODEV;
- goto cleanup3;
- }
-
- /* done with PLX IO access */
- iounmap(iobase);
- release_mem_region(nxp_pci_io_base, iolength);
-
- pci_set_drvdata(dev, hcd);
- return 0;
-
-cleanup3:
- release_mem_region(pci_mem_phy0, memlength);
-cleanup2:
- iounmap(iobase);
-cleanup1:
- release_mem_region(nxp_pci_io_base, iolength);
- return ret_status;
-}
-
-static void isp1761_pci_remove(struct pci_dev *dev)
-{
- struct usb_hcd *hcd;
-
- hcd = pci_get_drvdata(dev);
-
- usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
- usb_put_hcd(hcd);
-
- pci_disable_device(dev);
-}
-
-static void isp1761_pci_shutdown(struct pci_dev *dev)
-{
- printk(KERN_ERR "ips1761_pci_shutdown\n");
-}
-
-static const struct pci_device_id isp1760_plx [] = {
- {
- .class = PCI_CLASS_BRIDGE_OTHER << 8,
- .class_mask = ~0,
- .vendor = PCI_VENDOR_ID_PLX,
- .device = 0x5406,
- .subvendor = PCI_VENDOR_ID_PLX,
- .subdevice = 0x9054,
- },
- { }
-};
-MODULE_DEVICE_TABLE(pci, isp1760_plx);
-
-static struct pci_driver isp1761_pci_driver = {
- .name = "isp1760",
- .id_table = isp1760_plx,
- .probe = isp1761_pci_probe,
- .remove = isp1761_pci_remove,
- .shutdown = isp1761_pci_shutdown,
-};
-#endif
-
-static int isp1760_plat_probe(struct platform_device *pdev)
-{
- int ret = 0;
- struct usb_hcd *hcd;
- struct resource *mem_res;
- struct resource *irq_res;
- resource_size_t mem_size;
- struct isp1760_platform_data *priv = dev_get_platdata(&pdev->dev);
- unsigned int devflags = 0;
- unsigned long irqflags = IRQF_SHARED;
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem_res) {
- pr_warning("isp1760: Memory resource not available\n");
- ret = -ENODEV;
- goto out;
- }
- mem_size = resource_size(mem_res);
- if (!request_mem_region(mem_res->start, mem_size, "isp1760")) {
- pr_warning("isp1760: Cannot reserve the memory resource\n");
- ret = -EBUSY;
- goto out;
- }
-
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq_res) {
- pr_warning("isp1760: IRQ resource not available\n");
- ret = -ENODEV;
- goto cleanup;
- }
-
- irqflags |= irq_res->flags & IRQF_TRIGGER_MASK;
-
- if (priv) {
- if (priv->is_isp1761)
- devflags |= ISP1760_FLAG_ISP1761;
- if (priv->bus_width_16)
- devflags |= ISP1760_FLAG_BUS_WIDTH_16;
- if (priv->port1_otg)
- devflags |= ISP1760_FLAG_OTG_EN;
- if (priv->analog_oc)
- devflags |= ISP1760_FLAG_ANALOG_OC;
- if (priv->dack_polarity_high)
- devflags |= ISP1760_FLAG_DACK_POL_HIGH;
- if (priv->dreq_polarity_high)
- devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
- }
-
- hcd = isp1760_register(mem_res->start, mem_size, irq_res->start,
- irqflags, -ENOENT,
- &pdev->dev, dev_name(&pdev->dev), devflags);
-
- platform_set_drvdata(pdev, hcd);
-
- if (IS_ERR(hcd)) {
- pr_warning("isp1760: Failed to register the HCD device\n");
- ret = -ENODEV;
- goto cleanup;
- }
-
- pr_info("ISP1760 USB device initialised\n");
- return ret;
-
-cleanup:
- release_mem_region(mem_res->start, mem_size);
-out:
- return ret;
-}
-
-static int isp1760_plat_remove(struct platform_device *pdev)
-{
- struct resource *mem_res;
- resource_size_t mem_size;
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
-
- usb_remove_hcd(hcd);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- mem_size = resource_size(mem_res);
- release_mem_region(mem_res->start, mem_size);
-
- usb_put_hcd(hcd);
-
- return 0;
-}
-
-static struct platform_driver isp1760_plat_driver = {
- .probe = isp1760_plat_probe,
- .remove = isp1760_plat_remove,
- .driver = {
- .name = "isp1760",
- },
-};
-
-static int __init isp1760_init(void)
-{
- int ret, any_ret = -ENODEV;
-
- init_kmem_once();
-
- ret = platform_driver_register(&isp1760_plat_driver);
- if (!ret)
- any_ret = 0;
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
- ret = platform_driver_register(&isp1760_of_driver);
- if (!ret)
- any_ret = 0;
-#endif
-#ifdef CONFIG_PCI
- ret = pci_register_driver(&isp1761_pci_driver);
- if (!ret)
- any_ret = 0;
-#endif
-
- if (any_ret)
- deinit_kmem_cache();
- return any_ret;
-}
-module_init(isp1760_init);
-
-static void __exit isp1760_exit(void)
-{
- platform_driver_unregister(&isp1760_plat_driver);
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
- platform_driver_unregister(&isp1760_of_driver);
-#endif
-#ifdef CONFIG_PCI
- pci_unregister_driver(&isp1761_pci_driver);
-#endif
- deinit_kmem_cache();
-}
-module_exit(isp1760_exit);
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 6234c75da33f..a98833cbfcf3 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -55,6 +55,7 @@
* single thread (max3421_spi_thread).
*/
+#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/usb.h>
@@ -1291,7 +1292,7 @@ max3421_handle_irqs(struct usb_hcd *hcd)
char sbuf[16 * 16], *dp, *end;
int i;
- if (jiffies - last_time > 5*HZ) {
+ if (time_after(jiffies, last_time + 5*HZ)) {
dp = sbuf;
end = sbuf + sizeof(sbuf);
*dp = '\0';
@@ -1660,7 +1661,8 @@ hub_descriptor(struct usb_hub_descriptor *desc)
*/
desc->bDescriptorType = 0x29; /* hub descriptor */
desc->bDescLength = 9;
- desc->wHubCharacteristics = cpu_to_le16(0x0001);
+ desc->wHubCharacteristics = cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM |
+ HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = 1;
}
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index dc9e4e61f1c8..7cce85a1f7dc 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -33,7 +33,17 @@
for ((index) = 0; (index) < AT91_MAX_USBH_PORTS; (index)++)
/* interface, function and usb clocks; sometimes also an AHB clock */
-static struct clk *iclk, *fclk, *uclk, *hclk;
+#define hcd_to_ohci_at91_priv(h) \
+ ((struct ohci_at91_priv *)hcd_to_ohci(h)->priv)
+
+struct ohci_at91_priv {
+ struct clk *iclk;
+ struct clk *fclk;
+ struct clk *uclk;
+ struct clk *hclk;
+ bool clocked;
+ bool wakeup; /* Saved wake-up state for resume */
+};
/* interface and function clocks; sometimes also an AHB clock */
#define DRIVER_DESC "OHCI Atmel driver"
@@ -41,45 +51,53 @@ static struct clk *iclk, *fclk, *uclk, *hclk;
static const char hcd_name[] = "ohci-atmel";
static struct hc_driver __read_mostly ohci_at91_hc_driver;
-static int clocked;
+
+static const struct ohci_driver_overrides ohci_at91_drv_overrides __initconst = {
+ .extra_priv_size = sizeof(struct ohci_at91_priv),
+};
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
-static void at91_start_clock(void)
+static void at91_start_clock(struct ohci_at91_priv *ohci_at91)
{
+ if (ohci_at91->clocked)
+ return;
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
- clk_set_rate(uclk, 48000000);
- clk_prepare_enable(uclk);
+ clk_set_rate(ohci_at91->uclk, 48000000);
+ clk_prepare_enable(ohci_at91->uclk);
}
- clk_prepare_enable(hclk);
- clk_prepare_enable(iclk);
- clk_prepare_enable(fclk);
- clocked = 1;
+ clk_prepare_enable(ohci_at91->hclk);
+ clk_prepare_enable(ohci_at91->iclk);
+ clk_prepare_enable(ohci_at91->fclk);
+ ohci_at91->clocked = true;
}
-static void at91_stop_clock(void)
+static void at91_stop_clock(struct ohci_at91_priv *ohci_at91)
{
- clk_disable_unprepare(fclk);
- clk_disable_unprepare(iclk);
- clk_disable_unprepare(hclk);
+ if (!ohci_at91->clocked)
+ return;
+ clk_disable_unprepare(ohci_at91->fclk);
+ clk_disable_unprepare(ohci_at91->iclk);
+ clk_disable_unprepare(ohci_at91->hclk);
if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_disable_unprepare(uclk);
- clocked = 0;
+ clk_disable_unprepare(ohci_at91->uclk);
+ ohci_at91->clocked = false;
}
static void at91_start_hc(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ohci_regs __iomem *regs = hcd->regs;
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
dev_dbg(&pdev->dev, "start\n");
/*
* Start the USB clocks.
*/
- at91_start_clock();
+ at91_start_clock(ohci_at91);
/*
* The USB host controller must remain in reset.
@@ -91,6 +109,7 @@ static void at91_stop_hc(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ohci_regs __iomem *regs = hcd->regs;
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
dev_dbg(&pdev->dev, "stop\n");
@@ -102,7 +121,7 @@ static void at91_stop_hc(struct platform_device *pdev)
/*
* Stop the USB clocks.
*/
- at91_stop_clock();
+ at91_stop_clock(ohci_at91);
}
@@ -128,7 +147,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
struct at91_usbh_data *board;
struct ohci_hcd *ohci;
int retval;
- struct usb_hcd *hcd = NULL;
+ struct usb_hcd *hcd;
+ struct ohci_at91_priv *ohci_at91;
struct device *dev = &pdev->dev;
struct resource *res;
int irq;
@@ -142,6 +162,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
hcd = usb_create_hcd(driver, dev, "at91");
if (!hcd)
return -ENOMEM;
+ ohci_at91 = hcd_to_ohci_at91_priv(hcd);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcd->regs = devm_ioremap_resource(dev, res);
@@ -152,29 +173,29 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- iclk = devm_clk_get(dev, "ohci_clk");
- if (IS_ERR(iclk)) {
+ ohci_at91->iclk = devm_clk_get(dev, "ohci_clk");
+ if (IS_ERR(ohci_at91->iclk)) {
dev_err(dev, "failed to get ohci_clk\n");
- retval = PTR_ERR(iclk);
+ retval = PTR_ERR(ohci_at91->iclk);
goto err;
}
- fclk = devm_clk_get(dev, "uhpck");
- if (IS_ERR(fclk)) {
+ ohci_at91->fclk = devm_clk_get(dev, "uhpck");
+ if (IS_ERR(ohci_at91->fclk)) {
dev_err(dev, "failed to get uhpck\n");
- retval = PTR_ERR(fclk);
+ retval = PTR_ERR(ohci_at91->fclk);
goto err;
}
- hclk = devm_clk_get(dev, "hclk");
- if (IS_ERR(hclk)) {
+ ohci_at91->hclk = devm_clk_get(dev, "hclk");
+ if (IS_ERR(ohci_at91->hclk)) {
dev_err(dev, "failed to get hclk\n");
- retval = PTR_ERR(hclk);
+ retval = PTR_ERR(ohci_at91->hclk);
goto err;
}
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
- uclk = devm_clk_get(dev, "usb_clk");
- if (IS_ERR(uclk)) {
+ ohci_at91->uclk = devm_clk_get(dev, "usb_clk");
+ if (IS_ERR(ohci_at91->uclk)) {
dev_err(dev, "failed to get uclk\n");
- retval = PTR_ERR(uclk);
+ retval = PTR_ERR(ohci_at91->uclk);
goto err;
}
}
@@ -347,11 +368,13 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
*/
desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);
- desc->wHubCharacteristics |= cpu_to_le16(0x0001);
+ desc->wHubCharacteristics |=
+ cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM);
if (pdata->overcurrent_supported) {
desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM);
- desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001);
+ desc->wHubCharacteristics |=
+ cpu_to_le16(HUB_CHAR_INDV_PORT_OCPM);
}
dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n",
@@ -593,19 +616,27 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int
-ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
+ohci_hcd_at91_drv_suspend(struct device *dev)
{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
- bool do_wakeup = device_may_wakeup(&pdev->dev);
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
int ret;
- if (do_wakeup)
+ /*
+ * Disable wakeup if we are going to sleep with slow clock mode
+ * enabled.
+ */
+ ohci_at91->wakeup = device_may_wakeup(dev)
+ && !at91_suspend_entering_slow_clock();
+
+ if (ohci_at91->wakeup)
enable_irq_wake(hcd->irq);
- ret = ohci_suspend(hcd, do_wakeup);
+ ret = ohci_suspend(hcd, ohci_at91->wakeup);
if (ret) {
- disable_irq_wake(hcd->irq);
+ if (ohci_at91->wakeup)
+ disable_irq_wake(hcd->irq);
return ret;
}
/*
@@ -615,7 +646,7 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
*
* REVISIT: some boards will be able to turn VBUS off...
*/
- if (at91_suspend_entering_slow_clock()) {
+ if (!ohci_at91->wakeup) {
ohci->hc_control = ohci_readl(ohci, &ohci->regs->control);
ohci->hc_control &= OHCI_CTRL_RWC;
ohci_writel(ohci, ohci->hc_control, &ohci->regs->control);
@@ -623,38 +654,37 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
/* flush the writes */
(void) ohci_readl (ohci, &ohci->regs->control);
- at91_stop_clock();
+ at91_stop_clock(ohci_at91);
}
return ret;
}
-static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
+static int ohci_hcd_at91_drv_resume(struct device *dev)
{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
- if (device_may_wakeup(&pdev->dev))
+ if (ohci_at91->wakeup)
disable_irq_wake(hcd->irq);
- if (!clocked)
- at91_start_clock();
+ at91_start_clock(ohci_at91);
ohci_resume(hcd, false);
return 0;
}
-#else
-#define ohci_hcd_at91_drv_suspend NULL
-#define ohci_hcd_at91_drv_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend,
+ ohci_hcd_at91_drv_resume);
+
static struct platform_driver ohci_hcd_at91_driver = {
.probe = ohci_hcd_at91_drv_probe,
.remove = ohci_hcd_at91_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
- .suspend = ohci_hcd_at91_drv_suspend,
- .resume = ohci_hcd_at91_drv_resume,
.driver = {
.name = "at91_ohci",
+ .pm = &ohci_hcd_at91_pm_ops,
.of_match_table = of_match_ptr(at91_ohci_dt_ids),
},
};
@@ -665,7 +695,7 @@ static int __init ohci_at91_init(void)
return -ENODEV;
pr_info("%s: " DRIVER_DESC "\n", hcd_name);
- ohci_init_driver(&ohci_at91_hc_driver, NULL);
+ ohci_init_driver(&ohci_at91_hc_driver, &ohci_at91_drv_overrides);
/*
* The Atmel HW has some unusual quirks, which require Atmel-specific
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index 1c76999b2184..e5c33bc98ea4 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -431,7 +431,6 @@ static struct platform_driver ohci_hcd_da8xx_driver = {
.resume = ohci_da8xx_resume,
#endif
.driver = {
- .owner = THIS_MODULE,
.name = "ohci",
},
};
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 0aa17c937115..fe2aedd8a54d 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -544,15 +544,15 @@ ohci_hub_descriptor (
temp = 1 + (ohci->num_ports / 8);
desc->bDescLength = 7 + 2 * temp;
- temp = 0;
+ temp = HUB_CHAR_COMMON_LPSM | HUB_CHAR_COMMON_OCPM;
if (rh & RH_A_NPS) /* no power switching? */
- temp |= 0x0002;
+ temp |= HUB_CHAR_NO_LPSM;
if (rh & RH_A_PSM) /* per-port power switching? */
- temp |= 0x0001;
+ temp |= HUB_CHAR_INDV_PORT_LPSM;
if (rh & RH_A_NOCP) /* no overcurrent reporting? */
- temp |= 0x0010;
+ temp |= HUB_CHAR_NO_OCPM;
else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
- temp |= 0x0008;
+ temp |= HUB_CHAR_INDV_PORT_OCPM;
desc->wHubCharacteristics = cpu_to_le16(temp);
/* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
diff --git a/drivers/usb/host/ohci-jz4740.c b/drivers/usb/host/ohci-jz4740.c
index 8ddd8f5470cb..4db78f169256 100644
--- a/drivers/usb/host/ohci-jz4740.c
+++ b/drivers/usb/host/ohci-jz4740.c
@@ -239,7 +239,6 @@ static struct platform_driver ohci_hcd_jz4740_driver = {
.remove = jz4740_ohci_remove,
.driver = {
.name = "jz4740-ohci",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index b81d202b15a2..185ceee52d47 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -38,7 +38,8 @@
struct ohci_platform_priv {
struct clk *clks[OHCI_MAX_CLKS];
struct reset_control *rst;
- struct phy *phy;
+ struct phy **phys;
+ int num_phys;
};
static const char hcd_name[] = "ohci-platform";
@@ -47,7 +48,7 @@ static int ohci_platform_power_on(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
- int clk, ret;
+ int clk, ret, phy_num;
for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) {
ret = clk_prepare_enable(priv->clks[clk]);
@@ -55,20 +56,28 @@ static int ohci_platform_power_on(struct platform_device *dev)
goto err_disable_clks;
}
- if (priv->phy) {
- ret = phy_init(priv->phy);
- if (ret)
- goto err_disable_clks;
-
- ret = phy_power_on(priv->phy);
- if (ret)
- goto err_exit_phy;
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ if (priv->phys[phy_num]) {
+ ret = phy_init(priv->phys[phy_num]);
+ if (ret)
+ goto err_exit_phy;
+ ret = phy_power_on(priv->phys[phy_num]);
+ if (ret) {
+ phy_exit(priv->phys[phy_num]);
+ goto err_exit_phy;
+ }
+ }
}
return 0;
err_exit_phy:
- phy_exit(priv->phy);
+ while (--phy_num >= 0) {
+ if (priv->phys[phy_num]) {
+ phy_power_off(priv->phys[phy_num]);
+ phy_exit(priv->phys[phy_num]);
+ }
+ }
err_disable_clks:
while (--clk >= 0)
clk_disable_unprepare(priv->clks[clk]);
@@ -80,11 +89,13 @@ static void ohci_platform_power_off(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
- int clk;
+ int clk, phy_num;
- if (priv->phy) {
- phy_power_off(priv->phy);
- phy_exit(priv->phy);
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ if (priv->phys[phy_num]) {
+ phy_power_off(priv->phys[phy_num]);
+ phy_exit(priv->phys[phy_num]);
+ }
}
for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
@@ -112,7 +123,8 @@ static int ohci_platform_probe(struct platform_device *dev)
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ohci_platform_priv *priv;
struct ohci_hcd *ohci;
- int err, irq, clk = 0;
+ const char *phy_name;
+ int err, irq, phy_num, clk = 0;
if (usb_disabled())
return -ENODEV;
@@ -160,12 +172,38 @@ static int ohci_platform_probe(struct platform_device *dev)
of_property_read_u32(dev->dev.of_node, "num-ports",
&ohci->num_ports);
- priv->phy = devm_phy_get(&dev->dev, "usb");
- if (IS_ERR(priv->phy)) {
- err = PTR_ERR(priv->phy);
- if (err == -EPROBE_DEFER)
- goto err_put_hcd;
- priv->phy = NULL;
+ priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
+ "phys", "#phy-cells");
+ priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1;
+
+ priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
+ sizeof(struct phy *), GFP_KERNEL);
+ if (!priv->phys)
+ return -ENOMEM;
+
+ for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+ err = of_property_read_string_index(
+ dev->dev.of_node,
+ "phy-names", phy_num,
+ &phy_name);
+
+ if (err < 0) {
+ if (priv->num_phys > 1) {
+ dev_err(&dev->dev, "phy-names not provided");
+ goto err_put_hcd;
+ } else
+ phy_name = "usb";
+ }
+
+ priv->phys[phy_num] = devm_phy_get(&dev->dev,
+ phy_name);
+ if (IS_ERR(priv->phys[phy_num])) {
+ err = PTR_ERR(priv->phys[phy_num]);
+ if ((priv->num_phys > 1) ||
+ (err == -EPROBE_DEFER))
+ goto err_put_hcd;
+ priv->phys[phy_num] = NULL;
+ }
}
for (clk = 0; clk < OHCI_MAX_CLKS; clk++) {
@@ -328,6 +366,7 @@ static int ohci_platform_resume(struct device *dev)
static const struct of_device_id ohci_platform_ids[] = {
{ .compatible = "generic-ohci", },
+ { .compatible = "cavium,octeon-6335-ohci", },
{ }
};
MODULE_DEVICE_TABLE(of, ohci_platform_ids);
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index 965e3e9e688a..4f87a5c61b08 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -229,7 +229,6 @@ static struct platform_driver ohci_hcd_ppc_of_driver = {
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "ppc-of-ohci",
- .owner = THIS_MODULE,
.of_match_table = ohci_hcd_ppc_of_match,
},
};
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index 095113ea1fcb..7a1919ca543a 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -249,14 +249,14 @@ static int ohci_s3c2410_hub_control(
*/
desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM);
- desc->wHubCharacteristics |= cpu_to_le16(0x0001);
+ desc->wHubCharacteristics |= cpu_to_le16(
+ HUB_CHAR_INDV_PORT_LPSM);
if (info->enable_oc) {
desc->wHubCharacteristics &= ~cpu_to_le16(
HUB_CHAR_OCPM);
desc->wHubCharacteristics |= cpu_to_le16(
- 0x0008 |
- 0x0001);
+ HUB_CHAR_INDV_PORT_OCPM);
}
dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n",
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c
index 4e81c804c73e..a8b8d8b8d9f3 100644
--- a/drivers/usb/host/ohci-sm501.c
+++ b/drivers/usb/host/ohci-sm501.c
@@ -265,7 +265,6 @@ static struct platform_driver ohci_hcd_sm501_driver = {
.suspend = ohci_sm501_suspend,
.resume = ohci_sm501_resume,
.driver = {
- .owner = THIS_MODULE,
.name = "sm501-usb",
},
};
diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c
index bef6dfb0405a..e1b208da460a 100644
--- a/drivers/usb/host/ohci-tilegx.c
+++ b/drivers/usb/host/ohci-tilegx.c
@@ -199,7 +199,6 @@ static struct platform_driver ohci_hcd_tilegx_driver = {
.shutdown = ohci_hcd_tilegx_drv_shutdown,
.driver = {
.name = "tilegx-ohci",
- .owner = THIS_MODULE,
}
};
diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c
index bb409588d39c..e9a6eec39142 100644
--- a/drivers/usb/host/ohci-tmio.c
+++ b/drivers/usb/host/ohci-tmio.c
@@ -368,6 +368,5 @@ static struct platform_driver ohci_hcd_tmio_driver = {
.resume = ohci_hcd_tmio_drv_resume,
.driver = {
.name = "tmio-ohci",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 036924e640f5..ef7efb278b15 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -457,11 +457,11 @@ static void ehci_hub_descriptor(struct oxu_hcd *oxu,
memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
- temp = 0x0008; /* per-port overcurrent reporting */
+ temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */
if (HCS_PPC(oxu->hcs_params))
- temp |= 0x0001; /* per-port power control */
+ temp |= HUB_CHAR_INDV_PORT_LPSM; /* per-port power control */
else
- temp |= 0x0002; /* no power switching */
+ temp |= HUB_CHAR_NO_LPSM; /* no power switching */
desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp);
}
@@ -2598,9 +2598,7 @@ static int oxu_hcd_init(struct usb_hcd *hcd)
spin_lock_init(&oxu->lock);
- init_timer(&oxu->watchdog);
- oxu->watchdog.function = oxu_watchdog;
- oxu->watchdog.data = (unsigned long) oxu;
+ setup_timer(&oxu->watchdog, oxu_watchdog, (unsigned long)oxu);
/*
* hw default: 1K periodic list heads, one per frame.
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index ce636466edb7..f9400564cb72 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -603,9 +603,9 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev)
msleep(10);
}
if (wait_time <= 0)
- dev_warn(&pdev->dev, "OHCI: BIOS handoff failed"
- " (BIOS bug?) %08x\n",
- readl(base + OHCI_CONTROL));
+ dev_warn(&pdev->dev,
+ "OHCI: BIOS handoff failed (BIOS bug?) %08x\n",
+ readl(base + OHCI_CONTROL));
}
#endif
@@ -733,8 +733,9 @@ static void ehci_bios_handoff(struct pci_dev *pdev,
* and hope nothing goes too wrong
*/
if (try_handoff)
- dev_warn(&pdev->dev, "EHCI: BIOS handoff failed"
- " (BIOS bug?) %08x\n", cap);
+ dev_warn(&pdev->dev,
+ "EHCI: BIOS handoff failed (BIOS bug?) %08x\n",
+ cap);
pci_write_config_byte(pdev, offset + 2, 0);
}
@@ -781,8 +782,9 @@ static void quirk_usb_disable_ehci(struct pci_dev *pdev)
case 0: /* Illegal reserved cap, set cap=0 so we exit */
cap = 0; /* then fallthrough... */
default:
- dev_warn(&pdev->dev, "EHCI: unrecognized capability "
- "%02x\n", cap & 0xff);
+ dev_warn(&pdev->dev,
+ "EHCI: unrecognized capability %02x\n",
+ cap & 0xff);
}
offset = (cap >> 8) & 0xff;
}
@@ -893,8 +895,7 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev)
*/
if (!IS_ENABLED(CONFIG_USB_XHCI_HCD)) {
dev_warn(&xhci_pdev->dev,
- "CONFIG_USB_XHCI_HCD is turned off, "
- "defaulting to EHCI.\n");
+ "CONFIG_USB_XHCI_HCD is turned off, defaulting to EHCI.\n");
dev_warn(&xhci_pdev->dev,
"USB 3.0 devices will work at USB 2.0 speeds.\n");
usb_disable_xhci_ports(xhci_pdev);
@@ -919,8 +920,9 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev)
pci_read_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
&ports_available);
- dev_dbg(&xhci_pdev->dev, "USB 3.0 ports that are now enabled "
- "under xHCI: 0x%x\n", ports_available);
+ dev_dbg(&xhci_pdev->dev,
+ "USB 3.0 ports that are now enabled under xHCI: 0x%x\n",
+ ports_available);
/* Read XUSB2PRM, xHCI USB 2.0 Port Routing Mask Register
* Indicate the USB 2.0 ports to be controlled by the xHCI host.
@@ -941,8 +943,9 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev)
pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
&ports_available);
- dev_dbg(&xhci_pdev->dev, "USB 2.0 ports that are now switched over "
- "to xHCI: 0x%x\n", ports_available);
+ dev_dbg(&xhci_pdev->dev,
+ "USB 2.0 ports that are now switched over to xHCI: 0x%x\n",
+ ports_available);
}
EXPORT_SYMBOL_GPL(usb_enable_intel_xhci_ports);
@@ -1010,8 +1013,9 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
/* Assume a buggy BIOS and take HC ownership anyway */
if (timeout) {
- dev_warn(&pdev->dev, "xHCI BIOS handoff failed"
- " (BIOS bug ?) %08x\n", val);
+ dev_warn(&pdev->dev,
+ "xHCI BIOS handoff failed (BIOS bug ?) %08x\n",
+ val);
writel(val & ~XHCI_HC_BIOS_OWNED, base + ext_cap_offset);
}
}
@@ -1039,8 +1043,8 @@ hc_init:
if (timeout) {
val = readl(op_reg_base + XHCI_STS_OFFSET);
dev_warn(&pdev->dev,
- "xHCI HW not ready after 5 sec (HC bug?) "
- "status = 0x%x\n", val);
+ "xHCI HW not ready after 5 sec (HC bug?) status = 0x%x\n",
+ val);
}
/* Send the halt and disable interrupts command */
@@ -1054,8 +1058,8 @@ hc_init:
if (timeout) {
val = readl(op_reg_base + XHCI_STS_OFFSET);
dev_warn(&pdev->dev,
- "xHCI HW did not halt within %d usec "
- "status = 0x%x\n", XHCI_MAX_HALT_USEC, val);
+ "xHCI HW did not halt within %d usec status = 0x%x\n",
+ XHCI_MAX_HALT_USEC, val);
}
iounmap(base);
@@ -1075,8 +1079,8 @@ static void quirk_usb_early_handoff(struct pci_dev *pdev)
return;
if (pci_enable_device(pdev) < 0) {
- dev_warn(&pdev->dev, "Can't enable PCI device, "
- "BIOS handoff failed.\n");
+ dev_warn(&pdev->dev,
+ "Can't enable PCI device, BIOS handoff failed.\n");
return;
}
if (pdev->class == PCI_CLASS_SERIAL_USB_UHCI)
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index c4bcfaedeec9..bdc82fea0a1f 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2141,7 +2141,8 @@ static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597,
desc->bNbrPorts = r8a66597->max_root_hub;
desc->bDescLength = 9;
desc->bPwrOn2PwrGood = 0;
- desc->wHubCharacteristics = cpu_to_le16(0x0011);
+ desc->wHubCharacteristics =
+ cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM);
desc->u.hs.DeviceRemovable[0] =
((1 << r8a66597->max_root_hub) - 1) << 1;
desc->u.hs.DeviceRemovable[1] = ~0;
@@ -2483,9 +2484,8 @@ static int r8a66597_probe(struct platform_device *pdev)
r8a66597->max_root_hub = 2;
spin_lock_init(&r8a66597->lock);
- init_timer(&r8a66597->rh_timer);
- r8a66597->rh_timer.function = r8a66597_timer;
- r8a66597->rh_timer.data = (unsigned long)r8a66597;
+ setup_timer(&r8a66597->rh_timer, r8a66597_timer,
+ (unsigned long)r8a66597);
r8a66597->reg = reg;
/* make sure no interrupts are pending */
@@ -2496,9 +2496,8 @@ static int r8a66597_probe(struct platform_device *pdev)
for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) {
INIT_LIST_HEAD(&r8a66597->pipe_queue[i]);
- init_timer(&r8a66597->td_timer[i]);
- r8a66597->td_timer[i].function = r8a66597_td_timer;
- r8a66597->td_timer[i].data = (unsigned long)r8a66597;
+ setup_timer(&r8a66597->td_timer[i], r8a66597_td_timer,
+ (unsigned long)r8a66597);
setup_timer(&r8a66597->interval_timer[i],
r8a66597_interval_timer,
(unsigned long)r8a66597);
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 25fb1da8d3d7..4f4ba1ea9e9b 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1103,12 +1103,12 @@ sl811h_hub_descriptor (
desc->bPwrOn2PwrGood = sl811->board->potpg;
if (!desc->bPwrOn2PwrGood)
desc->bPwrOn2PwrGood = 10;
- temp = 0x0001;
+ temp = HUB_CHAR_INDV_PORT_LPSM;
} else
- temp = 0x0002;
+ temp = HUB_CHAR_NO_LPSM;
/* no overcurrent errors detection/handling */
- temp |= 0x0010;
+ temp |= HUB_CHAR_NO_OCPM;
desc->wHubCharacteristics = cpu_to_le16(temp);
@@ -1691,9 +1691,7 @@ sl811h_probe(struct platform_device *dev)
spin_lock_init(&sl811->lock);
INIT_LIST_HEAD(&sl811->async);
sl811->board = dev_get_platdata(&dev->dev);
- init_timer(&sl811->timer);
- sl811->timer.function = sl811h_timer;
- sl811->timer.data = (unsigned long) sl811;
+ setup_timer(&sl811->timer, sl811h_timer, (unsigned long)sl811);
sl811->addr_reg = addr_reg;
sl811->data_reg = data_reg;
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 2894e54e5b9c..ad97e8a1ad1c 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -2590,15 +2590,15 @@ static int u132_roothub_descriptor(struct u132 *u132,
desc->bNbrPorts = u132->num_ports;
temp = 1 + (u132->num_ports / 8);
desc->bDescLength = 7 + 2 * temp;
- temp = 0;
+ temp = HUB_CHAR_COMMON_LPSM | HUB_CHAR_COMMON_OCPM;
if (rh_a & RH_A_NPS)
- temp |= 0x0002;
+ temp |= HUB_CHAR_NO_LPSM;
if (rh_a & RH_A_PSM)
- temp |= 0x0001;
+ temp |= HUB_CHAR_INDV_PORT_LPSM;
if (rh_a & RH_A_NOCP)
- temp |= 0x0010;
+ temp |= HUB_CHAR_NO_OCPM;
else if (rh_a & RH_A_OCPM)
- temp |= 0x0008;
+ temp |= HUB_CHAR_INDV_PORT_OCPM;
desc->wHubCharacteristics = cpu_to_le16(temp);
retval = u132_read_pcimem(u132, roothub.b, &rh_b);
if (retval)
diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c
index 05f57ffdf9ab..0342991c9507 100644
--- a/drivers/usb/host/uhci-grlib.c
+++ b/drivers/usb/host/uhci-grlib.c
@@ -188,7 +188,6 @@ static struct platform_driver uhci_grlib_driver = {
.shutdown = uhci_hcd_grlib_shutdown,
.driver = {
.name = "grlib-uhci",
- .owner = THIS_MODULE,
.of_match_table = uhci_hcd_grlib_of_match,
},
};
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 93e17b12fb33..19ba5eafb31e 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -17,8 +17,9 @@ static const __u8 root_hub_hub_des[] =
0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */
0x02, /* __u8 bNbrPorts; */
- 0x0a, /* __u16 wHubCharacteristics; */
- 0x00, /* (per-port OC, no power switching) */
+ HUB_CHAR_NO_LPSM | /* __u16 wHubCharacteristics; */
+ HUB_CHAR_INDV_PORT_OCPM, /* (per-port OC, no power switching) */
+ 0x00,
0x01, /* __u8 bPwrOn2pwrGood; 2ms */
0x00, /* __u8 bHubContrCurrent; 0 mA */
0x00, /* __u8 DeviceRemovable; *** 7 Ports max */
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index cf8f46003f62..3a3e3eeba291 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -147,7 +147,6 @@ static struct platform_driver uhci_platform_driver = {
.shutdown = uhci_hcd_platform_shutdown,
.driver = {
.name = "platform-uhci",
- .owner = THIS_MODULE,
.of_match_table = platform_uhci_ids,
},
};
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index bb89175ca6e5..745717ec9c89 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -552,7 +552,7 @@ void xhci_dbg_ctx(struct xhci_hcd *xhci,
if (ctx->type == XHCI_CTX_TYPE_INPUT) {
struct xhci_input_control_ctx *ctrl_ctx =
- xhci_get_input_control_ctx(xhci, ctx);
+ xhci_get_input_control_ctx(ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "Could not get input context, bad type.\n");
return;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 5cb3d7a10017..f8336408ef07 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -535,7 +535,7 @@ static void xhci_free_container_ctx(struct xhci_hcd *xhci,
kfree(ctx);
}
-struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_hcd *xhci,
+struct xhci_input_control_ctx *xhci_get_input_control_ctx(
struct xhci_container_ctx *ctx)
{
if (ctx->type != XHCI_CTX_TYPE_INPUT)
@@ -784,8 +784,7 @@ void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
* Reinstalls the "normal" endpoint ring (at its previous dequeue mark,
* not at the beginning of the ring).
*/
-void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci,
- struct xhci_ep_ctx *ep_ctx,
+void xhci_setup_no_streams_ep_input_ctx(struct xhci_ep_ctx *ep_ctx,
struct xhci_virt_ep *ep)
{
dma_addr_t addr;
@@ -833,9 +832,8 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
static void xhci_init_endpoint_timer(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep)
{
- init_timer(&ep->stop_cmd_timer);
- ep->stop_cmd_timer.data = (unsigned long) ep;
- ep->stop_cmd_timer.function = xhci_stop_endpoint_command_watchdog;
+ setup_timer(&ep->stop_cmd_timer, xhci_stop_endpoint_command_watchdog,
+ (unsigned long)ep);
ep->xhci = xhci;
}
@@ -1342,8 +1340,7 @@ static u32 xhci_get_endpoint_mult(struct usb_device *udev,
return ep->ss_ep_comp.bmAttributes;
}
-static u32 xhci_get_endpoint_type(struct usb_device *udev,
- struct usb_host_endpoint *ep)
+static u32 xhci_get_endpoint_type(struct usb_host_endpoint *ep)
{
int in;
u32 type;
@@ -1376,8 +1373,7 @@ static u32 xhci_get_endpoint_type(struct usb_device *udev,
* Basically, this is the maxpacket size, multiplied by the burst size
* and mult size.
*/
-static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci,
- struct usb_device *udev,
+static u32 xhci_get_max_esit_payload(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
int max_burst;
@@ -1418,7 +1414,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
ep_index = xhci_get_endpoint_index(&ep->desc);
ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
- endpoint_type = xhci_get_endpoint_type(udev, ep);
+ endpoint_type = xhci_get_endpoint_type(ep);
if (!endpoint_type)
return -EINVAL;
ep_ctx->ep_info2 = cpu_to_le32(endpoint_type);
@@ -1484,7 +1480,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
}
ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet) |
MAX_BURST(max_burst));
- max_esit_payload = xhci_get_max_esit_payload(xhci, udev, ep);
+ max_esit_payload = xhci_get_max_esit_payload(udev, ep);
ep_ctx->tx_info = cpu_to_le32(MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload));
/*
@@ -1773,7 +1769,7 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
return command;
}
-void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv)
+void xhci_urb_free_priv(struct urb_priv *urb_priv)
{
if (urb_priv) {
kfree(urb_priv->td[0]);
@@ -1926,7 +1922,7 @@ static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
}
/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */
-static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci, gfp_t mem_flags)
+static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci)
{
struct {
dma_addr_t input_dma;
@@ -2452,7 +2448,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
flags);
if (!xhci->event_ring)
goto fail;
- if (xhci_check_trb_in_td_math(xhci, flags) < 0)
+ if (xhci_check_trb_in_td_math(xhci) < 0)
goto fail;
xhci->erst.entries = dma_alloc_coherent(dev,
@@ -2509,9 +2505,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci_print_ir_set(xhci, 0);
/* init command timeout timer */
- init_timer(&xhci->cmd_timer);
- xhci->cmd_timer.data = (unsigned long) xhci;
- xhci->cmd_timer.function = xhci_handle_command_timeout;
+ setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
+ (unsigned long)xhci);
/*
* XXX: Might need to set the Interrupter Moderation Register to
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e692e769c50c..88da8d629820 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -299,7 +299,7 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
* seconds), then it should assume that the there are
* larger problems with the xHC and assert HCRST.
*/
- ret = xhci_handshake(xhci, &xhci->op_regs->cmd_ring,
+ ret = xhci_handshake(&xhci->op_regs->cmd_ring,
CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
if (ret < 0) {
xhci_err(xhci, "Stopped the command ring failed, "
@@ -609,7 +609,7 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
spin_unlock(&xhci->lock);
usb_hcd_giveback_urb(hcd, urb, status);
- xhci_urb_free_priv(xhci, urb_priv);
+ xhci_urb_free_priv(urb_priv);
spin_lock(&xhci->lock);
}
}
@@ -1110,7 +1110,7 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
* is not waiting on the configure endpoint command.
*/
virt_dev = xhci->devs[slot_id];
- ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "Could not get input context, bad type.\n");
return;
@@ -2354,8 +2354,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
status = 0;
break;
}
- xhci_warn(xhci, "ERROR Unknown event condition, HC probably "
- "busted\n");
+ xhci_warn(xhci, "ERROR Unknown event condition %u, HC probably busted\n",
+ trb_comp_code);
goto cleanup;
}
@@ -2497,7 +2497,7 @@ cleanup:
urb = td->urb;
urb_priv = urb->hcpriv;
- xhci_urb_free_priv(xhci, urb_priv);
+ xhci_urb_free_priv(urb_priv);
usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
if ((urb->actual_length != urb->transfer_buffer_length &&
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index c50d8d202618..ec8ac1674854 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -60,8 +60,7 @@ MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default");
* handshake done). There are two failure modes: "usec" have passed (major
* hardware flakeout), or the register reads as all-ones (hardware removed).
*/
-int xhci_handshake(struct xhci_hcd *xhci, void __iomem *ptr,
- u32 mask, u32 done, int usec)
+int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
{
u32 result;
@@ -111,7 +110,7 @@ int xhci_halt(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Halt the HC");
xhci_quiesce(xhci);
- ret = xhci_handshake(xhci, &xhci->op_regs->status,
+ ret = xhci_handshake(&xhci->op_regs->status,
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
if (!ret) {
xhci->xhc_state |= XHCI_STATE_HALTED;
@@ -140,7 +139,7 @@ static int xhci_start(struct xhci_hcd *xhci)
* Wait for the HCHalted Status bit to be 0 to indicate the host is
* running.
*/
- ret = xhci_handshake(xhci, &xhci->op_regs->status,
+ ret = xhci_handshake(&xhci->op_regs->status,
STS_HALT, 0, XHCI_MAX_HALT_USEC);
if (ret == -ETIMEDOUT)
xhci_err(xhci, "Host took too long to start, "
@@ -175,7 +174,7 @@ int xhci_reset(struct xhci_hcd *xhci)
command |= CMD_RESET;
writel(command, &xhci->op_regs->command);
- ret = xhci_handshake(xhci, &xhci->op_regs->command,
+ ret = xhci_handshake(&xhci->op_regs->command,
CMD_RESET, 0, 10 * 1000 * 1000);
if (ret)
return ret;
@@ -186,7 +185,7 @@ int xhci_reset(struct xhci_hcd *xhci)
* xHCI cannot write to any doorbells or operational registers other
* than status until the "Controller Not Ready" flag is cleared.
*/
- ret = xhci_handshake(xhci, &xhci->op_regs->status,
+ ret = xhci_handshake(&xhci->op_regs->status,
STS_CNR, 0, 10 * 1000 * 1000);
for (i = 0; i < 2; ++i) {
@@ -473,10 +472,8 @@ static void compliance_mode_recovery(unsigned long arg)
static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci)
{
xhci->port_status_u0 = 0;
- init_timer(&xhci->comp_mode_recovery_timer);
-
- xhci->comp_mode_recovery_timer.data = (unsigned long) xhci;
- xhci->comp_mode_recovery_timer.function = compliance_mode_recovery;
+ setup_timer(&xhci->comp_mode_recovery_timer,
+ compliance_mode_recovery, (unsigned long)xhci);
xhci->comp_mode_recovery_timer.expires = jiffies +
msecs_to_jiffies(COMP_MODE_RCVRY_MSECS);
@@ -929,7 +926,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
/* Some chips from Fresco Logic need an extraordinary delay */
delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1;
- if (xhci_handshake(xhci, &xhci->op_regs->status,
+ if (xhci_handshake(&xhci->op_regs->status,
STS_HALT, STS_HALT, delay)) {
xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
spin_unlock_irq(&xhci->lock);
@@ -944,7 +941,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
command = readl(&xhci->op_regs->command);
command |= CMD_CSS;
writel(command, &xhci->op_regs->command);
- if (xhci_handshake(xhci, &xhci->op_regs->status,
+ if (xhci_handshake(&xhci->op_regs->status,
STS_SAVE, 0, 10 * 1000)) {
xhci_warn(xhci, "WARN: xHC save state timeout\n");
spin_unlock_irq(&xhci->lock);
@@ -1011,7 +1008,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
command = readl(&xhci->op_regs->command);
command |= CMD_CRS;
writel(command, &xhci->op_regs->command);
- if (xhci_handshake(xhci, &xhci->op_regs->status,
+ if (xhci_handshake(&xhci->op_regs->status,
STS_RESTORE, 0, 10 * 1000)) {
xhci_warn(xhci, "WARN: xHC restore state timeout\n");
spin_unlock_irq(&xhci->lock);
@@ -1082,7 +1079,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
command = readl(&xhci->op_regs->command);
command |= CMD_RUN;
writel(command, &xhci->op_regs->command);
- xhci_handshake(xhci, &xhci->op_regs->status, STS_HALT,
+ xhci_handshake(&xhci->op_regs->status, STS_HALT,
0, 250 * 1000);
/* step 5: walk topology and initialize portsc,
@@ -1276,7 +1273,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id,
return -ENOMEM;
command->in_ctx = xhci->devs[slot_id]->in_ctx;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -1374,7 +1371,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
ret = xhci_check_maxpacket(xhci, slot_id,
ep_index, urb);
if (ret < 0) {
- xhci_urb_free_priv(xhci, urb_priv);
+ xhci_urb_free_priv(urb_priv);
urb->hcpriv = NULL;
return ret;
}
@@ -1440,7 +1437,7 @@ dying:
urb->ep->desc.bEndpointAddress, urb);
ret = -ESHUTDOWN;
free_priv:
- xhci_urb_free_priv(xhci, urb_priv);
+ xhci_urb_free_priv(urb_priv);
urb->hcpriv = NULL;
spin_unlock_irqrestore(&xhci->lock, flags);
return ret;
@@ -1553,7 +1550,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock_irqrestore(&xhci->lock, flags);
usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
- xhci_urb_free_priv(xhci, urb_priv);
+ xhci_urb_free_priv(urb_priv);
return ret;
}
if ((xhci->xhc_state & XHCI_STATE_DYING) ||
@@ -1660,7 +1657,7 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
in_ctx = xhci->devs[udev->slot_id]->in_ctx;
out_ctx = xhci->devs[udev->slot_id]->out_ctx;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -1676,8 +1673,10 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
cpu_to_le32(EP_STATE_DISABLED)) ||
le32_to_cpu(ctrl_ctx->drop_flags) &
xhci_get_endpoint_flag(&ep->desc)) {
- xhci_warn(xhci, "xHCI %s called with disabled ep %p\n",
- __func__, ep);
+ /* Do not warn when called after a usb_device_reset */
+ if (xhci->devs[udev->slot_id]->eps[ep_index].ring != NULL)
+ xhci_warn(xhci, "xHCI %s called with disabled ep %p\n",
+ __func__, ep);
return 0;
}
@@ -1714,7 +1713,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep)
{
struct xhci_hcd *xhci;
- struct xhci_container_ctx *in_ctx, *out_ctx;
+ struct xhci_container_ctx *in_ctx;
unsigned int ep_index;
struct xhci_input_control_ctx *ctrl_ctx;
u32 added_ctxs;
@@ -1745,8 +1744,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
virt_dev = xhci->devs[udev->slot_id];
in_ctx = virt_dev->in_ctx;
- out_ctx = virt_dev->out_ctx;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -1758,8 +1756,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
* to add it again without dropping it, reject the addition.
*/
if (virt_dev->eps[ep_index].ring &&
- !(le32_to_cpu(ctrl_ctx->drop_flags) &
- xhci_get_endpoint_flag(&ep->desc))) {
+ !(le32_to_cpu(ctrl_ctx->drop_flags) & added_ctxs)) {
xhci_warn(xhci, "Trying to add endpoint 0x%x "
"without dropping it.\n",
(unsigned int) ep->desc.bEndpointAddress);
@@ -1769,8 +1766,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
/* If the HCD has already noted the endpoint is enabled,
* ignore this request.
*/
- if (le32_to_cpu(ctrl_ctx->add_flags) &
- xhci_get_endpoint_flag(&ep->desc)) {
+ if (le32_to_cpu(ctrl_ctx->add_flags) & added_ctxs) {
xhci_warn(xhci, "xHCI %s called with enabled ep %p\n",
__func__, ep);
return 0;
@@ -1816,7 +1812,7 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir
struct xhci_slot_ctx *slot_ctx;
int i;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -2542,7 +2538,7 @@ static int xhci_reserve_bandwidth(struct xhci_hcd *xhci,
if (virt_dev->tt_info)
old_active_eps = virt_dev->tt_info->active_eps;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -2639,7 +2635,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
spin_lock_irqsave(&xhci->lock, flags);
virt_dev = xhci->devs[udev->slot_id];
- ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
if (!ctrl_ctx) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
@@ -2758,7 +2754,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
command->in_ctx = virt_dev->in_ctx;
/* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */
- ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -2883,7 +2879,7 @@ static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci,
dma_addr_t addr;
in_ctx = xhci->devs[slot_id]->in_ctx;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -3173,7 +3169,7 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
return -ENOMEM;
}
- ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(config_cmd->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -3328,7 +3324,7 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
*/
ep_index = xhci_get_endpoint_index(&eps[0]->desc);
command = vdev->eps[ep_index].stream_info->free_streams_command;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
if (!ctrl_ctx) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
@@ -3346,7 +3342,7 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev,
xhci_endpoint_copy(xhci, command->in_ctx,
vdev->out_ctx, ep_index);
- xhci_setup_no_streams_ep_input_ctx(xhci, ep_ctx,
+ xhci_setup_no_streams_ep_input_ctx(ep_ctx,
&vdev->eps[ep_index]);
}
xhci_setup_input_ctx_for_config_ep(xhci, command->in_ctx,
@@ -3820,7 +3816,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
command->completion = &xhci->addr_dev;
slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
- ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -4003,7 +3999,7 @@ static int __maybe_unused xhci_change_max_exit_latency(struct xhci_hcd *xhci,
/* Attempt to issue an Evaluate Context command to change the MEL. */
command = xhci->lpm_command;
- ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
if (!ctrl_ctx) {
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
@@ -4741,7 +4737,7 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
return -ENOMEM;
}
- ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx);
+ ctrl_ctx = xhci_get_input_control_ctx(config_cmd->in_ctx);
if (!ctrl_ctx) {
xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
__func__);
@@ -4910,6 +4906,10 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
if (retval)
goto error;
xhci_dbg(xhci, "Called HCD init\n");
+
+ xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%08x\n",
+ xhci->hcc_params, xhci->hci_version, xhci->quirks);
+
return 0;
error:
kfree(xhci);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index cc7c5bb7cbcf..974514762a14 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1605,6 +1605,8 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci)
dev_warn(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
#define xhci_warn_ratelimited(xhci, fmt, args...) \
dev_warn_ratelimited(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
+#define xhci_info(xhci, fmt, args...) \
+ dev_info(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
/*
* Registers should always be accessed with double word or quad word accesses.
@@ -1712,8 +1714,7 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci,
struct xhci_ep_ctx *ep_ctx,
struct xhci_stream_info *stream_info);
-void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci,
- struct xhci_ep_ctx *ep_ctx,
+void xhci_setup_no_streams_ep_input_ctx(struct xhci_ep_ctx *ep_ctx,
struct xhci_virt_ep *ep);
void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci,
struct xhci_virt_device *virt_dev, bool drop_control_ep);
@@ -1727,14 +1728,13 @@ struct xhci_ring *xhci_stream_id_to_ring(
struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
bool allocate_in_ctx, bool allocate_completion,
gfp_t mem_flags);
-void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv);
+void xhci_urb_free_priv(struct urb_priv *urb_priv);
void xhci_free_command(struct xhci_hcd *xhci,
struct xhci_command *command);
/* xHCI host controller glue */
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
-int xhci_handshake(struct xhci_hcd *xhci, void __iomem *ptr,
- u32 mask, u32 done, int usec);
+int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
void xhci_quiesce(struct xhci_hcd *xhci);
int xhci_halt(struct xhci_hcd *xhci);
int xhci_reset(struct xhci_hcd *xhci);
@@ -1864,7 +1864,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
void xhci_ring_device(struct xhci_hcd *xhci, int slot_id);
/* xHCI contexts */
-struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx);
+struct xhci_input_control_ctx *xhci_get_input_control_ctx(struct xhci_container_ctx *ctx);
struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx);
struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int ep_index);
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 37b44b04a701..6431d08c8d9d 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -299,9 +299,7 @@ static inline void mts_show_command(struct scsi_cmnd *srb)
MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len);
out:
- MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
- srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
- srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+ MTS_DEBUG( " %10ph\n", srb->cmnd);
}
#else
diff --git a/drivers/usb/isp1760/Kconfig b/drivers/usb/isp1760/Kconfig
new file mode 100644
index 000000000000..c94b7d953399
--- /dev/null
+++ b/drivers/usb/isp1760/Kconfig
@@ -0,0 +1,59 @@
+config USB_ISP1760
+ tristate "NXP ISP 1760/1761 support"
+ depends on USB || USB_GADGET
+ help
+ Say Y or M here if your system as an ISP1760 USB host controller
+ or an ISP1761 USB dual-role controller.
+
+ This driver does not support isochronous transfers or OTG.
+ This USB controller is usually attached to a non-DMA-Master
+ capable bus. NXP's eval kit brings this chip on PCI card
+ where the chip itself is behind a PLB to simulate such
+ a bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called isp1760.
+
+config USB_ISP1760_HCD
+ bool
+
+config USB_ISP1761_UDC
+ bool
+
+if USB_ISP1760
+
+choice
+ bool "ISP1760 Mode Selection"
+ default USB_ISP1760_DUAL_ROLE if (USB && USB_GADGET)
+ default USB_ISP1760_HOST_ROLE if (USB && !USB_GADGET)
+ default USB_ISP1760_GADGET_ROLE if (!USB && USB_GADGET)
+
+config USB_ISP1760_HOST_ROLE
+ bool "Host only mode"
+ depends on USB=y || USB=USB_ISP1760
+ select USB_ISP1760_HCD
+ help
+ Select this if you want to use the ISP1760 in host mode only. The
+ gadget function will be disabled.
+
+config USB_ISP1760_GADGET_ROLE
+ bool "Gadget only mode"
+ depends on USB_GADGET=y || USB_GADGET=USB_ISP1760
+ select USB_ISP1761_UDC
+ help
+ Select this if you want to use the ISP1760 in peripheral mode only.
+ The host function will be disabled.
+
+config USB_ISP1760_DUAL_ROLE
+ bool "Dual Role mode"
+ depends on USB=y || USB=USB_ISP1760
+ depends on USB_GADGET=y || USB_GADGET=USB_ISP1760
+ select USB_ISP1760_HCD
+ select USB_ISP1761_UDC
+ help
+ Select this if you want to use the ISP1760 in both host and
+ peripheral modes.
+
+endchoice
+
+endif
diff --git a/drivers/usb/isp1760/Makefile b/drivers/usb/isp1760/Makefile
new file mode 100644
index 000000000000..2b741074ad2b
--- /dev/null
+++ b/drivers/usb/isp1760/Makefile
@@ -0,0 +1,5 @@
+isp1760-y := isp1760-core.o isp1760-if.o
+isp1760-$(CONFIG_USB_ISP1760_HCD) += isp1760-hcd.o
+isp1760-$(CONFIG_USB_ISP1761_UDC) += isp1760-udc.o
+
+obj-$(CONFIG_USB_ISP1760) += isp1760.o
diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c
new file mode 100644
index 000000000000..b9827556455f
--- /dev/null
+++ b/drivers/usb/isp1760/isp1760-core.c
@@ -0,0 +1,177 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ * Sebastian Siewior <bigeasy@linutronix.de>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "isp1760-core.h"
+#include "isp1760-hcd.h"
+#include "isp1760-regs.h"
+#include "isp1760-udc.h"
+
+static void isp1760_init_core(struct isp1760_device *isp)
+{
+ u32 otgctrl;
+ u32 hwmode;
+
+ /* Low-level chip reset */
+ if (isp->rst_gpio) {
+ gpiod_set_value_cansleep(isp->rst_gpio, 1);
+ mdelay(50);
+ gpiod_set_value_cansleep(isp->rst_gpio, 0);
+ }
+
+ /*
+ * Reset the host controller, including the CPU interface
+ * configuration.
+ */
+ isp1760_write32(isp->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
+ msleep(100);
+
+ /* Setup HW Mode Control: This assumes a level active-low interrupt */
+ hwmode = HW_DATA_BUS_32BIT;
+
+ if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16)
+ hwmode &= ~HW_DATA_BUS_32BIT;
+ if (isp->devflags & ISP1760_FLAG_ANALOG_OC)
+ hwmode |= HW_ANA_DIGI_OC;
+ if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH)
+ hwmode |= HW_DACK_POL_HIGH;
+ if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
+ hwmode |= HW_DREQ_POL_HIGH;
+ if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH)
+ hwmode |= HW_INTR_HIGH_ACT;
+ if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
+ hwmode |= HW_INTR_EDGE_TRIG;
+
+ /*
+ * The ISP1761 has a dedicated DC IRQ line but supports sharing the HC
+ * IRQ line for both the host and device controllers. Hardcode IRQ
+ * sharing for now and disable the DC interrupts globally to avoid
+ * spurious interrupts during HCD registration.
+ */
+ if (isp->devflags & ISP1760_FLAG_ISP1761) {
+ isp1760_write32(isp->regs, DC_MODE, 0);
+ hwmode |= HW_COMN_IRQ;
+ }
+
+ /*
+ * We have to set this first in case we're in 16-bit mode.
+ * Write it twice to ensure correct upper bits if switching
+ * to 16-bit mode.
+ */
+ isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
+ isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
+
+ /*
+ * PORT 1 Control register of the ISP1760 is the OTG control register
+ * on ISP1761.
+ *
+ * TODO: Really support OTG. For now we configure port 1 in device mode
+ * when OTG is requested.
+ */
+ if ((isp->devflags & ISP1760_FLAG_ISP1761) &&
+ (isp->devflags & ISP1760_FLAG_OTG_EN))
+ otgctrl = ((HW_DM_PULLDOWN | HW_DP_PULLDOWN) << 16)
+ | HW_OTG_DISABLE;
+ else
+ otgctrl = (HW_SW_SEL_HC_DC << 16)
+ | (HW_VBUS_DRV | HW_SEL_CP_EXT);
+
+ isp1760_write32(isp->regs, HC_PORT1_CTRL, otgctrl);
+
+ dev_info(isp->dev, "bus width: %u, oc: %s\n",
+ isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32,
+ isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital");
+}
+
+void isp1760_set_pullup(struct isp1760_device *isp, bool enable)
+{
+ isp1760_write32(isp->regs, HW_OTG_CTRL_SET,
+ enable ? HW_DP_PULLUP : HW_DP_PULLUP << 16);
+}
+
+int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
+ struct device *dev, unsigned int devflags)
+{
+ struct isp1760_device *isp;
+ bool udc_disabled = !(devflags & ISP1760_FLAG_ISP1761);
+ int ret;
+
+ /*
+ * If neither the HCD not the UDC is enabled return an error, as no
+ * device would be registered.
+ */
+ if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) &&
+ (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled))
+ return -ENODEV;
+
+ /* prevent usb-core allocating DMA pages */
+ dev->dma_mask = NULL;
+
+ isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
+ if (!isp)
+ return -ENOMEM;
+
+ isp->dev = dev;
+ isp->devflags = devflags;
+
+ isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
+ if (IS_ERR(isp->rst_gpio))
+ return PTR_ERR(isp->rst_gpio);
+
+ isp->regs = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(isp->regs))
+ return PTR_ERR(isp->regs);
+
+ isp1760_init_core(isp);
+
+ if (IS_ENABLED(CONFIG_USB_ISP1760_HCD) && !usb_disabled()) {
+ ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq,
+ irqflags | IRQF_SHARED, dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) {
+ ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED |
+ IRQF_DISABLED);
+ if (ret < 0) {
+ isp1760_hcd_unregister(&isp->hcd);
+ return ret;
+ }
+ }
+
+ dev_set_drvdata(dev, isp);
+
+ return 0;
+}
+
+void isp1760_unregister(struct device *dev)
+{
+ struct isp1760_device *isp = dev_get_drvdata(dev);
+
+ isp1760_udc_unregister(isp);
+ isp1760_hcd_unregister(&isp->hcd);
+}
+
+MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP");
+MODULE_AUTHOR("Sebastian Siewior <bigeasy@linuxtronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/isp1760/isp1760-core.h b/drivers/usb/isp1760/isp1760-core.h
new file mode 100644
index 000000000000..c70f8368a794
--- /dev/null
+++ b/drivers/usb/isp1760/isp1760-core.h
@@ -0,0 +1,68 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ * Sebastian Siewior <bigeasy@linutronix.de>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.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 _ISP1760_CORE_H_
+#define _ISP1760_CORE_H_
+
+#include <linux/ioport.h>
+
+#include "isp1760-hcd.h"
+#include "isp1760-udc.h"
+
+struct device;
+struct gpio_desc;
+
+/*
+ * Device flags that can vary from board to board. All of these
+ * indicate the most "atypical" case, so that a devflags of 0 is
+ * a sane default configuration.
+ */
+#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */
+#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */
+#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */
+#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */
+#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */
+#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */
+#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */
+#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */
+
+struct isp1760_device {
+ struct device *dev;
+
+ void __iomem *regs;
+ unsigned int devflags;
+ struct gpio_desc *rst_gpio;
+
+ struct isp1760_hcd hcd;
+ struct isp1760_udc udc;
+};
+
+int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
+ struct device *dev, unsigned int devflags);
+void isp1760_unregister(struct device *dev);
+
+void isp1760_set_pullup(struct isp1760_device *isp, bool enable);
+
+static inline u32 isp1760_read32(void __iomem *base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+static inline void isp1760_write32(void __iomem *base, u32 reg, u32 val)
+{
+ writel(val, base + reg);
+}
+
+#endif
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
index 395649f357aa..eba9b82e2d70 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/isp1760/isp1760-hcd.c
@@ -11,6 +11,7 @@
* (c) 2011 Arvid Brodin <arvid.brodin@enea.com>
*
*/
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -24,73 +25,96 @@
#include <linux/timer.h>
#include <asm/unaligned.h>
#include <asm/cacheflush.h>
-#include <linux/gpio.h>
+#include "isp1760-core.h"
#include "isp1760-hcd.h"
+#include "isp1760-regs.h"
static struct kmem_cache *qtd_cachep;
static struct kmem_cache *qh_cachep;
static struct kmem_cache *urb_listitem_cachep;
-enum queue_head_types {
- QH_CONTROL,
- QH_BULK,
- QH_INTERRUPT,
- QH_END
-};
-
-struct isp1760_hcd {
- u32 hcs_params;
- spinlock_t lock;
- struct slotinfo atl_slots[32];
- int atl_done_map;
- struct slotinfo int_slots[32];
- int int_done_map;
- struct memory_chunk memory_pool[BLOCKS];
- struct list_head qh_list[QH_END];
-
- /* periodic schedule support */
-#define DEFAULT_I_TDPS 1024
- unsigned periodic_size;
- unsigned i_thresh;
- unsigned long reset_done;
- unsigned long next_statechange;
- unsigned int devflags;
-
- int rst_gpio;
-};
+typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd);
static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
{
- return (struct isp1760_hcd *) (hcd->hcd_priv);
+ return *(struct isp1760_hcd **)hcd->hcd_priv;
}
-/* Section 2.2 Host Controller Capability Registers */
-#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
-#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
-#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
-#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
-#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
-#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
-#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
-
-/* Section 2.3 Host Controller Operational Registers */
-#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
-#define CMD_RESET (1<<1) /* reset HC not bus */
-#define CMD_RUN (1<<0) /* start/stop HC */
-#define STS_PCD (1<<2) /* port change detect */
-#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
-
-#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
-#define PORT_POWER (1<<12) /* true: has power (see PPC) */
-#define PORT_USB11(x) (((x) & (3 << 10)) == (1 << 10)) /* USB 1.1 device */
-#define PORT_RESET (1<<8) /* reset port */
-#define PORT_SUSPEND (1<<7) /* suspend port */
-#define PORT_RESUME (1<<6) /* resume it */
-#define PORT_PE (1<<2) /* port enable */
-#define PORT_CSC (1<<1) /* connect status change */
-#define PORT_CONNECT (1<<0) /* device connected */
-#define PORT_RWC_BITS (PORT_CSC)
+/* urb state*/
+#define DELETE_URB (0x0008)
+#define NO_TRANSFER_ACTIVE (0xffffffff)
+
+/* Philips Proprietary Transfer Descriptor (PTD) */
+typedef __u32 __bitwise __dw;
+struct ptd {
+ __dw dw0;
+ __dw dw1;
+ __dw dw2;
+ __dw dw3;
+ __dw dw4;
+ __dw dw5;
+ __dw dw6;
+ __dw dw7;
+};
+#define PTD_OFFSET 0x0400
+#define ISO_PTD_OFFSET 0x0400
+#define INT_PTD_OFFSET 0x0800
+#define ATL_PTD_OFFSET 0x0c00
+#define PAYLOAD_OFFSET 0x1000
+
+
+/* ATL */
+/* DW0 */
+#define DW0_VALID_BIT 1
+#define FROM_DW0_VALID(x) ((x) & 0x01)
+#define TO_DW0_LENGTH(x) (((u32) x) << 3)
+#define TO_DW0_MAXPACKET(x) (((u32) x) << 18)
+#define TO_DW0_MULTI(x) (((u32) x) << 29)
+#define TO_DW0_ENDPOINT(x) (((u32) x) << 31)
+/* DW1 */
+#define TO_DW1_DEVICE_ADDR(x) (((u32) x) << 3)
+#define TO_DW1_PID_TOKEN(x) (((u32) x) << 10)
+#define DW1_TRANS_BULK ((u32) 2 << 12)
+#define DW1_TRANS_INT ((u32) 3 << 12)
+#define DW1_TRANS_SPLIT ((u32) 1 << 14)
+#define DW1_SE_USB_LOSPEED ((u32) 2 << 16)
+#define TO_DW1_PORT_NUM(x) (((u32) x) << 18)
+#define TO_DW1_HUB_NUM(x) (((u32) x) << 25)
+/* DW2 */
+#define TO_DW2_DATA_START_ADDR(x) (((u32) x) << 8)
+#define TO_DW2_RL(x) ((x) << 25)
+#define FROM_DW2_RL(x) (((x) >> 25) & 0xf)
+/* DW3 */
+#define FROM_DW3_NRBYTESTRANSFERRED(x) ((x) & 0x7fff)
+#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) ((x) & 0x07ff)
+#define TO_DW3_NAKCOUNT(x) ((x) << 19)
+#define FROM_DW3_NAKCOUNT(x) (((x) >> 19) & 0xf)
+#define TO_DW3_CERR(x) ((x) << 23)
+#define FROM_DW3_CERR(x) (((x) >> 23) & 0x3)
+#define TO_DW3_DATA_TOGGLE(x) ((x) << 25)
+#define FROM_DW3_DATA_TOGGLE(x) (((x) >> 25) & 0x1)
+#define TO_DW3_PING(x) ((x) << 26)
+#define FROM_DW3_PING(x) (((x) >> 26) & 0x1)
+#define DW3_ERROR_BIT (1 << 28)
+#define DW3_BABBLE_BIT (1 << 29)
+#define DW3_HALT_BIT (1 << 30)
+#define DW3_ACTIVE_BIT (1 << 31)
+#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01)
+
+#define INT_UNDERRUN (1 << 2)
+#define INT_BABBLE (1 << 1)
+#define INT_EXACT (1 << 0)
+
+#define SETUP_PID (2)
+#define IN_PID (1)
+#define OUT_PID (0)
+
+/* Errata 1 */
+#define RL_COUNTER (0)
+#define NAK_COUNTER (0)
+#define ERR_COUNTER (2)
struct isp1760_qtd {
u8 packet_type;
@@ -137,12 +161,12 @@ struct urb_listitem {
*/
static u32 reg_read32(void __iomem *base, u32 reg)
{
- return readl(base + reg);
+ return isp1760_read32(base, reg);
}
static void reg_write32(void __iomem *base, u32 reg, u32 val)
{
- writel(val, base + reg);
+ isp1760_write32(base, reg, val);
}
/*
@@ -443,42 +467,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
int result;
u32 scratch, hwmode;
- /* low-level chip reset */
- if (gpio_is_valid(priv->rst_gpio)) {
- unsigned int rst_lvl;
-
- rst_lvl = (priv->devflags &
- ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0;
-
- gpio_set_value(priv->rst_gpio, rst_lvl);
- mdelay(50);
- gpio_set_value(priv->rst_gpio, !rst_lvl);
- }
-
- /* Setup HW Mode Control: This assumes a level active-low interrupt */
- hwmode = HW_DATA_BUS_32BIT;
-
- if (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16)
- hwmode &= ~HW_DATA_BUS_32BIT;
- if (priv->devflags & ISP1760_FLAG_ANALOG_OC)
- hwmode |= HW_ANA_DIGI_OC;
- if (priv->devflags & ISP1760_FLAG_DACK_POL_HIGH)
- hwmode |= HW_DACK_POL_HIGH;
- if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
- hwmode |= HW_DREQ_POL_HIGH;
- if (priv->devflags & ISP1760_FLAG_INTR_POL_HIGH)
- hwmode |= HW_INTR_HIGH_ACT;
- if (priv->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
- hwmode |= HW_INTR_EDGE_TRIG;
-
- /*
- * We have to set this first in case we're in 16-bit mode.
- * Write it twice to ensure correct upper bits if switching
- * to 16-bit mode.
- */
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
- reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
-
reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe);
/* Change bus pattern */
scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG);
@@ -488,46 +476,33 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
return -ENODEV;
}
- /* pre reset */
+ /*
+ * The RESET_HC bit in the SW_RESET register is supposed to reset the
+ * host controller without touching the CPU interface registers, but at
+ * least on the ISP1761 it seems to behave as the RESET_ALL bit and
+ * reset the whole device. We thus can't use it here, so let's reset
+ * the host controller through the EHCI USB Command register. The device
+ * has been reset in core code anyway, so this shouldn't matter.
+ */
reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0);
reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
- /* reset */
- reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
- mdelay(100);
-
- reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_HC);
- mdelay(100);
-
result = ehci_reset(hcd);
if (result)
return result;
/* Step 11 passed */
- dev_info(hcd->self.controller, "bus width: %d, oc: %s\n",
- (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ?
- 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
- "analog" : "digital");
-
/* ATL reset */
+ hwmode = reg_read32(hcd->regs, HC_HW_MODE_CTRL) & ~ALL_ATX_RESET;
reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
mdelay(10);
reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK);
- /*
- * PORT 1 Control register of the ISP1760 is the OTG control
- * register on ISP1761. Since there is no OTG or device controller
- * support in this driver, we use port 1 as a "normal" USB host port on
- * both chips.
- */
- reg_write32(hcd->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2);
- mdelay(10);
-
priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS);
return priv_init(hcd);
@@ -743,8 +718,9 @@ static void qtd_free(struct isp1760_qtd *qtd)
}
static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
- struct slotinfo *slots, struct isp1760_qtd *qtd,
- struct isp1760_qh *qh, struct ptd *ptd)
+ struct isp1760_slotinfo *slots,
+ struct isp1760_qtd *qtd, struct isp1760_qh *qh,
+ struct ptd *ptd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
int skip_map;
@@ -857,7 +833,7 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
int ptd_offset;
- struct slotinfo *slots;
+ struct isp1760_slotinfo *slots;
int curr_slot, free_slot;
int n;
struct ptd ptd;
@@ -1097,7 +1073,7 @@ static void handle_done_ptds(struct usb_hcd *hcd)
struct isp1760_qh *qh;
int slot;
int state;
- struct slotinfo *slots;
+ struct isp1760_slotinfo *slots;
u32 ptd_offset;
struct isp1760_qtd *qtd;
int modified;
@@ -1359,9 +1335,7 @@ static int isp1760_run(struct usb_hcd *hcd)
if (retval)
return retval;
- init_timer(&errata2_timer);
- errata2_timer.function = errata2_function;
- errata2_timer.data = (unsigned long) hcd;
+ setup_timer(&errata2_timer, errata2_function, (unsigned long)hcd);
errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
add_timer(&errata2_timer);
@@ -1798,13 +1772,13 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
/* per-port overcurrent reporting */
- temp = 0x0008;
+ temp = HUB_CHAR_INDV_PORT_OCPM;
if (HCS_PPC(priv->hcs_params))
/* per-port power control */
- temp |= 0x0001;
+ temp |= HUB_CHAR_INDV_PORT_LPSM;
else
/* no power switching */
- temp |= 0x0002;
+ temp |= HUB_CHAR_NO_LPSM;
desc->wHubCharacteristics = cpu_to_le16(temp);
}
@@ -2163,7 +2137,7 @@ static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
static const struct hc_driver isp1760_hc_driver = {
.description = "isp1760-hcd",
.product_desc = "NXP ISP1760 USB Host Controller",
- .hcd_priv_size = sizeof(struct isp1760_hcd),
+ .hcd_priv_size = sizeof(struct isp1760_hcd *),
.irq = isp1760_irq,
.flags = HCD_MEMORY | HCD_USB2,
.reset = isp1760_hc_setup,
@@ -2179,7 +2153,7 @@ static const struct hc_driver isp1760_hc_driver = {
.clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete,
};
-int __init init_kmem_once(void)
+int __init isp1760_init_kmem_once(void)
{
urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem",
sizeof(struct urb_listitem), 0, SLAB_TEMPORARY |
@@ -2206,63 +2180,56 @@ int __init init_kmem_once(void)
return 0;
}
-void deinit_kmem_cache(void)
+void isp1760_deinit_kmem_cache(void)
{
kmem_cache_destroy(qtd_cachep);
kmem_cache_destroy(qh_cachep);
kmem_cache_destroy(urb_listitem_cachep);
}
-struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
- int irq, unsigned long irqflags,
- int rst_gpio,
- struct device *dev, const char *busname,
- unsigned int devflags)
+int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
+ struct resource *mem, int irq, unsigned long irqflags,
+ struct device *dev)
{
struct usb_hcd *hcd;
- struct isp1760_hcd *priv;
int ret;
- if (usb_disabled())
- return ERR_PTR(-ENODEV);
-
- /* prevent usb-core allocating DMA pages */
- dev->dma_mask = NULL;
-
hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev));
if (!hcd)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
+
+ *(struct isp1760_hcd **)hcd->hcd_priv = priv;
+
+ priv->hcd = hcd;
- priv = hcd_to_priv(hcd);
- priv->devflags = devflags;
- priv->rst_gpio = rst_gpio;
init_memory(priv);
- hcd->regs = ioremap(res_start, res_len);
- if (!hcd->regs) {
- ret = -EIO;
- goto err_put;
- }
hcd->irq = irq;
- hcd->rsrc_start = res_start;
- hcd->rsrc_len = res_len;
+ hcd->regs = regs;
+ hcd->rsrc_start = mem->start;
+ hcd->rsrc_len = resource_size(mem);
+
+ /* This driver doesn't support wakeup requests */
+ hcd->cant_recv_wakeups = 1;
ret = usb_add_hcd(hcd, irq, irqflags);
if (ret)
- goto err_unmap;
+ goto error;
+
device_wakeup_enable(hcd->self.controller);
- return hcd;
+ return 0;
-err_unmap:
- iounmap(hcd->regs);
+error:
+ usb_put_hcd(hcd);
+ return ret;
+}
-err_put:
- usb_put_hcd(hcd);
+void isp1760_hcd_unregister(struct isp1760_hcd *priv)
+{
+ if (!priv->hcd)
+ return;
- return ERR_PTR(ret);
+ usb_remove_hcd(priv->hcd);
+ usb_put_hcd(priv->hcd);
}
-
-MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP");
-MODULE_AUTHOR("Sebastian Siewior <bigeasy@linuxtronix.de>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/isp1760/isp1760-hcd.h b/drivers/usb/isp1760/isp1760-hcd.h
new file mode 100644
index 000000000000..0c1c98d6ea08
--- /dev/null
+++ b/drivers/usb/isp1760/isp1760-hcd.h
@@ -0,0 +1,102 @@
+#ifndef _ISP1760_HCD_H_
+#define _ISP1760_HCD_H_
+
+#include <linux/spinlock.h>
+
+struct isp1760_qh;
+struct isp1760_qtd;
+struct resource;
+struct usb_hcd;
+
+/*
+ * 60kb divided in:
+ * - 32 blocks @ 256 bytes
+ * - 20 blocks @ 1024 bytes
+ * - 4 blocks @ 8192 bytes
+ */
+
+#define BLOCK_1_NUM 32
+#define BLOCK_2_NUM 20
+#define BLOCK_3_NUM 4
+
+#define BLOCK_1_SIZE 256
+#define BLOCK_2_SIZE 1024
+#define BLOCK_3_SIZE 8192
+#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
+#define MAX_PAYLOAD_SIZE BLOCK_3_SIZE
+#define PAYLOAD_AREA_SIZE 0xf000
+
+struct isp1760_slotinfo {
+ struct isp1760_qh *qh;
+ struct isp1760_qtd *qtd;
+ unsigned long timestamp;
+};
+
+/* chip memory management */
+struct isp1760_memory_chunk {
+ unsigned int start;
+ unsigned int size;
+ unsigned int free;
+};
+
+enum isp1760_queue_head_types {
+ QH_CONTROL,
+ QH_BULK,
+ QH_INTERRUPT,
+ QH_END
+};
+
+struct isp1760_hcd {
+#ifdef CONFIG_USB_ISP1760_HCD
+ struct usb_hcd *hcd;
+
+ u32 hcs_params;
+ spinlock_t lock;
+ struct isp1760_slotinfo atl_slots[32];
+ int atl_done_map;
+ struct isp1760_slotinfo int_slots[32];
+ int int_done_map;
+ struct isp1760_memory_chunk memory_pool[BLOCKS];
+ struct list_head qh_list[QH_END];
+
+ /* periodic schedule support */
+#define DEFAULT_I_TDPS 1024
+ unsigned periodic_size;
+ unsigned i_thresh;
+ unsigned long reset_done;
+ unsigned long next_statechange;
+#endif
+};
+
+#ifdef CONFIG_USB_ISP1760_HCD
+int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
+ struct resource *mem, int irq, unsigned long irqflags,
+ struct device *dev);
+void isp1760_hcd_unregister(struct isp1760_hcd *priv);
+
+int isp1760_init_kmem_once(void);
+void isp1760_deinit_kmem_cache(void);
+#else
+static inline int isp1760_hcd_register(struct isp1760_hcd *priv,
+ void __iomem *regs, struct resource *mem,
+ int irq, unsigned long irqflags,
+ struct device *dev)
+{
+ return 0;
+}
+
+static inline void isp1760_hcd_unregister(struct isp1760_hcd *priv)
+{
+}
+
+static inline int isp1760_init_kmem_once(void)
+{
+ return 0;
+}
+
+static inline void isp1760_deinit_kmem_cache(void)
+{
+}
+#endif
+
+#endif /* _ISP1760_HCD_H_ */
diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c
new file mode 100644
index 000000000000..264be4d21706
--- /dev/null
+++ b/drivers/usb/isp1760/isp1760-if.c
@@ -0,0 +1,309 @@
+/*
+ * Glue code for the ISP1760 driver and bus
+ * Currently there is support for
+ * - OpenFirmware
+ * - PCI
+ * - PDEV (generic platform device centralized driver model)
+ *
+ * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb/isp1760.h>
+#include <linux/usb/hcd.h>
+
+#include "isp1760-core.h"
+#include "isp1760-regs.h"
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+
+#ifdef CONFIG_PCI
+static int isp1761_pci_init(struct pci_dev *dev)
+{
+ resource_size_t mem_start;
+ resource_size_t mem_length;
+ u8 __iomem *iobase;
+ u8 latency, limit;
+ int retry_count;
+ u32 reg_data;
+
+ /* Grab the PLX PCI shared memory of the ISP 1761 we need */
+ mem_start = pci_resource_start(dev, 3);
+ mem_length = pci_resource_len(dev, 3);
+ if (mem_length < 0xffff) {
+ printk(KERN_ERR "memory length for this resource is wrong\n");
+ return -ENOMEM;
+ }
+
+ if (!request_mem_region(mem_start, mem_length, "ISP-PCI")) {
+ printk(KERN_ERR "host controller already in use\n");
+ return -EBUSY;
+ }
+
+ /* map available memory */
+ iobase = ioremap_nocache(mem_start, mem_length);
+ if (!iobase) {
+ printk(KERN_ERR "Error ioremap failed\n");
+ release_mem_region(mem_start, mem_length);
+ return -ENOMEM;
+ }
+
+ /* bad pci latencies can contribute to overruns */
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
+ if (latency) {
+ pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
+ if (limit && limit < latency)
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
+ }
+
+ /* Try to check whether we can access Scratch Register of
+ * Host Controller or not. The initial PCI access is retried until
+ * local init for the PCI bridge is completed
+ */
+ retry_count = 20;
+ reg_data = 0;
+ while ((reg_data != 0xFACE) && retry_count) {
+ /*by default host is in 16bit mode, so
+ * io operations at this stage must be 16 bit
+ * */
+ writel(0xface, iobase + HC_SCRATCH_REG);
+ udelay(100);
+ reg_data = readl(iobase + HC_SCRATCH_REG) & 0x0000ffff;
+ retry_count--;
+ }
+
+ iounmap(iobase);
+ release_mem_region(mem_start, mem_length);
+
+ /* Host Controller presence is detected by writing to scratch register
+ * and reading back and checking the contents are same or not
+ */
+ if (reg_data != 0xFACE) {
+ dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
+ return -ENOMEM;
+ }
+
+ /* Grab the PLX PCI mem maped port start address we need */
+ mem_start = pci_resource_start(dev, 0);
+ mem_length = pci_resource_len(dev, 0);
+
+ if (!request_mem_region(mem_start, mem_length, "ISP1761 IO MEM")) {
+ printk(KERN_ERR "request region #1\n");
+ return -EBUSY;
+ }
+
+ iobase = ioremap_nocache(mem_start, mem_length);
+ if (!iobase) {
+ printk(KERN_ERR "ioremap #1\n");
+ release_mem_region(mem_start, mem_length);
+ return -ENOMEM;
+ }
+
+ /* configure PLX PCI chip to pass interrupts */
+#define PLX_INT_CSR_REG 0x68
+ reg_data = readl(iobase + PLX_INT_CSR_REG);
+ reg_data |= 0x900;
+ writel(reg_data, iobase + PLX_INT_CSR_REG);
+
+ /* done with PLX IO access */
+ iounmap(iobase);
+ release_mem_region(mem_start, mem_length);
+
+ return 0;
+}
+
+static int isp1761_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ unsigned int devflags = 0;
+ int ret;
+
+ if (!dev->irq)
+ return -ENODEV;
+
+ if (pci_enable_device(dev) < 0)
+ return -ENODEV;
+
+ ret = isp1761_pci_init(dev);
+ if (ret < 0)
+ goto error;
+
+ pci_set_master(dev);
+
+ dev->dev.dma_mask = NULL;
+ ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev,
+ devflags);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ pci_disable_device(dev);
+ return ret;
+}
+
+static void isp1761_pci_remove(struct pci_dev *dev)
+{
+ isp1760_unregister(&dev->dev);
+
+ pci_disable_device(dev);
+}
+
+static void isp1761_pci_shutdown(struct pci_dev *dev)
+{
+ printk(KERN_ERR "ips1761_pci_shutdown\n");
+}
+
+static const struct pci_device_id isp1760_plx [] = {
+ {
+ .class = PCI_CLASS_BRIDGE_OTHER << 8,
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_PLX,
+ .device = 0x5406,
+ .subvendor = PCI_VENDOR_ID_PLX,
+ .subdevice = 0x9054,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, isp1760_plx);
+
+static struct pci_driver isp1761_pci_driver = {
+ .name = "isp1760",
+ .id_table = isp1760_plx,
+ .probe = isp1761_pci_probe,
+ .remove = isp1761_pci_remove,
+ .shutdown = isp1761_pci_shutdown,
+};
+#endif
+
+static int isp1760_plat_probe(struct platform_device *pdev)
+{
+ unsigned long irqflags;
+ unsigned int devflags = 0;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ int ret;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq_res) {
+ pr_warning("isp1760: IRQ resource not available\n");
+ return -ENODEV;
+ }
+ irqflags = irq_res->flags & IRQF_TRIGGER_MASK;
+
+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ struct device_node *dp = pdev->dev.of_node;
+ u32 bus_width = 0;
+
+ if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
+ devflags |= ISP1760_FLAG_ISP1761;
+
+ /* Some systems wire up only 16 of the 32 data lines */
+ of_property_read_u32(dp, "bus-width", &bus_width);
+ if (bus_width == 16)
+ devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+
+ if (of_property_read_bool(dp, "port1-otg"))
+ devflags |= ISP1760_FLAG_OTG_EN;
+
+ if (of_property_read_bool(dp, "analog-oc"))
+ devflags |= ISP1760_FLAG_ANALOG_OC;
+
+ if (of_property_read_bool(dp, "dack-polarity"))
+ devflags |= ISP1760_FLAG_DACK_POL_HIGH;
+
+ if (of_property_read_bool(dp, "dreq-polarity"))
+ devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+ } else if (dev_get_platdata(&pdev->dev)) {
+ struct isp1760_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
+
+ if (pdata->is_isp1761)
+ devflags |= ISP1760_FLAG_ISP1761;
+ if (pdata->bus_width_16)
+ devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+ if (pdata->port1_otg)
+ devflags |= ISP1760_FLAG_OTG_EN;
+ if (pdata->analog_oc)
+ devflags |= ISP1760_FLAG_ANALOG_OC;
+ if (pdata->dack_polarity_high)
+ devflags |= ISP1760_FLAG_DACK_POL_HIGH;
+ if (pdata->dreq_polarity_high)
+ devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+ }
+
+ ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev,
+ devflags);
+ if (ret < 0)
+ return ret;
+
+ pr_info("ISP1760 USB device initialised\n");
+ return 0;
+}
+
+static int isp1760_plat_remove(struct platform_device *pdev)
+{
+ isp1760_unregister(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id isp1760_of_match[] = {
+ { .compatible = "nxp,usb-isp1760", },
+ { .compatible = "nxp,usb-isp1761", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, isp1760_of_match);
+#endif
+
+static struct platform_driver isp1760_plat_driver = {
+ .probe = isp1760_plat_probe,
+ .remove = isp1760_plat_remove,
+ .driver = {
+ .name = "isp1760",
+ .of_match_table = of_match_ptr(isp1760_of_match),
+ },
+};
+
+static int __init isp1760_init(void)
+{
+ int ret, any_ret = -ENODEV;
+
+ isp1760_init_kmem_once();
+
+ ret = platform_driver_register(&isp1760_plat_driver);
+ if (!ret)
+ any_ret = 0;
+#ifdef CONFIG_PCI
+ ret = pci_register_driver(&isp1761_pci_driver);
+ if (!ret)
+ any_ret = 0;
+#endif
+
+ if (any_ret)
+ isp1760_deinit_kmem_cache();
+ return any_ret;
+}
+module_init(isp1760_init);
+
+static void __exit isp1760_exit(void)
+{
+ platform_driver_unregister(&isp1760_plat_driver);
+#ifdef CONFIG_PCI
+ pci_unregister_driver(&isp1761_pci_driver);
+#endif
+ isp1760_deinit_kmem_cache();
+}
+module_exit(isp1760_exit);
diff --git a/drivers/usb/isp1760/isp1760-regs.h b/drivers/usb/isp1760/isp1760-regs.h
new file mode 100644
index 000000000000..b67095c9a9d4
--- /dev/null
+++ b/drivers/usb/isp1760/isp1760-regs.h
@@ -0,0 +1,230 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ * Sebastian Siewior <bigeasy@linutronix.de>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.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 _ISP1760_REGS_H_
+#define _ISP1760_REGS_H_
+
+/* -----------------------------------------------------------------------------
+ * Host Controller
+ */
+
+/* EHCI capability registers */
+#define HC_CAPLENGTH 0x000
+#define HC_LENGTH(p) (((p) >> 00) & 0x00ff) /* bits 7:0 */
+#define HC_VERSION(p) (((p) >> 16) & 0xffff) /* bits 31:16 */
+
+#define HC_HCSPARAMS 0x004
+#define HCS_INDICATOR(p) ((p) & (1 << 16)) /* true: has port indicators */
+#define HCS_PPC(p) ((p) & (1 << 4)) /* true: port power control */
+#define HCS_N_PORTS(p) (((p) >> 0) & 0xf) /* bits 3:0, ports on HC */
+
+#define HC_HCCPARAMS 0x008
+#define HCC_ISOC_CACHE(p) ((p) & (1 << 7)) /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p) (((p) >> 4) & 0x7) /* bits 6:4, uframes cached */
+
+/* EHCI operational registers */
+#define HC_USBCMD 0x020
+#define CMD_LRESET (1 << 7) /* partial reset (no ports, etc) */
+#define CMD_RESET (1 << 1) /* reset HC not bus */
+#define CMD_RUN (1 << 0) /* start/stop HC */
+
+#define HC_USBSTS 0x024
+#define STS_PCD (1 << 2) /* port change detect */
+
+#define HC_FRINDEX 0x02c
+
+#define HC_CONFIGFLAG 0x060
+#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
+
+#define HC_PORTSC1 0x064
+#define PORT_OWNER (1 << 13) /* true: companion hc owns this port */
+#define PORT_POWER (1 << 12) /* true: has power (see PPC) */
+#define PORT_USB11(x) (((x) & (3 << 10)) == (1 << 10)) /* USB 1.1 device */
+#define PORT_RESET (1 << 8) /* reset port */
+#define PORT_SUSPEND (1 << 7) /* suspend port */
+#define PORT_RESUME (1 << 6) /* resume it */
+#define PORT_PE (1 << 2) /* port enable */
+#define PORT_CSC (1 << 1) /* connect status change */
+#define PORT_CONNECT (1 << 0) /* device connected */
+#define PORT_RWC_BITS (PORT_CSC)
+
+#define HC_ISO_PTD_DONEMAP_REG 0x130
+#define HC_ISO_PTD_SKIPMAP_REG 0x134
+#define HC_ISO_PTD_LASTPTD_REG 0x138
+#define HC_INT_PTD_DONEMAP_REG 0x140
+#define HC_INT_PTD_SKIPMAP_REG 0x144
+#define HC_INT_PTD_LASTPTD_REG 0x148
+#define HC_ATL_PTD_DONEMAP_REG 0x150
+#define HC_ATL_PTD_SKIPMAP_REG 0x154
+#define HC_ATL_PTD_LASTPTD_REG 0x158
+
+/* Configuration Register */
+#define HC_HW_MODE_CTRL 0x300
+#define ALL_ATX_RESET (1 << 31)
+#define HW_ANA_DIGI_OC (1 << 15)
+#define HW_DEV_DMA (1 << 11)
+#define HW_COMN_IRQ (1 << 10)
+#define HW_COMN_DMA (1 << 9)
+#define HW_DATA_BUS_32BIT (1 << 8)
+#define HW_DACK_POL_HIGH (1 << 6)
+#define HW_DREQ_POL_HIGH (1 << 5)
+#define HW_INTR_HIGH_ACT (1 << 2)
+#define HW_INTR_EDGE_TRIG (1 << 1)
+#define HW_GLOBAL_INTR_EN (1 << 0)
+
+#define HC_CHIP_ID_REG 0x304
+#define HC_SCRATCH_REG 0x308
+
+#define HC_RESET_REG 0x30c
+#define SW_RESET_RESET_HC (1 << 1)
+#define SW_RESET_RESET_ALL (1 << 0)
+
+#define HC_BUFFER_STATUS_REG 0x334
+#define ISO_BUF_FILL (1 << 2)
+#define INT_BUF_FILL (1 << 1)
+#define ATL_BUF_FILL (1 << 0)
+
+#define HC_MEMORY_REG 0x33c
+#define ISP_BANK(x) ((x) << 16)
+
+#define HC_PORT1_CTRL 0x374
+#define PORT1_POWER (3 << 3)
+#define PORT1_INIT1 (1 << 7)
+#define PORT1_INIT2 (1 << 23)
+#define HW_OTG_CTRL_SET 0x374
+#define HW_OTG_CTRL_CLR 0x376
+#define HW_OTG_DISABLE (1 << 10)
+#define HW_OTG_SE0_EN (1 << 9)
+#define HW_BDIS_ACON_EN (1 << 8)
+#define HW_SW_SEL_HC_DC (1 << 7)
+#define HW_VBUS_CHRG (1 << 6)
+#define HW_VBUS_DISCHRG (1 << 5)
+#define HW_VBUS_DRV (1 << 4)
+#define HW_SEL_CP_EXT (1 << 3)
+#define HW_DM_PULLDOWN (1 << 2)
+#define HW_DP_PULLDOWN (1 << 1)
+#define HW_DP_PULLUP (1 << 0)
+
+/* Interrupt Register */
+#define HC_INTERRUPT_REG 0x310
+
+#define HC_INTERRUPT_ENABLE 0x314
+#define HC_ISO_INT (1 << 9)
+#define HC_ATL_INT (1 << 8)
+#define HC_INTL_INT (1 << 7)
+#define HC_EOT_INT (1 << 3)
+#define HC_SOT_INT (1 << 1)
+#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT)
+
+#define HC_ISO_IRQ_MASK_OR_REG 0x318
+#define HC_INT_IRQ_MASK_OR_REG 0x31c
+#define HC_ATL_IRQ_MASK_OR_REG 0x320
+#define HC_ISO_IRQ_MASK_AND_REG 0x324
+#define HC_INT_IRQ_MASK_AND_REG 0x328
+#define HC_ATL_IRQ_MASK_AND_REG 0x32c
+
+/* -----------------------------------------------------------------------------
+ * Peripheral Controller
+ */
+
+/* Initialization Registers */
+#define DC_ADDRESS 0x0200
+#define DC_DEVEN (1 << 7)
+
+#define DC_MODE 0x020c
+#define DC_DMACLKON (1 << 9)
+#define DC_VBUSSTAT (1 << 8)
+#define DC_CLKAON (1 << 7)
+#define DC_SNDRSU (1 << 6)
+#define DC_GOSUSP (1 << 5)
+#define DC_SFRESET (1 << 4)
+#define DC_GLINTENA (1 << 3)
+#define DC_WKUPCS (1 << 2)
+
+#define DC_INTCONF 0x0210
+#define DC_CDBGMOD_ACK_NAK (0 << 6)
+#define DC_CDBGMOD_ACK (1 << 6)
+#define DC_CDBGMOD_ACK_1NAK (2 << 6)
+#define DC_DDBGMODIN_ACK_NAK (0 << 4)
+#define DC_DDBGMODIN_ACK (1 << 4)
+#define DC_DDBGMODIN_ACK_1NAK (2 << 4)
+#define DC_DDBGMODOUT_ACK_NYET_NAK (0 << 2)
+#define DC_DDBGMODOUT_ACK_NYET (1 << 2)
+#define DC_DDBGMODOUT_ACK_NYET_1NAK (2 << 2)
+#define DC_INTLVL (1 << 1)
+#define DC_INTPOL (1 << 0)
+
+#define DC_DEBUG 0x0212
+#define DC_INTENABLE 0x0214
+#define DC_IEPTX(n) (1 << (11 + 2 * (n)))
+#define DC_IEPRX(n) (1 << (10 + 2 * (n)))
+#define DC_IEPRXTX(n) (3 << (10 + 2 * (n)))
+#define DC_IEP0SETUP (1 << 8)
+#define DC_IEVBUS (1 << 7)
+#define DC_IEDMA (1 << 6)
+#define DC_IEHS_STA (1 << 5)
+#define DC_IERESM (1 << 4)
+#define DC_IESUSP (1 << 3)
+#define DC_IEPSOF (1 << 2)
+#define DC_IESOF (1 << 1)
+#define DC_IEBRST (1 << 0)
+
+/* Data Flow Registers */
+#define DC_EPINDEX 0x022c
+#define DC_EP0SETUP (1 << 5)
+#define DC_ENDPIDX(n) ((n) << 1)
+#define DC_EPDIR (1 << 0)
+
+#define DC_CTRLFUNC 0x0228
+#define DC_CLBUF (1 << 4)
+#define DC_VENDP (1 << 3)
+#define DC_DSEN (1 << 2)
+#define DC_STATUS (1 << 1)
+#define DC_STALL (1 << 0)
+
+#define DC_DATAPORT 0x0220
+#define DC_BUFLEN 0x021c
+#define DC_DATACOUNT_MASK 0xffff
+#define DC_BUFSTAT 0x021e
+#define DC_EPMAXPKTSZ 0x0204
+
+#define DC_EPTYPE 0x0208
+#define DC_NOEMPKT (1 << 4)
+#define DC_EPENABLE (1 << 3)
+#define DC_DBLBUF (1 << 2)
+#define DC_ENDPTYP_ISOC (1 << 0)
+#define DC_ENDPTYP_BULK (2 << 0)
+#define DC_ENDPTYP_INTERRUPT (3 << 0)
+
+/* DMA Registers */
+#define DC_DMACMD 0x0230
+#define DC_DMATXCOUNT 0x0234
+#define DC_DMACONF 0x0238
+#define DC_DMAHW 0x023c
+#define DC_DMAINTREASON 0x0250
+#define DC_DMAINTEN 0x0254
+#define DC_DMAEP 0x0258
+#define DC_DMABURSTCOUNT 0x0264
+
+/* General Registers */
+#define DC_INTERRUPT 0x0218
+#define DC_CHIPID 0x0270
+#define DC_FRAMENUM 0x0274
+#define DC_SCRATCH 0x0278
+#define DC_UNLOCKDEV 0x027c
+#define DC_INTPULSEWIDTH 0x0280
+#define DC_TESTMODE 0x0284
+
+#endif
diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c
new file mode 100644
index 000000000000..9612d7990565
--- /dev/null
+++ b/drivers/usb/isp1760/isp1760-udc.c
@@ -0,0 +1,1498 @@
+/*
+ * Driver for the NXP ISP1761 device controller
+ *
+ * Copyright 2014 Ideas on Board Oy
+ *
+ * Contacts:
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+
+#include "isp1760-core.h"
+#include "isp1760-regs.h"
+#include "isp1760-udc.h"
+
+#define ISP1760_VBUS_POLL_INTERVAL msecs_to_jiffies(500)
+
+struct isp1760_request {
+ struct usb_request req;
+ struct list_head queue;
+ struct isp1760_ep *ep;
+ unsigned int packet_size;
+};
+
+static inline struct isp1760_udc *gadget_to_udc(struct usb_gadget *gadget)
+{
+ return container_of(gadget, struct isp1760_udc, gadget);
+}
+
+static inline struct isp1760_ep *ep_to_udc_ep(struct usb_ep *ep)
+{
+ return container_of(ep, struct isp1760_ep, ep);
+}
+
+static inline struct isp1760_request *req_to_udc_req(struct usb_request *req)
+{
+ return container_of(req, struct isp1760_request, req);
+}
+
+static inline u32 isp1760_udc_read(struct isp1760_udc *udc, u16 reg)
+{
+ return isp1760_read32(udc->regs, reg);
+}
+
+static inline void isp1760_udc_write(struct isp1760_udc *udc, u16 reg, u32 val)
+{
+ isp1760_write32(udc->regs, reg, val);
+}
+
+/* -----------------------------------------------------------------------------
+ * Endpoint Management
+ */
+
+static struct isp1760_ep *isp1760_udc_find_ep(struct isp1760_udc *udc,
+ u16 index)
+{
+ unsigned int i;
+
+ if (index == 0)
+ return &udc->ep[0];
+
+ for (i = 1; i < ARRAY_SIZE(udc->ep); ++i) {
+ if (udc->ep[i].addr == index)
+ return udc->ep[i].desc ? &udc->ep[i] : NULL;
+ }
+
+ return NULL;
+}
+
+static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir)
+{
+ isp1760_udc_write(ep->udc, DC_EPINDEX,
+ DC_ENDPIDX(ep->addr & USB_ENDPOINT_NUMBER_MASK) |
+ (dir == USB_DIR_IN ? DC_EPDIR : 0));
+}
+
+/**
+ * isp1760_udc_select_ep - Select an endpoint for register access
+ * @ep: The endpoint
+ *
+ * The ISP1761 endpoint registers are banked. This function selects the target
+ * endpoint for banked register access. The selection remains valid until the
+ * next call to this function, the next direct access to the EPINDEX register
+ * or the next reset, whichever comes first.
+ *
+ * Called with the UDC spinlock held.
+ */
+static void isp1760_udc_select_ep(struct isp1760_ep *ep)
+{
+ __isp1760_udc_select_ep(ep, ep->addr & USB_ENDPOINT_DIR_MASK);
+}
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_ctrl_send_status(struct isp1760_ep *ep, int dir)
+{
+ struct isp1760_udc *udc = ep->udc;
+
+ /*
+ * Proceed to the status stage. The status stage data packet flows in
+ * the direction opposite to the data stage data packets, we thus need
+ * to select the OUT/IN endpoint for IN/OUT transfers.
+ */
+ isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) |
+ (dir == USB_DIR_IN ? 0 : DC_EPDIR));
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_STATUS);
+
+ /*
+ * The hardware will terminate the request automatically and go back to
+ * the setup stage without notifying us.
+ */
+ udc->ep0_state = ISP1760_CTRL_SETUP;
+}
+
+/* Called without the UDC spinlock held. */
+static void isp1760_udc_request_complete(struct isp1760_ep *ep,
+ struct isp1760_request *req,
+ int status)
+{
+ struct isp1760_udc *udc = ep->udc;
+ unsigned long flags;
+
+ dev_dbg(ep->udc->isp->dev, "completing request %p with status %d\n",
+ req, status);
+
+ req->ep = NULL;
+ req->req.status = status;
+ req->req.complete(&ep->ep, &req->req);
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /*
+ * When completing control OUT requests, move to the status stage after
+ * calling the request complete callback. This gives the gadget an
+ * opportunity to stall the control transfer if needed.
+ */
+ if (status == 0 && ep->addr == 0 && udc->ep0_dir == USB_DIR_OUT)
+ isp1760_udc_ctrl_send_status(ep, USB_DIR_OUT);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static void isp1760_udc_ctrl_send_stall(struct isp1760_ep *ep)
+{
+ struct isp1760_udc *udc = ep->udc;
+ unsigned long flags;
+
+ dev_dbg(ep->udc->isp->dev, "%s(ep%02x)\n", __func__, ep->addr);
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* Stall both the IN and OUT endpoints. */
+ __isp1760_udc_select_ep(ep, USB_DIR_OUT);
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL);
+ __isp1760_udc_select_ep(ep, USB_DIR_IN);
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL);
+
+ /* A protocol stall completes the control transaction. */
+ udc->ep0_state = ISP1760_CTRL_SETUP;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Data Endpoints
+ */
+
+/* Called with the UDC spinlock held. */
+static bool isp1760_udc_receive(struct isp1760_ep *ep,
+ struct isp1760_request *req)
+{
+ struct isp1760_udc *udc = ep->udc;
+ unsigned int len;
+ u32 *buf;
+ int i;
+
+ isp1760_udc_select_ep(ep);
+ len = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK;
+
+ dev_dbg(udc->isp->dev, "%s: received %u bytes (%u/%u done)\n",
+ __func__, len, req->req.actual, req->req.length);
+
+ len = min(len, req->req.length - req->req.actual);
+
+ if (!len) {
+ /*
+ * There's no data to be read from the FIFO, acknowledge the RX
+ * interrupt by clearing the buffer.
+ *
+ * TODO: What if another packet arrives in the meantime ? The
+ * datasheet doesn't clearly document how this should be
+ * handled.
+ */
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+ return false;
+ }
+
+ buf = req->req.buf + req->req.actual;
+
+ /*
+ * Make sure not to read more than one extra byte, otherwise data from
+ * the next packet might be removed from the FIFO.
+ */
+ for (i = len; i > 2; i -= 4, ++buf)
+ *buf = le32_to_cpu(isp1760_udc_read(udc, DC_DATAPORT));
+ if (i > 0)
+ *(u16 *)buf = le16_to_cpu(readw(udc->regs + DC_DATAPORT));
+
+ req->req.actual += len;
+
+ /*
+ * TODO: The short_not_ok flag isn't supported yet, but isn't used by
+ * any gadget driver either.
+ */
+
+ dev_dbg(udc->isp->dev,
+ "%s: req %p actual/length %u/%u maxpacket %u packet size %u\n",
+ __func__, req, req->req.actual, req->req.length, ep->maxpacket,
+ len);
+
+ ep->rx_pending = false;
+
+ /*
+ * Complete the request if all data has been received or if a short
+ * packet has been received.
+ */
+ if (req->req.actual == req->req.length || len < ep->maxpacket) {
+ list_del(&req->queue);
+ return true;
+ }
+
+ return false;
+}
+
+static void isp1760_udc_transmit(struct isp1760_ep *ep,
+ struct isp1760_request *req)
+{
+ struct isp1760_udc *udc = ep->udc;
+ u32 *buf = req->req.buf + req->req.actual;
+ int i;
+
+ req->packet_size = min(req->req.length - req->req.actual,
+ ep->maxpacket);
+
+ dev_dbg(udc->isp->dev, "%s: transferring %u bytes (%u/%u done)\n",
+ __func__, req->packet_size, req->req.actual,
+ req->req.length);
+
+ __isp1760_udc_select_ep(ep, USB_DIR_IN);
+
+ if (req->packet_size)
+ isp1760_udc_write(udc, DC_BUFLEN, req->packet_size);
+
+ /*
+ * Make sure not to write more than one extra byte, otherwise extra data
+ * will stay in the FIFO and will be transmitted during the next control
+ * request. The endpoint control CLBUF bit is supposed to allow flushing
+ * the FIFO for this kind of conditions, but doesn't seem to work.
+ */
+ for (i = req->packet_size; i > 2; i -= 4, ++buf)
+ isp1760_udc_write(udc, DC_DATAPORT, cpu_to_le32(*buf));
+ if (i > 0)
+ writew(cpu_to_le16(*(u16 *)buf), udc->regs + DC_DATAPORT);
+
+ if (ep->addr == 0)
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+ if (!req->packet_size)
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_VENDP);
+}
+
+static void isp1760_ep_rx_ready(struct isp1760_ep *ep)
+{
+ struct isp1760_udc *udc = ep->udc;
+ struct isp1760_request *req;
+ bool complete;
+
+ spin_lock(&udc->lock);
+
+ if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_OUT) {
+ spin_unlock(&udc->lock);
+ dev_dbg(udc->isp->dev, "%s: invalid ep0 state %u\n", __func__,
+ udc->ep0_state);
+ return;
+ }
+
+ if (ep->addr != 0 && !ep->desc) {
+ spin_unlock(&udc->lock);
+ dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__,
+ ep->addr);
+ return;
+ }
+
+ if (list_empty(&ep->queue)) {
+ ep->rx_pending = true;
+ spin_unlock(&udc->lock);
+ dev_dbg(udc->isp->dev, "%s: ep%02x (%p) has no request queued\n",
+ __func__, ep->addr, ep);
+ return;
+ }
+
+ req = list_first_entry(&ep->queue, struct isp1760_request,
+ queue);
+ complete = isp1760_udc_receive(ep, req);
+
+ spin_unlock(&udc->lock);
+
+ if (complete)
+ isp1760_udc_request_complete(ep, req, 0);
+}
+
+static void isp1760_ep_tx_complete(struct isp1760_ep *ep)
+{
+ struct isp1760_udc *udc = ep->udc;
+ struct isp1760_request *complete = NULL;
+ struct isp1760_request *req;
+ bool need_zlp;
+
+ spin_lock(&udc->lock);
+
+ if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_IN) {
+ spin_unlock(&udc->lock);
+ dev_dbg(udc->isp->dev, "TX IRQ: invalid endpoint state %u\n",
+ udc->ep0_state);
+ return;
+ }
+
+ if (list_empty(&ep->queue)) {
+ /*
+ * This can happen for the control endpoint when the reply to
+ * the GET_STATUS IN control request is sent directly by the
+ * setup IRQ handler. Just proceed to the status stage.
+ */
+ if (ep->addr == 0) {
+ isp1760_udc_ctrl_send_status(ep, USB_DIR_IN);
+ spin_unlock(&udc->lock);
+ return;
+ }
+
+ spin_unlock(&udc->lock);
+ dev_dbg(udc->isp->dev, "%s: ep%02x has no request queued\n",
+ __func__, ep->addr);
+ return;
+ }
+
+ req = list_first_entry(&ep->queue, struct isp1760_request,
+ queue);
+ req->req.actual += req->packet_size;
+
+ need_zlp = req->req.actual == req->req.length &&
+ !(req->req.length % ep->maxpacket) &&
+ req->packet_size && req->req.zero;
+
+ dev_dbg(udc->isp->dev,
+ "TX IRQ: req %p actual/length %u/%u maxpacket %u packet size %u zero %u need zlp %u\n",
+ req, req->req.actual, req->req.length, ep->maxpacket,
+ req->packet_size, req->req.zero, need_zlp);
+
+ /*
+ * Complete the request if all data has been sent and we don't need to
+ * transmit a zero length packet.
+ */
+ if (req->req.actual == req->req.length && !need_zlp) {
+ complete = req;
+ list_del(&req->queue);
+
+ if (ep->addr == 0)
+ isp1760_udc_ctrl_send_status(ep, USB_DIR_IN);
+
+ if (!list_empty(&ep->queue))
+ req = list_first_entry(&ep->queue,
+ struct isp1760_request, queue);
+ else
+ req = NULL;
+ }
+
+ /*
+ * Transmit the next packet or start the next request, if any.
+ *
+ * TODO: If the endpoint is stalled the next request shouldn't be
+ * started, but what about the next packet ?
+ */
+ if (req)
+ isp1760_udc_transmit(ep, req);
+
+ spin_unlock(&udc->lock);
+
+ if (complete)
+ isp1760_udc_request_complete(ep, complete, 0);
+}
+
+static int __isp1760_udc_set_halt(struct isp1760_ep *ep, bool halt)
+{
+ struct isp1760_udc *udc = ep->udc;
+
+ dev_dbg(udc->isp->dev, "%s: %s halt on ep%02x\n", __func__,
+ halt ? "set" : "clear", ep->addr);
+
+ if (ep->desc && usb_endpoint_xfer_isoc(ep->desc)) {
+ dev_dbg(udc->isp->dev, "%s: ep%02x is isochronous\n", __func__,
+ ep->addr);
+ return -EINVAL;
+ }
+
+ isp1760_udc_select_ep(ep);
+ isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0);
+
+ if (ep->addr == 0) {
+ /* When halting the control endpoint, stall both IN and OUT. */
+ __isp1760_udc_select_ep(ep, USB_DIR_IN);
+ isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0);
+ } else if (!halt) {
+ /* Reset the data PID by cycling the endpoint enable bit. */
+ u16 eptype = isp1760_udc_read(udc, DC_EPTYPE);
+
+ isp1760_udc_write(udc, DC_EPTYPE, eptype & ~DC_EPENABLE);
+ isp1760_udc_write(udc, DC_EPTYPE, eptype);
+
+ /*
+ * Disabling the endpoint emptied the transmit FIFO, fill it
+ * again if a request is pending.
+ *
+ * TODO: Does the gadget framework require synchronizatino with
+ * the TX IRQ handler ?
+ */
+ if ((ep->addr & USB_DIR_IN) && !list_empty(&ep->queue)) {
+ struct isp1760_request *req;
+
+ req = list_first_entry(&ep->queue,
+ struct isp1760_request, queue);
+ isp1760_udc_transmit(ep, req);
+ }
+ }
+
+ ep->halted = halt;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Control Endpoint
+ */
+
+static int isp1760_udc_get_status(struct isp1760_udc *udc,
+ const struct usb_ctrlrequest *req)
+{
+ struct isp1760_ep *ep;
+ u16 status;
+
+ if (req->wLength != cpu_to_le16(2) || req->wValue != cpu_to_le16(0))
+ return -EINVAL;
+
+ switch (req->bRequestType) {
+ case USB_DIR_IN | USB_RECIP_DEVICE:
+ status = udc->devstatus;
+ break;
+
+ case USB_DIR_IN | USB_RECIP_INTERFACE:
+ status = 0;
+ break;
+
+ case USB_DIR_IN | USB_RECIP_ENDPOINT:
+ ep = isp1760_udc_find_ep(udc, le16_to_cpu(req->wIndex));
+ if (!ep)
+ return -EINVAL;
+
+ status = 0;
+ if (ep->halted)
+ status |= 1 << USB_ENDPOINT_HALT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | DC_EPDIR);
+ isp1760_udc_write(udc, DC_BUFLEN, 2);
+
+ writew(cpu_to_le16(status), udc->regs + DC_DATAPORT);
+
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+
+ dev_dbg(udc->isp->dev, "%s: status 0x%04x\n", __func__, status);
+
+ return 0;
+}
+
+static int isp1760_udc_set_address(struct isp1760_udc *udc, u16 addr)
+{
+ if (addr > 127) {
+ dev_dbg(udc->isp->dev, "invalid device address %u\n", addr);
+ return -EINVAL;
+ }
+
+ if (udc->gadget.state != USB_STATE_DEFAULT &&
+ udc->gadget.state != USB_STATE_ADDRESS) {
+ dev_dbg(udc->isp->dev, "can't set address in state %u\n",
+ udc->gadget.state);
+ return -EINVAL;
+ }
+
+ usb_gadget_set_state(&udc->gadget, addr ? USB_STATE_ADDRESS :
+ USB_STATE_DEFAULT);
+
+ isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN | addr);
+
+ spin_lock(&udc->lock);
+ isp1760_udc_ctrl_send_status(&udc->ep[0], USB_DIR_OUT);
+ spin_unlock(&udc->lock);
+
+ return 0;
+}
+
+static bool isp1760_ep0_setup_standard(struct isp1760_udc *udc,
+ struct usb_ctrlrequest *req)
+{
+ bool stall;
+
+ switch (req->bRequest) {
+ case USB_REQ_GET_STATUS:
+ return isp1760_udc_get_status(udc, req);
+
+ case USB_REQ_CLEAR_FEATURE:
+ switch (req->bRequestType) {
+ case USB_DIR_OUT | USB_RECIP_DEVICE: {
+ /* TODO: Handle remote wakeup feature. */
+ return true;
+ }
+
+ case USB_DIR_OUT | USB_RECIP_ENDPOINT: {
+ u16 index = le16_to_cpu(req->wIndex);
+ struct isp1760_ep *ep;
+
+ if (req->wLength != cpu_to_le16(0) ||
+ req->wValue != cpu_to_le16(USB_ENDPOINT_HALT))
+ return true;
+
+ ep = isp1760_udc_find_ep(udc, index);
+ if (!ep)
+ return true;
+
+ spin_lock(&udc->lock);
+
+ /*
+ * If the endpoint is wedged only the gadget can clear
+ * the halt feature. Pretend success in that case, but
+ * keep the endpoint halted.
+ */
+ if (!ep->wedged)
+ stall = __isp1760_udc_set_halt(ep, false);
+ else
+ stall = false;
+
+ if (!stall)
+ isp1760_udc_ctrl_send_status(&udc->ep[0],
+ USB_DIR_OUT);
+
+ spin_unlock(&udc->lock);
+ return stall;
+ }
+
+ default:
+ return true;
+ }
+ break;
+
+ case USB_REQ_SET_FEATURE:
+ switch (req->bRequestType) {
+ case USB_DIR_OUT | USB_RECIP_DEVICE: {
+ /* TODO: Handle remote wakeup and test mode features */
+ return true;
+ }
+
+ case USB_DIR_OUT | USB_RECIP_ENDPOINT: {
+ u16 index = le16_to_cpu(req->wIndex);
+ struct isp1760_ep *ep;
+
+ if (req->wLength != cpu_to_le16(0) ||
+ req->wValue != cpu_to_le16(USB_ENDPOINT_HALT))
+ return true;
+
+ ep = isp1760_udc_find_ep(udc, index);
+ if (!ep)
+ return true;
+
+ spin_lock(&udc->lock);
+
+ stall = __isp1760_udc_set_halt(ep, true);
+ if (!stall)
+ isp1760_udc_ctrl_send_status(&udc->ep[0],
+ USB_DIR_OUT);
+
+ spin_unlock(&udc->lock);
+ return stall;
+ }
+
+ default:
+ return true;
+ }
+ break;
+
+ case USB_REQ_SET_ADDRESS:
+ if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+ return true;
+
+ return isp1760_udc_set_address(udc, le16_to_cpu(req->wValue));
+
+ case USB_REQ_SET_CONFIGURATION:
+ if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+ return true;
+
+ if (udc->gadget.state != USB_STATE_ADDRESS &&
+ udc->gadget.state != USB_STATE_CONFIGURED)
+ return true;
+
+ stall = udc->driver->setup(&udc->gadget, req) < 0;
+ if (stall)
+ return true;
+
+ usb_gadget_set_state(&udc->gadget, req->wValue ?
+ USB_STATE_CONFIGURED : USB_STATE_ADDRESS);
+
+ /*
+ * SET_CONFIGURATION (and SET_INTERFACE) must reset the halt
+ * feature on all endpoints. There is however no need to do so
+ * explicitly here as the gadget driver will disable and
+ * reenable endpoints, clearing the halt feature.
+ */
+ return false;
+
+ default:
+ return udc->driver->setup(&udc->gadget, req) < 0;
+ }
+}
+
+static void isp1760_ep0_setup(struct isp1760_udc *udc)
+{
+ union {
+ struct usb_ctrlrequest r;
+ u32 data[2];
+ } req;
+ unsigned int count;
+ bool stall = false;
+
+ spin_lock(&udc->lock);
+
+ isp1760_udc_write(udc, DC_EPINDEX, DC_EP0SETUP);
+
+ count = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK;
+ if (count != sizeof(req)) {
+ spin_unlock(&udc->lock);
+
+ dev_err(udc->isp->dev, "invalid length %u for setup packet\n",
+ count);
+
+ isp1760_udc_ctrl_send_stall(&udc->ep[0]);
+ return;
+ }
+
+ req.data[0] = isp1760_udc_read(udc, DC_DATAPORT);
+ req.data[1] = isp1760_udc_read(udc, DC_DATAPORT);
+
+ if (udc->ep0_state != ISP1760_CTRL_SETUP) {
+ spin_unlock(&udc->lock);
+ dev_dbg(udc->isp->dev, "unexpected SETUP packet\n");
+ return;
+ }
+
+ /* Move to the data stage. */
+ if (!req.r.wLength)
+ udc->ep0_state = ISP1760_CTRL_STATUS;
+ else if (req.r.bRequestType & USB_DIR_IN)
+ udc->ep0_state = ISP1760_CTRL_DATA_IN;
+ else
+ udc->ep0_state = ISP1760_CTRL_DATA_OUT;
+
+ udc->ep0_dir = req.r.bRequestType & USB_DIR_IN;
+ udc->ep0_length = le16_to_cpu(req.r.wLength);
+
+ spin_unlock(&udc->lock);
+
+ dev_dbg(udc->isp->dev,
+ "%s: bRequestType 0x%02x bRequest 0x%02x wValue 0x%04x wIndex 0x%04x wLength 0x%04x\n",
+ __func__, req.r.bRequestType, req.r.bRequest,
+ le16_to_cpu(req.r.wValue), le16_to_cpu(req.r.wIndex),
+ le16_to_cpu(req.r.wLength));
+
+ if ((req.r.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+ stall = isp1760_ep0_setup_standard(udc, &req.r);
+ else
+ stall = udc->driver->setup(&udc->gadget, &req.r) < 0;
+
+ if (stall)
+ isp1760_udc_ctrl_send_stall(&udc->ep[0]);
+}
+
+/* -----------------------------------------------------------------------------
+ * Gadget Endpoint Operations
+ */
+
+static int isp1760_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct isp1760_ep *uep = ep_to_udc_ep(ep);
+ struct isp1760_udc *udc = uep->udc;
+ unsigned long flags;
+ unsigned int type;
+
+ dev_dbg(uep->udc->isp->dev, "%s\n", __func__);
+
+ /*
+ * Validate the descriptor. The control endpoint can't be enabled
+ * manually.
+ */
+ if (desc->bDescriptorType != USB_DT_ENDPOINT ||
+ desc->bEndpointAddress == 0 ||
+ desc->bEndpointAddress != uep->addr ||
+ le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket) {
+ dev_dbg(udc->isp->dev,
+ "%s: invalid descriptor type %u addr %02x ep addr %02x max packet size %u/%u\n",
+ __func__, desc->bDescriptorType,
+ desc->bEndpointAddress, uep->addr,
+ le16_to_cpu(desc->wMaxPacketSize), ep->maxpacket);
+ return -EINVAL;
+ }
+
+ switch (usb_endpoint_type(desc)) {
+ case USB_ENDPOINT_XFER_ISOC:
+ type = DC_ENDPTYP_ISOC;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ type = DC_ENDPTYP_BULK;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ type = DC_ENDPTYP_INTERRUPT;
+ break;
+ case USB_ENDPOINT_XFER_CONTROL:
+ default:
+ dev_dbg(udc->isp->dev, "%s: control endpoints unsupported\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ uep->desc = desc;
+ uep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+ uep->rx_pending = false;
+ uep->halted = false;
+ uep->wedged = false;
+
+ isp1760_udc_select_ep(uep);
+ isp1760_udc_write(udc, DC_EPMAXPKTSZ, uep->maxpacket);
+ isp1760_udc_write(udc, DC_BUFLEN, uep->maxpacket);
+ isp1760_udc_write(udc, DC_EPTYPE, DC_EPENABLE | type);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static int isp1760_ep_disable(struct usb_ep *ep)
+{
+ struct isp1760_ep *uep = ep_to_udc_ep(ep);
+ struct isp1760_udc *udc = uep->udc;
+ struct isp1760_request *req, *nreq;
+ LIST_HEAD(req_list);
+ unsigned long flags;
+
+ dev_dbg(udc->isp->dev, "%s\n", __func__);
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (!uep->desc) {
+ dev_dbg(udc->isp->dev, "%s: endpoint not enabled\n", __func__);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -EINVAL;
+ }
+
+ uep->desc = NULL;
+ uep->maxpacket = 0;
+
+ isp1760_udc_select_ep(uep);
+ isp1760_udc_write(udc, DC_EPTYPE, 0);
+
+ /* TODO Synchronize with the IRQ handler */
+
+ list_splice_init(&uep->queue, &req_list);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ list_for_each_entry_safe(req, nreq, &req_list, queue) {
+ list_del(&req->queue);
+ isp1760_udc_request_complete(uep, req, -ESHUTDOWN);
+ }
+
+ return 0;
+}
+
+static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct isp1760_request *req;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+
+ return &req->req;
+}
+
+static void isp1760_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+ struct isp1760_request *req = req_to_udc_req(_req);
+
+ kfree(req);
+}
+
+static int isp1760_ep_queue(struct usb_ep *ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct isp1760_request *req = req_to_udc_req(_req);
+ struct isp1760_ep *uep = ep_to_udc_ep(ep);
+ struct isp1760_udc *udc = uep->udc;
+ bool complete = false;
+ unsigned long flags;
+ int ret = 0;
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ dev_dbg(udc->isp->dev,
+ "%s: req %p (%u bytes%s) ep %p(0x%02x)\n", __func__, _req,
+ _req->length, _req->zero ? " (zlp)" : "", uep, uep->addr);
+
+ req->ep = uep;
+
+ if (uep->addr == 0) {
+ if (_req->length != udc->ep0_length &&
+ udc->ep0_state != ISP1760_CTRL_DATA_IN) {
+ dev_dbg(udc->isp->dev,
+ "%s: invalid length %u for req %p\n",
+ __func__, _req->length, req);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ switch (udc->ep0_state) {
+ case ISP1760_CTRL_DATA_IN:
+ dev_dbg(udc->isp->dev, "%s: transmitting req %p\n",
+ __func__, req);
+
+ list_add_tail(&req->queue, &uep->queue);
+ isp1760_udc_transmit(uep, req);
+ break;
+
+ case ISP1760_CTRL_DATA_OUT:
+ list_add_tail(&req->queue, &uep->queue);
+ __isp1760_udc_select_ep(uep, USB_DIR_OUT);
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+ break;
+
+ case ISP1760_CTRL_STATUS:
+ complete = true;
+ break;
+
+ default:
+ dev_dbg(udc->isp->dev, "%s: invalid ep0 state\n",
+ __func__);
+ ret = -EINVAL;
+ break;
+ }
+ } else if (uep->desc) {
+ bool empty = list_empty(&uep->queue);
+
+ list_add_tail(&req->queue, &uep->queue);
+ if ((uep->addr & USB_DIR_IN) && !uep->halted && empty)
+ isp1760_udc_transmit(uep, req);
+ else if (!(uep->addr & USB_DIR_IN) && uep->rx_pending)
+ complete = isp1760_udc_receive(uep, req);
+ } else {
+ dev_dbg(udc->isp->dev,
+ "%s: can't queue request to disabled ep%02x\n",
+ __func__, uep->addr);
+ ret = -ESHUTDOWN;
+ }
+
+done:
+ if (ret < 0)
+ req->ep = NULL;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (complete)
+ isp1760_udc_request_complete(uep, req, 0);
+
+ return ret;
+}
+
+static int isp1760_ep_dequeue(struct usb_ep *ep, struct usb_request *_req)
+{
+ struct isp1760_request *req = req_to_udc_req(_req);
+ struct isp1760_ep *uep = ep_to_udc_ep(ep);
+ struct isp1760_udc *udc = uep->udc;
+ unsigned long flags;
+
+ dev_dbg(uep->udc->isp->dev, "%s(ep%02x)\n", __func__, uep->addr);
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (req->ep != uep)
+ req = NULL;
+ else
+ list_del(&req->queue);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ if (!req)
+ return -EINVAL;
+
+ isp1760_udc_request_complete(uep, req, -ECONNRESET);
+ return 0;
+}
+
+static int __isp1760_ep_set_halt(struct isp1760_ep *uep, bool stall, bool wedge)
+{
+ struct isp1760_udc *udc = uep->udc;
+ int ret;
+
+ if (!uep->addr) {
+ /*
+ * Halting the control endpoint is only valid as a delayed error
+ * response to a SETUP packet. Make sure EP0 is in the right
+ * stage and that the gadget isn't trying to clear the halt
+ * condition.
+ */
+ if (WARN_ON(udc->ep0_state == ISP1760_CTRL_SETUP || !stall ||
+ wedge)) {
+ return -EINVAL;
+ }
+ }
+
+ if (uep->addr && !uep->desc) {
+ dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__,
+ uep->addr);
+ return -EINVAL;
+ }
+
+ if (uep->addr & USB_DIR_IN) {
+ /* Refuse to halt IN endpoints with active transfers. */
+ if (!list_empty(&uep->queue)) {
+ dev_dbg(udc->isp->dev,
+ "%s: ep%02x has request pending\n", __func__,
+ uep->addr);
+ return -EAGAIN;
+ }
+ }
+
+ ret = __isp1760_udc_set_halt(uep, stall);
+ if (ret < 0)
+ return ret;
+
+ if (!uep->addr) {
+ /*
+ * Stalling EP0 completes the control transaction, move back to
+ * the SETUP state.
+ */
+ udc->ep0_state = ISP1760_CTRL_SETUP;
+ return 0;
+ }
+
+ if (wedge)
+ uep->wedged = true;
+ else if (!stall)
+ uep->wedged = false;
+
+ return 0;
+}
+
+static int isp1760_ep_set_halt(struct usb_ep *ep, int value)
+{
+ struct isp1760_ep *uep = ep_to_udc_ep(ep);
+ unsigned long flags;
+ int ret;
+
+ dev_dbg(uep->udc->isp->dev, "%s: %s halt on ep%02x\n", __func__,
+ value ? "set" : "clear", uep->addr);
+
+ spin_lock_irqsave(&uep->udc->lock, flags);
+ ret = __isp1760_ep_set_halt(uep, value, false);
+ spin_unlock_irqrestore(&uep->udc->lock, flags);
+
+ return ret;
+}
+
+static int isp1760_ep_set_wedge(struct usb_ep *ep)
+{
+ struct isp1760_ep *uep = ep_to_udc_ep(ep);
+ unsigned long flags;
+ int ret;
+
+ dev_dbg(uep->udc->isp->dev, "%s: set wedge on ep%02x)\n", __func__,
+ uep->addr);
+
+ spin_lock_irqsave(&uep->udc->lock, flags);
+ ret = __isp1760_ep_set_halt(uep, true, true);
+ spin_unlock_irqrestore(&uep->udc->lock, flags);
+
+ return ret;
+}
+
+static void isp1760_ep_fifo_flush(struct usb_ep *ep)
+{
+ struct isp1760_ep *uep = ep_to_udc_ep(ep);
+ struct isp1760_udc *udc = uep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ isp1760_udc_select_ep(uep);
+
+ /*
+ * Set the CLBUF bit twice to flush both buffers in case double
+ * buffering is enabled.
+ */
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+ isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static const struct usb_ep_ops isp1760_ep_ops = {
+ .enable = isp1760_ep_enable,
+ .disable = isp1760_ep_disable,
+ .alloc_request = isp1760_ep_alloc_request,
+ .free_request = isp1760_ep_free_request,
+ .queue = isp1760_ep_queue,
+ .dequeue = isp1760_ep_dequeue,
+ .set_halt = isp1760_ep_set_halt,
+ .set_wedge = isp1760_ep_set_wedge,
+ .fifo_flush = isp1760_ep_fifo_flush,
+};
+
+/* -----------------------------------------------------------------------------
+ * Device States
+ */
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_connect(struct isp1760_udc *udc)
+{
+ usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED);
+ mod_timer(&udc->vbus_timer, jiffies + ISP1760_VBUS_POLL_INTERVAL);
+}
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_disconnect(struct isp1760_udc *udc)
+{
+ if (udc->gadget.state < USB_STATE_POWERED)
+ return;
+
+ dev_dbg(udc->isp->dev, "Device disconnected in state %u\n",
+ udc->gadget.state);
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED);
+
+ if (udc->driver->disconnect)
+ udc->driver->disconnect(&udc->gadget);
+
+ del_timer(&udc->vbus_timer);
+
+ /* TODO Reset all endpoints ? */
+}
+
+static void isp1760_udc_init_hw(struct isp1760_udc *udc)
+{
+ /*
+ * The device controller currently shares its interrupt with the host
+ * controller, the DC_IRQ polarity and signaling mode are ignored. Set
+ * the to active-low level-triggered.
+ *
+ * Configure the control, in and out pipes to generate interrupts on
+ * ACK tokens only (and NYET for the out pipe). The default
+ * configuration also generates an interrupt on the first NACK token.
+ */
+ isp1760_udc_write(udc, DC_INTCONF, DC_CDBGMOD_ACK | DC_DDBGMODIN_ACK |
+ DC_DDBGMODOUT_ACK_NYET);
+
+ isp1760_udc_write(udc, DC_INTENABLE, DC_IEPRXTX(7) | DC_IEPRXTX(6) |
+ DC_IEPRXTX(5) | DC_IEPRXTX(4) | DC_IEPRXTX(3) |
+ DC_IEPRXTX(2) | DC_IEPRXTX(1) | DC_IEPRXTX(0) |
+ DC_IEP0SETUP | DC_IEVBUS | DC_IERESM | DC_IESUSP |
+ DC_IEHS_STA | DC_IEBRST);
+
+ if (udc->connected)
+ isp1760_set_pullup(udc->isp, true);
+
+ isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN);
+}
+
+static void isp1760_udc_reset(struct isp1760_udc *udc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /*
+ * The bus reset has reset most registers to their default value,
+ * reinitialize the UDC hardware.
+ */
+ isp1760_udc_init_hw(udc);
+
+ udc->ep0_state = ISP1760_CTRL_SETUP;
+ udc->gadget.speed = USB_SPEED_FULL;
+
+ usb_gadget_udc_reset(&udc->gadget, udc->driver);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static void isp1760_udc_suspend(struct isp1760_udc *udc)
+{
+ if (udc->gadget.state < USB_STATE_DEFAULT)
+ return;
+
+ if (udc->driver->suspend)
+ udc->driver->suspend(&udc->gadget);
+}
+
+static void isp1760_udc_resume(struct isp1760_udc *udc)
+{
+ if (udc->gadget.state < USB_STATE_DEFAULT)
+ return;
+
+ if (udc->driver->resume)
+ udc->driver->resume(&udc->gadget);
+}
+
+/* -----------------------------------------------------------------------------
+ * Gadget Operations
+ */
+
+static int isp1760_udc_get_frame(struct usb_gadget *gadget)
+{
+ struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+ return isp1760_udc_read(udc, DC_FRAMENUM) & ((1 << 11) - 1);
+}
+
+static int isp1760_udc_wakeup(struct usb_gadget *gadget)
+{
+ struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+ dev_dbg(udc->isp->dev, "%s\n", __func__);
+ return -ENOTSUPP;
+}
+
+static int isp1760_udc_set_selfpowered(struct usb_gadget *gadget,
+ int is_selfpowered)
+{
+ struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+ if (is_selfpowered)
+ udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+ else
+ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+ return 0;
+}
+
+static int isp1760_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+ isp1760_set_pullup(udc->isp, is_on);
+ udc->connected = is_on;
+
+ return 0;
+}
+
+static int isp1760_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+ /* The hardware doesn't support low speed. */
+ if (driver->max_speed < USB_SPEED_FULL) {
+ dev_err(udc->isp->dev, "Invalid gadget driver\n");
+ return -EINVAL;
+ }
+
+ spin_lock(&udc->lock);
+
+ if (udc->driver) {
+ dev_err(udc->isp->dev, "UDC already has a gadget driver\n");
+ spin_unlock(&udc->lock);
+ return -EBUSY;
+ }
+
+ udc->driver = driver;
+
+ spin_unlock(&udc->lock);
+
+ dev_dbg(udc->isp->dev, "starting UDC with driver %s\n",
+ driver->function);
+
+ udc->devstatus = 0;
+ udc->connected = true;
+
+ usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED);
+
+ /* DMA isn't supported yet, don't enable the DMA clock. */
+ isp1760_udc_write(udc, DC_MODE, DC_GLINTENA);
+
+ isp1760_udc_init_hw(udc);
+
+ dev_dbg(udc->isp->dev, "UDC started with driver %s\n",
+ driver->function);
+
+ return 0;
+}
+
+static int isp1760_udc_stop(struct usb_gadget *gadget)
+{
+ struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+ dev_dbg(udc->isp->dev, "%s\n", __func__);
+
+ del_timer_sync(&udc->vbus_timer);
+
+ isp1760_udc_write(udc, DC_MODE, 0);
+
+ spin_lock(&udc->lock);
+ udc->driver = NULL;
+ spin_unlock(&udc->lock);
+
+ return 0;
+}
+
+static struct usb_gadget_ops isp1760_udc_ops = {
+ .get_frame = isp1760_udc_get_frame,
+ .wakeup = isp1760_udc_wakeup,
+ .set_selfpowered = isp1760_udc_set_selfpowered,
+ .pullup = isp1760_udc_pullup,
+ .udc_start = isp1760_udc_start,
+ .udc_stop = isp1760_udc_stop,
+};
+
+/* -----------------------------------------------------------------------------
+ * Interrupt Handling
+ */
+
+static irqreturn_t isp1760_udc_irq(int irq, void *dev)
+{
+ struct isp1760_udc *udc = dev;
+ unsigned int i;
+ u32 status;
+
+ status = isp1760_udc_read(udc, DC_INTERRUPT)
+ & isp1760_udc_read(udc, DC_INTENABLE);
+ isp1760_udc_write(udc, DC_INTERRUPT, status);
+
+ if (status & DC_IEVBUS) {
+ dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__);
+ /* The VBUS interrupt is only triggered when VBUS appears. */
+ spin_lock(&udc->lock);
+ isp1760_udc_connect(udc);
+ spin_unlock(&udc->lock);
+ }
+
+ if (status & DC_IEBRST) {
+ dev_dbg(udc->isp->dev, "%s(BRST)\n", __func__);
+
+ isp1760_udc_reset(udc);
+ }
+
+ for (i = 0; i <= 7; ++i) {
+ struct isp1760_ep *ep = &udc->ep[i*2];
+
+ if (status & DC_IEPTX(i)) {
+ dev_dbg(udc->isp->dev, "%s(EPTX%u)\n", __func__, i);
+ isp1760_ep_tx_complete(ep);
+ }
+
+ if (status & DC_IEPRX(i)) {
+ dev_dbg(udc->isp->dev, "%s(EPRX%u)\n", __func__, i);
+ isp1760_ep_rx_ready(i ? ep - 1 : ep);
+ }
+ }
+
+ if (status & DC_IEP0SETUP) {
+ dev_dbg(udc->isp->dev, "%s(EP0SETUP)\n", __func__);
+
+ isp1760_ep0_setup(udc);
+ }
+
+ if (status & DC_IERESM) {
+ dev_dbg(udc->isp->dev, "%s(RESM)\n", __func__);
+ isp1760_udc_resume(udc);
+ }
+
+ if (status & DC_IESUSP) {
+ dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__);
+
+ spin_lock(&udc->lock);
+ if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT))
+ isp1760_udc_disconnect(udc);
+ else
+ isp1760_udc_suspend(udc);
+ spin_unlock(&udc->lock);
+ }
+
+ if (status & DC_IEHS_STA) {
+ dev_dbg(udc->isp->dev, "%s(HS_STA)\n", __func__);
+ udc->gadget.speed = USB_SPEED_HIGH;
+ }
+
+ return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void isp1760_udc_vbus_poll(unsigned long data)
+{
+ struct isp1760_udc *udc = (struct isp1760_udc *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT))
+ isp1760_udc_disconnect(udc);
+ else if (udc->gadget.state >= USB_STATE_POWERED)
+ mod_timer(&udc->vbus_timer,
+ jiffies + ISP1760_VBUS_POLL_INTERVAL);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Registration
+ */
+
+static void isp1760_udc_init_eps(struct isp1760_udc *udc)
+{
+ unsigned int i;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+ for (i = 0; i < ARRAY_SIZE(udc->ep); ++i) {
+ struct isp1760_ep *ep = &udc->ep[i];
+ unsigned int ep_num = (i + 1) / 2;
+ bool is_in = !(i & 1);
+
+ ep->udc = udc;
+
+ INIT_LIST_HEAD(&ep->queue);
+
+ ep->addr = (ep_num && is_in ? USB_DIR_IN : USB_DIR_OUT)
+ | ep_num;
+ ep->desc = NULL;
+
+ sprintf(ep->name, "ep%u%s", ep_num,
+ ep_num ? (is_in ? "in" : "out") : "");
+
+ ep->ep.ops = &isp1760_ep_ops;
+ ep->ep.name = ep->name;
+
+ /*
+ * Hardcode the maximum packet sizes for now, to 64 bytes for
+ * the control endpoint and 512 bytes for all other endpoints.
+ * This fits in the 8kB FIFO without double-buffering.
+ */
+ if (ep_num == 0) {
+ ep->ep.maxpacket = 64;
+ ep->maxpacket = 64;
+ udc->gadget.ep0 = &ep->ep;
+ } else {
+ ep->ep.maxpacket = 512;
+ ep->maxpacket = 0;
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ }
+ }
+}
+
+static int isp1760_udc_init(struct isp1760_udc *udc)
+{
+ u16 scratch;
+ u32 chipid;
+
+ /*
+ * Check that the controller is present by writing to the scratch
+ * register, modifying the bus pattern by reading from the chip ID
+ * register, and reading the scratch register value back. The chip ID
+ * and scratch register contents must match the expected values.
+ */
+ isp1760_udc_write(udc, DC_SCRATCH, 0xbabe);
+ chipid = isp1760_udc_read(udc, DC_CHIPID);
+ scratch = isp1760_udc_read(udc, DC_SCRATCH);
+
+ if (scratch != 0xbabe) {
+ dev_err(udc->isp->dev,
+ "udc: scratch test failed (0x%04x/0x%08x)\n",
+ scratch, chipid);
+ return -ENODEV;
+ }
+
+ if (chipid != 0x00011582) {
+ dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid);
+ return -ENODEV;
+ }
+
+ /* Reset the device controller. */
+ isp1760_udc_write(udc, DC_MODE, DC_SFRESET);
+ usleep_range(10000, 11000);
+ isp1760_udc_write(udc, DC_MODE, 0);
+ usleep_range(10000, 11000);
+
+ return 0;
+}
+
+int isp1760_udc_register(struct isp1760_device *isp, int irq,
+ unsigned long irqflags)
+{
+ struct isp1760_udc *udc = &isp->udc;
+ const char *devname;
+ int ret;
+
+ udc->irq = -1;
+ udc->isp = isp;
+ udc->regs = isp->regs;
+
+ spin_lock_init(&udc->lock);
+ setup_timer(&udc->vbus_timer, isp1760_udc_vbus_poll,
+ (unsigned long)udc);
+
+ ret = isp1760_udc_init(udc);
+ if (ret < 0)
+ return ret;
+
+ devname = dev_name(isp->dev);
+ udc->irqname = kmalloc(strlen(devname) + 7, GFP_KERNEL);
+ if (!udc->irqname)
+ return -ENOMEM;
+
+ sprintf(udc->irqname, "%s (udc)", devname);
+
+ ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | IRQF_DISABLED |
+ irqflags, udc->irqname, udc);
+ if (ret < 0)
+ goto error;
+
+ udc->irq = irq;
+
+ /*
+ * Initialize the gadget static fields and register its device. Gadget
+ * fields that vary during the life time of the gadget are initialized
+ * by the UDC core.
+ */
+ udc->gadget.ops = &isp1760_udc_ops;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->gadget.max_speed = USB_SPEED_HIGH;
+ udc->gadget.name = "isp1761_udc";
+
+ isp1760_udc_init_eps(udc);
+
+ ret = usb_add_gadget_udc(isp->dev, &udc->gadget);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ if (udc->irq >= 0)
+ free_irq(udc->irq, udc);
+ kfree(udc->irqname);
+
+ return ret;
+}
+
+void isp1760_udc_unregister(struct isp1760_device *isp)
+{
+ struct isp1760_udc *udc = &isp->udc;
+
+ if (!udc->isp)
+ return;
+
+ usb_del_gadget_udc(&udc->gadget);
+
+ free_irq(udc->irq, udc);
+ kfree(udc->irqname);
+}
diff --git a/drivers/usb/isp1760/isp1760-udc.h b/drivers/usb/isp1760/isp1760-udc.h
new file mode 100644
index 000000000000..26899ed81145
--- /dev/null
+++ b/drivers/usb/isp1760/isp1760-udc.h
@@ -0,0 +1,106 @@
+/*
+ * Driver for the NXP ISP1761 device controller
+ *
+ * Copyright 2014 Ideas on Board Oy
+ *
+ * Contacts:
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.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 _ISP1760_UDC_H_
+#define _ISP1760_UDC_H_
+
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/usb/gadget.h>
+
+struct isp1760_device;
+struct isp1760_udc;
+
+enum isp1760_ctrl_state {
+ ISP1760_CTRL_SETUP, /* Waiting for a SETUP transaction */
+ ISP1760_CTRL_DATA_IN, /* Setup received, data IN stage */
+ ISP1760_CTRL_DATA_OUT, /* Setup received, data OUT stage */
+ ISP1760_CTRL_STATUS, /* 0-length request in status stage */
+};
+
+struct isp1760_ep {
+ struct isp1760_udc *udc;
+ struct usb_ep ep;
+
+ struct list_head queue;
+
+ unsigned int addr;
+ unsigned int maxpacket;
+ char name[7];
+
+ const struct usb_endpoint_descriptor *desc;
+
+ bool rx_pending;
+ bool halted;
+ bool wedged;
+};
+
+/**
+ * struct isp1760_udc - UDC state information
+ * irq: IRQ number
+ * irqname: IRQ name (as passed to request_irq)
+ * regs: Base address of the UDC registers
+ * driver: Gadget driver
+ * gadget: Gadget device
+ * lock: Protects driver, vbus_timer, ep, ep0_*, DC_EPINDEX register
+ * ep: Array of endpoints
+ * ep0_state: Control request state for endpoint 0
+ * ep0_dir: Direction of the current control request
+ * ep0_length: Length of the current control request
+ * connected: Tracks gadget driver bus connection state
+ */
+struct isp1760_udc {
+#ifdef CONFIG_USB_ISP1761_UDC
+ struct isp1760_device *isp;
+
+ int irq;
+ char *irqname;
+ void __iomem *regs;
+
+ struct usb_gadget_driver *driver;
+ struct usb_gadget gadget;
+
+ spinlock_t lock;
+ struct timer_list vbus_timer;
+
+ struct isp1760_ep ep[15];
+
+ enum isp1760_ctrl_state ep0_state;
+ u8 ep0_dir;
+ u16 ep0_length;
+
+ bool connected;
+
+ unsigned int devstatus;
+#endif
+};
+
+#ifdef CONFIG_USB_ISP1761_UDC
+int isp1760_udc_register(struct isp1760_device *isp, int irq,
+ unsigned long irqflags);
+void isp1760_udc_unregister(struct isp1760_device *isp);
+#else
+static inline int isp1760_udc_register(struct isp1760_device *isp, int irq,
+ unsigned long irqflags)
+{
+ return 0;
+}
+
+static inline void isp1760_udc_unregister(struct isp1760_device *isp)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index 40ef40affe83..588d62a73e1a 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -124,12 +124,8 @@ static void async_complete(struct urb *urb)
} else if (rq->dr->bRequest == 3) {
memcpy(priv->reg, rq->reg, sizeof(priv->reg));
#if 0
- dev_dbg(&priv->usbdev->dev,
- "async_complete regs %02x %02x %02x %02x %02x %02x %02x\n",
- (unsigned int)priv->reg[0], (unsigned int)priv->reg[1],
- (unsigned int)priv->reg[2], (unsigned int)priv->reg[3],
- (unsigned int)priv->reg[4], (unsigned int)priv->reg[5],
- (unsigned int)priv->reg[6]);
+ dev_dbg(&priv->usbdev->dev, "async_complete regs %7ph\n",
+ priv->reg);
#endif
/* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */
if (rq->reg[2] & rq->reg[1] & 0x10 && pp)
@@ -742,9 +738,7 @@ static int uss720_probe(struct usb_interface *intf,
set_1284_register(pp, 2, 0x0c, GFP_KERNEL);
/* debugging */
get_1284_register(pp, 0, &reg, GFP_KERNEL);
- dev_dbg(&intf->dev, "reg: %02x %02x %02x %02x %02x %02x %02x\n",
- priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3],
- priv->reg[4], priv->reg[5], priv->reg[6]);
+ dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
endpoint = &interface->endpoint[2];
dev_dbg(&intf->dev, "epaddr %d interval %d\n",
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index b005010240e5..14e1628483d9 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -63,11 +63,13 @@ comment "Platform Glue Layer"
config USB_MUSB_DAVINCI
tristate "DaVinci"
depends on ARCH_DAVINCI_DMx
+ depends on NOP_USB_XCEIV
depends on BROKEN
config USB_MUSB_DA8XX
tristate "DA8xx/OMAP-L1x"
depends on ARCH_DAVINCI_DA8XX
+ depends on NOP_USB_XCEIV
depends on BROKEN
config USB_MUSB_TUSB6010
@@ -77,12 +79,13 @@ config USB_MUSB_TUSB6010
config USB_MUSB_OMAP2PLUS
tristate "OMAP2430 and onwards"
- depends on ARCH_OMAP2PLUS && USB
+ depends on ARCH_OMAP2PLUS && USB && OMAP_CONTROL_PHY
select GENERIC_PHY
config USB_MUSB_AM35X
tristate "AM35x"
depends on ARCH_OMAP
+ depends on NOP_USB_XCEIV
config USB_MUSB_DSPS
tristate "TI DSPS platforms"
@@ -93,6 +96,7 @@ config USB_MUSB_DSPS
config USB_MUSB_BLACKFIN
tristate "Blackfin"
depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
+ depends on NOP_USB_XCEIV
config USB_MUSB_UX500
tristate "Ux500 platforms"
@@ -100,6 +104,7 @@ config USB_MUSB_UX500
config USB_MUSB_JZ4740
tristate "JZ4740"
+ depends on NOP_USB_XCEIV
depends on MACH_JZ4740 || COMPILE_TEST
depends on USB_MUSB_GADGET
depends on USB_OTG_BLACKLIST_HUB
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index 178250145613..6123b748d262 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -608,7 +608,7 @@ static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume);
static struct platform_driver bfin_driver = {
.probe = bfin_probe,
- .remove = __exit_p(bfin_remove),
+ .remove = bfin_remove,
.driver = {
.name = "musb-blackfin",
.pm = &bfin_pm_ops,
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 34cce3e38c49..e6f4cbfeed97 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -567,6 +567,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->xceiv->otg->state = OTG_STATE_A_HOST;
musb->is_active = 1;
+ musb_host_resume_root_hub(musb);
break;
case OTG_STATE_B_WAIT_ACON:
musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
@@ -2500,6 +2501,12 @@ static int musb_runtime_resume(struct device *dev)
musb_restore_context(musb);
first = 0;
+ if (musb->need_finish_resume) {
+ musb->need_finish_resume = 0;
+ schedule_delayed_work(&musb->finish_resume_work,
+ msecs_to_jiffies(20));
+ }
+
return 0;
}
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index c39a16ad7832..be84562d021b 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -9,9 +9,9 @@
#define RNDIS_REG(x) (0x80 + ((x - 1) * 4))
-#define EP_MODE_AUTOREG_NONE 0
-#define EP_MODE_AUTOREG_ALL_NEOP 1
-#define EP_MODE_AUTOREG_ALWAYS 3
+#define EP_MODE_AUTOREQ_NONE 0
+#define EP_MODE_AUTOREQ_ALL_NEOP 1
+#define EP_MODE_AUTOREQ_ALWAYS 3
#define EP_MODE_DMA_TRANSPARENT 0
#define EP_MODE_DMA_RNDIS 1
@@ -404,19 +404,19 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
/* auto req */
cppi41_set_autoreq_mode(cppi41_channel,
- EP_MODE_AUTOREG_ALL_NEOP);
+ EP_MODE_AUTOREQ_ALL_NEOP);
} else {
musb_writel(musb->ctrl_base,
RNDIS_REG(cppi41_channel->port_num), 0);
cppi41_set_dma_mode(cppi41_channel,
EP_MODE_DMA_TRANSPARENT);
cppi41_set_autoreq_mode(cppi41_channel,
- EP_MODE_AUTOREG_NONE);
+ EP_MODE_AUTOREQ_NONE);
}
} else {
/* fallback mode */
cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT);
- cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREG_NONE);
+ cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE);
len = min_t(u32, packet_sz, len);
}
cppi41_channel->prog_len = len;
@@ -549,10 +549,15 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
csr &= ~MUSB_TXCSR_DMAENAB;
musb_writew(epio, MUSB_TXCSR, csr);
} else {
+ cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE);
+
csr = musb_readw(epio, MUSB_RXCSR);
csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB);
musb_writew(epio, MUSB_RXCSR, csr);
+ /* wait to drain cppi dma pipe line */
+ udelay(50);
+
csr = musb_readw(epio, MUSB_RXCSR);
if (csr & MUSB_RXCSR_RXPKTRDY) {
csr |= MUSB_RXCSR_FLUSHFIFO;
@@ -566,13 +571,14 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
tdbit <<= 16;
do {
- musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
+ if (is_tx)
+ musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
ret = dmaengine_terminate_all(cppi41_channel->dc);
} while (ret == -EAGAIN);
- musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
-
if (is_tx) {
+ musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
+
csr = musb_readw(epio, MUSB_TXCSR);
if (csr & MUSB_TXCSR_TXPKTRDY) {
csr |= MUSB_TXCSR_FLUSHFIFO;
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 48131aa8472c..78a283e9ce40 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -196,7 +196,7 @@ static ssize_t musb_test_mode_write(struct file *file,
memset(buf, 0x00, sizeof(buf));
- if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (strstarts(buf, "force host"))
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 49b04cb6f5ca..b2d9040c7685 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1612,9 +1612,7 @@ done:
static int
musb_gadget_set_self_powered(struct usb_gadget *gadget, int is_selfpowered)
{
- struct musb *musb = gadget_to_musb(gadget);
-
- musb->is_self_powered = !!is_selfpowered;
+ gadget->is_selfpowered = !!is_selfpowered;
return 0;
}
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index 2af45a0c8930..10d30afe4a3c 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -85,7 +85,7 @@ static int service_tx_status_request(
switch (recip) {
case USB_RECIP_DEVICE:
- result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED;
+ result[0] = musb->g.is_selfpowered << USB_DEVICE_SELF_POWERED;
result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
if (musb->g.is_otg) {
result[0] |= musb->g.b_hnp_enable
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index b072420e44f5..294e159f4afe 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -72,7 +72,6 @@ void musb_host_finish_resume(struct work_struct *work)
musb->xceiv->otg->state = OTG_STATE_A_HOST;
spin_unlock_irqrestore(&musb->lock, flags);
- musb_host_resume_root_hub(musb);
}
void musb_port_suspend(struct musb *musb, bool do_suspend)
@@ -349,9 +348,9 @@ int musb_hub_control(
desc->bDescriptorType = 0x29;
desc->bNbrPorts = 1;
desc->wHubCharacteristics = cpu_to_le16(
- 0x0001 /* per-port power switching */
- | 0x0010 /* no overcurrent reporting */
- );
+ HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */
+ | HUB_CHAR_NO_OCPM /* no overcurrent reporting */
+ );
desc->bPwrOn2PwrGood = 5; /* msec/2 */
desc->bHubContrCurrent = 0;
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index ab38aa32a6c1..94eb2923afed 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -107,19 +107,6 @@ static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
#define fsl_writel(val, addr) writel(val, addr)
#endif /* CONFIG_PPC32 */
-/* Routines to access transceiver ULPI registers */
-u8 view_ulpi(u8 addr)
-{
- u32 temp;
-
- temp = 0x40000000 | (addr << 16);
- fsl_writel(temp, &usb_dr_regs->ulpiview);
- udelay(1000);
- while (temp & 0x40)
- temp = fsl_readl(&usb_dr_regs->ulpiview);
- return (le32_to_cpu(temp) & 0x0000ff00) >> 8;
-}
-
int write_ulpi(u8 addr, u8 data)
{
u32 temp;
@@ -460,28 +447,6 @@ static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
fsl_otg_del_timer(fsm, timer);
}
-/*
- * Reduce timer count by 1, and find timeout conditions.
- * Called by fsl_otg 1ms timer interrupt
- */
-int fsl_otg_tick_timer(void)
-{
- struct fsl_otg_timer *tmp_timer, *del_tmp;
- int expired = 0;
-
- list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
- tmp_timer->count--;
- /* check if timer expires */
- if (!tmp_timer->count) {
- list_del(&tmp_timer->list);
- tmp_timer->function(tmp_timer->data);
- expired = 1;
- }
- }
-
- return expired;
-}
-
/* Reset controller, not reset the bus */
void otg_reset_controller(void)
{
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index f1b719b45a53..70be50b734b2 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/usb_phy_generic.h>
#include <linux/slab.h>
@@ -39,6 +40,10 @@
#include "phy-generic.h"
+#define VBUS_IRQ_FLAGS \
+ (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \
+ IRQF_ONESHOT)
+
struct platform_device *usb_phy_generic_register(void)
{
return platform_device_register_simple("usb_phy_generic",
@@ -59,19 +64,79 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
{
- int value;
+ if (!nop->gpiod_reset)
+ return;
+
+ gpiod_direction_output(nop->gpiod_reset, !asserted);
+ usleep_range(10000, 20000);
+ gpiod_set_value(nop->gpiod_reset, asserted);
+}
+
+/* interface to regulator framework */
+static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
+{
+ struct regulator *vbus_draw = nop->vbus_draw;
+ int enabled;
+ int ret;
- if (!gpio_is_valid(nop->gpio_reset))
+ if (!vbus_draw)
return;
- value = asserted;
- if (nop->reset_active_low)
- value = !value;
+ enabled = nop->vbus_draw_enabled;
+ if (mA) {
+ regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
+ if (!enabled) {
+ ret = regulator_enable(vbus_draw);
+ if (ret < 0)
+ return;
+ nop->vbus_draw_enabled = 1;
+ }
+ } else {
+ if (enabled) {
+ ret = regulator_disable(vbus_draw);
+ if (ret < 0)
+ return;
+ nop->vbus_draw_enabled = 0;
+ }
+ }
+ nop->mA = mA;
+}
+
+
+static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
+{
+ struct usb_phy_generic *nop = data;
+ struct usb_otg *otg = nop->phy.otg;
+ int vbus, status;
+
+ vbus = gpiod_get_value(nop->gpiod_vbus);
+ if ((vbus ^ nop->vbus) == 0)
+ return IRQ_HANDLED;
+ nop->vbus = vbus;
+
+ if (vbus) {
+ status = USB_EVENT_VBUS;
+ otg->state = OTG_STATE_B_PERIPHERAL;
+ nop->phy.last_event = status;
+ usb_gadget_vbus_connect(otg->gadget);
+
+ /* drawing a "unit load" is *always* OK, except for OTG */
+ nop_set_vbus_draw(nop, 100);
+
+ atomic_notifier_call_chain(&nop->phy.notifier, status,
+ otg->gadget);
+ } else {
+ nop_set_vbus_draw(nop, 0);
- gpio_set_value_cansleep(nop->gpio_reset, value);
+ usb_gadget_vbus_disconnect(otg->gadget);
+ status = USB_EVENT_NONE;
+ otg->state = OTG_STATE_B_IDLE;
+ nop->phy.last_event = status;
- if (!asserted)
- usleep_range(10000, 20000);
+ atomic_notifier_call_chain(&nop->phy.notifier, status,
+ otg->gadget);
+ }
+ return IRQ_HANDLED;
}
int usb_gen_phy_init(struct usb_phy *phy)
@@ -143,36 +208,47 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
struct usb_phy_generic_platform_data *pdata)
{
enum usb_phy_type type = USB_PHY_TYPE_USB2;
- int err;
+ int err = 0;
u32 clk_rate = 0;
bool needs_vcc = false;
- nop->reset_active_low = true; /* default behaviour */
-
if (dev->of_node) {
struct device_node *node = dev->of_node;
- enum of_gpio_flags flags = 0;
if (of_property_read_u32(node, "clock-frequency", &clk_rate))
clk_rate = 0;
needs_vcc = of_property_read_bool(node, "vcc-supply");
- nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios",
- 0, &flags);
- if (nop->gpio_reset == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
- nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
-
+ nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset");
+ err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
+ if (!err) {
+ nop->gpiod_vbus = devm_gpiod_get_optional(dev,
+ "vbus-detect");
+ err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
+ }
} else if (pdata) {
type = pdata->type;
clk_rate = pdata->clk_rate;
needs_vcc = pdata->needs_vcc;
- nop->gpio_reset = pdata->gpio_reset;
- } else {
- nop->gpio_reset = -1;
+ if (gpio_is_valid(pdata->gpio_reset)) {
+ err = devm_gpio_request_one(dev, pdata->gpio_reset, 0,
+ dev_name(dev));
+ if (!err)
+ nop->gpiod_reset =
+ gpio_to_desc(pdata->gpio_reset);
+ }
+ nop->gpiod_vbus = pdata->gpiod_vbus;
+ }
+
+ if (err == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (err) {
+ dev_err(dev, "Error requesting RESET or VBUS GPIO\n");
+ return err;
}
+ if (nop->gpiod_reset)
+ gpiod_direction_output(nop->gpiod_reset, 1);
nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
GFP_KERNEL);
@@ -201,24 +277,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
return -EPROBE_DEFER;
}
- if (gpio_is_valid(nop->gpio_reset)) {
- unsigned long gpio_flags;
-
- /* Assert RESET */
- if (nop->reset_active_low)
- gpio_flags = GPIOF_OUT_INIT_LOW;
- else
- gpio_flags = GPIOF_OUT_INIT_HIGH;
-
- err = devm_gpio_request_one(dev, nop->gpio_reset,
- gpio_flags, dev_name(dev));
- if (err) {
- dev_err(dev, "Error requesting RESET GPIO %d\n",
- nop->gpio_reset);
- return err;
- }
- }
-
nop->dev = dev;
nop->phy.dev = nop->dev;
nop->phy.label = "nop-xceiv";
@@ -247,6 +305,18 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
if (err)
return err;
+ if (nop->gpiod_vbus) {
+ err = devm_request_threaded_irq(&pdev->dev,
+ gpiod_to_irq(nop->gpiod_vbus),
+ NULL, nop_gpio_vbus_thread,
+ VBUS_IRQ_FLAGS, "vbus_detect",
+ nop);
+ if (err) {
+ dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
+ gpiod_to_irq(nop->gpiod_vbus), err);
+ return err;
+ }
+ }
nop->phy.init = usb_gen_phy_init;
nop->phy.shutdown = usb_gen_phy_shutdown;
diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h
index d8feacc0b7fb..0d0eadd54ed9 100644
--- a/drivers/usb/phy/phy-generic.h
+++ b/drivers/usb/phy/phy-generic.h
@@ -2,14 +2,20 @@
#define _PHY_GENERIC_H_
#include <linux/usb/usb_phy_generic.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
struct usb_phy_generic {
struct usb_phy phy;
struct device *dev;
struct clk *clk;
struct regulator *vcc;
- int gpio_reset;
- bool reset_active_low;
+ struct gpio_desc *gpiod_reset;
+ struct gpio_desc *gpiod_vbus;
+ struct regulator *vbus_draw;
+ bool vbus_draw_enabled;
+ unsigned long mA;
+ unsigned int vbus;
};
int usb_gen_phy_init(struct usb_phy *phy);
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index b9589f663683..8f7cb068d29b 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -40,6 +40,7 @@
#define BM_USBPHY_CTRL_SFTRST BIT(31)
#define BM_USBPHY_CTRL_CLKGATE BIT(30)
+#define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27)
#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26)
#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25)
#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23)
@@ -69,6 +70,9 @@
#define ANADIG_USB2_LOOPBACK_SET 0x244
#define ANADIG_USB2_LOOPBACK_CLR 0x248
+#define ANADIG_USB1_MISC 0x1f0
+#define ANADIG_USB2_MISC 0x250
+
#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12)
#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11)
@@ -80,6 +84,11 @@
#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2)
#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5)
+#define BM_ANADIG_USB1_MISC_RX_VPIN_FS BIT(29)
+#define BM_ANADIG_USB1_MISC_RX_VMIN_FS BIT(28)
+#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29)
+#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28)
+
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
/* Do disconnection between PHY and controller without vbus */
@@ -131,8 +140,7 @@ static const struct mxs_phy_data vf610_phy_data = {
};
static const struct mxs_phy_data imx6sx_phy_data = {
- .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
- MXS_PHY_NEED_IP_FIX,
+ .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
};
static const struct of_device_id mxs_phy_dt_ids[] = {
@@ -256,6 +264,18 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
usleep_range(500, 1000);
}
+static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
+{
+ void __iomem *base = mxs_phy->phy.io_priv;
+ u32 phyctrl = readl(base + HW_USBPHY_CTRL);
+
+ if (IS_ENABLED(CONFIG_USB_OTG) &&
+ !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
+ return true;
+
+ return false;
+}
+
static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
{
bool vbus_is_on = false;
@@ -270,7 +290,7 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
- if (on && !vbus_is_on)
+ if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
__mxs_phy_disconnect_line(mxs_phy, true);
else
__mxs_phy_disconnect_line(mxs_phy, false);
@@ -293,6 +313,17 @@ static int mxs_phy_init(struct usb_phy *phy)
static void mxs_phy_shutdown(struct usb_phy *phy)
{
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+ u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP |
+ BM_USBPHY_CTRL_ENDPDMCHG_WKUP |
+ BM_USBPHY_CTRL_ENIDCHG_WKUP |
+ BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
+ BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
+ BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
+ BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
+ BM_USBPHY_CTRL_ENAUTO_PWRON_PLL;
+
+ writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR);
+ writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD);
writel(BM_USBPHY_CTRL_CLKGATE,
phy->io_priv + HW_USBPHY_CTRL_SET);
@@ -300,13 +331,56 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
clk_disable_unprepare(mxs_phy->clk);
}
+static bool mxs_phy_is_low_speed_connection(struct mxs_phy *mxs_phy)
+{
+ unsigned int line_state;
+ /* bit definition is the same for all controllers */
+ unsigned int dp_bit = BM_ANADIG_USB1_MISC_RX_VPIN_FS,
+ dm_bit = BM_ANADIG_USB1_MISC_RX_VMIN_FS;
+ unsigned int reg = ANADIG_USB1_MISC;
+
+ /* If the SoCs don't have anatop, quit */
+ if (!mxs_phy->regmap_anatop)
+ return false;
+
+ if (mxs_phy->port_id == 0)
+ reg = ANADIG_USB1_MISC;
+ else if (mxs_phy->port_id == 1)
+ reg = ANADIG_USB2_MISC;
+
+ regmap_read(mxs_phy->regmap_anatop, reg, &line_state);
+
+ if ((line_state & (dp_bit | dm_bit)) == dm_bit)
+ return true;
+ else
+ return false;
+}
+
static int mxs_phy_suspend(struct usb_phy *x, int suspend)
{
int ret;
struct mxs_phy *mxs_phy = to_mxs_phy(x);
+ bool low_speed_connection, vbus_is_on;
+
+ low_speed_connection = mxs_phy_is_low_speed_connection(mxs_phy);
+ vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
if (suspend) {
- writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
+ /*
+ * FIXME: Do not power down RXPWD1PT1 bit for low speed
+ * connect. The low speed connection will have problem at
+ * very rare cases during usb suspend and resume process.
+ */
+ if (low_speed_connection & vbus_is_on) {
+ /*
+ * If value to be set as pwd value is not 0xffffffff,
+ * several 32Khz cycles are needed.
+ */
+ mxs_phy_clock_switch_delay();
+ writel(0xffbfffff, x->io_priv + HW_USBPHY_PWD);
+ } else {
+ writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
+ }
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_SET);
clk_disable_unprepare(mxs_phy->clk);
@@ -359,7 +433,9 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy,
dev_dbg(phy->dev, "%s device has disconnected\n",
(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
- if (speed == USB_SPEED_HIGH)
+ /* Sometimes, the speed is not high speed when the error occurs */
+ if (readl(phy->io_priv + HW_USBPHY_CTRL) &
+ BM_USBPHY_CTRL_ENHOSTDISCONDETECT)
writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
phy->io_priv + HW_USBPHY_CTRL_CLR);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 371478704899..4cf77d3c3bd2 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -363,6 +363,7 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
int id;
int enable;
+ int cable;
int ret;
/*
@@ -376,6 +377,16 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
id = usbhs_platform_call(priv, get_id, pdev);
if (enable && !mod) {
+ if (priv->edev) {
+ cable = extcon_get_cable_state(priv->edev, "USB-HOST");
+ if ((cable > 0 && id != USBHS_HOST) ||
+ (!cable && id != USBHS_GADGET)) {
+ dev_info(&pdev->dev,
+ "USB cable plugged in doesn't match the selected role!\n");
+ return;
+ }
+ }
+
ret = usbhs_mod_change(priv, id);
if (ret < 0)
return;
@@ -514,6 +525,12 @@ static int usbhs_probe(struct platform_device *pdev)
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
+ if (of_property_read_bool(pdev->dev.of_node, "extcon")) {
+ priv->edev = extcon_get_edev_by_phandle(&pdev->dev, 0);
+ if (IS_ERR(priv->edev))
+ return PTR_ERR(priv->edev);
+ }
+
/*
* care platform info
*/
@@ -615,7 +632,7 @@ static int usbhs_probe(struct platform_device *pdev)
*/
ret = usbhs_platform_call(priv, hardware_init, pdev);
if (ret < 0) {
- dev_err(&pdev->dev, "platform prove failed.\n");
+ dev_err(&pdev->dev, "platform init failed.\n");
goto probe_end_mod_exit;
}
@@ -632,16 +649,12 @@ static int usbhs_probe(struct platform_device *pdev)
/*
* manual call notify_hotplug for cold plug
*/
- ret = usbhsc_drvcllbck_notify_hotplug(pdev);
- if (ret < 0)
- goto probe_end_call_remove;
+ usbhsc_drvcllbck_notify_hotplug(pdev);
dev_info(&pdev->dev, "probed\n");
return ret;
-probe_end_call_remove:
- usbhs_platform_call(priv, hardware_exit, pdev);
probe_end_mod_exit:
usbhs_mod_remove(priv);
probe_end_fifo_exit:
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index 0427cdd1a483..fc96e924edc4 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -17,6 +17,7 @@
#ifndef RENESAS_USB_DRIVER_H
#define RENESAS_USB_DRIVER_H
+#include <linux/extcon.h>
#include <linux/platform_device.h>
#include <linux/usb/renesas_usbhs.h>
@@ -254,6 +255,8 @@ struct usbhs_priv {
struct delayed_work notify_hotplug_work;
struct platform_device *pdev;
+ struct extcon_dev *edev;
+
spinlock_t lock;
u32 flags;
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index f46271ce1b15..d891bff39d66 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -1054,10 +1054,8 @@ static void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo)
fifo->rx_chan = NULL;
}
-static void usbhsf_dma_init(struct usbhs_priv *priv,
- struct usbhs_fifo *fifo)
+static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo)
{
- struct device *dev = usbhs_priv_to_dev(priv);
dma_cap_mask_t mask;
dma_cap_zero(mask);
@@ -1069,6 +1067,27 @@ static void usbhsf_dma_init(struct usbhs_priv *priv,
dma_cap_set(DMA_SLAVE, mask);
fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter,
&fifo->rx_slave);
+}
+
+static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo)
+{
+ fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx");
+ if (IS_ERR(fifo->tx_chan))
+ fifo->tx_chan = NULL;
+ fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx");
+ if (IS_ERR(fifo->rx_chan))
+ fifo->rx_chan = NULL;
+}
+
+static void usbhsf_dma_init(struct usbhs_priv *priv,
+ struct usbhs_fifo *fifo)
+{
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ if (dev->of_node)
+ usbhsf_dma_init_dt(dev, fifo);
+ else
+ usbhsf_dma_init_pdev(fifo);
if (fifo->tx_chan || fifo->rx_chan)
dev_dbg(dev, "enable DMAEngine (%s%s%s)\n",
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 8697e6efcabf..e0384af77e56 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -926,6 +926,8 @@ static int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self)
else
usbhsg_status_clr(gpriv, USBHSG_STATUS_SELF_POWERED);
+ gadget->is_selfpowered = (is_self != 0);
+
return 0;
}
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index f0d323125871..96eead619282 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -1234,7 +1234,8 @@ static int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv,
desc->bNbrPorts = roothub_id;
desc->bDescLength = 9;
desc->bPwrOn2PwrGood = 0;
- desc->wHubCharacteristics = cpu_to_le16(0x0011);
+ desc->wHubCharacteristics =
+ cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM);
desc->u.hs.DeviceRemovable[0] = (roothub_id << 1);
desc->u.hs.DeviceRemovable[1] = ~0;
dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__);
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index f4c56fc1a9f6..f40c856ff758 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -56,6 +56,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x0846, 0x1100) }, /* NetGear Managed Switch M4100 series, M5300 series, M7100 series */
{ USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
{ USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */
+ { USB_DEVICE(0x0908, 0x01FF) }, /* Siemens RUGGEDCOM USB Serial Console */
{ USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */
{ USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */
{ USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 220b4be89641..e4473a9109cf 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1309,35 +1309,6 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *current_position = data;
unsigned char *data1;
-#ifdef NOTMOS7840
- Data = 0x00;
- status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
- mos7840_port->shadowLCR = Data;
- dev_dbg(&port->dev, "%s: LINE_CONTROL_REGISTER is %x\n", __func__, Data);
- dev_dbg(&port->dev, "%s: mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
-
- /* Data = 0x03; */
- /* status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data); */
- /* mos7840_port->shadowLCR=Data;//Need to add later */
-
- Data |= SERIAL_LCR_DLAB; /* data latch enable in LCR 0x80 */
- status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
-
- /* Data = 0x0c; */
- /* status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data); */
- Data = 0x00;
- status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data);
- dev_dbg(&port->dev, "%s: DLL value is %x\n", __func__, Data);
-
- Data = 0x0;
- status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data);
- dev_dbg(&port->dev, "%s: DLM value is %x\n", __func__, Data);
-
- Data = Data & ~SERIAL_LCR_DLAB;
- dev_dbg(&port->dev, "%s: mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
- status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
-#endif
-
if (mos7840_port_paranoia_check(port, __func__))
return -1;
@@ -1614,37 +1585,6 @@ static int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port,
*clk_sel_val = 0x70;
}
return 0;
-
-#ifdef NOTMCS7840
-
- for (i = 0; i < ARRAY_SIZE(mos7840_divisor_table); i++) {
- if (mos7840_divisor_table[i].BaudRate == baudrate) {
- *divisor = mos7840_divisor_table[i].Divisor;
- return 0;
- }
- }
-
- /* After trying for all the standard baud rates *
- * Try calculating the divisor for this baud rate */
-
- if (baudrate > 75 && baudrate < 230400) {
- /* get the divisor */
- custom = (__u16) (230400L / baudrate);
-
- /* Check for round off */
- round1 = (__u16) (2304000L / baudrate);
- round = (__u16) (round1 - (custom * 10));
- if (round > 4)
- custom++;
- *divisor = custom;
-
- dev_dbg(&port->dev, " Baud %d = %d\n", baudrate, custom);
- return 0;
- }
-
- dev_dbg(&port->dev, "%s", " Baud calculation Failed...\n");
- return -1;
-#endif
}
/*****************************************************************************
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index efdcee15b520..f0c0c53359ad 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -507,18 +507,10 @@ static void option_instat_callback(struct urb *urb);
#define VIATELECOM_VENDOR_ID 0x15eb
#define VIATELECOM_PRODUCT_CDS7 0x0001
-/* some devices interfaces need special handling due to a number of reasons */
-enum option_blacklist_reason {
- OPTION_BLACKLIST_NONE = 0,
- OPTION_BLACKLIST_SENDSETUP = 1,
- OPTION_BLACKLIST_RESERVED_IF = 2
-};
-
-#define MAX_BL_NUM 11
struct option_blacklist_info {
- /* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP */
+ /* bitmask of interface numbers blacklisted for send_setup */
const unsigned long sendsetup;
- /* bitfield of interface numbers for OPTION_BLACKLIST_RESERVED_IF */
+ /* bitmask of interface numbers that are reserved */
const unsigned long reserved;
};
@@ -1822,36 +1814,13 @@ struct option_private {
module_usb_serial_driver(serial_drivers, option_ids);
-static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason,
- const struct option_blacklist_info *blacklist)
-{
- unsigned long num;
- const unsigned long *intf_list;
-
- if (blacklist) {
- if (reason == OPTION_BLACKLIST_SENDSETUP)
- intf_list = &blacklist->sendsetup;
- else if (reason == OPTION_BLACKLIST_RESERVED_IF)
- intf_list = &blacklist->reserved;
- else {
- BUG_ON(reason);
- return false;
- }
-
- for_each_set_bit(num, intf_list, MAX_BL_NUM + 1) {
- if (num == ifnum)
- return true;
- }
- }
- return false;
-}
-
static int option_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
struct usb_interface_descriptor *iface_desc =
&serial->interface->cur_altsetting->desc;
struct usb_device_descriptor *dev_desc = &serial->dev->descriptor;
+ const struct option_blacklist_info *blacklist;
/* Never bind to the CD-Rom emulation interface */
if (iface_desc->bInterfaceClass == 0x08)
@@ -1862,10 +1831,9 @@ static int option_probe(struct usb_serial *serial,
* the same class/subclass/protocol as the serial interfaces. Look at
* the Windows driver .INF files for reserved interface numbers.
*/
- if (is_blacklisted(
- iface_desc->bInterfaceNumber,
- OPTION_BLACKLIST_RESERVED_IF,
- (const struct option_blacklist_info *) id->driver_info))
+ blacklist = (void *)id->driver_info;
+ if (blacklist && test_bit(iface_desc->bInterfaceNumber,
+ &blacklist->reserved))
return -ENODEV;
/*
* Don't bind network interface on Samsung GT-B3730, it is handled by
@@ -1876,8 +1844,8 @@ static int option_probe(struct usb_serial *serial,
iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA)
return -ENODEV;
- /* Store device id so we can use it during attach. */
- usb_set_serial_data(serial, (void *)id);
+ /* Store the blacklist info so we can use it during attach. */
+ usb_set_serial_data(serial, (void *)blacklist);
return 0;
}
@@ -1885,7 +1853,7 @@ static int option_probe(struct usb_serial *serial,
static int option_attach(struct usb_serial *serial)
{
struct usb_interface_descriptor *iface_desc;
- const struct usb_device_id *id;
+ const struct option_blacklist_info *blacklist;
struct usb_wwan_intf_private *data;
struct option_private *priv;
@@ -1899,16 +1867,16 @@ static int option_attach(struct usb_serial *serial)
return -ENOMEM;
}
- /* Retrieve device id stored at probe. */
- id = usb_get_serial_data(serial);
+ /* Retrieve blacklist info stored at probe. */
+ blacklist = usb_get_serial_data(serial);
+
iface_desc = &serial->interface->cur_altsetting->desc;
priv->bInterfaceNumber = iface_desc->bInterfaceNumber;
data->private = priv;
- if (!is_blacklisted(iface_desc->bInterfaceNumber,
- OPTION_BLACKLIST_SENDSETUP,
- (struct option_blacklist_info *)id->driver_info)) {
+ if (!blacklist || !test_bit(iface_desc->bInterfaceNumber,
+ &blacklist->sendsetup)) {
data->send_setup = option_send_setup;
}
spin_lock_init(&data->susp_lock);
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 1ae9d40f96bf..11f6f61c2381 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -218,7 +218,8 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
memset(desc, 0, sizeof(*desc));
desc->bDescriptorType = 0x29;
desc->bDescLength = 9;
- desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001));
+ desc->wHubCharacteristics = __constant_cpu_to_le16(
+ HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
desc->bNbrPorts = VHCI_NPORTS;
desc->u.hs.DeviceRemovable[0] = 0xff;
desc->u.hs.DeviceRemovable[1] = 0xff;
diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c
index fe8bc777ab88..aa5af817f31c 100644
--- a/drivers/usb/wusbcore/rh.c
+++ b/drivers/usb/wusbcore/rh.c
@@ -185,9 +185,9 @@ static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
descr->bDescriptorType = 0x29; /* HUB type */
descr->bNbrPorts = wusbhc->ports_max;
descr->wHubCharacteristics = cpu_to_le16(
- 0x00 /* All ports power at once */
+ HUB_CHAR_COMMON_LPSM /* All ports power at once */
| 0x00 /* not part of compound device */
- | 0x10 /* No overcurrent protection */
+ | HUB_CHAR_NO_OCPM /* No overcurrent protection */
| 0x00 /* 8 FS think time FIXME ?? */
| 0x00); /* No port indicators */
descr->bPwrOn2PwrGood = 0;
diff --git a/drivers/uwb/lc-dev.c b/drivers/uwb/lc-dev.c
index 8c7cfab5cee3..72033589db19 100644
--- a/drivers/uwb/lc-dev.c
+++ b/drivers/uwb/lc-dev.c
@@ -43,13 +43,6 @@ static inline void uwb_mac_addr_init(struct uwb_mac_addr *addr)
memset(&addr->data, 0xff, sizeof(addr->data));
}
-/* @returns !0 if a device @addr is a broadcast address */
-static inline int uwb_dev_addr_bcast(const struct uwb_dev_addr *addr)
-{
- static const struct uwb_dev_addr bcast = { .data = { 0xff, 0xff } };
- return !uwb_dev_addr_cmp(addr, &bcast);
-}
-
/*
* Add callback @new to be called when an event occurs in @rc.
*/