summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-17 09:36:43 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-17 09:36:43 -0800
commit17f8d2009367c3da82882f70ccbdca9f8c7b5f20 (patch)
tree80b8dc44dc22336501072cdc3b3e49d14c3867e1 /drivers
parent3ad7945754000d868ed86315d33085a914c422c1 (diff)
parentda87d45b195148d670ab995367d52aa9e8a9a1fa (diff)
Merge tag 'usb-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH: "Here is the "big" set of USB and Thunderbolt driver updates for 7.0-rc1. Overall more lines were removed than added, thanks to dropping the obsolete isp1362 USB host controller driver, always a nice change. Other than that, nothing major happening here, highlights are: - lots of dwc3 driver updates and new hardware support added - usb gadget function driver updates - usb phy driver updates - typec driver updates and additions - USB rust binding updates for syntax and formatting changes - more usb serial device ids added - other smaller USB core and driver updates and additions All of these have been in linux-next for a long time, with no reported problems" * tag 'usb-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (77 commits) usb: typec: ucsi: Add Thunderbolt alternate mode support usb: typec: hd3ss3220: Check if regulator needs to be switched usb: phy: tegra: parametrize PORTSC1 register offset usb: phy: tegra: parametrize HSIC PTS value usb: phy: tegra: return error value from utmi_wait_register usb: phy: tegra: cosmetic fixes dt-bindings: usb: renesas,usbhs: Add RZ/G3E SoC support usb: dwc2: fix resume failure if dr_mode is host usb: cdns3: fix role switching during resume usb: dwc3: gadget: Move vbus draw to workqueue context USB: serial: option: add Telit FN920C04 RNDIS compositions usb: dwc3: Log dwc3 address in traces usb: gadget: tegra-xudc: Add handling for BLCG_COREPLL_PWRDN usb: phy: tegra: add HSIC support usb: phy: tegra: use phy type directly usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi usb: typec: ucsi: Support mode selection to activate altmodes usb: typec: Introduce mode_selection bit usb: typec: Implement mode selection usb: typec: Expose alternate mode priority via sysfs ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/chrome/cros_ec_typec.c1
-rw-r--r--drivers/thunderbolt/path.c2
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/cdns3/core.c2
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c9
-rw-r--r--drivers/usb/chipidea/udc.c7
-rw-r--r--drivers/usb/core/hcd.c4
-rw-r--r--drivers/usb/dwc2/core.c1
-rw-r--r--drivers/usb/dwc3/Kconfig11
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/core.c215
-rw-r--r--drivers/usb/dwc3/core.h14
-rw-r--r--drivers/usb/dwc3/debugfs.c44
-rw-r--r--drivers/usb/dwc3/drd.c86
-rw-r--r--drivers/usb/dwc3/dwc3-google.c626
-rw-r--r--drivers/usb/dwc3/dwc3-imx8mp.c47
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c71
-rw-r--r--drivers/usb/dwc3/ep0.c22
-rw-r--r--drivers/usb/dwc3/gadget.c172
-rw-r--r--drivers/usb/dwc3/gadget.h4
-rw-r--r--drivers/usb/dwc3/io.h11
-rw-r--r--drivers/usb/dwc3/trace.h88
-rw-r--r--drivers/usb/dwc3/ulpi.c10
-rw-r--r--drivers/usb/fotg210/fotg210-hcd.c6
-rw-r--r--drivers/usb/gadget/configfs.c24
-rw-r--r--drivers/usb/gadget/function/f_acm.c2
-rw-r--r--drivers/usb/gadget/function/f_fs.c26
-rw-r--r--drivers/usb/gadget/function/f_hid.c2
-rw-r--r--drivers/usb/gadget/function/f_loopback.c2
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c6
-rw-r--r--drivers/usb/gadget/function/f_midi.c112
-rw-r--r--drivers/usb/gadget/function/f_midi2.c10
-rw-r--r--drivers/usb/gadget/function/f_ncm.c128
-rw-r--r--drivers/usb/gadget/function/f_obex.c2
-rw-r--r--drivers/usb/gadget/function/f_phonet.c2
-rw-r--r--drivers/usb/gadget/function/f_printer.c2
-rw-r--r--drivers/usb/gadget/function/f_serial.c2
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c54
-rw-r--r--drivers/usb/gadget/function/f_tcm.c5
-rw-r--r--drivers/usb/gadget/function/f_uac1.c2
-rw-r--r--drivers/usb/gadget/function/f_uac1_legacy.c2
-rw-r--r--drivers/usb/gadget/function/f_uac2.c2
-rw-r--r--drivers/usb/gadget/function/g_zero.h1
-rw-r--r--drivers/usb/gadget/function/u_ether.c45
-rw-r--r--drivers/usb/gadget/function/u_ether.h30
-rw-r--r--drivers/usb/gadget/function/u_ether_configfs.h179
-rw-r--r--drivers/usb/gadget/function/u_midi.h2
-rw-r--r--drivers/usb/gadget/function/u_ncm.h4
-rw-r--r--drivers/usb/gadget/function/uvc_configfs.c36
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c30
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/vhub.h1
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c4
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c12
-rw-r--r--drivers/usb/host/Kconfig14
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-hcd.c8
-rw-r--r--drivers/usb/host/isp1362-hcd.c2769
-rw-r--r--drivers/usb/host/isp1362.h914
-rw-r--r--drivers/usb/host/ohci-hcd.c3
-rw-r--r--drivers/usb/host/uhci-hcd.c5
-rw-r--r--drivers/usb/host/xhci-dbgcap.c259
-rw-r--r--drivers/usb/host/xhci-dbgcap.h39
-rw-r--r--drivers/usb/host/xhci-tegra.c21
-rw-r--r--drivers/usb/misc/onboard_usb_dev.h8
-rw-r--r--drivers/usb/phy/phy-generic.c74
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c297
-rw-r--r--drivers/usb/serial/option.c6
-rw-r--r--drivers/usb/typec/Makefile2
-rw-r--r--drivers/usb/typec/altmodes/displayport.c6
-rw-r--r--drivers/usb/typec/altmodes/thunderbolt.c2
-rw-r--r--drivers/usb/typec/bus.c25
-rw-r--r--drivers/usb/typec/bus.h6
-rw-r--r--drivers/usb/typec/class.c136
-rw-r--r--drivers/usb/typec/class.h3
-rw-r--r--drivers/usb/typec/hd3ss3220.c30
-rw-r--r--drivers/usb/typec/mode_selection.c283
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c2
-rw-r--r--drivers/usb/typec/ucsi/Kconfig1
-rw-r--r--drivers/usb/typec/ucsi/Makefile4
-rw-r--r--drivers/usb/typec/ucsi/cros_ec_ucsi.c22
-rw-r--r--drivers/usb/typec/ucsi/psy.c54
-rw-r--r--drivers/usb/typec/ucsi/thunderbolt.c212
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c30
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h24
-rw-r--r--drivers/usb/usbip/stub_tx.c4
85 files changed, 2940 insertions, 4508 deletions
diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index b712bcff6fb2..c0806c562bb9 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -491,6 +491,7 @@ static int cros_typec_init_ports(struct cros_typec_data *typec)
cap->driver_data = cros_port;
cap->ops = &cros_typec_usb_mode_ops;
+ cap->no_mode_control = !typec->ap_driven_altmode;
cros_port->port = typec_register_port(dev, cap);
if (IS_ERR(cros_port->port)) {
diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
index f9b11dadfbdd..50659bd55d7b 100644
--- a/drivers/thunderbolt/path.c
+++ b/drivers/thunderbolt/path.c
@@ -586,7 +586,7 @@ int tb_path_activate(struct tb_path *path)
tb_dbg(path->tb, "%s path activation complete\n", path->name);
return 0;
err:
- tb_WARN(path->tb, "%s path activation failed\n", path->name);
+ tb_warn(path->tb, "%s path activation failed: %d\n", path->name, res);
return res;
}
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 949eca0adebe..6f3c86149887 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -30,7 +30,6 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_FHCI_HCD) += host/
obj-$(CONFIG_USB_XHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
-obj-$(CONFIG_USB_ISP1362_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/
obj-$(CONFIG_USB_FSL_USB2) += host/
obj-$(CONFIG_USB_FOTG210_HCD) += host/
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index 1243a5cea91b..f0e32227c0b7 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -551,7 +551,7 @@ int cdns_resume(struct cdns *cdns)
}
}
- if (cdns->roles[cdns->role]->resume)
+ if (!role_changed && cdns->roles[cdns->role]->resume)
cdns->roles[cdns->role]->resume(cdns, power_lost);
return 0;
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index d4ee9e16332f..56d2ba824a0b 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -385,6 +385,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
+ const char *irq_name;
imx_platform_flag = of_device_get_match_data(&pdev->dev);
@@ -525,10 +526,16 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
data->wakeup_irq = platform_get_irq_optional(pdev, 1);
if (data->wakeup_irq > 0) {
+ irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", pdata.name);
+ if (!irq_name) {
+ dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n");
+ goto err_clk;
+ }
+
ret = devm_request_threaded_irq(dev, data->wakeup_irq,
NULL, ci_wakeup_irq_handler,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
- pdata.name, data);
+ irq_name, data);
if (ret)
goto err_clk;
}
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 64a421ae0f05..c8d931d9d433 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -931,6 +931,13 @@ __acquires(hwep->lock)
list_del_init(&hwreq->queue);
hwreq->req.status = -ESHUTDOWN;
+ /* Unmap DMA and clean up bounce buffers before giving back */
+ usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent,
+ &hwreq->req, hwep->dir);
+
+ if (hwreq->sgt.sgl)
+ sglist_do_debounce(hwreq, false);
+
if (hwreq->req.complete != NULL) {
spin_unlock(hwep->lock);
usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 24feb0de1c00..2d99a59d9f3f 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -77,10 +77,6 @@
/*-------------------------------------------------------------------------*/
-/* Keep track of which host controller drivers are loaded */
-unsigned long usb_hcds_loaded;
-EXPORT_SYMBOL_GPL(usb_hcds_loaded);
-
/* host controllers we manage */
DEFINE_IDR (usb_bus_idr);
EXPORT_SYMBOL_GPL (usb_bus_idr);
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index c3d24312db0f..f375c5185bfe 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -578,6 +578,7 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{
switch (hsotg->dr_mode) {
case USB_DR_MODE_HOST:
+ dwc2_force_mode(hsotg, true);
/*
* NOTE: This is required for some rockchip soc based
* platforms on their host-only dwc2.
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index bf3e04635131..240b15bc52cb 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -211,4 +211,15 @@ config USB_DWC3_APPLE
mode on these machines.
Say 'Y' or 'M' if you have such device.
+config USB_DWC3_GOOGLE
+ tristate "Google Platform"
+ help
+ Support the DesignWare Core USB3 IP found on Google Tensor SoCs,
+ starting with the G5 generation (Laguna). This driver includes
+ support for hibernation in host mode.
+ Say 'Y' or 'M' if you have one such device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dwc3-google.ko.
+
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 89d46ab50068..073bef5309b5 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
obj-$(CONFIG_USB_DWC3_GENERIC_PLAT) += dwc3-generic-plat.o
+obj-$(CONFIG_USB_DWC3_GOOGLE) += dwc3-google.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 93fd5fdf95cb..161a4d58b2ce 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -114,23 +114,23 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
int i;
for (i = 0; i < dwc->num_usb3_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i));
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i));
if (enable && !dwc->dis_u3_susphy_quirk)
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
else
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg);
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg);
}
for (i = 0; i < dwc->num_usb2_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
if (enable && !dwc->dis_u2_susphy_quirk)
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
else
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
}
}
EXPORT_SYMBOL_GPL(dwc3_enable_susphy);
@@ -140,7 +140,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
unsigned int hw_mode;
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
/*
* For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and
@@ -155,10 +155,10 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy)
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(mode);
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
dwc->current_dr_role = mode;
- trace_dwc3_set_prtcap(mode);
+ trace_dwc3_set_prtcap(dwc, mode);
}
EXPORT_SYMBOL_GPL(dwc3_set_prtcap);
@@ -216,9 +216,9 @@ static void __dwc3_set_mode(struct work_struct *work)
if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
reg |= DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
/*
* Wait for internal clocks to synchronized. DWC_usb31 and
@@ -228,9 +228,9 @@ static void __dwc3_set_mode(struct work_struct *work)
*/
msleep(100);
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_CORESOFTRESET;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
}
spin_lock_irqsave(&dwc->lock, flags);
@@ -254,9 +254,9 @@ static void __dwc3_set_mode(struct work_struct *work)
phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);
if (dwc->dis_split_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg = dwc3_readl(dwc, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_SPLITDISABLE;
- dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ dwc3_writel(dwc, DWC3_GUCTL3, reg);
}
}
break;
@@ -306,11 +306,11 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
struct dwc3 *dwc = dep->dwc;
u32 reg;
- dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE,
- DWC3_GDBGFIFOSPACE_NUM(dep->number) |
- DWC3_GDBGFIFOSPACE_TYPE(type));
+ dwc3_writel(dwc, DWC3_GDBGFIFOSPACE,
+ DWC3_GDBGFIFOSPACE_NUM(dep->number) |
+ DWC3_GDBGFIFOSPACE_TYPE(type));
- reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE);
+ reg = dwc3_readl(dwc, DWC3_GDBGFIFOSPACE);
return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg);
}
@@ -332,7 +332,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST)
return 0;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg |= DWC3_DCTL_CSFTRST;
reg &= ~DWC3_DCTL_RUN_STOP;
dwc3_gadget_dctl_write_safe(dwc, reg);
@@ -347,7 +347,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
retries = 10;
do {
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
@@ -387,12 +387,12 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
if (dwc->fladj == 0)
return;
- reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ reg = dwc3_readl(dwc, DWC3_GFLADJ);
dft = reg & DWC3_GFLADJ_30MHZ_MASK;
if (dft != dwc->fladj) {
reg &= ~DWC3_GFLADJ_30MHZ_MASK;
reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
- dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ dwc3_writel(dwc, DWC3_GFLADJ, reg);
}
}
@@ -424,10 +424,10 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
return;
}
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+ reg = dwc3_readl(dwc, DWC3_GUCTL);
reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period);
- dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+ dwc3_writel(dwc, DWC3_GUCTL, reg);
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return;
@@ -455,7 +455,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
*/
decr = 480000000 / rate;
- reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+ reg = dwc3_readl(dwc, DWC3_GFLADJ);
reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK
& ~DWC3_GFLADJ_240MHZDECR
& ~DWC3_GFLADJ_240MHZDECR_PLS1;
@@ -466,7 +466,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc)
if (dwc->gfladj_refclk_lpm_sel)
reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
- dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+ dwc3_writel(dwc, DWC3_GFLADJ, reg);
}
/**
@@ -569,16 +569,16 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
evt = dwc->ev_buf;
evt->lpos = 0;
- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
- lower_32_bits(evt->dma));
- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
- upper_32_bits(evt->dma));
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
- DWC3_GEVNTSIZ_SIZE(evt->length));
+ dwc3_writel(dwc, DWC3_GEVNTADRLO(0),
+ lower_32_bits(evt->dma));
+ dwc3_writel(dwc, DWC3_GEVNTADRHI(0),
+ upper_32_bits(evt->dma));
+ dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_SIZE(evt->length));
/* Clear any stale event */
- reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg);
+ reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg);
return 0;
}
@@ -593,7 +593,7 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
* Exynos platforms may not be able to access event buffer if the
* controller failed to halt on dwc3_core_exit().
*/
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
if (!(reg & DWC3_DSTS_DEVCTRLHLT))
return;
@@ -601,14 +601,14 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
evt->lpos = 0;
- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
+ dwc3_writel(dwc, DWC3_GEVNTADRLO(0), 0);
+ dwc3_writel(dwc, DWC3_GEVNTADRHI(0), 0);
+ dwc3_writel(dwc, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
| DWC3_GEVNTSIZ_SIZE(0));
/* Clear any stale event */
- reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg);
+ reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg);
}
static void dwc3_core_num_eps(struct dwc3 *dwc)
@@ -622,18 +622,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
{
struct dwc3_hwparams *parms = &dwc->hwparams;
- parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0);
- parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1);
- parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2);
- parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3);
- parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4);
- parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5);
- parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
- parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7);
- parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
+ parms->hwparams0 = dwc3_readl(dwc, DWC3_GHWPARAMS0);
+ parms->hwparams1 = dwc3_readl(dwc, DWC3_GHWPARAMS1);
+ parms->hwparams2 = dwc3_readl(dwc, DWC3_GHWPARAMS2);
+ parms->hwparams3 = dwc3_readl(dwc, DWC3_GHWPARAMS3);
+ parms->hwparams4 = dwc3_readl(dwc, DWC3_GHWPARAMS4);
+ parms->hwparams5 = dwc3_readl(dwc, DWC3_GHWPARAMS5);
+ parms->hwparams6 = dwc3_readl(dwc, DWC3_GHWPARAMS6);
+ parms->hwparams7 = dwc3_readl(dwc, DWC3_GHWPARAMS7);
+ parms->hwparams8 = dwc3_readl(dwc, DWC3_GHWPARAMS8);
if (DWC3_IP_IS(DWC32))
- parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
+ parms->hwparams9 = dwc3_readl(dwc, DWC3_GHWPARAMS9);
}
static void dwc3_config_soc_bus(struct dwc3 *dwc)
@@ -641,10 +641,10 @@ static void dwc3_config_soc_bus(struct dwc3 *dwc)
if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ reg = dwc3_readl(dwc, DWC3_GSBUSCFG0);
reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
- dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
+ dwc3_writel(dwc, DWC3_GSBUSCFG0, reg);
}
}
@@ -668,7 +668,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index));
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(index));
/*
* Make sure UX_EXIT_PX is cleared as that causes issues with some
@@ -706,7 +706,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index)
if (dwc->dis_del_phy_power_chg_quirk)
reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg);
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(index), reg);
return 0;
}
@@ -715,7 +715,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index));
/* Select the HS PHY interface */
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
@@ -727,7 +727,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
} else if (dwc->hsphy_interface &&
!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
} else {
/* Relying on default value. */
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
@@ -777,7 +777,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index)
if (dwc->ulpi_ext_vbus_drv)
reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
return 0;
}
@@ -991,7 +991,7 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+ reg = dwc3_readl(dwc, DWC3_GSNPSID);
dwc->ip = DWC3_GSNPS_ID(reg);
if (dwc->ip == DWC4_IP)
dwc->ip = DWC32_IP;
@@ -1000,8 +1000,8 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
if (DWC3_IP_IS(DWC3)) {
dwc->revision = reg;
} else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
- dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
- dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
+ dwc->revision = dwc3_readl(dwc, DWC3_VER_NUMBER);
+ dwc->version_type = dwc3_readl(dwc, DWC3_VER_TYPE);
} else {
return false;
}
@@ -1015,7 +1015,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
unsigned int hw_mode;
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
power_opt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
@@ -1093,7 +1093,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
if (DWC3_VER_IS_PRIOR(DWC3, 190A))
reg |= DWC3_GCTL_U2RSTECN;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
}
static int dwc3_core_get_phy(struct dwc3 *dwc);
@@ -1113,7 +1113,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
int ret;
int i;
- cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ cfg = dwc3_readl(dwc, DWC3_GSBUSCFG0);
/*
* Handle property "snps,incr-burst-type-adjustment".
@@ -1188,7 +1188,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
break;
}
- dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
+ dwc3_writel(dwc, DWC3_GSBUSCFG0, cfg);
}
static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
@@ -1213,12 +1213,12 @@ static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
* (3x or more) to be within the requirement.
*/
scale = DIV_ROUND_UP(clk_get_rate(dwc->susp_clk), 16000);
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
if ((reg & DWC3_GCTL_PWRDNSCALE_MASK) < DWC3_GCTL_PWRDNSCALE(scale) ||
(reg & DWC3_GCTL_PWRDNSCALE_MASK) > DWC3_GCTL_PWRDNSCALE(scale*3)) {
reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
reg |= DWC3_GCTL_PWRDNSCALE(scale);
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
}
}
@@ -1241,7 +1241,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
tx_maxburst = dwc->tx_max_burst_prd;
if (rx_thr_num && rx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
reg |= DWC31_RXTHRNUMPKTSEL_PRD;
reg &= ~DWC31_RXTHRNUMPKT_PRD(~0);
@@ -1250,11 +1250,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0);
reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
}
if (tx_thr_num && tx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
reg |= DWC31_TXTHRNUMPKTSEL_PRD;
reg &= ~DWC31_TXTHRNUMPKT_PRD(~0);
@@ -1263,7 +1263,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0);
reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
}
}
@@ -1274,7 +1274,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
if (DWC3_IP_IS(DWC3)) {
if (rx_thr_num && rx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
reg |= DWC3_GRXTHRCFG_PKTCNTSEL;
reg &= ~DWC3_GRXTHRCFG_RXPKTCNT(~0);
@@ -1283,11 +1283,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC3_GRXTHRCFG_MAXRXBURSTSIZE(~0);
reg |= DWC3_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
}
if (tx_thr_num && tx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
reg |= DWC3_GTXTHRCFG_PKTCNTSEL;
reg &= ~DWC3_GTXTHRCFG_TXPKTCNT(~0);
@@ -1296,11 +1296,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC3_GTXTHRCFG_MAXTXBURSTSIZE(~0);
reg |= DWC3_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
}
} else {
if (rx_thr_num && rx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
reg |= DWC31_GRXTHRCFG_PKTCNTSEL;
reg &= ~DWC31_GRXTHRCFG_RXPKTCNT(~0);
@@ -1309,11 +1309,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC31_GRXTHRCFG_MAXRXBURSTSIZE(~0);
reg |= DWC31_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
}
if (tx_thr_num && tx_maxburst) {
- reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GTXTHRCFG);
reg |= DWC31_GTXTHRCFG_PKTCNTSEL;
reg &= ~DWC31_GTXTHRCFG_TXPKTCNT(~0);
@@ -1322,7 +1322,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc)
reg &= ~DWC31_GTXTHRCFG_MAXTXBURSTSIZE(~0);
reg |= DWC31_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst);
- dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GTXTHRCFG, reg);
}
}
}
@@ -1345,7 +1345,7 @@ int dwc3_core_init(struct dwc3 *dwc)
* Write Linux Version Code to our GUID register so it's easy to figure
* out which kernel version a bug was found.
*/
- dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
+ dwc3_writel(dwc, DWC3_GUID, LINUX_VERSION_CODE);
ret = dwc3_phy_setup(dwc);
if (ret)
@@ -1410,9 +1410,9 @@ int dwc3_core_init(struct dwc3 *dwc)
* DWC_usb31 controller.
*/
if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg = dwc3_readl(dwc, DWC3_GUCTL2);
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
- dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ dwc3_writel(dwc, DWC3_GUCTL2, reg);
}
/*
@@ -1425,9 +1425,9 @@ int dwc3_core_init(struct dwc3 *dwc)
* setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0.
*/
if (DWC3_VER_IS(DWC3, 320A)) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg = dwc3_readl(dwc, DWC3_GUCTL2);
reg &= ~DWC3_GUCTL2_LC_TIMER;
- dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ dwc3_writel(dwc, DWC3_GUCTL2, reg);
}
/*
@@ -1440,13 +1440,13 @@ int dwc3_core_init(struct dwc3 *dwc)
* legacy ULPI PHYs.
*/
if (dwc->resume_hs_terminations) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg = dwc3_readl(dwc, DWC3_GUCTL1);
reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST;
- dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ dwc3_writel(dwc, DWC3_GUCTL1, reg);
}
if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg = dwc3_readl(dwc, DWC3_GUCTL1);
/*
* Enable hardware control of sending remote wakeup
@@ -1481,7 +1481,7 @@ int dwc3_core_init(struct dwc3 *dwc)
reg &= ~DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
}
- dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ dwc3_writel(dwc, DWC3_GUCTL1, reg);
}
dwc3_config_threshold(dwc);
@@ -1492,9 +1492,9 @@ int dwc3_core_init(struct dwc3 *dwc)
int i;
for (i = 0; i < dwc->num_usb3_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_LLUCTL(i));
+ reg = dwc3_readl(dwc, DWC3_LLUCTL(i));
reg |= DWC3_LLUCTL_FORCE_GEN1;
- dwc3_writel(dwc->regs, DWC3_LLUCTL(i), reg);
+ dwc3_writel(dwc, DWC3_LLUCTL(i), reg);
}
}
@@ -1513,9 +1513,9 @@ int dwc3_core_init(struct dwc3 *dwc)
* function is available only from version 1.70a.
*/
if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg = dwc3_readl(dwc, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE;
- dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ dwc3_writel(dwc, DWC3_GUCTL3, reg);
}
return 0;
@@ -2155,6 +2155,20 @@ static int dwc3_get_num_ports(struct dwc3 *dwc)
return 0;
}
+static void dwc3_vbus_draw_work(struct work_struct *work)
+{
+ struct dwc3 *dwc = container_of(work, struct dwc3, vbus_draw_work);
+ union power_supply_propval val = {0};
+ int ret;
+
+ val.intval = 1000 * (dwc->current_limit);
+ ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
+
+ if (ret < 0)
+ dev_dbg(dwc->dev, "Error (%d) setting vbus draw (%d mA)\n",
+ ret, dwc->current_limit);
+}
+
static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
{
struct power_supply *usb_psy;
@@ -2169,6 +2183,7 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
if (!usb_psy)
return ERR_PTR(-EPROBE_DEFER);
+ INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work);
return usb_psy;
}
@@ -2395,8 +2410,10 @@ void dwc3_core_remove(struct dwc3 *dwc)
dwc3_free_event_buffers(dwc);
- if (dwc->usb_psy)
+ if (dwc->usb_psy) {
+ cancel_work_sync(&dwc->vbus_draw_work);
power_supply_put(dwc->usb_psy);
+ }
}
EXPORT_SYMBOL_GPL(dwc3_core_remove);
@@ -2439,9 +2456,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
int ret;
if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) {
- dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) &
+ dwc->susphy_state = (dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)) &
DWC3_GUSB2PHYCFG_SUSPHY) ||
- (dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)) &
+ (dwc3_readl(dwc, DWC3_GUSB3PIPECTL(0)) &
DWC3_GUSB3PIPECTL_SUSPHY);
/*
* TI AM62 platform requires SUSPHY to be
@@ -2471,10 +2488,10 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
if (dwc->dis_u2_susphy_quirk ||
dwc->dis_enblslpm_quirk) {
for (i = 0; i < dwc->num_usb2_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
reg |= DWC3_GUSB2PHYCFG_ENBLSLPM |
DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
}
/* Give some time for USB2 PHY to suspend */
@@ -2534,14 +2551,14 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
}
/* Restore GUSB2PHYCFG bits that were modified in suspend */
for (i = 0; i < dwc->num_usb2_ports; i++) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i));
if (dwc->dis_u2_susphy_quirk)
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg);
}
for (i = 0; i < dwc->num_usb2_ports; i++)
@@ -2723,9 +2740,9 @@ void dwc3_pm_complete(struct dwc3 *dwc)
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST &&
dwc->dis_split_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg = dwc3_readl(dwc, DWC3_GUCTL3);
reg |= DWC3_GUCTL3_SPLITDISABLE;
- dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ dwc3_writel(dwc, DWC3_GUCTL3, reg);
}
}
EXPORT_SYMBOL_GPL(dwc3_pm_complete);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 45757169b672..a35b3db1f9f3 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -165,10 +165,10 @@
#define DWC3_DCFG1 0xc740 /* DWC_usb32 only */
#define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10))
-#define DWC3_DEPCMDPAR2 0x00
-#define DWC3_DEPCMDPAR1 0x04
-#define DWC3_DEPCMDPAR0 0x08
-#define DWC3_DEPCMD 0x0c
+#define DWC3_DEPCMDPAR2(n) (DWC3_DEP_BASE(n) + 0x00)
+#define DWC3_DEPCMDPAR1(n) (DWC3_DEP_BASE(n) + 0x04)
+#define DWC3_DEPCMDPAR0(n) (DWC3_DEP_BASE(n) + 0x08)
+#define DWC3_DEPCMD(n) (DWC3_DEP_BASE(n) + 0x0c)
#define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4))
@@ -749,8 +749,6 @@ struct dwc3_ep {
struct list_head pending_list;
struct list_head started_list;
- void __iomem *regs;
-
struct dwc3_trb *trb_pool;
dma_addr_t trb_pool_dma;
struct dwc3 *dwc;
@@ -1060,6 +1058,8 @@ struct dwc3_glue_ops {
* @role_switch_default_mode: default operation mode of controller while
* usb role is USB_ROLE_NONE.
* @usb_psy: pointer to power supply interface.
+ * @vbus_draw_work: Work to set the vbus drawing limit
+ * @current_limit: How much current to draw from vbus, in milliAmperes.
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to array of USB2 PHYs
@@ -1246,6 +1246,8 @@ struct dwc3 {
enum usb_dr_mode role_switch_default_mode;
struct power_supply *usb_psy;
+ struct work_struct vbus_draw_work;
+ unsigned int current_limit;
u32 fladj;
u32 ref_clk_per;
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index d18bf5e32cc8..ffb1101f55c7 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -36,23 +36,19 @@
#define dump_ep_register_set(n) \
{ \
.name = "DEPCMDPAR2("__stringify(n)")", \
- .offset = DWC3_DEP_BASE(n) + \
- DWC3_DEPCMDPAR2, \
+ .offset = DWC3_DEPCMDPAR2(n), \
}, \
{ \
.name = "DEPCMDPAR1("__stringify(n)")", \
- .offset = DWC3_DEP_BASE(n) + \
- DWC3_DEPCMDPAR1, \
+ .offset = DWC3_DEPCMDPAR1(n), \
}, \
{ \
.name = "DEPCMDPAR0("__stringify(n)")", \
- .offset = DWC3_DEP_BASE(n) + \
- DWC3_DEPCMDPAR0, \
+ .offset = DWC3_DEPCMDPAR0(n), \
}, \
{ \
.name = "DEPCMD("__stringify(n)")", \
- .offset = DWC3_DEP_BASE(n) + \
- DWC3_DEPCMD, \
+ .offset = DWC3_DEPCMD(n), \
}
@@ -300,14 +296,14 @@ static void dwc3_host_lsp(struct seq_file *s)
reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel);
- dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
- val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
+ dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
+ val = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val);
if (dbc_enabled && sel < 256) {
reg |= DWC3_GDBGLSPMUX_ENDBC;
- dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
- val = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
+ dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
+ val = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val);
}
}
@@ -320,8 +316,8 @@ static void dwc3_gadget_lsp(struct seq_file *s)
for (i = 0; i < 16; i++) {
reg = DWC3_GDBGLSPMUX_DEVSELECT(i);
- dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
- reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP);
+ dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
+ reg = dwc3_readl(dwc, DWC3_GDBGLSP);
seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg);
}
}
@@ -339,7 +335,7 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused)
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_GSTS);
+ reg = dwc3_readl(dwc, DWC3_GSTS);
current_mode = DWC3_GSTS_CURMOD(reg);
switch (current_mode) {
@@ -410,7 +406,7 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
spin_unlock_irqrestore(&dwc->lock, flags);
mode = DWC3_GCTL_PRTCAP(reg);
@@ -482,7 +478,7 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= DWC3_DCTL_TSTCTRL_MASK;
reg >>= 1;
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -581,7 +577,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_GSTS);
+ reg = dwc3_readl(dwc, DWC3_GSTS);
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
seq_puts(s, "Not available\n");
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -589,7 +585,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
return 0;
}
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
state = DWC3_DSTS_USBLNKST(reg);
speed = reg & DWC3_DSTS_CONNECTSPD;
@@ -643,14 +639,14 @@ static ssize_t dwc3_link_state_write(struct file *file,
return ret;
spin_lock_irqsave(&dwc->lock, flags);
- reg = dwc3_readl(dwc->regs, DWC3_GSTS);
+ reg = dwc3_readl(dwc, DWC3_GSTS);
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
spin_unlock_irqrestore(&dwc->lock, flags);
pm_runtime_put_sync(dwc->dev);
return -EINVAL;
}
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
if (speed < DWC3_DSTS_SUPERSPEED &&
@@ -946,10 +942,10 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused)
spin_lock_irqsave(&dwc->lock, flags);
reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number);
- dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg);
+ dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg);
- lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0);
- upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1);
+ lower_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO0);
+ upper_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO1);
ep_info = ((u64)upper_32_bits << 32) | lower_32_bits;
seq_printf(s, "0x%016llx\n", ep_info);
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 589bbeb27454..f3e37d383627 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -18,25 +18,25 @@
static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
{
- u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+ u32 reg = dwc3_readl(dwc, DWC3_OEVTEN);
reg &= ~(disable_mask);
- dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+ dwc3_writel(dwc, DWC3_OEVTEN, reg);
}
static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
{
- u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+ u32 reg = dwc3_readl(dwc, DWC3_OEVTEN);
reg |= (enable_mask);
- dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+ dwc3_writel(dwc, DWC3_OEVTEN, reg);
}
static void dwc3_otg_clear_events(struct dwc3 *dwc)
{
- u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ u32 reg = dwc3_readl(dwc, DWC3_OEVT);
- dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+ dwc3_writel(dwc, DWC3_OEVTEN, reg);
}
#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \
@@ -72,18 +72,18 @@ static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
struct dwc3 *dwc = _dwc;
irqreturn_t ret = IRQ_NONE;
- reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ reg = dwc3_readl(dwc, DWC3_OEVT);
if (reg) {
/* ignore non OTG events, we can't disable them in OEVTEN */
if (!(reg & DWC3_OTG_ALL_EVENTS)) {
- dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ dwc3_writel(dwc, DWC3_OEVT, reg);
return IRQ_NONE;
}
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
!(reg & DWC3_OEVT_DEVICEMODE))
dwc->otg_restart_host = true;
- dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ dwc3_writel(dwc, DWC3_OEVT, reg);
ret = IRQ_WAKE_THREAD;
}
@@ -100,23 +100,23 @@ static void dwc3_otgregs_init(struct dwc3 *dwc)
* the signal outputs sent to the PHY, the OTG FSM logic of the
* core and also the resets to the VBUS filters inside the core.
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg = dwc3_readl(dwc, DWC3_OCFG);
reg |= DWC3_OCFG_SFTRSTMASK;
- dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ dwc3_writel(dwc, DWC3_OCFG, reg);
/* Disable hibernation for simplicity */
- reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg = dwc3_readl(dwc, DWC3_GCTL);
reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
- dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_writel(dwc, DWC3_GCTL, reg);
/*
* Initialize OTG registers as per
* Figure 11-4 OTG Driver Overall Programming Flow
*/
/* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */
- reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg = dwc3_readl(dwc, DWC3_OCFG);
reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
- dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ dwc3_writel(dwc, DWC3_OCFG, reg);
/* OEVT = FFFF */
dwc3_otg_clear_events(dwc);
/* OEVTEN = 0 */
@@ -127,11 +127,11 @@ static void dwc3_otgregs_init(struct dwc3 *dwc)
* OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0,
* OCTL.HNPReq = 0
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg |= DWC3_OCTL_PERIMODE;
reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN |
DWC3_OCTL_HNPREQ);
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
}
static int dwc3_otg_get_irq(struct dwc3 *dwc)
@@ -175,9 +175,9 @@ void dwc3_otg_init(struct dwc3 *dwc)
/* GCTL.PrtCapDir=2'b11 */
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true);
/* GUSB2PHYCFG0.SusPHY=0 */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
/* Initialize OTG registers */
dwc3_otgregs_init(dwc);
@@ -203,17 +203,17 @@ void dwc3_otg_host_init(struct dwc3 *dwc)
* OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0,
* OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE |
DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
/*
* OCFG.DisPrtPwrCutoff = 0/1
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg = dwc3_readl(dwc, DWC3_OCFG);
reg &= ~DWC3_OCFG_DISPWRCUTTOFF;
- dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ dwc3_writel(dwc, DWC3_OCFG, reg);
/*
* OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP
@@ -229,15 +229,15 @@ void dwc3_otg_host_init(struct dwc3 *dwc)
/* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */
if (!dwc->dis_u2_susphy_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
/* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg |= DWC3_OCTL_PRTPWRCTL;
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
}
/* should be called after Host controller driver is stopped */
@@ -258,9 +258,9 @@ static void dwc3_otg_host_exit(struct dwc3 *dwc)
*/
/* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL);
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
}
/* should be called before the gadget controller driver is started */
@@ -274,27 +274,27 @@ static void dwc3_otg_device_init(struct dwc3 *dwc)
* OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
* but we keep them 0 for simple dual-role operation.
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg = dwc3_readl(dwc, DWC3_OCFG);
/* OCFG.OTGSftRstMsk = 0/1 */
reg |= DWC3_OCFG_SFTRSTMASK;
- dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ dwc3_writel(dwc, DWC3_OCFG, reg);
/*
* OCTL.PeriMode = 1
* OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0
* OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
*/
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg |= DWC3_OCTL_PERIMODE;
reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ |
DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
/* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */
dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN);
/* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */
if (!dwc->dis_u2_susphy_quirk) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
/* GCTL.GblHibernationEn = 0. Already 0. */
}
@@ -319,10 +319,10 @@ static void dwc3_otg_device_exit(struct dwc3 *dwc)
DWC3_OEVTEN_BDEVBHOSTENDEN);
/* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */
- reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg = dwc3_readl(dwc, DWC3_OCTL);
reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ);
reg |= DWC3_OCTL_PERIMODE;
- dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ dwc3_writel(dwc, DWC3_OCTL, reg);
}
void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
@@ -341,7 +341,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
return;
if (!ignore_idstatus) {
- reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ reg = dwc3_readl(dwc, DWC3_OSTS);
id = !!(reg & DWC3_OSTS_CONIDSTS);
dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE :
@@ -381,6 +381,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
dwc3_otgregs_init(dwc);
dwc3_otg_host_init(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_pre_set_role(dwc, USB_ROLE_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
dev_err(dwc->dev, "failed to initialize host\n");
@@ -406,6 +407,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
otg_set_vbus(dwc->usb2_phy->otg, false);
if (dwc->usb2_generic_phy[0])
phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
+ dwc3_pre_set_role(dwc, USB_ROLE_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
@@ -433,10 +435,12 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
+ u32 mode = event ? DWC3_GCTL_PRTCAP_HOST : DWC3_GCTL_PRTCAP_DEVICE;
+ enum usb_role role = mode == DWC3_GCTL_PRTCAP_HOST ?
+ USB_ROLE_HOST : USB_ROLE_DEVICE;
- dwc3_set_mode(dwc, event ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ dwc3_pre_set_role(dwc, role);
+ dwc3_set_mode(dwc, mode);
return NOTIFY_DONE;
}
diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c
new file mode 100644
index 000000000000..2105c72af753
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-google.c
@@ -0,0 +1,626 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dwc3-google.c - Google DWC3 Specific Glue Layer
+ *
+ * Copyright (c) 2025, Google LLC
+ * Author: Roy Luo <royluo@google.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include "core.h"
+#include "glue.h"
+
+/* HOST CFG registers */
+#define HC_STATUS_OFFSET 0x0
+#define HC_STATUS_CURRENT_POWER_STATE_U2PMU GENMASK(1, 0)
+#define HC_STATUS_CURRENT_POWER_STATE_U3PMU GENMASK(4, 3)
+
+#define HOST_CFG1_OFFSET 0x4
+#define HOST_CFG1_PME_EN BIT(3)
+#define HOST_CFG1_PM_POWER_STATE_REQUEST GENMASK(5, 4)
+#define HOST_CFG1_PM_POWER_STATE_D0 0x0
+#define HOST_CFG1_PM_POWER_STATE_D3 0x3
+
+/* USBINT registers */
+#define USBINT_CFG1_OFFSET 0x0
+#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK BIT(2)
+#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK BIT(3)
+#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN BIT(8)
+#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN BIT(9)
+#define USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR BIT(14)
+#define USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR BIT(15)
+
+#define USBINT_STATUS_OFFSET 0x4
+#define USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW BIT(2)
+#define USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW BIT(3)
+
+#define USBCS_TOP_CTRL_CFG1_OFFSET 0xc
+#define USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE BIT(5)
+
+#define DWC3_GOOGLE_MAX_RESETS 4
+
+struct dwc3_google {
+ struct device *dev;
+ struct dwc3 dwc;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct reset_control_bulk_data rsts[DWC3_GOOGLE_MAX_RESETS];
+ int num_rsts;
+ struct reset_control *non_sticky_rst;
+ struct device *usb_psw_pd;
+ struct device_link *usb_psw_pd_dl;
+ struct notifier_block usb_psw_pd_nb;
+ struct device *usb_top_pd;
+ struct device_link *usb_top_pd_dl;
+ struct regmap *usb_cfg_regmap;
+ unsigned int host_cfg_offset;
+ unsigned int usbint_cfg_offset;
+ int hs_pme_irq;
+ int ss_pme_irq;
+ bool is_usb2only;
+ bool is_hibernation;
+};
+
+#define to_dwc3_google(d) container_of_const((d), struct dwc3_google, dwc)
+
+static int dwc3_google_rst_init(struct dwc3_google *google)
+{
+ int ret;
+
+ google->num_rsts = 4;
+ google->rsts[0].id = "non_sticky";
+ google->rsts[1].id = "sticky";
+ google->rsts[2].id = "drd_bus";
+ google->rsts[3].id = "top";
+
+ ret = devm_reset_control_bulk_get_exclusive(google->dev,
+ google->num_rsts,
+ google->rsts);
+
+ if (ret < 0)
+ return ret;
+
+ google->non_sticky_rst = google->rsts[0].rstc;
+
+ return 0;
+}
+
+static int dwc3_google_set_pmu_state(struct dwc3_google *google, int state)
+{
+ u32 reg;
+ int ret;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->host_cfg_offset + HOST_CFG1_OFFSET, &reg);
+
+ reg &= ~HOST_CFG1_PM_POWER_STATE_REQUEST;
+ reg |= (FIELD_PREP(HOST_CFG1_PM_POWER_STATE_REQUEST, state) |
+ HOST_CFG1_PME_EN);
+ regmap_write(google->usb_cfg_regmap,
+ google->host_cfg_offset + HOST_CFG1_OFFSET, reg);
+
+ ret = regmap_read_poll_timeout(google->usb_cfg_regmap,
+ google->host_cfg_offset + HC_STATUS_OFFSET, reg,
+ (FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U2PMU,
+ reg) == state &&
+ FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U3PMU,
+ reg) == state),
+ 10, 10000);
+
+ if (ret)
+ dev_err(google->dev, "failed to set PMU state %d\n", state);
+
+ return ret;
+}
+
+/*
+ * Clear pme interrupts and report their status.
+ * The hardware requires write-1 then write-0 sequence to clear the interrupt bits.
+ */
+static u32 dwc3_google_clear_pme_irqs(struct dwc3_google *google)
+{
+ u32 irq_status, reg_set, reg_clear;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_STATUS_OFFSET, &irq_status);
+
+ irq_status &= (USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW |
+ USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW);
+ if (!irq_status)
+ return irq_status;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, &reg_set);
+
+ reg_clear = reg_set;
+ if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW) {
+ reg_set |= USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR;
+ reg_clear &= ~USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR;
+ }
+ if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW) {
+ reg_set |= USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR;
+ reg_clear &= ~USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR;
+ }
+
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg_set);
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg_clear);
+
+ return irq_status;
+}
+
+static void dwc3_google_enable_pme_irq(struct dwc3_google *google)
+{
+ u32 reg;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, &reg);
+ reg &= ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK |
+ USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK);
+ reg |= (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN |
+ USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN);
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg);
+
+ enable_irq(google->hs_pme_irq);
+ enable_irq(google->ss_pme_irq);
+ enable_irq_wake(google->hs_pme_irq);
+ enable_irq_wake(google->ss_pme_irq);
+}
+
+static void dwc3_google_disable_pme_irq(struct dwc3_google *google)
+{
+ u32 reg;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, &reg);
+ reg &= ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN |
+ USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN);
+ reg |= (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK |
+ USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK);
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg);
+
+ disable_irq_wake(google->hs_pme_irq);
+ disable_irq_wake(google->ss_pme_irq);
+ disable_irq_nosync(google->hs_pme_irq);
+ disable_irq_nosync(google->ss_pme_irq);
+}
+
+static irqreturn_t dwc3_google_resume_irq(int irq, void *data)
+{
+ struct dwc3_google *google = data;
+ struct dwc3 *dwc = &google->dwc;
+ u32 irq_status, dr_role;
+
+ irq_status = dwc3_google_clear_pme_irqs(google);
+ dr_role = dwc->current_dr_role;
+
+ if (!irq_status || !google->is_hibernation ||
+ dr_role != DWC3_GCTL_PRTCAP_HOST) {
+ dev_dbg(google->dev, "spurious pme irq %d, hibernation %d, dr_role %u\n",
+ irq, google->is_hibernation, dr_role);
+ return IRQ_HANDLED;
+ }
+
+ if (dwc->xhci)
+ pm_runtime_resume(&dwc->xhci->dev);
+
+ return IRQ_HANDLED;
+}
+
+static int dwc3_google_request_irq(struct dwc3_google *google, struct platform_device *pdev,
+ const char *irq_name, const char *req_name)
+{
+ int ret;
+ int irq;
+
+ irq = platform_get_irq_byname(pdev, irq_name);
+ if (irq < 0)
+ return irq;
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(google->dev, irq, NULL,
+ dwc3_google_resume_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ req_name, google);
+ if (ret < 0) {
+ dev_err(google->dev, "failed to request irq %s\n", req_name);
+ return ret;
+ }
+
+ return irq;
+}
+
+static int dwc3_google_usb_psw_pd_notifier(struct notifier_block *nb, unsigned long action, void *d)
+{
+ struct dwc3_google *google = container_of(nb, struct dwc3_google, usb_psw_pd_nb);
+ int ret;
+
+ if (!google->is_hibernation)
+ return NOTIFY_OK;
+
+ if (action == GENPD_NOTIFY_OFF) {
+ dev_dbg(google->dev, "enter D3 power state\n");
+ dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D3);
+ ret = reset_control_assert(google->non_sticky_rst);
+ if (ret)
+ dev_err(google->dev, "non sticky reset assert failed: %d\n", ret);
+ } else if (action == GENPD_NOTIFY_ON) {
+ dev_dbg(google->dev, "enter D0 power state\n");
+ dwc3_google_clear_pme_irqs(google);
+ ret = reset_control_deassert(google->non_sticky_rst);
+ if (ret)
+ dev_err(google->dev, "non sticky reset deassert failed: %d\n", ret);
+ dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D0);
+ }
+
+ return NOTIFY_OK;
+}
+
+static void dwc3_google_pm_domain_deinit(struct dwc3_google *google)
+{
+ if (google->usb_top_pd_dl)
+ device_link_del(google->usb_top_pd_dl);
+
+ if (!IS_ERR_OR_NULL(google->usb_top_pd)) {
+ device_set_wakeup_capable(google->usb_top_pd, false);
+ dev_pm_domain_detach(google->usb_top_pd, true);
+ }
+
+ if (google->usb_psw_pd_dl)
+ device_link_del(google->usb_psw_pd_dl);
+
+ if (!IS_ERR_OR_NULL(google->usb_psw_pd)) {
+ dev_pm_genpd_remove_notifier(google->usb_psw_pd);
+ dev_pm_domain_detach(google->usb_psw_pd, true);
+ }
+}
+
+static int dwc3_google_pm_domain_init(struct dwc3_google *google)
+{
+ int ret;
+
+ /*
+ * Establish PM RUNTIME link between dwc dev and its power domain usb_psw_pd,
+ * register notifier block to handle hibernation.
+ */
+ google->usb_psw_pd = dev_pm_domain_attach_by_name(google->dev, "psw");
+ if (IS_ERR_OR_NULL(google->usb_psw_pd)) {
+ dev_err(google->dev, "failed to get psw pd");
+ ret = google->usb_psw_pd ? PTR_ERR(google->usb_psw_pd) : -ENODATA;
+ return ret;
+ }
+
+ google->usb_psw_pd_nb.notifier_call = dwc3_google_usb_psw_pd_notifier;
+ ret = dev_pm_genpd_add_notifier(google->usb_psw_pd, &google->usb_psw_pd_nb);
+ if (ret) {
+ dev_err(google->dev, "failed to add psw pd notifier");
+ goto err;
+ }
+
+ google->usb_psw_pd_dl = device_link_add(google->dev, google->usb_psw_pd,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!google->usb_psw_pd_dl) {
+ dev_err(google->usb_psw_pd, "failed to add device link");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /*
+ * usb_top_pd is the parent power domain of usb_psw_pd. Keeping usb_top_pd on
+ * while usb_psw_pd is off places the controller in a power-gated state,
+ * essential for hibernation. Acquire a handle to usb_top_pd and sets it as
+ * wakeup-capable to allow the domain to be left on during system suspend.
+ */
+ google->usb_top_pd = dev_pm_domain_attach_by_name(google->dev, "top");
+ if (IS_ERR_OR_NULL(google->usb_top_pd)) {
+ dev_err(google->dev, "failed to get top pd");
+ ret = google->usb_top_pd ? PTR_ERR(google->usb_top_pd) : -ENODATA;
+ goto err;
+ }
+ device_set_wakeup_capable(google->usb_top_pd, true);
+
+ google->usb_top_pd_dl = device_link_add(google->dev, google->usb_top_pd,
+ DL_FLAG_STATELESS);
+ if (!google->usb_top_pd_dl) {
+ dev_err(google->usb_top_pd, "failed to add device link");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dwc3_google_pm_domain_deinit(google);
+
+ return ret;
+}
+
+static void dwc3_google_program_usb2only(struct dwc3_google *google)
+{
+ u32 reg;
+
+ regmap_read(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBCS_TOP_CTRL_CFG1_OFFSET, &reg);
+ reg |= USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE;
+ regmap_write(google->usb_cfg_regmap,
+ google->usbint_cfg_offset + USBCS_TOP_CTRL_CFG1_OFFSET, reg);
+}
+
+static int dwc3_google_probe(struct platform_device *pdev)
+{
+ struct dwc3_probe_data probe_data = {};
+ struct device *dev = &pdev->dev;
+ struct dwc3_google *google;
+ struct resource *res;
+ int ret;
+ u32 args[2];
+
+ google = devm_kzalloc(&pdev->dev, sizeof(*google), GFP_KERNEL);
+ if (!google)
+ return -ENOMEM;
+
+ google->dev = &pdev->dev;
+
+ ret = dwc3_google_pm_domain_init(google);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to init pdom\n");
+
+ google->usb_cfg_regmap =
+ syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "google,usb-cfg-csr",
+ ARRAY_SIZE(args), args);
+ if (IS_ERR(google->usb_cfg_regmap)) {
+ return dev_err_probe(dev, PTR_ERR(google->usb_cfg_regmap),
+ "invalid usb cfg csr\n");
+ }
+
+ google->host_cfg_offset = args[0];
+ google->usbint_cfg_offset = args[1];
+
+ if (device_property_match_string(dev, "phy-names", "usb3-phy") < 0) {
+ google->is_usb2only = true;
+ dwc3_google_program_usb2only(google);
+ }
+
+ ret = devm_clk_bulk_get_all_enabled(dev, &google->clks);
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "failed to get and enable clks\n");
+ goto err_deinit_pdom;
+ }
+ google->num_clks = ret;
+
+ ret = dwc3_google_rst_init(google);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to get resets\n");
+ goto err_deinit_pdom;
+ }
+
+ ret = reset_control_bulk_deassert(google->num_rsts, google->rsts);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to deassert rsts\n");
+ goto err_deinit_pdom;
+ }
+
+ ret = dwc3_google_request_irq(google, pdev, "hs_pme", "USB HS wakeup");
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "failed to request hs pme irq");
+ goto err_reset_assert;
+ }
+ google->hs_pme_irq = ret;
+
+ ret = dwc3_google_request_irq(google, pdev, "ss_pme", "USB SS wakeup");
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "failed to request ss pme irq");
+ goto err_reset_assert;
+ }
+ google->ss_pme_irq = ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = dev_err_probe(dev, -ENODEV, "invalid memory\n");
+ goto err_reset_assert;
+ }
+
+ device_init_wakeup(dev, true);
+
+ google->dwc.dev = dev;
+ probe_data.dwc = &google->dwc;
+ probe_data.res = res;
+ probe_data.ignore_clocks_and_resets = true;
+ ret = dwc3_core_probe(&probe_data);
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n");
+ goto err_reset_assert;
+ }
+
+ return 0;
+
+err_reset_assert:
+ reset_control_bulk_assert(google->num_rsts, google->rsts);
+
+err_deinit_pdom:
+ dwc3_google_pm_domain_deinit(google);
+
+ return ret;
+}
+
+static void dwc3_google_remove(struct platform_device *pdev)
+{
+ struct dwc3 *dwc = platform_get_drvdata(pdev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+
+ dwc3_core_remove(&google->dwc);
+
+ reset_control_bulk_assert(google->num_rsts, google->rsts);
+
+ dwc3_google_pm_domain_deinit(google);
+}
+
+static int dwc3_google_suspend(struct dwc3_google *google, pm_message_t msg)
+{
+ if (pm_runtime_suspended(google->dev))
+ return 0;
+
+ if (google->dwc.current_dr_role == DWC3_GCTL_PRTCAP_HOST) {
+ /*
+ * Follow dwc3_suspend_common() guidelines for deciding between
+ * a full teardown and hibernation.
+ */
+ if (PMSG_IS_AUTO(msg) || device_may_wakeup(google->dev)) {
+ dev_dbg(google->dev, "enter hibernation");
+ pm_runtime_get_sync(google->usb_top_pd);
+ device_wakeup_enable(google->usb_top_pd);
+ dwc3_google_enable_pme_irq(google);
+ google->is_hibernation = true;
+ return 0;
+ }
+ }
+
+ reset_control_bulk_assert(google->num_rsts, google->rsts);
+ clk_bulk_disable_unprepare(google->num_clks, google->clks);
+
+ return 0;
+}
+
+static int dwc3_google_resume(struct dwc3_google *google, pm_message_t msg)
+{
+ int ret;
+
+ if (google->is_hibernation) {
+ dev_dbg(google->dev, "exit hibernation");
+ dwc3_google_disable_pme_irq(google);
+ device_wakeup_disable(google->usb_top_pd);
+ pm_runtime_put_sync(google->usb_top_pd);
+ google->is_hibernation = false;
+ return 0;
+ }
+
+ if (google->is_usb2only)
+ dwc3_google_program_usb2only(google);
+
+ ret = clk_bulk_prepare_enable(google->num_clks, google->clks);
+ if (ret)
+ return ret;
+
+ ret = reset_control_bulk_deassert(google->num_rsts, google->rsts);
+ if (ret) {
+ clk_bulk_disable_unprepare(google->num_clks, google->clks);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc3_google_pm_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+ int ret;
+
+ ret = dwc3_pm_suspend(&google->dwc);
+ if (ret)
+ return ret;
+
+ return dwc3_google_suspend(google, PMSG_SUSPEND);
+}
+
+static int dwc3_google_pm_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+ int ret;
+
+ ret = dwc3_google_resume(google, PMSG_RESUME);
+ if (ret)
+ return ret;
+
+ return dwc3_pm_resume(&google->dwc);
+}
+
+static void dwc3_google_complete(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ dwc3_pm_complete(dwc);
+}
+
+static int dwc3_google_prepare(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+
+ return dwc3_pm_prepare(dwc);
+}
+
+static int dwc3_google_runtime_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+ int ret;
+
+ ret = dwc3_runtime_suspend(&google->dwc);
+ if (ret)
+ return ret;
+
+ return dwc3_google_suspend(google, PMSG_AUTO_SUSPEND);
+}
+
+static int dwc3_google_runtime_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ struct dwc3_google *google = to_dwc3_google(dwc);
+ int ret;
+
+ ret = dwc3_google_resume(google, PMSG_AUTO_RESUME);
+ if (ret)
+ return ret;
+
+ return dwc3_runtime_resume(&google->dwc);
+}
+
+static int dwc3_google_runtime_idle(struct device *dev)
+{
+ return dwc3_runtime_idle(dev_get_drvdata(dev));
+}
+
+static const struct dev_pm_ops dwc3_google_dev_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(dwc3_google_pm_suspend, dwc3_google_pm_resume)
+ RUNTIME_PM_OPS(dwc3_google_runtime_suspend, dwc3_google_runtime_resume,
+ dwc3_google_runtime_idle)
+ .complete = pm_sleep_ptr(dwc3_google_complete),
+ .prepare = pm_sleep_ptr(dwc3_google_prepare),
+};
+
+static const struct of_device_id dwc3_google_of_match[] = {
+ { .compatible = "google,lga-dwc3" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dwc3_google_of_match);
+
+static struct platform_driver dwc3_google_driver = {
+ .probe = dwc3_google_probe,
+ .remove = dwc3_google_remove,
+ .driver = {
+ .name = "dwc3-google",
+ .pm = pm_ptr(&dwc3_google_dev_pm_ops),
+ .of_match_table = dwc3_google_of_match,
+ },
+};
+
+module_platform_driver(dwc3_google_driver);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare DWC3 Google Glue Driver");
diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c
index 45c276a31d84..b3d7252bd910 100644
--- a/drivers/usb/dwc3/dwc3-imx8mp.c
+++ b/drivers/usb/dwc3/dwc3-imx8mp.c
@@ -51,7 +51,7 @@
struct dwc3_imx8mp {
struct device *dev;
- struct platform_device *dwc3;
+ struct platform_device *dwc3_pdev;
void __iomem *hsio_blk_base;
void __iomem *glue_base;
struct clk *hsio_clk;
@@ -100,7 +100,7 @@ static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx,
pm_message_t msg)
{
- struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
+ struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev);
u32 val;
if (!dwc3)
@@ -142,7 +142,7 @@ static const struct software_node dwc3_imx8mp_swnode = {
static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
{
struct dwc3_imx8mp *dwc3_imx = _dwc3_imx;
- struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
+ struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3_pdev);
if (!dwc3_imx->pm_suspended)
return IRQ_HANDLED;
@@ -158,11 +158,31 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
return IRQ_HANDLED;
}
+static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role)
+{
+ if (role == USB_ROLE_HOST)
+ /*
+ * For xhci host, we need disable dwc core auto
+ * suspend, because during this auto suspend delay(5s),
+ * xhci host RUN_STOP is cleared and wakeup is not
+ * enabled, if device is inserted, xhci host can't
+ * response the connection.
+ */
+ pm_runtime_dont_use_autosuspend(dwc->dev);
+ else
+ pm_runtime_use_autosuspend(dwc->dev);
+}
+
+struct dwc3_glue_ops dwc3_imx_glue_ops = {
+ .pre_set_role = dwc3_imx_pre_set_role,
+};
+
static int dwc3_imx8mp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct dwc3_imx8mp *dwc3_imx;
+ struct dwc3 *dwc3;
struct resource *res;
int err, irq;
@@ -233,13 +253,24 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
goto remove_swnode;
}
- dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np);
- if (!dwc3_imx->dwc3) {
+ dwc3_imx->dwc3_pdev = of_find_device_by_node(dwc3_np);
+ if (!dwc3_imx->dwc3_pdev) {
dev_err(dev, "failed to get dwc3 platform device\n");
err = -ENODEV;
goto depopulate;
}
+ dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev);
+ if (!dwc3) {
+ err = dev_err_probe(dev, -EPROBE_DEFER, "failed to get dwc3 platform data\n");
+ goto depopulate;
+ }
+
+ dwc3->glue_ops = &dwc3_imx_glue_ops;
+
+ if (dwc3->dr_mode == USB_DR_MODE_HOST)
+ pm_runtime_dont_use_autosuspend(dwc3->dev);
+
err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
IRQF_ONESHOT, dev_name(dev), dwc3_imx);
if (err) {
@@ -253,7 +284,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev)
return 0;
put_dwc3:
- put_device(&dwc3_imx->dwc3->dev);
+ put_device(&dwc3_imx->dwc3_pdev->dev);
depopulate:
of_platform_depopulate(dev);
remove_swnode:
@@ -270,7 +301,7 @@ static void dwc3_imx8mp_remove(struct platform_device *pdev)
struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
- put_device(&dwc3_imx->dwc3->dev);
+ put_device(&dwc3_imx->dwc3_pdev->dev);
pm_runtime_get_sync(dev);
of_platform_depopulate(dev);
@@ -296,7 +327,7 @@ static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg)
{
- struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3);
+ struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3_pdev);
int ret = 0;
if (!dwc3_imx->pm_suspended)
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index 0a8c47876ff9..f41b0da5e89d 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -132,21 +132,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err;
}
- /*
- * The following core resets are not required unless a USB3 PHY
- * is used, and the subsequent register settings are not required
- * unless a core reset is performed (they should be set properly
- * by the first-stage boot loader, but may be reverted by a core
- * reset). They may also break the configuration if USB3 is actually
- * in use but the usb3-phy entry is missing from the device tree.
- * Therefore, skip these operations in this case.
- */
- if (!priv_data->usb3_phy) {
- /* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */
- writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
- goto skip_usb3_phy;
- }
-
crst = devm_reset_control_get_exclusive(dev, "usb_crst");
if (IS_ERR(crst)) {
ret = PTR_ERR(crst);
@@ -171,22 +156,31 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err;
}
- ret = reset_control_assert(crst);
- if (ret < 0) {
- dev_err(dev, "Failed to assert core reset\n");
- goto err;
- }
-
- ret = reset_control_assert(hibrst);
- if (ret < 0) {
- dev_err(dev, "Failed to assert hibernation reset\n");
- goto err;
- }
-
- ret = reset_control_assert(apbrst);
- if (ret < 0) {
- dev_err(dev, "Failed to assert APB reset\n");
- goto err;
+ /*
+ * Asserting the core resets is not required unless a USB3 PHY is used.
+ * They may also break the configuration if USB3 is actually in use but
+ * the usb3-phy entry is missing from the device tree. Therefore, skip
+ * a full reset cycle and just deassert the resets if the phy is
+ * absent.
+ */
+ if (priv_data->usb3_phy) {
+ ret = reset_control_assert(crst);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assert core reset\n");
+ goto err;
+ }
+
+ ret = reset_control_assert(hibrst);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assert hibernation reset\n");
+ goto err;
+ }
+
+ ret = reset_control_assert(apbrst);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assert APB reset\n");
+ goto err;
+ }
}
ret = phy_init(priv_data->usb3_phy);
@@ -201,11 +195,15 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err;
}
- /* Set PIPE Power Present signal in FPD Power Present Register*/
- writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT);
-
- /* Set the PIPE Clock Select bit in FPD PIPE Clock register */
- writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
+ if (priv_data->usb3_phy) {
+ /* Set PIPE Power Present signal in FPD Power Present Register*/
+ writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT);
+ /* Set the PIPE Clock Select bit in FPD PIPE Clock register */
+ writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
+ } else {
+ /* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */
+ writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK);
+ }
ret = reset_control_deassert(crst);
if (ret < 0) {
@@ -225,7 +223,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
goto err;
}
-skip_usb3_phy:
/* ulpi reset via gpio-modepin or gpio-framework driver */
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio)) {
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index e0bad5708664..bfe616194dfa 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -361,7 +361,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
if ((dwc->speed == DWC3_DSTS_SUPERSPEED) ||
(dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) {
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (reg & DWC3_DCTL_INITU1ENA)
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
if (reg & DWC3_DCTL_INITU2ENA)
@@ -417,12 +417,12 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
if (set && dwc->dis_u1_entry_quirk)
return -EINVAL;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (set)
reg |= DWC3_DCTL_INITU1ENA;
else
reg &= ~DWC3_DCTL_INITU1ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
return 0;
}
@@ -441,12 +441,12 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
if (set && dwc->dis_u2_entry_quirk)
return -EINVAL;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (set)
reg |= DWC3_DCTL_INITU2ENA;
else
reg &= ~DWC3_DCTL_INITU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
return 0;
}
@@ -612,10 +612,10 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL;
}
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
reg |= DWC3_DCFG_DEVADDR(addr);
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
if (addr)
usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS);
@@ -672,12 +672,12 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
* Enable transition to U1/U2 state when
* nothing is pending from application.
*/
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (!dwc->dis_u1_entry_quirk)
reg |= DWC3_DCTL_ACCEPTU1ENA;
if (!dwc->dis_u2_entry_quirk)
reg |= DWC3_DCTL_ACCEPTU2ENA;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
}
break;
@@ -717,7 +717,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
dwc->u2sel = le16_to_cpu(timing.u2sel);
dwc->u2pel = le16_to_cpu(timing.u2pel);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (reg & DWC3_DCTL_INITU2ENA)
param = dwc->u2pel;
if (reg & DWC3_DCTL_INITU1ENA)
@@ -833,7 +833,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
goto out;
- trace_dwc3_ctrl_req(ctrl);
+ trace_dwc3_ctrl_req(dwc, ctrl);
len = le16_to_cpu(ctrl->wLength);
if (!len) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 8a35a6901db7..c65291e7b8d9 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -42,7 +42,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
switch (mode) {
@@ -73,7 +73,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
return DWC3_DSTS_USBLNKST(reg);
}
@@ -97,7 +97,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
*/
if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
while (--retries) {
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
if (reg & DWC3_DSTS_DCNRD)
udelay(5);
else
@@ -108,15 +108,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
return -ETIMEDOUT;
}
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
/* set no action before sending new link state change */
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
/* set requested state */
reg |= DWC3_DCTL_ULSTCHNGREQ(state);
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
/*
* The following code is racy when called from dwc3_gadget_wakeup,
@@ -128,7 +128,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
/* wait for a change in DSTS */
retries = 10000;
while (--retries) {
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
if (DWC3_DSTS_USBLNKST(reg) == state)
return 0;
@@ -260,11 +260,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
int ret = 0;
u32 reg;
- dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param);
- dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
+ dwc3_writel(dwc, DWC3_DGCMDPAR, param);
+ dwc3_writel(dwc, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT);
do {
- reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
+ reg = dwc3_readl(dwc, DWC3_DGCMD);
if (!(reg & DWC3_DGCMD_CMDACT)) {
status = DWC3_DGCMD_STATUS(reg);
if (status)
@@ -278,7 +278,7 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
status = -ETIMEDOUT;
}
- trace_dwc3_gadget_generic_cmd(cmd, param, status);
+ trace_dwc3_gadget_generic_cmd(dwc, cmd, param, status);
return ret;
}
@@ -320,6 +320,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
int cmd_status = 0;
int ret = -EINVAL;
+ u8 epnum = dep->number;
/*
* When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or
@@ -333,7 +334,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
*/
if (dwc->gadget->speed <= USB_SPEED_HIGH ||
DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@@ -345,7 +346,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
}
if (saved_config)
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
/*
@@ -355,9 +356,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
* improve performance.
*/
if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) {
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0);
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1);
- dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2);
+ dwc3_writel(dwc, DWC3_DEPCMDPAR0(epnum), params->param0);
+ dwc3_writel(dwc, DWC3_DEPCMDPAR1(epnum), params->param1);
+ dwc3_writel(dwc, DWC3_DEPCMDPAR2(epnum), params->param2);
}
/*
@@ -381,7 +382,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
else
cmd |= DWC3_DEPCMD_CMDACT;
- dwc3_writel(dep->regs, DWC3_DEPCMD, cmd);
+ dwc3_writel(dwc, DWC3_DEPCMD(epnum), cmd);
if (!(cmd & DWC3_DEPCMD_CMDACT) ||
(DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER &&
@@ -391,7 +392,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
}
do {
- reg = dwc3_readl(dep->regs, DWC3_DEPCMD);
+ reg = dwc3_readl(dwc, DWC3_DEPCMD(epnum));
if (!(reg & DWC3_DEPCMD_CMDACT)) {
cmd_status = DWC3_DEPCMD_STATUS(reg);
@@ -447,9 +448,9 @@ skip_status:
mdelay(1);
if (saved_config) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= saved_config;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
return ret;
@@ -726,7 +727,7 @@ static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc)
u32 reg;
/* Check if TXFIFOs start at non-zero addr */
- reg = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ reg = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg);
ram_depth -= (fifo_0_start >> 16);
@@ -754,7 +755,7 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
/* Read ep0IN related TXFIFO size */
dep = dwc->eps[1];
- size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
if (DWC3_IP_IS(DWC3))
fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
@@ -769,10 +770,10 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
/* Don't change TXFRAMNUM on usb31 version */
size = DWC3_IP_IS(DWC3) ? 0 :
- dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
+ dwc3_readl(dwc, DWC3_GTXFIFOSIZ(num >> 1)) &
DWC31_GTXFIFOSIZ_TXFRAMNUM;
- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
+ dwc3_writel(dwc, DWC3_GTXFIFOSIZ(num >> 1), size);
dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
}
dwc->num_ep_resized = 0;
@@ -875,7 +876,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
fifo_size++;
/* Check if TXFIFOs start at non-zero addr */
- tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ tmp = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0));
fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
@@ -898,7 +899,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
return -ENOMEM;
}
- dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
+ dwc3_writel(dwc, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
dep->flags |= DWC3_EP_TXFIFO_RESIZED;
dwc->num_ep_resized++;
@@ -942,9 +943,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
dep->type = usb_endpoint_type(desc);
dep->flags |= DWC3_EP_ENABLED;
- reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
+ reg = dwc3_readl(dwc, DWC3_DALEPENA);
reg |= DWC3_DALEPENA_EP(dep->number);
- dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ dwc3_writel(dwc, DWC3_DALEPENA, reg);
dep->trb_dequeue = 0;
dep->trb_enqueue = 0;
@@ -1079,9 +1080,9 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
if (dep->flags & DWC3_EP_STALL)
__dwc3_gadget_ep_set_halt(dep, 0, false);
- reg = dwc3_readl(dwc->regs, DWC3_DALEPENA);
+ reg = dwc3_readl(dwc, DWC3_DALEPENA);
reg &= ~DWC3_DALEPENA_EP(dep->number);
- dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+ dwc3_writel(dwc, DWC3_DALEPENA, reg);
dwc3_remove_requests(dwc, dep, -ESHUTDOWN);
@@ -1742,7 +1743,7 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc)
{
u32 reg;
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
return DWC3_DSTS_SOFFN(reg);
}
@@ -2350,13 +2351,13 @@ static void dwc3_gadget_enable_linksts_evts(struct dwc3 *dwc, bool set)
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return;
- reg = dwc3_readl(dwc->regs, DWC3_DEVTEN);
+ reg = dwc3_readl(dwc, DWC3_DEVTEN);
if (set)
reg |= DWC3_DEVTEN_ULSTCNGEN;
else
reg &= ~DWC3_DEVTEN_ULSTCNGEN;
- dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+ dwc3_writel(dwc, DWC3_DEVTEN, reg);
}
static int dwc3_gadget_get_frame(struct usb_gadget *g)
@@ -2379,7 +2380,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
*
* We can check that via USB Link State bits in DSTS register.
*/
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
link_state = DWC3_DSTS_USBLNKST(reg);
@@ -2407,9 +2408,9 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
/* Recent versions do this automatically */
if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
/* write zeroes to Link Change Request */
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
}
/*
@@ -2529,7 +2530,7 @@ static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
if (ssp_rate == USB_SSP_GEN_UNKNOWN)
ssp_rate = dwc->max_ssp_rate;
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~DWC3_DCFG_SPEED_MASK;
reg &= ~DWC3_DCFG_NUMLANES(~0);
@@ -2542,7 +2543,7 @@ static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
dwc->max_ssp_rate != USB_SSP_GEN_2x1)
reg |= DWC3_DCFG_NUMLANES(1);
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
@@ -2560,7 +2561,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
return;
}
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_SPEED_MASK);
/*
@@ -2611,7 +2612,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
speed < USB_SPEED_SUPER_PLUS)
reg &= ~DWC3_DCFG_NUMLANES(~0);
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
@@ -2636,7 +2637,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
* mentioned in the dwc3 programming guide. It has been tested on an
* Exynos platforms.
*/
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
saved_config |= DWC3_GUSB2PHYCFG_SUSPHY;
reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
@@ -2648,9 +2649,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
}
if (saved_config)
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
if (is_on) {
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
reg &= ~DWC3_DCTL_TRGTULST_MASK;
@@ -2674,14 +2675,14 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
do {
usleep_range(1000, 2000);
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
reg &= DWC3_DSTS_DEVCTRLHLT;
} while (--timeout && !(!is_on ^ !reg));
if (saved_config) {
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
reg |= saved_config;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg);
}
if (!timeout)
@@ -2857,13 +2858,13 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
reg |= DWC3_DEVTEN_U3L2L1SUSPEN;
- dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+ dwc3_writel(dwc, DWC3_DEVTEN, reg);
}
static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
{
/* mask all interrupts */
- dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
+ dwc3_writel(dwc, DWC3_DEVTEN, 0x00);
}
static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
@@ -2904,10 +2905,10 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
nump = min_t(u32, nump, 16);
/* update NumP */
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~DWC3_DCFG_NUMP_MASK;
reg |= nump << DWC3_DCFG_NUMP_SHIFT;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
static int __dwc3_gadget_start(struct dwc3 *dwc)
@@ -2921,10 +2922,10 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* the core supports IMOD, disable it.
*/
if (dwc->imod_interval) {
- dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
} else if (dwc3_has_imod(dwc)) {
- dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
+ dwc3_writel(dwc, DWC3_DEV_IMOD(0), 0);
}
/*
@@ -2934,13 +2935,13 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* This way, we maximize the chances that we'll be able to get several
* bursts of data without going through any sort of endpoint throttling.
*/
- reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+ reg = dwc3_readl(dwc, DWC3_GRXTHRCFG);
if (DWC3_IP_IS(DWC3))
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
else
reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
- dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+ dwc3_writel(dwc, DWC3_GRXTHRCFG, reg);
dwc3_gadget_setup_nump(dwc);
@@ -2951,15 +2952,15 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* ACK with NumP=0 and PP=0 (for IN direction). This slightly improves
* the stream performance.
*/
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg |= DWC3_DCFG_IGNSTRMPP;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
/* Enable MST by default if the device is capable of MST */
if (DWC3_MST_CAPABLE(&dwc->hwparams)) {
- reg = dwc3_readl(dwc->regs, DWC3_DCFG1);
+ reg = dwc3_readl(dwc, DWC3_DCFG1);
reg &= ~DWC3_DCFG1_DIS_MST_ENH;
- dwc3_writel(dwc->regs, DWC3_DCFG1, reg);
+ dwc3_writel(dwc, DWC3_DCFG1, reg);
}
/* Start with SuperSpeed Default */
@@ -3123,8 +3124,6 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
{
struct dwc3 *dwc = gadget_to_dwc(g);
- union power_supply_propval val = {0};
- int ret;
if (dwc->usb2_phy)
return usb_phy_set_power(dwc->usb2_phy, mA);
@@ -3132,10 +3131,10 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
if (!dwc->usb_psy)
return -EOPNOTSUPP;
- val.intval = 1000 * mA;
- ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
+ dwc->current_limit = mA;
+ schedule_work(&dwc->vbus_draw_work);
- return ret;
+ return 0;
}
/**
@@ -3239,7 +3238,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
- size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
+ size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(dep->number >> 1));
if (DWC3_IP_IS(DWC3))
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
@@ -3288,7 +3287,7 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
mdwidth /= 8;
/* All OUT endpoints share a single RxFIFO space */
- size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
+ size = dwc3_readl(dwc, DWC3_GRXFIFOSIZ(0));
if (DWC3_IP_IS(DWC3))
size = DWC3_GRXFIFOSIZ_RXFDEP(size);
else
@@ -3381,7 +3380,6 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
dep->dwc = dwc;
dep->number = epnum;
dep->direction = direction;
- dep->regs = dwc->regs + DWC3_DEP_BASE(epnum);
dwc->eps[epnum] = dep;
dep->combo_num = 0;
dep->start_cmd_status = 0;
@@ -3742,9 +3740,9 @@ out:
return no_started_trb;
}
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg |= dwc->u1u2;
- dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+ dwc3_writel(dwc, DWC3_DCTL, reg);
dwc->u1u2 = 0;
}
@@ -4074,7 +4072,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_INITU1ENA;
reg &= ~DWC3_DCTL_INITU2ENA;
dwc3_gadget_dctl_write_safe(dwc, reg);
@@ -4163,7 +4161,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_stop_active_transfers(dwc);
dwc->connected = true;
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg);
dwc->test_mode = false;
@@ -4172,9 +4170,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_clear_stall_all_ep(dwc);
/* Reset device address to zero */
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
@@ -4188,7 +4186,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
if (!dwc->softconnect)
return;
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+ reg = dwc3_readl(dwc, DWC3_DSTS);
speed = reg & DWC3_DSTS_CONNECTSPD;
dwc->speed = speed;
@@ -4263,11 +4261,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
!dwc->usb2_gadget_lpm_disable &&
(speed != DWC3_DSTS_SUPERSPEED) &&
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg |= DWC3_DCFG_LPM_CAP;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold |
@@ -4290,12 +4288,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
dwc3_gadget_dctl_write_safe(dwc, reg);
} else {
if (dwc->usb2_gadget_lpm_disable) {
- reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+ reg = dwc3_readl(dwc, DWC3_DCFG);
reg &= ~DWC3_DCFG_LPM_CAP;
- dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+ dwc3_writel(dwc, DWC3_DCFG, reg);
}
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
dwc3_gadget_dctl_write_safe(dwc, reg);
}
@@ -4401,7 +4399,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
switch (dwc->link_state) {
case DWC3_LINK_STATE_U1:
case DWC3_LINK_STATE_U2:
- reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg = dwc3_readl(dwc, DWC3_DCTL);
u1u2 = reg & (DWC3_DCTL_INITU2ENA
| DWC3_DCTL_ACCEPTU2ENA
| DWC3_DCTL_INITU1ENA
@@ -4558,7 +4556,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
ret = IRQ_HANDLED;
/* Unmask interrupt */
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_SIZE(evt->length));
evt->flags &= ~DWC3_EVENT_PENDING;
@@ -4569,8 +4567,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
wmb();
if (dwc->imod_interval) {
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
- dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
+ dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval);
}
return ret;
@@ -4619,7 +4617,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
if (evt->flags & DWC3_EVENT_PENDING)
return IRQ_HANDLED;
- count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+ count = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0));
count &= DWC3_GEVNTCOUNT_MASK;
if (!count)
return IRQ_NONE;
@@ -4634,7 +4632,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
evt->flags |= DWC3_EVENT_PENDING;
/* Mask interrupt */
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ dwc3_writel(dwc, DWC3_GEVNTSIZ(0),
DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length));
amount = min(count, evt->length - evt->lpos);
@@ -4643,7 +4641,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
if (amount < count)
memcpy(evt->cache, evt->buf, count - amount);
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count);
return IRQ_WAKE_THREAD;
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index d73e735e4081..45f113b3c146 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -132,7 +132,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
{
u32 res_id;
- res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
+ res_id = dwc3_readl(dep->dwc, DWC3_DEPCMD(dep->number));
dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id);
}
@@ -147,7 +147,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value)
{
value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
- dwc3_writel(dwc->regs, DWC3_DCTL, value);
+ dwc3_writel(dwc, DWC3_DCTL, value);
}
#endif /* __DRIVERS_USB_DWC3_GADGET_H */
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 1e96ea339d48..cad9a2ae1547 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -16,9 +16,10 @@
#include "debug.h"
#include "core.h"
-static inline u32 dwc3_readl(void __iomem *base, u32 offset)
+static inline u32 dwc3_readl(struct dwc3 *dwc, u32 offset)
{
u32 value;
+ void __iomem *base = dwc->regs;
/*
* We requested the mem region starting from the Globals address
@@ -32,13 +33,15 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
* documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation
*/
- trace_dwc3_readl(base - DWC3_GLOBALS_REGS_START, offset, value);
+ trace_dwc3_readl(dwc, base - DWC3_GLOBALS_REGS_START, offset, value);
return value;
}
-static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
+static inline void dwc3_writel(struct dwc3 *dwc, u32 offset, u32 value)
{
+ void __iomem *base = dwc->regs;
+
/*
* We requested the mem region starting from the Globals address
* space, see dwc3_probe in core.c.
@@ -51,7 +54,7 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
* documentation, so we revert it back to the proper addresses, the
* same way they are described on SNPS documentation
*/
- trace_dwc3_writel(base - DWC3_GLOBALS_REGS_START, offset, value);
+ trace_dwc3_writel(dwc, base - DWC3_GLOBALS_REGS_START, offset, value);
}
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index b6ba984bafcd..5253da23d8b0 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -20,63 +20,70 @@
#include "debug.h"
DECLARE_EVENT_CLASS(dwc3_log_set_prtcap,
- TP_PROTO(u32 mode),
- TP_ARGS(mode),
+ TP_PROTO(struct dwc3 *dwc, u32 mode),
+ TP_ARGS(dwc, mode),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(u32, mode)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->mode = mode;
),
- TP_printk("mode %s", dwc3_mode_string(__entry->mode))
+ TP_printk("%pa: mode %s", &__entry->base_address, dwc3_mode_string(__entry->mode))
);
DEFINE_EVENT(dwc3_log_set_prtcap, dwc3_set_prtcap,
- TP_PROTO(u32 mode),
- TP_ARGS(mode)
+ TP_PROTO(struct dwc3 *dwc, u32 mode),
+ TP_ARGS(dwc, mode)
);
DECLARE_EVENT_CLASS(dwc3_log_io,
- TP_PROTO(void *base, u32 offset, u32 value),
- TP_ARGS(base, offset, value),
+ TP_PROTO(struct dwc3 *dwc, void *base, u32 offset, u32 value),
+ TP_ARGS(dwc, base, offset, value),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(void *, base)
__field(u32, offset)
__field(u32, value)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->base = base;
__entry->offset = offset;
__entry->value = value;
),
- TP_printk("addr %p offset %04x value %08x",
+ TP_printk("%pa: addr %p offset %04x value %08x",
+ &__entry->base_address,
__entry->base + __entry->offset,
__entry->offset,
__entry->value)
);
DEFINE_EVENT(dwc3_log_io, dwc3_readl,
- TP_PROTO(void __iomem *base, u32 offset, u32 value),
- TP_ARGS(base, offset, value)
+ TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value),
+ TP_ARGS(dwc, base, offset, value)
);
DEFINE_EVENT(dwc3_log_io, dwc3_writel,
- TP_PROTO(void __iomem *base, u32 offset, u32 value),
- TP_ARGS(base, offset, value)
+ TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value),
+ TP_ARGS(dwc, base, offset, value)
);
DECLARE_EVENT_CLASS(dwc3_log_event,
TP_PROTO(u32 event, struct dwc3 *dwc),
TP_ARGS(event, dwc),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(u32, event)
__field(u32, ep0state)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->event = event;
__entry->ep0state = dwc->ep0state;
),
- TP_printk("event (%08x): %s", __entry->event,
+ TP_printk("%pa: event (%08x): %s", &__entry->base_address, __entry->event,
dwc3_decode_event(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX,
__entry->event, __entry->ep0state))
);
@@ -87,9 +94,10 @@ DEFINE_EVENT(dwc3_log_event, dwc3_event,
);
DECLARE_EVENT_CLASS(dwc3_log_ctrl,
- TP_PROTO(struct usb_ctrlrequest *ctrl),
- TP_ARGS(ctrl),
+ TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl),
+ TP_ARGS(dwc, ctrl),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(__u8, bRequestType)
__field(__u8, bRequest)
__field(__u16, wValue)
@@ -97,13 +105,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
__field(__u16, wLength)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->bRequestType = ctrl->bRequestType;
__entry->bRequest = ctrl->bRequest;
__entry->wValue = le16_to_cpu(ctrl->wValue);
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
__entry->wLength = le16_to_cpu(ctrl->wLength);
),
- TP_printk("%s", usb_decode_ctrl(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX,
+ TP_printk("%pa: %s", &__entry->base_address, usb_decode_ctrl(__get_buf(DWC3_MSG_MAX),
+ DWC3_MSG_MAX,
__entry->bRequestType,
__entry->bRequest, __entry->wValue,
__entry->wIndex, __entry->wLength)
@@ -111,14 +121,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
);
DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req,
- TP_PROTO(struct usb_ctrlrequest *ctrl),
- TP_ARGS(ctrl)
+ TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl),
+ TP_ARGS(dwc, ctrl)
);
DECLARE_EVENT_CLASS(dwc3_log_request,
TP_PROTO(struct dwc3_request *req),
TP_ARGS(req),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__string(name, req->dep->name)
__field(struct dwc3_request *, req)
__field(unsigned int, actual)
@@ -129,7 +140,7 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
__field(int, no_interrupt)
),
TP_fast_assign(
- __assign_str(name);
+ __entry->base_address = req->dep->dwc->xhci_resources[0].start;
__entry->req = req;
__entry->actual = req->request.actual;
__entry->length = req->request.length;
@@ -138,8 +149,10 @@ DECLARE_EVENT_CLASS(dwc3_log_request,
__entry->short_not_ok = req->request.short_not_ok;
__entry->no_interrupt = req->request.no_interrupt;
),
- TP_printk("%s: req %p length %u/%u %s%s%s ==> %d",
- __get_str(name), __entry->req, __entry->actual, __entry->length,
+ TP_printk("%pa: %s: req %p length %u/%u %s%s%s ==> %d",
+ &__entry->base_address,
+ __get_str(name), __entry->req,
+ __entry->actual, __entry->length,
__entry->zero ? "Z" : "z",
__entry->short_not_ok ? "S" : "s",
__entry->no_interrupt ? "i" : "I",
@@ -173,28 +186,30 @@ DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback,
);
DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
- TP_PROTO(unsigned int cmd, u32 param, int status),
- TP_ARGS(cmd, param, status),
+ TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status),
+ TP_ARGS(dwc, cmd, param, status),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__field(unsigned int, cmd)
__field(u32, param)
__field(int, status)
),
TP_fast_assign(
+ __entry->base_address = dwc->xhci_resources[0].start;
__entry->cmd = cmd;
__entry->param = param;
__entry->status = status;
),
- TP_printk("cmd '%s' [%x] param %08x --> status: %s",
- dwc3_gadget_generic_cmd_string(__entry->cmd),
+ TP_printk("%pa: cmd '%s' [%x] param %08x --> status: %s",
+ &__entry->base_address, dwc3_gadget_generic_cmd_string(__entry->cmd),
__entry->cmd, __entry->param,
dwc3_gadget_generic_cmd_status_string(__entry->status)
)
);
DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd,
- TP_PROTO(unsigned int cmd, u32 param, int status),
- TP_ARGS(cmd, param, status)
+ TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status),
+ TP_ARGS(dwc, cmd, param, status)
);
DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
@@ -202,6 +217,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
struct dwc3_gadget_ep_cmd_params *params, int cmd_status),
TP_ARGS(dep, cmd, params, cmd_status),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__string(name, dep->name)
__field(unsigned int, cmd)
__field(u32, param0)
@@ -210,6 +226,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
__field(int, cmd_status)
),
TP_fast_assign(
+ __entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name);
__entry->cmd = cmd;
__entry->param0 = params->param0;
@@ -217,8 +234,9 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
__entry->param2 = params->param2;
__entry->cmd_status = cmd_status;
),
- TP_printk("%s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
- __get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
+ TP_printk("%pa: %s: cmd '%s' [%x] params %08x %08x %08x --> status: %s",
+ &__entry->base_address, __get_str(name),
+ dwc3_gadget_ep_cmd_string(__entry->cmd),
__entry->cmd, __entry->param0,
__entry->param1, __entry->param2,
dwc3_ep_cmd_status_string(__entry->cmd_status)
@@ -235,6 +253,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb),
TP_ARGS(dep, trb),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__string(name, dep->name)
__field(struct dwc3_trb *, trb)
__field(u32, bpl)
@@ -246,6 +265,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__field(u32, dequeue)
),
TP_fast_assign(
+ __entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name);
__entry->trb = trb;
__entry->bpl = trb->bpl;
@@ -256,8 +276,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__entry->enqueue = dep->trb_enqueue;
__entry->dequeue = dep->trb_dequeue;
),
- TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)",
- __get_str(name), __entry->trb, __entry->enqueue,
+ TP_printk("%pa: %s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)",
+ &__entry->base_address, __get_str(name), __entry->trb, __entry->enqueue,
__entry->dequeue, __entry->bph, __entry->bpl,
({char *s;
int pcm = ((__entry->size >> 24) & 3) + 1;
@@ -307,6 +327,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
TP_PROTO(struct dwc3_ep *dep),
TP_ARGS(dep),
TP_STRUCT__entry(
+ __field(phys_addr_t, base_address)
__string(name, dep->name)
__field(unsigned int, maxpacket)
__field(unsigned int, maxpacket_limit)
@@ -318,6 +339,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__field(u8, trb_dequeue)
),
TP_fast_assign(
+ __entry->base_address = dep->dwc->xhci_resources[0].start;
__assign_str(name);
__entry->maxpacket = dep->endpoint.maxpacket;
__entry->maxpacket_limit = dep->endpoint.maxpacket_limit;
@@ -328,8 +350,8 @@ DECLARE_EVENT_CLASS(dwc3_log_ep,
__entry->trb_enqueue = dep->trb_enqueue;
__entry->trb_dequeue = dep->trb_dequeue;
),
- TP_printk("%s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
- __get_str(name), __entry->maxpacket,
+ TP_printk("%pa: %s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c",
+ &__entry->base_address, __get_str(name), __entry->maxpacket,
__entry->maxpacket_limit, __entry->max_streams,
__entry->maxburst, __entry->trb_enqueue,
__entry->trb_dequeue,
diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c
index f23f4c9a557e..57daad15f502 100644
--- a/drivers/usb/dwc3/ulpi.c
+++ b/drivers/usb/dwc3/ulpi.c
@@ -33,13 +33,13 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read)
if (read)
ns += DWC3_ULPI_BASE_DELAY;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0));
if (reg & DWC3_GUSB2PHYCFG_SUSPHY)
usleep_range(1000, 1200);
while (count--) {
ndelay(ns);
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0));
if (reg & DWC3_GUSB2PHYACC_DONE)
return 0;
cpu_relax();
@@ -55,13 +55,13 @@ static int dwc3_ulpi_read(struct device *dev, u8 addr)
int ret;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYACC(0), reg);
ret = dwc3_ulpi_busyloop(dwc, addr, true);
if (ret)
return ret;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
+ reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0));
return DWC3_GUSB2PHYACC_DATA(reg);
}
@@ -73,7 +73,7 @@ static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val)
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
reg |= DWC3_GUSB2PHYACC_WRITE | val;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
+ dwc3_writel(dwc, DWC3_GUSB2PHYACC(0), reg);
return dwc3_ulpi_busyloop(dwc, addr, false);
}
diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c
index 64c4965a160f..fbb5d590eab6 100644
--- a/drivers/usb/fotg210/fotg210-hcd.c
+++ b/drivers/usb/fotg210/fotg210-hcd.c
@@ -5625,11 +5625,6 @@ int __init fotg210_hcd_init(void)
if (usb_disabled())
return -ENODEV;
- set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
- if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
- test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
- pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n");
-
pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n",
hcd_name, sizeof(struct fotg210_qh),
sizeof(struct fotg210_qtd),
@@ -5643,5 +5638,4 @@ int __init fotg210_hcd_init(void)
void __exit fotg210_hcd_cleanup(void)
{
debugfs_remove(fotg210_debug_root);
- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
}
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 6bcac85c5550..acef1c6f199c 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -409,7 +409,7 @@ static void gadget_info_attr_release(struct config_item *item)
kfree(gi);
}
-static struct configfs_item_operations gadget_root_item_ops = {
+static const struct configfs_item_operations gadget_root_item_ops = {
.release = gadget_info_attr_release,
};
@@ -514,7 +514,7 @@ static void config_usb_cfg_unlink(
WARN(1, "Unable to locate function to unbind\n");
}
-static struct configfs_item_operations gadget_config_item_ops = {
+static const struct configfs_item_operations gadget_config_item_ops = {
.release = gadget_config_attr_release,
.allow_link = config_usb_cfg_link,
.drop_link = config_usb_cfg_unlink,
@@ -663,7 +663,7 @@ static void function_drop(
config_item_put(item);
}
-static struct configfs_group_operations functions_ops = {
+static const struct configfs_group_operations functions_ops = {
.make_group = &function_make,
.drop_item = &function_drop,
};
@@ -766,7 +766,7 @@ static void config_desc_drop(
config_item_put(item);
}
-static struct configfs_group_operations config_desc_ops = {
+static const struct configfs_group_operations config_desc_ops = {
.make_group = &config_desc_make,
.drop_item = &config_desc_drop,
};
@@ -799,7 +799,7 @@ static void gadget_language_attr_release(struct config_item *item)
kfree(gs);
}
-static struct configfs_item_operations gadget_language_langid_item_ops = {
+static const struct configfs_item_operations gadget_language_langid_item_ops = {
.release = gadget_language_attr_release,
};
@@ -852,7 +852,7 @@ static void gadget_string_release(struct config_item *item)
kfree(string);
}
-static struct configfs_item_operations gadget_string_item_ops = {
+static const struct configfs_item_operations gadget_string_item_ops = {
.release = gadget_string_release,
};
@@ -901,7 +901,7 @@ static void gadget_language_string_drop(struct config_group *group,
string->usb_string.id = i++;
}
-static struct configfs_group_operations gadget_language_langid_group_ops = {
+static const struct configfs_group_operations gadget_language_langid_group_ops = {
.make_item = gadget_language_string_make,
.drop_item = gadget_language_string_drop,
};
@@ -960,7 +960,7 @@ static void gadget_language_drop(struct config_group *group,
config_item_put(item);
}
-static struct configfs_group_operations gadget_language_group_ops = {
+static const struct configfs_group_operations gadget_language_group_ops = {
.make_group = &gadget_language_make,
.drop_item = &gadget_language_drop,
};
@@ -1266,7 +1266,7 @@ static void os_desc_unlink(struct config_item *os_desc_ci,
mutex_unlock(&gi->lock);
}
-static struct configfs_item_operations os_desc_ops = {
+static const struct configfs_item_operations os_desc_ops = {
.allow_link = os_desc_link,
.drop_link = os_desc_unlink,
};
@@ -1391,7 +1391,7 @@ static void usb_os_desc_ext_prop_release(struct config_item *item)
kfree(ext_prop); /* frees a whole chunk */
}
-static struct configfs_item_operations ext_prop_ops = {
+static const struct configfs_item_operations ext_prop_ops = {
.release = usb_os_desc_ext_prop_release,
};
@@ -1456,7 +1456,7 @@ static void ext_prop_drop(struct config_group *group, struct config_item *item)
config_item_put(item);
}
-static struct configfs_group_operations interf_grp_ops = {
+static const struct configfs_group_operations interf_grp_ops = {
.make_item = &ext_prop_make,
.drop_item = &ext_prop_drop,
};
@@ -2061,7 +2061,7 @@ static void gadgets_drop(struct config_group *group, struct config_item *item)
config_item_put(item);
}
-static struct configfs_group_operations gadgets_ops = {
+static const struct configfs_group_operations gadgets_ops = {
.make_group = &gadgets_make,
.drop_item = &gadgets_drop,
};
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index 106046e17c4e..0ad857f1f325 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -793,7 +793,7 @@ static void acm_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations acm_item_ops = {
+static const struct configfs_item_operations acm_item_ops = {
.release = acm_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index fa467a40949d..2c6986e381ca 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1509,7 +1509,7 @@ static int ffs_dmabuf_attach(struct file *file, int fd)
goto err_dmabuf_detach;
}
- dir = epfile->in ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ dir = epfile->in ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
err = ffs_dma_resv_lock(dmabuf, nonblock);
if (err)
@@ -1639,7 +1639,7 @@ static int ffs_dmabuf_transfer(struct file *file,
/* Make sure we don't have writers */
timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS);
retl = dma_resv_wait_timeout(dmabuf->resv,
- dma_resv_usage_rw(epfile->in),
+ dma_resv_usage_rw(!epfile->in),
true, timeout);
if (retl == 0)
retl = -EBUSY;
@@ -1684,7 +1684,7 @@ static int ffs_dmabuf_transfer(struct file *file,
dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops,
&priv->lock, priv->context, seqno);
- resv_dir = epfile->in ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ;
+ resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE;
dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir);
dma_resv_unlock(dmabuf->resv);
@@ -1744,10 +1744,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
int fd;
- if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
+ return -EFAULT;
return ffs_dmabuf_attach(file, fd);
}
@@ -1755,10 +1753,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
int fd;
- if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&fd, (void __user *)value, sizeof(fd)))
+ return -EFAULT;
return ffs_dmabuf_detach(file, fd);
}
@@ -1766,10 +1762,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
{
struct usb_ffs_dmabuf_transfer_req req;
- if (copy_from_user(&req, (void __user *)value, sizeof(req))) {
- ret = -EFAULT;
- break;
- }
+ if (copy_from_user(&req, (void __user *)value, sizeof(req)))
+ return -EFAULT;
return ffs_dmabuf_transfer(file, &req);
}
@@ -4000,7 +3994,7 @@ static void ffs_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations ffs_item_ops = {
+static const struct configfs_item_operations ffs_item_ops = {
.release = ffs_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 3ddfd4f66f0b..bee0d0458ff7 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -1328,7 +1328,7 @@ static void hid_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations hidg_item_ops = {
+static const struct configfs_item_operations hidg_item_ops = {
.release = hid_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 49b009a7d5d7..39862e236837 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -464,7 +464,7 @@ static void lb_attr_release(struct config_item *item)
usb_put_function_instance(&lb_opts->func_inst);
}
-static struct configfs_item_operations lb_item_ops = {
+static const struct configfs_item_operations lb_item_ops = {
.release = lb_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 94d478b6bcd3..5c3f34a2f35c 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -3153,7 +3153,7 @@ static void fsg_lun_attr_release(struct config_item *item)
kfree(lun_opts);
}
-static struct configfs_item_operations fsg_lun_item_ops = {
+static const struct configfs_item_operations fsg_lun_item_ops = {
.release = fsg_lun_attr_release,
};
@@ -3369,7 +3369,7 @@ static void fsg_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations fsg_item_ops = {
+static const struct configfs_item_operations fsg_item_ops = {
.release = fsg_attr_release,
};
@@ -3462,7 +3462,7 @@ static struct configfs_attribute *fsg_attrs[] = {
NULL,
};
-static struct configfs_group_operations fsg_group_ops = {
+static const struct configfs_group_operations fsg_group_ops = {
.make_group = fsg_lun_make,
.drop_item = fsg_lun_drop,
};
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index da82598fcef8..f592f9eb85d4 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -875,6 +875,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f);
struct usb_string *us;
+ struct f_midi_opts *opts;
int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0;
midi->gadget = cdev->gadget;
@@ -883,6 +884,10 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
if (status < 0)
goto fail_register;
+ opts = container_of(f->fi, struct f_midi_opts, func_inst);
+ if (opts->interface_string)
+ midi_string_defs[STRING_FUNC_IDX].s = opts->interface_string;
+
/* maybe allocate device-global string ID */
us = usb_gstrings_attach(c->cdev, midi_strings,
ARRAY_SIZE(midi_string_defs));
@@ -1086,7 +1091,7 @@ static void midi_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations midi_item_ops = {
+static const struct configfs_item_operations midi_item_ops = {
.release = midi_attr_release,
};
@@ -1178,59 +1183,60 @@ end: \
\
CONFIGFS_ATTR(f_midi_opts_, name);
+#define F_MIDI_OPT_STRING(name) \
+static ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \
+{ \
+ struct f_midi_opts *opts = to_f_midi_opts(item); \
+ ssize_t result; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->name) { \
+ result = strscpy(page, opts->name, PAGE_SIZE); \
+ } else { \
+ page[0] = 0; \
+ result = 0; \
+ } \
+ \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_midi_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_midi_opts *opts = to_f_midi_opts(item); \
+ int ret; \
+ char *c; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt > 1) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ c = kstrndup(page, len, GFP_KERNEL); \
+ if (!c) { \
+ ret = -ENOMEM; \
+ goto end; \
+ } \
+ kfree(opts->name); \
+ opts->name = c; \
+ ret = len; \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_midi_opts_, name)
+
F_MIDI_OPT_SIGNED(index, true, SNDRV_CARDS);
F_MIDI_OPT(buflen, false, 0);
F_MIDI_OPT(qlen, false, 0);
F_MIDI_OPT(in_ports, true, MAX_PORTS);
F_MIDI_OPT(out_ports, true, MAX_PORTS);
-
-static ssize_t f_midi_opts_id_show(struct config_item *item, char *page)
-{
- struct f_midi_opts *opts = to_f_midi_opts(item);
- ssize_t result;
-
- mutex_lock(&opts->lock);
- if (opts->id) {
- result = strscpy(page, opts->id, PAGE_SIZE);
- } else {
- page[0] = 0;
- result = 0;
- }
-
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_midi_opts_id_store(struct config_item *item,
- const char *page, size_t len)
-{
- struct f_midi_opts *opts = to_f_midi_opts(item);
- int ret;
- char *c;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt > 1) {
- ret = -EBUSY;
- goto end;
- }
-
- c = kstrndup(page, len, GFP_KERNEL);
- if (!c) {
- ret = -ENOMEM;
- goto end;
- }
- if (opts->id_allocated)
- kfree(opts->id);
- opts->id = c;
- opts->id_allocated = true;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-CONFIGFS_ATTR(f_midi_opts_, id);
+F_MIDI_OPT_STRING(id);
+F_MIDI_OPT_STRING(interface_string);
static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_index,
@@ -1239,6 +1245,7 @@ static struct configfs_attribute *midi_attrs[] = {
&f_midi_opts_attr_in_ports,
&f_midi_opts_attr_out_ports,
&f_midi_opts_attr_id,
+ &f_midi_opts_attr_interface_string,
NULL,
};
@@ -1262,8 +1269,8 @@ static void f_midi_free_inst(struct usb_function_instance *f)
mutex_unlock(&opts->lock);
if (free) {
- if (opts->id_allocated)
- kfree(opts->id);
+ kfree(opts->id);
+ kfree(opts->interface_string);
kfree(opts);
}
}
@@ -1279,7 +1286,8 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
- opts->id = SNDRV_DEFAULT_STR1;
+ opts->id = NULL;
+ opts->interface_string = NULL;
opts->buflen = 512;
opts->qlen = 32;
opts->in_ports = 1;
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index de16b02d857e..95ec87bed3ee 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2316,7 +2316,7 @@ static void f_midi2_block_opts_release(struct config_item *item)
kfree(opts);
}
-static struct configfs_item_operations f_midi2_block_item_ops = {
+static const struct configfs_item_operations f_midi2_block_item_ops = {
.release = f_midi2_block_opts_release,
};
@@ -2479,11 +2479,11 @@ static void f_midi2_ep_opts_release(struct config_item *item)
kfree(opts);
}
-static struct configfs_item_operations f_midi2_ep_item_ops = {
+static const struct configfs_item_operations f_midi2_ep_item_ops = {
.release = f_midi2_ep_opts_release,
};
-static struct configfs_group_operations f_midi2_ep_group_ops = {
+static const struct configfs_group_operations f_midi2_ep_group_ops = {
.make_group = f_midi2_opts_block_make,
.drop_item = f_midi2_opts_block_drop,
};
@@ -2618,11 +2618,11 @@ static void f_midi2_opts_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_midi2_item_ops = {
+static const struct configfs_item_operations f_midi2_item_ops = {
.release = f_midi2_opts_release,
};
-static struct configfs_group_operations f_midi2_group_ops = {
+static const struct configfs_group_operations f_midi2_group_ops = {
.make_group = f_midi2_opts_ep_make,
.drop_item = f_midi2_opts_ep_drop,
};
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 0e38330271d5..e23adc132f88 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -83,6 +83,11 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
return container_of(f, struct f_ncm, port.func);
}
+static inline struct f_ncm_opts *func_to_ncm_opts(struct usb_function *f)
+{
+ return container_of(f->fi, struct f_ncm_opts, func_inst);
+}
+
/*-------------------------------------------------------------------------*/
/*
@@ -859,6 +864,7 @@ invalid:
static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* Control interface has only altsetting 0 */
@@ -881,12 +887,13 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (alt > 1)
goto fail;
- if (ncm->netdev) {
- DBG(cdev, "reset ncm\n");
- ncm->netdev = NULL;
- gether_disconnect(&ncm->port);
- ncm_reset_values(ncm);
- }
+ scoped_guard(mutex, &opts->lock)
+ if (opts->net) {
+ DBG(cdev, "reset ncm\n");
+ opts->net = NULL;
+ gether_disconnect(&ncm->port);
+ ncm_reset_values(ncm);
+ }
/*
* CDC Network only sends data in non-default altsettings.
@@ -919,7 +926,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
net = gether_connect(&ncm->port);
if (IS_ERR(net))
return PTR_ERR(net);
- ncm->netdev = net;
+ scoped_guard(mutex, &opts->lock)
+ opts->net = net;
}
spin_lock(&ncm->lock);
@@ -1366,14 +1374,16 @@ err:
static void ncm_disable(struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "ncm deactivated\n");
- if (ncm->netdev) {
- ncm->netdev = NULL;
- gether_disconnect(&ncm->port);
- }
+ scoped_guard(mutex, &opts->lock)
+ if (opts->net) {
+ opts->net = NULL;
+ gether_disconnect(&ncm->port);
+ }
if (ncm->notify->enabled) {
usb_ep_disable(ncm->notify);
@@ -1433,39 +1443,44 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_ncm *ncm = func_to_ncm(f);
+ struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f);
struct usb_string *us;
int status = 0;
struct usb_ep *ep;
- struct f_ncm_opts *ncm_opts;
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
+ struct net_device *netdev __free(free_gether_netdev) = NULL;
struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
- ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
-
if (cdev->use_os_string) {
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
if (!os_desc_table)
return -ENOMEM;
}
- mutex_lock(&ncm_opts->lock);
- gether_set_gadget(ncm_opts->net, cdev->gadget);
- if (!ncm_opts->bound) {
- ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
- status = gether_register_netdev(ncm_opts->net);
+ netdev = gether_setup_default();
+ if (IS_ERR(netdev))
+ return -ENOMEM;
+
+ scoped_guard(mutex, &ncm_opts->lock) {
+ gether_apply_opts(netdev, &ncm_opts->net_opts);
+ netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN;
}
- mutex_unlock(&ncm_opts->lock);
+ gether_set_gadget(netdev, cdev->gadget);
+ status = gether_register_netdev(netdev);
if (status)
return status;
- ncm_opts->bound = true;
-
- ncm_string_defs[1].s = ncm->ethaddr;
+ /* export host's Ethernet address in CDC format */
+ status = gether_get_host_addr_cdc(netdev, ncm->ethaddr,
+ sizeof(ncm->ethaddr));
+ if (status < 12)
+ return -EINVAL;
+ ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
@@ -1563,6 +1578,8 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
f->os_desc_n = 1;
}
ncm->notify_req = no_free_ptr(request);
+ ncm->netdev = no_free_ptr(netdev);
+ ncm->port.ioport = netdev_priv(ncm->netdev);
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
@@ -1577,19 +1594,19 @@ static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
}
/* f_ncm_item_ops */
-USB_ETHERNET_CONFIGFS_ITEM(ncm);
+USB_ETHER_OPTS_ITEM(ncm);
/* f_ncm_opts_dev_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
+USB_ETHER_OPTS_ATTR_DEV_ADDR(ncm);
/* f_ncm_opts_host_addr */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
+USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm);
/* f_ncm_opts_qmult */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
+USB_ETHER_OPTS_ATTR_QMULT(ncm);
/* f_ncm_opts_ifname */
-USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
+USB_ETHER_OPTS_ATTR_IFNAME(ncm);
static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
char *page)
@@ -1655,34 +1672,27 @@ static void ncm_free_inst(struct usb_function_instance *f)
struct f_ncm_opts *opts;
opts = container_of(f, struct f_ncm_opts, func_inst);
- if (opts->bound)
- gether_cleanup(netdev_priv(opts->net));
- else
- free_netdev(opts->net);
kfree(opts->ncm_interf_group);
kfree(opts);
}
static struct usb_function_instance *ncm_alloc_inst(void)
{
- struct f_ncm_opts *opts;
+ struct usb_function_instance *ret;
struct usb_os_desc *descs[1];
char *names[1];
struct config_group *ncm_interf_group;
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ struct f_ncm_opts *opts __free(kfree) = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
+
+ opts->net = NULL;
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
+ gether_setup_opts_default(&opts->net_opts, "usb");
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
- opts->net = gether_setup_default();
- if (IS_ERR(opts->net)) {
- struct net_device *net = opts->net;
- kfree(opts);
- return ERR_CAST(net);
- }
opts->max_segment_size = ETH_FRAME_LEN;
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
@@ -1693,26 +1703,22 @@ static struct usb_function_instance *ncm_alloc_inst(void)
ncm_interf_group =
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
names, THIS_MODULE);
- if (IS_ERR(ncm_interf_group)) {
- ncm_free_inst(&opts->func_inst);
+ if (IS_ERR(ncm_interf_group))
return ERR_CAST(ncm_interf_group);
- }
opts->ncm_interf_group = ncm_interf_group;
- return &opts->func_inst;
+ ret = &opts->func_inst;
+ retain_and_null_ptr(opts);
+ return ret;
}
static void ncm_free(struct usb_function *f)
{
- struct f_ncm *ncm;
- struct f_ncm_opts *opts;
+ struct f_ncm_opts *opts = func_to_ncm_opts(f);
- ncm = func_to_ncm(f);
- opts = container_of(f->fi, struct f_ncm_opts, func_inst);
- kfree(ncm);
- mutex_lock(&opts->lock);
- opts->refcnt--;
- mutex_unlock(&opts->lock);
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt--;
+ kfree(func_to_ncm(f));
}
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -1736,13 +1742,15 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
+
+ ncm->port.ioport = NULL;
+ gether_cleanup(netdev_priv(ncm->netdev));
}
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;
- int status;
/* allocate and initialize one new instance */
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
@@ -1750,22 +1758,12 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_ncm_opts, func_inst);
- mutex_lock(&opts->lock);
- opts->refcnt++;
- /* export host's Ethernet address in CDC format */
- status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
- sizeof(ncm->ethaddr));
- if (status < 12) { /* strlen("01234567890a") */
- kfree(ncm);
- mutex_unlock(&opts->lock);
- return ERR_PTR(-EINVAL);
- }
+ scoped_guard(mutex, &opts->lock)
+ opts->refcnt++;
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
- ncm->port.ioport = netdev_priv(opts->net);
- mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
ncm->port.supports_multi_frame = true;
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index 1305e2326cdf..6d498f63183e 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -390,7 +390,7 @@ static void obex_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations obex_item_ops = {
+static const struct configfs_item_operations obex_item_ops = {
.release = obex_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 0aa9e8224cae..d644d5495fdc 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -585,7 +585,7 @@ static void phonet_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations phonet_item_ops = {
+static const struct configfs_item_operations phonet_item_ops = {
.release = phonet_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index d295ade8fa67..b2fa2b56c37e 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -1220,7 +1220,7 @@ static void printer_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations printer_item_ops = {
+static const struct configfs_item_operations printer_item_ops = {
.release = printer_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 0f266bc067f5..e6b412e0e045 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -260,7 +260,7 @@ static void serial_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations serial_item_ops = {
+static const struct configfs_item_operations serial_item_ops = {
.release = serial_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index ec5fd25020fd..22104e9c6cab 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -46,6 +46,7 @@ struct f_sourcesink {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned buflen;
+ unsigned bulk_maxburst;
unsigned bulk_qlen;
unsigned iso_qlen;
};
@@ -328,6 +329,12 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
source_sink_intf_alt0.bInterfaceNumber = id;
source_sink_intf_alt1.bInterfaceNumber = id;
+ if (ss->bulk_maxburst > 15)
+ ss->bulk_maxburst = 15;
+
+ ss_source_comp_desc.bMaxBurst = ss->bulk_maxburst;
+ ss_sink_comp_desc.bMaxBurst = ss->bulk_maxburst;
+
/* allocate bulk endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
if (!ss->in_ep) {
@@ -853,6 +860,7 @@ static struct usb_function *source_sink_alloc_func(
ss->isoc_mult = ss_opts->isoc_mult;
ss->isoc_maxburst = ss_opts->isoc_maxburst;
ss->buflen = ss_opts->bulk_buflen;
+ ss->bulk_maxburst = ss_opts->bulk_maxburst;
ss->bulk_qlen = ss_opts->bulk_qlen;
ss->iso_qlen = ss_opts->iso_qlen;
@@ -882,7 +890,7 @@ static void ss_attr_release(struct config_item *item)
usb_put_function_instance(&ss_opts->func_inst);
}
-static struct configfs_item_operations ss_item_ops = {
+static const struct configfs_item_operations ss_item_ops = {
.release = ss_attr_release,
};
@@ -1101,6 +1109,49 @@ end:
CONFIGFS_ATTR(f_ss_opts_, isoc_maxburst);
+static ssize_t f_ss_opts_bulk_maxburst_show(struct config_item *item, char *page)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sysfs_emit(page, "%u\n", opts->bulk_maxburst);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_bulk_maxburst_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ struct f_ss_opts *opts = to_f_ss_opts(item);
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 15) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->bulk_maxburst = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+CONFIGFS_ATTR(f_ss_opts_, bulk_maxburst);
+
static ssize_t f_ss_opts_bulk_buflen_show(struct config_item *item, char *page)
{
struct f_ss_opts *opts = to_f_ss_opts(item);
@@ -1222,6 +1273,7 @@ static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_attr_isoc_mult,
&f_ss_opts_attr_isoc_maxburst,
&f_ss_opts_attr_bulk_buflen,
+ &f_ss_opts_attr_bulk_maxburst,
&f_ss_opts_attr_bulk_qlen,
&f_ss_opts_attr_iso_qlen,
NULL,
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 6e8804f04baa..efb94fd82533 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1227,7 +1227,7 @@ static void usbg_submit_cmd(struct usbg_cmd *cmd)
goto out;
target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf,
- cmd->sense_iu.sense, cmd->unpacked_lun, 0,
+ cmd->sense_iu.sense, cmd->unpacked_lun, cmd->data_len,
cmd->prio_attr, dir, flags);
return;
@@ -1389,6 +1389,7 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req)
cmd->tmr_func = 0;
cmd->tmr_rsp = RC_RESPONSE_UNKNOWN;
cmd->flags = 0;
+ cmd->data_len = 0;
cmd_iu = (struct command_iu *)iu;
@@ -2446,7 +2447,7 @@ static void tcm_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations tcm_item_ops = {
+static const struct configfs_item_operations tcm_item_ops = {
.release = tcm_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 9da9fb4e1239..efe9f270b02d 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -1512,7 +1512,7 @@ static void f_uac1_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac1_item_ops = {
+static const struct configfs_item_operations f_uac1_item_ops = {
.release = f_uac1_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index 49cf5aae90ca..8fc452b4b39a 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -812,7 +812,7 @@ static void f_uac1_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac1_item_ops = {
+static const struct configfs_item_operations f_uac1_item_ops = {
.release = f_uac1_attr_release,
};
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index dd252ff2fb4e..98f0f50dc7a8 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1874,7 +1874,7 @@ static void f_uac2_attr_release(struct config_item *item)
usb_put_function_instance(&opts->func_inst);
}
-static struct configfs_item_operations f_uac2_item_ops = {
+static const struct configfs_item_operations f_uac2_item_ops = {
.release = f_uac2_attr_release,
};
diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 98b8462ad538..7bd66004821f 100644
--- a/drivers/usb/gadget/function/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
@@ -34,6 +34,7 @@ struct f_ss_opts {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned bulk_buflen;
+ unsigned bulk_maxburst;
unsigned bulk_qlen;
unsigned iso_qlen;
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index c47965d850d4..338f6e2a85a9 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -1040,6 +1040,36 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)
}
EXPORT_SYMBOL_GPL(gether_set_ifname);
+void gether_setup_opts_default(struct gether_opts *opts, const char *name)
+{
+ opts->qmult = QMULT_DEFAULT;
+ snprintf(opts->name, sizeof(opts->name), "%s%%d", name);
+ eth_random_addr(opts->dev_mac);
+ opts->addr_assign_type = NET_ADDR_RANDOM;
+ eth_random_addr(opts->host_mac);
+}
+EXPORT_SYMBOL_GPL(gether_setup_opts_default);
+
+void gether_apply_opts(struct net_device *net, struct gether_opts *opts)
+{
+ struct eth_dev *dev = netdev_priv(net);
+
+ dev->qmult = opts->qmult;
+
+ if (opts->ifname_set) {
+ strscpy(net->name, opts->name, sizeof(net->name));
+ dev->ifname_set = true;
+ }
+
+ memcpy(dev->host_mac, opts->host_mac, sizeof(dev->host_mac));
+
+ if (opts->addr_assign_type == NET_ADDR_SET) {
+ memcpy(dev->dev_mac, opts->dev_mac, sizeof(dev->dev_mac));
+ net->addr_assign_type = opts->addr_assign_type;
+ }
+}
+EXPORT_SYMBOL_GPL(gether_apply_opts);
+
void gether_suspend(struct gether *link)
{
struct eth_dev *dev = link->ioport;
@@ -1096,6 +1126,21 @@ void gether_cleanup(struct eth_dev *dev)
}
EXPORT_SYMBOL_GPL(gether_cleanup);
+void gether_unregister_free_netdev(struct net_device *net)
+{
+ if (!net)
+ return;
+
+ struct eth_dev *dev = netdev_priv(net);
+
+ if (net->reg_state == NETREG_REGISTERED) {
+ unregister_netdev(net);
+ flush_work(&dev->work);
+ }
+ free_netdev(net);
+}
+EXPORT_SYMBOL_GPL(gether_unregister_free_netdev);
+
/**
* gether_connect - notify network layer that USB link is active
* @link: the USB link, set up with endpoints, descriptors matching
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 34be220cef77..a212a8ec5eb1 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -38,6 +38,31 @@
struct eth_dev;
+/**
+ * struct gether_opts - Options for Ethernet gadget function instances
+ * @name: Pattern for the network interface name (e.g., "usb%d").
+ * Used to generate the net device name.
+ * @qmult: Queue length multiplier for high/super speed.
+ * @host_mac: The MAC address to be used by the host side.
+ * @dev_mac: The MAC address to be used by the device side.
+ * @ifname_set: True if the interface name pattern has been set by userspace.
+ * @addr_assign_type: The method used for assigning the device MAC address
+ * (e.g., NET_ADDR_RANDOM, NET_ADDR_SET).
+ *
+ * This structure caches network-related settings provided through configfs
+ * before the net_device is fully instantiated. This allows for early
+ * configuration while deferring net_device allocation until the function
+ * is bound.
+ */
+struct gether_opts {
+ char name[IFNAMSIZ];
+ unsigned int qmult;
+ u8 host_mac[ETH_ALEN];
+ u8 dev_mac[ETH_ALEN];
+ bool ifname_set;
+ unsigned char addr_assign_type;
+};
+
/*
* This represents the USB side of an "ethernet" link, managed by a USB
* function which provides control and (maybe) framing. Two functions
@@ -258,6 +283,11 @@ int gether_get_ifname(struct net_device *net, char *name, int len);
int gether_set_ifname(struct net_device *net, const char *name, int len);
void gether_cleanup(struct eth_dev *dev);
+void gether_unregister_free_netdev(struct net_device *net);
+DEFINE_FREE(free_gether_netdev, struct net_device *, gether_unregister_free_netdev(_T));
+
+void gether_setup_opts_default(struct gether_opts *opts, const char *name);
+void gether_apply_opts(struct net_device *net, struct gether_opts *opts);
void gether_suspend(struct gether *link);
void gether_resume(struct gether *link);
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index f558c3139ebe..217990a266b2 100644
--- a/drivers/usb/gadget/function/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -13,6 +13,13 @@
#ifndef __U_ETHER_CONFIGFS_H
#define __U_ETHER_CONFIGFS_H
+#include <linux/cleanup.h>
+#include <linux/hex.h>
+#include <linux/if_ether.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+
#define USB_ETHERNET_CONFIGFS_ITEM(_f_) \
static void _f_##_attr_release(struct config_item *item) \
{ \
@@ -21,7 +28,7 @@
usb_put_function_instance(&opts->func_inst); \
} \
\
- static struct configfs_item_operations _f_##_item_ops = { \
+ static const struct configfs_item_operations _f_##_item_ops = { \
.release = _f_##_attr_release, \
}
@@ -197,4 +204,174 @@ out: \
\
CONFIGFS_ATTR(_f_##_opts_, _n_)
+#define USB_ETHER_OPTS_ITEM(_f_) \
+ static void _f_##_attr_release(struct config_item *item) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ \
+ usb_put_function_instance(&opts->func_inst); \
+ } \
+ \
+ static struct configfs_item_operations _f_##_item_ops = { \
+ .release = _f_##_attr_release, \
+ }
+
+#define USB_ETHER_OPTS_ATTR_DEV_ADDR(_f_) \
+ static ssize_t _f_##_opts_dev_addr_show(struct config_item *item, \
+ char *page) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ \
+ guard(mutex)(&opts->lock); \
+ return sysfs_emit(page, "%pM\n", opts->net_opts.dev_mac); \
+ } \
+ \
+ static ssize_t _f_##_opts_dev_addr_store(struct config_item *item, \
+ const char *page, size_t len) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ u8 new_addr[ETH_ALEN]; \
+ const char *p = page; \
+ \
+ guard(mutex)(&opts->lock); \
+ if (opts->refcnt) \
+ return -EBUSY; \
+ \
+ for (int i = 0; i < ETH_ALEN; i++) { \
+ unsigned char num; \
+ if ((*p == '.') || (*p == ':')) \
+ p++; \
+ num = hex_to_bin(*p++) << 4; \
+ num |= hex_to_bin(*p++); \
+ new_addr[i] = num; \
+ } \
+ if (!is_valid_ether_addr(new_addr)) \
+ return -EINVAL; \
+ memcpy(opts->net_opts.dev_mac, new_addr, ETH_ALEN); \
+ opts->net_opts.addr_assign_type = NET_ADDR_SET; \
+ return len; \
+ } \
+ \
+ CONFIGFS_ATTR(_f_##_opts_, dev_addr)
+
+#define USB_ETHER_OPTS_ATTR_HOST_ADDR(_f_) \
+ static ssize_t _f_##_opts_host_addr_show(struct config_item *item, \
+ char *page) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ \
+ guard(mutex)(&opts->lock); \
+ return sysfs_emit(page, "%pM\n", opts->net_opts.host_mac); \
+ } \
+ \
+ static ssize_t _f_##_opts_host_addr_store(struct config_item *item, \
+ const char *page, size_t len) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ u8 new_addr[ETH_ALEN]; \
+ const char *p = page; \
+ \
+ guard(mutex)(&opts->lock); \
+ if (opts->refcnt) \
+ return -EBUSY; \
+ \
+ for (int i = 0; i < ETH_ALEN; i++) { \
+ unsigned char num; \
+ if ((*p == '.') || (*p == ':')) \
+ p++; \
+ num = hex_to_bin(*p++) << 4; \
+ num |= hex_to_bin(*p++); \
+ new_addr[i] = num; \
+ } \
+ if (!is_valid_ether_addr(new_addr)) \
+ return -EINVAL; \
+ memcpy(opts->net_opts.host_mac, new_addr, ETH_ALEN); \
+ return len; \
+ } \
+ \
+ CONFIGFS_ATTR(_f_##_opts_, host_addr)
+
+#define USB_ETHER_OPTS_ATTR_QMULT(_f_) \
+ static ssize_t _f_##_opts_qmult_show(struct config_item *item, \
+ char *page) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ \
+ guard(mutex)(&opts->lock); \
+ return sysfs_emit(page, "%u\n", opts->net_opts.qmult); \
+ } \
+ \
+ static ssize_t _f_##_opts_qmult_store(struct config_item *item, \
+ const char *page, size_t len) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ u32 val; \
+ int ret; \
+ \
+ guard(mutex)(&opts->lock); \
+ if (opts->refcnt) \
+ return -EBUSY; \
+ \
+ ret = kstrtou32(page, 0, &val); \
+ if (ret) \
+ return ret; \
+ \
+ opts->net_opts.qmult = val; \
+ return len; \
+ } \
+ \
+ CONFIGFS_ATTR(_f_##_opts_, qmult)
+
+#define USB_ETHER_OPTS_ATTR_IFNAME(_f_) \
+ static ssize_t _f_##_opts_ifname_show(struct config_item *item, \
+ char *page) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ const char *name; \
+ \
+ guard(mutex)(&opts->lock); \
+ rtnl_lock(); \
+ if (opts->net_opts.ifname_set) \
+ name = opts->net_opts.name; \
+ else if (opts->net) \
+ name = netdev_name(opts->net); \
+ else \
+ name = "(inactive net_device)"; \
+ rtnl_unlock(); \
+ return sysfs_emit(page, "%s\n", name); \
+ } \
+ \
+ static ssize_t _f_##_opts_ifname_store(struct config_item *item, \
+ const char *page, size_t len) \
+ { \
+ struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
+ char tmp[IFNAMSIZ]; \
+ const char *p; \
+ size_t c_len = len; \
+ \
+ if (c_len > 0 && page[c_len - 1] == '\n') \
+ c_len--; \
+ \
+ if (c_len >= sizeof(tmp)) \
+ return -E2BIG; \
+ \
+ strscpy(tmp, page, c_len + 1); \
+ if (!dev_valid_name(tmp)) \
+ return -EINVAL; \
+ \
+ /* Require exactly one %d */ \
+ p = strchr(tmp, '%'); \
+ if (!p || p[1] != 'd' || strchr(p + 2, '%')) \
+ return -EINVAL; \
+ \
+ guard(mutex)(&opts->lock); \
+ if (opts->refcnt) \
+ return -EBUSY; \
+ strscpy(opts->net_opts.name, tmp, sizeof(opts->net_opts.name)); \
+ opts->net_opts.ifname_set = true; \
+ return len; \
+ } \
+ \
+ CONFIGFS_ATTR(_f_##_opts_, ifname)
+
#endif /* __U_ETHER_CONFIGFS_H */
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
index 2e400b495cb8..41cb8aa73f09 100644
--- a/drivers/usb/gadget/function/u_midi.h
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -19,7 +19,7 @@ struct f_midi_opts {
struct usb_function_instance func_inst;
int index;
char *id;
- bool id_allocated;
+ char *interface_string;
unsigned int in_ports;
unsigned int out_ports;
unsigned int buflen;
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index 49ec095cdb4b..d99330fe31e8 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -15,11 +15,13 @@
#include <linux/usb/composite.h>
+#include "u_ether.h"
+
struct f_ncm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
- bool bound;
+ struct gether_opts net_opts;
struct config_group *ncm_interf_group;
struct usb_os_desc ncm_os_desc;
char ncm_ext_compat_id[16];
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 5a87516ddb31..4cec6c586d67 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -128,7 +128,7 @@ static void uvcg_config_item_release(struct config_item *item)
kfree(group);
}
-static struct configfs_item_operations uvcg_config_item_ops = {
+static const struct configfs_item_operations uvcg_config_item_ops = {
.release = uvcg_config_item_release,
};
@@ -285,7 +285,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group,
return &h->item;
}
-static struct configfs_group_operations uvcg_control_header_grp_ops = {
+static const struct configfs_group_operations uvcg_control_header_grp_ops = {
.make_item = uvcg_control_header_make,
};
@@ -1233,7 +1233,7 @@ static void uvcg_extension_drop_link(struct config_item *src, struct config_item
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_extension_item_ops = {
+static const struct configfs_item_operations uvcg_extension_item_ops = {
.release = uvcg_extension_release,
.allow_link = uvcg_extension_allow_link,
.drop_link = uvcg_extension_drop_link,
@@ -1298,7 +1298,7 @@ static struct config_item *uvcg_extension_make(struct config_group *group, const
return &xu->item;
}
-static struct configfs_group_operations uvcg_extensions_grp_ops = {
+static const struct configfs_group_operations uvcg_extensions_grp_ops = {
.make_item = uvcg_extension_make,
.drop_item = uvcg_extension_drop,
};
@@ -1414,7 +1414,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_control_class_item_ops = {
+static const struct configfs_item_operations uvcg_control_class_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_control_class_allow_link,
.drop_link = uvcg_control_class_drop_link,
@@ -1664,7 +1664,7 @@ static void uvcg_format_drop_link(struct config_item *src, struct config_item *t
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_format_item_operations = {
+static const struct configfs_item_operations uvcg_format_item_operations = {
.release = uvcg_config_item_release,
.allow_link = uvcg_format_allow_link,
.drop_link = uvcg_format_drop_link,
@@ -1840,7 +1840,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_streaming_header_item_ops = {
+static const struct configfs_item_operations uvcg_streaming_header_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_streaming_header_allow_link,
.drop_link = uvcg_streaming_header_drop_link,
@@ -1914,7 +1914,7 @@ static struct config_item
return &h->item;
}
-static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
+static const struct configfs_group_operations uvcg_streaming_header_grp_ops = {
.make_item = uvcg_streaming_header_make,
};
@@ -2261,7 +2261,7 @@ static void uvcg_format_set_indices(struct config_group *fmt)
* streaming/uncompressed/<NAME>
*/
-static struct configfs_group_operations uvcg_uncompressed_group_ops = {
+static const struct configfs_group_operations uvcg_uncompressed_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2508,7 +2508,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
+static const struct configfs_group_operations uvcg_uncompressed_grp_ops = {
.make_group = uvcg_uncompressed_make,
};
@@ -2525,7 +2525,7 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = {
* streaming/mjpeg/<NAME>
*/
-static struct configfs_group_operations uvcg_mjpeg_group_ops = {
+static const struct configfs_group_operations uvcg_mjpeg_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2698,7 +2698,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
+static const struct configfs_group_operations uvcg_mjpeg_grp_ops = {
.make_group = uvcg_mjpeg_make,
};
@@ -2715,7 +2715,7 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = {
* streaming/framebased/<NAME>
*/
-static struct configfs_group_operations uvcg_framebased_group_ops = {
+static const struct configfs_group_operations uvcg_framebased_group_ops = {
.make_item = uvcg_frame_make,
.drop_item = uvcg_frame_drop,
};
@@ -2953,7 +2953,7 @@ static struct config_group *uvcg_framebased_make(struct config_group *group,
return &h->fmt.group;
}
-static struct configfs_group_operations uvcg_framebased_grp_ops = {
+static const struct configfs_group_operations uvcg_framebased_grp_ops = {
.make_group = uvcg_framebased_make,
};
@@ -3056,7 +3056,7 @@ static void uvcg_color_matching_release(struct config_item *item)
kfree(color_match);
}
-static struct configfs_item_operations uvcg_color_matching_item_ops = {
+static const struct configfs_item_operations uvcg_color_matching_item_ops = {
.release = uvcg_color_matching_release,
};
@@ -3089,7 +3089,7 @@ static struct config_group *uvcg_color_matching_make(struct config_group *group,
return &color_match->group;
}
-static struct configfs_group_operations uvcg_color_matching_grp_group_ops = {
+static const struct configfs_group_operations uvcg_color_matching_grp_group_ops = {
.make_group = uvcg_color_matching_make,
};
@@ -3530,7 +3530,7 @@ out:
mutex_unlock(su_mutex);
}
-static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+static const struct configfs_item_operations uvcg_streaming_class_item_ops = {
.release = uvcg_config_item_release,
.allow_link = uvcg_streaming_class_allow_link,
.drop_link = uvcg_streaming_class_drop_link,
@@ -3698,7 +3698,7 @@ static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt)
mutex_unlock(&opts->lock);
}
-static struct configfs_item_operations uvc_func_item_ops = {
+static const struct configfs_item_operations uvc_func_item_ops = {
.release = uvc_func_item_release,
.allow_link = uvc_func_allow_link,
.drop_link = uvc_func_drop_link,
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
index f2685f89b3e5..19c1849ae665 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/dma-mapping.h>
+#include <linux/reset.h>
#include "vhub.h"
@@ -280,6 +281,8 @@ static void ast_vhub_remove(struct platform_device *pdev)
if (vhub->clk)
clk_disable_unprepare(vhub->clk);
+ reset_control_assert(vhub->rst);
+
spin_unlock_irqrestore(&vhub->lock, flags);
if (vhub->ep0_bufs)
@@ -294,6 +297,7 @@ static void ast_vhub_remove(struct platform_device *pdev)
static int ast_vhub_probe(struct platform_device *pdev)
{
enum usb_device_speed max_speed;
+ const u64 *dma_mask_ptr;
struct ast_vhub *vhub;
struct resource *res;
int i, rc = 0;
@@ -348,6 +352,16 @@ static int ast_vhub_probe(struct platform_device *pdev)
goto err;
}
+ vhub->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+ if (IS_ERR(vhub->rst)) {
+ rc = PTR_ERR(vhub->rst);
+ goto err;
+ }
+
+ rc = reset_control_deassert(vhub->rst);
+ if (rc)
+ goto err;
+
/* Check if we need to limit the HW to USB1 */
max_speed = usb_get_maximum_speed(&pdev->dev);
if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH)
@@ -370,6 +384,12 @@ static int ast_vhub_probe(struct platform_device *pdev)
goto err;
}
+ dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev);
+ if (dma_mask_ptr) {
+ rc = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr);
+ if (rc)
+ goto err;
+ }
/*
* Allocate DMA buffers for all EP0s in one chunk,
* one per port and one for the vHub itself
@@ -412,15 +432,25 @@ static int ast_vhub_probe(struct platform_device *pdev)
return rc;
}
+static const u64 dma_mask_32 = DMA_BIT_MASK(32);
+static const u64 dma_mask_64 = DMA_BIT_MASK(64);
+
static const struct of_device_id ast_vhub_dt_ids[] = {
{
.compatible = "aspeed,ast2400-usb-vhub",
+ .data = &dma_mask_32,
},
{
.compatible = "aspeed,ast2500-usb-vhub",
+ .data = &dma_mask_32,
},
{
.compatible = "aspeed,ast2600-usb-vhub",
+ .data = &dma_mask_32,
+ },
+ {
+ .compatible = "aspeed,ast2700-usb-vhub",
+ .data = &dma_mask_64,
},
{ }
};
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
index 6b9dfa6e10eb..aca2050e2db0 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -388,6 +388,7 @@ struct ast_vhub {
spinlock_t lock;
struct work_struct wake_work;
struct clk *clk;
+ struct reset_control *rst;
/* EP0 DMA buffers allocated in one chunk */
void *ep0_bufs;
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
index 5c3d8b64c0e7..f47aac078f6b 100644
--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -35,8 +35,8 @@ static int poll_oip(struct bdc *bdc, u32 usec)
u32 status;
int ret;
- ret = readl_poll_timeout(bdc->regs + BDC_BDCSC, status,
- (BDC_CSTS(status) != BDC_OIP), 10, usec);
+ ret = readl_poll_timeout_atomic(bdc->regs + BDC_BDCSC, status,
+ (BDC_CSTS(status) != BDC_OIP), 10, usec);
if (ret)
dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status);
else
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index 9d2007f448c0..7f7251c10e95 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -3392,17 +3392,18 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
{
u32 val, imod;
+ val = xudc_readl(xudc, BLCG);
if (xudc->soc->has_ipfs) {
- val = xudc_readl(xudc, BLCG);
val |= BLCG_ALL;
val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE |
BLCG_COREPLL_PWRDN);
val |= BLCG_IOPLL_0_PWRDN;
val |= BLCG_IOPLL_1_PWRDN;
val |= BLCG_IOPLL_2_PWRDN;
-
- xudc_writel(xudc, val, BLCG);
+ } else {
+ val &= ~BLCG_COREPLL_PWRDN;
}
+ xudc_writel(xudc, val, BLCG);
if (xudc->soc->port_speed_quirk)
tegra_xudc_limit_port_speed(xudc);
@@ -3953,6 +3954,7 @@ static void tegra_xudc_remove(struct platform_device *pdev)
static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
{
unsigned long flags;
+ u32 val;
dev_dbg(xudc->dev, "entering ELPG\n");
@@ -3965,6 +3967,10 @@ static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc)
spin_unlock_irqrestore(&xudc->lock, flags);
+ val = xudc_readl(xudc, BLCG);
+ val |= BLCG_COREPLL_PWRDN;
+ xudc_writel(xudc, val, BLCG);
+
clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks);
regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index c4f17ce5c77b..0a277a07cf70 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -383,18 +383,6 @@ config USB_ISP116X_HCD
To compile this driver as a module, choose M here: the
module will be called isp116x-hcd.
-config USB_ISP1362_HCD
- tristate "ISP1362 HCD support"
- depends on HAS_IOPORT
- depends on COMPILE_TEST # nothing uses this
- help
- Supports the Philips ISP1362 chip as a host controller
-
- This driver does not support isochronous transfers.
-
- To compile this driver as a module, choose M here: the
- module will be called isp1362-hcd.
-
config USB_MAX3421_HCD
tristate "MAX3421 HCD (USB-over-SPI) support"
depends on USB && SPI
@@ -616,7 +604,7 @@ config USB_UHCI_ASPEED
config USB_FHCI_HCD
tristate "Freescale QE USB Host Controller support"
- depends on OF_GPIO && QE_GPIO && QUICC_ENGINE
+ depends on QE_GPIO && QUICC_ENGINE
select FSL_GTM
select QE_USB
help
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 4df946c05ba0..a07e7ba9cd53 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -52,7 +52,6 @@ obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
-obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 6d1d190c914d..3c46bb18c7f3 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1354,12 +1354,6 @@ static int __init ehci_hcd_init(void)
if (usb_disabled())
return -ENODEV;
- set_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
- if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) ||
- test_bit(USB_OHCI_LOADED, &usb_hcds_loaded))
- printk(KERN_WARNING "Warning! ehci_hcd should always be loaded"
- " before uhci_hcd and ohci_hcd, not after\n");
-
pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n",
hcd_name,
sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
@@ -1390,7 +1384,6 @@ clean0:
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
#endif
- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
return retval;
}
module_init(ehci_hcd_init);
@@ -1404,6 +1397,5 @@ static void __exit ehci_hcd_cleanup(void)
#ifdef CONFIG_DYNAMIC_DEBUG
debugfs_remove(ehci_debug_root);
#endif
- clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);
}
module_exit(ehci_hcd_cleanup);
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
deleted file mode 100644
index 954fc5ad565b..000000000000
--- a/drivers/usb/host/isp1362-hcd.c
+++ /dev/null
@@ -1,2769 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * ISP1362 HCD (Host Controller Driver) for USB.
- *
- * Copyright (C) 2005 Lothar Wassmann <LW@KARO-electronics.de>
- *
- * Derived from the SL811 HCD, rewritten for ISP116x.
- * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
- *
- * Portions:
- * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
- * Copyright (C) 2004 David Brownell
- */
-
-/*
- * The ISP1362 chip requires a large delay (300ns and 462ns) between
- * accesses to the address and data register.
- * The following timing options exist:
- *
- * 1. Configure your memory controller to add such delays if it can (the best)
- * 2. Implement platform-specific delay function possibly
- * combined with configuring the memory controller; see
- * include/linux/usb_isp1362.h for more info.
- * 3. Use ndelay (easiest, poorest).
- *
- * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the
- * platform specific section of isp1362.h to select the appropriate variant.
- *
- * Also note that according to the Philips "ISP1362 Errata" document
- * Rev 1.00 from 27 May data corruption may occur when the #WR signal
- * is reasserted (even with #CS deasserted) within 132ns after a
- * write cycle to any controller register. If the hardware doesn't
- * implement the recommended fix (gating the #WR with #CS) software
- * must ensure that no further write cycle (not necessarily to the chip!)
- * is issued by the CPU within this interval.
-
- * For PXA25x this can be ensured by using VLIO with the maximum
- * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz.
- */
-
-#undef ISP1362_DEBUG
-
-/*
- * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and
- * GET_INTERFACE requests correctly when the SETUP and DATA stages of the
- * requests are carried out in separate frames. This will delay any SETUP
- * packets until the start of the next frame so that this situation is
- * unlikely to occur (and makes usbtest happy running with a PXA255 target
- * device).
- */
-#undef BUGGY_PXA2XX_UDC_USBTEST
-
-#undef PTD_TRACE
-#undef URB_TRACE
-#undef VERBOSE
-#undef REGISTERS
-
-/* This enables a memory test on the ISP1362 chip memory to make sure the
- * chip access timing is correct.
- */
-#undef CHIP_BUFFER_TEST
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/usb.h>
-#include <linux/usb/isp1362.h>
-#include <linux/usb/hcd.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/io.h>
-#include <linux/bitmap.h>
-#include <linux/prefetch.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-
-#include <asm/irq.h>
-#include <asm/byteorder.h>
-#include <linux/unaligned.h>
-
-static int dbg_level;
-#ifdef ISP1362_DEBUG
-module_param(dbg_level, int, 0644);
-#else
-module_param(dbg_level, int, 0);
-#endif
-
-#include "../core/usb.h"
-#include "isp1362.h"
-
-
-#define DRIVER_VERSION "2005-04-04"
-#define DRIVER_DESC "ISP1362 USB Host Controller Driver"
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
-static const char hcd_name[] = "isp1362-hcd";
-
-static void isp1362_hc_stop(struct usb_hcd *hcd);
-static int isp1362_hc_start(struct usb_hcd *hcd);
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * When called from the interrupthandler only isp1362_hcd->irqenb is modified,
- * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon
- * completion.
- * We don't need a 'disable' counterpart, since interrupts will be disabled
- * only by the interrupt handler.
- */
-static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask)
-{
- if ((isp1362_hcd->irqenb | mask) == isp1362_hcd->irqenb)
- return;
- if (mask & ~isp1362_hcd->irqenb)
- isp1362_write_reg16(isp1362_hcd, HCuPINT, mask & ~isp1362_hcd->irqenb);
- isp1362_hcd->irqenb |= mask;
- if (isp1362_hcd->irq_active)
- return;
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd,
- u16 offset)
-{
- struct isp1362_ep_queue *epq = NULL;
-
- if (offset < isp1362_hcd->istl_queue[1].buf_start)
- epq = &isp1362_hcd->istl_queue[0];
- else if (offset < isp1362_hcd->intl_queue.buf_start)
- epq = &isp1362_hcd->istl_queue[1];
- else if (offset < isp1362_hcd->atl_queue.buf_start)
- epq = &isp1362_hcd->intl_queue;
- else if (offset < isp1362_hcd->atl_queue.buf_start +
- isp1362_hcd->atl_queue.buf_size)
- epq = &isp1362_hcd->atl_queue;
-
- if (epq)
- DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name);
- else
- pr_warn("%s: invalid PTD $%04x\n", __func__, offset);
-
- return epq;
-}
-
-static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index)
-{
- int offset;
-
- if (index * epq->blk_size > epq->buf_size) {
- pr_warn("%s: Bad %s index %d(%d)\n",
- __func__, epq->name, index,
- epq->buf_size / epq->blk_size);
- return -EINVAL;
- }
- offset = epq->buf_start + index * epq->blk_size;
- DBG(3, "%s: %s PTD[%02x] # %04x\n", __func__, epq->name, index, offset);
-
- return offset;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size,
- int mps)
-{
- u16 xfer_size = min_t(size_t, MAX_XFER_SIZE, size);
-
- xfer_size = min_t(size_t, xfer_size, epq->buf_avail * epq->blk_size - PTD_HEADER_SIZE);
- if (xfer_size < size && xfer_size % mps)
- xfer_size -= xfer_size % mps;
-
- return xfer_size;
-}
-
-static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
- struct isp1362_ep *ep, u16 len)
-{
- int ptd_offset = -EINVAL;
- int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1;
- int found;
-
- BUG_ON(len > epq->buf_size);
-
- if (!epq->buf_avail)
- return -ENOMEM;
-
- if (ep->num_ptds)
- pr_err("%s: %s len %d/%d num_ptds %d buf_map %08lx skip_map %08lx\n", __func__,
- epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map);
- BUG_ON(ep->num_ptds != 0);
-
- found = bitmap_find_next_zero_area(&epq->buf_map, epq->buf_count, 0,
- num_ptds, 0);
- if (found >= epq->buf_count)
- return -EOVERFLOW;
-
- DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__,
- num_ptds, found, len, (int)(epq->blk_size - PTD_HEADER_SIZE));
- ptd_offset = get_ptd_offset(epq, found);
- WARN_ON(ptd_offset < 0);
- ep->ptd_offset = ptd_offset;
- ep->num_ptds += num_ptds;
- epq->buf_avail -= num_ptds;
- BUG_ON(epq->buf_avail > epq->buf_count);
- ep->ptd_index = found;
- bitmap_set(&epq->buf_map, found, num_ptds);
- DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n",
- __func__, epq->name, ep->ptd_index, ep->ptd_offset,
- epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map);
-
- return found;
-}
-
-static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
-{
- int last = ep->ptd_index + ep->num_ptds;
-
- if (last > epq->buf_count)
- pr_err("%s: ep %p req %d len %d %s PTD[%d] $%04x num_ptds %d buf_count %d buf_avail %d buf_map %08lx skip_map %08lx\n",
- __func__, ep, ep->num_req, ep->length, epq->name, ep->ptd_index,
- ep->ptd_offset, ep->num_ptds, epq->buf_count, epq->buf_avail,
- epq->buf_map, epq->skip_map);
- BUG_ON(last > epq->buf_count);
-
- bitmap_clear(&epq->buf_map, ep->ptd_index, ep->num_ptds);
- bitmap_set(&epq->skip_map, ep->ptd_index, ep->num_ptds);
- epq->buf_avail += ep->num_ptds;
- epq->ptd_count--;
-
- BUG_ON(epq->buf_avail > epq->buf_count);
- BUG_ON(epq->ptd_count > epq->buf_count);
-
- DBG(1, "%s: Done %s PTDs $%04x released %d avail %d count %d\n",
- __func__, epq->name,
- ep->ptd_offset, ep->num_ptds, epq->buf_avail, epq->buf_count);
- DBG(1, "%s: buf_map %08lx skip_map %08lx\n", __func__,
- epq->buf_map, epq->skip_map);
-
- ep->num_ptds = 0;
- ep->ptd_offset = -EINVAL;
- ep->ptd_index = -EINVAL;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- Set up PTD's.
-*/
-static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
- struct isp1362_ep *ep, struct isp1362_ep_queue *epq,
- u16 fno)
-{
- struct ptd *ptd;
- int toggle;
- int dir;
- u16 len;
- size_t buf_len = urb->transfer_buffer_length - urb->actual_length;
-
- DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep);
-
- ptd = &ep->ptd;
-
- ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length;
-
- switch (ep->nextpid) {
- case USB_PID_IN:
- toggle = usb_gettoggle(urb->dev, ep->epnum, 0);
- dir = PTD_DIR_IN;
- if (usb_pipecontrol(urb->pipe)) {
- len = min_t(size_t, ep->maxpacket, buf_len);
- } else if (usb_pipeisoc(urb->pipe)) {
- len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE);
- ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset;
- } else
- len = max_transfer_size(epq, buf_len, ep->maxpacket);
- DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket,
- (int)buf_len);
- break;
- case USB_PID_OUT:
- toggle = usb_gettoggle(urb->dev, ep->epnum, 1);
- dir = PTD_DIR_OUT;
- if (usb_pipecontrol(urb->pipe))
- len = min_t(size_t, ep->maxpacket, buf_len);
- else if (usb_pipeisoc(urb->pipe))
- len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE);
- else
- len = max_transfer_size(epq, buf_len, ep->maxpacket);
- if (len == 0)
- pr_info("%s: Sending ZERO packet: %d\n", __func__,
- urb->transfer_flags & URB_ZERO_PACKET);
- DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket,
- (int)buf_len);
- break;
- case USB_PID_SETUP:
- toggle = 0;
- dir = PTD_DIR_SETUP;
- len = sizeof(struct usb_ctrlrequest);
- DBG(1, "%s: SETUP len %d\n", __func__, len);
- ep->data = urb->setup_packet;
- break;
- case USB_PID_ACK:
- toggle = 1;
- len = 0;
- dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ?
- PTD_DIR_OUT : PTD_DIR_IN;
- DBG(1, "%s: ACK len %d\n", __func__, len);
- break;
- default:
- toggle = dir = len = 0;
- pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid);
- BUG_ON(1);
- }
-
- ep->length = len;
- if (!len)
- ep->data = NULL;
-
- ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle);
- ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) |
- PTD_EP(ep->epnum);
- ptd->len = PTD_LEN(len) | PTD_DIR(dir);
- ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));
-
- if (usb_pipeint(urb->pipe)) {
- ptd->faddr |= PTD_SF_INT(ep->branch);
- ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0);
- }
- if (usb_pipeisoc(urb->pipe))
- ptd->faddr |= PTD_SF_ISO(fno);
-
- DBG(1, "%s: Finished\n", __func__);
-}
-
-static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
- struct isp1362_ep_queue *epq)
-{
- struct ptd *ptd = &ep->ptd;
- int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length;
-
- prefetch(ptd);
- isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE);
- if (len)
- isp1362_write_buffer(isp1362_hcd, ep->data,
- ep->ptd_offset + PTD_HEADER_SIZE, len);
-
- dump_ptd(ptd);
- dump_ptd_out_data(ptd, ep->data);
-}
-
-static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
- struct isp1362_ep_queue *epq)
-{
- struct ptd *ptd = &ep->ptd;
- int act_len;
-
- WARN_ON(list_empty(&ep->active));
- BUG_ON(ep->ptd_offset < 0);
-
- list_del_init(&ep->active);
- DBG(1, "%s: ep %p removed from active list %p\n", __func__, ep, &epq->active);
-
- prefetchw(ptd);
- isp1362_read_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE);
- dump_ptd(ptd);
- act_len = PTD_GET_COUNT(ptd);
- if (PTD_GET_DIR(ptd) != PTD_DIR_IN || act_len == 0)
- return;
- if (act_len > ep->length)
- pr_err("%s: ep %p PTD $%04x act_len %d ep->length %d\n", __func__, ep,
- ep->ptd_offset, act_len, ep->length);
- BUG_ON(act_len > ep->length);
- /* Only transfer the amount of data that has actually been overwritten
- * in the chip buffer. We don't want any data that doesn't belong to the
- * transfer to leak out of the chip to the callers transfer buffer!
- */
- prefetchw(ep->data);
- isp1362_read_buffer(isp1362_hcd, ep->data,
- ep->ptd_offset + PTD_HEADER_SIZE, act_len);
- dump_ptd_in_data(ptd, ep->data);
-}
-
-/*
- * INT PTDs will stay in the chip until data is available.
- * This function will remove a PTD from the chip when the URB is dequeued.
- * Must be called with the spinlock held and IRQs disabled
- */
-static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
-
-{
- int index;
- struct isp1362_ep_queue *epq;
-
- DBG(1, "%s: ep %p PTD[%d] $%04x\n", __func__, ep, ep->ptd_index, ep->ptd_offset);
- BUG_ON(ep->ptd_offset < 0);
-
- epq = get_ptd_queue(isp1362_hcd, ep->ptd_offset);
- BUG_ON(!epq);
-
- /* put ep in remove_list for cleanup */
- WARN_ON(!list_empty(&ep->remove_list));
- list_add_tail(&ep->remove_list, &isp1362_hcd->remove_list);
- /* let SOF interrupt handle the cleanup */
- isp1362_enable_int(isp1362_hcd, HCuPINT_SOF);
-
- index = ep->ptd_index;
- if (index < 0)
- /* ISO queues don't have SKIP registers */
- return;
-
- DBG(1, "%s: Disabling PTD[%02x] $%04x %08lx|%08x\n", __func__,
- index, ep->ptd_offset, epq->skip_map, 1 << index);
-
- /* prevent further processing of PTD (will be effective after next SOF) */
- epq->skip_map |= 1 << index;
- if (epq == &isp1362_hcd->atl_queue) {
- DBG(2, "%s: ATLSKIP = %08x -> %08lx\n", __func__,
- isp1362_read_reg32(isp1362_hcd, HCATLSKIP), epq->skip_map);
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, epq->skip_map);
- if (~epq->skip_map == 0)
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
- } else if (epq == &isp1362_hcd->intl_queue) {
- DBG(2, "%s: INTLSKIP = %08x -> %08lx\n", __func__,
- isp1362_read_reg32(isp1362_hcd, HCINTLSKIP), epq->skip_map);
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, epq->skip_map);
- if (~epq->skip_map == 0)
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
- }
-}
-
-/*
- Take done or failed requests out of schedule. Give back
- processed urbs.
-*/
-static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
- struct urb *urb, int status)
- __releases(isp1362_hcd->lock)
- __acquires(isp1362_hcd->lock)
-{
- urb->hcpriv = NULL;
- ep->error_count = 0;
-
- if (usb_pipecontrol(urb->pipe))
- ep->nextpid = USB_PID_SETUP;
-
- URB_DBG("%s: req %d FA %d ep%d%s %s: len %d/%d %s stat %d\n", __func__,
- ep->num_req, usb_pipedevice(urb->pipe),
- usb_pipeendpoint(urb->pipe),
- !usb_pipein(urb->pipe) ? "out" : "in",
- usb_pipecontrol(urb->pipe) ? "ctrl" :
- usb_pipeint(urb->pipe) ? "int" :
- usb_pipebulk(urb->pipe) ? "bulk" :
- "iso",
- urb->actual_length, urb->transfer_buffer_length,
- !(urb->transfer_flags & URB_SHORT_NOT_OK) ?
- "short_ok" : "", urb->status);
-
-
- usb_hcd_unlink_urb_from_ep(isp1362_hcd_to_hcd(isp1362_hcd), urb);
- spin_unlock(&isp1362_hcd->lock);
- usb_hcd_giveback_urb(isp1362_hcd_to_hcd(isp1362_hcd), urb, status);
- spin_lock(&isp1362_hcd->lock);
-
- /* take idle endpoints out of the schedule right away */
- if (!list_empty(&ep->hep->urb_list))
- return;
-
- /* async deschedule */
- if (!list_empty(&ep->schedule)) {
- list_del_init(&ep->schedule);
- return;
- }
-
-
- if (ep->interval) {
- /* periodic deschedule */
- DBG(1, "deschedule qh%d/%p branch %d load %d bandwidth %d -> %d\n", ep->interval,
- ep, ep->branch, ep->load,
- isp1362_hcd->load[ep->branch],
- isp1362_hcd->load[ep->branch] - ep->load);
- isp1362_hcd->load[ep->branch] -= ep->load;
- ep->branch = PERIODIC_SIZE;
- }
-}
-
-/*
- * Analyze transfer results, handle partial transfers and errors
-*/
-static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
-{
- struct urb *urb = get_urb(ep);
- struct usb_device *udev;
- struct ptd *ptd;
- int short_ok;
- u16 len;
- int urbstat = -EINPROGRESS;
- u8 cc;
-
- DBG(2, "%s: ep %p req %d\n", __func__, ep, ep->num_req);
-
- udev = urb->dev;
- ptd = &ep->ptd;
- cc = PTD_GET_CC(ptd);
- if (cc == PTD_NOTACCESSED) {
- pr_err("%s: req %d PTD %p Untouched by ISP1362\n", __func__,
- ep->num_req, ptd);
- cc = PTD_DEVNOTRESP;
- }
-
- short_ok = !(urb->transfer_flags & URB_SHORT_NOT_OK);
- len = urb->transfer_buffer_length - urb->actual_length;
-
- /* Data underrun is special. For allowed underrun
- we clear the error and continue as normal. For
- forbidden underrun we finish the DATA stage
- immediately while for control transfer,
- we do a STATUS stage.
- */
- if (cc == PTD_DATAUNDERRUN) {
- if (short_ok) {
- DBG(1, "%s: req %d Allowed data underrun short_%sok %d/%d/%d byte\n",
- __func__, ep->num_req, short_ok ? "" : "not_",
- PTD_GET_COUNT(ptd), ep->maxpacket, len);
- cc = PTD_CC_NOERROR;
- urbstat = 0;
- } else {
- DBG(1, "%s: req %d Data Underrun %s nextpid %02x short_%sok %d/%d/%d byte\n",
- __func__, ep->num_req,
- usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid,
- short_ok ? "" : "not_",
- PTD_GET_COUNT(ptd), ep->maxpacket, len);
- /* save the data underrun error code for later and
- * proceed with the status stage
- */
- urb->actual_length += PTD_GET_COUNT(ptd);
- if (usb_pipecontrol(urb->pipe)) {
- ep->nextpid = USB_PID_ACK;
- BUG_ON(urb->actual_length > urb->transfer_buffer_length);
-
- if (urb->status == -EINPROGRESS)
- urb->status = cc_to_error[PTD_DATAUNDERRUN];
- } else {
- usb_settoggle(udev, ep->epnum, ep->nextpid == USB_PID_OUT,
- PTD_GET_TOGGLE(ptd));
- urbstat = cc_to_error[PTD_DATAUNDERRUN];
- }
- goto out;
- }
- }
-
- if (cc != PTD_CC_NOERROR) {
- if (++ep->error_count >= 3 || cc == PTD_CC_STALL || cc == PTD_DATAOVERRUN) {
- urbstat = cc_to_error[cc];
- DBG(1, "%s: req %d nextpid %02x, status %d, error %d, error_count %d\n",
- __func__, ep->num_req, ep->nextpid, urbstat, cc,
- ep->error_count);
- }
- goto out;
- }
-
- switch (ep->nextpid) {
- case USB_PID_OUT:
- if (PTD_GET_COUNT(ptd) != ep->length)
- pr_err("%s: count=%d len=%d\n", __func__,
- PTD_GET_COUNT(ptd), ep->length);
- BUG_ON(PTD_GET_COUNT(ptd) != ep->length);
- urb->actual_length += ep->length;
- BUG_ON(urb->actual_length > urb->transfer_buffer_length);
- usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd));
- if (urb->actual_length == urb->transfer_buffer_length) {
- DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__,
- ep->num_req, len, ep->maxpacket, urbstat);
- if (usb_pipecontrol(urb->pipe)) {
- DBG(3, "%s: req %d %s Wait for ACK\n", __func__,
- ep->num_req,
- usb_pipein(urb->pipe) ? "IN" : "OUT");
- ep->nextpid = USB_PID_ACK;
- } else {
- if (len % ep->maxpacket ||
- !(urb->transfer_flags & URB_ZERO_PACKET)) {
- urbstat = 0;
- DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n",
- __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT",
- urbstat, len, ep->maxpacket, urb->actual_length);
- }
- }
- }
- break;
- case USB_PID_IN:
- len = PTD_GET_COUNT(ptd);
- BUG_ON(len > ep->length);
- urb->actual_length += len;
- BUG_ON(urb->actual_length > urb->transfer_buffer_length);
- usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd));
- /* if transfer completed or (allowed) data underrun */
- if ((urb->transfer_buffer_length == urb->actual_length) ||
- len % ep->maxpacket) {
- DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__,
- ep->num_req, len, ep->maxpacket, urbstat);
- if (usb_pipecontrol(urb->pipe)) {
- DBG(3, "%s: req %d %s Wait for ACK\n", __func__,
- ep->num_req,
- usb_pipein(urb->pipe) ? "IN" : "OUT");
- ep->nextpid = USB_PID_ACK;
- } else {
- urbstat = 0;
- DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n",
- __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT",
- urbstat, len, ep->maxpacket, urb->actual_length);
- }
- }
- break;
- case USB_PID_SETUP:
- if (urb->transfer_buffer_length == urb->actual_length) {
- ep->nextpid = USB_PID_ACK;
- } else if (usb_pipeout(urb->pipe)) {
- usb_settoggle(udev, 0, 1, 1);
- ep->nextpid = USB_PID_OUT;
- } else {
- usb_settoggle(udev, 0, 0, 1);
- ep->nextpid = USB_PID_IN;
- }
- break;
- case USB_PID_ACK:
- DBG(3, "%s: req %d got ACK %d -> 0\n", __func__, ep->num_req,
- urbstat);
- WARN_ON(urbstat != -EINPROGRESS);
- urbstat = 0;
- ep->nextpid = 0;
- break;
- default:
- BUG_ON(1);
- }
-
- out:
- if (urbstat != -EINPROGRESS) {
- DBG(2, "%s: Finishing ep %p req %d urb %p status %d\n", __func__,
- ep, ep->num_req, urb, urbstat);
- finish_request(isp1362_hcd, ep, urb, urbstat);
- }
-}
-
-static void finish_unlinks(struct isp1362_hcd *isp1362_hcd)
-{
- struct isp1362_ep *ep;
- struct isp1362_ep *tmp;
-
- list_for_each_entry_safe(ep, tmp, &isp1362_hcd->remove_list, remove_list) {
- struct isp1362_ep_queue *epq =
- get_ptd_queue(isp1362_hcd, ep->ptd_offset);
- int index = ep->ptd_index;
-
- BUG_ON(epq == NULL);
- if (index >= 0) {
- DBG(1, "%s: remove PTD[%d] $%04x\n", __func__, index, ep->ptd_offset);
- BUG_ON(ep->num_ptds == 0);
- release_ptd_buffers(epq, ep);
- }
- if (!list_empty(&ep->hep->urb_list)) {
- struct urb *urb = get_urb(ep);
-
- DBG(1, "%s: Finishing req %d ep %p from remove_list\n", __func__,
- ep->num_req, ep);
- finish_request(isp1362_hcd, ep, urb, -ESHUTDOWN);
- }
- WARN_ON(list_empty(&ep->active));
- if (!list_empty(&ep->active)) {
- list_del_init(&ep->active);
- DBG(1, "%s: ep %p removed from active list\n", __func__, ep);
- }
- list_del_init(&ep->remove_list);
- DBG(1, "%s: ep %p removed from remove_list\n", __func__, ep);
- }
- DBG(1, "%s: Done\n", __func__);
-}
-
-static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count)
-{
- if (count > 0) {
- if (count < isp1362_hcd->atl_queue.ptd_count)
- isp1362_write_reg16(isp1362_hcd, HCATLDTC, count);
- isp1362_enable_int(isp1362_hcd, HCuPINT_ATL);
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, isp1362_hcd->atl_queue.skip_map);
- isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
- } else
- isp1362_enable_int(isp1362_hcd, HCuPINT_SOF);
-}
-
-static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd)
-{
- isp1362_enable_int(isp1362_hcd, HCuPINT_INTL);
- isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, isp1362_hcd->intl_queue.skip_map);
-}
-
-static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip)
-{
- isp1362_enable_int(isp1362_hcd, flip ? HCuPINT_ISTL1 : HCuPINT_ISTL0);
- isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, flip ?
- HCBUFSTAT_ISTL1_FULL : HCBUFSTAT_ISTL0_FULL);
-}
-
-static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
- struct isp1362_ep *ep, struct isp1362_ep_queue *epq)
-{
- int index;
-
- prepare_ptd(isp1362_hcd, urb, ep, epq, 0);
- index = claim_ptd_buffers(epq, ep, ep->length);
- if (index == -ENOMEM) {
- DBG(1, "%s: req %d No free %s PTD available: %d, %08lx:%08lx\n", __func__,
- ep->num_req, epq->name, ep->num_ptds, epq->buf_map, epq->skip_map);
- return index;
- } else if (index == -EOVERFLOW) {
- DBG(1, "%s: req %d Not enough space for %d byte %s PTD %d %08lx:%08lx\n",
- __func__, ep->num_req, ep->length, epq->name, ep->num_ptds,
- epq->buf_map, epq->skip_map);
- return index;
- } else
- BUG_ON(index < 0);
- list_add_tail(&ep->active, &epq->active);
- DBG(1, "%s: ep %p req %d len %d added to active list %p\n", __func__,
- ep, ep->num_req, ep->length, &epq->active);
- DBG(1, "%s: Submitting %s PTD $%04x for ep %p req %d\n", __func__, epq->name,
- ep->ptd_offset, ep, ep->num_req);
- isp1362_write_ptd(isp1362_hcd, ep, epq);
- __clear_bit(ep->ptd_index, &epq->skip_map);
-
- return 0;
-}
-
-static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd)
-{
- int ptd_count = 0;
- struct isp1362_ep_queue *epq = &isp1362_hcd->atl_queue;
- struct isp1362_ep *ep;
- int defer = 0;
-
- if (atomic_read(&epq->finishing)) {
- DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
- return;
- }
-
- list_for_each_entry(ep, &isp1362_hcd->async, schedule) {
- struct urb *urb = get_urb(ep);
- int ret;
-
- if (!list_empty(&ep->active)) {
- DBG(2, "%s: Skipping active %s ep %p\n", __func__, epq->name, ep);
- continue;
- }
-
- DBG(1, "%s: Processing %s ep %p req %d\n", __func__, epq->name,
- ep, ep->num_req);
-
- ret = submit_req(isp1362_hcd, urb, ep, epq);
- if (ret == -ENOMEM) {
- defer = 1;
- break;
- } else if (ret == -EOVERFLOW) {
- defer = 1;
- continue;
- }
-#ifdef BUGGY_PXA2XX_UDC_USBTEST
- defer = ep->nextpid == USB_PID_SETUP;
-#endif
- ptd_count++;
- }
-
- /* Avoid starving of endpoints */
- if (isp1362_hcd->async.next != isp1362_hcd->async.prev) {
- DBG(2, "%s: Cycling ASYNC schedule %d\n", __func__, ptd_count);
- list_move(&isp1362_hcd->async, isp1362_hcd->async.next);
- }
- if (ptd_count || defer)
- enable_atl_transfers(isp1362_hcd, defer ? 0 : ptd_count);
-
- epq->ptd_count += ptd_count;
- if (epq->ptd_count > epq->stat_maxptds) {
- epq->stat_maxptds = epq->ptd_count;
- DBG(0, "%s: max_ptds: %d\n", __func__, epq->stat_maxptds);
- }
-}
-
-static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd)
-{
- int ptd_count = 0;
- struct isp1362_ep_queue *epq = &isp1362_hcd->intl_queue;
- struct isp1362_ep *ep;
-
- if (atomic_read(&epq->finishing)) {
- DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
- return;
- }
-
- list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) {
- struct urb *urb = get_urb(ep);
- int ret;
-
- if (!list_empty(&ep->active)) {
- DBG(1, "%s: Skipping active %s ep %p\n", __func__,
- epq->name, ep);
- continue;
- }
-
- DBG(1, "%s: Processing %s ep %p req %d\n", __func__,
- epq->name, ep, ep->num_req);
- ret = submit_req(isp1362_hcd, urb, ep, epq);
- if (ret == -ENOMEM)
- break;
- else if (ret == -EOVERFLOW)
- continue;
- ptd_count++;
- }
-
- if (ptd_count) {
- static int last_count;
-
- if (ptd_count != last_count) {
- DBG(0, "%s: ptd_count: %d\n", __func__, ptd_count);
- last_count = ptd_count;
- }
- enable_intl_transfers(isp1362_hcd);
- }
-
- epq->ptd_count += ptd_count;
- if (epq->ptd_count > epq->stat_maxptds)
- epq->stat_maxptds = epq->ptd_count;
-}
-
-static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
-{
- u16 ptd_offset = ep->ptd_offset;
- int num_ptds = (ep->length + PTD_HEADER_SIZE + (epq->blk_size - 1)) / epq->blk_size;
-
- DBG(2, "%s: PTD offset $%04x + %04x => %d * %04x -> $%04x\n", __func__, ptd_offset,
- ep->length, num_ptds, epq->blk_size, ptd_offset + num_ptds * epq->blk_size);
-
- ptd_offset += num_ptds * epq->blk_size;
- if (ptd_offset < epq->buf_start + epq->buf_size)
- return ptd_offset;
- else
- return -ENOMEM;
-}
-
-static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd)
-{
- int ptd_count = 0;
- int flip = isp1362_hcd->istl_flip;
- struct isp1362_ep_queue *epq;
- int ptd_offset;
- struct isp1362_ep *ep;
- struct isp1362_ep *tmp;
- u16 fno = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
-
- fill2:
- epq = &isp1362_hcd->istl_queue[flip];
- if (atomic_read(&epq->finishing)) {
- DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
- return;
- }
-
- if (!list_empty(&epq->active))
- return;
-
- ptd_offset = epq->buf_start;
- list_for_each_entry_safe(ep, tmp, &isp1362_hcd->isoc, schedule) {
- struct urb *urb = get_urb(ep);
- s16 diff = fno - (u16)urb->start_frame;
-
- DBG(1, "%s: Processing %s ep %p\n", __func__, epq->name, ep);
-
- if (diff > urb->number_of_packets) {
- /* time frame for this URB has elapsed */
- finish_request(isp1362_hcd, ep, urb, -EOVERFLOW);
- continue;
- } else if (diff < -1) {
- /* URB is not due in this frame or the next one.
- * Comparing with '-1' instead of '0' accounts for double
- * buffering in the ISP1362 which enables us to queue the PTD
- * one frame ahead of time
- */
- } else if (diff == -1) {
- /* submit PTD's that are due in the next frame */
- prepare_ptd(isp1362_hcd, urb, ep, epq, fno);
- if (ptd_offset + PTD_HEADER_SIZE + ep->length >
- epq->buf_start + epq->buf_size) {
- pr_err("%s: Not enough ISO buffer space for %d byte PTD\n",
- __func__, ep->length);
- continue;
- }
- ep->ptd_offset = ptd_offset;
- list_add_tail(&ep->active, &epq->active);
-
- ptd_offset = next_ptd(epq, ep);
- if (ptd_offset < 0) {
- pr_warn("%s: req %d No more %s PTD buffers available\n",
- __func__, ep->num_req, epq->name);
- break;
- }
- }
- }
- list_for_each_entry(ep, &epq->active, active) {
- if (epq->active.next == &ep->active)
- ep->ptd.mps |= PTD_LAST_MSK;
- isp1362_write_ptd(isp1362_hcd, ep, epq);
- ptd_count++;
- }
-
- if (ptd_count)
- enable_istl_transfers(isp1362_hcd, flip);
-
- epq->ptd_count += ptd_count;
- if (epq->ptd_count > epq->stat_maxptds)
- epq->stat_maxptds = epq->ptd_count;
-
- /* check, whether the second ISTL buffer may also be filled */
- if (!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- (flip ? HCBUFSTAT_ISTL0_FULL : HCBUFSTAT_ISTL1_FULL))) {
- fno++;
- ptd_count = 0;
- flip = 1 - flip;
- goto fill2;
- }
-}
-
-static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map,
- struct isp1362_ep_queue *epq)
-{
- struct isp1362_ep *ep;
- struct isp1362_ep *tmp;
-
- if (list_empty(&epq->active)) {
- DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name);
- return;
- }
-
- DBG(1, "%s: Finishing %s transfers %08lx\n", __func__, epq->name, done_map);
-
- atomic_inc(&epq->finishing);
- list_for_each_entry_safe(ep, tmp, &epq->active, active) {
- int index = ep->ptd_index;
-
- DBG(1, "%s: Checking %s PTD[%02x] $%04x\n", __func__, epq->name,
- index, ep->ptd_offset);
-
- BUG_ON(index < 0);
- if (__test_and_clear_bit(index, &done_map)) {
- isp1362_read_ptd(isp1362_hcd, ep, epq);
- epq->free_ptd = index;
- BUG_ON(ep->num_ptds == 0);
- release_ptd_buffers(epq, ep);
-
- DBG(1, "%s: ep %p req %d removed from active list\n", __func__,
- ep, ep->num_req);
- if (!list_empty(&ep->remove_list)) {
- list_del_init(&ep->remove_list);
- DBG(1, "%s: ep %p removed from remove list\n", __func__, ep);
- }
- DBG(1, "%s: Postprocessing %s ep %p req %d\n", __func__, epq->name,
- ep, ep->num_req);
- postproc_ep(isp1362_hcd, ep);
- }
- if (!done_map)
- break;
- }
- if (done_map)
- pr_warn("%s: done_map not clear: %08lx:%08lx\n",
- __func__, done_map, epq->skip_map);
- atomic_dec(&epq->finishing);
-}
-
-static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq)
-{
- struct isp1362_ep *ep;
- struct isp1362_ep *tmp;
-
- if (list_empty(&epq->active)) {
- DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name);
- return;
- }
-
- DBG(1, "%s: Finishing %s transfers\n", __func__, epq->name);
-
- atomic_inc(&epq->finishing);
- list_for_each_entry_safe(ep, tmp, &epq->active, active) {
- DBG(1, "%s: Checking PTD $%04x\n", __func__, ep->ptd_offset);
-
- isp1362_read_ptd(isp1362_hcd, ep, epq);
- DBG(1, "%s: Postprocessing %s ep %p\n", __func__, epq->name, ep);
- postproc_ep(isp1362_hcd, ep);
- }
- WARN_ON(epq->blk_size != 0);
- atomic_dec(&epq->finishing);
-}
-
-static irqreturn_t isp1362_irq(struct usb_hcd *hcd)
-{
- int handled = 0;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- u16 irqstat;
- u16 svc_mask;
-
- spin_lock(&isp1362_hcd->lock);
-
- BUG_ON(isp1362_hcd->irq_active++);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
-
- irqstat = isp1362_read_reg16(isp1362_hcd, HCuPINT);
- DBG(3, "%s: got IRQ %04x:%04x\n", __func__, irqstat, isp1362_hcd->irqenb);
-
- /* only handle interrupts that are currently enabled */
- irqstat &= isp1362_hcd->irqenb;
- isp1362_write_reg16(isp1362_hcd, HCuPINT, irqstat);
- svc_mask = irqstat;
-
- if (irqstat & HCuPINT_SOF) {
- isp1362_hcd->irqenb &= ~HCuPINT_SOF;
- isp1362_hcd->irq_stat[ISP1362_INT_SOF]++;
- handled = 1;
- svc_mask &= ~HCuPINT_SOF;
- DBG(3, "%s: SOF\n", __func__);
- isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
- if (!list_empty(&isp1362_hcd->remove_list))
- finish_unlinks(isp1362_hcd);
- if (!list_empty(&isp1362_hcd->async) && !(irqstat & HCuPINT_ATL)) {
- if (list_empty(&isp1362_hcd->atl_queue.active)) {
- start_atl_transfers(isp1362_hcd);
- } else {
- isp1362_enable_int(isp1362_hcd, HCuPINT_ATL);
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP,
- isp1362_hcd->atl_queue.skip_map);
- isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
- }
- }
- }
-
- if (irqstat & HCuPINT_ISTL0) {
- isp1362_hcd->irq_stat[ISP1362_INT_ISTL0]++;
- handled = 1;
- svc_mask &= ~HCuPINT_ISTL0;
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL0_FULL);
- DBG(1, "%s: ISTL0\n", __func__);
- WARN_ON((int)!!isp1362_hcd->istl_flip);
- WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- HCBUFSTAT_ISTL0_ACTIVE);
- WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- HCBUFSTAT_ISTL0_DONE));
- isp1362_hcd->irqenb &= ~HCuPINT_ISTL0;
- }
-
- if (irqstat & HCuPINT_ISTL1) {
- isp1362_hcd->irq_stat[ISP1362_INT_ISTL1]++;
- handled = 1;
- svc_mask &= ~HCuPINT_ISTL1;
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL1_FULL);
- DBG(1, "%s: ISTL1\n", __func__);
- WARN_ON(!(int)isp1362_hcd->istl_flip);
- WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- HCBUFSTAT_ISTL1_ACTIVE);
- WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
- HCBUFSTAT_ISTL1_DONE));
- isp1362_hcd->irqenb &= ~HCuPINT_ISTL1;
- }
-
- if (irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) {
- WARN_ON((irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) ==
- (HCuPINT_ISTL0 | HCuPINT_ISTL1));
- finish_iso_transfers(isp1362_hcd,
- &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]);
- start_iso_transfers(isp1362_hcd);
- isp1362_hcd->istl_flip = 1 - isp1362_hcd->istl_flip;
- }
-
- if (irqstat & HCuPINT_INTL) {
- u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE);
- u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCINTLSKIP);
- isp1362_hcd->irq_stat[ISP1362_INT_INTL]++;
-
- DBG(2, "%s: INTL\n", __func__);
-
- svc_mask &= ~HCuPINT_INTL;
-
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, skip_map | done_map);
- if (~(done_map | skip_map) == 0)
- /* All PTDs are finished, disable INTL processing entirely */
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
-
- handled = 1;
- WARN_ON(!done_map);
- if (done_map) {
- DBG(3, "%s: INTL done_map %08x\n", __func__, done_map);
- finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue);
- start_intl_transfers(isp1362_hcd);
- }
- }
-
- if (irqstat & HCuPINT_ATL) {
- u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE);
- u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCATLSKIP);
- isp1362_hcd->irq_stat[ISP1362_INT_ATL]++;
-
- DBG(2, "%s: ATL\n", __func__);
-
- svc_mask &= ~HCuPINT_ATL;
-
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, skip_map | done_map);
- if (~(done_map | skip_map) == 0)
- isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
- if (done_map) {
- DBG(3, "%s: ATL done_map %08x\n", __func__, done_map);
- finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue);
- start_atl_transfers(isp1362_hcd);
- }
- handled = 1;
- }
-
- if (irqstat & HCuPINT_OPR) {
- u32 intstat = isp1362_read_reg32(isp1362_hcd, HCINTSTAT);
- isp1362_hcd->irq_stat[ISP1362_INT_OPR]++;
-
- svc_mask &= ~HCuPINT_OPR;
- DBG(2, "%s: OPR %08x:%08x\n", __func__, intstat, isp1362_hcd->intenb);
- intstat &= isp1362_hcd->intenb;
- if (intstat & OHCI_INTR_UE) {
- pr_err("Unrecoverable error\n");
- /* FIXME: do here reset or cleanup or whatever */
- }
- if (intstat & OHCI_INTR_RHSC) {
- isp1362_hcd->rhstatus = isp1362_read_reg32(isp1362_hcd, HCRHSTATUS);
- isp1362_hcd->rhport[0] = isp1362_read_reg32(isp1362_hcd, HCRHPORT1);
- isp1362_hcd->rhport[1] = isp1362_read_reg32(isp1362_hcd, HCRHPORT2);
- }
- if (intstat & OHCI_INTR_RD) {
- pr_info("%s: RESUME DETECTED\n", __func__);
- isp1362_show_reg(isp1362_hcd, HCCONTROL);
- usb_hcd_resume_root_hub(hcd);
- }
- isp1362_write_reg32(isp1362_hcd, HCINTSTAT, intstat);
- irqstat &= ~HCuPINT_OPR;
- handled = 1;
- }
-
- if (irqstat & HCuPINT_SUSP) {
- isp1362_hcd->irq_stat[ISP1362_INT_SUSP]++;
- handled = 1;
- svc_mask &= ~HCuPINT_SUSP;
-
- pr_info("%s: SUSPEND IRQ\n", __func__);
- }
-
- if (irqstat & HCuPINT_CLKRDY) {
- isp1362_hcd->irq_stat[ISP1362_INT_CLKRDY]++;
- handled = 1;
- isp1362_hcd->irqenb &= ~HCuPINT_CLKRDY;
- svc_mask &= ~HCuPINT_CLKRDY;
- pr_info("%s: CLKRDY IRQ\n", __func__);
- }
-
- if (svc_mask)
- pr_err("%s: Unserviced interrupt(s) %04x\n", __func__, svc_mask);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
- isp1362_hcd->irq_active--;
- spin_unlock(&isp1362_hcd->lock);
-
- return IRQ_RETVAL(handled);
-}
-
-/*-------------------------------------------------------------------------*/
-
-#define MAX_PERIODIC_LOAD 900 /* out of 1000 usec */
-static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load)
-{
- int i, branch = -ENOSPC;
-
- /* search for the least loaded schedule branch of that interval
- * which has enough bandwidth left unreserved.
- */
- for (i = 0; i < interval; i++) {
- if (branch < 0 || isp1362_hcd->load[branch] > isp1362_hcd->load[i]) {
- int j;
-
- for (j = i; j < PERIODIC_SIZE; j += interval) {
- if ((isp1362_hcd->load[j] + load) > MAX_PERIODIC_LOAD) {
- pr_err("%s: new load %d load[%02x] %d max %d\n", __func__,
- load, j, isp1362_hcd->load[j], MAX_PERIODIC_LOAD);
- break;
- }
- }
- if (j < PERIODIC_SIZE)
- continue;
- branch = i;
- }
- }
- return branch;
-}
-
-/* NB! ALL the code above this point runs with isp1362_hcd->lock
- held, irqs off
-*/
-
-/*-------------------------------------------------------------------------*/
-
-static int isp1362_urb_enqueue(struct usb_hcd *hcd,
- struct urb *urb,
- gfp_t mem_flags)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- struct usb_device *udev = urb->dev;
- unsigned int pipe = urb->pipe;
- int is_out = !usb_pipein(pipe);
- int type = usb_pipetype(pipe);
- int epnum = usb_pipeendpoint(pipe);
- struct usb_host_endpoint *hep = urb->ep;
- struct isp1362_ep *ep = NULL;
- unsigned long flags;
- int retval = 0;
-
- DBG(3, "%s: urb %p\n", __func__, urb);
-
- if (type == PIPE_ISOCHRONOUS) {
- pr_err("Isochronous transfers not supported\n");
- return -ENOSPC;
- }
-
- URB_DBG("%s: FA %d ep%d%s %s: len %d %s%s\n", __func__,
- usb_pipedevice(pipe), epnum,
- is_out ? "out" : "in",
- usb_pipecontrol(pipe) ? "ctrl" :
- usb_pipeint(pipe) ? "int" :
- usb_pipebulk(pipe) ? "bulk" :
- "iso",
- urb->transfer_buffer_length,
- (urb->transfer_flags & URB_ZERO_PACKET) ? "ZERO_PACKET " : "",
- !(urb->transfer_flags & URB_SHORT_NOT_OK) ?
- "short_ok" : "");
-
- /* avoid all allocations within spinlocks: request or endpoint */
- if (!hep->hcpriv) {
- ep = kzalloc(sizeof *ep, mem_flags);
- if (!ep)
- return -ENOMEM;
- }
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- /* don't submit to a dead or disabled port */
- if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) &
- USB_PORT_STAT_ENABLE) ||
- !HC_IS_RUNNING(hcd->state)) {
- kfree(ep);
- retval = -ENODEV;
- goto fail_not_linked;
- }
-
- retval = usb_hcd_link_urb_to_ep(hcd, urb);
- if (retval) {
- kfree(ep);
- goto fail_not_linked;
- }
-
- if (hep->hcpriv) {
- ep = hep->hcpriv;
- } else {
- INIT_LIST_HEAD(&ep->schedule);
- INIT_LIST_HEAD(&ep->active);
- INIT_LIST_HEAD(&ep->remove_list);
- ep->udev = usb_get_dev(udev);
- ep->hep = hep;
- ep->epnum = epnum;
- ep->maxpacket = usb_maxpacket(udev, urb->pipe);
- ep->ptd_offset = -EINVAL;
- ep->ptd_index = -EINVAL;
- usb_settoggle(udev, epnum, is_out, 0);
-
- if (type == PIPE_CONTROL)
- ep->nextpid = USB_PID_SETUP;
- else if (is_out)
- ep->nextpid = USB_PID_OUT;
- else
- ep->nextpid = USB_PID_IN;
-
- switch (type) {
- case PIPE_ISOCHRONOUS:
- case PIPE_INTERRUPT:
- if (urb->interval > PERIODIC_SIZE)
- urb->interval = PERIODIC_SIZE;
- ep->interval = urb->interval;
- ep->branch = PERIODIC_SIZE;
- ep->load = usb_calc_bus_time(udev->speed, !is_out,
- type == PIPE_ISOCHRONOUS,
- usb_maxpacket(udev, pipe)) / 1000;
- break;
- }
- hep->hcpriv = ep;
- }
- ep->num_req = isp1362_hcd->req_serial++;
-
- /* maybe put endpoint into schedule */
- switch (type) {
- case PIPE_CONTROL:
- case PIPE_BULK:
- if (list_empty(&ep->schedule)) {
- DBG(1, "%s: Adding ep %p req %d to async schedule\n",
- __func__, ep, ep->num_req);
- list_add_tail(&ep->schedule, &isp1362_hcd->async);
- }
- break;
- case PIPE_ISOCHRONOUS:
- case PIPE_INTERRUPT:
- urb->interval = ep->interval;
-
- /* urb submitted for already existing EP */
- if (ep->branch < PERIODIC_SIZE)
- break;
-
- retval = balance(isp1362_hcd, ep->interval, ep->load);
- if (retval < 0) {
- pr_err("%s: balance returned %d\n", __func__, retval);
- goto fail;
- }
- ep->branch = retval;
- retval = 0;
- isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
- DBG(1, "%s: Current frame %04x branch %02x start_frame %04x(%04x)\n",
- __func__, isp1362_hcd->fmindex, ep->branch,
- ((isp1362_hcd->fmindex + PERIODIC_SIZE - 1) &
- ~(PERIODIC_SIZE - 1)) + ep->branch,
- (isp1362_hcd->fmindex & (PERIODIC_SIZE - 1)) + ep->branch);
-
- if (list_empty(&ep->schedule)) {
- if (type == PIPE_ISOCHRONOUS) {
- u16 frame = isp1362_hcd->fmindex;
-
- frame += max_t(u16, 8, ep->interval);
- frame &= ~(ep->interval - 1);
- frame |= ep->branch;
- if (frame_before(frame, isp1362_hcd->fmindex))
- frame += ep->interval;
- urb->start_frame = frame;
-
- DBG(1, "%s: Adding ep %p to isoc schedule\n", __func__, ep);
- list_add_tail(&ep->schedule, &isp1362_hcd->isoc);
- } else {
- DBG(1, "%s: Adding ep %p to periodic schedule\n", __func__, ep);
- list_add_tail(&ep->schedule, &isp1362_hcd->periodic);
- }
- } else
- DBG(1, "%s: ep %p already scheduled\n", __func__, ep);
-
- DBG(2, "%s: load %d bandwidth %d -> %d\n", __func__,
- ep->load / ep->interval, isp1362_hcd->load[ep->branch],
- isp1362_hcd->load[ep->branch] + ep->load);
- isp1362_hcd->load[ep->branch] += ep->load;
- }
-
- urb->hcpriv = hep;
- ALIGNSTAT(isp1362_hcd, urb->transfer_buffer);
-
- switch (type) {
- case PIPE_CONTROL:
- case PIPE_BULK:
- start_atl_transfers(isp1362_hcd);
- break;
- case PIPE_INTERRUPT:
- start_intl_transfers(isp1362_hcd);
- break;
- case PIPE_ISOCHRONOUS:
- start_iso_transfers(isp1362_hcd);
- break;
- default:
- BUG();
- }
- fail:
- if (retval)
- usb_hcd_unlink_urb_from_ep(hcd, urb);
-
-
- fail_not_linked:
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (retval)
- DBG(0, "%s: urb %p failed with %d\n", __func__, urb, retval);
- return retval;
-}
-
-static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- struct usb_host_endpoint *hep;
- unsigned long flags;
- struct isp1362_ep *ep;
- int retval = 0;
-
- DBG(3, "%s: urb %p\n", __func__, urb);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- retval = usb_hcd_check_unlink_urb(hcd, urb, status);
- if (retval)
- goto done;
-
- hep = urb->hcpriv;
-
- if (!hep) {
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- return -EIDRM;
- }
-
- ep = hep->hcpriv;
- if (ep) {
- /* In front of queue? */
- if (ep->hep->urb_list.next == &urb->urb_list) {
- if (!list_empty(&ep->active)) {
- DBG(1, "%s: urb %p ep %p req %d active PTD[%d] $%04x\n", __func__,
- urb, ep, ep->num_req, ep->ptd_index, ep->ptd_offset);
- /* disable processing and queue PTD for removal */
- remove_ptd(isp1362_hcd, ep);
- urb = NULL;
- }
- }
- if (urb) {
- DBG(1, "%s: Finishing ep %p req %d\n", __func__, ep,
- ep->num_req);
- finish_request(isp1362_hcd, ep, urb, status);
- } else
- DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb);
- } else {
- pr_warn("%s: No EP in URB %p\n", __func__, urb);
- retval = -EINVAL;
- }
-done:
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- DBG(3, "%s: exit\n", __func__);
-
- return retval;
-}
-
-static void isp1362_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
-{
- struct isp1362_ep *ep = hep->hcpriv;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
-
- DBG(1, "%s: ep %p\n", __func__, ep);
- if (!ep)
- return;
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- if (!list_empty(&hep->urb_list)) {
- if (!list_empty(&ep->active) && list_empty(&ep->remove_list)) {
- DBG(1, "%s: Removing ep %p req %d PTD[%d] $%04x\n", __func__,
- ep, ep->num_req, ep->ptd_index, ep->ptd_offset);
- remove_ptd(isp1362_hcd, ep);
- pr_info("%s: Waiting for Interrupt to clean up\n", __func__);
- }
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- /* Wait for interrupt to clear out active list */
- while (!list_empty(&ep->active))
- msleep(1);
-
- DBG(1, "%s: Freeing EP %p\n", __func__, ep);
-
- usb_put_dev(ep->udev);
- kfree(ep);
- hep->hcpriv = NULL;
-}
-
-static int isp1362_get_frame(struct usb_hcd *hcd)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- u32 fmnum;
- unsigned long flags;
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- fmnum = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- return (int)fmnum;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Adapted from ohci-hub.c */
-static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- int ports, i, changed = 0;
- unsigned long flags;
-
- if (!HC_IS_RUNNING(hcd->state))
- return -ESHUTDOWN;
-
- /* Report no status change now, if we are scheduled to be
- called later */
- if (timer_pending(&hcd->rh_timer))
- return 0;
-
- ports = isp1362_hcd->rhdesca & RH_A_NDP;
- BUG_ON(ports > 2);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- /* init status */
- if (isp1362_hcd->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))
- buf[0] = changed = 1;
- else
- buf[0] = 0;
-
- for (i = 0; i < ports; i++) {
- u32 status = isp1362_hcd->rhport[i];
-
- if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC |
- RH_PS_OCIC | RH_PS_PRSC)) {
- changed = 1;
- buf[0] |= 1 << (i + 1);
- continue;
- }
-
- if (!(status & RH_PS_CCS))
- continue;
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- return changed;
-}
-
-static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
- struct usb_hub_descriptor *desc)
-{
- u32 reg = isp1362_hcd->rhdesca;
-
- DBG(3, "%s: enter\n", __func__);
-
- desc->bDescriptorType = USB_DT_HUB;
- desc->bDescLength = 9;
- desc->bHubContrCurrent = 0;
- desc->bNbrPorts = reg & 0x3;
- /* Power switching, device type, overcurrent. */
- 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;
- desc->u.hs.DeviceRemovable[1] = ~0;
-
- DBG(3, "%s: exit\n", __func__);
-}
-
-/* Adapted from ohci-hub.c */
-static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
- u16 wIndex, char *buf, u16 wLength)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- int retval = 0;
- unsigned long flags;
- unsigned long t1;
- int ports = isp1362_hcd->rhdesca & RH_A_NDP;
- u32 tmp = 0;
-
- switch (typeReq) {
- case ClearHubFeature:
- DBG(0, "ClearHubFeature: ");
- switch (wValue) {
- case C_HUB_OVER_CURRENT:
- DBG(0, "C_HUB_OVER_CURRENT\n");
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- case C_HUB_LOCAL_POWER:
- DBG(0, "C_HUB_LOCAL_POWER\n");
- break;
- default:
- goto error;
- }
- break;
- case SetHubFeature:
- DBG(0, "SetHubFeature: ");
- switch (wValue) {
- case C_HUB_OVER_CURRENT:
- case C_HUB_LOCAL_POWER:
- DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n");
- break;
- default:
- goto error;
- }
- break;
- case GetHubDescriptor:
- DBG(0, "GetHubDescriptor\n");
- isp1362_hub_descriptor(isp1362_hcd, (struct usb_hub_descriptor *)buf);
- break;
- case GetHubStatus:
- DBG(0, "GetHubStatus\n");
- put_unaligned(cpu_to_le32(0), (__le32 *) buf);
- break;
- case GetPortStatus:
-#ifndef VERBOSE
- DBG(0, "GetPortStatus\n");
-#endif
- if (!wIndex || wIndex > ports)
- goto error;
- tmp = isp1362_hcd->rhport[--wIndex];
- put_unaligned(cpu_to_le32(tmp), (__le32 *) buf);
- break;
- case ClearPortFeature:
- DBG(0, "ClearPortFeature: ");
- if (!wIndex || wIndex > ports)
- goto error;
- wIndex--;
-
- switch (wValue) {
- case USB_PORT_FEAT_ENABLE:
- DBG(0, "USB_PORT_FEAT_ENABLE\n");
- tmp = RH_PS_CCS;
- break;
- case USB_PORT_FEAT_C_ENABLE:
- DBG(0, "USB_PORT_FEAT_C_ENABLE\n");
- tmp = RH_PS_PESC;
- break;
- case USB_PORT_FEAT_SUSPEND:
- DBG(0, "USB_PORT_FEAT_SUSPEND\n");
- tmp = RH_PS_POCI;
- break;
- case USB_PORT_FEAT_C_SUSPEND:
- DBG(0, "USB_PORT_FEAT_C_SUSPEND\n");
- tmp = RH_PS_PSSC;
- break;
- case USB_PORT_FEAT_POWER:
- DBG(0, "USB_PORT_FEAT_POWER\n");
- tmp = RH_PS_LSDA;
-
- break;
- case USB_PORT_FEAT_C_CONNECTION:
- DBG(0, "USB_PORT_FEAT_C_CONNECTION\n");
- tmp = RH_PS_CSC;
- break;
- case USB_PORT_FEAT_C_OVER_CURRENT:
- DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n");
- tmp = RH_PS_OCIC;
- break;
- case USB_PORT_FEAT_C_RESET:
- DBG(0, "USB_PORT_FEAT_C_RESET\n");
- tmp = RH_PS_PRSC;
- break;
- default:
- goto error;
- }
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, tmp);
- isp1362_hcd->rhport[wIndex] =
- isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- case SetPortFeature:
- DBG(0, "SetPortFeature: ");
- if (!wIndex || wIndex > ports)
- goto error;
- wIndex--;
- switch (wValue) {
- case USB_PORT_FEAT_SUSPEND:
- DBG(0, "USB_PORT_FEAT_SUSPEND\n");
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS);
- isp1362_hcd->rhport[wIndex] =
- isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- case USB_PORT_FEAT_POWER:
- DBG(0, "USB_PORT_FEAT_POWER\n");
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS);
- isp1362_hcd->rhport[wIndex] =
- isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- case USB_PORT_FEAT_RESET:
- DBG(0, "USB_PORT_FEAT_RESET\n");
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH);
- while (time_before(jiffies, t1)) {
- /* spin until any current reset finishes */
- for (;;) {
- tmp = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
- if (!(tmp & RH_PS_PRS))
- break;
- udelay(500);
- }
- if (!(tmp & RH_PS_CCS))
- break;
- /* Reset lasts 10ms (claims datasheet) */
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, (RH_PS_PRS));
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- msleep(10);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- }
-
- isp1362_hcd->rhport[wIndex] = isp1362_read_reg32(isp1362_hcd,
- HCRHPORT1 + wIndex);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- break;
- default:
- goto error;
- }
- break;
-
- default:
- error:
- /* "protocol stall" on error */
- DBG(0, "PROTOCOL STALL\n");
- retval = -EPIPE;
- }
-
- return retval;
-}
-
-#ifdef CONFIG_PM
-static int isp1362_bus_suspend(struct usb_hcd *hcd)
-{
- int status = 0;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
-
- if (time_before(jiffies, isp1362_hcd->next_statechange))
- msleep(5);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
- switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
- case OHCI_USB_RESUME:
- DBG(0, "%s: resume/suspend?\n", __func__);
- isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
- isp1362_hcd->hc_control |= OHCI_USB_RESET;
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- fallthrough;
- case OHCI_USB_RESET:
- status = -EBUSY;
- pr_warn("%s: needs reinit!\n", __func__);
- goto done;
- case OHCI_USB_SUSPEND:
- pr_warn("%s: already suspended?\n", __func__);
- goto done;
- }
- DBG(0, "%s: suspend root hub\n", __func__);
-
- /* First stop any processing */
- hcd->state = HC_STATE_QUIESCING;
- if (!list_empty(&isp1362_hcd->atl_queue.active) ||
- !list_empty(&isp1362_hcd->intl_queue.active) ||
- !list_empty(&isp1362_hcd->istl_queue[0] .active) ||
- !list_empty(&isp1362_hcd->istl_queue[1] .active)) {
- int limit;
-
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0);
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0);
- isp1362_write_reg16(isp1362_hcd, HCBUFSTAT, 0);
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
- isp1362_write_reg32(isp1362_hcd, HCINTSTAT, OHCI_INTR_SF);
-
- DBG(0, "%s: stopping schedules ...\n", __func__);
- limit = 2000;
- while (limit > 0) {
- udelay(250);
- limit -= 250;
- if (isp1362_read_reg32(isp1362_hcd, HCINTSTAT) & OHCI_INTR_SF)
- break;
- }
- mdelay(7);
- if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ATL) {
- u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE);
- finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue);
- }
- if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_INTL) {
- u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE);
- finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue);
- }
- if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL0)
- finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[0]);
- if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL1)
- finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[1]);
- }
- DBG(0, "%s: HCINTSTAT: %08x\n", __func__,
- isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
- isp1362_write_reg32(isp1362_hcd, HCINTSTAT,
- isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
-
- /* Suspend hub */
- isp1362_hcd->hc_control = OHCI_USB_SUSPEND;
- isp1362_show_reg(isp1362_hcd, HCCONTROL);
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- isp1362_show_reg(isp1362_hcd, HCCONTROL);
-
-#if 1
- isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
- if ((isp1362_hcd->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_SUSPEND) {
- pr_err("%s: controller won't suspend %08x\n", __func__,
- isp1362_hcd->hc_control);
- status = -EBUSY;
- } else
-#endif
- {
- /* no resumes until devices finish suspending */
- isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(5);
- }
-done:
- if (status == 0) {
- hcd->state = HC_STATE_SUSPENDED;
- DBG(0, "%s: HCD suspended: %08x\n", __func__,
- isp1362_read_reg32(isp1362_hcd, HCCONTROL));
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- return status;
-}
-
-static int isp1362_bus_resume(struct usb_hcd *hcd)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- u32 port;
- unsigned long flags;
- int status = -EINPROGRESS;
-
- if (time_before(jiffies, isp1362_hcd->next_statechange))
- msleep(5);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
- pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control);
- if (hcd->state == HC_STATE_RESUMING) {
- pr_warn("%s: duplicate resume\n", __func__);
- status = 0;
- } else
- switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
- case OHCI_USB_SUSPEND:
- DBG(0, "%s: resume root hub\n", __func__);
- isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
- isp1362_hcd->hc_control |= OHCI_USB_RESUME;
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- break;
- case OHCI_USB_RESUME:
- /* HCFS changes sometime after INTR_RD */
- DBG(0, "%s: remote wakeup\n", __func__);
- break;
- case OHCI_USB_OPER:
- DBG(0, "%s: odd resume\n", __func__);
- status = 0;
- hcd->self.root_hub->dev.power.power_state = PMSG_ON;
- break;
- default: /* RESET, we lost power */
- DBG(0, "%s: root hub hardware reset\n", __func__);
- status = -EBUSY;
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (status == -EBUSY) {
- DBG(0, "%s: Restarting HC\n", __func__);
- isp1362_hc_stop(hcd);
- return isp1362_hc_start(hcd);
- }
- if (status != -EINPROGRESS)
- return status;
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- port = isp1362_read_reg32(isp1362_hcd, HCRHDESCA) & RH_A_NDP;
- while (port--) {
- u32 stat = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + port);
-
- /* force global, not selective, resume */
- if (!(stat & RH_PS_PSS)) {
- DBG(0, "%s: Not Resuming RH port %d\n", __func__, port);
- continue;
- }
- DBG(0, "%s: Resuming RH port %d\n", __func__, port);
- isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + port, RH_PS_POCI);
- }
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- /* Some controllers (lucent) need extra-long delays */
- hcd->state = HC_STATE_RESUMING;
- mdelay(20 /* usb 11.5.1.10 */ + 15);
-
- isp1362_hcd->hc_control = OHCI_USB_OPER;
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_show_reg(isp1362_hcd, HCCONTROL);
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- /* TRSMRCY */
- msleep(10);
-
- /* keep it alive for ~5x suspend + resume costs */
- isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(250);
-
- hcd->self.root_hub->dev.power.power_state = PMSG_ON;
- hcd->state = HC_STATE_RUNNING;
- return 0;
-}
-#else
-#define isp1362_bus_suspend NULL
-#define isp1362_bus_resume NULL
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-static void dump_irq(struct seq_file *s, char *label, u16 mask)
-{
- seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask,
- mask & HCuPINT_CLKRDY ? " clkrdy" : "",
- mask & HCuPINT_SUSP ? " susp" : "",
- mask & HCuPINT_OPR ? " opr" : "",
- mask & HCuPINT_EOT ? " eot" : "",
- mask & HCuPINT_ATL ? " atl" : "",
- mask & HCuPINT_SOF ? " sof" : "");
-}
-
-static void dump_int(struct seq_file *s, char *label, u32 mask)
-{
- seq_printf(s, "%-15s %08x%s%s%s%s%s%s%s\n", label, mask,
- mask & OHCI_INTR_MIE ? " MIE" : "",
- mask & OHCI_INTR_RHSC ? " rhsc" : "",
- mask & OHCI_INTR_FNO ? " fno" : "",
- mask & OHCI_INTR_UE ? " ue" : "",
- mask & OHCI_INTR_RD ? " rd" : "",
- mask & OHCI_INTR_SF ? " sof" : "",
- mask & OHCI_INTR_SO ? " so" : "");
-}
-
-static void dump_ctrl(struct seq_file *s, char *label, u32 mask)
-{
- seq_printf(s, "%-15s %08x%s%s%s\n", label, mask,
- mask & OHCI_CTRL_RWC ? " rwc" : "",
- mask & OHCI_CTRL_RWE ? " rwe" : "",
- ({
- char *hcfs;
- switch (mask & OHCI_CTRL_HCFS) {
- case OHCI_USB_OPER:
- hcfs = " oper";
- break;
- case OHCI_USB_RESET:
- hcfs = " reset";
- break;
- case OHCI_USB_RESUME:
- hcfs = " resume";
- break;
- case OHCI_USB_SUSPEND:
- hcfs = " suspend";
- break;
- default:
- hcfs = " ?";
- }
- hcfs;
- }));
-}
-
-static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd)
-{
- seq_printf(s, "HCREVISION [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCREVISION),
- isp1362_read_reg32(isp1362_hcd, HCREVISION));
- seq_printf(s, "HCCONTROL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCONTROL),
- isp1362_read_reg32(isp1362_hcd, HCCONTROL));
- seq_printf(s, "HCCMDSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCMDSTAT),
- isp1362_read_reg32(isp1362_hcd, HCCMDSTAT));
- seq_printf(s, "HCINTSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTSTAT),
- isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
- seq_printf(s, "HCINTENB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTENB),
- isp1362_read_reg32(isp1362_hcd, HCINTENB));
- seq_printf(s, "HCFMINTVL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMINTVL),
- isp1362_read_reg32(isp1362_hcd, HCFMINTVL));
- seq_printf(s, "HCFMREM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMREM),
- isp1362_read_reg32(isp1362_hcd, HCFMREM));
- seq_printf(s, "HCFMNUM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMNUM),
- isp1362_read_reg32(isp1362_hcd, HCFMNUM));
- seq_printf(s, "HCLSTHRESH [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCLSTHRESH),
- isp1362_read_reg32(isp1362_hcd, HCLSTHRESH));
- seq_printf(s, "HCRHDESCA [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCA),
- isp1362_read_reg32(isp1362_hcd, HCRHDESCA));
- seq_printf(s, "HCRHDESCB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCB),
- isp1362_read_reg32(isp1362_hcd, HCRHDESCB));
- seq_printf(s, "HCRHSTATUS [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHSTATUS),
- isp1362_read_reg32(isp1362_hcd, HCRHSTATUS));
- seq_printf(s, "HCRHPORT1 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT1),
- isp1362_read_reg32(isp1362_hcd, HCRHPORT1));
- seq_printf(s, "HCRHPORT2 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT2),
- isp1362_read_reg32(isp1362_hcd, HCRHPORT2));
- seq_printf(s, "\n");
- seq_printf(s, "HCHWCFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCHWCFG),
- isp1362_read_reg16(isp1362_hcd, HCHWCFG));
- seq_printf(s, "HCDMACFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCDMACFG),
- isp1362_read_reg16(isp1362_hcd, HCDMACFG));
- seq_printf(s, "HCXFERCTR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCXFERCTR),
- isp1362_read_reg16(isp1362_hcd, HCXFERCTR));
- seq_printf(s, "HCuPINT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINT),
- isp1362_read_reg16(isp1362_hcd, HCuPINT));
- seq_printf(s, "HCuPINTENB [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINTENB),
- isp1362_read_reg16(isp1362_hcd, HCuPINTENB));
- seq_printf(s, "HCCHIPID [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCCHIPID),
- isp1362_read_reg16(isp1362_hcd, HCCHIPID));
- seq_printf(s, "HCSCRATCH [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCSCRATCH),
- isp1362_read_reg16(isp1362_hcd, HCSCRATCH));
- seq_printf(s, "HCBUFSTAT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCBUFSTAT),
- isp1362_read_reg16(isp1362_hcd, HCBUFSTAT));
- seq_printf(s, "HCDIRADDR [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCDIRADDR),
- isp1362_read_reg32(isp1362_hcd, HCDIRADDR));
-#if 0
- seq_printf(s, "HCDIRDATA [%02x] %04x\n", ISP1362_REG_NO(HCDIRDATA),
- isp1362_read_reg16(isp1362_hcd, HCDIRDATA));
-#endif
- seq_printf(s, "HCISTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLBUFSZ),
- isp1362_read_reg16(isp1362_hcd, HCISTLBUFSZ));
- seq_printf(s, "HCISTLRATE [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLRATE),
- isp1362_read_reg16(isp1362_hcd, HCISTLRATE));
- seq_printf(s, "\n");
- seq_printf(s, "HCINTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBUFSZ),
- isp1362_read_reg16(isp1362_hcd, HCINTLBUFSZ));
- seq_printf(s, "HCINTLBLKSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBLKSZ),
- isp1362_read_reg16(isp1362_hcd, HCINTLBLKSZ));
- seq_printf(s, "HCINTLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLDONE),
- isp1362_read_reg32(isp1362_hcd, HCINTLDONE));
- seq_printf(s, "HCINTLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLSKIP),
- isp1362_read_reg32(isp1362_hcd, HCINTLSKIP));
- seq_printf(s, "HCINTLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLLAST),
- isp1362_read_reg32(isp1362_hcd, HCINTLLAST));
- seq_printf(s, "HCINTLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLCURR),
- isp1362_read_reg16(isp1362_hcd, HCINTLCURR));
- seq_printf(s, "\n");
- seq_printf(s, "HCATLBUFSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBUFSZ),
- isp1362_read_reg16(isp1362_hcd, HCATLBUFSZ));
- seq_printf(s, "HCATLBLKSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBLKSZ),
- isp1362_read_reg16(isp1362_hcd, HCATLBLKSZ));
-#if 0
- seq_printf(s, "HCATLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDONE),
- isp1362_read_reg32(isp1362_hcd, HCATLDONE));
-#endif
- seq_printf(s, "HCATLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLSKIP),
- isp1362_read_reg32(isp1362_hcd, HCATLSKIP));
- seq_printf(s, "HCATLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLLAST),
- isp1362_read_reg32(isp1362_hcd, HCATLLAST));
- seq_printf(s, "HCATLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLCURR),
- isp1362_read_reg16(isp1362_hcd, HCATLCURR));
- seq_printf(s, "\n");
- seq_printf(s, "HCATLDTC [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTC),
- isp1362_read_reg16(isp1362_hcd, HCATLDTC));
- seq_printf(s, "HCATLDTCTO [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTCTO),
- isp1362_read_reg16(isp1362_hcd, HCATLDTCTO));
-}
-
-static int isp1362_show(struct seq_file *s, void *unused)
-{
- struct isp1362_hcd *isp1362_hcd = s->private;
- struct isp1362_ep *ep;
- int i;
-
- seq_printf(s, "%s\n%s version %s\n",
- isp1362_hcd_to_hcd(isp1362_hcd)->product_desc, hcd_name, DRIVER_VERSION);
-
- /* collect statistics to help estimate potential win for
- * DMA engines that care about alignment (PXA)
- */
- seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n",
- isp1362_hcd->stat16, isp1362_hcd->stat8, isp1362_hcd->stat4,
- isp1362_hcd->stat2, isp1362_hcd->stat1);
- seq_printf(s, "max # ptds in ATL fifo: %d\n", isp1362_hcd->atl_queue.stat_maxptds);
- seq_printf(s, "max # ptds in INTL fifo: %d\n", isp1362_hcd->intl_queue.stat_maxptds);
- seq_printf(s, "max # ptds in ISTL fifo: %d\n",
- max(isp1362_hcd->istl_queue[0] .stat_maxptds,
- isp1362_hcd->istl_queue[1] .stat_maxptds));
-
- /* FIXME: don't show the following in suspended state */
- spin_lock_irq(&isp1362_hcd->lock);
-
- dump_irq(s, "hc_irq_enable", isp1362_read_reg16(isp1362_hcd, HCuPINTENB));
- dump_irq(s, "hc_irq_status", isp1362_read_reg16(isp1362_hcd, HCuPINT));
- dump_int(s, "ohci_int_enable", isp1362_read_reg32(isp1362_hcd, HCINTENB));
- dump_int(s, "ohci_int_status", isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
- dump_ctrl(s, "ohci_control", isp1362_read_reg32(isp1362_hcd, HCCONTROL));
-
- for (i = 0; i < NUM_ISP1362_IRQS; i++)
- if (isp1362_hcd->irq_stat[i])
- seq_printf(s, "%-15s: %d\n",
- ISP1362_INT_NAME(i), isp1362_hcd->irq_stat[i]);
-
- dump_regs(s, isp1362_hcd);
- list_for_each_entry(ep, &isp1362_hcd->async, schedule) {
- struct urb *urb;
-
- seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, ep->epnum,
- ({
- char *s;
- switch (ep->nextpid) {
- case USB_PID_IN:
- s = "in";
- break;
- case USB_PID_OUT:
- s = "out";
- break;
- case USB_PID_SETUP:
- s = "setup";
- break;
- case USB_PID_ACK:
- s = "status";
- break;
- default:
- s = "?";
- break;
- }
- s;}), ep->maxpacket) ;
- list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
- seq_printf(s, " urb%p, %d/%d\n", urb,
- urb->actual_length,
- urb->transfer_buffer_length);
- }
- }
- if (!list_empty(&isp1362_hcd->async))
- seq_printf(s, "\n");
- dump_ptd_queue(&isp1362_hcd->atl_queue);
-
- seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
-
- list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) {
- seq_printf(s, "branch:%2d load:%3d PTD[%d] $%04x:\n", ep->branch,
- isp1362_hcd->load[ep->branch], ep->ptd_index, ep->ptd_offset);
-
- seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
- ep->interval, ep,
- (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ",
- ep->udev->devnum, ep->epnum,
- (ep->epnum == 0) ? "" :
- ((ep->nextpid == USB_PID_IN) ?
- "in" : "out"), ep->maxpacket);
- }
- dump_ptd_queue(&isp1362_hcd->intl_queue);
-
- seq_printf(s, "ISO:\n");
-
- list_for_each_entry(ep, &isp1362_hcd->isoc, schedule) {
- seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
- ep->interval, ep,
- (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ",
- ep->udev->devnum, ep->epnum,
- (ep->epnum == 0) ? "" :
- ((ep->nextpid == USB_PID_IN) ?
- "in" : "out"), ep->maxpacket);
- }
-
- spin_unlock_irq(&isp1362_hcd->lock);
- seq_printf(s, "\n");
-
- return 0;
-}
-DEFINE_SHOW_ATTRIBUTE(isp1362);
-
-/* expect just one isp1362_hcd per system */
-static void create_debug_file(struct isp1362_hcd *isp1362_hcd)
-{
- debugfs_create_file("isp1362", S_IRUGO, usb_debug_root, isp1362_hcd,
- &isp1362_fops);
-}
-
-static void remove_debug_file(struct isp1362_hcd *isp1362_hcd)
-{
- debugfs_lookup_and_remove("isp1362", usb_debug_root);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void __isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
-{
- int tmp = 20;
-
- isp1362_write_reg16(isp1362_hcd, HCSWRES, HCSWRES_MAGIC);
- isp1362_write_reg32(isp1362_hcd, HCCMDSTAT, OHCI_HCR);
- while (--tmp) {
- mdelay(1);
- if (!(isp1362_read_reg32(isp1362_hcd, HCCMDSTAT) & OHCI_HCR))
- break;
- }
- if (!tmp)
- pr_err("Software reset timeout\n");
-}
-
-static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- __isp1362_sw_reset(isp1362_hcd);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-}
-
-static int isp1362_mem_config(struct usb_hcd *hcd)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
- u32 total;
- u16 istl_size = ISP1362_ISTL_BUFSIZE;
- u16 intl_blksize = ISP1362_INTL_BLKSIZE + PTD_HEADER_SIZE;
- u16 intl_size = ISP1362_INTL_BUFFERS * intl_blksize;
- u16 atl_blksize = ISP1362_ATL_BLKSIZE + PTD_HEADER_SIZE;
- u16 atl_buffers = (ISP1362_BUF_SIZE - (istl_size + intl_size)) / atl_blksize;
- u16 atl_size;
- int i;
-
- WARN_ON(istl_size & 3);
- WARN_ON(atl_blksize & 3);
- WARN_ON(intl_blksize & 3);
- WARN_ON(atl_blksize < PTD_HEADER_SIZE);
- WARN_ON(intl_blksize < PTD_HEADER_SIZE);
-
- BUG_ON((unsigned)ISP1362_INTL_BUFFERS > 32);
- if (atl_buffers > 32)
- atl_buffers = 32;
- atl_size = atl_buffers * atl_blksize;
- total = atl_size + intl_size + istl_size;
- dev_info(hcd->self.controller, "ISP1362 Memory usage:\n");
- dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n",
- istl_size / 2, istl_size, 0, istl_size / 2);
- dev_info(hcd->self.controller, " INTL: %4d * (%3zu+8): %4d @ $%04x\n",
- ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE,
- intl_size, istl_size);
- dev_info(hcd->self.controller, " ATL : %4d * (%3zu+8): %4d @ $%04x\n",
- atl_buffers, atl_blksize - PTD_HEADER_SIZE,
- atl_size, istl_size + intl_size);
- dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total,
- ISP1362_BUF_SIZE - total);
-
- if (total > ISP1362_BUF_SIZE) {
- dev_err(hcd->self.controller, "%s: Memory requested: %d, available %d\n",
- __func__, total, ISP1362_BUF_SIZE);
- return -ENOMEM;
- }
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- for (i = 0; i < 2; i++) {
- isp1362_hcd->istl_queue[i].buf_start = i * istl_size / 2,
- isp1362_hcd->istl_queue[i].buf_size = istl_size / 2;
- isp1362_hcd->istl_queue[i].blk_size = 4;
- INIT_LIST_HEAD(&isp1362_hcd->istl_queue[i].active);
- snprintf(isp1362_hcd->istl_queue[i].name,
- sizeof(isp1362_hcd->istl_queue[i].name), "ISTL%d", i);
- DBG(3, "%s: %5s buf $%04x %d\n", __func__,
- isp1362_hcd->istl_queue[i].name,
- isp1362_hcd->istl_queue[i].buf_start,
- isp1362_hcd->istl_queue[i].buf_size);
- }
- isp1362_write_reg16(isp1362_hcd, HCISTLBUFSZ, istl_size / 2);
-
- isp1362_hcd->intl_queue.buf_start = istl_size;
- isp1362_hcd->intl_queue.buf_size = intl_size;
- isp1362_hcd->intl_queue.buf_count = ISP1362_INTL_BUFFERS;
- isp1362_hcd->intl_queue.blk_size = intl_blksize;
- isp1362_hcd->intl_queue.buf_avail = isp1362_hcd->intl_queue.buf_count;
- isp1362_hcd->intl_queue.skip_map = ~0;
- INIT_LIST_HEAD(&isp1362_hcd->intl_queue.active);
-
- isp1362_write_reg16(isp1362_hcd, HCINTLBUFSZ,
- isp1362_hcd->intl_queue.buf_size);
- isp1362_write_reg16(isp1362_hcd, HCINTLBLKSZ,
- isp1362_hcd->intl_queue.blk_size - PTD_HEADER_SIZE);
- isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0);
- isp1362_write_reg32(isp1362_hcd, HCINTLLAST,
- 1 << (ISP1362_INTL_BUFFERS - 1));
-
- isp1362_hcd->atl_queue.buf_start = istl_size + intl_size;
- isp1362_hcd->atl_queue.buf_size = atl_size;
- isp1362_hcd->atl_queue.buf_count = atl_buffers;
- isp1362_hcd->atl_queue.blk_size = atl_blksize;
- isp1362_hcd->atl_queue.buf_avail = isp1362_hcd->atl_queue.buf_count;
- isp1362_hcd->atl_queue.skip_map = ~0;
- INIT_LIST_HEAD(&isp1362_hcd->atl_queue.active);
-
- isp1362_write_reg16(isp1362_hcd, HCATLBUFSZ,
- isp1362_hcd->atl_queue.buf_size);
- isp1362_write_reg16(isp1362_hcd, HCATLBLKSZ,
- isp1362_hcd->atl_queue.blk_size - PTD_HEADER_SIZE);
- isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0);
- isp1362_write_reg32(isp1362_hcd, HCATLLAST,
- 1 << (atl_buffers - 1));
-
- snprintf(isp1362_hcd->atl_queue.name,
- sizeof(isp1362_hcd->atl_queue.name), "ATL");
- snprintf(isp1362_hcd->intl_queue.name,
- sizeof(isp1362_hcd->intl_queue.name), "INTL");
- DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__,
- isp1362_hcd->intl_queue.name,
- isp1362_hcd->intl_queue.buf_start,
- ISP1362_INTL_BUFFERS, isp1362_hcd->intl_queue.blk_size,
- isp1362_hcd->intl_queue.buf_size);
- DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__,
- isp1362_hcd->atl_queue.name,
- isp1362_hcd->atl_queue.buf_start,
- atl_buffers, isp1362_hcd->atl_queue.blk_size,
- isp1362_hcd->atl_queue.buf_size);
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- return 0;
-}
-
-static int isp1362_hc_reset(struct usb_hcd *hcd)
-{
- int ret = 0;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long t;
- unsigned long timeout = 100;
- unsigned long flags;
- int clkrdy = 0;
-
- pr_debug("%s:\n", __func__);
-
- if (isp1362_hcd->board && isp1362_hcd->board->reset) {
- isp1362_hcd->board->reset(hcd->self.controller, 1);
- msleep(20);
- if (isp1362_hcd->board->clock)
- isp1362_hcd->board->clock(hcd->self.controller, 1);
- isp1362_hcd->board->reset(hcd->self.controller, 0);
- } else
- isp1362_sw_reset(isp1362_hcd);
-
- /* chip has been reset. First we need to see a clock */
- t = jiffies + msecs_to_jiffies(timeout);
- while (!clkrdy && time_before_eq(jiffies, t)) {
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- clkrdy = isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_CLKRDY;
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (!clkrdy)
- msleep(4);
- }
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_CLKRDY);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (!clkrdy) {
- pr_err("Clock not ready after %lums\n", timeout);
- ret = -ENODEV;
- }
- return ret;
-}
-
-static void isp1362_hc_stop(struct usb_hcd *hcd)
-{
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
- u32 tmp;
-
- pr_debug("%s:\n", __func__);
-
- timer_delete_sync(&hcd->rh_timer);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
-
- /* Switch off power for all ports */
- tmp = isp1362_read_reg32(isp1362_hcd, HCRHDESCA);
- tmp &= ~(RH_A_NPS | RH_A_PSM);
- isp1362_write_reg32(isp1362_hcd, HCRHDESCA, tmp);
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS);
-
- /* Reset the chip */
- if (isp1362_hcd->board && isp1362_hcd->board->reset)
- isp1362_hcd->board->reset(hcd->self.controller, 1);
- else
- __isp1362_sw_reset(isp1362_hcd);
-
- if (isp1362_hcd->board && isp1362_hcd->board->clock)
- isp1362_hcd->board->clock(hcd->self.controller, 0);
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-}
-
-#ifdef CHIP_BUFFER_TEST
-static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd)
-{
- int ret = 0;
- u16 *ref;
- unsigned long flags;
-
- ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL);
- if (ref) {
- int offset;
- u16 *tst = &ref[ISP1362_BUF_SIZE / 2];
-
- for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) {
- ref[offset] = ~offset;
- tst[offset] = offset;
- }
-
- for (offset = 0; offset < 4; offset++) {
- int j;
-
- for (j = 0; j < 8; j++) {
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j);
- isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- if (memcmp(ref, tst, j)) {
- ret = -ENODEV;
- pr_err("%s: memory check with %d byte offset %d failed\n",
- __func__, j, offset);
- dump_data((u8 *)ref + offset, j);
- dump_data((u8 *)tst + offset, j);
- }
- }
- }
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE);
- isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- if (memcmp(ref, tst, ISP1362_BUF_SIZE)) {
- ret = -ENODEV;
- pr_err("%s: memory check failed\n", __func__);
- dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2);
- }
-
- for (offset = 0; offset < 256; offset++) {
- int test_size = 0;
-
- yield();
-
- memset(tst, 0, ISP1362_BUF_SIZE);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
- isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))),
- ISP1362_BUF_SIZE / 2)) {
- pr_err("%s: Failed to clear buffer\n", __func__);
- dump_data((u8 *)tst, ISP1362_BUF_SIZE);
- break;
- }
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE);
- isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref),
- offset * 2 + PTD_HEADER_SIZE, test_size);
- isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
- PTD_HEADER_SIZE + test_size);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
- dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size);
- dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
- PTD_HEADER_SIZE + test_size);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
- ret = -ENODEV;
- pr_err("%s: memory check with offset %02x failed\n",
- __func__, offset);
- break;
- }
- pr_warn("%s: memory check with offset %02x ok after second read\n",
- __func__, offset);
- }
- }
- kfree(ref);
- }
- return ret;
-}
-#endif
-
-static int isp1362_hc_start(struct usb_hcd *hcd)
-{
- int ret;
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- struct isp1362_platform_data *board = isp1362_hcd->board;
- u16 hwcfg;
- u16 chipid;
- unsigned long flags;
-
- pr_debug("%s:\n", __func__);
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- if ((chipid & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
- pr_err("%s: Invalid chip ID %04x\n", __func__, chipid);
- return -ENODEV;
- }
-
-#ifdef CHIP_BUFFER_TEST
- ret = isp1362_chip_test(isp1362_hcd);
- if (ret)
- return -ENODEV;
-#endif
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- /* clear interrupt status and disable all interrupt sources */
- isp1362_write_reg16(isp1362_hcd, HCuPINT, 0xff);
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
-
- /* HW conf */
- hwcfg = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1);
- if (board->sel15Kres)
- hwcfg |= HCHWCFG_PULLDOWN_DS2 |
- ((MAX_ROOT_PORTS > 1) ? HCHWCFG_PULLDOWN_DS1 : 0);
- if (board->clknotstop)
- hwcfg |= HCHWCFG_CLKNOTSTOP;
- if (board->oc_enable)
- hwcfg |= HCHWCFG_ANALOG_OC;
- if (board->int_act_high)
- hwcfg |= HCHWCFG_INT_POL;
- if (board->int_edge_triggered)
- hwcfg |= HCHWCFG_INT_TRIGGER;
- if (board->dreq_act_high)
- hwcfg |= HCHWCFG_DREQ_POL;
- if (board->dack_act_high)
- hwcfg |= HCHWCFG_DACK_POL;
- isp1362_write_reg16(isp1362_hcd, HCHWCFG, hwcfg);
- isp1362_show_reg(isp1362_hcd, HCHWCFG);
- isp1362_write_reg16(isp1362_hcd, HCDMACFG, 0);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- ret = isp1362_mem_config(hcd);
- if (ret)
- return ret;
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
-
- /* Root hub conf */
- isp1362_hcd->rhdesca = 0;
- if (board->no_power_switching)
- isp1362_hcd->rhdesca |= RH_A_NPS;
- if (board->power_switching_mode)
- isp1362_hcd->rhdesca |= RH_A_PSM;
- if (board->potpg)
- isp1362_hcd->rhdesca |= (board->potpg << 24) & RH_A_POTPGT;
- else
- isp1362_hcd->rhdesca |= (25 << 24) & RH_A_POTPGT;
-
- isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca & ~RH_A_OCPM);
- isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca | RH_A_OCPM);
- isp1362_hcd->rhdesca = isp1362_read_reg32(isp1362_hcd, HCRHDESCA);
-
- isp1362_hcd->rhdescb = RH_B_PPCM;
- isp1362_write_reg32(isp1362_hcd, HCRHDESCB, isp1362_hcd->rhdescb);
- isp1362_hcd->rhdescb = isp1362_read_reg32(isp1362_hcd, HCRHDESCB);
-
- isp1362_read_reg32(isp1362_hcd, HCFMINTVL);
- isp1362_write_reg32(isp1362_hcd, HCFMINTVL, (FSMP(FI) << 16) | FI);
- isp1362_write_reg32(isp1362_hcd, HCLSTHRESH, LSTHRESH);
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- isp1362_hcd->hc_control = OHCI_USB_OPER;
- hcd->state = HC_STATE_RUNNING;
-
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- /* Set up interrupts */
- isp1362_hcd->intenb = OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE;
- isp1362_hcd->intenb |= OHCI_INTR_RD;
- isp1362_hcd->irqenb = HCuPINT_OPR | HCuPINT_SUSP;
- isp1362_write_reg32(isp1362_hcd, HCINTENB, isp1362_hcd->intenb);
- isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
-
- /* Go operational */
- isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
- /* enable global power */
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC | RH_HS_DRWE);
-
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static const struct hc_driver isp1362_hc_driver = {
- .description = hcd_name,
- .product_desc = "ISP1362 Host Controller",
- .hcd_priv_size = sizeof(struct isp1362_hcd),
-
- .irq = isp1362_irq,
- .flags = HCD_USB11 | HCD_MEMORY,
-
- .reset = isp1362_hc_reset,
- .start = isp1362_hc_start,
- .stop = isp1362_hc_stop,
-
- .urb_enqueue = isp1362_urb_enqueue,
- .urb_dequeue = isp1362_urb_dequeue,
- .endpoint_disable = isp1362_endpoint_disable,
-
- .get_frame_number = isp1362_get_frame,
-
- .hub_status_data = isp1362_hub_status_data,
- .hub_control = isp1362_hub_control,
- .bus_suspend = isp1362_bus_suspend,
- .bus_resume = isp1362_bus_resume,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static void isp1362_remove(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
-
- remove_debug_file(isp1362_hcd);
- DBG(0, "%s: Removing HCD\n", __func__);
- usb_remove_hcd(hcd);
- DBG(0, "%s: put_hcd\n", __func__);
- usb_put_hcd(hcd);
- DBG(0, "%s: Done\n", __func__);
-}
-
-static int isp1362_probe(struct platform_device *pdev)
-{
- struct usb_hcd *hcd;
- struct isp1362_hcd *isp1362_hcd;
- struct resource *data, *irq_res;
- void __iomem *addr_reg;
- void __iomem *data_reg;
- int irq;
- int retval = 0;
- unsigned int irq_flags = 0;
-
- if (usb_disabled())
- return -ENODEV;
-
- /* basic sanity checks first. board-specific init logic should
- * have initialized this the three resources and probably board
- * specific platform_data. we don't probe for IRQs, and do only
- * minimal sanity checking.
- */
- if (pdev->num_resources < 3)
- return -ENODEV;
-
- irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq_res)
- return -ENODEV;
-
- irq = irq_res->start;
-
- addr_reg = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(addr_reg))
- return PTR_ERR(addr_reg);
-
- data_reg = devm_platform_get_and_ioremap_resource(pdev, 0, &data);
- if (IS_ERR(data_reg))
- return PTR_ERR(data_reg);
-
- /* allocate and initialize hcd */
- hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev));
- if (!hcd)
- return -ENOMEM;
-
- hcd->rsrc_start = data->start;
- isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- isp1362_hcd->data_reg = data_reg;
- isp1362_hcd->addr_reg = addr_reg;
-
- isp1362_hcd->next_statechange = jiffies;
- spin_lock_init(&isp1362_hcd->lock);
- INIT_LIST_HEAD(&isp1362_hcd->async);
- INIT_LIST_HEAD(&isp1362_hcd->periodic);
- INIT_LIST_HEAD(&isp1362_hcd->isoc);
- INIT_LIST_HEAD(&isp1362_hcd->remove_list);
- isp1362_hcd->board = dev_get_platdata(&pdev->dev);
-#if USE_PLATFORM_DELAY
- if (!isp1362_hcd->board->delay) {
- dev_err(hcd->self.controller, "No platform delay function given\n");
- retval = -ENODEV;
- goto err;
- }
-#endif
-
- if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE)
- irq_flags |= IRQF_TRIGGER_RISING;
- if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE)
- irq_flags |= IRQF_TRIGGER_FALLING;
- if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL)
- irq_flags |= IRQF_TRIGGER_HIGH;
- if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL)
- irq_flags |= IRQF_TRIGGER_LOW;
-
- retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED);
- if (retval != 0)
- goto err;
- device_wakeup_enable(hcd->self.controller);
-
- dev_info(&pdev->dev, "%s, irq %d\n", hcd->product_desc, irq);
-
- create_debug_file(isp1362_hcd);
-
- return 0;
-
- err:
- usb_put_hcd(hcd);
-
- return retval;
-}
-
-#ifdef CONFIG_PM
-static int isp1362_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
- int retval = 0;
-
- DBG(0, "%s: Suspending device\n", __func__);
-
- if (state.event == PM_EVENT_FREEZE) {
- DBG(0, "%s: Suspending root hub\n", __func__);
- retval = isp1362_bus_suspend(hcd);
- } else {
- DBG(0, "%s: Suspending RH ports\n", __func__);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- }
- if (retval == 0)
- pdev->dev.power.power_state = state;
- return retval;
-}
-
-static int isp1362_resume(struct platform_device *pdev)
-{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
- struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
- unsigned long flags;
-
- DBG(0, "%s: Resuming\n", __func__);
-
- if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
- DBG(0, "%s: Resume RH ports\n", __func__);
- spin_lock_irqsave(&isp1362_hcd->lock, flags);
- isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC);
- spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
- return 0;
- }
-
- pdev->dev.power.power_state = PMSG_ON;
-
- return isp1362_bus_resume(isp1362_hcd_to_hcd(isp1362_hcd));
-}
-#else
-#define isp1362_suspend NULL
-#define isp1362_resume NULL
-#endif
-
-static struct platform_driver isp1362_driver = {
- .probe = isp1362_probe,
- .remove = isp1362_remove,
-
- .suspend = isp1362_suspend,
- .resume = isp1362_resume,
- .driver = {
- .name = hcd_name,
- },
-};
-
-module_platform_driver(isp1362_driver);
diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h
deleted file mode 100644
index 74ca4be24723..000000000000
--- a/drivers/usb/host/isp1362.h
+++ /dev/null
@@ -1,914 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * ISP1362 HCD (Host Controller Driver) for USB.
- *
- * COPYRIGHT (C) by L. Wassmann <LW@KARO-electronics.de>
- */
-
-/* ------------------------------------------------------------------------- */
-
-#define MAX_ROOT_PORTS 2
-
-#define USE_32BIT 0
-
-/* These options are mutually exclusive */
-#define USE_PLATFORM_DELAY 0
-#define USE_NDELAY 0
-
-#define DUMMY_DELAY_ACCESS do {} while (0)
-
-/* ------------------------------------------------------------------------- */
-
-#define USB_RESET_WIDTH 50
-#define MAX_XFER_SIZE 1023
-
-/* Buffer sizes */
-#define ISP1362_BUF_SIZE 4096
-#define ISP1362_ISTL_BUFSIZE 512
-#define ISP1362_INTL_BLKSIZE 64
-#define ISP1362_INTL_BUFFERS 16
-#define ISP1362_ATL_BLKSIZE 64
-
-#define ISP1362_REG_WRITE_OFFSET 0x80
-
-#define REG_WIDTH_16 0x000
-#define REG_WIDTH_32 0x100
-#define REG_WIDTH_MASK 0x100
-#define REG_NO_MASK 0x0ff
-
-#ifdef ISP1362_DEBUG
-typedef const unsigned int isp1362_reg_t;
-
-#define REG_ACCESS_R 0x200
-#define REG_ACCESS_W 0x400
-#define REG_ACCESS_RW 0x600
-#define REG_ACCESS_MASK 0x600
-
-#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK)
-
-#define ISP1362_REG(name, addr, width, rw) \
-static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw))
-
-#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W))
-#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w))
-#else
-typedef const unsigned char isp1362_reg_t;
-#define ISP1362_REG_NO(r) (r)
-
-#define ISP1362_REG(name, addr, width, rw) \
-static isp1362_reg_t __maybe_unused ISP1362_REG_##name = addr
-
-#define REG_ACCESS_TEST(r) do {} while (0)
-#define REG_WIDTH_TEST(r, w) do {} while (0)
-#endif
-
-/* OHCI compatible registers */
-/*
- * Note: Some of the ISP1362 'OHCI' registers implement only
- * a subset of the bits defined in the OHCI spec.
- *
- * Bitmasks for the individual bits of these registers are defined in "ohci.h"
- */
-ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R);
-ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW);
-
-/* Philips ISP1362 specific registers */
-ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW);
-#define HCHWCFG_DISABLE_SUSPEND (1 << 15)
-#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14)
-#define HCHWCFG_PULLDOWN_DS2 (1 << 13)
-#define HCHWCFG_PULLDOWN_DS1 (1 << 12)
-#define HCHWCFG_CLKNOTSTOP (1 << 11)
-#define HCHWCFG_ANALOG_OC (1 << 10)
-#define HCHWCFG_ONEINT (1 << 9)
-#define HCHWCFG_DACK_MODE (1 << 8)
-#define HCHWCFG_ONEDMA (1 << 7)
-#define HCHWCFG_DACK_POL (1 << 6)
-#define HCHWCFG_DREQ_POL (1 << 5)
-#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
-#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
-#define HCHWCFG_INT_POL (1 << 2)
-#define HCHWCFG_INT_TRIGGER (1 << 1)
-#define HCHWCFG_INT_ENABLE (1 << 0)
-
-ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW);
-#define HCDMACFG_CTR_ENABLE (1 << 7)
-#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
-#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
-#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
-#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
-#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
-#define HCDMACFG_DMA_ENABLE (1 << 4)
-#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
-#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK)
-#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0)
-#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1)
-#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2)
-#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3)
-#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4)
-#define HCDMACFG_DMA_RW_SELECT (1 << 0)
-
-ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW);
-
-ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW);
-#define HCuPINT_SOF (1 << 0)
-#define HCuPINT_ISTL0 (1 << 1)
-#define HCuPINT_ISTL1 (1 << 2)
-#define HCuPINT_EOT (1 << 3)
-#define HCuPINT_OPR (1 << 4)
-#define HCuPINT_SUSP (1 << 5)
-#define HCuPINT_CLKRDY (1 << 6)
-#define HCuPINT_INTL (1 << 7)
-#define HCuPINT_ATL (1 << 8)
-#define HCuPINT_OTG (1 << 9)
-
-ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW);
-/* same bit definitions apply as for HCuPINT */
-
-ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R);
-#define HCCHIPID_MASK 0xff00
-#define HCCHIPID_MAGIC 0x3600
-
-ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW);
-
-ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W);
-#define HCSWRES_MAGIC 0x00f6
-
-ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW);
-#define HCBUFSTAT_ISTL0_FULL (1 << 0)
-#define HCBUFSTAT_ISTL1_FULL (1 << 1)
-#define HCBUFSTAT_INTL_ACTIVE (1 << 2)
-#define HCBUFSTAT_ATL_ACTIVE (1 << 3)
-#define HCBUFSTAT_RESET_HWPP (1 << 4)
-#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5)
-#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6)
-#define HCBUFSTAT_ISTL0_DONE (1 << 8)
-#define HCBUFSTAT_ISTL1_DONE (1 << 9)
-#define HCBUFSTAT_PAIRED_PTDPP (1 << 10)
-
-ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW);
-#define HCDIRADDR_ADDR_MASK 0x0000ffff
-#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK)
-#define HCDIRADDR_COUNT_MASK 0xffff0000
-#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK)
-ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW);
-
-ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW);
-
-ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R);
-ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R);
-
-ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R);
-ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW);
-ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R);
-
-ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW);
-
-
-ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R);
-ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW);
-ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW);
-
-/* Philips transfer descriptor, cpu-endian */
-struct ptd {
- u16 count;
-#define PTD_COUNT_MSK (0x3ff << 0)
-#define PTD_TOGGLE_MSK (1 << 10)
-#define PTD_ACTIVE_MSK (1 << 11)
-#define PTD_CC_MSK (0xf << 12)
- u16 mps;
-#define PTD_MPS_MSK (0x3ff << 0)
-#define PTD_SPD_MSK (1 << 10)
-#define PTD_LAST_MSK (1 << 11)
-#define PTD_EP_MSK (0xf << 12)
- u16 len;
-#define PTD_LEN_MSK (0x3ff << 0)
-#define PTD_DIR_MSK (3 << 10)
-#define PTD_DIR_SETUP (0)
-#define PTD_DIR_OUT (1)
-#define PTD_DIR_IN (2)
- u16 faddr;
-#define PTD_FA_MSK (0x7f << 0)
-/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */
-#define PTD_SF_ISO_MSK (0xff << 8)
-#define PTD_SF_INT_MSK (0x1f << 8)
-#define PTD_PR_MSK (0x07 << 13)
-} __attribute__ ((packed, aligned(2)));
-#define PTD_HEADER_SIZE sizeof(struct ptd)
-
-/* ------------------------------------------------------------------------- */
-/* Copied from ohci.h: */
-/*
- * Hardware transfer status codes -- CC from PTD
- */
-#define PTD_CC_NOERROR 0x00
-#define PTD_CC_CRC 0x01
-#define PTD_CC_BITSTUFFING 0x02
-#define PTD_CC_DATATOGGLEM 0x03
-#define PTD_CC_STALL 0x04
-#define PTD_DEVNOTRESP 0x05
-#define PTD_PIDCHECKFAIL 0x06
-#define PTD_UNEXPECTEDPID 0x07
-#define PTD_DATAOVERRUN 0x08
-#define PTD_DATAUNDERRUN 0x09
- /* 0x0A, 0x0B reserved for hardware */
-#define PTD_BUFFEROVERRUN 0x0C
-#define PTD_BUFFERUNDERRUN 0x0D
- /* 0x0E, 0x0F reserved for HCD */
-#define PTD_NOTACCESSED 0x0F
-
-
-/* map OHCI TD status codes (CC) to errno values */
-static const int cc_to_error[16] = {
- /* No Error */ 0,
- /* CRC Error */ -EILSEQ,
- /* Bit Stuff */ -EPROTO,
- /* Data Togg */ -EILSEQ,
- /* Stall */ -EPIPE,
- /* DevNotResp */ -ETIMEDOUT,
- /* PIDCheck */ -EPROTO,
- /* UnExpPID */ -EPROTO,
- /* DataOver */ -EOVERFLOW,
- /* DataUnder */ -EREMOTEIO,
- /* (for hw) */ -EIO,
- /* (for hw) */ -EIO,
- /* BufferOver */ -ECOMM,
- /* BuffUnder */ -ENOSR,
- /* (for HCD) */ -EALREADY,
- /* (for HCD) */ -EALREADY
-};
-
-
-/*
- * HcControl (control) register masks
- */
-#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
-#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
-#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
-
-/* pre-shifted values for HCFS */
-# define OHCI_USB_RESET (0 << 6)
-# define OHCI_USB_RESUME (1 << 6)
-# define OHCI_USB_OPER (2 << 6)
-# define OHCI_USB_SUSPEND (3 << 6)
-
-/*
- * HcCommandStatus (cmdstatus) register masks
- */
-#define OHCI_HCR (1 << 0) /* host controller reset */
-#define OHCI_SOC (3 << 16) /* scheduling overrun count */
-
-/*
- * masks used with interrupt registers:
- * HcInterruptStatus (intrstatus)
- * HcInterruptEnable (intrenable)
- * HcInterruptDisable (intrdisable)
- */
-#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
-#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
-#define OHCI_INTR_SF (1 << 2) /* start frame */
-#define OHCI_INTR_RD (1 << 3) /* resume detect */
-#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
-#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
-#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
-#define OHCI_INTR_OC (1 << 30) /* ownership change */
-#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
-
-/* roothub.portstatus [i] bits */
-#define RH_PS_CCS 0x00000001 /* current connect status */
-#define RH_PS_PES 0x00000002 /* port enable status*/
-#define RH_PS_PSS 0x00000004 /* port suspend status */
-#define RH_PS_POCI 0x00000008 /* port over current indicator */
-#define RH_PS_PRS 0x00000010 /* port reset status */
-#define RH_PS_PPS 0x00000100 /* port power status */
-#define RH_PS_LSDA 0x00000200 /* low speed device attached */
-#define RH_PS_CSC 0x00010000 /* connect status change */
-#define RH_PS_PESC 0x00020000 /* port enable status change */
-#define RH_PS_PSSC 0x00040000 /* port suspend status change */
-#define RH_PS_OCIC 0x00080000 /* over current indicator change */
-#define RH_PS_PRSC 0x00100000 /* port reset status change */
-
-/* roothub.status bits */
-#define RH_HS_LPS 0x00000001 /* local power status */
-#define RH_HS_OCI 0x00000002 /* over current indicator */
-#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
-#define RH_HS_LPSC 0x00010000 /* local power status change */
-#define RH_HS_OCIC 0x00020000 /* over current indicator change */
-#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
-
-/* roothub.b masks */
-#define RH_B_DR 0x0000ffff /* device removable flags */
-#define RH_B_PPCM 0xffff0000 /* port power control mask */
-
-/* roothub.a masks */
-#define RH_A_NDP (0xff << 0) /* number of downstream ports */
-#define RH_A_PSM (1 << 8) /* power switching mode */
-#define RH_A_NPS (1 << 9) /* no power switching */
-#define RH_A_DT (1 << 10) /* device type (mbz) */
-#define RH_A_OCPM (1 << 11) /* over current protection mode */
-#define RH_A_NOCP (1 << 12) /* no over current protection */
-#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
-
-#define FI 0x2edf /* 12000 bits per frame (-1) */
-#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
-#define LSTHRESH 0x628 /* lowspeed bit threshold */
-
-/* ------------------------------------------------------------------------- */
-
-/* PTD accessor macros. */
-#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
-#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
-#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
-#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
-#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
-#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
-#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
-#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
-#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
-#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
-#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
-#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
-#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
-#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
-#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
-#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
-#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
-#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
-#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
-#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
-#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
-#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
-#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8)
-#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK)
-#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8)
-#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK)
-#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13)
-#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK)
-
-#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
-#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
-
-struct isp1362_ep {
- struct usb_host_endpoint *hep;
- struct usb_device *udev;
-
- /* philips transfer descriptor */
- struct ptd ptd;
-
- u8 maxpacket;
- u8 epnum;
- u8 nextpid;
- u16 error_count;
- u16 length; /* of current packet */
- s16 ptd_offset; /* buffer offset in ISP1362 where
- PTD has been stored
- (for access thru HCDIRDATA) */
- int ptd_index;
- int num_ptds;
- void *data; /* to databuf */
- /* queue of active EPs (the ones transmitted to the chip) */
- struct list_head active;
-
- /* periodic schedule */
- u8 branch;
- u16 interval;
- u16 load;
- u16 last_iso;
-
- /* async schedule */
- struct list_head schedule; /* list of all EPs that need processing */
- struct list_head remove_list;
- int num_req;
-};
-
-struct isp1362_ep_queue {
- struct list_head active; /* list of PTDs currently processed by HC */
- atomic_t finishing;
- unsigned long buf_map;
- unsigned long skip_map;
- int free_ptd;
- u16 buf_start;
- u16 buf_size;
- u16 blk_size; /* PTD buffer block size for ATL and INTL */
- u8 buf_count;
- u8 buf_avail;
- char name[16];
-
- /* for statistical tracking */
- u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */
- u8 ptd_count; /* number of ptds submitted to this queue */
-};
-
-struct isp1362_hcd {
- spinlock_t lock;
- void __iomem *addr_reg;
- void __iomem *data_reg;
-
- struct isp1362_platform_data *board;
-
- unsigned long stat1, stat2, stat4, stat8, stat16;
-
- /* HC registers */
- u32 intenb; /* "OHCI" interrupts */
- u16 irqenb; /* uP interrupts */
-
- /* Root hub registers */
- u32 rhdesca;
- u32 rhdescb;
- u32 rhstatus;
- u32 rhport[MAX_ROOT_PORTS];
- unsigned long next_statechange;
-
- /* HC control reg shadow copy */
- u32 hc_control;
-
- /* async schedule: control, bulk */
- struct list_head async;
-
- /* periodic schedule: int */
- u16 load[PERIODIC_SIZE];
- struct list_head periodic;
- u16 fmindex;
-
- /* periodic schedule: isochronous */
- struct list_head isoc;
- unsigned int istl_flip:1;
- unsigned int irq_active:1;
-
- /* Schedules for the current frame */
- struct isp1362_ep_queue atl_queue;
- struct isp1362_ep_queue intl_queue;
- struct isp1362_ep_queue istl_queue[2];
-
- /* list of PTDs retrieved from HC */
- struct list_head remove_list;
- enum {
- ISP1362_INT_SOF,
- ISP1362_INT_ISTL0,
- ISP1362_INT_ISTL1,
- ISP1362_INT_EOT,
- ISP1362_INT_OPR,
- ISP1362_INT_SUSP,
- ISP1362_INT_CLKRDY,
- ISP1362_INT_INTL,
- ISP1362_INT_ATL,
- ISP1362_INT_OTG,
- NUM_ISP1362_IRQS
- } IRQ_NAMES;
- unsigned int irq_stat[NUM_ISP1362_IRQS];
- int req_serial;
-};
-
-static inline const char *ISP1362_INT_NAME(int n)
-{
- switch (n) {
- case ISP1362_INT_SOF: return "SOF";
- case ISP1362_INT_ISTL0: return "ISTL0";
- case ISP1362_INT_ISTL1: return "ISTL1";
- case ISP1362_INT_EOT: return "EOT";
- case ISP1362_INT_OPR: return "OPR";
- case ISP1362_INT_SUSP: return "SUSP";
- case ISP1362_INT_CLKRDY: return "CLKRDY";
- case ISP1362_INT_INTL: return "INTL";
- case ISP1362_INT_ATL: return "ATL";
- case ISP1362_INT_OTG: return "OTG";
- default: return "unknown";
- }
-}
-
-static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr)
-{
- unsigned long p = (unsigned long)ptr;
- if (!(p & 0xf))
- isp1362_hcd->stat16++;
- else if (!(p & 0x7))
- isp1362_hcd->stat8++;
- else if (!(p & 0x3))
- isp1362_hcd->stat4++;
- else if (!(p & 0x1))
- isp1362_hcd->stat2++;
- else
- isp1362_hcd->stat1++;
-}
-
-static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd)
-{
- return (struct isp1362_hcd *) (hcd->hcd_priv);
-}
-
-static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd)
-{
- return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv);
-}
-
-#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0)
-
-/*
- * ISP1362 HW Interface
- */
-
-#define DBG(level, fmt...) \
- do { \
- if (dbg_level > level) \
- pr_debug(fmt); \
- } while (0)
-
-#ifdef VERBOSE
-# define VDBG(fmt...) DBG(3, fmt)
-#else
-# define VDBG(fmt...) do {} while (0)
-#endif
-
-#ifdef REGISTERS
-# define RDBG(fmt...) DBG(1, fmt)
-#else
-# define RDBG(fmt...) do {} while (0)
-#endif
-
-#ifdef URB_TRACE
-#define URB_DBG(fmt...) DBG(0, fmt)
-#else
-#define URB_DBG(fmt...) do {} while (0)
-#endif
-
-
-#if USE_PLATFORM_DELAY
-#if USE_NDELAY
-#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously.
-#endif
-#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d)
-#elif USE_NDELAY
-#define isp1362_delay(h, d) ndelay(d)
-#else
-#define isp1362_delay(h, d) do {} while (0)
-#endif
-
-#define get_urb(ep) ({ \
- BUG_ON(list_empty(&ep->hep->urb_list)); \
- container_of(ep->hep->urb_list.next, struct urb, urb_list); \
-})
-
-/* basic access functions for ISP1362 chip registers */
-/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure,
- * that all register accesses are performed with interrupts disabled, since the interrupt
- * handler has no way of restoring the previous state.
- */
-static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg)
-{
- REG_ACCESS_TEST(reg);
- DUMMY_DELAY_ACCESS;
- writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg);
- DUMMY_DELAY_ACCESS;
- isp1362_delay(isp1362_hcd, 1);
-}
-
-static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val)
-{
- DUMMY_DELAY_ACCESS;
- writew(val, isp1362_hcd->data_reg);
-}
-
-static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd)
-{
- u16 val;
-
- DUMMY_DELAY_ACCESS;
- val = readw(isp1362_hcd->data_reg);
-
- return val;
-}
-
-static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val)
-{
-#if USE_32BIT
- DUMMY_DELAY_ACCESS;
- writel(val, isp1362_hcd->data_reg);
-#else
- DUMMY_DELAY_ACCESS;
- writew((u16)val, isp1362_hcd->data_reg);
- DUMMY_DELAY_ACCESS;
- writew(val >> 16, isp1362_hcd->data_reg);
-#endif
-}
-
-static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd)
-{
- u32 val;
-
-#if USE_32BIT
- DUMMY_DELAY_ACCESS;
- val = readl(isp1362_hcd->data_reg);
-#else
- DUMMY_DELAY_ACCESS;
- val = (u32)readw(isp1362_hcd->data_reg);
- DUMMY_DELAY_ACCESS;
- val |= (u32)readw(isp1362_hcd->data_reg) << 16;
-#endif
- return val;
-}
-
-/* use readsw/writesw to access the fifo whenever possible */
-/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */
-static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
-{
- u8 *dp = buf;
- u16 data;
-
- if (!len)
- return;
-
- RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf);
-#if USE_32BIT
- if (len >= 4) {
- RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2);
- readsl(isp1362_hcd->data_reg, dp, len >> 2);
- dp += len & ~3;
- len &= 3;
- }
-#endif
- if (len >= 2) {
- RDBG("%s: Using readsw for %d words\n", __func__, len >> 1);
- insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
- dp += len & ~1;
- len &= 1;
- }
-
- BUG_ON(len & ~1);
- if (len > 0) {
- data = isp1362_read_data16(isp1362_hcd);
- RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__,
- (u8)data, (u32)dp);
- *dp = (u8)data;
- }
-}
-
-static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
-{
- u8 *dp = buf;
- u16 data;
-
- if (!len)
- return;
-
- if ((unsigned long)dp & 0x1) {
- /* not aligned */
- for (; len > 1; len -= 2) {
- data = *dp++;
- data |= *dp++ << 8;
- isp1362_write_data16(isp1362_hcd, data);
- }
- if (len)
- isp1362_write_data16(isp1362_hcd, *dp);
- return;
- }
-
- RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf);
-#if USE_32BIT
- if (len >= 4) {
- RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2);
- writesl(isp1362_hcd->data_reg, dp, len >> 2);
- dp += len & ~3;
- len &= 3;
- }
-#endif
- if (len >= 2) {
- RDBG("%s: Using writesw for %d words\n", __func__, len >> 1);
- outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
- dp += len & ~1;
- len &= 1;
- }
-
- BUG_ON(len & ~1);
- if (len > 0) {
- /* finally write any trailing byte; we don't need to care
- * about the high byte of the last word written
- */
- data = (u16)*dp;
- RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__,
- data, (u32)dp);
- isp1362_write_data16(isp1362_hcd, data);
- }
-}
-
-#define isp1362_read_reg16(d, r) ({ \
- u16 __v; \
- REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
- isp1362_write_addr(d, ISP1362_REG_##r); \
- __v = isp1362_read_data16(d); \
- RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \
- ISP1362_REG_NO(ISP1362_REG_##r)); \
- __v; \
-})
-
-#define isp1362_read_reg32(d, r) ({ \
- u32 __v; \
- REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
- isp1362_write_addr(d, ISP1362_REG_##r); \
- __v = isp1362_read_data32(d); \
- RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \
- ISP1362_REG_NO(ISP1362_REG_##r)); \
- __v; \
-})
-
-#define isp1362_write_reg16(d, r, v) { \
- REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
- isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
- isp1362_write_data16(d, (u16)(v)); \
- RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \
- ISP1362_REG_NO(ISP1362_REG_##r)); \
-}
-
-#define isp1362_write_reg32(d, r, v) { \
- REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
- isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
- isp1362_write_data32(d, (u32)(v)); \
- RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \
- ISP1362_REG_NO(ISP1362_REG_##r)); \
-}
-
-#define isp1362_set_mask16(d, r, m) { \
- u16 __v; \
- __v = isp1362_read_reg16(d, r); \
- if ((__v | m) != __v) \
- isp1362_write_reg16(d, r, __v | m); \
-}
-
-#define isp1362_clr_mask16(d, r, m) { \
- u16 __v; \
- __v = isp1362_read_reg16(d, r); \
- if ((__v & ~m) != __v) \
- isp1362_write_reg16(d, r, __v & ~m); \
-}
-
-#define isp1362_set_mask32(d, r, m) { \
- u32 __v; \
- __v = isp1362_read_reg32(d, r); \
- if ((__v | m) != __v) \
- isp1362_write_reg32(d, r, __v | m); \
-}
-
-#define isp1362_clr_mask32(d, r, m) { \
- u32 __v; \
- __v = isp1362_read_reg32(d, r); \
- if ((__v & ~m) != __v) \
- isp1362_write_reg32(d, r, __v & ~m); \
-}
-
-#define isp1362_show_reg(d, r) { \
- if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \
- DBG(0, "%-12s[%02x]: %08x\n", #r, \
- ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \
- else \
- DBG(0, "%-12s[%02x]: %04x\n", #r, \
- ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \
-}
-
-static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len)
-{
- len = (len + 1) & ~1;
-
- isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE);
- isp1362_write_reg32(isp1362_hcd, HCDIRADDR,
- HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len));
-}
-
-static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
-{
- isp1362_write_diraddr(isp1362_hcd, offset, len);
-
- DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n",
- __func__, len, offset, buf);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
-
- isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA);
-
- isp1362_read_fifo(isp1362_hcd, buf, len);
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
-}
-
-static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
-{
- isp1362_write_diraddr(isp1362_hcd, offset, len);
-
- DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n",
- __func__, len, offset, buf);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
-
- isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET);
- isp1362_write_fifo(isp1362_hcd, buf, len);
-
- isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
-}
-
-static void __attribute__((unused)) dump_data(char *buf, int len)
-{
- if (dbg_level > 0) {
- int k;
- int lf = 0;
-
- for (k = 0; k < len; ++k) {
- if (!lf)
- DBG(0, "%04x:", k);
- printk(" %02x", ((u8 *) buf)[k]);
- lf = 1;
- if (!k)
- continue;
- if (k % 16 == 15) {
- printk("\n");
- lf = 0;
- continue;
- }
- if (k % 8 == 7)
- printk(" ");
- if (k % 4 == 3)
- printk(" ");
- }
- if (lf)
- printk("\n");
- }
-}
-
-#if defined(PTD_TRACE)
-
-static void dump_ptd(struct ptd *ptd)
-{
- DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n",
- container_of(ptd, struct isp1362_ep, ptd),
- PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd),
- PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
- PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd),
- PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd));
- DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr);
-}
-
-static void dump_ptd_out_data(struct ptd *ptd, u8 *buf)
-{
- if (dbg_level > 0) {
- if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) {
- DBG(0, "--out->\n");
- dump_data(buf, PTD_GET_LEN(ptd));
- }
- }
-}
-
-static void dump_ptd_in_data(struct ptd *ptd, u8 *buf)
-{
- if (dbg_level > 0) {
- if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) {
- DBG(0, "<--in--\n");
- dump_data(buf, PTD_GET_COUNT(ptd));
- }
- DBG(0, "-----\n");
- }
-}
-
-static void dump_ptd_queue(struct isp1362_ep_queue *epq)
-{
- struct isp1362_ep *ep;
- int dbg = dbg_level;
-
- dbg_level = 1;
- list_for_each_entry(ep, &epq->active, active) {
- dump_ptd(&ep->ptd);
- dump_data(ep->data, ep->length);
- }
- dbg_level = dbg;
-}
-#else
-#define dump_ptd(ptd) do {} while (0)
-#define dump_ptd_in_data(ptd, buf) do {} while (0)
-#define dump_ptd_out_data(ptd, buf) do {} while (0)
-#define dump_ptd_data(ptd, buf) do {} while (0)
-#define dump_ptd_queue(epq) do {} while (0)
-#endif
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 9c7f3008646e..30840922f729 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1282,7 +1282,6 @@ static int __init ohci_hcd_mod_init(void)
pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name,
sizeof (struct ed), sizeof (struct td));
- set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root);
@@ -1332,7 +1331,6 @@ static int __init ohci_hcd_mod_init(void)
debugfs_remove(ohci_debug_root);
ohci_debug_root = NULL;
- clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
return retval;
}
module_init(ohci_hcd_mod_init);
@@ -1352,7 +1350,6 @@ static void __exit ohci_hcd_mod_exit(void)
ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
#endif
debugfs_remove(ohci_debug_root);
- clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);
}
module_exit(ohci_hcd_mod_exit);
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 14e6dfef16c6..8bb11109b66c 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -867,8 +867,6 @@ static int __init uhci_hcd_init(void)
if (usb_disabled())
return -ENODEV;
- set_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
-
#ifdef CONFIG_DYNAMIC_DEBUG
errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
if (!errbuf)
@@ -912,8 +910,6 @@ up_failed:
errbuf_failed:
#endif
-
- clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
return retval;
}
@@ -930,7 +926,6 @@ static void __exit uhci_hcd_cleanup(void)
#ifdef CONFIG_DYNAMIC_DEBUG
kfree(errbuf);
#endif
- clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);
}
module_init(uhci_hcd_init);
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 9da4f3b452cb..9fd497e0dc7f 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -29,6 +29,12 @@
#include "xhci-trace.h"
#include "xhci-dbgcap.h"
+static const struct dbc_str dbc_str_default = {
+ .manufacturer = "Linux Foundation",
+ .product = "Linux USB Debug Target",
+ .serial = "0001",
+};
+
static void dbc_free_ctx(struct device *dev, struct xhci_container_ctx *ctx)
{
if (!ctx)
@@ -52,55 +58,6 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring)
kfree(ring);
}
-static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings)
-{
- struct usb_string_descriptor *s_desc;
- u32 string_length;
-
- /* Serial string: */
- s_desc = (struct usb_string_descriptor *)strings->serial;
- utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL),
- UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
- DBC_MAX_STRING_LENGTH);
-
- s_desc->bLength = (strlen(DBC_STRING_SERIAL) + 1) * 2;
- s_desc->bDescriptorType = USB_DT_STRING;
- string_length = s_desc->bLength;
- string_length <<= 8;
-
- /* Product string: */
- s_desc = (struct usb_string_descriptor *)strings->product;
- utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT),
- UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
- DBC_MAX_STRING_LENGTH);
-
- s_desc->bLength = (strlen(DBC_STRING_PRODUCT) + 1) * 2;
- s_desc->bDescriptorType = USB_DT_STRING;
- string_length += s_desc->bLength;
- string_length <<= 8;
-
- /* Manufacture string: */
- s_desc = (struct usb_string_descriptor *)strings->manufacturer;
- utf8s_to_utf16s(DBC_STRING_MANUFACTURER,
- strlen(DBC_STRING_MANUFACTURER),
- UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
- DBC_MAX_STRING_LENGTH);
-
- s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2;
- s_desc->bDescriptorType = USB_DT_STRING;
- string_length += s_desc->bLength;
- string_length <<= 8;
-
- /* String0: */
- strings->string0[0] = 4;
- strings->string0[1] = USB_DT_STRING;
- strings->string0[2] = 0x09;
- strings->string0[3] = 0x04;
- string_length += 4;
-
- return string_length;
-}
-
static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc)
{
struct xhci_ep_ctx *ep_ctx;
@@ -124,7 +81,65 @@ static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc)
ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state);
}
-static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length)
+static u8 get_str_desc_len(const char *desc)
+{
+ return ((struct usb_string_descriptor *)desc)->bLength;
+}
+
+static u32 dbc_prepare_info_context_str_len(struct dbc_str_descs *descs)
+{
+ u32 len;
+
+ len = get_str_desc_len(descs->serial);
+ len <<= 8;
+ len += get_str_desc_len(descs->product);
+ len <<= 8;
+ len += get_str_desc_len(descs->manufacturer);
+ len <<= 8;
+ len += get_str_desc_len(descs->string0);
+
+ return len;
+}
+
+static int xhci_dbc_populate_str_desc(char *desc, const char *src)
+{
+ struct usb_string_descriptor *s_desc;
+ int len;
+
+ s_desc = (struct usb_string_descriptor *)desc;
+
+ /* len holds number of 2 byte UTF-16 characters */
+ len = utf8s_to_utf16s(src, strlen(src), UTF16_LITTLE_ENDIAN,
+ (wchar_t *)s_desc->wData, USB_MAX_STRING_LEN * 2);
+ if (len < 0)
+ return len;
+
+ s_desc->bLength = len * 2 + 2;
+ s_desc->bDescriptorType = USB_DT_STRING;
+
+ return s_desc->bLength;
+}
+
+static void xhci_dbc_populate_str_descs(struct dbc_str_descs *str_descs,
+ struct dbc_str *str)
+{
+ /* Serial string: */
+ xhci_dbc_populate_str_desc(str_descs->serial, str->serial);
+
+ /* Product string: */
+ xhci_dbc_populate_str_desc(str_descs->product, str->product);
+
+ /* Manufacturer string: */
+ xhci_dbc_populate_str_desc(str_descs->manufacturer, str->manufacturer);
+
+ /* String0: */
+ str_descs->string0[0] = 4;
+ str_descs->string0[1] = USB_DT_STRING;
+ str_descs->string0[2] = 0x09;
+ str_descs->string0[3] = 0x04;
+}
+
+static void xhci_dbc_init_contexts(struct xhci_dbc *dbc)
{
struct dbc_info_context *info;
u32 dev_info;
@@ -135,12 +150,12 @@ static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length)
/* Populate info Context: */
info = (struct dbc_info_context *)dbc->ctx->bytes;
- dma = dbc->string_dma;
+ dma = dbc->str_descs_dma;
info->string0 = cpu_to_le64(dma);
- info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH);
- info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2);
- info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3);
- info->length = cpu_to_le32(string_length);
+ info->manufacturer = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN);
+ info->product = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 2);
+ info->serial = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 3);
+ info->length = cpu_to_le32(dbc_prepare_info_context_str_len(dbc->str_descs));
/* Populate bulk in and out endpoint contexts: */
xhci_dbc_init_ep_contexts(dbc);
@@ -525,7 +540,6 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
{
int ret;
dma_addr_t deq;
- u32 string_length;
struct device *dev = dbc->dev;
/* Allocate various rings for events and transfers: */
@@ -552,11 +566,11 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
goto ctx_fail;
/* Allocate the string table: */
- dbc->string_size = sizeof(*dbc->string);
- dbc->string = dma_alloc_coherent(dev, dbc->string_size,
- &dbc->string_dma, flags);
- if (!dbc->string)
- goto string_fail;
+ dbc->str_descs_size = sizeof(*dbc->str_descs);
+ dbc->str_descs = dma_alloc_coherent(dev, dbc->str_descs_size,
+ &dbc->str_descs_dma, flags);
+ if (!dbc->str_descs)
+ goto str_descs_fail;
/* Setup ERST register: */
writel(dbc->erst.num_entries, &dbc->regs->ersts);
@@ -566,16 +580,16 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags)
dbc->ring_evt->dequeue);
lo_hi_writeq(deq, &dbc->regs->erdp);
- /* Setup strings and contexts: */
- string_length = xhci_dbc_populate_strings(dbc->string);
- xhci_dbc_init_contexts(dbc, string_length);
+ /* Setup string descriptors and contexts: */
+ xhci_dbc_populate_str_descs(dbc->str_descs, &dbc->str);
+ xhci_dbc_init_contexts(dbc);
xhci_dbc_eps_init(dbc);
dbc->state = DS_INITIALIZED;
return 0;
-string_fail:
+str_descs_fail:
dbc_free_ctx(dev, dbc->ctx);
dbc->ctx = NULL;
ctx_fail:
@@ -600,8 +614,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc)
xhci_dbc_eps_exit(dbc);
- dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma);
- dbc->string = NULL;
+ dma_free_coherent(dbc->dev, dbc->str_descs_size, dbc->str_descs, dbc->str_descs_dma);
+ dbc->str_descs = NULL;
dbc_free_ctx(dbc->dev, dbc->ctx);
dbc->ctx = NULL;
@@ -1196,6 +1210,108 @@ static ssize_t dbc_bcdDevice_store(struct device *dev,
return size;
}
+static ssize_t dbc_manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+
+ return sysfs_emit(buf, "%s\n", dbc->str.manufacturer);
+}
+
+static ssize_t dbc_manufacturer_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+ size_t len;
+
+ if (dbc->state != DS_DISABLED)
+ return -EBUSY;
+
+ len = strcspn(buf, "\n");
+ if (!len)
+ return -EINVAL;
+
+ if (len > USB_MAX_STRING_LEN)
+ return -E2BIG;
+
+ memcpy(dbc->str.manufacturer, buf, len);
+ dbc->str.manufacturer[len] = '\0';
+
+ return size;
+}
+
+static ssize_t dbc_product_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+
+ return sysfs_emit(buf, "%s\n", dbc->str.product);
+}
+
+static ssize_t dbc_product_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+ size_t len;
+
+ if (dbc->state != DS_DISABLED)
+ return -EBUSY;
+
+ len = strcspn(buf, "\n");
+ if (!len)
+ return -EINVAL;
+
+ if (len > USB_MAX_STRING_LEN)
+ return -E2BIG;
+
+ memcpy(dbc->str.product, buf, len);
+ dbc->str.product[len] = '\0';
+
+ return size;
+}
+
+static ssize_t dbc_serial_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+
+ return sysfs_emit(buf, "%s\n", dbc->str.serial);
+}
+
+static ssize_t dbc_serial_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ struct xhci_dbc *dbc = xhci->dbc;
+ size_t len;
+
+ if (dbc->state != DS_DISABLED)
+ return -EBUSY;
+
+ len = strcspn(buf, "\n");
+ if (!len)
+ return -EINVAL;
+
+ if (len > USB_MAX_STRING_LEN)
+ return -E2BIG;
+
+ memcpy(dbc->str.serial, buf, len);
+ dbc->str.serial[len] = '\0';
+
+ return size;
+}
+
static ssize_t dbc_bInterfaceProtocol_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1283,6 +1399,9 @@ static DEVICE_ATTR_RW(dbc);
static DEVICE_ATTR_RW(dbc_idVendor);
static DEVICE_ATTR_RW(dbc_idProduct);
static DEVICE_ATTR_RW(dbc_bcdDevice);
+static DEVICE_ATTR_RW(dbc_serial);
+static DEVICE_ATTR_RW(dbc_product);
+static DEVICE_ATTR_RW(dbc_manufacturer);
static DEVICE_ATTR_RW(dbc_bInterfaceProtocol);
static DEVICE_ATTR_RW(dbc_poll_interval_ms);
@@ -1291,6 +1410,9 @@ static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc_idVendor.attr,
&dev_attr_dbc_idProduct.attr,
&dev_attr_dbc_bcdDevice.attr,
+ &dev_attr_dbc_serial.attr,
+ &dev_attr_dbc_product.attr,
+ &dev_attr_dbc_manufacturer.attr,
&dev_attr_dbc_bInterfaceProtocol.attr,
&dev_attr_dbc_poll_interval_ms.attr,
NULL
@@ -1316,6 +1438,9 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
dbc->bInterfaceProtocol = DBC_PROTOCOL;
dbc->poll_interval = DBC_POLL_INTERVAL_DEFAULT;
+ /* initialize serial, product and manufacturer with default values */
+ dbc->str = dbc_str_default;
+
if (readl(&dbc->regs->control) & DBC_CTRL_DBC_ENABLE)
goto err;
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index 5426c971d2d3..20ae4e7617f2 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -47,10 +47,6 @@ struct dbc_info_context {
#define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8)
#define DBC_MAX_PACKET 1024
-#define DBC_MAX_STRING_LENGTH 64
-#define DBC_STRING_MANUFACTURER "Linux Foundation"
-#define DBC_STRING_PRODUCT "Linux USB Debug Target"
-#define DBC_STRING_SERIAL "0001"
#define DBC_CONTEXT_SIZE 64
/*
@@ -63,11 +59,31 @@ struct dbc_info_context {
#define DBC_PORTSC_LINK_CHANGE BIT(22)
#define DBC_PORTSC_CONFIG_CHANGE BIT(23)
+/*
+ * The maximum length of a string descriptor is 255, because the bLength
+ * field in the usb_string_descriptor struct is __u8. In practice the
+ * maximum length is 254, because a string descriptor consists of a 2 byte
+ * header followed by UTF-16 characters (2 bytes each). This allows for
+ * only 126 characters (code points) in the string, which is where
+ * USB_MAX_STRING_LEN comes from.
+ */
+#define USB_MAX_STRING_DESC_LEN 254
+
struct dbc_str_descs {
- char string0[DBC_MAX_STRING_LENGTH];
- char manufacturer[DBC_MAX_STRING_LENGTH];
- char product[DBC_MAX_STRING_LENGTH];
- char serial[DBC_MAX_STRING_LENGTH];
+ char string0[USB_MAX_STRING_DESC_LEN];
+ char manufacturer[USB_MAX_STRING_DESC_LEN];
+ char product[USB_MAX_STRING_DESC_LEN];
+ char serial[USB_MAX_STRING_DESC_LEN];
+};
+
+/*
+ * NULL terminated UTF-8 strings used to create UTF-16 strings
+ * (with maxiumum USB_MAX_STRING_LEN 2 byte characters).
+ */
+struct dbc_str {
+ char manufacturer[USB_MAX_STRING_LEN+1];
+ char product[USB_MAX_STRING_LEN+1];
+ char serial[USB_MAX_STRING_LEN+1];
};
#define DBC_PROTOCOL 1 /* GNU Remote Debug Command */
@@ -133,9 +149,10 @@ struct xhci_dbc {
struct xhci_erst erst;
struct xhci_container_ctx *ctx;
- struct dbc_str_descs *string;
- dma_addr_t string_dma;
- size_t string_size;
+ struct dbc_str_descs *str_descs;
+ dma_addr_t str_descs_dma;
+ size_t str_descs_size;
+ struct dbc_str str;
u16 idVendor;
u16 idProduct;
u16 bcdDevice;
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 8b492871d21d..3f6aa2440b05 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -1570,7 +1570,6 @@ static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xu
data = irq_get_irq_data(tegra->wake_irqs[i]);
if (!data) {
dev_warn(tegra->dev, "get wake event %d irq data fail\n", i);
- irq_dispose_mapping(tegra->wake_irqs[i]);
break;
}
@@ -1583,16 +1582,6 @@ static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xu
return 0;
}
-static void tegra_xusb_dispose_wake(struct tegra_xusb *tegra)
-{
- unsigned int i;
-
- for (i = 0; i < tegra->num_wakes; i++)
- irq_dispose_mapping(tegra->wake_irqs[i]);
-
- tegra->num_wakes = 0;
-}
-
static int tegra_xusb_probe(struct platform_device *pdev)
{
struct tegra_xusb *tegra;
@@ -1648,10 +1637,8 @@ static int tegra_xusb_probe(struct platform_device *pdev)
return err;
tegra->padctl = tegra_xusb_padctl_get(&pdev->dev);
- if (IS_ERR(tegra->padctl)) {
- err = PTR_ERR(tegra->padctl);
- goto dispose_wake;
- }
+ if (IS_ERR(tegra->padctl))
+ return PTR_ERR(tegra->padctl);
np = of_parse_phandle(pdev->dev.of_node, "nvidia,xusb-padctl", 0);
if (!np) {
@@ -1975,8 +1962,6 @@ put_powerdomains:
put_padctl:
of_node_put(np);
tegra_xusb_padctl_put(tegra->padctl);
-dispose_wake:
- tegra_xusb_dispose_wake(tegra);
return err;
}
@@ -2009,8 +1994,6 @@ static void tegra_xusb_remove(struct platform_device *pdev)
if (tegra->padctl_irq)
pm_runtime_disable(&pdev->dev);
- tegra_xusb_dispose_wake(tegra);
-
pm_runtime_put(&pdev->dev);
tegra_xusb_disable(tegra);
diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h
index c1462be5526d..1a1e86e60e04 100644
--- a/drivers/usb/misc/onboard_usb_dev.h
+++ b/drivers/usb/misc/onboard_usb_dev.h
@@ -115,6 +115,13 @@ static const struct onboard_dev_pdata vialab_vl817_data = {
.is_hub = true,
};
+static const struct onboard_dev_pdata wch_ch334_data = {
+ .reset_us = 14000,
+ .num_supplies = 2,
+ .supply_names = { "vdd33", "v5" },
+ .is_hub = true,
+};
+
static const struct onboard_dev_pdata xmos_xvf3500_data = {
.reset_us = 1,
.num_supplies = 2,
@@ -146,6 +153,7 @@ static const struct of_device_id onboard_dev_match[] = {
{ .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
{ .compatible = "usbbda,5414", .data = &realtek_rts5411_data, },
+ { .compatible = "usb1a86,8091", .data = &wch_ch334_data, },
{ .compatible = "usb1da0,5511", .data = &parade_ps5511_data, },
{ .compatible = "usb1da0,55a1", .data = &parade_ps5511_data, },
{ .compatible = "usb2109,817", .data = &vialab_vl817_data, },
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 8423be59ec0f..de26b302334d 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
-#include <linux/of.h>
+#include <linux/property.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
@@ -49,15 +49,13 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
int ret = 0;
if (suspend) {
- if (!IS_ERR(nop->clk))
- clk_disable_unprepare(nop->clk);
+ clk_disable_unprepare(nop->clk);
if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev))
ret = regulator_disable(nop->vcc);
} else {
if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev))
ret = regulator_enable(nop->vcc);
- if (!IS_ERR(nop->clk))
- clk_prepare_enable(nop->clk);
+ clk_prepare_enable(nop->clk);
}
return ret;
@@ -137,11 +135,9 @@ int usb_gen_phy_init(struct usb_phy *phy)
dev_err(phy->dev, "Failed to enable power\n");
}
- if (!IS_ERR(nop->clk)) {
- ret = clk_prepare_enable(nop->clk);
- if (ret)
- return ret;
- }
+ ret = clk_prepare_enable(nop->clk);
+ if (ret)
+ return ret;
nop_reset(nop);
@@ -155,8 +151,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
gpiod_set_value_cansleep(nop->gpiod_reset, 1);
- if (!IS_ERR(nop->clk))
- clk_disable_unprepare(nop->clk);
+ clk_disable_unprepare(nop->clk);
if (!IS_ERR(nop->vcc)) {
if (regulator_disable(nop->vcc))
@@ -202,18 +197,9 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
{
enum usb_phy_type type = USB_PHY_TYPE_USB2;
int err = 0;
-
u32 clk_rate = 0;
- bool needs_clk = false;
-
- if (dev->of_node) {
- struct device_node *node = dev->of_node;
-
- if (of_property_read_u32(node, "clock-frequency", &clk_rate))
- clk_rate = 0;
- needs_clk = of_property_present(node, "clocks");
- }
+ device_property_read_u32(dev, "clock-frequency", &clk_rate);
nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
GPIOD_ASIS);
err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
@@ -235,20 +221,16 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
if (!nop->phy.otg)
return -ENOMEM;
- nop->clk = devm_clk_get(dev, "main_clk");
- if (IS_ERR(nop->clk)) {
- dev_dbg(dev, "Can't get phy clock: %ld\n",
- PTR_ERR(nop->clk));
- if (needs_clk)
- return PTR_ERR(nop->clk);
- }
+ nop->clk = devm_clk_get_optional(dev, "main_clk");
+ if (IS_ERR(nop->clk))
+ return dev_err_probe(dev, PTR_ERR(nop->clk),
+ "Can't get phy clock\n");
- if (!IS_ERR(nop->clk) && clk_rate) {
+ if (clk_rate) {
err = clk_set_rate(nop->clk, clk_rate);
- if (err) {
- dev_err(dev, "Error setting clock rate\n");
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err,
+ "Error setting clock rate\n");
}
nop->vcc = devm_regulator_get_optional(dev, "vcc");
@@ -282,7 +264,6 @@ EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
static int usb_phy_generic_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *dn = dev->of_node;
struct usb_phy_generic *nop;
int err;
@@ -293,17 +274,17 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
err = usb_phy_gen_create_phy(dev, nop);
if (err)
return err;
+
if (nop->gpiod_vbus) {
- err = devm_request_threaded_irq(&pdev->dev,
+ err = devm_request_threaded_irq(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;
- }
+ if (err)
+ return dev_err_probe(dev, err, "can't request irq %i\n",
+ gpiod_to_irq(nop->gpiod_vbus));
+
nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE;
}
@@ -312,16 +293,13 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
nop->phy.shutdown = usb_gen_phy_shutdown;
err = usb_add_phy_dev(&nop->phy);
- if (err) {
- dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
- err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "can't register transceiver\n");
platform_set_drvdata(pdev, nop);
- device_set_wakeup_capable(&pdev->dev,
- of_property_read_bool(dn, "wakeup-source"));
+ device_set_wakeup_capable(dev,
+ device_property_read_bool(dev, "wakeup-source"));
return 0;
}
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index fb9031628d39..00443a7beaeb 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -29,17 +29,26 @@
#include <linux/usb/tegra_usb_phy.h>
#include <linux/usb/ulpi.h>
+#define USB_TXFILLTUNING 0x154
+#define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16)
+#define USB_FIFO_TXFILL_MASK 0x1f0000
+
#define ULPI_VIEWPORT 0x170
/* PORTSC PTS/PHCD bits, Tegra20 only */
#define TEGRA_USB_PORTSC1 0x184
-#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
-#define TEGRA_USB_PORTSC1_PHCD BIT(23)
+#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
+#define TEGRA_USB_PORTSC1_PHCD BIT(23)
+#define TEGRA_USB_PORTSC1_WKOC BIT(22)
+#define TEGRA_USB_PORTSC1_WKDS BIT(21)
+#define TEGRA_USB_PORTSC1_WKCN BIT(20)
/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
+#define TEGRA30_USB_PORTSC1 0x174
#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4
-#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
-#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22)
+#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
+#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22)
+#define TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC 4
/* Bits of PORTSC1, which will get cleared by writing 1 into them */
#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
@@ -51,11 +60,12 @@
#define USB_SUSP_CLR BIT(5)
#define USB_PHY_CLK_VALID BIT(7)
#define UTMIP_RESET BIT(11)
-#define UHSIC_RESET BIT(11)
#define UTMIP_PHY_ENABLE BIT(12)
#define ULPI_PHY_ENABLE BIT(13)
#define USB_SUSP_SET BIT(14)
+#define UHSIC_RESET BIT(14)
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
+#define UHSIC_PHY_ENABLE BIT(19)
#define USB_PHY_VBUS_SENSORS 0x404
#define B_SESS_VLD_WAKEUP_EN BIT(14)
@@ -156,6 +166,58 @@
#define UTMIP_BIAS_CFG1 0x83c
#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
+/*
+ * Tegra20 has no UTMIP registers on PHY2 and UHSIC registers start from 0x800
+ * just where UTMIP registers should have been. This is the case only with Tegra20
+ * Tegra30+ have UTMIP registers at 0x800 and UHSIC registers are shifted by 0x400
+ * to 0xc00, but register layout is preserved.
+ */
+#define UHSIC_PLL_CFG1 0x804
+#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
+#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14)
+
+#define UHSIC_HSRX_CFG0 0x808
+#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2)
+#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8)
+#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13)
+
+#define UHSIC_HSRX_CFG1 0x80c
+#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
+
+#define UHSIC_TX_CFG0 0x810
+#define UHSIC_HS_READY_WAIT_FOR_VALID BIT(9)
+
+#define UHSIC_MISC_CFG0 0x814
+#define UHSIC_SUSPEND_EXIT_ON_EDGE BIT(7)
+#define UHSIC_DETECT_SHORT_CONNECT BIT(8)
+#define UHSIC_FORCE_XCVR_MODE BIT(15)
+#define UHSIC_DISABLE_BUSRESET BIT(20)
+
+#define UHSIC_MISC_CFG1 0x818
+#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2)
+
+#define UHSIC_PADS_CFG0 0x81c
+#define UHSIC_TX_RTUNEN 0xf000
+#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12)
+
+#define UHSIC_PADS_CFG1 0x820
+#define UHSIC_PD_BG BIT(2)
+#define UHSIC_PD_TX BIT(3)
+#define UHSIC_PD_TRK BIT(4)
+#define UHSIC_PD_RX BIT(5)
+#define UHSIC_PD_ZI BIT(6)
+#define UHSIC_RX_SEL BIT(7)
+#define UHSIC_RPD_DATA BIT(9)
+#define UHSIC_RPD_STROBE BIT(10)
+#define UHSIC_RPU_DATA BIT(11)
+#define UHSIC_RPU_STROBE BIT(12)
+
+#define UHSIC_CMD_CFG0 0x824
+#define UHSIC_PRETEND_CONNECT_DETECT BIT(5)
+
+#define UHSIC_STAT_CFG0 0x828
+#define UHSIC_CONNECT_DETECT BIT(0)
+
/* For Tegra30 and above only, the address is different in Tegra20 */
#define USB_USBMODE 0x1f8
#define USB_USBMODE_MASK (3 << 0)
@@ -174,7 +236,8 @@ struct tegra_xtal_freq {
u8 enable_delay;
u8 stable_count;
u8 active_delay;
- u8 xtal_freq_count;
+ u8 utmi_xtal_freq_count;
+ u16 hsic_xtal_freq_count;
u16 debounce;
};
@@ -184,7 +247,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x02,
.stable_count = 0x2F,
.active_delay = 0x04,
- .xtal_freq_count = 0x76,
+ .utmi_xtal_freq_count = 0x76,
+ .hsic_xtal_freq_count = 0x1CA,
.debounce = 0x7530,
},
{
@@ -192,7 +256,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x02,
.stable_count = 0x33,
.active_delay = 0x05,
- .xtal_freq_count = 0x7F,
+ .utmi_xtal_freq_count = 0x7F,
+ .hsic_xtal_freq_count = 0x1F0,
.debounce = 0x7EF4,
},
{
@@ -200,7 +265,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x03,
.stable_count = 0x4B,
.active_delay = 0x06,
- .xtal_freq_count = 0xBB,
+ .utmi_xtal_freq_count = 0xBB,
+ .hsic_xtal_freq_count = 0x2DD,
.debounce = 0xBB80,
},
{
@@ -208,7 +274,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = {
.enable_delay = 0x04,
.stable_count = 0x66,
.active_delay = 0x09,
- .xtal_freq_count = 0xFE,
+ .utmi_xtal_freq_count = 0xFE,
+ .hsic_xtal_freq_count = 0x3E0,
.debounce = 0xFDE8,
},
};
@@ -532,7 +599,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
val = readl_relaxed(base + UTMIP_PLL_CFG1);
val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
- val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
+ val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->utmi_xtal_freq_count) |
UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
writel_relaxed(val, base + UTMIP_PLL_CFG1);
}
@@ -803,17 +870,170 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
return 0;
}
+static u32 tegra_hsic_readl(struct tegra_usb_phy *phy, u32 reg)
+{
+ void __iomem *base = phy->regs;
+ u32 shift = phy->soc_config->uhsic_registers_offset;
+
+ return readl_relaxed(base + shift + reg);
+}
+
+static void tegra_hsic_writel(struct tegra_usb_phy *phy, u32 reg, u32 value)
+{
+ void __iomem *base = phy->regs;
+ u32 shift = phy->soc_config->uhsic_registers_offset;
+
+ writel_relaxed(value, base + shift + reg);
+}
+
+static int uhsic_phy_power_on(struct tegra_usb_phy *phy)
+{
+ struct tegra_utmip_config *config = phy->config;
+ void __iomem *base = phy->regs;
+ u32 val;
+ int err = 0;
+
+ val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1);
+ val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
+ UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
+ val |= UHSIC_RX_SEL;
+ tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val);
+
+ udelay(2);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ udelay(30);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val |= UHSIC_PHY_ENABLE;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG0);
+ val &= ~(UHSIC_IDLE_WAIT(~0) |
+ UHSIC_ELASTIC_UNDERRUN_LIMIT(~0) |
+ UHSIC_ELASTIC_OVERRUN_LIMIT(~0));
+ val |= UHSIC_IDLE_WAIT(config->idle_wait_delay) |
+ UHSIC_ELASTIC_UNDERRUN_LIMIT(config->elastic_limit) |
+ UHSIC_ELASTIC_OVERRUN_LIMIT(config->elastic_limit);
+ tegra_hsic_writel(phy, UHSIC_HSRX_CFG0, val);
+
+ val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG1);
+ val &= ~UHSIC_HS_SYNC_START_DLY(~0);
+ val |= UHSIC_HS_SYNC_START_DLY(config->hssync_start_delay);
+ tegra_hsic_writel(phy, UHSIC_HSRX_CFG1, val);
+
+ val = tegra_hsic_readl(phy, UHSIC_MISC_CFG0);
+ val |= UHSIC_SUSPEND_EXIT_ON_EDGE;
+ tegra_hsic_writel(phy, UHSIC_MISC_CFG0, val);
+
+ val = tegra_hsic_readl(phy, UHSIC_MISC_CFG1);
+ val &= ~UHSIC_PLLU_STABLE_COUNT(~0);
+ val |= UHSIC_PLLU_STABLE_COUNT(phy->freq->stable_count);
+ tegra_hsic_writel(phy, UHSIC_MISC_CFG1, val);
+
+ val = tegra_hsic_readl(phy, UHSIC_PLL_CFG1);
+ val &= ~(UHSIC_XTAL_FREQ_COUNT(~0) |
+ UHSIC_PLLU_ENABLE_DLY_COUNT(~0));
+ val |= UHSIC_XTAL_FREQ_COUNT(phy->freq->hsic_xtal_freq_count) |
+ UHSIC_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
+ tegra_hsic_writel(phy, UHSIC_PLL_CFG1, val);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val &= ~UHSIC_RESET;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ udelay(2);
+
+ if (phy->soc_config->requires_usbmode_setup) {
+ val = readl_relaxed(base + USB_USBMODE);
+ val &= ~USB_USBMODE_MASK;
+ if (phy->mode == USB_DR_MODE_HOST)
+ val |= USB_USBMODE_HOST;
+ else
+ val |= USB_USBMODE_DEVICE;
+ writel_relaxed(val, base + USB_USBMODE);
+ }
+
+ set_pts(phy, phy->soc_config->uhsic_pts_value);
+
+ val = readl_relaxed(base + USB_TXFILLTUNING);
+ if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) {
+ val = USB_FIFO_TXFILL_THRES(0x10);
+ writel_relaxed(val, base + USB_TXFILLTUNING);
+ }
+
+ val = readl_relaxed(base + phy->soc_config->portsc1_offset);
+ val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS |
+ TEGRA_USB_PORTSC1_WKCN);
+ writel_relaxed(val, base + phy->soc_config->portsc1_offset);
+
+ val = tegra_hsic_readl(phy, UHSIC_PADS_CFG0);
+ val &= ~UHSIC_TX_RTUNEN;
+ val |= UHSIC_TX_RTUNE(phy->soc_config->uhsic_tx_rtune);
+ tegra_hsic_writel(phy, UHSIC_PADS_CFG0, val);
+
+ err = utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID);
+
+ if (err)
+ dev_err(phy->u_phy.dev,
+ "Timeout waiting for PHY to stabilize on enable (HSIC)\n");
+
+ return err;
+}
+
+static int uhsic_phy_power_off(struct tegra_usb_phy *phy)
+{
+ void __iomem *base = phy->regs;
+ u32 val;
+
+ set_phcd(phy, true);
+
+ val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1);
+ val |= (UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX |
+ UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE);
+ tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ udelay(30);
+
+ val = readl_relaxed(base + USB_SUSP_CTRL);
+ val &= ~UHSIC_PHY_ENABLE;
+ writel_relaxed(val, base + USB_SUSP_CTRL);
+
+ return 0;
+}
+
static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
{
- int err;
+ int err = 0;
if (phy->powered_on)
return 0;
- if (phy->is_ulpi_phy)
- err = ulpi_phy_power_on(phy);
- else
+ switch (phy->phy_type) {
+ case USBPHY_INTERFACE_MODE_UTMI:
err = utmi_phy_power_on(phy);
+ break;
+
+ case USBPHY_INTERFACE_MODE_ULPI:
+ err = ulpi_phy_power_on(phy);
+ break;
+
+ case USBPHY_INTERFACE_MODE_HSIC:
+ err = uhsic_phy_power_on(phy);
+ break;
+
+ default:
+ break;
+ }
+
if (err)
return err;
@@ -827,15 +1047,28 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
{
- int err;
+ int err = 0;
if (!phy->powered_on)
return 0;
- if (phy->is_ulpi_phy)
- err = ulpi_phy_power_off(phy);
- else
+ switch (phy->phy_type) {
+ case USBPHY_INTERFACE_MODE_UTMI:
err = utmi_phy_power_off(phy);
+ break;
+
+ case USBPHY_INTERFACE_MODE_ULPI:
+ err = ulpi_phy_power_off(phy);
+ break;
+
+ case USBPHY_INTERFACE_MODE_HSIC:
+ err = uhsic_phy_power_off(phy);
+ break;
+
+ default:
+ break;
+ }
+
if (err)
return err;
@@ -854,7 +1087,7 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy)
usb_phy_set_wakeup(u_phy, false);
tegra_usb_phy_power_off(phy);
- if (!phy->is_ulpi_phy)
+ if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI)
utmip_pad_close(phy);
regulator_disable(phy->vbus);
@@ -1040,7 +1273,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
goto disable_clk;
}
- if (!phy->is_ulpi_phy) {
+ if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) {
err = utmip_pad_open(phy);
if (err)
goto disable_vbus;
@@ -1057,7 +1290,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy)
return 0;
close_phy:
- if (!phy->is_ulpi_phy)
+ if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI)
utmip_pad_close(phy);
disable_vbus:
@@ -1095,8 +1328,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
struct resource *res;
int err;
- tegra_phy->is_ulpi_phy = false;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev, "Failed to get UTMI pad regs\n");
@@ -1231,6 +1462,10 @@ static const struct tegra_phy_soc_config tegra20_soc_config = {
.requires_usbmode_setup = false,
.requires_extra_tuning_parameters = false,
.requires_pmc_ao_power_up = false,
+ .uhsic_registers_offset = 0,
+ .uhsic_tx_rtune = 0, /* 40 ohm */
+ .uhsic_pts_value = 0, /* UTMI */
+ .portsc1_offset = TEGRA_USB_PORTSC1,
};
static const struct tegra_phy_soc_config tegra30_soc_config = {
@@ -1239,6 +1474,10 @@ static const struct tegra_phy_soc_config tegra30_soc_config = {
.requires_usbmode_setup = true,
.requires_extra_tuning_parameters = true,
.requires_pmc_ao_power_up = true,
+ .uhsic_registers_offset = 0x400,
+ .uhsic_tx_rtune = 8, /* 50 ohm */
+ .uhsic_pts_value = TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC,
+ .portsc1_offset = TEGRA30_USB_PORTSC1,
};
static const struct of_device_id tegra_usb_phy_id_table[] = {
@@ -1252,7 +1491,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct tegra_usb_phy *tegra_phy;
- enum usb_phy_interface phy_type;
struct reset_control *reset;
struct gpio_desc *gpiod;
struct resource *res;
@@ -1314,9 +1552,10 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
return err;
}
- phy_type = of_usb_get_phy_mode(np);
- switch (phy_type) {
+ tegra_phy->phy_type = of_usb_get_phy_mode(np);
+ switch (tegra_phy->phy_type) {
case USBPHY_INTERFACE_MODE_UTMI:
+ case USBPHY_INTERFACE_MODE_HSIC:
err = utmi_phy_probe(tegra_phy, pdev);
if (err)
return err;
@@ -1341,8 +1580,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
break;
case USBPHY_INTERFACE_MODE_ULPI:
- tegra_phy->is_ulpi_phy = true;
-
tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link");
err = PTR_ERR_OR_ZERO(tegra_phy->clk);
if (err) {
@@ -1382,7 +1619,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
- phy_type);
+ tegra_phy->phy_type);
return -EINVAL;
}
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 9f2cc5fb9f45..d4505a426446 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1401,12 +1401,16 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a1, 0xff), /* Telit FN20C04 (RNDIS) */
+ .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a2, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a3, 0xff), /* Telit FN920C04 (ECM) */
.driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a6, 0xff), /* Telit FN920C04 (RNDIS) */
+ .driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a7, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a8, 0xff), /* Telit FN920C04 (ECM) */
@@ -1415,6 +1419,8 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10ab, 0xff), /* Telit FN920C04 (RNDIS) */
+ .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x30), /* Telit FE990B (rmnet) */
.driver_info = NCTRL(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x40) },
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 7a368fea61bc..8a6a1c663eb6 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC) += typec.o
-typec-y := class.o mux.o bus.o pd.o retimer.o
+typec-y := class.o mux.o bus.o pd.o retimer.o mode_selection.o
typec-$(CONFIG_ACPI) += port-mapper.o
obj-$(CONFIG_TYPEC) += altmodes/
obj-$(CONFIG_TYPEC_TCPM) += tcpm/
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index d96ab106a980..d185688a16b1 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -804,8 +804,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
if (plug)
typec_altmode_set_drvdata(plug, dp);
- dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
- schedule_work(&dp->work);
+ if (!alt->mode_selection) {
+ dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
+ schedule_work(&dp->work);
+ }
return 0;
}
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
index 6eadf7835f8f..c4c5da6154da 100644
--- a/drivers/usb/typec/altmodes/thunderbolt.c
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -307,7 +307,7 @@ static int tbt_altmode_probe(struct typec_altmode *alt)
typec_altmode_set_drvdata(alt, tbt);
typec_altmode_set_ops(alt, &tbt_altmode_ops);
- if (tbt_ready(alt)) {
+ if (!alt->mode_selection && tbt_ready(alt)) {
if (tbt->plug[TYPEC_PLUG_SOP_P])
tbt->state = TBT_STATE_SOP_P_ENTER;
else if (tbt->plug[TYPEC_PLUG_SOP_PP])
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index a884cec9ab7e..e84b134a3381 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -445,7 +445,23 @@ static struct attribute *typec_attrs[] = {
&dev_attr_description.attr,
NULL
};
-ATTRIBUTE_GROUPS(typec);
+
+static umode_t typec_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ if (is_typec_partner_altmode(kobj_to_dev(kobj)))
+ return attr->mode;
+ return 0;
+}
+
+static const struct attribute_group typec_group = {
+ .is_visible = typec_is_visible,
+ .attrs = typec_attrs,
+};
+
+static const struct attribute_group *typec_groups[] = {
+ &typec_group,
+ NULL
+};
static int typec_match(struct device *dev, const struct device_driver *driver)
{
@@ -453,6 +469,9 @@ static int typec_match(struct device *dev, const struct device_driver *driver)
struct typec_altmode *altmode = to_typec_altmode(dev);
const struct typec_device_id *id;
+ if (!is_typec_partner_altmode(dev))
+ return 0;
+
for (id = drv->id_table; id->svid; id++)
if (id->svid == altmode->svid)
return 1;
@@ -463,6 +482,9 @@ static int typec_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
const struct typec_altmode *altmode = to_typec_altmode(dev);
+ if (!is_typec_partner_altmode(dev))
+ return 0;
+
if (add_uevent_var(env, "SVID=%04X", altmode->svid))
return -ENOMEM;
@@ -547,3 +569,4 @@ const struct bus_type typec_bus = {
.probe = typec_probe,
.remove = typec_remove,
};
+EXPORT_SYMBOL_GPL(typec_bus);
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 643b8c81786d..7df5deb1dd3a 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -5,7 +5,6 @@
#include <linux/usb/typec_altmode.h>
-struct bus_type;
struct typec_mux;
struct typec_retimer;
@@ -28,9 +27,4 @@ struct altmode {
#define to_altmode(d) container_of(d, struct altmode, adev)
-extern const struct bus_type typec_bus;
-extern const struct device_type typec_altmode_dev_type;
-
-#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type)
-
#endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 9b2647cb199b..dbba53f02497 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -235,7 +235,7 @@ static int altmode_match(struct device *dev, const void *data)
struct typec_altmode *adev = to_typec_altmode(dev);
const struct typec_device_id *id = data;
- if (!is_typec_altmode(dev))
+ if (!is_typec_port_altmode(dev))
return 0;
return (adev->svid == id->svid);
@@ -445,11 +445,88 @@ svid_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR_RO(svid);
+static int increment_duplicated_priority(struct device *dev, void *data)
+{
+ if (is_typec_port_altmode(dev)) {
+ struct typec_altmode **alt_target = (struct typec_altmode **)data;
+ struct typec_altmode *alt = to_typec_altmode(dev);
+
+ if (alt != *alt_target && alt->priority == (*alt_target)->priority) {
+ alt->priority++;
+ *alt_target = alt;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int find_duplicated_priority(struct device *dev, void *data)
+{
+ if (is_typec_port_altmode(dev)) {
+ struct typec_altmode **alt_target = (struct typec_altmode **)data;
+ struct typec_altmode *alt = to_typec_altmode(dev);
+
+ if (alt != *alt_target && alt->priority == (*alt_target)->priority)
+ return 1;
+ }
+ return 0;
+}
+
+static int typec_mode_set_priority(struct typec_altmode *alt, const u8 priority)
+{
+ struct typec_port *port = to_typec_port(alt->dev.parent);
+ const u8 old_priority = alt->priority;
+ int res = 1;
+
+ alt->priority = priority;
+ while (res) {
+ res = device_for_each_child(&port->dev, &alt, find_duplicated_priority);
+ if (res) {
+ alt->priority++;
+ if (alt->priority == 0) {
+ alt->priority = old_priority;
+ return -EOVERFLOW;
+ }
+ }
+ }
+
+ res = 1;
+ alt->priority = priority;
+ while (res)
+ res = device_for_each_child(&port->dev, &alt,
+ increment_duplicated_priority);
+
+ return 0;
+}
+
+static ssize_t priority_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ u8 val;
+ int err = kstrtou8(buf, 10, &val);
+
+ if (!err)
+ err = typec_mode_set_priority(to_typec_altmode(dev), val);
+
+ if (!err)
+ return size;
+ return err;
+}
+
+static ssize_t priority_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", to_typec_altmode(dev)->priority);
+}
+static DEVICE_ATTR_RW(priority);
+
static struct attribute *typec_altmode_attrs[] = {
&dev_attr_active.attr,
&dev_attr_mode.attr,
&dev_attr_svid.attr,
&dev_attr_vdo.attr,
+ &dev_attr_priority.attr,
NULL
};
@@ -457,11 +534,17 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
+ struct typec_port *port = typec_altmode2port(adev);
- if (attr == &dev_attr_active.attr)
- if (!is_typec_port(adev->dev.parent) &&
- (!adev->ops || !adev->ops->activate))
- return 0444;
+ if (attr == &dev_attr_active.attr) {
+ if (!is_typec_port(adev->dev.parent)) {
+ if (!port->mode_control || !adev->ops || !adev->ops->activate)
+ return 0444;
+ }
+ } else if (attr == &dev_attr_priority.attr) {
+ if (!is_typec_port(adev->dev.parent) || !port->mode_control)
+ return 0;
+ }
return attr->mode;
}
@@ -532,15 +615,31 @@ static void typec_altmode_release(struct device *dev)
kfree(alt);
}
-const struct device_type typec_altmode_dev_type = {
- .name = "typec_alternate_mode",
+const struct device_type typec_port_altmode_dev_type = {
+ .name = "typec_port_alternate_mode",
.groups = typec_altmode_groups,
.release = typec_altmode_release,
};
+EXPORT_SYMBOL_GPL(typec_port_altmode_dev_type);
+
+const struct device_type typec_plug_altmode_dev_type = {
+ .name = "typec_plug_alternate_mode",
+ .groups = typec_altmode_groups,
+ .release = typec_altmode_release,
+};
+EXPORT_SYMBOL_GPL(typec_plug_altmode_dev_type);
+
+const struct device_type typec_partner_altmode_dev_type = {
+ .name = "typec_partner_alternate_mode",
+ .groups = typec_altmode_groups,
+ .release = typec_altmode_release,
+};
+EXPORT_SYMBOL_GPL(typec_partner_altmode_dev_type);
static struct typec_altmode *
typec_register_altmode(struct device *parent,
- const struct typec_altmode_desc *desc)
+ const struct typec_altmode_desc *desc,
+ const struct device_type *type)
{
unsigned int id = altmode_id_get(parent);
bool is_port = is_typec_port(parent);
@@ -556,6 +655,7 @@ typec_register_altmode(struct device *parent,
alt->adev.svid = desc->svid;
alt->adev.mode = desc->mode;
alt->adev.vdo = desc->vdo;
+ alt->adev.mode_selection = desc->mode_selection;
alt->roles = desc->roles;
alt->id = id;
@@ -575,7 +675,7 @@ typec_register_altmode(struct device *parent,
alt->adev.dev.parent = parent;
alt->adev.dev.groups = alt->groups;
- alt->adev.dev.type = &typec_altmode_dev_type;
+ alt->adev.dev.type = type;
dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id);
get_device(alt->adev.dev.parent);
@@ -584,9 +684,7 @@ typec_register_altmode(struct device *parent,
if (!is_port)
typec_altmode_set_partner(alt);
- /* The partners are bind to drivers */
- if (is_typec_partner(parent))
- alt->adev.dev.bus = &typec_bus;
+ alt->adev.dev.bus = &typec_bus;
/* Plug alt modes need a class to generate udev events. */
if (is_typec_plug(parent))
@@ -963,7 +1061,7 @@ struct typec_altmode *
typec_partner_register_altmode(struct typec_partner *partner,
const struct typec_altmode_desc *desc)
{
- return typec_register_altmode(&partner->dev, desc);
+ return typec_register_altmode(&partner->dev, desc, &typec_partner_altmode_dev_type);
}
EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
@@ -1193,7 +1291,7 @@ struct typec_altmode *
typec_plug_register_altmode(struct typec_plug *plug,
const struct typec_altmode_desc *desc)
{
- return typec_register_altmode(&plug->dev, desc);
+ return typec_register_altmode(&plug->dev, desc, &typec_plug_altmode_dev_type);
}
EXPORT_SYMBOL_GPL(typec_plug_register_altmode);
@@ -2482,6 +2580,7 @@ typec_port_register_altmode(struct typec_port *port,
struct typec_altmode *adev;
struct typec_mux *mux;
struct typec_retimer *retimer;
+ int ret;
mux = typec_mux_get(&port->dev);
if (IS_ERR(mux))
@@ -2493,13 +2592,19 @@ typec_port_register_altmode(struct typec_port *port,
return ERR_CAST(retimer);
}
- adev = typec_register_altmode(&port->dev, desc);
+ adev = typec_register_altmode(&port->dev, desc, &typec_port_altmode_dev_type);
if (IS_ERR(adev)) {
typec_retimer_put(retimer);
typec_mux_put(mux);
} else {
to_altmode(adev)->mux = mux;
to_altmode(adev)->retimer = retimer;
+
+ ret = typec_mode_set_priority(adev, 0);
+ if (ret) {
+ typec_unregister_altmode(adev);
+ return ERR_PTR(ret);
+ }
}
return adev;
@@ -2694,6 +2799,7 @@ struct typec_port *typec_register_port(struct device *parent,
}
port->pd = cap->pd;
+ port->mode_control = !cap->no_mode_control;
ret = device_add(&port->dev);
if (ret) {
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index db2fe96c48ff..d3435936ee7c 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -9,6 +9,7 @@
struct typec_mux;
struct typec_switch;
struct usb_device;
+struct mode_selection;
struct typec_plug {
struct device dev;
@@ -39,6 +40,7 @@ struct typec_partner {
u8 usb_capability;
struct usb_power_delivery *pd;
+ struct mode_selection *sel;
void (*attach)(struct typec_partner *partner, struct device *dev);
void (*deattach)(struct typec_partner *partner, struct device *dev);
@@ -62,6 +64,7 @@ struct typec_port {
struct mutex partner_link_lock;
enum typec_orientation orientation;
+ bool mode_control;
struct typec_switch *sw;
struct typec_mux *mux;
struct typec_retimer *retimer;
diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
index 3876f4faead6..3e39b800e6b5 100644
--- a/drivers/usb/typec/hd3ss3220.c
+++ b/drivers/usb/typec/hd3ss3220.c
@@ -204,6 +204,23 @@ static const struct typec_operations hd3ss3220_ops = {
.port_type_set = hd3ss3220_port_type_set,
};
+static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on)
+{
+ int ret;
+
+ if (regulator_is_enabled(hd3ss3220->vbus) == on)
+ return;
+
+ if (on)
+ ret = regulator_enable(hd3ss3220->vbus);
+ else
+ ret = regulator_disable(hd3ss3220->vbus);
+
+ if (ret)
+ dev_err(hd3ss3220->dev,
+ "vbus regulator %s failed: %d\n", on ? "disable" : "enable", ret);
+}
+
static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
{
enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
@@ -221,6 +238,9 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
break;
}
+ if (hd3ss3220->vbus && !hd3ss3220->id_gpiod)
+ hd3ss3220_regulator_control(hd3ss3220, role_state == USB_ROLE_HOST);
+
hd3ss3220->role_state = role_state;
}
@@ -330,18 +350,10 @@ static const struct regmap_config config = {
static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id)
{
struct hd3ss3220 *hd3ss3220 = dev_id;
- int ret;
int id;
id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod);
- if (!id)
- ret = regulator_enable(hd3ss3220->vbus);
- else
- ret = regulator_disable(hd3ss3220->vbus);
-
- if (ret)
- dev_err(hd3ss3220->dev,
- "vbus regulator %s failed: %d\n", id ? "disable" : "enable", ret);
+ hd3ss3220_regulator_control(hd3ss3220, !id);
return IRQ_HANDLED;
}
diff --git a/drivers/usb/typec/mode_selection.c b/drivers/usb/typec/mode_selection.c
new file mode 100644
index 000000000000..a95b31e21b52
--- /dev/null
+++ b/drivers/usb/typec/mode_selection.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2025 Google LLC.
+ */
+
+#include <linux/types.h>
+#include <linux/list_sort.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/usb/typec_altmode.h>
+
+#include "class.h"
+
+/**
+ * struct mode_state - State tracking for a specific Type-C alternate mode
+ * @svid: Standard or Vendor ID of the Alternate Mode
+ * @priority: Mode priority
+ * @error: Outcome of the last attempt to enter the mode
+ * @list: List head to link this mode state into a prioritized list
+ */
+struct mode_state {
+ u16 svid;
+ u8 priority;
+ int error;
+ struct list_head list;
+};
+
+/**
+ * struct mode_selection - Manages the selection and state of Alternate Modes
+ * @mode_list: Prioritized list of available Alternate Modes
+ * @lock: Mutex to protect mode_list
+ * @work: Work structure
+ * @partner: Handle to the Type-C partner device
+ * @active_svid: svid of currently active mode
+ * @timeout: Timeout for a mode entry attempt, ms
+ * @delay: Delay between mode entry/exit attempts, ms
+ */
+struct mode_selection {
+ struct list_head mode_list;
+ /* Protects the mode_list*/
+ struct mutex lock;
+ struct delayed_work work;
+ struct typec_partner *partner;
+ u16 active_svid;
+ unsigned int timeout;
+ unsigned int delay;
+};
+
+/**
+ * struct mode_order - Mode activation tracking
+ * @svid: Standard or Vendor ID of the Alternate Mode
+ * @enter: Flag indicating if the driver is currently attempting to enter or
+ * exit the mode
+ * @result: Outcome of the attempt to activate the mode
+ */
+struct mode_order {
+ u16 svid;
+ int enter;
+ int result;
+};
+
+static int activate_altmode(struct device *dev, void *data)
+{
+ if (is_typec_partner_altmode(dev)) {
+ struct typec_altmode *alt = to_typec_altmode(dev);
+ struct mode_order *order = (struct mode_order *)data;
+
+ if (order->svid == alt->svid) {
+ if (alt->ops && alt->ops->activate)
+ order->result = alt->ops->activate(alt, order->enter);
+ else
+ order->result = -EOPNOTSUPP;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mode_selection_activate(struct mode_selection *sel,
+ const u16 svid, const int enter)
+
+ __must_hold(&sel->lock)
+{
+ struct mode_order order = {.svid = svid, .enter = enter, .result = -ENODEV};
+
+ /*
+ * The port driver may acquire its internal mutex during alternate mode
+ * activation. Since this is the same mutex that may be held during the
+ * execution of typec_altmode_state_update(), it is crucial to release
+ * sel->mutex before activation to avoid potential deadlock.
+ * Note that sel->mode_list must remain invariant throughout this unlocked
+ * interval.
+ */
+ mutex_unlock(&sel->lock);
+ device_for_each_child(&sel->partner->dev, &order, activate_altmode);
+ mutex_lock(&sel->lock);
+
+ return order.result;
+}
+
+static void mode_list_clean(struct mode_selection *sel)
+{
+ struct mode_state *ms, *tmp;
+
+ list_for_each_entry_safe(ms, tmp, &sel->mode_list, list) {
+ list_del(&ms->list);
+ kfree(ms);
+ }
+}
+
+/**
+ * mode_selection_work_fn() - Alternate mode activation task
+ * @work: work structure
+ *
+ * - If the Alternate Mode currently prioritized at the top of the list is already
+ * active, the entire selection process is considered finished.
+ * - If a different Alternate Mode is currently active, the system must exit that
+ * active mode first before attempting any new entry.
+ *
+ * The function then checks the result of the attempt to entre the current mode,
+ * stored in the `ms->error` field:
+ * - if the attempt FAILED, the mode is deactivated and removed from the list.
+ * - `ms->error` value of 0 signifies that the mode has not yet been activated.
+ *
+ * Once successfully activated, the task is scheduled for subsequent entry after
+ * a timeout period. The alternate mode driver is expected to call back with the
+ * actual mode entry result via `typec_altmode_state_update()`.
+ */
+static void mode_selection_work_fn(struct work_struct *work)
+{
+ struct mode_selection *sel = container_of(work,
+ struct mode_selection, work.work);
+ struct mode_state *ms;
+ unsigned int delay = sel->delay;
+ int result;
+
+ guard(mutex)(&sel->lock);
+
+ ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list);
+ if (!ms)
+ return;
+
+ if (sel->active_svid == ms->svid) {
+ dev_dbg(&sel->partner->dev, "%x altmode is active\n", ms->svid);
+ mode_list_clean(sel);
+ } else if (sel->active_svid != 0) {
+ result = mode_selection_activate(sel, sel->active_svid, 0);
+ if (result)
+ mode_list_clean(sel);
+ else
+ sel->active_svid = 0;
+ } else if (ms->error) {
+ dev_err(&sel->partner->dev, "%x: entry error %pe\n",
+ ms->svid, ERR_PTR(ms->error));
+ mode_selection_activate(sel, ms->svid, 0);
+ list_del(&ms->list);
+ kfree(ms);
+ } else {
+ result = mode_selection_activate(sel, ms->svid, 1);
+ if (result) {
+ dev_err(&sel->partner->dev, "%x: activation error %pe\n",
+ ms->svid, ERR_PTR(result));
+ list_del(&ms->list);
+ kfree(ms);
+ } else {
+ delay = sel->timeout;
+ ms->error = -ETIMEDOUT;
+ }
+ }
+
+ if (!list_empty(&sel->mode_list))
+ schedule_delayed_work(&sel->work, msecs_to_jiffies(delay));
+}
+
+void typec_altmode_state_update(struct typec_partner *partner, const u16 svid,
+ const int error)
+{
+ struct mode_selection *sel = partner->sel;
+ struct mode_state *ms;
+
+ if (sel) {
+ mutex_lock(&sel->lock);
+ ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list);
+ if (ms && ms->svid == svid) {
+ ms->error = error;
+ if (cancel_delayed_work(&sel->work))
+ schedule_delayed_work(&sel->work, 0);
+ }
+ if (!error)
+ sel->active_svid = svid;
+ else
+ sel->active_svid = 0;
+ mutex_unlock(&sel->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(typec_altmode_state_update);
+
+static int compare_priorities(void *priv,
+ const struct list_head *a, const struct list_head *b)
+{
+ const struct mode_state *msa = container_of(a, struct mode_state, list);
+ const struct mode_state *msb = container_of(b, struct mode_state, list);
+
+ if (msa->priority < msb->priority)
+ return -1;
+ return 1;
+}
+
+static int altmode_add_to_list(struct device *dev, void *data)
+{
+ if (is_typec_partner_altmode(dev)) {
+ struct list_head *list = (struct list_head *)data;
+ struct typec_altmode *altmode = to_typec_altmode(dev);
+ const struct typec_altmode *pdev = typec_altmode_get_partner(altmode);
+ struct mode_state *ms;
+
+ if (pdev && altmode->ops && altmode->ops->activate) {
+ ms = kzalloc(sizeof(*ms), GFP_KERNEL);
+ if (!ms)
+ return -ENOMEM;
+ ms->svid = pdev->svid;
+ ms->priority = pdev->priority;
+ INIT_LIST_HEAD(&ms->list);
+ list_add_tail(&ms->list, list);
+ }
+ }
+ return 0;
+}
+
+int typec_mode_selection_start(struct typec_partner *partner,
+ const unsigned int delay, const unsigned int timeout)
+{
+ struct mode_selection *sel;
+ int ret;
+
+ if (partner->usb_mode == USB_MODE_USB4)
+ return -EBUSY;
+
+ if (partner->sel)
+ return -EALREADY;
+
+ sel = kzalloc(sizeof(*sel), GFP_KERNEL);
+ if (!sel)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&sel->mode_list);
+
+ ret = device_for_each_child(&partner->dev, &sel->mode_list,
+ altmode_add_to_list);
+
+ if (ret || list_empty(&sel->mode_list)) {
+ mode_list_clean(sel);
+ kfree(sel);
+ return ret;
+ }
+
+ list_sort(NULL, &sel->mode_list, compare_priorities);
+ sel->partner = partner;
+ sel->delay = delay;
+ sel->timeout = timeout;
+ mutex_init(&sel->lock);
+ INIT_DELAYED_WORK(&sel->work, mode_selection_work_fn);
+ schedule_delayed_work(&sel->work, msecs_to_jiffies(delay));
+ partner->sel = sel;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_mode_selection_start);
+
+void typec_mode_selection_delete(struct typec_partner *partner)
+{
+ struct mode_selection *sel = partner->sel;
+
+ if (sel) {
+ partner->sel = NULL;
+ cancel_delayed_work_sync(&sel->work);
+ mode_list_clean(sel);
+ mutex_destroy(&sel->lock);
+ kfree(sel);
+ }
+}
+EXPORT_SYMBOL_GPL(typec_mode_selection_delete);
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index be49a976428f..b7828160b81d 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1808,7 +1808,7 @@ static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt,
/*
* PD3.0 Spec 6.4.4.3.2: The SVIDs are returned 2 per VDO (see Table
* 6-43), and can be returned maximum 6 VDOs per response (see Figure
- * 6-19). If the Respondersupports 12 or more SVID then the Discover
+ * 6-19). If the Responder supports 12 or more SVID then the Discover
* SVIDs Command Shall be executed multiple times until a Discover
* SVIDs VDO is returned ending either with a SVID value of 0x0000 in
* the last part of the last VDO or with a VDO containing two SVIDs
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index b812be4d0e67..87dd992a4b9e 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -73,7 +73,6 @@ config CROS_EC_UCSI
tristate "UCSI Driver for ChromeOS EC"
depends on MFD_CROS_EC_DEV
depends on CROS_USBPD_NOTIFY
- depends on !EXTCON_TCSS_CROS_EC
default MFD_CROS_EC_DEV
help
This driver enables UCSI support for a ChromeOS EC. The EC is
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index dbc571763eff..c7e38bf01350 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -17,6 +17,10 @@ ifneq ($(CONFIG_TYPEC_DP_ALTMODE),)
typec_ucsi-y += displayport.o
endif
+ifneq ($(CONFIG_TYPEC_TBT_ALTMODE),)
+ typec_ucsi-y += thunderbolt.o
+endif
+
obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o
diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
index eed2a7d0ebc6..6bca2dce211c 100644
--- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c
+++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/wait.h>
+#include <linux/usb/typec_altmode.h>
#include "ucsi.h"
@@ -33,6 +34,11 @@
/* Number of times to attempt recovery from a write timeout before giving up. */
#define WRITE_TMO_CTR_MAX 5
+/* Delay between mode entry/exit attempts, ms */
+static const unsigned int mode_selection_delay = 1000;
+/* Timeout for a mode entry attempt, ms */
+static const unsigned int mode_selection_timeout = 4000;
+
struct cros_ucsi_data {
struct device *dev;
struct ucsi *ucsi;
@@ -134,6 +140,20 @@ static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
return ret;
}
+static void cros_ucsi_add_partner_altmodes(struct ucsi_connector *con)
+{
+ if (!con->typec_cap.no_mode_control)
+ typec_mode_selection_start(con->partner,
+ mode_selection_delay,
+ mode_selection_timeout);
+}
+
+static void cros_ucsi_remove_partner_altmodes(struct ucsi_connector *con)
+{
+ if (!con->typec_cap.no_mode_control)
+ typec_mode_selection_delete(con->partner);
+}
+
static const struct ucsi_operations cros_ucsi_ops = {
.read_version = cros_ucsi_read_version,
.read_cci = cros_ucsi_read_cci,
@@ -141,6 +161,8 @@ static const struct ucsi_operations cros_ucsi_ops = {
.read_message_in = cros_ucsi_read_message_in,
.async_control = cros_ucsi_async_control,
.sync_control = cros_ucsi_sync_control,
+ .add_partner_altmodes = cros_ucsi_add_partner_altmodes,
+ .remove_partner_altmodes = cros_ucsi_remove_partner_altmodes,
};
static void cros_ucsi_work(struct work_struct *work)
diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c
index 3abe9370ffaa..c8ebab8ba7e7 100644
--- a/drivers/usb/typec/ucsi/psy.c
+++ b/drivers/usb/typec/ucsi/psy.c
@@ -112,15 +112,20 @@ static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
union power_supply_propval *val)
{
u32 pdo;
+ int max_voltage = 0;
switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
case UCSI_CONSTAT_PWR_OPMODE_PD:
- if (con->num_pdos > 0) {
- pdo = con->src_pdos[con->num_pdos - 1];
- val->intval = pdo_fixed_voltage(pdo) * 1000;
- } else {
- val->intval = 0;
+ for (int i = 0; i < con->num_pdos; i++) {
+ int pdo_voltage = 0;
+
+ pdo = con->src_pdos[i];
+ if (pdo_type(pdo) == PDO_TYPE_FIXED)
+ pdo_voltage = pdo_fixed_voltage(pdo) * 1000;
+ max_voltage = (pdo_voltage > max_voltage) ? pdo_voltage
+ : max_voltage;
}
+ val->intval = max_voltage;
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
@@ -168,6 +173,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
union power_supply_propval *val)
{
u32 pdo;
+ int max_current = 0;
if (!UCSI_CONSTAT(con, CONNECTED)) {
val->intval = 0;
@@ -176,12 +182,16 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
case UCSI_CONSTAT_PWR_OPMODE_PD:
- if (con->num_pdos > 0) {
- pdo = con->src_pdos[con->num_pdos - 1];
- val->intval = pdo_max_current(pdo) * 1000;
- } else {
- val->intval = 0;
+ for (int i = 0; i < con->num_pdos; i++) {
+ int pdo_current = 0;
+
+ pdo = con->src_pdos[i];
+ if (pdo_type(pdo) == PDO_TYPE_FIXED)
+ pdo_current = pdo_max_current(pdo) * 1000;
+ max_current = (pdo_current > max_current) ? pdo_current
+ : max_current;
}
+ val->intval = max_current;
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
@@ -202,10 +212,28 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con,
static int ucsi_psy_get_current_now(struct ucsi_connector *con,
union power_supply_propval *val)
{
- if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD)
- val->intval = rdo_op_current(con->rdo) * 1000;
- else
+ if (!UCSI_CONSTAT(con, CONNECTED)) {
val->intval = 0;
+ return 0;
+ }
+
+ switch (UCSI_CONSTAT(con, PWR_OPMODE)) {
+ case UCSI_CONSTAT_PWR_OPMODE_PD:
+ val->intval = rdo_op_current(con->rdo) * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+ val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+ val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
+ break;
+ case UCSI_CONSTAT_PWR_OPMODE_BC:
+ case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+ /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
+ default:
+ val->intval = UCSI_TYPEC_DEFAULT_CURRENT * 1000;
+ break;
+ }
return 0;
}
diff --git a/drivers/usb/typec/ucsi/thunderbolt.c b/drivers/usb/typec/ucsi/thunderbolt.c
new file mode 100644
index 000000000000..434d3d8ea5b6
--- /dev/null
+++ b/drivers/usb/typec/ucsi/thunderbolt.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UCSI Thunderbolt Alternate Mode Support
+ *
+ * Copyright 2026 Google LLC
+ */
+
+#include <linux/usb/typec_tbt.h>
+#include <linux/usb/pd_vdo.h>
+#include <linux/err.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/gfp_types.h>
+#include <linux/types.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/workqueue.h>
+
+#include "ucsi.h"
+
+/**
+ * struct ucsi_tbt - Thunderbolt Alternate Mode private data structure
+ * @con: Pointer to UCSI connector structure
+ * @alt: Pointer to typec altmode structure
+ * @work: Work structure
+ * @cam: An offset into the list of alternate modes supported by the PPM
+ * @header: VDO header
+ */
+struct ucsi_tbt {
+ struct ucsi_connector *con;
+ struct typec_altmode *alt;
+ struct work_struct work;
+ int cam;
+ u32 header;
+};
+
+static void ucsi_thunderbolt_work(struct work_struct *work)
+{
+ struct ucsi_tbt *tbt = container_of(work, struct ucsi_tbt, work);
+
+ if (typec_altmode_vdm(tbt->alt, tbt->header, NULL, 0))
+ dev_err(&tbt->alt->dev, "VDM 0x%x failed\n", tbt->header);
+
+ tbt->header = 0;
+}
+
+static int ucsi_thunderbolt_set_altmode(struct ucsi_tbt *tbt,
+ bool enter, u32 vdo)
+{
+ int svdm_version;
+ int cmd;
+ int ret;
+ u64 command = UCSI_SET_NEW_CAM |
+ UCSI_CONNECTOR_NUMBER(tbt->con->num) |
+ UCSI_SET_NEW_CAM_SET_AM(tbt->cam) |
+ ((u64)vdo << 32);
+
+ if (enter)
+ command |= (1 << 23);
+
+ ret = ucsi_send_command(tbt->con->ucsi, command, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ svdm_version = typec_altmode_get_svdm_version(tbt->alt);
+ if (svdm_version < 0)
+ return svdm_version;
+
+ if (enter)
+ cmd = CMD_ENTER_MODE;
+ else
+ cmd = CMD_EXIT_MODE;
+ tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
+ tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
+ tbt->header |= VDO_CMDT(CMDT_RSP_ACK);
+
+ schedule_work(&tbt->work);
+
+ return 0;
+}
+
+static int ucsi_thunderbolt_enter(struct typec_altmode *alt, u32 *vdo)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ struct ucsi_connector *con = tbt->con;
+ u64 command;
+ u8 cur = 0;
+ int ret;
+
+ if (!ucsi_con_mutex_lock(con))
+ return -ENOTCONN;
+
+ command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num);
+ ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur));
+ if (ret < 0) {
+ if (con->ucsi->version > 0x0100)
+ goto err_unlock;
+ cur = 0xff;
+ }
+
+ if (cur != 0xff) {
+ if (cur >= UCSI_MAX_ALTMODES || con->port_altmode[cur] != alt)
+ ret = -EBUSY;
+ else
+ ret = 0;
+ goto err_unlock;
+ }
+
+ ret = ucsi_thunderbolt_set_altmode(tbt, true, *vdo);
+ ucsi_altmode_update_active(tbt->con);
+
+err_unlock:
+ ucsi_con_mutex_unlock(con);
+
+ return ret;
+}
+
+static int ucsi_thunderbolt_exit(struct typec_altmode *alt)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ int ret;
+
+ if (!ucsi_con_mutex_lock(tbt->con))
+ return -ENOTCONN;
+
+ ret = ucsi_thunderbolt_set_altmode(tbt, false, 0);
+
+ ucsi_con_mutex_unlock(tbt->con);
+
+ return ret;
+}
+
+static int ucsi_thunderbolt_vdm(struct typec_altmode *alt,
+ u32 header, const u32 *data, int count)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ int cmd_type = PD_VDO_CMDT(header);
+ int cmd = PD_VDO_CMD(header);
+ int svdm_version;
+
+ if (!ucsi_con_mutex_lock(tbt->con))
+ return -ENOTCONN;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0) {
+ ucsi_con_mutex_unlock(tbt->con);
+ return svdm_version;
+ }
+
+ switch (cmd_type) {
+ case CMDT_INIT:
+ if (PD_VDO_SVDM_VER(header) < svdm_version) {
+ svdm_version = PD_VDO_SVDM_VER(header);
+ typec_partner_set_svdm_version(tbt->con->partner, svdm_version);
+ }
+ tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
+ tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
+ tbt->header |= VDO_CMDT(CMDT_RSP_ACK);
+
+ schedule_work(&tbt->work);
+ break;
+ default:
+ break;
+ }
+
+ ucsi_con_mutex_unlock(tbt->con);
+
+ return 0;
+}
+
+static const struct typec_altmode_ops ucsi_thunderbolt_ops = {
+ .enter = ucsi_thunderbolt_enter,
+ .exit = ucsi_thunderbolt_exit,
+ .vdm = ucsi_thunderbolt_vdm,
+};
+
+struct typec_altmode *ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc)
+{
+ struct typec_altmode *alt;
+ struct ucsi_tbt *tbt;
+
+ alt = typec_port_register_altmode(con->port, desc);
+ if (IS_ERR(alt) || !override)
+ return alt;
+
+ tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL);
+ if (!tbt) {
+ typec_unregister_altmode(alt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tbt->cam = offset;
+ tbt->con = con;
+ tbt->alt = alt;
+ INIT_WORK(&tbt->work, ucsi_thunderbolt_work);
+ typec_altmode_set_drvdata(alt, tbt);
+ typec_altmode_set_ops(alt, &ucsi_thunderbolt_ops);
+
+ return alt;
+}
+
+void ucsi_thunderbolt_remove_partner(struct typec_altmode *alt)
+{
+ struct ucsi_tbt *tbt;
+
+ if (alt) {
+ tbt = typec_altmode_get_drvdata(alt);
+ if (tbt)
+ cancel_work_sync(&tbt->work);
+ }
+}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index a7b388dc7fa0..91b6c71dd739 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_tbt.h>
#include "ucsi.h"
#include "trace.h"
@@ -314,6 +315,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
{
const struct typec_altmode *altmode = NULL;
u64 command;
+ u16 svid = 0;
int ret;
u8 cur;
int i;
@@ -335,6 +337,10 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
for (i = 0; con->partner_altmode[i]; i++)
typec_altmode_update_active(con->partner_altmode[i],
con->partner_altmode[i] == altmode);
+
+ if (altmode)
+ svid = altmode->svid;
+ typec_altmode_state_update(con->partner, svid, 0);
}
static int ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid)
@@ -412,6 +418,9 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
alt = ucsi_register_displayport(con, override,
i, desc);
break;
+ case USB_TYPEC_TBT_SID:
+ alt = ucsi_register_thunderbolt(con, override, i, desc);
+ break;
default:
alt = typec_port_register_altmode(con->port, desc);
break;
@@ -609,6 +618,8 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
desc.vdo = alt[j].mid;
desc.svid = alt[j].svid;
desc.roles = TYPEC_PORT_DRD;
+ desc.mode_selection = con->ucsi->ops->add_partner_altmodes &&
+ !con->typec_cap.no_mode_control;
ret = ucsi_register_altmode(con, &desc, recipient);
if (ret)
@@ -640,12 +651,15 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
}
while (adev[i]) {
- if (recipient == UCSI_RECIPIENT_SOP &&
- (adev[i]->svid == USB_TYPEC_DP_SID ||
- (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
- adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) {
+ if (recipient == UCSI_RECIPIENT_SOP) {
pdev = typec_altmode_get_partner(adev[i]);
- ucsi_displayport_remove_partner((void *)pdev);
+
+ if (adev[i]->svid == USB_TYPEC_DP_SID ||
+ (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
+ adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))
+ ucsi_displayport_remove_partner((void *)pdev);
+ else if (adev[i]->svid == USB_TYPEC_TBT_SID)
+ ucsi_thunderbolt_remove_partner((void *)pdev);
}
typec_unregister_altmode(adev[i]);
adev[i++] = NULL;
@@ -831,6 +845,8 @@ static int ucsi_check_altmodes(struct ucsi_connector *con)
if (con->partner_altmode[0]) {
num_partner_am = ucsi_get_num_altmode(con->partner_altmode);
typec_partner_set_num_altmodes(con->partner, num_partner_am);
+ if (con->ucsi->ops->add_partner_altmodes)
+ con->ucsi->ops->add_partner_altmodes(con);
ucsi_altmode_update_active(con);
return 0;
} else {
@@ -1119,6 +1135,8 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
return;
typec_set_mode(con->port, TYPEC_STATE_SAFE);
+ if (con->ucsi->ops->remove_partner_altmodes)
+ con->ucsi->ops->remove_partner_altmodes(con);
typec_partner_set_usb_power_delivery(con->partner, NULL);
ucsi_unregister_partner_pdos(con);
@@ -1307,6 +1325,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) {
ucsi_partner_change(con);
+ ucsi_altmode_update_active(con);
/* Complete pending data role swap */
if (!completion_done(&con->complete))
@@ -1659,6 +1678,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
cap->driver_data = con;
cap->ops = &ucsi_ops;
+ cap->no_mode_control = !(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE);
if (ucsi->version >= UCSI_VERSION_2_0)
con->typec_cap.orientation_aware = true;
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 410389ef173a..43a0d01ade8f 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -70,6 +70,8 @@ struct dentry;
* @update_altmodes: Squashes duplicate DP altmodes
* @update_connector: Update connector capabilities before registering
* @connector_status: Updates connector status, called holding connector lock
+ * @add_partner_altmodes: Start mode selection
+ * @remove_partner_altmodes: Clean mode selection
*
* Read and write routines for UCSI interface. @sync_write must wait for the
* Command Completion Event from the PPM before returning, and @async_write must
@@ -88,6 +90,8 @@ struct ucsi_operations {
struct ucsi_altmode *updated);
void (*update_connector)(struct ucsi_connector *con);
void (*connector_status)(struct ucsi_connector *con);
+ void (*add_partner_altmodes)(struct ucsi_connector *con);
+ void (*remove_partner_altmodes)(struct ucsi_connector *con);
};
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops);
@@ -596,6 +600,26 @@ static inline void
ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
#endif /* CONFIG_TYPEC_DP_ALTMODE */
+#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE)
+struct typec_altmode *
+ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc);
+
+void ucsi_thunderbolt_remove_partner(struct typec_altmode *adev);
+#else
+static inline struct typec_altmode *
+ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc)
+{
+ return typec_port_register_altmode(con->port, desc);
+}
+
+static inline void
+ucsi_thunderbolt_remove_partner(struct typec_altmode *adev) { }
+#endif /* CONFIG_TYPEC_TBT_ALTMODE */
+
#ifdef CONFIG_DEBUG_FS
void ucsi_debugfs_init(void);
void ucsi_debugfs_exit(void);
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index 55919c3762ba..cd92d9fac327 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -55,8 +55,8 @@ void stub_complete(struct urb *urb)
"stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n");
return;
case -ECONNRESET:
- dev_info(&urb->dev->dev,
- "unlinked by a call to usb_unlink_urb()\n");
+ dev_dbg(&urb->dev->dev,
+ "unlinked by a call to usb_unlink_urb()\n");
break;
case -EPIPE:
dev_info(&urb->dev->dev, "endpoint %d is stalled\n",