Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: dwc3: Add Apple Silicon DWC3 glue layer driver

The dwc3 controller present on Apple Silicon SoCs like the M1 requires
a specific order of operations synchronized between its PHY and its
Type-C controller. Specifically, the PHY first has to go through initial
bringup (which requires knowledge of the lane mode and orientation)
before dwc3 itself can be brought up and can then finalize the PHY
configuration.
Additionally, dwc3 has to be teared down and re-initialized whenever
the cable is changed due to hardware quirks that prevent a new device
from being recognized and due to the PHY being unable to switch lane
mode or orientation while dwc3 is up and running.

These controllers also have a Apple-specific MMIO region after the
common dwc3 region where some controls have to be updated. PHY bringup
and shutdown also requires SUSPHY to be enabled for the ports to work
correctly.

In the future, this driver will also gain support for USB3-via-USB4
tunneling which will require additional tweaks.

Add a glue driver that takes of all of these constraints.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Sven Peter <sven@kernel.org>
Link: https://patch.msgid.link/20251015-b4-aplpe-dwc3-v2-5-cbd65a2d511a@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Sven Peter and committed by
Greg Kroah-Hartman
0ec946d3 f854920e

+502
+1
MAINTAINERS
··· 2462 2462 F: drivers/soc/apple/* 2463 2463 F: drivers/spi/spi-apple.c 2464 2464 F: drivers/spmi/spmi-apple-controller.c 2465 + F: drivers/usb/dwc3/dwc3-apple.c 2465 2466 F: drivers/video/backlight/apple_dwi_bl.c 2466 2467 F: drivers/watchdog/apple_wdt.c 2467 2468 F: include/dt-bindings/interrupt-controller/apple-aic.h
+11
drivers/usb/dwc3/Kconfig
··· 200 200 the dwc3 child node in the device tree. 201 201 Say 'Y' or 'M' here if your platform integrates DWC3 in a similar way. 202 202 203 + config USB_DWC3_APPLE 204 + tristate "Apple Silicon DWC3 Platform Driver" 205 + depends on OF && ARCH_APPLE 206 + default USB_DWC3 207 + select USB_ROLE_SWITCH 208 + help 209 + Support Apple Silicon SoCs with DesignWare Core USB3 IP. 210 + The DesignWare Core USB3 IP has to be used in dual-role 211 + mode on these machines. 212 + Say 'Y' or 'M' if you have such device. 213 + 203 214 endif
+1
drivers/usb/dwc3/Makefile
··· 43 43 ## 44 44 45 45 obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o 46 + obj-$(CONFIG_USB_DWC3_APPLE) += dwc3-apple.o 46 47 obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o 47 48 obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o 48 49 obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
+489
drivers/usb/dwc3/dwc3-apple.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Apple Silicon DWC3 Glue driver 4 + * Copyright (C) The Asahi Linux Contributors 5 + * 6 + * Based on: 7 + * - dwc3-qcom.c Copyright (c) 2018, The Linux Foundation. All rights reserved. 8 + * - dwc3-of-simple.c Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com 9 + */ 10 + 11 + #include <linux/of.h> 12 + #include <linux/module.h> 13 + #include <linux/mutex.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/reset.h> 16 + 17 + #include "glue.h" 18 + 19 + /* 20 + * This platform requires a very specific sequence of operations to bring up dwc3 and its USB3 PHY: 21 + * 22 + * 1) The PHY itself has to be brought up; for this we need to know the mode (USB3, 23 + * USB3+DisplayPort, USB4, etc) and the lane orientation. This happens through typec_mux_set. 24 + * 2) DWC3 has to be brought up but we must not touch the gadget area or start xhci yet. 25 + * 3) The PHY bring-up has to be finalized and dwc3's PIPE interface has to be switched to the 26 + * USB3 PHY, this is done inside phy_set_mode. 27 + * 4) We can now initialize xhci or gadget mode. 28 + * 29 + * We can switch 1 and 2 but 3 has to happen after (1 and 2) and 4 has to happen after 3. 30 + * 31 + * And then to bring this all down again: 32 + * 33 + * 1) DWC3 has to exit host or gadget mode and must no longer touch those registers 34 + * 2) The PHY has to switch dwc3's PIPE interface back to the dummy backend 35 + * 3) The PHY itself can be shut down, this happens from typec_mux_set 36 + * 37 + * We also can't transition the PHY from one mode to another while dwc3 is up and running (this is 38 + * slightly wrong, some transitions are possible, others aren't but because we have no documentation 39 + * for this I'd rather play it safe). 40 + * 41 + * After both the PHY and dwc3 are initialized we will only ever see a single "new device connected" 42 + * event. If we just keep them running only the first device plugged in will ever work. XHCI's port 43 + * status register actually does show the correct state but no interrupt ever comes in. In gadget 44 + * mode we don't even get a USBDisconnected event and everything looks like there's still something 45 + * connected on the other end. 46 + * This can be partially explained because the USB2 D+/D- lines are connected through a stateful 47 + * eUSB2 repeater which in turn is controlled by a variant of the TI TPS6598x USB PD chip which 48 + * resets the repeater out-of-band everytime the CC lines are (dis)connected. This then requires a 49 + * PHY reset to make sure the PHY and the eUSB2 repeater state are synchronized again. 50 + * 51 + * And to make this all extra fun: If we get the order of some of this wrong either the port is just 52 + * broken until a phy+dwc3 reset, or it's broken until a full SoC reset (likely because we can't 53 + * reset some parts of the PHY), or some watchdog kicks in after a few seconds and forces a full SoC 54 + * reset (mostly seen this with USB4/Thunderbolt but there's clearly some watchdog that hates 55 + * invalid states). 56 + * 57 + * Hence there's really no good way to keep dwc3 fully up and running after we disconnect a cable 58 + * because then we can't shut down the PHY anymore. And if we kept the PHY running in whatever mode 59 + * it was until the next cable is connected we'd need to tear it all down and bring it back up again 60 + * anyway to detect and use the next device. 61 + * 62 + * Instead, we just shut down everything when a cable is disconnected and transition to 63 + * DWC3_APPLE_NO_CABLE. 64 + * During initial probe we don't have any information about the connected cable and can't bring up 65 + * the PHY properly and thus also can't fully bring up dwc3. Instead, we just keep everything off 66 + * and defer the first dwc3 probe until we get the first cable connected event. Until then we stay 67 + * in DWC3_APPLE_PROBE_PENDING. 68 + * Once a cable is connected we then keep track of the controller mode here by transitioning to 69 + * DWC3_APPLE_HOST or DWC3_APPLE_DEVICE. 70 + */ 71 + enum dwc3_apple_state { 72 + DWC3_APPLE_PROBE_PENDING, /* Before first cable connection, dwc3_core_probe not called */ 73 + DWC3_APPLE_NO_CABLE, /* No cable connected, dwc3 suspended after dwc3_core_exit */ 74 + DWC3_APPLE_HOST, /* Cable connected, dwc3 in host mode */ 75 + DWC3_APPLE_DEVICE, /* Cable connected, dwc3 in device mode */ 76 + }; 77 + 78 + /** 79 + * struct dwc3_apple - Apple-specific DWC3 USB controller 80 + * @dwc: Core DWC3 structure 81 + * @dev: Pointer to the device structure 82 + * @mmio_resource: Resource to be passed to dwc3_core_probe 83 + * @apple_regs: Apple-specific DWC3 registers 84 + * @resets: Reset control 85 + * @role_sw: USB role switch 86 + * @lock: Mutex for synchronizing access 87 + * @state: Current state of the controller, see documentation for the enum for details 88 + */ 89 + struct dwc3_apple { 90 + struct dwc3 dwc; 91 + 92 + struct device *dev; 93 + struct resource *mmio_resource; 94 + void __iomem *apple_regs; 95 + 96 + struct reset_control *resets; 97 + struct usb_role_switch *role_sw; 98 + 99 + struct mutex lock; 100 + 101 + enum dwc3_apple_state state; 102 + }; 103 + 104 + #define to_dwc3_apple(d) container_of((d), struct dwc3_apple, dwc) 105 + 106 + /* 107 + * Apple Silicon dwc3 vendor-specific registers 108 + * 109 + * These registers were identified by tracing XNU's memory access patterns and correlating them with 110 + * debug output over serial to determine their names. We don't exactly know what these do but 111 + * without these USB3 devices sometimes don't work. 112 + */ 113 + #define APPLE_DWC3_REGS_START 0xcd00 114 + #define APPLE_DWC3_REGS_END 0xcdff 115 + 116 + #define APPLE_DWC3_CIO_LFPS_OFFSET 0xcd38 117 + #define APPLE_DWC3_CIO_LFPS_OFFSET_VALUE 0xf800f80 118 + 119 + #define APPLE_DWC3_CIO_BW_NGT_OFFSET 0xcd3c 120 + #define APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE 0xfc00fc0 121 + 122 + #define APPLE_DWC3_CIO_LINK_TIMER 0xcd40 123 + #define APPLE_DWC3_CIO_PENDING_HP_TIMER GENMASK(23, 16) 124 + #define APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE 0x14 125 + #define APPLE_DWC3_CIO_PM_LC_TIMER GENMASK(15, 8) 126 + #define APPLE_DWC3_CIO_PM_LC_TIMER_VALUE 0xa 127 + #define APPLE_DWC3_CIO_PM_ENTRY_TIMER GENMASK(7, 0) 128 + #define APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE 0x10 129 + 130 + static inline void dwc3_apple_writel(struct dwc3_apple *appledwc, u32 offset, u32 value) 131 + { 132 + writel(value, appledwc->apple_regs + offset - APPLE_DWC3_REGS_START); 133 + } 134 + 135 + static inline u32 dwc3_apple_readl(struct dwc3_apple *appledwc, u32 offset) 136 + { 137 + return readl(appledwc->apple_regs + offset - APPLE_DWC3_REGS_START); 138 + } 139 + 140 + static inline void dwc3_apple_mask(struct dwc3_apple *appledwc, u32 offset, u32 mask, u32 value) 141 + { 142 + u32 reg; 143 + 144 + reg = dwc3_apple_readl(appledwc, offset); 145 + reg &= ~mask; 146 + reg |= value; 147 + dwc3_apple_writel(appledwc, offset, reg); 148 + } 149 + 150 + static void dwc3_apple_setup_cio(struct dwc3_apple *appledwc) 151 + { 152 + dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_LFPS_OFFSET, APPLE_DWC3_CIO_LFPS_OFFSET_VALUE); 153 + dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_BW_NGT_OFFSET, 154 + APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE); 155 + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PENDING_HP_TIMER, 156 + FIELD_PREP(APPLE_DWC3_CIO_PENDING_HP_TIMER, 157 + APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE)); 158 + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER, 159 + FIELD_PREP(APPLE_DWC3_CIO_PM_LC_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER_VALUE)); 160 + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_ENTRY_TIMER, 161 + FIELD_PREP(APPLE_DWC3_CIO_PM_ENTRY_TIMER, 162 + APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE)); 163 + } 164 + 165 + static void dwc3_apple_set_ptrcap(struct dwc3_apple *appledwc, u32 mode) 166 + { 167 + guard(spinlock_irqsave)(&appledwc->dwc.lock); 168 + dwc3_set_prtcap(&appledwc->dwc, mode, false); 169 + } 170 + 171 + static int dwc3_apple_core_probe(struct dwc3_apple *appledwc) 172 + { 173 + struct dwc3_probe_data probe_data = {}; 174 + int ret; 175 + 176 + lockdep_assert_held(&appledwc->lock); 177 + WARN_ON_ONCE(appledwc->state != DWC3_APPLE_PROBE_PENDING); 178 + 179 + appledwc->dwc.dev = appledwc->dev; 180 + probe_data.dwc = &appledwc->dwc; 181 + probe_data.res = appledwc->mmio_resource; 182 + probe_data.ignore_clocks_and_resets = true; 183 + probe_data.skip_core_init_mode = true; 184 + probe_data.properties = DWC3_DEFAULT_PROPERTIES; 185 + 186 + ret = dwc3_core_probe(&probe_data); 187 + if (ret) 188 + return ret; 189 + 190 + appledwc->state = DWC3_APPLE_NO_CABLE; 191 + return 0; 192 + } 193 + 194 + static int dwc3_apple_core_init(struct dwc3_apple *appledwc) 195 + { 196 + int ret; 197 + 198 + lockdep_assert_held(&appledwc->lock); 199 + 200 + switch (appledwc->state) { 201 + case DWC3_APPLE_PROBE_PENDING: 202 + ret = dwc3_apple_core_probe(appledwc); 203 + if (ret) 204 + dev_err(appledwc->dev, "Failed to probe DWC3 Core, err=%d\n", ret); 205 + break; 206 + case DWC3_APPLE_NO_CABLE: 207 + ret = dwc3_core_init(&appledwc->dwc); 208 + if (ret) 209 + dev_err(appledwc->dev, "Failed to initialize DWC3 Core, err=%d\n", ret); 210 + break; 211 + default: 212 + /* Unreachable unless there's a bug in this driver */ 213 + WARN_ON_ONCE(1); 214 + ret = -EINVAL; 215 + break; 216 + } 217 + 218 + return ret; 219 + } 220 + 221 + static void dwc3_apple_phy_set_mode(struct dwc3_apple *appledwc, enum phy_mode mode) 222 + { 223 + lockdep_assert_held(&appledwc->lock); 224 + 225 + /* 226 + * This platform requires SUSPHY to be enabled here already in order to properly configure 227 + * the PHY and switch dwc3's PIPE interface to USB3 PHY. 228 + */ 229 + dwc3_enable_susphy(&appledwc->dwc, true); 230 + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], mode); 231 + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], mode); 232 + } 233 + 234 + static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state) 235 + { 236 + int ret, ret_reset; 237 + 238 + lockdep_assert_held(&appledwc->lock); 239 + 240 + ret = reset_control_deassert(appledwc->resets); 241 + if (ret) { 242 + dev_err(appledwc->dev, "Failed to deassert resets, err=%d\n", ret); 243 + return ret; 244 + } 245 + 246 + ret = dwc3_apple_core_init(appledwc); 247 + if (ret) 248 + goto reset_assert; 249 + 250 + /* 251 + * Now that the core is initialized and already went through dwc3_core_soft_reset we can 252 + * configure some unknown Apple-specific settings and then bring up xhci or gadget mode. 253 + */ 254 + dwc3_apple_setup_cio(appledwc); 255 + 256 + switch (state) { 257 + case DWC3_APPLE_HOST: 258 + appledwc->dwc.dr_mode = USB_DR_MODE_HOST; 259 + dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST); 260 + dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_HOST); 261 + ret = dwc3_host_init(&appledwc->dwc); 262 + if (ret) { 263 + dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret); 264 + goto core_exit; 265 + } 266 + 267 + break; 268 + case DWC3_APPLE_DEVICE: 269 + appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL; 270 + dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE); 271 + dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_DEVICE); 272 + ret = dwc3_gadget_init(&appledwc->dwc); 273 + if (ret) { 274 + dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret); 275 + goto core_exit; 276 + } 277 + break; 278 + default: 279 + /* Unreachable unless there's a bug in this driver */ 280 + WARN_ON_ONCE(1); 281 + ret = -EINVAL; 282 + goto core_exit; 283 + } 284 + 285 + appledwc->state = state; 286 + return 0; 287 + 288 + core_exit: 289 + dwc3_core_exit(&appledwc->dwc); 290 + reset_assert: 291 + ret_reset = reset_control_assert(appledwc->resets); 292 + if (ret_reset) 293 + dev_warn(appledwc->dev, "Failed to assert resets, err=%d\n", ret_reset); 294 + 295 + return ret; 296 + } 297 + 298 + static int dwc3_apple_exit(struct dwc3_apple *appledwc) 299 + { 300 + int ret = 0; 301 + 302 + lockdep_assert_held(&appledwc->lock); 303 + 304 + switch (appledwc->state) { 305 + case DWC3_APPLE_PROBE_PENDING: 306 + case DWC3_APPLE_NO_CABLE: 307 + /* Nothing to do if we're already off */ 308 + return 0; 309 + case DWC3_APPLE_DEVICE: 310 + dwc3_gadget_exit(&appledwc->dwc); 311 + break; 312 + case DWC3_APPLE_HOST: 313 + dwc3_host_exit(&appledwc->dwc); 314 + break; 315 + } 316 + 317 + /* 318 + * This platform requires SUSPHY to be enabled in order to properly power down the PHY 319 + * and switch dwc3's PIPE interface back to a dummy PHY (i.e. no USB3 support and USB2 via 320 + * a different PHY connected through ULPI). 321 + */ 322 + dwc3_enable_susphy(&appledwc->dwc, true); 323 + dwc3_core_exit(&appledwc->dwc); 324 + appledwc->state = DWC3_APPLE_NO_CABLE; 325 + 326 + ret = reset_control_assert(appledwc->resets); 327 + if (ret) { 328 + dev_err(appledwc->dev, "Failed to assert resets, err=%d\n", ret); 329 + return ret; 330 + } 331 + 332 + return 0; 333 + } 334 + 335 + static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role) 336 + { 337 + struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw); 338 + int ret; 339 + 340 + guard(mutex)(&appledwc->lock); 341 + 342 + /* 343 + * We need to tear all of dwc3 down and re-initialize it every time a cable is 344 + * connected or disconnected or when the mode changes. See the documentation for enum 345 + * dwc3_apple_state for details. 346 + */ 347 + ret = dwc3_apple_exit(appledwc); 348 + if (ret) 349 + return ret; 350 + 351 + switch (role) { 352 + case USB_ROLE_NONE: 353 + /* Nothing to do if no cable is connected */ 354 + return 0; 355 + case USB_ROLE_HOST: 356 + return dwc3_apple_init(appledwc, DWC3_APPLE_HOST); 357 + case USB_ROLE_DEVICE: 358 + return dwc3_apple_init(appledwc, DWC3_APPLE_DEVICE); 359 + default: 360 + dev_err(appledwc->dev, "Invalid target role: %d\n", role); 361 + return -EINVAL; 362 + } 363 + } 364 + 365 + static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) 366 + { 367 + struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw); 368 + 369 + guard(mutex)(&appledwc->lock); 370 + 371 + switch (appledwc->state) { 372 + case DWC3_APPLE_HOST: 373 + return USB_ROLE_HOST; 374 + case DWC3_APPLE_DEVICE: 375 + return USB_ROLE_DEVICE; 376 + case DWC3_APPLE_NO_CABLE: 377 + case DWC3_APPLE_PROBE_PENDING: 378 + return USB_ROLE_NONE; 379 + default: 380 + /* Unreachable unless there's a bug in this driver */ 381 + dev_err(appledwc->dev, "Invalid internal state: %d\n", appledwc->state); 382 + return USB_ROLE_NONE; 383 + } 384 + } 385 + 386 + static int dwc3_apple_setup_role_switch(struct dwc3_apple *appledwc) 387 + { 388 + struct usb_role_switch_desc dwc3_role_switch = { NULL }; 389 + 390 + dwc3_role_switch.fwnode = dev_fwnode(appledwc->dev); 391 + dwc3_role_switch.set = dwc3_usb_role_switch_set; 392 + dwc3_role_switch.get = dwc3_usb_role_switch_get; 393 + dwc3_role_switch.driver_data = appledwc; 394 + appledwc->role_sw = usb_role_switch_register(appledwc->dev, &dwc3_role_switch); 395 + if (IS_ERR(appledwc->role_sw)) 396 + return PTR_ERR(appledwc->role_sw); 397 + 398 + return 0; 399 + } 400 + 401 + static int dwc3_apple_probe(struct platform_device *pdev) 402 + { 403 + struct device *dev = &pdev->dev; 404 + struct dwc3_apple *appledwc; 405 + int ret; 406 + 407 + appledwc = devm_kzalloc(&pdev->dev, sizeof(*appledwc), GFP_KERNEL); 408 + if (!appledwc) 409 + return -ENOMEM; 410 + 411 + appledwc->dev = &pdev->dev; 412 + mutex_init(&appledwc->lock); 413 + 414 + appledwc->resets = devm_reset_control_array_get_exclusive(dev); 415 + if (IS_ERR(appledwc->resets)) 416 + return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->resets), 417 + "Failed to get resets\n"); 418 + 419 + ret = reset_control_assert(appledwc->resets); 420 + if (ret) { 421 + dev_err(&pdev->dev, "Failed to assert resets, err=%d\n", ret); 422 + return ret; 423 + } 424 + 425 + appledwc->mmio_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwc3-core"); 426 + if (!appledwc->mmio_resource) { 427 + dev_err(dev, "Failed to get DWC3 MMIO\n"); 428 + return -EINVAL; 429 + } 430 + 431 + appledwc->apple_regs = devm_platform_ioremap_resource_byname(pdev, "dwc3-apple"); 432 + if (IS_ERR(appledwc->apple_regs)) 433 + return dev_err_probe(dev, PTR_ERR(appledwc->apple_regs), 434 + "Failed to map Apple-specific MMIO\n"); 435 + 436 + /* 437 + * On this platform, DWC3 can only be brought up after parts of the PHY have been 438 + * initialized with knowledge of the target mode and cable orientation from typec_set_mux. 439 + * Since this has not happened here we cannot setup DWC3 yet and instead defer this until 440 + * the first cable is connected. See the documentation for enum dwc3_apple_state for 441 + * details. 442 + */ 443 + appledwc->state = DWC3_APPLE_PROBE_PENDING; 444 + ret = dwc3_apple_setup_role_switch(appledwc); 445 + if (ret) 446 + return dev_err_probe(&pdev->dev, ret, "Failed to setup role switch\n"); 447 + 448 + return 0; 449 + } 450 + 451 + static void dwc3_apple_remove(struct platform_device *pdev) 452 + { 453 + struct dwc3 *dwc = platform_get_drvdata(pdev); 454 + struct dwc3_apple *appledwc = to_dwc3_apple(dwc); 455 + 456 + guard(mutex)(&appledwc->lock); 457 + 458 + usb_role_switch_unregister(appledwc->role_sw); 459 + 460 + /* 461 + * If we're still in DWC3_APPLE_PROBE_PENDING we never got any cable connected event and 462 + * dwc3_core_probe was never called and there's hence no need to call dwc3_core_remove. 463 + * dwc3_apple_exit can be called unconditionally because it checks the state itself. 464 + */ 465 + dwc3_apple_exit(appledwc); 466 + if (appledwc->state != DWC3_APPLE_PROBE_PENDING) 467 + dwc3_core_remove(&appledwc->dwc); 468 + } 469 + 470 + static const struct of_device_id dwc3_apple_of_match[] = { 471 + { .compatible = "apple,t8103-dwc3" }, 472 + {} 473 + }; 474 + MODULE_DEVICE_TABLE(of, dwc3_apple_of_match); 475 + 476 + static struct platform_driver dwc3_apple_driver = { 477 + .probe = dwc3_apple_probe, 478 + .remove = dwc3_apple_remove, 479 + .driver = { 480 + .name = "dwc3-apple", 481 + .of_match_table = dwc3_apple_of_match, 482 + }, 483 + }; 484 + 485 + module_platform_driver(dwc3_apple_driver); 486 + 487 + MODULE_LICENSE("GPL"); 488 + MODULE_AUTHOR("Sven Peter <sven@kernel.org>"); 489 + MODULE_DESCRIPTION("DesignWare DWC3 Apple Silicon Glue Driver");