Skip to content

Commit e4e6a9d

Browse files
committed
Merge branch 'refs/heads/bits/040-dwc3' into asahi-wip
2 parents b73df29 + df8d199 commit e4e6a9d

4 files changed

Lines changed: 131 additions & 5 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
%YAML 1.2
3+
---
4+
$id: http://devicetree.org/schemas/usb/apple,dwc3.yaml#
5+
$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+
title: Apple Silicon DWC3 USB controller
8+
9+
maintainers:
10+
- Sven Peter <[email protected]>
11+
12+
description:
13+
On Apple Silicon SoCs such as the M1 each Type-C port has a corresponding
14+
USB controller based on the Synopsys DesignWare USB3 controller.
15+
16+
The common content of this binding is defined in snps,dwc3.yaml.
17+
18+
allOf:
19+
- $ref: snps,dwc3.yaml#
20+
21+
select:
22+
properties:
23+
compatible:
24+
contains:
25+
const: apple,dwc3
26+
required:
27+
- compatible
28+
29+
properties:
30+
compatible:
31+
items:
32+
- enum:
33+
- apple,t8103-dwc3
34+
- apple,t6000-dwc3
35+
- const: apple,dwc3
36+
- const: snps,dwc3
37+
38+
reg:
39+
maxItems: 1
40+
41+
interrupts:
42+
maxItems: 1
43+
44+
unevaluatedProperties: false
45+
46+
required:
47+
- compatible
48+
- reg
49+
- interrupts
50+
51+
examples:
52+
- |
53+
#include <dt-bindings/interrupt-controller/apple-aic.h>
54+
#include <dt-bindings/interrupt-controller/irq.h>
55+
56+
usb@82280000 {
57+
compatible = "apple,t8103-dwc3", "apple,dwc3", "snps,dwc3";
58+
reg = <0x82280000 0x10000>;
59+
interrupts = <AIC_IRQ 777 IRQ_TYPE_LEVEL_HIGH>;
60+
61+
dr_mode = "otg";
62+
usb-role-switch;
63+
role-switch-default-mode = "host";
64+
};

drivers/usb/dwc3/core.c

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
116116
dwc->current_dr_role = mode;
117117
}
118118

119+
static void dwc3_core_exit(struct dwc3 *dwc);
120+
static int dwc3_core_init_for_resume(struct dwc3 *dwc);
121+
119122
static void __dwc3_set_mode(struct work_struct *work)
120123
{
121124
struct dwc3 *dwc = work_to_dwc(work);
@@ -134,7 +137,7 @@ static void __dwc3_set_mode(struct work_struct *work)
134137
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
135138
dwc3_otg_update(dwc, 0);
136139

137-
if (!desired_dr_role)
140+
if (!desired_dr_role && !dwc->role_switch_reset_quirk)
138141
goto out;
139142

140143
if (desired_dr_role == dwc->current_dr_role)
@@ -162,13 +165,32 @@ static void __dwc3_set_mode(struct work_struct *work)
162165
break;
163166
}
164167

168+
if (dwc->role_switch_reset_quirk) {
169+
if (dwc->current_dr_role) {
170+
dwc->current_dr_role = 0;
171+
dwc3_core_exit(dwc);
172+
}
173+
174+
if (desired_dr_role) {
175+
ret = dwc3_core_init_for_resume(dwc);
176+
if (ret) {
177+
dev_err(dwc->dev,
178+
"failed to reinitialize core\n");
179+
goto out;
180+
}
181+
} else {
182+
goto out;
183+
}
184+
}
185+
165186
/*
166187
* When current_dr_role is not set, there's no role switching.
167188
* Only perform GCTL.CoreSoftReset when there's DRD role switching.
168189
*/
169-
if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
190+
if (dwc->role_switch_reset_quirk ||
191+
(dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
170192
DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
171-
desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
193+
desired_dr_role != DWC3_GCTL_PRTCAP_OTG))) {
172194
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
173195
reg |= DWC3_GCTL_CORESOFTRESET;
174196
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -1381,6 +1403,18 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
13811403
ret = dwc3_drd_init(dwc);
13821404
if (ret)
13831405
return dev_err_probe(dev, ret, "failed to initialize dual-role\n");
1406+
1407+
/*
1408+
* If the role switch reset quirk is required the first role
1409+
* switch notification will initialize the core such that we
1410+
* have to shut it down here. Make sure that the __dwc3_set_mode
1411+
* queued by dwc3_drd_init has completed before since it
1412+
* may still try to access MMIO.
1413+
*/
1414+
if (dwc->role_switch_reset_quirk) {
1415+
flush_work(&dwc->drd_work);
1416+
dwc3_core_exit(dwc);
1417+
}
13841418
break;
13851419
default:
13861420
dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
@@ -1851,6 +1885,22 @@ static int dwc3_probe(struct platform_device *pdev)
18511885
if (ret)
18521886
goto err_put_psy;
18531887

1888+
if (dev->of_node) {
1889+
if (of_device_is_compatible(dev->of_node, "apple,dwc3")) {
1890+
if (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
1891+
!IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
1892+
dev_err(dev,
1893+
"Apple DWC3 requires role switch support.\n"
1894+
);
1895+
ret = -EINVAL;
1896+
goto err_put_psy;
1897+
}
1898+
1899+
dwc->dr_mode = USB_DR_MODE_OTG;
1900+
dwc->role_switch_reset_quirk = true;
1901+
}
1902+
}
1903+
18541904
ret = reset_control_deassert(dwc->reset);
18551905
if (ret)
18561906
goto err_put_psy;
@@ -1974,7 +2024,6 @@ static void dwc3_remove(struct platform_device *pdev)
19742024
power_supply_put(dwc->usb_psy);
19752025
}
19762026

1977-
#ifdef CONFIG_PM
19782027
static int dwc3_core_init_for_resume(struct dwc3 *dwc)
19792028
{
19802029
int ret;
@@ -2001,6 +2050,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc)
20012050
return ret;
20022051
}
20032052

2053+
#ifdef CONFIG_PM
20042054
static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
20052055
{
20062056
unsigned long flags;

drivers/usb/dwc3/core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,7 @@ struct dwc3_scratchpad_array {
11161116
* @dis_split_quirk: set to disable split boundary.
11171117
* @wakeup_configured: set if the device is configured for remote wakeup.
11181118
* @suspended: set to track suspend event due to U3/L2.
1119+
* @role_switch_reset_quirk: set to force reinitialization after any role switch
11191120
* @imod_interval: set the interrupt moderation interval in 250ns
11201121
* increments or 0 to disable.
11211122
* @max_cfg_eps: current max number of IN eps used across all USB configs.
@@ -1334,6 +1335,8 @@ struct dwc3 {
13341335
unsigned wakeup_configured:1;
13351336
unsigned suspended:1;
13361337

1338+
unsigned role_switch_reset_quirk:1;
1339+
13371340
u16 imod_interval;
13381341

13391342
int max_cfg_eps;

drivers/usb/dwc3/drd.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,9 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
461461
break;
462462
}
463463

464+
if (dwc->role_switch_reset_quirk && role == USB_ROLE_NONE)
465+
mode = 0;
466+
464467
dwc3_set_mode(dwc, mode);
465468
return 0;
466469
}
@@ -489,6 +492,10 @@ static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
489492
role = USB_ROLE_DEVICE;
490493
break;
491494
}
495+
496+
if (dwc->role_switch_reset_quirk && !dwc->current_dr_role)
497+
role = USB_ROLE_NONE;
498+
492499
spin_unlock_irqrestore(&dwc->lock, flags);
493500
return role;
494501
}
@@ -499,7 +506,9 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc)
499506
u32 mode;
500507

501508
dwc->role_switch_default_mode = usb_get_role_switch_default_mode(dwc->dev);
502-
if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) {
509+
if (dwc->role_switch_reset_quirk) {
510+
mode = 0;
511+
} else if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) {
503512
mode = DWC3_GCTL_PRTCAP_HOST;
504513
} else {
505514
dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;

0 commit comments

Comments
 (0)