From 1ba6543612770530e787e1cf5b8229d61df7e7af Mon Sep 17 00:00:00 2001 From: Dj Date: Sat, 10 Oct 2026 23:51:19 -0400 Subject: [PATCH 1/3] phy: apple: T6050 fixed DisplayPort PHY register sequences The link sequences of the T6050 fixed DP PHY, taken unchanged from aurora-silicon/linux#248 ("phy: apple: Add T6050 fixed DP and four-clock crossbar support", 440f79f96363) as the single file the T6040 Type-C PHY needs for its DisplayPort mode: the T6050 ADT names its PHY "atc-phy,t6050", "atc-phy,t6040", and the sequences run unchanged on the T6040 PHY core. Only the data header is carried here; the T6050 PHY driver, its binding and the crossbar changes of #248 stay in that pull request. Once #248 lands, this commit disappears on rebase. [fbalazsy: picked drivers/phy/apple/atc-t6050-dp-data.h alone from #248] --- drivers/phy/apple/atc-t6050-dp-data.h | 513 ++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 drivers/phy/apple/atc-t6050-dp-data.h diff --git a/drivers/phy/apple/atc-t6050-dp-data.h b/drivers/phy/apple/atc-t6050-dp-data.h new file mode 100644 index 0000000000000..e4a09dae6c3f5 --- /dev/null +++ b/drivers/phy/apple/atc-t6050-dp-data.h @@ -0,0 +1,513 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* T6050 fixed DisplayPort PHY register sequences. */ +static const struct atc_op dp_init[] = { + { ATC_MASK, 39, 0x0, 0x1, 0x1, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_POLL, 39, 0x4, 0x1, 0x1, 1000 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x0, 0x2, 0x2, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_POLL, 39, 0x4, 0x2, 0x2, 1000 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x0, 0x4, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_POWER, 2, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x2c, 0x1, 0x0, 0 }, + { ATC_POLL, 39, 0x4, 0x100, 0x0, 1000 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x0, 0x8, 0x8, 0 }, + { ATC_TUNABLE, 41, 0x0, 0x0, 0x0, 0 }, + { ATC_TUNABLE, 40, 0x0, 0x0, 0x1, 0 }, + { ATC_TUNABLE, 43, 0x0, 0x0, 0x2, 0 }, + { ATC_TUNABLE, 42, 0x0, 0x0, 0x3, 0 }, + { ATC_TUNABLE, 20, 0x0, 0x0, 0x4, 0 }, + { ATC_TUNABLE, 29, 0x0, 0x0, 0x5, 0 }, + { ATC_TUNABLE, 18, 0x0, 0x0, 0x6, 0 }, + { ATC_TUNABLE, 27, 0x0, 0x0, 0x7, 0 }, + { ATC_MASK, 4, 0x0, 0x3f0, 0x1c0, 0 }, + { ATC_MASK, 4, 0x18, 0xf000, 0x3000, 0 }, + { ATC_MASK, 9, 0x88, 0xff, 0x27, 0 }, + { ATC_MASK, 33, 0x4, 0x38, 0x8, 0 }, + { ATC_MASK, 10, 0xdc, 0x3ff0, 0x1760, 0 }, + { ATC_MASK, 21, 0xcc, 0x1f80000, 0x380000, 0 }, + { ATC_MASK, 30, 0xcc, 0x1f80000, 0x380000, 0 }, + { ATC_MASK, 20, 0x90, 0x1f80000, 0x380000, 0 }, + { ATC_MASK, 29, 0x90, 0x1f80000, 0x380000, 0 }, + { ATC_TUNABLE, 8, 0x0, 0x0, 0x8, 0 }, + { ATC_MASK, 2, 0x6c, 0x3c000fc, 0x48, 0 }, + { ATC_MASK, 2, 0x78, 0x1f, 0x14, 0 }, + { ATC_MASK, 2, 0x78, 0x3ffe0, 0x22000, 0 }, + { ATC_MASK, 2, 0x6c, 0x3, 0x2, 0 }, + { ATC_MASK, 2, 0xc, 0x2a, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0xa, 0 }, + { ATC_MASK, 39, 0x0, 0x10, 0x10, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x64, 0 }, +}; + +static const struct atc_op aux_on[] = { + { ATC_MASK, 34, 0x0, 0x3, 0x3, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 34, 0x0, 0xc, 0xc, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 34, 0x0, 0x300, 0x100, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 33, 0x0, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, +}; + +static const struct atc_op aux_off[] = { + { ATC_MASK, 33, 0x0, 0x1, 0x1, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 34, 0x0, 0x300, 0x300, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 34, 0x0, 0x8, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 34, 0x0, 0x2, 0x0, 0 }, +}; + +static const struct atc_op dp_poweroff[] = { + { ATC_MASK, 2, 0x120, 0x100, 0x100, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x200, 0x200, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x20, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 5, 0x0, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x10, 0x10, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x4, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0xc8, 0 }, + { ATC_POWER, 2, 0x0, 0x0, 0x0, 0 }, + { ATC_MASK, 39, 0x2c, 0x1, 0x1, 0 }, + { ATC_POLL, 39, 0x4, 0x100, 0x100, 1000 }, + { ATC_MASK, 39, 0x0, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x0, 0x8, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x0, 0x4, 0x4, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x0, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_POLL, 39, 0x4, 0x2, 0x0, 1000 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x0, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_POLL, 39, 0x4, 0x1, 0x0, 1000 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 39, 0x8, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, +}; + +static const struct atc_op dp_link_off[] = { + { ATC_MASK, 39, 0x14, 0x1007, 0x7, 0 }, + { ATC_MASK, 39, 0x18, 0x1, 0x0, 0 }, + { ATC_POLL, 39, 0x1c, 0x1, 0x0, 5000 }, + { ATC_MASK, 14, 0x0, 0x1, 0x1, 0 }, + { ATC_MASK, 22, 0x1c, 0xc00000, 0xc00000, 0 }, + { ATC_MASK, 22, 0x10, 0x600, 0x400, 0 }, + { ATC_MASK, 22, 0x4, 0x30000000, 0x20000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x100000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x400000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x4000000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x1000000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 22, 0x44, 0x1000, 0x0, 0 }, + { ATC_MASK, 2, 0x120, 0x100, 0x100, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x1c, 0xc00000, 0xc00000, 0 }, + { ATC_MASK, 31, 0x10, 0x600, 0x400, 0 }, + { ATC_MASK, 31, 0x4, 0x30000000, 0x20000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x100000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x400000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x4000000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x1000000, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 31, 0x44, 0x1000, 0x0, 0 }, + { ATC_MASK, 2, 0x120, 0x200, 0x200, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x120, 0x20, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x44, 0x6000000, 0x4000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x44, 0x18000000, 0x10000000, 0 }, + { ATC_MASK, 19, 0x24, 0xc0000000, 0x80000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x24, 0x30000000, 0x20000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x28, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 19, 0x40, 0xc000, 0xc000, 0 }, + { ATC_MASK, 16, 0x1d0, 0x3, 0x1, 0 }, + { ATC_MASK, 19, 0x40, 0x10000, 0x0, 0 }, + { ATC_MASK, 2, 0xc, 0x4000, 0x4000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x40, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x400, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x10, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x10, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x44, 0x6000000, 0x4000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x44, 0x18000000, 0x10000000, 0 }, + { ATC_MASK, 28, 0x24, 0xc0000000, 0x80000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x24, 0x30000000, 0x20000000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x28, 0x10, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 28, 0x40, 0xc000, 0xc000, 0 }, + { ATC_MASK, 25, 0x1d0, 0x3, 0x1, 0 }, + { ATC_MASK, 28, 0x40, 0x10000, 0x0, 0 }, + { ATC_MASK, 2, 0xc, 0x8000, 0x8000, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x80, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0xc, 0x800, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x10, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x10, 0x20, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 14, 0x0, 0xc, 0x8, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x440000, 0x440000, 0 }, + { ATC_MASK, 3, 0x8, 0x5, 0x5, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x880000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0xa, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x880000, 0x880000, 0 }, + { ATC_MASK, 3, 0x8, 0xa, 0xa, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x440000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x5, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x400000, 0x400000, 0 }, + { ATC_MASK, 3, 0x8, 0x4, 0x4, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x800000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x8, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x800000, 0x800000, 0 }, + { ATC_MASK, 3, 0x8, 0x8, 0x8, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x400000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x4, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x40000, 0x40000, 0 }, + { ATC_MASK, 3, 0x8, 0x1, 0x1, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x80000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x2, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x80000, 0x80000, 0 }, + { ATC_MASK, 3, 0x8, 0x2, 0x2, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 2, 0x160, 0x40000, 0x0, 0 }, + { ATC_MASK, 3, 0x8, 0x1, 0x0, 0 }, + { ATC_DELAY, 0, 0x0, 0x0, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x7, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x8, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x100, 500000 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x0, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x0, 500000 }, + { ATC_POLL, 14, 0x34, 0x8, 0x0, 500000 }, + { ATC_DELAY, 0, 0x0, 0x0, 0xa, 0 }, + { ATC_MASK, 6, 0x34, 0xf, 0x1, 0 }, + { ATC_MASK, 6, 0x1c, 0x7, 0x0, 0 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x8, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x100, 500000 }, + { ATC_MASK, 6, 0x1c, 0x8, 0x0, 0 }, + { ATC_POLL, 6, 0x2c, 0xff00, 0x0, 500000 }, + { ATC_MASK, 8, 0x0, 0xaaa, 0x0, 0 }, +}; + +static const struct atc_rate_op dp_link_on[] = { + { { ATC_MASK, 39, 0x14, 0x7, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 39, 0x18, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x6c, 0xfc, 0, 0 }, 0xf, { 0x48, 0x48, 0x48, 0x48 } }, + { { ATC_MASK, 14, 0x0, 0x40fff0, 0, 0 }, 0xf, { 0x1110, 0x1110, 0x1110, 0x1110 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x1, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x1, { 0x1e0e021c, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x1, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x1, { 0x156600, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x1, { 0x70000, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x1, { 0x1, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x2, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x2, { 0x0, 0x1e0e01c2, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x2, { 0x0, 0x7fffffe, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x2, { 0x0, 0x155200, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x2, { 0x0, 0x70000, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x2, { 0x0, 0x1, 0x0, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x4, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x1e0e01c2, 0x0 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x7fffffe, 0x0 } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x4, { 0x0, 0x0, 0x554800, 0x0 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x4, { 0x0, 0x0, 0x70000, 0x0 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x4, { 0x0, 0x0, 0x1, 0x0 } }, + { { ATC_MASK, 8, 0x34, 0x3, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 7, 0x80, 0xffffffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x1e0e02a3 } }, + { { ATC_MASK, 7, 0x84, 0xfffffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0xbff7ffc } }, + { { ATC_MASK, 7, 0x88, 0x7fffff, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x564800 } }, + { { ATC_MASK, 8, 0x8, 0x1f0000, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x70000 } }, + { { ATC_MASK, 8, 0x18, 0x1, 0, 0 }, 0x8, { 0x0, 0x0, 0x0, 0x1 } }, + { { ATC_MASK, 8, 0x0, 0xaaa, 0, 0 }, 0xf, { 0xaaa, 0xaaa, 0xaaa, 0xaaa } }, + { { ATC_MASK, 6, 0x30, 0x4000, 0, 0 }, 0xf, { 0x4000, 0x4000, 0x4000, 0x4000 } }, + { { ATC_MASK, 6, 0x34, 0xf, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 6, 0x18, 0x1fe000, 0, 0 }, 0xf, { 0x2000, 0x2000, 0x2000, 0x2000 } }, + { { ATC_MASK, 6, 0x20, 0x1, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 6, 0x1c, 0x7, 0, 0 }, 0xf, { 0x7, 0x7, 0x7, 0x7 } }, + { { ATC_MASK, 6, 0x1c, 0x8, 0, 0 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_POLL, 6, 0x2c, 0xff00, 0, 500000 }, 0xf, { 0x100, 0x100, 0x100, 0x100 } }, + { { ATC_MASK, 6, 0x1c, 0x8, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 6, 0x2c, 0xff00, 0, 500000 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 14, 0x34, 0x8, 0, 500000 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_MASK, 19, 0x3c, 0x18000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_MASK, 28, 0x3c, 0x18000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_MASK, 14, 0x0, 0xc, 0, 0 }, 0xf, { 0xc, 0xc, 0xc, 0xc } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x10, 0, 0 }, 0xf, { 0x10, 0x10, 0x10, 0x10 } }, + { { ATC_MASK, 2, 0x120, 0x40, 0, 0 }, 0xf, { 0x40, 0x40, 0x40, 0x40 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x4, 0, 0 }, 0xf, { 0x4, 0x4, 0x4, 0x4 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x100, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 2, 0x120, 0x400, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0xfffffc0, 0, 0 }, 0xf, { 0xaafb800, 0xaafb800, 0xaafb800, 0xaafb800 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x1000000, 0, 0 }, 0xf, { 0x1000000, 0x1000000, 0x1000000, 0x1000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x4000000, 0, 0 }, 0xf, { 0x4000000, 0x4000000, 0x4000000, 0x4000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x7c0, 0, 0 }, 0xf, { 0x300, 0x300, 0x300, 0x300 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x6c000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 21, 0x100, 0x100000, 0, 0 }, 0xf, { 0x100000, 0x100000, 0x100000, 0x100000 } }, + { { ATC_MASK, 21, 0x58, 0xc00f, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 21, 0x104, 0x20000000, 0, 0 }, 0xf, { 0x20000000, 0x20000000, 0x20000000, 0x20000000 } }, + { { ATC_MASK, 21, 0x100, 0x7ffff, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 22, 0x44, 0x100000, 0, 0 }, 0x7, { 0x100000, 0x100000, 0x100000, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0x7, { 0x1, 0x1, 0x1, 0x0 } }, + { { ATC_MASK, 22, 0x44, 0x400000, 0, 0 }, 0x7, { 0x400000, 0x400000, 0x400000, 0x0 } }, + { { ATC_MASK, 22, 0x4, 0x3000000, 0, 0 }, 0xf, { 0x2000000, 0x2000000, 0x2000000, 0x2000000 } }, + { { ATC_MASK, 22, 0x4, 0x30000000, 0, 0 }, 0xf, { 0x30000000, 0x30000000, 0x30000000, 0x30000000 } }, + { { ATC_MASK, 22, 0xc, 0x7000000, 0, 0 }, 0x1, { 0x5000000, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 22, 0xc, 0x7000000, 0, 0 }, 0xe, { 0x0, 0x4000000, 0x4000000, 0x4000000 } }, + { { ATC_MASK, 22, 0x8, 0x3, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 22, 0x10, 0x600, 0, 0 }, 0xf, { 0x600, 0x600, 0x600, 0x600 } }, + { { ATC_MASK, 22, 0x1c, 0xc00000, 0, 0 }, 0xf, { 0x800000, 0x800000, 0x800000, 0x800000 } }, + { { ATC_MASK, 2, 0x120, 0x20, 0, 0 }, 0xf, { 0x20, 0x20, 0x20, 0x20 } }, + { { ATC_MASK, 2, 0x120, 0x80, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x2, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 2, 0x120, 0x8, 0, 0 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x120, 0x200, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 2, 0x120, 0x800, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0xfffffc0, 0, 0 }, 0xf, { 0xaafb800, 0xaafb800, 0xaafb800, 0xaafb800 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x1000000, 0, 0 }, 0xf, { 0x1000000, 0x1000000, 0x1000000, 0x1000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x4000000, 0, 0 }, 0xf, { 0x4000000, 0x4000000, 0x4000000, 0x4000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x7c0, 0, 0 }, 0xf, { 0x300, 0x300, 0x300, 0x300 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x6c000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 30, 0x100, 0x100000, 0, 0 }, 0xf, { 0x100000, 0x100000, 0x100000, 0x100000 } }, + { { ATC_MASK, 30, 0x58, 0xc00f, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 30, 0x104, 0x20000000, 0, 0 }, 0xf, { 0x20000000, 0x20000000, 0x20000000, 0x20000000 } }, + { { ATC_MASK, 30, 0x100, 0x7ffff, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 31, 0x44, 0x100000, 0, 0 }, 0x7, { 0x100000, 0x100000, 0x100000, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0x7, { 0x1, 0x1, 0x1, 0x0 } }, + { { ATC_MASK, 31, 0x44, 0x400000, 0, 0 }, 0x7, { 0x400000, 0x400000, 0x400000, 0x0 } }, + { { ATC_MASK, 31, 0x4, 0x3000000, 0, 0 }, 0xf, { 0x2000000, 0x2000000, 0x2000000, 0x2000000 } }, + { { ATC_MASK, 31, 0x4, 0x30000000, 0, 0 }, 0xf, { 0x30000000, 0x30000000, 0x30000000, 0x30000000 } }, + { { ATC_MASK, 31, 0xc, 0x7000000, 0, 0 }, 0x1, { 0x5000000, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 31, 0xc, 0x7000000, 0, 0 }, 0xe, { 0x0, 0x4000000, 0x4000000, 0x4000000 } }, + { { ATC_MASK, 31, 0x8, 0x3, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 31, 0x10, 0x600, 0, 0 }, 0xf, { 0x600, 0x600, 0x600, 0x600 } }, + { { ATC_MASK, 31, 0x1c, 0xc00000, 0, 0 }, 0xf, { 0x800000, 0x800000, 0x800000, 0x800000 } }, + { { ATC_MASK, 19, 0xf4, 0x3000, 0, 0 }, 0xf, { 0x3000, 0x3000, 0x3000, 0x3000 } }, + { { ATC_MASK, 16, 0x188, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 16, 0x1d4, 0x700, 0, 0 }, 0xf, { 0x300, 0x300, 0x300, 0x300 } }, + { { ATC_MASK, 19, 0x24, 0x3f0000, 0, 0 }, 0xf, { 0x2a0000, 0x2a0000, 0x2a0000, 0x2a0000 } }, + { { ATC_MASK, 19, 0x3c, 0x18000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_MASK, 19, 0x78, 0xc0000, 0, 0 }, 0xf, { 0x80000, 0x80000, 0x80000, 0x80000 } }, + { { ATC_MASK, 19, 0x7c, 0xc0, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_MASK, 19, 0x84, 0xc000, 0, 0 }, 0xf, { 0x8000, 0x8000, 0x8000, 0x8000 } }, + { { ATC_MASK, 19, 0xd4, 0x600000, 0, 0 }, 0xf, { 0x400000, 0x400000, 0x400000, 0x400000 } }, + { { ATC_MASK, 19, 0xd8, 0x300, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_MASK, 19, 0x40, 0xc00, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 19, 0x48, 0x600, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_MASK, 19, 0x28, 0x1ff030, 0, 0 }, 0xf, { 0x11c020, 0x11c020, 0x11c020, 0x11c020 } }, + { { ATC_MASK, 19, 0x2c, 0xfc, 0, 0 }, 0xf, { 0xb0, 0xb0, 0xb0, 0xb0 } }, + { { ATC_MASK, 19, 0x28, 0xe00, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 19, 0x28, 0x1c0, 0, 0 }, 0xf, { 0x180, 0x180, 0x180, 0x180 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x10, 0, 0 }, 0xf, { 0x10, 0x10, 0x10, 0x10 } }, + { { ATC_MASK, 2, 0x10, 0x40, 0, 0 }, 0xf, { 0x40, 0x40, 0x40, 0x40 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x4, 0, 0 }, 0xf, { 0x4, 0x4, 0x4, 0x4 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x400, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_MASK, 2, 0xc, 0x1000, 0, 0 }, 0xf, { 0x1000, 0x1000, 0x1000, 0x1000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x40, 0, 0 }, 0xf, { 0x40, 0x40, 0x40, 0x40 } }, + { { ATC_MASK, 2, 0xc, 0x100, 0, 0 }, 0xf, { 0x100, 0x100, 0x100, 0x100 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x4000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 2, 0xc, 0x10000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 16, 0x1d0, 0x3, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 16, 0x1d0, 0x2, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x40, 0x30000, 0, 0 }, 0xf, { 0x30000, 0x30000, 0x30000, 0x30000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x40, 0xc000, 0, 0 }, 0xf, { 0xc000, 0xc000, 0xc000, 0xc000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x3010, 0, 0 }, 0xf, { 0x1010, 0x1010, 0x1010, 0x1010 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x600, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x3000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x24, 0x30000000, 0, 0 }, 0xf, { 0x30000000, 0x30000000, 0x30000000, 0x30000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x24, 0xc0000000, 0, 0 }, 0xf, { 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x600, 0, 0 }, 0xf, { 0x600, 0x600, 0x600, 0x600 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x600, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x28, 0x600, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 19, 0x44, 0x18000000, 0, 0 }, 0xf, { 0x18000000, 0x18000000, 0x18000000, 0x18000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 19, 0x40, 0x38, 0, 0 }, 0x1, { 0x28, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 19, 0x40, 0x38, 0, 0 }, 0xe, { 0x0, 0x20, 0x20, 0x20 } }, + { { ATC_MASK, 19, 0x44, 0x60000000, 0, 0 }, 0xf, { 0x40000000, 0x40000000, 0x40000000, 0x40000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 19, 0x44, 0x6000000, 0, 0 }, 0xf, { 0x6000000, 0x6000000, 0x6000000, 0x6000000 } }, + { { ATC_MASK, 19, 0x40, 0xc000, 0, 0 }, 0xf, { 0x8000, 0x8000, 0x8000, 0x8000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 20, 0x68, 0xc00f, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 20, 0xac, 0x20000000, 0, 0 }, 0xf, { 0x20000000, 0x20000000, 0x20000000, 0x20000000 } }, + { { ATC_MASK, 20, 0xa8, 0x7ffff, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 20, 0x18, 0x38, 0, 0 }, 0xf, { 0x20, 0x20, 0x20, 0x20 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0xf4, 0x3000, 0, 0 }, 0xf, { 0x3000, 0x3000, 0x3000, 0x3000 } }, + { { ATC_MASK, 25, 0x188, 0x1, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 25, 0x1d4, 0x700, 0, 0 }, 0xf, { 0x300, 0x300, 0x300, 0x300 } }, + { { ATC_MASK, 28, 0x24, 0x3f0000, 0, 0 }, 0xf, { 0x2a0000, 0x2a0000, 0x2a0000, 0x2a0000 } }, + { { ATC_MASK, 28, 0x3c, 0x18000, 0, 0 }, 0xf, { 0x10000, 0x10000, 0x10000, 0x10000 } }, + { { ATC_MASK, 28, 0x78, 0xc0000, 0, 0 }, 0xf, { 0x80000, 0x80000, 0x80000, 0x80000 } }, + { { ATC_MASK, 28, 0x7c, 0xc0, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_MASK, 28, 0x84, 0xc000, 0, 0 }, 0xf, { 0x8000, 0x8000, 0x8000, 0x8000 } }, + { { ATC_MASK, 28, 0xd4, 0x600000, 0, 0 }, 0xf, { 0x400000, 0x400000, 0x400000, 0x400000 } }, + { { ATC_MASK, 28, 0xd8, 0x300, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_MASK, 28, 0x40, 0xc00, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 28, 0x48, 0x600, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_MASK, 28, 0x28, 0x1ff030, 0, 0 }, 0xf, { 0x11c020, 0x11c020, 0x11c020, 0x11c020 } }, + { { ATC_MASK, 28, 0x2c, 0xfc, 0, 0 }, 0xf, { 0xb0, 0xb0, 0xb0, 0xb0 } }, + { { ATC_MASK, 28, 0x28, 0xe00, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 28, 0x28, 0x1c0, 0, 0 }, 0xf, { 0x180, 0x180, 0x180, 0x180 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x20, 0, 0 }, 0xf, { 0x20, 0x20, 0x20, 0x20 } }, + { { ATC_MASK, 2, 0x10, 0x80, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0x10, 0x2, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 2, 0x10, 0x8, 0, 0 }, 0xf, { 0x8, 0x8, 0x8, 0x8 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x800, 0, 0 }, 0xf, { 0x800, 0x800, 0x800, 0x800 } }, + { { ATC_MASK, 2, 0xc, 0x2000, 0, 0 }, 0xf, { 0x2000, 0x2000, 0x2000, 0x2000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x80, 0, 0 }, 0xf, { 0x80, 0x80, 0x80, 0x80 } }, + { { ATC_MASK, 2, 0xc, 0x200, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 2, 0xc, 0x8000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 2, 0xc, 0x20000, 0, 0 }, 0xf, { 0x20000, 0x20000, 0x20000, 0x20000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 25, 0x1d0, 0x3, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 25, 0x1d0, 0x2, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x40, 0x30000, 0, 0 }, 0xf, { 0x30000, 0x30000, 0x30000, 0x30000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x40, 0xc000, 0, 0 }, 0xf, { 0xc000, 0xc000, 0xc000, 0xc000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x3010, 0, 0 }, 0xf, { 0x1010, 0x1010, 0x1010, 0x1010 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x600, 0, 0 }, 0xf, { 0x200, 0x200, 0x200, 0x200 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x3000, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x24, 0x30000000, 0, 0 }, 0xf, { 0x30000000, 0x30000000, 0x30000000, 0x30000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x24, 0xc0000000, 0, 0 }, 0xf, { 0xc0000000, 0xc0000000, 0xc0000000, 0xc0000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x600, 0, 0 }, 0xf, { 0x600, 0x600, 0x600, 0x600 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x600, 0, 0 }, 0xf, { 0x400, 0x400, 0x400, 0x400 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x28, 0x600, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 28, 0x44, 0x18000000, 0, 0 }, 0xf, { 0x18000000, 0x18000000, 0x18000000, 0x18000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x2, 0x2, 0x2, 0x2 } }, + { { ATC_MASK, 28, 0x40, 0x38, 0, 0 }, 0x1, { 0x28, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 28, 0x40, 0x38, 0, 0 }, 0xe, { 0x0, 0x20, 0x20, 0x20 } }, + { { ATC_MASK, 28, 0x44, 0x60000000, 0, 0 }, 0xf, { 0x40000000, 0x40000000, 0x40000000, 0x40000000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 28, 0x44, 0x6000000, 0, 0 }, 0xf, { 0x6000000, 0x6000000, 0x6000000, 0x6000000 } }, + { { ATC_MASK, 28, 0x40, 0xc000, 0, 0 }, 0xf, { 0x8000, 0x8000, 0x8000, 0x8000 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 29, 0x68, 0xc00f, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_MASK, 29, 0xac, 0x20000000, 0, 0 }, 0xf, { 0x20000000, 0x20000000, 0x20000000, 0x20000000 } }, + { { ATC_MASK, 29, 0xa8, 0x7ffff, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 29, 0x18, 0x38, 0, 0 }, 0xf, { 0x20, 0x20, 0x20, 0x20 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_DELAY, 0, 0x0, 0x0, 0, 0 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, + { { ATC_MASK, 14, 0x0, 0x1, 0, 0 }, 0xf, { 0x0, 0x0, 0x0, 0x0 } }, + { { ATC_POLL, 39, 0x1c, 0x1, 0, 5000 }, 0xf, { 0x1, 0x1, 0x1, 0x1 } }, +}; + +static const u32 dp_training_default[16] = { + 0x3f024, 0x237013, 0x37200b, 0x46e004, 0x3f01c, 0x23700b, 0x372004, 0x3f024, 0x3f013, 0x237004, 0x3f024, 0x3f024, 0x3f004, 0x3f024, 0x3f024, 0x3f024 +}; From ec5ba4abeea9a28511bf883f1b3c78aca3f7243c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Bal=C3=A1zsy?= Date: Sun, 25 Oct 2026 02:10:28 +0200 Subject: [PATCH 2/3] dt-bindings: phy: apple,t6040-atcphy: DisplayPort mode switch and fixed DP PHY The T6040 Type-C PHY also carries DisplayPort: the PHY of a USB-C port as the alt mode switch of its connector (mode-switch), and the PHY of an HDMI converter as a fixed DisplayPort link. The fixed instance has no USB controller behind it, so no PIPE interface ("pipehandler" region), no reset line and no connector port. Describe the DP phy type, the mode switch and the ten-region variant, and require #reset-cells only where the PIPE interface exists. --- .../bindings/phy/apple,t6040-atcphy.yaml | 101 ++++++++++++++---- 1 file changed, 83 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/apple,t6040-atcphy.yaml b/Documentation/devicetree/bindings/phy/apple,t6040-atcphy.yaml index ce11949c34455..04c8907584946 100644 --- a/Documentation/devicetree/bindings/phy/apple,t6040-atcphy.yaml +++ b/Documentation/devicetree/bindings/phy/apple,t6040-atcphy.yaml @@ -11,10 +11,13 @@ maintainers: description: > The Type-C PHY of the Apple T6040 (M4 Pro) SoC, as used for USB2 and USB3 - host operation. USB3 is brought up by a PHY coprocessor running RTKit - firmware, reached through an ASC mailbox. The PHY also provides the reset - line of the attached DWC3 controller and acts as the Type-C orientation - switch. + host operation and for DisplayPort. USB3 is brought up by a PHY coprocessor + running RTKit firmware, reached through an ASC mailbox. The PHY also + provides the reset line of the attached DWC3 controller and acts as the + Type-C orientation switch and, where the port carries DisplayPort alt mode, + as its mode switch. The same PHY drives the fixed DisplayPort link of an + HDMI converter; that instance has no USB controller behind it and therefore + no PIPE interface. $defs: apple,tunable: @@ -35,6 +38,7 @@ properties: const: apple,t6040-atcphy reg: + minItems: 10 items: - description: USB2 PHY controls - description: USB2 event counter controls @@ -49,21 +53,37 @@ properties: - description: ATC common configuration controls reg-names: - items: - - const: bank0 - - const: bank1 - - const: pipehandler - - const: core - - const: cfg - - const: cpu - - const: cpu-data - - const: axi2af - - const: axi2af-lioa - - const: fabric - - const: common-cfg + oneOf: + - items: + - const: bank0 + - const: bank1 + - const: pipehandler + - const: core + - const: cfg + - const: cpu + - const: cpu-data + - const: axi2af + - const: axi2af-lioa + - const: fabric + - const: common-cfg + - description: A fixed DisplayPort PHY without a USB controller + items: + - const: bank0 + - const: bank1 + - const: core + - const: cfg + - const: cpu + - const: cpu-data + - const: axi2af + - const: axi2af-lioa + - const: fabric + - const: common-cfg "#phy-cells": const: 1 + description: > + The PHY type (dt-bindings/phy/phy.h): PHY_TYPE_USB2, PHY_TYPE_USB3 or + PHY_TYPE_DP. "#reset-cells": const: 0 @@ -81,9 +101,13 @@ properties: orientation-switch: true + mode-switch: true + port: $ref: /schemas/graph.yaml#/properties/port - description: Link to the SuperSpeed lanes of the Type-C connector. + description: > + Link to the SuperSpeed lanes of the Type-C connector. Absent on a fixed + DisplayPort PHY. apple,tunable-axi2af: $ref: "#/$defs/apple,tunable" @@ -134,9 +158,24 @@ required: - reg - reg-names - "#phy-cells" - - "#reset-cells" - power-domains +allOf: + - if: + properties: + reg-names: + contains: + const: pipehandler + then: + required: + - "#reset-cells" + else: + properties: + "#reset-cells": false + orientation-switch: false + mode-switch: false + port: false + additionalProperties: false examples: @@ -170,4 +209,30 @@ examples: orientation-switch; }; }; + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + phy@39aa90000 { + compatible = "apple,t6040-atcphy"; + reg = <0x3 0x9aa90000 0x0 0x4000>, + <0x3 0x9a800000 0x0 0x4000>, + <0x3 0x9b000000 0x0 0x30000>, + <0x3 0x9b200000 0x0 0x4000>, + <0x3 0x9b140000 0x0 0x4000>, + <0x3 0x9b0c0000 0x0 0x10000>, + <0x3 0x98000000 0x0 0x8000>, + <0x3 0x98020000 0x0 0x4000>, + <0x3 0x9b244000 0x0 0x4000>, + <0x3 0x9b250000 0x0 0x4000>; + reg-names = "bank0", "bank1", "core", "cfg", "cpu", "cpu-data", + "axi2af", "axi2af-lioa", "fabric", "common-cfg"; + #phy-cells = <1>; + power-domains = <&ps_atc3_common_dp>; + mboxes = <&atcphy3_mbox>; + mbox-names = "phy-cpu"; + nonposted-mmio; + }; + }; ... From 2f53d0f4ae0cb3623595864b70617d044575e170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Bal=C3=A1zsy?= Date: Sun, 25 Oct 2026 02:10:28 +0200 Subject: [PATCH 3/3] phy: apple: t6040: DisplayPort mode Add PHY_TYPE_DP to the T6040 Type-C PHY: PHY mode 3 of the paired AppleT6040TypeCPhy driver, DisplayPort on all four lanes, for a Type-C port whose controller selected DP alt mode with pin assignment C or E, and for the fixed DP PHY of an HDMI converter, a node without the PIPE interface and the DWC3 reset line, which now probes too. Bring-up follows the paired driver's mode 3: wake the CFG block, apply the generic ADT tunables and the DPTX tunables, set the lane crossbar to DP, hold the common block and both TX lanes on by override (the PHY coprocessor does not run in this mode), release the reset, then set the lane mode and the AUX block. On a Type-C port the USB3 side is shut down first. The per-rate link sequences (RBR to HBR3) and the drive-setting presets are the T6050 fixed-DP tables, whose PHY the T6050 ADT marks as T6040-compatible ("atc-phy,t6050", "atc-phy,t6040"); the T6050 clock shim steps are replaced by the T6040 DP clock switch of the CFG window, run after the lane program as the paired driver does. Pin assignment D (two DP lanes next to USB3) is not supported. The Type-C mode switch latches whether the port is in DP alt mode; apple_t6040_phy_dp_alt() (include/linux/soc/apple/dp-tunnel.h) exposes it for a DCP that lists several Type-C DP PHYs and routes to the one with a sink plugged. --- drivers/phy/apple/Kconfig | 21 +- drivers/phy/apple/t6040-usb2.c | 708 ++++++++++++++++++++++++++-- include/linux/soc/apple/dp-tunnel.h | 8 + 3 files changed, 698 insertions(+), 39 deletions(-) diff --git a/drivers/phy/apple/Kconfig b/drivers/phy/apple/Kconfig index 10c94455f0974..5348987cf1dc8 100644 --- a/drivers/phy/apple/Kconfig +++ b/drivers/phy/apple/Kconfig @@ -32,23 +32,26 @@ config PHY_APPLE_ATC_KUNIT_TEST If unsure, say N. config PHY_APPLE_T6040_USB2 - tristate "Apple T6040 Type-C PHY (USB2 and USB3 host)" + tristate "Apple T6040 Type-C PHY (USB2/USB3 host, DisplayPort)" depends on ARM64 && ARCH_APPLE depends on TYPEC depends on APPLE_RTKIT select GENERIC_PHY select APPLE_TUNABLE help - Build the PHY provider for the T6040 generation's Type-C ports. It - implements the USB2 host sequence and the USB3 host attach decoded - from the paired AppleT6040TypeCPhy driver, including the start of - the PHY coprocessor, and provides no device mode, DisplayPort, - USB4/Thunderbolt or retimer support. + Build the PHY provider for the T6040 generation's Type-C ports and + fixed DisplayPort PHYs. It implements the USB2 host sequence and the + USB3 host attach decoded from the paired AppleT6040TypeCPhy driver, + including the start of the PHY coprocessor, and the DisplayPort mode + (all four lanes, RBR to HBR3) a Type-C controller selects through the + PHY's mode switch or that an HDMI converter's PHY runs permanently. + It provides no device mode, DisplayPort pin assignment D, USB4, + Thunderbolt or retimer support. This driver performs no ATC-register writes at probe time. Enabling a - matching device-tree node causes MMIO writes when a USB2 consumer - powers on the PHY and therefore requires platform-specific safety - review. + matching device-tree node causes MMIO writes when a USB2, USB3 or + DisplayPort consumer powers on the PHY and therefore requires + platform-specific safety review. config PHY_APPLE_DPTX tristate "Apple DPTX PHY" diff --git a/drivers/phy/apple/t6040-usb2.c b/drivers/phy/apple/t6040-usb2.c index 1a1fe530f2111..9ff40f1c11019 100644 --- a/drivers/phy/apple/t6040-usb2.c +++ b/drivers/phy/apple/t6040-usb2.c @@ -43,9 +43,12 @@ #include #include #include +#include #include #include #include +#include +#include #define T6040_USB2_USBCTL 0x00 #define T6040_USB2_USBCTL_MODE GENMASK(2, 0) @@ -102,6 +105,16 @@ #define T6040_PIPEHANDLER_NONSELECTED_BUSY BIT(12) #define T6040_PIPEHANDLER_DUMMY_PHY_READY BIT(15) +/* + * DisplayPort (PHY mode 3). Offsets are into the "core" and "cfg" windows + * the USB3 path uses; names follow the paired AppleT6040TypeCPhy driver. + */ +#define T6040_CORE_AUX_CTRL 0x28000 /* ACIOPHY_AUX_TOP_BLK_AUX_CTRL */ +#define T6040_CORE_AUX_PWR 0x28400 /* ACIOPHY_AUX_SHM_BLK_AUX_PWR_REG */ + +#define T6040_CFG_DP_CLKSWITCH_CTRL 0x14 /* ATCPHY_DP_CLKSWITCH_CONTROL_REGS */ +#define T6040_CFG_DP_CLKSWITCH_STAT 0x18 /* ATCPHY_DP_CLKSWITCH_STATUS_REGS */ + /* PHY core ("core"), atc-phy reg[2] and the blocks behind it */ #define T6040_CORE_CMN_OVRD 0x0c /* CFG0_TOP */ #define T6040_CORE_CMN_OVRD_SLEEP_B_BIG BIT(0) @@ -214,6 +227,7 @@ struct t6040_usb2_phy { struct phy *usb3; bool usb3_ok; void __iomem *regs[T6040_USB3_NR_REGIONS]; + struct resource *res[T6040_USB3_NR_REGIONS]; struct resource *cpu_data; struct apple_tunable *tunables[T6040_USB3_NR_TUNABLES]; struct apple_tunable *tunable_pipehandler; @@ -224,6 +238,15 @@ struct t6040_usb2_phy { enum typec_orientation orientation; bool usb3_up; /* tuned by the coprocessor this power cycle */ bool pipe_switched; /* the controller's PIPE runs on this PHY */ + + /* DisplayPort (PHY mode 3); dp_ok is false when the DT lacks a DP route */ + struct phy *dp; + struct typec_mux_dev *mux; + bool dp_ok; + bool dp_up; /* DP mode is programmed on the PHY */ + bool dp_alt; /* the Type-C port is in DP alt mode, pin assignment C/E */ + bool dp_flip; /* plug orientation reversed for the DP lanes */ + int dp_rate; /* link rate index of the running link sequence, -1 = off */ }; static inline void t6040_usb2_mask32(void __iomem *reg, u32 mask, u32 set) @@ -906,16 +929,13 @@ static int t6040_usb3_power_on(struct phy *phy) return 0; } -static int t6040_usb3_power_off(struct phy *phy) +/* aciophy_phy_shutdown with the lock held: lanes, coprocessor, then the CFG block. */ +static void t6040_usb3_down_locked(struct t6040_usb2_phy *tphy) { - struct t6040_usb2_phy *tphy = phy_get_drvdata(phy); - - guard(mutex)(&tphy->lock); if (tphy->pipe_switched) { t6040_usb3_pipe_park(tphy); tphy->pipe_switched = false; } - /* aciophy_phy_shutdown: lanes, coprocessor, then the CFG block */ if (tphy->usb3_up) { u32 uc = readl(t6040_core(tphy) + T6040_CORE_UC_CTRL); @@ -928,6 +948,14 @@ static int t6040_usb3_power_off(struct phy *phy) readl(t6040_cfg(tphy) + T6040_CFG_STATUS)); tphy->usb3_up = false; } +} + +static int t6040_usb3_power_off(struct phy *phy) +{ + struct t6040_usb2_phy *tphy = phy_get_drvdata(phy); + + guard(mutex)(&tphy->lock); + t6040_usb3_down_locked(tphy); return 0; } @@ -974,6 +1002,591 @@ static void t6040_usb3_switch_unregister(void *sw) typec_switch_unregister(sw); } +/* ---- DisplayPort (PHY mode 3: DP on all four lanes) ---- + * + * The fixed DP PHY of an HDMI converter (no USB controller, no pipehandler) + * and a Type-C PHY whose port the Type-C controller has put into DP alt mode + * with pin assignment C or E are brought up the same way, as the paired + * AppleT6040TypeCPhy driver does for its mode 3: wake the CFG block, apply the + * generic ADT tunables and the DPTX tunables, set the lane crossbar to DP on + * all lanes, hold the common block and both TX lanes on (the PHY coprocessor + * does not run in this mode) and release the PHY reset. The DCP then sets the + * link rate through phy_configure(): the per-rate link sequence is the T6050 + * fixed-DP table (atc-t6050-dp-data.h; the T6050 ADT names its PHY + * "atc-phy,t6050", "atc-phy,t6040"), followed by the T6040 DP clock switch in + * the "cfg" window. Pin assignment D (two DP lanes next to USB3) is not + * supported. + */ + +/* The fixed DPTX tunable writes (apply_dptx_tunables, shared with USB3). */ +static void t6040_dp_fixed_tunables(void __iomem *core) +{ + static const struct { u32 off, clear, set; } dptx[] = { + { 0x00800, 0x000003f0, 0x000001c0 }, /* AUS40CMN_DIG RCAL0 */ + { 0x00818, 0x0000f000, 0x00003000 }, /* AUS40CMN_DIG RCAL5 */ + { 0x03888, 0x000000ff, 0x00000027 }, /* CIO4PLL FREQDESCRIPTOR_0_C */ + { 0x28004, 0x00000038, 0x00000008 }, /* AUX_LDO_CTRL */ + { 0x03adc, 0x00003ff0, 0x00001760 }, /* CIO4PLL CFG_FRACN_FUNC */ + { 0x0e0cc, 0x01f80000, 0x00380000 }, /* LANE0 TX_EQ_CTRL_REG11 */ + { 0x1e0cc, 0x01f80000, 0x00380000 }, /* LANE1 TX_EQ_CTRL_REG11 */ + { 0x0d090, 0x01f80000, 0x00380000 }, /* LANE0 RX_CFG TX_EQ_CTRL_REG11 */ + { 0x1d090, 0x01f80000, 0x00380000 }, /* LANE1 RX_CFG TX_EQ_CTRL_REG11 */ + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(dptx); i++) + t6040_usb2_mask32(core + dptx[i].off, dptx[i].clear, dptx[i].set); +} + +/* aciophy_xbar_config for PHY mode 3 (DP on all four lanes). */ +static void t6040_dp_xbar_config(struct t6040_usb2_phy *tphy, bool flip) +{ + void __iomem *core = t6040_core(tphy); + + /* core+0x6c, orientation-independent for DP */ + t6040_usb2_mask32(core + T6040_CORE_XBAR, ~0xfc3fff03u, 0x48); + /* core+0x78: lanes[4:0] = 0x14, then [17:5] by orientation */ + t6040_usb2_mask32(core + T6040_CORE_XBAR_LANES, 0x1f, 0x14); + t6040_usb2_mask32(core + T6040_CORE_XBAR_LANES, 0x3ffe0, flip ? 0x100 : 0x22000); + /* core+0x6c[1:0] = 2 (common to every mode) */ + t6040_usb2_mask32(core + T6040_CORE_XBAR, 0x3, 0x2); +} + +/* + * aciophy_dptx_adjust_lane_mode: the core+0x6c lane fields [4:2] and [7:5] + * are 7 for every DP 1.x link rate (UHBR rates would use 2). + */ +static void t6040_dp_adjust_lane_mode(struct t6040_usb2_phy *tphy) +{ + t6040_usb2_set32(t6040_core(tphy) + T6040_CORE_XBAR, 0x1c | 0xe0); +} + +/* aciophy_aux_init: the DP AUX block. */ +static void t6040_dp_aux_init(struct t6040_usb2_phy *tphy) +{ + void __iomem *core = t6040_core(tphy); + + t6040_usb2_set32(core + T6040_CORE_AUX_PWR, 0x3); + t6040_usb2_set32(core + T6040_CORE_AUX_PWR, 0xc); + t6040_usb2_mask32(core + T6040_CORE_AUX_PWR, 0x300, 0x100); + t6040_usb2_clear32(core + T6040_CORE_AUX_CTRL, 0x1); +} + +/* + * aciophy_configure_dp_clks: the two DP clock switches of the "cfg" window + * (control at 0x14, status at 0x18). Each switch enables its pixel clock, + * requests the two PMGR DP clocks and releases the requests one by one once + * they are acknowledged. On the J616s the pixel-clock enable is acknowledged + * with the enable bit alone and the PMGR acknowledgements arrive with the + * 0x400 (SWITCH1) / 0x800 (SWITCH2) bit set next to the requests; the + * requests are retried without that bit should the acknowledgment not come. + */ +static int t6040_dp_configure_clks(struct t6040_usb2_phy *tphy) +{ + void __iomem *cfg = t6040_cfg(tphy); + u32 s; + int ret; + +#define DP_CLK_POLL(bit, set) \ + readl_poll_timeout(cfg + T6040_CFG_DP_CLKSWITCH_STAT, s, \ + !!(s & BIT(bit)) == (set), 10, 200000) + + /* SWITCH1: pixel clock, then the PMGR DP0/DP1 requests */ + t6040_usb2_mask32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x473, 0x10); + ret = DP_CLK_POLL(0, true); + if (ret) + goto timeout; + t6040_usb2_set32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x60 | 0x400); + ret = DP_CLK_POLL(2, true) ?: DP_CLK_POLL(1, true); + if (ret) { + dev_dbg(tphy->dev, "DP clock switch 1: no PMGR ack with 0x400 (status %#x)\n", s); + t6040_usb2_clear32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x400); + ret = DP_CLK_POLL(2, true) ?: DP_CLK_POLL(1, true); + if (ret) + goto timeout; + } + t6040_usb2_clear32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x20); + ret = DP_CLK_POLL(1, false); + if (ret) + goto timeout; + t6040_usb2_clear32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x40); + ret = DP_CLK_POLL(2, false); + if (ret) + goto timeout; + + /* SWITCH2, the same way */ + t6040_usb2_clear32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x403); + t6040_usb2_mask32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0xb8c, 0x80); + ret = DP_CLK_POLL(3, true); + if (ret) + goto timeout; + t6040_usb2_set32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x300 | 0x800); + ret = DP_CLK_POLL(5, true) ?: DP_CLK_POLL(4, true); + if (ret) { + dev_dbg(tphy->dev, "DP clock switch 2: no PMGR ack with 0x800 (status %#x)\n", s); + t6040_usb2_clear32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x800); + ret = DP_CLK_POLL(5, true) ?: DP_CLK_POLL(4, true); + if (ret) + goto timeout; + } + t6040_usb2_clear32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x100); + ret = DP_CLK_POLL(4, false); + if (ret) + goto timeout; + t6040_usb2_clear32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x200); + ret = DP_CLK_POLL(5, false); + if (ret) + goto timeout; + t6040_usb2_clear32(cfg + T6040_CFG_DP_CLKSWITCH_CTRL, 0x80c); + dev_dbg(tphy->dev, "DP clock switches on (ctrl %#x status %#x)\n", + readl(cfg + T6040_CFG_DP_CLKSWITCH_CTRL), + readl(cfg + T6040_CFG_DP_CLKSWITCH_STAT)); + return 0; + +timeout: + dev_err(tphy->dev, "DP clock switch timed out (ctrl %#x status %#x)\n", + readl(cfg + T6040_CFG_DP_CLKSWITCH_CTRL), + readl(cfg + T6040_CFG_DP_CLKSWITCH_STAT)); + return ret; +#undef DP_CLK_POLL +} + +/* A PHY without the USB3 pieces (the HDMI converter's) only ever does DP. */ +static bool t6040_dp_only(struct t6040_usb2_phy *tphy) +{ + return !tphy->usb3_ok; +} + +/* apply_tunables' generic tables: the first six of t6040_usb3_tunable_list. */ +#define T6040_DP_NR_GENERIC_TUNABLES 6 + +/* + * slpctrl_init for DP on both lanes: the cmn_rm_ovrd/txrx_rm_ovrd releases of + * t6040_usb3_lanes for lane 0 and lane 1 (USB3 releases only the plug's lane). + * Without the PHY coprocessor nothing powers the common block and the TX lanes + * once their overrides are released, so they are held on by override: the + * reverse of aciophy_cmn_shutdown / aciophy_tx_powerdown, with the override + * enables (0x2a; 0x4/0x40/0x400 per lane) set. + */ +static void t6040_dp_slpctrl(struct t6040_usb2_phy *tphy) +{ + void __iomem *core = t6040_core(tphy); + unsigned int lane; + + t6040_usb2_set32(core + T6040_CORE_CMN_BIAS, T6040_CORE_CMN_BIAS_BGBIAS_EN_OV); + udelay(1); + t6040_usb2_set32(core + T6040_CORE_CMN_OVRD, T6040_CORE_CMN_OVRD_SLEEP_B_SML | 0x2a); + udelay(1); + t6040_usb2_set32(core + T6040_CORE_CMN_OVRD, T6040_CORE_CMN_OVRD_SLEEP_B_BIG); + udelay(1); + t6040_usb2_clear32(core + T6040_CORE_CMN_OVRD, T6040_CORE_CMN_OVRD_CLAMP); + udelay(1); + for (lane = 0; lane < 2; lane++) { + t6040_usb2_set32(core + T6040_CORE_SLP_CTRL, (0x4 | 0x40 | 0x400) << lane); + t6040_usb2_set32(core + T6040_CORE_SLP_CTRL, 0x10 << lane); + udelay(1); + t6040_usb2_set32(core + T6040_CORE_SLP_CTRL, 0x1 << lane); + udelay(1); + t6040_usb2_clear32(core + T6040_CORE_SLP_CTRL, 0x100 << lane); + udelay(1); + } + dev_dbg(tphy->dev, "DP lanes held on: cmn_ovrd %#x slp_ctrl %#x bias %#x\n", + readl(core + T6040_CORE_CMN_OVRD), readl(core + T6040_CORE_SLP_CTRL), + readl(core + T6040_CORE_CMN_BIAS)); +} + +/* + * aciophy_phy_init for mode 3: wake the CFG block (shared with USB3), apply + * the generic ADT tables and the DPTX tunables, xbar_config, slpctrl_init and + * the reset deassert of partC (no coprocessor firmware in this mode). + */ +static int t6040_dp_phy_init(struct t6040_usb2_phy *tphy, bool flip) +{ + void __iomem *cfg = t6040_cfg(tphy); + size_t i; + int ret; + + ret = t6040_usb3_wake(tphy); + if (ret) + return ret; + for (i = 0; i < T6040_DP_NR_GENERIC_TUNABLES; i++) + apple_tunable_apply(tphy->regs[t6040_usb3_tunable_list[i].region], + tphy->tunables[i]); + t6040_dp_fixed_tunables(t6040_core(tphy)); + t6040_dp_xbar_config(tphy, flip); + t6040_dp_slpctrl(tphy); + t6040_usb2_set32(cfg + T6040_CFG_CTRL, T6040_CFG_CTRL_PHY_RESET_N); + fsleep(100); + dev_dbg(tphy->dev, "DP PHY init (mode 3): cfg %#x/%#x\n", + readl(cfg + T6040_CFG_CTRL), readl(cfg + T6040_CFG_STATUS)); + return 0; +} + +/* aciophy_dptx_main_init up to the link rate, which .configure sets. */ +static void t6040_dp_mode_setup(struct t6040_usb2_phy *tphy, bool flip) +{ + t6040_dp_fixed_tunables(t6040_core(tphy)); + t6040_dp_xbar_config(tphy, flip); + t6040_dp_adjust_lane_mode(tphy); + t6040_dp_aux_init(tphy); +} + +/* + * The per-rate link sequences of the T6050 fixed DP PHY, which shares the + * ATC PHY core with the T6040. Bank numbers are the atc-phy "reg" indices of + * the T6050/T6040 ADT; banks 2..34 are offsets into "core". Bank 39 is the + * T6050 clock shim; on the T6040 the DP clock switch of the "cfg" window + * stands in for it and runs after the lane program, as AppleT6040TypeCPhy + * does. The sequences hold no power-domain or tunable steps that apply here. + */ +enum atc_operation { ATC_MASK, ATC_TUNABLE, ATC_DELAY, ATC_POLL, ATC_POWER }; +struct atc_op { + u8 op, bank; + u16 offset; + u32 mask, value, timeout; +}; + +struct atc_rate_op { + struct atc_op op; + u8 rates; + u32 values[4]; +}; + +#include "atc-t6050-dp-data.h" + +static const struct { u8 bank; u32 offset; } t6040_dp_core_banks[] = { + {2, 0}, {3, 0x400}, {4, 0x800}, {5, 0xa00}, {6, 0x1000}, + {7, 0x3000}, {8, 0x3200}, {9, 0x3800}, {10, 0x3a00}, + {14, 0x7000}, {16, 0x9000}, {18, 0xb000}, {19, 0xc000}, + {20, 0xd000}, {21, 0xe000}, {22, 0xf000}, {25, 0x19000}, + {27, 0x1b000}, {28, 0x1c000}, {29, 0x1d000}, {30, 0x1e000}, + {31, 0x1f000}, {33, 0x28000}, {34, 0x28400}, +}; + +static void __iomem *t6040_dp_bank(struct t6040_usb2_phy *tphy, u8 bank) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(t6040_dp_core_banks); i++) + if (t6040_dp_core_banks[i].bank == bank) + return t6040_core(tphy) + t6040_dp_core_banks[i].offset; + return NULL; +} + +static int t6040_dp_seq_step(struct t6040_usb2_phy *tphy, const struct atc_op *s) +{ + void __iomem *reg; + u32 v; + int ret; + + if (s->op == ATC_DELAY) { + udelay(s->value); + return 0; + } + if (s->op == ATC_POWER || s->op == ATC_TUNABLE || s->bank == 39) + return 0; + reg = t6040_dp_bank(tphy, s->bank); + if (!reg) { + dev_err(tphy->dev, "DP link sequence: no bank %u\n", s->bank); + return -EINVAL; + } + reg += s->offset; + if (s->op == ATC_MASK) { + writel((readl(reg) & ~s->mask) | s->value, reg); + return 0; + } + ret = readl_poll_timeout(reg, v, (v & s->mask) == s->value, + s->timeout > 1000 ? 1000 : 1, s->timeout); + if (ret) + dev_err(tphy->dev, "DP link sequence: bank %u+%#x timed out: %#x & %#x != %#x\n", + s->bank, s->offset, v, s->mask, s->value); + return ret; +} + +/* Take the link down if it is up, then bring it up at rate index @idx (-1: off). */ +static int t6040_dp_set_rate(struct t6040_usb2_phy *tphy, int idx) +{ + size_t i; + int ret; + + if (tphy->dp_rate >= 0) { + for (i = 0; i < ARRAY_SIZE(dp_link_off); i++) { + ret = t6040_dp_seq_step(tphy, &dp_link_off[i]); + if (ret) + return ret; + } + tphy->dp_rate = -1; + } + if (idx < 0) + return 0; + for (i = 0; i < ARRAY_SIZE(dp_link_on); i++) { + struct atc_op op = dp_link_on[i].op; + + if (!(dp_link_on[i].rates & BIT(idx))) + continue; + op.value = dp_link_on[i].values[idx]; + ret = t6040_dp_seq_step(tphy, &op); + if (ret) + return ret; + } + ret = t6040_dp_configure_clks(tphy); + if (ret) + return ret; + tphy->dp_rate = idx; + dev_dbg(tphy->dev, "DP link on, rate index %d\n", idx); + return 0; +} + +/* Drive settings of the T6050 sequence: lanes 0/1 on PMA group 1, 2/3 on group 0. */ +static void t6040_dp_drive(struct t6040_usb2_phy *tphy, unsigned int lane, u32 preset) +{ + unsigned int group = lane < 2 ? 1 : 0; + bool tx = lane & 1; + void __iomem *base = t6040_dp_bank(tphy, (tx ? 21 : 20) + 9 * group); + u32 ctrl = ((preset >> 2) & 0xf) | ((preset & 3) << 14); + u32 eq = ((preset >> 5) & 0x7fffe) | 1; + + if (tx) + t6040_usb2_mask32(base + 0x100, BIT(20), BIT(20)); + t6040_usb2_mask32(base + (tx ? 0x58 : 0x68), 0xc00f, ctrl); + t6040_usb2_mask32(base + (tx ? 0x104 : 0xac), BIT(29), BIT(29)); + t6040_usb2_mask32(base + (tx ? 0x100 : 0xa8), 0x7ffff, eq); +} + +/* ---- DP phy_ops ---- */ + +static int t6040_dp_power_on(struct phy *phy) +{ + struct t6040_usb2_phy *tphy = phy_get_drvdata(phy); + int ret; + + guard(mutex)(&tphy->lock); + if (!tphy->dp_ok || tphy->dp_up) + return 0; + /* + * A Type-C PHY in DP alt mode (pin assignment C/E) hands all four + * lanes to DP: aciophy_phy_shutdown of the USB3 side first, then the + * same mode-3 init as the fixed DP PHY. + */ + if (!t6040_dp_only(tphy)) + t6040_usb3_down_locked(tphy); + ret = t6040_dp_phy_init(tphy, tphy->dp_flip); + if (ret) { + dev_err(tphy->dev, "DP PHY init failed: %d\n", ret); + return ret; + } + t6040_dp_mode_setup(tphy, tphy->dp_flip); + tphy->dp_rate = -1; + tphy->dp_up = true; + dev_dbg(tphy->dev, "DP PHY mode set, lane map %s\n", + tphy->dp_flip ? "reversed" : "normal"); + return 0; +} + +static int t6040_dp_power_off(struct phy *phy) +{ + struct t6040_usb2_phy *tphy = phy_get_drvdata(phy); + + guard(mutex)(&tphy->lock); + tphy->dp_up = false; + return 0; +} + +/* GET_MAX_LANE_COUNT: DP on all four lanes in mode 3. */ +static int t6040_dp_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts_) +{ + struct phy_configure_opts_dp *opts = &opts_->dp; + + if (mode != PHY_MODE_DP) + return -EINVAL; + opts->lanes = 4; + return 0; +} + +static int t6040_dp_configure(struct phy *phy, union phy_configure_opts *opts_) +{ + struct t6040_usb2_phy *tphy = phy_get_drvdata(phy); + struct phy_configure_opts_dp *opts = &opts_->dp; + int idx = -1; + int ret; + + guard(mutex)(&tphy->lock); + if (!tphy->dp_up) + return -EINVAL; + + if (opts->set_rate) { + switch (opts->link_rate) { + case 1620: + idx = 0; + break; + case 2700: + idx = 1; + break; + case 5400: + idx = 2; + break; + case 8100: + idx = 3; + break; + case 0: + idx = -1; + break; + default: + return -EINVAL; + } + if (idx != tphy->dp_rate) { + ret = t6040_dp_set_rate(tphy, idx); + if (ret) + return ret; + } + } + if (opts->set_voltages && tphy->dp_rate >= 0) { + unsigned int i; + + for (i = 0; i < opts->lanes && i < 4; i++) { + unsigned int v = min_t(unsigned int, opts->voltage[i], 3); + unsigned int pe = min_t(unsigned int, opts->pre[i], 3); + + t6040_dp_drive(tphy, i, dp_training_default[4 * v + pe]); + } + } + return 0; +} + +/* ACTIVATE/DEACTIVATE: PHY_MODE_DP, submode = the DCP's DPTX port index. */ +static int t6040_dp_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + if (mode == PHY_MODE_DP || mode == PHY_MODE_INVALID) + return 0; + return -EOPNOTSUPP; +} + +static const struct phy_ops t6040_dp_phy_ops = { + .owner = THIS_MODULE, + .power_on = t6040_dp_power_on, + .power_off = t6040_dp_power_off, + .configure = t6040_dp_configure, + .validate = t6040_dp_validate, + .set_mode = t6040_dp_set_mode, +}; + +/* ---- Type-C mode switch: the Type-C controller asks for DP on a USB-C port ---- */ + +static int t6040_dp_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) +{ + struct t6040_usb2_phy *tphy = typec_mux_get_drvdata(mux); + bool dp = state->alt && state->alt->svid == USB_TYPEC_DP_SID; + bool alt = false; + + guard(mutex)(&tphy->lock); + tphy->dp_flip = (tphy->orientation == TYPEC_ORIENTATION_REVERSE); + /* + * Latch the DP alt mode state for the DCP's Type-C route. Pin + * assignments C and E put DP on all four lanes (PHY mode 3, as the + * fixed DP route); D (two DP lanes next to USB3, PHY mode 4 with the + * coprocessor) is not supported. The DP lanes themselves are brought + * up by the DCP through phy_power_on()/phy_configure() on the DP phy. + */ + if (dp) { + switch (state->mode) { + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + alt = true; + break; + case TYPEC_DP_STATE_D: + dev_warn_once(tphy->dev, "Type-C mux: DP pin assignment D (two lanes next to USB3) not supported\n"); + break; + default: + break; + } + } + tphy->dp_alt = alt; + dev_dbg(tphy->dev, "Type-C mux: %s state %lu -> DP alt mode %d, lane map %s\n", + dp ? "DP" : "non-DP", state->mode, alt, tphy->dp_flip ? "reversed" : "normal"); + if (alt && tphy->dp_up) + t6040_dp_xbar_config(tphy, tphy->dp_flip); + return 0; +} + +/* For the DCP's Type-C route: is this PHY's port in DP alt mode (C/E)? */ +bool apple_t6040_phy_dp_alt(struct phy *phy) +{ + struct t6040_usb2_phy *tphy = phy_get_drvdata(phy); + + return tphy && tphy->dp_alt; +} +EXPORT_SYMBOL_GPL(apple_t6040_phy_dp_alt); + +static void t6040_dp_mux_unregister(void *mux) +{ + typec_mux_unregister(mux); +} + +/* + * Everything the DP route needs from the device tree; a node without it gets + * no DP phy. A USB-C PHY offers DP through a Type-C mode switch; a fixed DP + * route (an HDMI converter's PHY) is driven straight by the DCP with no mux + * and uses the normal lane map. + */ +static int t6040_dp_probe(struct t6040_usb2_phy *tphy) +{ + struct device *dev = tphy->dev; + unsigned int i; + + tphy->dp_rate = -1; + + /* + * DP mode needs the "core" and "cfg" windows. A USB-C PHY has them + * alongside its USB regions; a fixed DP PHY has them without any + * pipehandler or USB3 coprocessor. A node with neither is USB2 only. + */ + if (!tphy->regs[T6040_USB3_CORE] || !tphy->regs[T6040_USB3_CFG]) + return 0; + + tphy->dp = devm_phy_create(dev, NULL, &t6040_dp_phy_ops); + if (IS_ERR(tphy->dp)) + return PTR_ERR(tphy->dp); + phy_set_drvdata(tphy->dp, tphy); + + /* DP-only node: the generic apply_tunables tables (absent = not applied). */ + if (t6040_dp_only(tphy)) { + for (i = 0; i < T6040_DP_NR_GENERIC_TUNABLES; i++) { + const char *prop = t6040_usb3_tunable_list[i].prop; + + if (!of_property_present(dev->of_node, prop) || + !tphy->res[t6040_usb3_tunable_list[i].region]) { + dev_warn(dev, "DP: no %s\n", prop); + continue; + } + struct resource *res = tphy->res[t6040_usb3_tunable_list[i].region]; + + tphy->tunables[i] = devm_apple_tunable_parse(dev, dev->of_node, prop, res); + if (IS_ERR(tphy->tunables[i])) { + dev_warn(dev, "DP: %s: %pe\n", prop, tphy->tunables[i]); + tphy->tunables[i] = NULL; + } + } + } + + if (of_property_present(dev->of_node, "mode-switch")) { + struct typec_mux_desc mux_desc = { + .drvdata = tphy, + .fwnode = dev_fwnode(dev), + .set = t6040_dp_mux_set, + }; + + tphy->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(tphy->mux)) + return PTR_ERR(tphy->mux); + if (devm_add_action_or_reset(dev, t6040_dp_mux_unregister, tphy->mux)) + return -ENOMEM; + } + + tphy->dp_ok = true; + return 0; +} + /* ---- the coprocessor's RTKit ---- */ /* The coprocessor places its buffers in its own __DATA SRAM. */ @@ -1027,6 +1640,33 @@ static const struct apple_rtkit_ops t6040_cpu_rtkit_ops = { .shmem_destroy = t6040_cpu_shmem_destroy, }; +/* + * Map the PHY register windows shared by the USB3 and DisplayPort paths. Each + * is optional: a USB-C PHY has the full set, the fixed HDMI DP PHY (atcphy3) + * lacks none of core/cfg but has no pipehandler. Returns true once at least + * "core" and "cfg" are mapped, which is all DP mode needs. + */ +static bool t6040_map_regions(struct t6040_usb2_phy *tphy, struct platform_device *pdev) +{ + struct device *dev = tphy->dev; + size_t i; + + for (i = 0; i < T6040_USB3_NR_REGIONS; i++) { + tphy->res[i] = platform_get_resource_byname(pdev, IORESOURCE_MEM, + t6040_usb3_region_names[i]); + if (!tphy->res[i]) + continue; + tphy->regs[i] = devm_ioremap_resource(dev, tphy->res[i]); + if (IS_ERR(tphy->regs[i])) { + dev_warn(dev, "\"%s\" region: %pe\n", + t6040_usb3_region_names[i], tphy->regs[i]); + tphy->regs[i] = NULL; + tphy->res[i] = NULL; + } + } + return tphy->regs[T6040_USB3_CORE] && tphy->regs[T6040_USB3_CFG]; +} + /* * Everything USB3 needs from the device tree. A missing piece leaves the * port at USB2; only a mailbox that has not probed yet defers. @@ -1040,7 +1680,6 @@ static int t6040_usb3_probe(struct t6040_usb2_phy *tphy, struct platform_device .set = t6040_usb3_sw_set, }; struct typec_switch_dev *sw; - struct resource *res[T6040_USB3_NR_REGIONS]; struct resource *ph; size_t i; int ret; @@ -1066,19 +1705,11 @@ static int t6040_usb3_probe(struct t6040_usb2_phy *tphy, struct platform_device return ret; for (i = 0; i < T6040_USB3_NR_REGIONS; i++) { - res[i] = platform_get_resource_byname(pdev, IORESOURCE_MEM, - t6040_usb3_region_names[i]); - if (!res[i]) { + if (!tphy->regs[i]) { dev_warn(dev, "USB3: no \"%s\" region, USB2 only\n", t6040_usb3_region_names[i]); return 0; } - tphy->regs[i] = devm_ioremap_resource(dev, res[i]); - if (IS_ERR(tphy->regs[i])) { - dev_warn(dev, "USB3: \"%s\" region: %pe, USB2 only\n", - t6040_usb3_region_names[i], tphy->regs[i]); - return 0; - } } tphy->cpu_data = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cpu-data"); ph = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pipehandler"); @@ -1090,7 +1721,7 @@ static int t6040_usb3_probe(struct t6040_usb2_phy *tphy, struct platform_device for (i = 0; i < T6040_USB3_NR_TUNABLES; i++) { tphy->tunables[i] = devm_apple_tunable_parse(dev, dev->of_node, t6040_usb3_tunable_list[i].prop, - res[t6040_usb3_tunable_list[i].region]); + tphy->res[t6040_usb3_tunable_list[i].region]); if (IS_ERR(tphy->tunables[i])) { dev_warn(dev, "USB3: %s: %pe, USB2 only\n", t6040_usb3_tunable_list[i].prop, tphy->tunables[i]); @@ -1206,6 +1837,8 @@ static struct phy *t6040_usb2_xlate(struct device *dev, return tphy->phy; case PHY_TYPE_USB3: return tphy->usb3; + case PHY_TYPE_DP: + return tphy->dp; default: return ERR_PTR(-ENODEV); } @@ -1267,22 +1900,37 @@ static int t6040_usb2_probe(struct platform_device *pdev) return PTR_ERR(tphy->phy); phy_set_drvdata(tphy->phy, tphy); - tphy->pipehandler = devm_platform_ioremap_resource_byname(pdev, "pipehandler"); - if (IS_ERR(tphy->pipehandler)) - return dev_err_probe(dev, PTR_ERR(tphy->pipehandler), - "failed to map pipehandler bank\n"); - - tphy->rcdev.owner = THIS_MODULE; - tphy->rcdev.nr_resets = 1; - tphy->rcdev.ops = &t6040_usb2_reset_ops; - tphy->rcdev.of_node = dev->of_node; - tphy->rcdev.of_reset_n_cells = 0; - tphy->rcdev.of_xlate = t6040_usb2_reset_xlate; - ret = devm_reset_controller_register(dev, &tphy->rcdev); + /* + * The USB-C PHYs have a pipehandler and drive a dwc3 (reset provider); + * the fixed HDMI DP PHY (atcphy3) has neither. Map it and register the + * reset controller only when present, so a DP-only node still probes. + */ + if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "pipehandler")) { + tphy->pipehandler = devm_platform_ioremap_resource_byname(pdev, + "pipehandler"); + if (IS_ERR(tphy->pipehandler)) + return dev_err_probe(dev, PTR_ERR(tphy->pipehandler), + "failed to map pipehandler bank\n"); + + tphy->rcdev.owner = THIS_MODULE; + tphy->rcdev.nr_resets = 1; + tphy->rcdev.ops = &t6040_usb2_reset_ops; + tphy->rcdev.of_node = dev->of_node; + tphy->rcdev.of_reset_n_cells = 0; + tphy->rcdev.of_xlate = t6040_usb2_reset_xlate; + ret = devm_reset_controller_register(dev, &tphy->rcdev); + if (ret) + return ret; + } + + /* Map the core/cfg (and USB3) windows once; both sub-probes share them. */ + t6040_map_regions(tphy, pdev); + + ret = t6040_usb3_probe(tphy, pdev); if (ret) return ret; - ret = t6040_usb3_probe(tphy, pdev); + ret = t6040_dp_probe(tphy); if (ret) return ret; @@ -1306,5 +1954,5 @@ static struct platform_driver t6040_usb2_driver = { module_platform_driver(t6040_usb2_driver); MODULE_AUTHOR("The Asahi Linux Contributors"); -MODULE_DESCRIPTION("Apple T6040 Type-C PHY, USB2 and USB3 host"); +MODULE_DESCRIPTION("Apple T6040 Type-C PHY, USB2/USB3 host and DisplayPort"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/include/linux/soc/apple/dp-tunnel.h b/include/linux/soc/apple/dp-tunnel.h index 7bd546080f25b..194a238927190 100644 --- a/include/linux/soc/apple/dp-tunnel.h +++ b/include/linux/soc/apple/dp-tunnel.h @@ -51,6 +51,14 @@ int apple_atc_dp_tunnel_rate(struct phy *phy, unsigned int dpin, u8 rate); */ int apple_atc_dp_tunnel_open(struct phy *phy); +/* + * T6040 Type-C PHY: true while the Type-C controller holds the PHY's port in + * DisplayPort alt mode with pin assignment C or E (DP on all four lanes), so + * appledrm can route a DCP that lists several Type-C DP PHYs to the one that + * has a DisplayPort sink plugged. + */ +bool apple_t6040_phy_dp_alt(struct phy *phy); + /* * Display crossbar: take the connection of an already selected output down or * bring it back up (clock gates and enables) around a link reconfiguration.