File Index
Files and directories
Sign in
FILE

dwc3-apple.c

File preview with syntax highlighting.

linux-6.19.14/drivers/usb/dwc3/dwc3-apple.c 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 */
 71enum 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 * @reset: 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 */
 89struct 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 *reset;
 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
130static 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
135static 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
140static 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
150static 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
165static 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
171static 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
194static 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
221static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state)
222{
223	int ret, ret_reset;
224
225	lockdep_assert_held(&appledwc->lock);
226
227	/*
228	 * The USB2 PHY on this platform must be configured for host or device mode while it is
229	 * still powered off and before dwc3 tries to access it. Otherwise, the new configuration
230	 * will sometimes only take affect after the *next* time dwc3 is brought up which causes
231	 * the connected device to just not work.
232	 * The USB3 PHY must be configured later after dwc3 has already been initialized.
233	 */
234	switch (state) {
235	case DWC3_APPLE_HOST:
236		phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_HOST);
237		break;
238	case DWC3_APPLE_DEVICE:
239		phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_DEVICE);
240		break;
241	default:
242		/* Unreachable unless there's a bug in this driver */
243		return -EINVAL;
244	}
245
246	ret = reset_control_deassert(appledwc->reset);
247	if (ret) {
248		dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret);
249		return ret;
250	}
251
252	ret = dwc3_apple_core_init(appledwc);
253	if (ret)
254		goto reset_assert;
255
256	/*
257	 * Now that the core is initialized and already went through dwc3_core_soft_reset we can
258	 * configure some unknown Apple-specific settings and then bring up xhci or gadget mode.
259	 */
260	dwc3_apple_setup_cio(appledwc);
261
262	switch (state) {
263	case DWC3_APPLE_HOST:
264		appledwc->dwc.dr_mode = USB_DR_MODE_HOST;
265		dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST);
266		/*
267		 * This platform requires SUSPHY to be enabled here already in order to properly
268		 * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY
269		 * has already been configured to the correct mode earlier.
270		 */
271		dwc3_enable_susphy(&appledwc->dwc, true);
272		phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_HOST);
273		ret = dwc3_host_init(&appledwc->dwc);
274		if (ret) {
275			dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret);
276			goto core_exit;
277		}
278
279		break;
280	case DWC3_APPLE_DEVICE:
281		appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL;
282		dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE);
283		/*
284		 * This platform requires SUSPHY to be enabled here already in order to properly
285		 * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY
286		 * has already been configured to the correct mode earlier.
287		 */
288		dwc3_enable_susphy(&appledwc->dwc, true);
289		phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
290		ret = dwc3_gadget_init(&appledwc->dwc);
291		if (ret) {
292			dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret);
293			goto core_exit;
294		}
295		break;
296	default:
297		/* Unreachable unless there's a bug in this driver */
298		WARN_ON_ONCE(1);
299		ret = -EINVAL;
300		goto core_exit;
301	}
302
303	appledwc->state = state;
304	return 0;
305
306core_exit:
307	dwc3_core_exit(&appledwc->dwc);
308reset_assert:
309	ret_reset = reset_control_assert(appledwc->reset);
310	if (ret_reset)
311		dev_warn(appledwc->dev, "Failed to assert reset, err=%d\n", ret_reset);
312
313	return ret;
314}
315
316static int dwc3_apple_exit(struct dwc3_apple *appledwc)
317{
318	int ret = 0;
319
320	lockdep_assert_held(&appledwc->lock);
321
322	switch (appledwc->state) {
323	case DWC3_APPLE_PROBE_PENDING:
324	case DWC3_APPLE_NO_CABLE:
325		/* Nothing to do if we're already off */
326		return 0;
327	case DWC3_APPLE_DEVICE:
328		dwc3_gadget_exit(&appledwc->dwc);
329		break;
330	case DWC3_APPLE_HOST:
331		dwc3_host_exit(&appledwc->dwc);
332		break;
333	}
334
335	/*
336	 * This platform requires SUSPHY to be enabled in order to properly power down the PHY
337	 * and switch dwc3's PIPE interface back to a dummy PHY (i.e. no USB3 support and USB2 via
338	 * a different PHY connected through ULPI).
339	 */
340	dwc3_enable_susphy(&appledwc->dwc, true);
341	dwc3_core_exit(&appledwc->dwc);
342	appledwc->state = DWC3_APPLE_NO_CABLE;
343
344	ret = reset_control_assert(appledwc->reset);
345	if (ret) {
346		dev_err(appledwc->dev, "Failed to assert reset, err=%d\n", ret);
347		return ret;
348	}
349
350	return 0;
351}
352
353static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role)
354{
355	struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw);
356	int ret;
357
358	guard(mutex)(&appledwc->lock);
359
360	/*
361	 * Skip role switches if appledwc is already in the desired state. The
362	 * USB-C port controller on M2 and M1/M2 Pro/Max/Ultra devices issues
363	 * additional interrupts which results in usb_role_switch_set_role()
364	 * calls with the current role.
365	 * Ignore those calls here to ensure the USB-C port controller and
366	 * appledwc are in a consistent state.
367	 * This matches the behaviour in __dwc3_set_mode().
368	 * Do no handle USB_ROLE_NONE for DWC3_APPLE_NO_CABLE and
369	 * DWC3_APPLE_PROBE_PENDING since that is no-op anyway.
370	 */
371	if (appledwc->state == DWC3_APPLE_HOST && role == USB_ROLE_HOST)
372		return 0;
373	if (appledwc->state == DWC3_APPLE_DEVICE && role == USB_ROLE_DEVICE)
374		return 0;
375
376	/*
377	 * We need to tear all of dwc3 down and re-initialize it every time a cable is
378	 * connected or disconnected or when the mode changes. See the documentation for enum
379	 * dwc3_apple_state for details.
380	 */
381	ret = dwc3_apple_exit(appledwc);
382	if (ret)
383		return ret;
384
385	switch (role) {
386	case USB_ROLE_NONE:
387		/* Nothing to do if no cable is connected */
388		return 0;
389	case USB_ROLE_HOST:
390		return dwc3_apple_init(appledwc, DWC3_APPLE_HOST);
391	case USB_ROLE_DEVICE:
392		return dwc3_apple_init(appledwc, DWC3_APPLE_DEVICE);
393	default:
394		dev_err(appledwc->dev, "Invalid target role: %d\n", role);
395		return -EINVAL;
396	}
397}
398
399static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
400{
401	struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw);
402
403	guard(mutex)(&appledwc->lock);
404
405	switch (appledwc->state) {
406	case DWC3_APPLE_HOST:
407		return USB_ROLE_HOST;
408	case DWC3_APPLE_DEVICE:
409		return USB_ROLE_DEVICE;
410	case DWC3_APPLE_NO_CABLE:
411	case DWC3_APPLE_PROBE_PENDING:
412		return USB_ROLE_NONE;
413	default:
414		/* Unreachable unless there's a bug in this driver */
415		dev_err(appledwc->dev, "Invalid internal state: %d\n", appledwc->state);
416		return USB_ROLE_NONE;
417	}
418}
419
420static int dwc3_apple_setup_role_switch(struct dwc3_apple *appledwc)
421{
422	struct usb_role_switch_desc dwc3_role_switch = { NULL };
423
424	dwc3_role_switch.fwnode = dev_fwnode(appledwc->dev);
425	dwc3_role_switch.set = dwc3_usb_role_switch_set;
426	dwc3_role_switch.get = dwc3_usb_role_switch_get;
427	dwc3_role_switch.driver_data = appledwc;
428	appledwc->role_sw = usb_role_switch_register(appledwc->dev, &dwc3_role_switch);
429	if (IS_ERR(appledwc->role_sw))
430		return PTR_ERR(appledwc->role_sw);
431
432	return 0;
433}
434
435static int dwc3_apple_probe(struct platform_device *pdev)
436{
437	struct device *dev = &pdev->dev;
438	struct dwc3_apple *appledwc;
439	int ret;
440
441	appledwc = devm_kzalloc(&pdev->dev, sizeof(*appledwc), GFP_KERNEL);
442	if (!appledwc)
443		return -ENOMEM;
444
445	appledwc->dev = &pdev->dev;
446	mutex_init(&appledwc->lock);
447
448	appledwc->reset = devm_reset_control_get_exclusive(dev, NULL);
449	if (IS_ERR(appledwc->reset))
450		return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->reset),
451				     "Failed to get reset control\n");
452
453	ret = reset_control_assert(appledwc->reset);
454	if (ret) {
455		dev_err(&pdev->dev, "Failed to assert reset, err=%d\n", ret);
456		return ret;
457	}
458
459	appledwc->mmio_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwc3-core");
460	if (!appledwc->mmio_resource) {
461		dev_err(dev, "Failed to get DWC3 MMIO\n");
462		return -EINVAL;
463	}
464
465	appledwc->apple_regs = devm_platform_ioremap_resource_byname(pdev, "dwc3-apple");
466	if (IS_ERR(appledwc->apple_regs))
467		return dev_err_probe(dev, PTR_ERR(appledwc->apple_regs),
468				     "Failed to map Apple-specific MMIO\n");
469
470	/*
471	 * On this platform, DWC3 can only be brought up after parts of the PHY have been
472	 * initialized with knowledge of the target mode and cable orientation from typec_set_mux.
473	 * Since this has not happened here we cannot setup DWC3 yet and instead defer this until
474	 * the first cable is connected. See the documentation for enum dwc3_apple_state for
475	 * details.
476	 */
477	appledwc->state = DWC3_APPLE_PROBE_PENDING;
478	ret = dwc3_apple_setup_role_switch(appledwc);
479	if (ret)
480		return dev_err_probe(&pdev->dev, ret, "Failed to setup role switch\n");
481
482	return 0;
483}
484
485static void dwc3_apple_remove(struct platform_device *pdev)
486{
487	struct dwc3 *dwc = platform_get_drvdata(pdev);
488	struct dwc3_apple *appledwc = to_dwc3_apple(dwc);
489
490	guard(mutex)(&appledwc->lock);
491
492	usb_role_switch_unregister(appledwc->role_sw);
493
494	/*
495	 * If we're still in DWC3_APPLE_PROBE_PENDING we never got any cable connected event and
496	 * dwc3_core_probe was never called and there's hence no need to call dwc3_core_remove.
497	 * dwc3_apple_exit can be called unconditionally because it checks the state itself.
498	 */
499	dwc3_apple_exit(appledwc);
500	if (appledwc->state != DWC3_APPLE_PROBE_PENDING)
501		dwc3_core_remove(&appledwc->dwc);
502}
503
504static const struct of_device_id dwc3_apple_of_match[] = {
505	{ .compatible = "apple,t8103-dwc3" },
506	{}
507};
508MODULE_DEVICE_TABLE(of, dwc3_apple_of_match);
509
510static struct platform_driver dwc3_apple_driver = {
511	.probe		= dwc3_apple_probe,
512	.remove		= dwc3_apple_remove,
513	.driver		= {
514		.name	= "dwc3-apple",
515		.of_match_table	= dwc3_apple_of_match,
516	},
517};
518
519module_platform_driver(dwc3_apple_driver);
520
521MODULE_LICENSE("GPL");
522MODULE_AUTHOR("Sven Peter <sven@kernel.org>");
523MODULE_DESCRIPTION("DesignWare DWC3 Apple Silicon Glue Driver");
~/ Read-only file preview.