* [PATCH 00/11] Apple Silicon USB3 support - TIPD changes
@ 2025-09-14 12:56 Sven Peter
2025-09-14 12:56 ` [PATCH 01/11] usb: typec: tipd: Clear interrupts first Sven Peter
` (10 more replies)
0 siblings, 11 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter, stable, Hector Martin
This series contains the required TIPD changes to support USB3 on Apple
Silicon machines and also prepares DisplayPort alternate mode and
Thunderbolt/USB4 support there.
As discussed in the series combining TIPD, DWC3, PHY, and DTS changes
this series contains only the TIPD changes which are already in a pretty
decent shape.
Link to v2 of the combined series: https://lore.kernel.org/asahi/20250906-atcphy-6-17-v2-0-52c348623ef6@kernel.org/
Changes since v2 of that series:
- Added r-b tags from Heikki and Neal
- Addressed Janne's feedback:
- Updated the tipd commit description to match the dwc3 glue driver
approach
- Removed a stale comment left-over from a WIP version and no longer
required
- Cleared cd321x->state.data after typec_set_mode is done
- Removed spurious NULL checks for typec_mux_put and typec_unregister_altmode
- Call cd321x_connect instead of open-coding almost the same logic
inside cd321x_interrupt
Best,
Sven
Signed-off-by: Sven Peter <sven@kernel.org>
---
Hector Martin (4):
usb: typec: tipd: Update partner identity when power status was updated
usb: typec: tipd: Use read_power_status function in probe
usb: typec: tipd: Read data status in probe and cache its value
usb: typec: tipd: Handle mode transitions for CD321x
Sven Peter (7):
usb: typec: tipd: Clear interrupts first
usb: typec: tipd: Move initial irq mask to tipd_data
usb: typec: tipd: Move switch_power_state to tipd_data
usb: typec: tipd: Trace data status for CD321x correctly
usb: typec: tipd: Add cd321x struct with separate size
usb: typec: tipd: Read USB4, Thunderbolt and DisplayPort status for cd321x
usb: typec: tipd: Register DisplayPort and Thunderbolt altmodes for cd321x
drivers/usb/typec/tipd/core.c | 559 ++++++++++++++++++++++++++++++++++----
drivers/usb/typec/tipd/tps6598x.h | 5 +
drivers/usb/typec/tipd/trace.h | 39 +++
3 files changed, 550 insertions(+), 53 deletions(-)
---
base-commit: 8f5ae30d69d7543eee0d70083daf4de8fe15d585
change-id: 20250914-apple-usb3-tipd-5907537d1eb5
Best regards,
--
Sven Peter <sven@kernel.org>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 01/11] usb: typec: tipd: Clear interrupts first
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 02/11] usb: typec: tipd: Move initial irq mask to tipd_data Sven Peter
` (9 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter, stable
Right now the interrupt handler first reads all updated status registers
and only then clears the interrupts. It's possible that a duplicate
interrupt for a changed register or plug state comes in after the
interrupts have been processed but before they have been cleared:
* plug is inserted, TPS_REG_INT_PLUG_EVENT is set
* TPS_REG_INT_EVENT1 is read
* tps6598x_handle_plug_event() has run and registered the plug
* plug is removed again, TPS_REG_INT_PLUG_EVENT is set (again)
* TPS_REG_INT_CLEAR1 is written, TPS_REG_INT_PLUG_EVENT is cleared
We then have no plug connected and no pending interrupt but the tipd
core still thinks there is a plug. It's possible to trigger this with
e.g. a slightly broken Type-C to USB A converter.
Fix this by first clearing the interrupts and only then reading the
updated registers.
Fixes: 45188f27b3d0 ("usb: typec: tipd: Add support for Apple CD321X")
Fixes: 0a4c005bd171 ("usb: typec: driver for TI TPS6598x USB Power Delivery controllers")
Cc: stable@kernel.org
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 24 +++++++++++-------------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index dcf141ada07812295a6f07e41d77f95f98116010..1c80296c3b273e24ceacb3feff432c4f6e6835cc 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -545,24 +545,23 @@ static irqreturn_t cd321x_interrupt(int irq, void *data)
if (!event)
goto err_unlock;
+ tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event);
+
if (!tps6598x_read_status(tps, &status))
- goto err_clear_ints;
+ goto err_unlock;
if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE)
if (!tps6598x_read_power_status(tps))
- goto err_clear_ints;
+ goto err_unlock;
if (event & APPLE_CD_REG_INT_DATA_STATUS_UPDATE)
if (!tps6598x_read_data_status(tps))
- goto err_clear_ints;
+ goto err_unlock;
/* Handle plug insert or removal */
if (event & APPLE_CD_REG_INT_PLUG_EVENT)
tps6598x_handle_plug_event(tps, status);
-err_clear_ints:
- tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event);
-
err_unlock:
mutex_unlock(&tps->lock);
@@ -668,25 +667,24 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data)
if (!(event1[0] | event1[1] | event2[0] | event2[1]))
goto err_unlock;
+ tps6598x_block_write(tps, TPS_REG_INT_CLEAR1, event1, intev_len);
+ tps6598x_block_write(tps, TPS_REG_INT_CLEAR2, event2, intev_len);
+
if (!tps6598x_read_status(tps, &status))
- goto err_clear_ints;
+ goto err_unlock;
if ((event1[0] | event2[0]) & TPS_REG_INT_POWER_STATUS_UPDATE)
if (!tps6598x_read_power_status(tps))
- goto err_clear_ints;
+ goto err_unlock;
if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE)
if (!tps6598x_read_data_status(tps))
- goto err_clear_ints;
+ goto err_unlock;
/* Handle plug insert or removal */
if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT)
tps6598x_handle_plug_event(tps, status);
-err_clear_ints:
- tps6598x_block_write(tps, TPS_REG_INT_CLEAR1, event1, intev_len);
- tps6598x_block_write(tps, TPS_REG_INT_CLEAR2, event2, intev_len);
-
err_unlock:
mutex_unlock(&tps->lock);
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 02/11] usb: typec: tipd: Move initial irq mask to tipd_data
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
2025-09-14 12:56 ` [PATCH 01/11] usb: typec: tipd: Clear interrupts first Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 03/11] usb: typec: tipd: Move switch_power_state " Sven Peter
` (8 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter
Since the irq mask was originally added more tipd variants have been
introduced and there's now struct tipd_data. Move the initial mask in
there.
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index 1c80296c3b273e24ceacb3feff432c4f6e6835cc..6d8bcbc9cad8a1394e066504d4c5ca570edd4e4f 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -112,6 +112,7 @@ struct tps6598x;
struct tipd_data {
irq_handler_t irq_handler;
+ u64 irq_mask1;
int (*register_port)(struct tps6598x *tps, struct fwnode_handle *node);
void (*trace_power_status)(u16 status);
void (*trace_status)(u32 status);
@@ -1298,7 +1299,6 @@ static int tps6598x_probe(struct i2c_client *client)
u32 status;
u32 vid;
int ret;
- u64 mask1;
tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
if (!tps)
@@ -1337,16 +1337,6 @@ static int tps6598x_probe(struct i2c_client *client)
if (ret)
return ret;
- /* CD321X chips have all interrupts masked initially */
- mask1 = APPLE_CD_REG_INT_POWER_STATUS_UPDATE |
- APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
- APPLE_CD_REG_INT_PLUG_EVENT;
-
- } else {
- /* Enable power status, data status and plug event interrupts */
- mask1 = TPS_REG_INT_POWER_STATUS_UPDATE |
- TPS_REG_INT_DATA_STATUS_UPDATE |
- TPS_REG_INT_PLUG_EVENT;
}
tps->data = i2c_get_match_data(client);
@@ -1364,7 +1354,7 @@ static int tps6598x_probe(struct i2c_client *client)
return ret;
}
- ret = tps6598x_write64(tps, TPS_REG_INT_MASK1, mask1);
+ ret = tps6598x_write64(tps, TPS_REG_INT_MASK1, tps->data->irq_mask1);
if (ret)
goto err_reset_controller;
@@ -1527,6 +1517,9 @@ static const struct dev_pm_ops tps6598x_pm_ops = {
static const struct tipd_data cd321x_data = {
.irq_handler = cd321x_interrupt,
+ .irq_mask1 = APPLE_CD_REG_INT_POWER_STATUS_UPDATE |
+ APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
+ APPLE_CD_REG_INT_PLUG_EVENT,
.register_port = tps6598x_register_port,
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
@@ -1536,6 +1529,9 @@ static const struct tipd_data cd321x_data = {
static const struct tipd_data tps6598x_data = {
.irq_handler = tps6598x_interrupt,
+ .irq_mask1 = TPS_REG_INT_POWER_STATUS_UPDATE |
+ TPS_REG_INT_DATA_STATUS_UPDATE |
+ TPS_REG_INT_PLUG_EVENT,
.register_port = tps6598x_register_port,
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
@@ -1546,6 +1542,9 @@ static const struct tipd_data tps6598x_data = {
static const struct tipd_data tps25750_data = {
.irq_handler = tps25750_interrupt,
+ .irq_mask1 = TPS_REG_INT_POWER_STATUS_UPDATE |
+ TPS_REG_INT_DATA_STATUS_UPDATE |
+ TPS_REG_INT_PLUG_EVENT,
.register_port = tps25750_register_port,
.trace_power_status = trace_tps25750_power_status,
.trace_status = trace_tps25750_status,
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 03/11] usb: typec: tipd: Move switch_power_state to tipd_data
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
2025-09-14 12:56 ` [PATCH 01/11] usb: typec: tipd: Clear interrupts first Sven Peter
2025-09-14 12:56 ` [PATCH 02/11] usb: typec: tipd: Move initial irq mask to tipd_data Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 04/11] usb: typec: tipd: Trace data status for CD321x correctly Sven Peter
` (7 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter
When support for CD321x was originally added no other hardware variant
was supported and there was no need for struct tipd_data. Now that it
exists move the special case in there so that we can drop the
of_device_is_compatible_check entirely.
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index 6d8bcbc9cad8a1394e066504d4c5ca570edd4e4f..4815c5c462837865a5f9d37bbc139249c82c2f75 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -118,6 +118,7 @@ struct tipd_data {
void (*trace_status)(u32 status);
int (*apply_patch)(struct tps6598x *tps);
int (*init)(struct tps6598x *tps);
+ int (*switch_power_state)(struct tps6598x *tps, u8 target_state);
int (*reset)(struct tps6598x *tps);
};
@@ -1293,7 +1294,6 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
static int tps6598x_probe(struct i2c_client *client)
{
- struct device_node *np = client->dev.of_node;
struct tps6598x *tps;
struct fwnode_handle *fwnode;
u32 status;
@@ -1331,18 +1331,16 @@ static int tps6598x_probe(struct i2c_client *client)
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
tps->i2c_protocol = true;
- if (np && of_device_is_compatible(np, "apple,cd321x")) {
- /* Switch CD321X chips to the correct system power state */
- ret = cd321x_switch_power_state(tps, TPS_SYSTEM_POWER_STATE_S0);
- if (ret)
- return ret;
-
- }
-
tps->data = i2c_get_match_data(client);
if (!tps->data)
return -EINVAL;
+ if (tps->data->switch_power_state) {
+ ret = tps->data->switch_power_state(tps, TPS_SYSTEM_POWER_STATE_S0);
+ if (ret)
+ return ret;
+ }
+
/* Make sure the controller has application firmware running */
ret = tps6598x_check_mode(tps);
if (ret < 0)
@@ -1525,6 +1523,7 @@ static const struct tipd_data cd321x_data = {
.trace_status = trace_tps6598x_status,
.init = cd321x_init,
.reset = cd321x_reset,
+ .switch_power_state = cd321x_switch_power_state,
};
static const struct tipd_data tps6598x_data = {
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 04/11] usb: typec: tipd: Trace data status for CD321x correctly
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
` (2 preceding siblings ...)
2025-09-14 12:56 ` [PATCH 03/11] usb: typec: tipd: Move switch_power_state " Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 05/11] usb: typec: tipd: Add cd321x struct with separate size Sven Peter
` (6 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter
Some bits inside the CD321x TPS_DATA_STATUS register have a different
function compared to the original tipd chip. Add these and introduce a
separate trace function to show them correctly.
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 8 +++++++-
drivers/usb/typec/tipd/tps6598x.h | 5 +++++
drivers/usb/typec/tipd/trace.h | 39 +++++++++++++++++++++++++++++++++++++++
3 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index 4815c5c462837865a5f9d37bbc139249c82c2f75..19d713937870304e68325a441b0de63eb5db3b80 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -114,6 +114,7 @@ struct tipd_data {
irq_handler_t irq_handler;
u64 irq_mask1;
int (*register_port)(struct tps6598x *tps, struct fwnode_handle *node);
+ void (*trace_data_status)(u32 status);
void (*trace_power_status)(u16 status);
void (*trace_status)(u32 status);
int (*apply_patch)(struct tps6598x *tps);
@@ -492,7 +493,9 @@ static bool tps6598x_read_data_status(struct tps6598x *tps)
dev_err(tps->dev, "failed to read data status: %d\n", ret);
return false;
}
- trace_tps6598x_data_status(data_status);
+
+ if (tps->data->trace_data_status)
+ tps->data->trace_data_status(data_status);
return true;
}
@@ -1519,6 +1522,7 @@ static const struct tipd_data cd321x_data = {
APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
APPLE_CD_REG_INT_PLUG_EVENT,
.register_port = tps6598x_register_port,
+ .trace_data_status = trace_cd321x_data_status,
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
.init = cd321x_init,
@@ -1532,6 +1536,7 @@ static const struct tipd_data tps6598x_data = {
TPS_REG_INT_DATA_STATUS_UPDATE |
TPS_REG_INT_PLUG_EVENT,
.register_port = tps6598x_register_port,
+ .trace_data_status = trace_tps6598x_data_status,
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
.apply_patch = tps6598x_apply_patch,
@@ -1545,6 +1550,7 @@ static const struct tipd_data tps25750_data = {
TPS_REG_INT_DATA_STATUS_UPDATE |
TPS_REG_INT_PLUG_EVENT,
.register_port = tps25750_register_port,
+ .trace_data_status = trace_tps6598x_data_status,
.trace_power_status = trace_tps25750_power_status,
.trace_status = trace_tps25750_status,
.apply_patch = tps25750_apply_patch,
diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h
index cecb8d11d23972dab0d8c15458b4052af7510b03..03edbb77bbd6d8093b2560db83e5913e25d06154 100644
--- a/drivers/usb/typec/tipd/tps6598x.h
+++ b/drivers/usb/typec/tipd/tps6598x.h
@@ -197,6 +197,11 @@
#define TPS_DATA_STATUS_FORCE_LSX BIT(23)
#define TPS_DATA_STATUS_POWER_MISMATCH BIT(24)
+/* modified TPS_REG_DATA_STATUS bits for CD321x (and likely also TPS65987DDK) */
+#define CD321X_DATA_STATUS_HPD_IRQ BIT(14)
+#define CD321X_DATA_STATUS_HPD_LEVEL BIT(15)
+#define CD321X_DATA_STATUS_USB4_CONNECTION BIT(23)
+
#define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK GENMASK(11, 10)
#define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT(x) \
TPS_FIELD_GET(TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK, (x))
diff --git a/drivers/usb/typec/tipd/trace.h b/drivers/usb/typec/tipd/trace.h
index bea383f2db9de5bbf1804fbad9ee6b134407b932..e9e40425138a01f15e35867f38f62e13623dbcec 100644
--- a/drivers/usb/typec/tipd/trace.h
+++ b/drivers/usb/typec/tipd/trace.h
@@ -217,6 +217,26 @@
{ TPS_DATA_STATUS_FORCE_LSX, "FORCE_LSX" }, \
{ TPS_DATA_STATUS_POWER_MISMATCH, "POWER_MISMATCH" })
+#define show_cd321x_data_status_flags(data_status) \
+ __print_flags(data_status & TPS_DATA_STATUS_FLAGS_MASK, "|", \
+ { TPS_DATA_STATUS_DATA_CONNECTION, "DATA_CONNECTION" }, \
+ { TPS_DATA_STATUS_UPSIDE_DOWN, "DATA_UPSIDE_DOWN" }, \
+ { TPS_DATA_STATUS_ACTIVE_CABLE, "ACTIVE_CABLE" }, \
+ { TPS_DATA_STATUS_USB2_CONNECTION, "USB2_CONNECTION" }, \
+ { TPS_DATA_STATUS_USB3_CONNECTION, "USB3_CONNECTION" }, \
+ { TPS_DATA_STATUS_USB3_GEN2, "USB3_GEN2" }, \
+ { TPS_DATA_STATUS_USB_DATA_ROLE, "USB_DATA_ROLE" }, \
+ { TPS_DATA_STATUS_DP_CONNECTION, "DP_CONNECTION" }, \
+ { TPS_DATA_STATUS_DP_SINK, "DP_SINK" }, \
+ { CD321X_DATA_STATUS_HPD_IRQ, "HPD_IRQ" }, \
+ { CD321X_DATA_STATUS_HPD_LEVEL, "HPD_LEVEL" }, \
+ { TPS_DATA_STATUS_TBT_CONNECTION, "TBT_CONNECTION" }, \
+ { TPS_DATA_STATUS_TBT_TYPE, "TBT_TYPE" }, \
+ { TPS_DATA_STATUS_OPTICAL_CABLE, "OPTICAL_CABLE" }, \
+ { TPS_DATA_STATUS_ACTIVE_LINK_TRAIN, "ACTIVE_LINK_TRAIN" }, \
+ { CD321X_DATA_STATUS_USB4_CONNECTION, "USB4" }, \
+ { TPS_DATA_STATUS_POWER_MISMATCH, "POWER_MISMATCH" })
+
#define show_data_status_dp_pin_assignment(data_status) \
__print_symbolic(TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT(data_status), \
{ TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_E, "E" }, \
@@ -388,6 +408,25 @@ TRACE_EVENT(tps6598x_data_status,
)
);
+TRACE_EVENT(cd321x_data_status,
+ TP_PROTO(u32 data_status),
+ TP_ARGS(data_status),
+
+ TP_STRUCT__entry(
+ __field(u32, data_status)
+ ),
+
+ TP_fast_assign(
+ __entry->data_status = data_status;
+ ),
+
+ TP_printk("%s%s%s",
+ show_cd321x_data_status_flags(__entry->data_status),
+ __entry->data_status & TPS_DATA_STATUS_DP_CONNECTION ? ", DP pinout " : "",
+ maybe_show_data_status_dp_pin_assignment(__entry->data_status)
+ )
+);
+
#endif /* _TPS6598X_TRACE_H_ */
/* This part must be outside protection */
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 05/11] usb: typec: tipd: Add cd321x struct with separate size
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
` (3 preceding siblings ...)
2025-09-14 12:56 ` [PATCH 04/11] usb: typec: tipd: Trace data status for CD321x correctly Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 06/11] usb: typec: tipd: Read USB4, Thunderbolt and DisplayPort status for cd321x Sven Peter
` (5 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter
We're about to add more fields to struct tps6598x which are only relevant
for Apple's CD321x and to ensure that we don't waste memory everywhere for
those add a separate struct for cd321x and prepare to allocate more space
inside probe.
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index 19d713937870304e68325a441b0de63eb5db3b80..51b0f3be8b66a743ddc3ea96c1b25f597a1e8f6c 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -113,6 +113,7 @@ struct tps6598x;
struct tipd_data {
irq_handler_t irq_handler;
u64 irq_mask1;
+ size_t tps_struct_size;
int (*register_port)(struct tps6598x *tps, struct fwnode_handle *node);
void (*trace_data_status)(u32 status);
void (*trace_power_status)(u16 status);
@@ -148,6 +149,10 @@ struct tps6598x {
const struct tipd_data *data;
};
+struct cd321x {
+ struct tps6598x tps;
+};
+
static enum power_supply_property tps6598x_psy_props[] = {
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_ONLINE,
@@ -1297,18 +1302,24 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
static int tps6598x_probe(struct i2c_client *client)
{
+ const struct tipd_data *data;
struct tps6598x *tps;
struct fwnode_handle *fwnode;
u32 status;
u32 vid;
int ret;
- tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ data = i2c_get_match_data(client);
+ if (!data)
+ return -EINVAL;
+
+ tps = devm_kzalloc(&client->dev, data->tps_struct_size, GFP_KERNEL);
if (!tps)
return -ENOMEM;
mutex_init(&tps->lock);
tps->dev = &client->dev;
+ tps->data = data;
tps->reset = devm_gpiod_get_optional(tps->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(tps->reset))
@@ -1334,10 +1345,6 @@ static int tps6598x_probe(struct i2c_client *client)
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
tps->i2c_protocol = true;
- tps->data = i2c_get_match_data(client);
- if (!tps->data)
- return -EINVAL;
-
if (tps->data->switch_power_state) {
ret = tps->data->switch_power_state(tps, TPS_SYSTEM_POWER_STATE_S0);
if (ret)
@@ -1521,6 +1528,7 @@ static const struct tipd_data cd321x_data = {
.irq_mask1 = APPLE_CD_REG_INT_POWER_STATUS_UPDATE |
APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
APPLE_CD_REG_INT_PLUG_EVENT,
+ .tps_struct_size = sizeof(struct cd321x),
.register_port = tps6598x_register_port,
.trace_data_status = trace_cd321x_data_status,
.trace_power_status = trace_tps6598x_power_status,
@@ -1535,6 +1543,7 @@ static const struct tipd_data tps6598x_data = {
.irq_mask1 = TPS_REG_INT_POWER_STATUS_UPDATE |
TPS_REG_INT_DATA_STATUS_UPDATE |
TPS_REG_INT_PLUG_EVENT,
+ .tps_struct_size = sizeof(struct tps6598x),
.register_port = tps6598x_register_port,
.trace_data_status = trace_tps6598x_data_status,
.trace_power_status = trace_tps6598x_power_status,
@@ -1549,6 +1558,7 @@ static const struct tipd_data tps25750_data = {
.irq_mask1 = TPS_REG_INT_POWER_STATUS_UPDATE |
TPS_REG_INT_DATA_STATUS_UPDATE |
TPS_REG_INT_PLUG_EVENT,
+ .tps_struct_size = sizeof(struct tps6598x),
.register_port = tps25750_register_port,
.trace_data_status = trace_tps6598x_data_status,
.trace_power_status = trace_tps25750_power_status,
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 06/11] usb: typec: tipd: Read USB4, Thunderbolt and DisplayPort status for cd321x
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
` (4 preceding siblings ...)
2025-09-14 12:56 ` [PATCH 05/11] usb: typec: tipd: Add cd321x struct with separate size Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 07/11] usb: typec: tipd: Register DisplayPort and Thunderbolt altmodes " Sven Peter
` (4 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter
CD321x supports various alternate modes and stores information once
these are entered into separate status registers. Read those when they
are active when reading TPS_DATA_STATUS to prepare supporting these.
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 80 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 76 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index 51b0f3be8b66a743ddc3ea96c1b25f597a1e8f6c..afd11b3e1ae596c7f3283e4336aaa57874c9378d 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -35,14 +35,18 @@
#define TPS_REG_INT_MASK2 0x17
#define TPS_REG_INT_CLEAR1 0x18
#define TPS_REG_INT_CLEAR2 0x19
-#define TPS_REG_SYSTEM_POWER_STATE 0x20
#define TPS_REG_STATUS 0x1a
+#define TPS_REG_SYSTEM_POWER_STATE 0x20
+#define TPS_REG_USB4_STATUS 0x24
#define TPS_REG_SYSTEM_CONF 0x28
#define TPS_REG_CTRL_CONF 0x29
#define TPS_REG_BOOT_STATUS 0x2D
#define TPS_REG_POWER_STATUS 0x3f
#define TPS_REG_PD_STATUS 0x40
#define TPS_REG_RX_IDENTITY_SOP 0x48
+#define TPS_REG_CF_VID_STATUS 0x5e
+#define TPS_REG_DP_SID_STATUS 0x58
+#define TPS_REG_INTEL_VID_STATUS 0x59
#define TPS_REG_DATA_STATUS 0x5f
#define TPS_REG_SLEEP_CONF 0x70
@@ -85,6 +89,31 @@ struct tps6598x_rx_identity_reg {
struct usb_pd_identity identity;
} __packed;
+/* TPS_REG_USB4_STATUS */
+struct tps6598x_usb4_status_reg {
+ u8 mode_status;
+ __le32 eudo;
+ __le32 unknown;
+} __packed;
+
+/* TPS_REG_DP_SID_STATUS */
+struct tps6598x_dp_sid_status_reg {
+ u8 mode_status;
+ __le32 status_tx;
+ __le32 status_rx;
+ __le32 configure;
+ __le32 mode_data;
+} __packed;
+
+/* TPS_REG_INTEL_VID_STATUS */
+struct tps6598x_intel_vid_status_reg {
+ u8 mode_status;
+ __le32 attention_vdo;
+ __le16 enter_vdo;
+ __le16 device_mode;
+ __le16 cable_mode;
+} __packed;
+
/* Standard Task return codes */
#define TPS_TASK_TIMEOUT 1
#define TPS_TASK_REJECTED 3
@@ -121,6 +150,7 @@ struct tipd_data {
int (*apply_patch)(struct tps6598x *tps);
int (*init)(struct tps6598x *tps);
int (*switch_power_state)(struct tps6598x *tps, u8 target_state);
+ bool (*read_data_status)(struct tps6598x *tps);
int (*reset)(struct tps6598x *tps);
};
@@ -151,6 +181,10 @@ struct tps6598x {
struct cd321x {
struct tps6598x tps;
+
+ struct tps6598x_dp_sid_status_reg dp_sid_status;
+ struct tps6598x_intel_vid_status_reg intel_vid_status;
+ struct tps6598x_usb4_status_reg usb4_status;
};
static enum power_supply_property tps6598x_psy_props[] = {
@@ -505,6 +539,41 @@ static bool tps6598x_read_data_status(struct tps6598x *tps)
return true;
}
+static bool cd321x_read_data_status(struct tps6598x *tps)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+ int ret;
+
+ ret = tps6598x_read_data_status(tps);
+ if (ret < 0)
+ return false;
+
+ if (tps->data_status & TPS_DATA_STATUS_DP_CONNECTION) {
+ ret = tps6598x_block_read(tps, TPS_REG_DP_SID_STATUS,
+ &cd321x->dp_sid_status, sizeof(cd321x->dp_sid_status));
+ if (ret)
+ dev_err(tps->dev, "Failed to read DP SID Status: %d\n",
+ ret);
+ }
+
+ if (tps->data_status & TPS_DATA_STATUS_TBT_CONNECTION) {
+ ret = tps6598x_block_read(tps, TPS_REG_INTEL_VID_STATUS,
+ &cd321x->intel_vid_status, sizeof(cd321x->intel_vid_status));
+ if (ret)
+ dev_err(tps->dev, "Failed to read Intel VID Status: %d\n", ret);
+ }
+
+ if (tps->data_status & CD321X_DATA_STATUS_USB4_CONNECTION) {
+ ret = tps6598x_block_read(tps, TPS_REG_USB4_STATUS,
+ &cd321x->usb4_status, sizeof(cd321x->usb4_status));
+ if (ret)
+ dev_err(tps->dev,
+ "Failed to read USB4 Status: %d\n", ret);
+ }
+
+ return true;
+}
+
static bool tps6598x_read_power_status(struct tps6598x *tps)
{
u16 pwr_status;
@@ -565,7 +634,7 @@ static irqreturn_t cd321x_interrupt(int irq, void *data)
goto err_unlock;
if (event & APPLE_CD_REG_INT_DATA_STATUS_UPDATE)
- if (!tps6598x_read_data_status(tps))
+ if (!tps->data->read_data_status(tps))
goto err_unlock;
/* Handle plug insert or removal */
@@ -614,7 +683,7 @@ static irqreturn_t tps25750_interrupt(int irq, void *data)
goto err_clear_ints;
if (event[0] & TPS_REG_INT_DATA_STATUS_UPDATE)
- if (!tps6598x_read_data_status(tps))
+ if (!tps->data->read_data_status(tps))
goto err_clear_ints;
/*
@@ -688,7 +757,7 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data)
goto err_unlock;
if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE)
- if (!tps6598x_read_data_status(tps))
+ if (!tps->data->read_data_status(tps))
goto err_unlock;
/* Handle plug insert or removal */
@@ -1534,6 +1603,7 @@ static const struct tipd_data cd321x_data = {
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
.init = cd321x_init,
+ .read_data_status = cd321x_read_data_status,
.reset = cd321x_reset,
.switch_power_state = cd321x_switch_power_state,
};
@@ -1550,6 +1620,7 @@ static const struct tipd_data tps6598x_data = {
.trace_status = trace_tps6598x_status,
.apply_patch = tps6598x_apply_patch,
.init = tps6598x_init,
+ .read_data_status = tps6598x_read_data_status,
.reset = tps6598x_reset,
};
@@ -1565,6 +1636,7 @@ static const struct tipd_data tps25750_data = {
.trace_status = trace_tps25750_status,
.apply_patch = tps25750_apply_patch,
.init = tps25750_init,
+ .read_data_status = tps6598x_read_data_status,
.reset = tps25750_reset,
};
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 07/11] usb: typec: tipd: Register DisplayPort and Thunderbolt altmodes for cd321x
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
` (5 preceding siblings ...)
2025-09-14 12:56 ` [PATCH 06/11] usb: typec: tipd: Read USB4, Thunderbolt and DisplayPort status for cd321x Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 08/11] usb: typec: tipd: Update partner identity when power status was updated Sven Peter
` (3 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter
Ports equipped with a CD321x are only found on Apple Silicon machines
and always support DisplayPort, Thunderbolt and USB4. Register these
port modes unconditionally.
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 85 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 82 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index afd11b3e1ae596c7f3283e4336aaa57874c9378d..c7cf936e5a61a331271c05b68ff1b77b89c0f643 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -16,6 +16,8 @@
#include <linux/interrupt.h>
#include <linux/usb/typec.h>
#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_tbt.h>
#include <linux/usb/role.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
@@ -144,6 +146,7 @@ struct tipd_data {
u64 irq_mask1;
size_t tps_struct_size;
int (*register_port)(struct tps6598x *tps, struct fwnode_handle *node);
+ void (*unregister_port)(struct tps6598x *tps);
void (*trace_data_status)(u32 status);
void (*trace_power_status)(u16 status);
void (*trace_status)(u32 status);
@@ -185,6 +188,9 @@ struct cd321x {
struct tps6598x_dp_sid_status_reg dp_sid_status;
struct tps6598x_intel_vid_status_reg intel_vid_status;
struct tps6598x_usb4_status_reg usb4_status;
+
+ struct typec_altmode *port_altmode_dp;
+ struct typec_altmode *port_altmode_tbt;
};
static enum power_supply_property tps6598x_psy_props[] = {
@@ -964,6 +970,76 @@ tps6598x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
return 0;
}
+static int cd321x_register_port_altmodes(struct cd321x *cd321x)
+{
+ struct typec_altmode_desc desc;
+ struct typec_altmode *amode;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.svid = USB_TYPEC_DP_SID;
+ desc.mode = USB_TYPEC_DP_MODE;
+ desc.vdo = DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D));
+ desc.vdo |= DP_CAP_DFP_D;
+ amode = typec_port_register_altmode(cd321x->tps.port, &desc);
+ if (IS_ERR(amode))
+ return PTR_ERR(amode);
+ cd321x->port_altmode_dp = amode;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.svid = USB_TYPEC_TBT_SID;
+ desc.mode = TYPEC_ANY_MODE;
+ amode = typec_port_register_altmode(cd321x->tps.port, &desc);
+ if (IS_ERR(amode)) {
+ typec_unregister_altmode(cd321x->port_altmode_dp);
+ cd321x->port_altmode_dp = NULL;
+ return PTR_ERR(amode);
+ }
+ cd321x->port_altmode_tbt = amode;
+
+ return 0;
+}
+
+static int
+cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+ int ret;
+
+ ret = tps6598x_register_port(tps, fwnode);
+ if (ret)
+ return ret;
+
+ ret = cd321x_register_port_altmodes(cd321x);
+ if (ret)
+ goto err_unregister_port;
+
+ typec_set_mode(tps->port, TYPEC_STATE_SAFE);
+
+ return 0;
+
+err_unregister_port:
+ typec_unregister_port(tps->port);
+ return ret;
+}
+
+static void
+tps6598x_unregister_port(struct tps6598x *tps)
+{
+ typec_unregister_port(tps->port);
+}
+
+static void
+cd321x_unregister_port(struct tps6598x *tps)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+
+ typec_unregister_altmode(cd321x->port_altmode_dp);
+ cd321x->port_altmode_dp = NULL;
+ typec_unregister_altmode(cd321x->port_altmode_tbt);
+ cd321x->port_altmode_tbt = NULL;
+ typec_unregister_port(tps->port);
+}
+
static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw,
const char **firmware_name)
{
@@ -1505,7 +1581,7 @@ static int tps6598x_probe(struct i2c_client *client)
err_disconnect:
tps6598x_disconnect(tps, 0);
err_unregister_port:
- typec_unregister_port(tps->port);
+ tps->data->unregister_port(tps);
err_role_put:
usb_role_switch_put(tps->role_sw);
err_fwnode_put:
@@ -1529,7 +1605,7 @@ static void tps6598x_remove(struct i2c_client *client)
devm_free_irq(tps->dev, client->irq, tps);
tps6598x_disconnect(tps, 0);
- typec_unregister_port(tps->port);
+ tps->data->unregister_port(tps);
usb_role_switch_put(tps->role_sw);
/* Reset PD controller to remove any applied patch */
@@ -1598,7 +1674,8 @@ static const struct tipd_data cd321x_data = {
APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
APPLE_CD_REG_INT_PLUG_EVENT,
.tps_struct_size = sizeof(struct cd321x),
- .register_port = tps6598x_register_port,
+ .register_port = cd321x_register_port,
+ .unregister_port = cd321x_unregister_port,
.trace_data_status = trace_cd321x_data_status,
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
@@ -1615,6 +1692,7 @@ static const struct tipd_data tps6598x_data = {
TPS_REG_INT_PLUG_EVENT,
.tps_struct_size = sizeof(struct tps6598x),
.register_port = tps6598x_register_port,
+ .unregister_port = tps6598x_unregister_port,
.trace_data_status = trace_tps6598x_data_status,
.trace_power_status = trace_tps6598x_power_status,
.trace_status = trace_tps6598x_status,
@@ -1631,6 +1709,7 @@ static const struct tipd_data tps25750_data = {
TPS_REG_INT_PLUG_EVENT,
.tps_struct_size = sizeof(struct tps6598x),
.register_port = tps25750_register_port,
+ .unregister_port = tps6598x_unregister_port,
.trace_data_status = trace_tps6598x_data_status,
.trace_power_status = trace_tps25750_power_status,
.trace_status = trace_tps25750_status,
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 08/11] usb: typec: tipd: Update partner identity when power status was updated
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
` (6 preceding siblings ...)
2025-09-14 12:56 ` [PATCH 07/11] usb: typec: tipd: Register DisplayPort and Thunderbolt altmodes " Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-15 13:14 ` Heikki Krogerus
2025-09-14 12:56 ` [PATCH 09/11] usb: typec: tipd: Use read_power_status function in probe Sven Peter
` (2 subsequent siblings)
10 siblings, 1 reply; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter, Hector Martin
From: Hector Martin <marcan@marcan.st>
Whenever the power status is changed make sure to also update the
partner identity to be able to detect changes once de-bouncing and mode
changes are added for CD321x.
Signed-off-by: Hector Martin <marcan@marcan.st>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index c7cf936e5a61a331271c05b68ff1b77b89c0f643..e16c6c07c72a3e285f1fc94db72bed8dc3217a1d 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -635,9 +635,16 @@ static irqreturn_t cd321x_interrupt(int irq, void *data)
if (!tps6598x_read_status(tps, &status))
goto err_unlock;
- if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE)
+ if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) {
if (!tps6598x_read_power_status(tps))
goto err_unlock;
+ if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) {
+ if (tps6598x_read_partner_identity(tps)) {
+ dev_err(tps->dev, "failed to read partner identity\n");
+ tps->partner_identity = (struct usb_pd_identity) {0};
+ }
+ }
+ }
if (event & APPLE_CD_REG_INT_DATA_STATUS_UPDATE)
if (!tps->data->read_data_status(tps))
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 09/11] usb: typec: tipd: Use read_power_status function in probe
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
` (7 preceding siblings ...)
2025-09-14 12:56 ` [PATCH 08/11] usb: typec: tipd: Update partner identity when power status was updated Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 10/11] usb: typec: tipd: Read data status in probe and cache its value Sven Peter
2025-09-14 12:56 ` [PATCH 11/11] usb: typec: tipd: Handle mode transitions for CD321x Sven Peter
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter, Hector Martin
From: Hector Martin <marcan@marcan.st>
We need the initial power status to be able to reliably detect connector
changes once we introduce de-bouncing for CD321x next. read_power_status
takes care of this and also forwards the status to the trace subsystem
so let's use that instead of open-coding it inside probe.
Signed-off-by: Hector Martin <marcan@marcan.st>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index e16c6c07c72a3e285f1fc94db72bed8dc3217a1d..f546f4ae563af3d411ec6591fe9c3b122e4efaab 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -1549,11 +1549,8 @@ static int tps6598x_probe(struct i2c_client *client)
goto err_role_put;
if (status & TPS_STATUS_PLUG_PRESENT) {
- ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &tps->pwr_status);
- if (ret < 0) {
- dev_err(tps->dev, "failed to read power status: %d\n", ret);
+ if (!tps6598x_read_power_status(tps))
goto err_unregister_port;
- }
ret = tps6598x_connect(tps, status);
if (ret)
dev_err(&client->dev, "failed to register partner\n");
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 10/11] usb: typec: tipd: Read data status in probe and cache its value
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
` (8 preceding siblings ...)
2025-09-14 12:56 ` [PATCH 09/11] usb: typec: tipd: Use read_power_status function in probe Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 12:56 ` [PATCH 11/11] usb: typec: tipd: Handle mode transitions for CD321x Sven Peter
10 siblings, 0 replies; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter, Hector Martin
From: Hector Martin <marcan@marcan.st>
Just like for power status we also need to keep track of data status to
be able to detect mode changes once we introduce de-bouncing for CD321x.
Read it during probe and keep a cached copy of its value.
Signed-off-by: Hector Martin <marcan@marcan.st>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index f546f4ae563af3d411ec6591fe9c3b122e4efaab..f347e5bc625497ddff270e3e9e4d2ddaf3ca6bc1 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -176,6 +176,7 @@ struct tps6598x {
int wakeup;
u32 status; /* status reg */
+ u32 data_status;
u16 pwr_status;
struct delayed_work wq_poll;
@@ -538,6 +539,7 @@ static bool tps6598x_read_data_status(struct tps6598x *tps)
dev_err(tps->dev, "failed to read data status: %d\n", ret);
return false;
}
+ tps->data_status = data_status;
if (tps->data->trace_data_status)
tps->data->trace_data_status(data_status);
@@ -1551,6 +1553,8 @@ static int tps6598x_probe(struct i2c_client *client)
if (status & TPS_STATUS_PLUG_PRESENT) {
if (!tps6598x_read_power_status(tps))
goto err_unregister_port;
+ if (!tps->data->read_data_status(tps))
+ goto err_unregister_port;
ret = tps6598x_connect(tps, status);
if (ret)
dev_err(&client->dev, "failed to register partner\n");
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 11/11] usb: typec: tipd: Handle mode transitions for CD321x
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
` (9 preceding siblings ...)
2025-09-14 12:56 ` [PATCH 10/11] usb: typec: tipd: Read data status in probe and cache its value Sven Peter
@ 2025-09-14 12:56 ` Sven Peter
2025-09-14 13:29 ` Janne Grunau
10 siblings, 1 reply; 14+ messages in thread
From: Sven Peter @ 2025-09-14 12:56 UTC (permalink / raw)
To: Heikki Krogerus, Greg Kroah-Hartman
Cc: Janne Grunau, Neal Gompa, linux-usb, linux-kernel, asahi,
linux-arm-kernel, Sven Peter, Hector Martin
From: Hector Martin <marcan@marcan.st>
On Apple Silicon machines there is no control over which alt mode is
chosen. The CD321x' firmware negotiates the target mode on its own and
only lets the main CPU know after the mode has already been chosen.
Especially after plugging a new cable in this can result to quick mode
changes from e.g. power only -> USB3 only -> USB3+DisplayPort in a short
time. It is not possile to influence this in any way and we also do not
get direct access to the PDOs or VDOs exchanged via USB PD.
Additionally, mode changes must be tightly synchronized between DWC3 and
the Type C PHY and most mode changes require a full reset of DWC3 to
make the port work correctly.
This is all done synchronously from the role switch handler inside the
DWC3 glue driver on these machines to avoid tripping any failsafes or
watchdogs inside the Type-C PHY that may, in the worst case, reset the
entire SoC.
To be able to control all this we trigger the entire process in the
correct order directly from the TIPD driver and de-bounce any mode
changes to avoid tearing down and re-setting DWC3 back up multiple times
any time a new connection is made.
Signed-off-by: Hector Martin <marcan@marcan.st>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Co-developed-by: Sven Peter <sven@kernel.org>
Signed-off-by: Sven Peter <sven@kernel.org>
---
drivers/usb/typec/tipd/core.c | 290 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 286 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index f347e5bc625497ddff270e3e9e4d2ddaf3ca6bc1..2b1049c9a6f3c4300f4a25a97fe502c47e82a134 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -17,6 +17,7 @@
#include <linux/usb/typec.h>
#include <linux/usb/typec_altmode.h>
#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_tbt.h>
#include <linux/usb/role.h>
#include <linux/workqueue.h>
@@ -120,6 +121,9 @@ struct tps6598x_intel_vid_status_reg {
#define TPS_TASK_TIMEOUT 1
#define TPS_TASK_REJECTED 3
+/* Debounce delay for mode changes, in milliseconds */
+#define CD321X_DEBOUNCE_DELAY_MS 500
+
enum {
TPS_MODE_APP,
TPS_MODE_BOOT,
@@ -145,6 +149,7 @@ struct tipd_data {
irq_handler_t irq_handler;
u64 irq_mask1;
size_t tps_struct_size;
+ void (*remove)(struct tps6598x *tps);
int (*register_port)(struct tps6598x *tps, struct fwnode_handle *node);
void (*unregister_port)(struct tps6598x *tps);
void (*trace_data_status)(u32 status);
@@ -155,6 +160,7 @@ struct tipd_data {
int (*switch_power_state)(struct tps6598x *tps, u8 target_state);
bool (*read_data_status)(struct tps6598x *tps);
int (*reset)(struct tps6598x *tps);
+ int (*connect)(struct tps6598x *tps, u32 status);
};
struct tps6598x {
@@ -183,6 +189,17 @@ struct tps6598x {
const struct tipd_data *data;
};
+struct cd321x_status {
+ u32 status;
+ u32 pwr_status;
+ u32 data_status;
+ u32 status_changed;
+ struct usb_pd_identity partner_identity;
+ struct tps6598x_dp_sid_status_reg dp_sid_status;
+ struct tps6598x_intel_vid_status_reg intel_vid_status;
+ struct tps6598x_usb4_status_reg usb4_status;
+};
+
struct cd321x {
struct tps6598x tps;
@@ -192,6 +209,13 @@ struct cd321x {
struct typec_altmode *port_altmode_dp;
struct typec_altmode *port_altmode_tbt;
+
+ struct typec_mux *mux;
+ struct typec_mux_state state;
+
+ struct cd321x_status update_status;
+ struct delayed_work update_work;
+ struct usb_pd_identity cur_partner_identity;
};
static enum power_supply_property tps6598x_psy_props[] = {
@@ -613,6 +637,233 @@ static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status)
}
}
+static void cd321x_typec_update_mode(struct tps6598x *tps, struct cd321x_status *st)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+
+ if (!(st->data_status & TPS_DATA_STATUS_DATA_CONNECTION)) {
+ if (cd321x->state.mode == TYPEC_STATE_SAFE)
+ return;
+ cd321x->state.alt = NULL;
+ cd321x->state.mode = TYPEC_STATE_SAFE;
+ cd321x->state.data = NULL;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ } else if (st->data_status & TPS_DATA_STATUS_DP_CONNECTION) {
+ struct typec_displayport_data dp_data;
+ unsigned long mode;
+
+ switch (TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT(st->data_status)) {
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_A:
+ mode = TYPEC_DP_STATE_A;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_B:
+ mode = TYPEC_DP_STATE_B;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_C:
+ mode = TYPEC_DP_STATE_C;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_D:
+ mode = TYPEC_DP_STATE_D;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_E:
+ mode = TYPEC_DP_STATE_E;
+ break;
+ case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_F:
+ mode = TYPEC_DP_STATE_F;
+ break;
+ default:
+ dev_err(tps->dev, "Invalid DP pin assignment\n");
+ return;
+ }
+
+ if (cd321x->state.alt == cd321x->port_altmode_dp &&
+ cd321x->state.mode == mode) {
+ return;
+ }
+
+ dp_data.status = le32_to_cpu(st->dp_sid_status.status_rx);
+ dp_data.conf = le32_to_cpu(st->dp_sid_status.configure);
+ cd321x->state.alt = cd321x->port_altmode_dp;
+ cd321x->state.data = &dp_data;
+ cd321x->state.mode = mode;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ } else if (st->data_status & TPS_DATA_STATUS_TBT_CONNECTION) {
+ struct typec_thunderbolt_data tbt_data;
+
+ if (cd321x->state.alt == cd321x->port_altmode_tbt &&
+ cd321x->state.mode == TYPEC_TBT_MODE)
+ return;
+
+ tbt_data.cable_mode = le16_to_cpu(st->intel_vid_status.cable_mode);
+ tbt_data.device_mode = le16_to_cpu(st->intel_vid_status.device_mode);
+ tbt_data.enter_vdo = le16_to_cpu(st->intel_vid_status.enter_vdo);
+ cd321x->state.alt = cd321x->port_altmode_tbt;
+ cd321x->state.mode = TYPEC_TBT_MODE;
+ cd321x->state.data = &tbt_data;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ } else if (st->data_status & CD321X_DATA_STATUS_USB4_CONNECTION) {
+ struct enter_usb_data eusb_data;
+
+ if (cd321x->state.alt == NULL && cd321x->state.mode == TYPEC_MODE_USB4)
+ return;
+
+ eusb_data.eudo = le32_to_cpu(st->usb4_status.eudo);
+ eusb_data.active_link_training =
+ !!(st->data_status & TPS_DATA_STATUS_ACTIVE_LINK_TRAIN);
+
+ cd321x->state.alt = NULL;
+ cd321x->state.data = &eusb_data;
+ cd321x->state.mode = TYPEC_MODE_USB4;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ } else {
+ if (cd321x->state.alt == NULL && cd321x->state.mode == TYPEC_STATE_USB)
+ return;
+ cd321x->state.alt = NULL;
+ cd321x->state.mode = TYPEC_STATE_USB;
+ cd321x->state.data = NULL;
+ typec_mux_set(cd321x->mux, &cd321x->state);
+ }
+
+ /* Clear data since it's no longer used after typec_mux_set and points to the stack */
+ cd321x->state.data = NULL;
+}
+
+static void cd321x_update_work(struct work_struct *work)
+{
+ struct cd321x *cd321x = container_of(to_delayed_work(work),
+ struct cd321x, update_work);
+ struct tps6598x *tps = &cd321x->tps;
+ struct cd321x_status st;
+
+ guard(mutex)(&tps->lock);
+
+ st = cd321x->update_status;
+ cd321x->update_status.status_changed = 0;
+
+ bool old_connected = !!tps->partner;
+ bool new_connected = st.status & TPS_STATUS_PLUG_PRESENT;
+ bool was_disconnected = st.status_changed & TPS_STATUS_PLUG_PRESENT;
+
+ bool usb_connection = st.data_status &
+ (TPS_DATA_STATUS_USB2_CONNECTION | TPS_DATA_STATUS_USB3_CONNECTION);
+
+ enum usb_role old_role = usb_role_switch_get_role(tps->role_sw);
+ enum usb_role new_role = USB_ROLE_NONE;
+ enum typec_pwr_opmode pwr_opmode = TYPEC_PWR_MODE_USB;
+ enum typec_orientation orientation = TYPEC_ORIENTATION_NONE;
+
+ if (usb_connection) {
+ if (tps->data_status & TPS_DATA_STATUS_USB_DATA_ROLE)
+ new_role = USB_ROLE_DEVICE;
+ else
+ new_role = USB_ROLE_HOST;
+ }
+
+ if (new_connected) {
+ pwr_opmode = TPS_POWER_STATUS_PWROPMODE(st.pwr_status);
+ orientation = TPS_STATUS_TO_UPSIDE_DOWN(st.status) ?
+ TYPEC_ORIENTATION_REVERSE : TYPEC_ORIENTATION_NORMAL;
+ }
+
+ bool is_pd = pwr_opmode == TYPEC_PWR_MODE_PD;
+ bool partner_changed = old_connected && new_connected &&
+ (was_disconnected ||
+ (is_pd && memcmp(&st.partner_identity,
+ &cd321x->cur_partner_identity, sizeof(struct usb_pd_identity))));
+
+ /* If we are switching from an active role, transition to USB_ROLE_NONE first */
+ if (old_role != USB_ROLE_NONE && (new_role != old_role || was_disconnected))
+ usb_role_switch_set_role(tps->role_sw, USB_ROLE_NONE);
+
+ /* Process partner disconnection or change */
+ if (!new_connected || partner_changed) {
+ if (!IS_ERR(tps->partner))
+ typec_unregister_partner(tps->partner);
+ tps->partner = NULL;
+ }
+
+ /* If there was a disconnection, set PHY to off */
+ if (!new_connected || was_disconnected) {
+ cd321x->state.alt = NULL;
+ cd321x->state.mode = TYPEC_STATE_SAFE;
+ cd321x->state.data = NULL;
+ typec_set_mode(tps->port, TYPEC_STATE_SAFE);
+ }
+
+ /* Update Type-C properties */
+ typec_set_pwr_opmode(tps->port, pwr_opmode);
+ typec_set_pwr_role(tps->port, TPS_STATUS_TO_TYPEC_PORTROLE(st.status));
+ typec_set_vconn_role(tps->port, TPS_STATUS_TO_TYPEC_VCONN(st.status));
+ typec_set_orientation(tps->port, orientation);
+ typec_set_data_role(tps->port, TPS_STATUS_TO_TYPEC_DATAROLE(st.status));
+ power_supply_changed(tps->psy);
+
+ /* If the plug is disconnected, we are done */
+ if (!new_connected)
+ return;
+
+ /* Set up partner if we were previously disconnected (or changed). */
+ if (!tps->partner) {
+ struct typec_partner_desc desc;
+
+ desc.usb_pd = is_pd;
+ desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
+ desc.identity = NULL;
+
+ if (desc.usb_pd)
+ desc.identity = &st.partner_identity;
+
+ tps->partner = typec_register_partner(tps->port, &desc);
+ if (IS_ERR(tps->partner))
+ dev_warn(tps->dev, "%s: failed to register partnet\n", __func__);
+
+ if (desc.identity) {
+ typec_partner_set_identity(tps->partner);
+ cd321x->cur_partner_identity = st.partner_identity;
+ }
+ }
+
+ /* Update the TypeC MUX/PHY state */
+ cd321x_typec_update_mode(tps, &st);
+
+ /* Launch the USB role switch */
+ usb_role_switch_set_role(tps->role_sw, new_role);
+
+ power_supply_changed(tps->psy);
+}
+
+static void cd321x_queue_status(struct cd321x *cd321x)
+{
+ cd321x->update_status.status_changed |= cd321x->update_status.status ^ cd321x->tps.status;
+
+ cd321x->update_status.status = cd321x->tps.status;
+ cd321x->update_status.pwr_status = cd321x->tps.pwr_status;
+ cd321x->update_status.data_status = cd321x->tps.data_status;
+
+ cd321x->update_status.partner_identity = cd321x->tps.partner_identity;
+ cd321x->update_status.dp_sid_status = cd321x->dp_sid_status;
+ cd321x->update_status.intel_vid_status = cd321x->intel_vid_status;
+ cd321x->update_status.usb4_status = cd321x->usb4_status;
+}
+
+static int cd321x_connect(struct tps6598x *tps, u32 status)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+
+ tps->status = status;
+ cd321x_queue_status(cd321x);
+
+ /*
+ * Cancel pending work if not already running, then requeue after CD321X_DEBOUNCE_DELAY_MS
+ * regardless since the work function will check for any plug or altmodes changes since
+ * its last run anyway.
+ */
+ cancel_delayed_work(&cd321x->update_work);
+ schedule_delayed_work(&cd321x->update_work, msecs_to_jiffies(CD321X_DEBOUNCE_DELAY_MS));
+
+ return 0;
+}
+
static irqreturn_t cd321x_interrupt(int irq, void *data)
{
struct tps6598x *tps = data;
@@ -652,9 +903,8 @@ static irqreturn_t cd321x_interrupt(int irq, void *data)
if (!tps->data->read_data_status(tps))
goto err_unlock;
- /* Handle plug insert or removal */
- if (event & APPLE_CD_REG_INT_PLUG_EVENT)
- tps6598x_handle_plug_event(tps, status);
+ /* Can be called uncondtionally since it will check for any changes itself */
+ cd321x_connect(tps, status);
err_unlock:
mutex_unlock(&tps->lock);
@@ -1014,6 +1264,8 @@ cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
int ret;
+ INIT_DELAYED_WORK(&cd321x->update_work, cd321x_update_work);
+
ret = tps6598x_register_port(tps, fwnode);
if (ret)
return ret;
@@ -1022,10 +1274,24 @@ cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
if (ret)
goto err_unregister_port;
+ cd321x->mux = fwnode_typec_mux_get(fwnode);
+ if (IS_ERR(cd321x->mux)) {
+ ret = PTR_ERR(cd321x->mux);
+ goto err_unregister_altmodes;
+ }
+
+ cd321x->state.alt = NULL;
+ cd321x->state.mode = TYPEC_STATE_SAFE;
+ cd321x->state.data = NULL;
typec_set_mode(tps->port, TYPEC_STATE_SAFE);
return 0;
+err_unregister_altmodes:
+ typec_unregister_altmode(cd321x->port_altmode_dp);
+ typec_unregister_altmode(cd321x->port_altmode_tbt);
+ cd321x->port_altmode_dp = NULL;
+ cd321x->port_altmode_tbt = NULL;
err_unregister_port:
typec_unregister_port(tps->port);
return ret;
@@ -1042,6 +1308,8 @@ cd321x_unregister_port(struct tps6598x *tps)
{
struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+ typec_mux_put(cd321x->mux);
+ cd321x->mux = NULL;
typec_unregister_altmode(cd321x->port_altmode_dp);
cd321x->port_altmode_dp = NULL;
typec_unregister_altmode(cd321x->port_altmode_tbt);
@@ -1454,6 +1722,13 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
return 0;
}
+static void cd321x_remove(struct tps6598x *tps)
+{
+ struct cd321x *cd321x = container_of(tps, struct cd321x, tps);
+
+ cancel_delayed_work_sync(&cd321x->update_work);
+}
+
static int tps6598x_probe(struct i2c_client *client)
{
const struct tipd_data *data;
@@ -1555,7 +1830,7 @@ static int tps6598x_probe(struct i2c_client *client)
goto err_unregister_port;
if (!tps->data->read_data_status(tps))
goto err_unregister_port;
- ret = tps6598x_connect(tps, status);
+ ret = tps->data->connect(tps, status);
if (ret)
dev_err(&client->dev, "failed to register partner\n");
}
@@ -1612,6 +1887,9 @@ static void tps6598x_remove(struct i2c_client *client)
else
devm_free_irq(tps->dev, client->irq, tps);
+ if (tps->data->remove)
+ tps->data->remove(tps);
+
tps6598x_disconnect(tps, 0);
tps->data->unregister_port(tps);
usb_role_switch_put(tps->role_sw);
@@ -1682,6 +1960,7 @@ static const struct tipd_data cd321x_data = {
APPLE_CD_REG_INT_DATA_STATUS_UPDATE |
APPLE_CD_REG_INT_PLUG_EVENT,
.tps_struct_size = sizeof(struct cd321x),
+ .remove = cd321x_remove,
.register_port = cd321x_register_port,
.unregister_port = cd321x_unregister_port,
.trace_data_status = trace_cd321x_data_status,
@@ -1691,6 +1970,7 @@ static const struct tipd_data cd321x_data = {
.read_data_status = cd321x_read_data_status,
.reset = cd321x_reset,
.switch_power_state = cd321x_switch_power_state,
+ .connect = cd321x_connect,
};
static const struct tipd_data tps6598x_data = {
@@ -1708,6 +1988,7 @@ static const struct tipd_data tps6598x_data = {
.init = tps6598x_init,
.read_data_status = tps6598x_read_data_status,
.reset = tps6598x_reset,
+ .connect = tps6598x_connect,
};
static const struct tipd_data tps25750_data = {
@@ -1725,6 +2006,7 @@ static const struct tipd_data tps25750_data = {
.init = tps25750_init,
.read_data_status = tps6598x_read_data_status,
.reset = tps25750_reset,
+ .connect = tps6598x_connect,
};
static const struct of_device_id tps6598x_of_match[] = {
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 11/11] usb: typec: tipd: Handle mode transitions for CD321x
2025-09-14 12:56 ` [PATCH 11/11] usb: typec: tipd: Handle mode transitions for CD321x Sven Peter
@ 2025-09-14 13:29 ` Janne Grunau
0 siblings, 0 replies; 14+ messages in thread
From: Janne Grunau @ 2025-09-14 13:29 UTC (permalink / raw)
To: Sven Peter
Cc: Heikki Krogerus, Greg Kroah-Hartman, Neal Gompa, linux-usb,
linux-kernel, asahi, linux-arm-kernel, Hector Martin
On Sun, Sep 14, 2025 at 12:56:16PM +0000, Sven Peter wrote:
> From: Hector Martin <marcan@marcan.st>
>
> On Apple Silicon machines there is no control over which alt mode is
> chosen. The CD321x' firmware negotiates the target mode on its own and
> only lets the main CPU know after the mode has already been chosen.
> Especially after plugging a new cable in this can result to quick mode
> changes from e.g. power only -> USB3 only -> USB3+DisplayPort in a short
> time. It is not possile to influence this in any way and we also do not
> get direct access to the PDOs or VDOs exchanged via USB PD.
>
> Additionally, mode changes must be tightly synchronized between DWC3 and
> the Type C PHY and most mode changes require a full reset of DWC3 to
> make the port work correctly.
> This is all done synchronously from the role switch handler inside the
> DWC3 glue driver on these machines to avoid tripping any failsafes or
> watchdogs inside the Type-C PHY that may, in the worst case, reset the
> entire SoC.
>
> To be able to control all this we trigger the entire process in the
> correct order directly from the TIPD driver and de-bounce any mode
> changes to avoid tearing down and re-setting DWC3 back up multiple times
> any time a new connection is made.
>
> Signed-off-by: Hector Martin <marcan@marcan.st>
> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> Reviewed-by: Neal Gompa <neal@gompa.dev>
> Co-developed-by: Sven Peter <sven@kernel.org>
> Signed-off-by: Sven Peter <sven@kernel.org>
> ---
> drivers/usb/typec/tipd/core.c | 290 +++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 286 insertions(+), 4 deletions(-)
Reviewed-by: Janne Grunau <j@jannau.net>
Janne
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 08/11] usb: typec: tipd: Update partner identity when power status was updated
2025-09-14 12:56 ` [PATCH 08/11] usb: typec: tipd: Update partner identity when power status was updated Sven Peter
@ 2025-09-15 13:14 ` Heikki Krogerus
0 siblings, 0 replies; 14+ messages in thread
From: Heikki Krogerus @ 2025-09-15 13:14 UTC (permalink / raw)
To: Sven Peter
Cc: Greg Kroah-Hartman, Janne Grunau, Neal Gompa, linux-usb,
linux-kernel, asahi, linux-arm-kernel, Hector Martin
On Sun, Sep 14, 2025 at 12:56:13PM +0000, Sven Peter wrote:
> From: Hector Martin <marcan@marcan.st>
>
> Whenever the power status is changed make sure to also update the
> partner identity to be able to detect changes once de-bouncing and mode
> changes are added for CD321x.
>
> Signed-off-by: Hector Martin <marcan@marcan.st>
> Reviewed-by: Neal Gompa <neal@gompa.dev>
> Signed-off-by: Sven Peter <sven@kernel.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> ---
> drivers/usb/typec/tipd/core.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
> index c7cf936e5a61a331271c05b68ff1b77b89c0f643..e16c6c07c72a3e285f1fc94db72bed8dc3217a1d 100644
> --- a/drivers/usb/typec/tipd/core.c
> +++ b/drivers/usb/typec/tipd/core.c
> @@ -635,9 +635,16 @@ static irqreturn_t cd321x_interrupt(int irq, void *data)
> if (!tps6598x_read_status(tps, &status))
> goto err_unlock;
>
> - if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE)
> + if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) {
> if (!tps6598x_read_power_status(tps))
> goto err_unlock;
> + if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) {
> + if (tps6598x_read_partner_identity(tps)) {
> + dev_err(tps->dev, "failed to read partner identity\n");
> + tps->partner_identity = (struct usb_pd_identity) {0};
> + }
> + }
> + }
>
> if (event & APPLE_CD_REG_INT_DATA_STATUS_UPDATE)
> if (!tps->data->read_data_status(tps))
>
> --
> 2.34.1
>
--
heikki
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-09-15 13:14 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-14 12:56 [PATCH 00/11] Apple Silicon USB3 support - TIPD changes Sven Peter
2025-09-14 12:56 ` [PATCH 01/11] usb: typec: tipd: Clear interrupts first Sven Peter
2025-09-14 12:56 ` [PATCH 02/11] usb: typec: tipd: Move initial irq mask to tipd_data Sven Peter
2025-09-14 12:56 ` [PATCH 03/11] usb: typec: tipd: Move switch_power_state " Sven Peter
2025-09-14 12:56 ` [PATCH 04/11] usb: typec: tipd: Trace data status for CD321x correctly Sven Peter
2025-09-14 12:56 ` [PATCH 05/11] usb: typec: tipd: Add cd321x struct with separate size Sven Peter
2025-09-14 12:56 ` [PATCH 06/11] usb: typec: tipd: Read USB4, Thunderbolt and DisplayPort status for cd321x Sven Peter
2025-09-14 12:56 ` [PATCH 07/11] usb: typec: tipd: Register DisplayPort and Thunderbolt altmodes " Sven Peter
2025-09-14 12:56 ` [PATCH 08/11] usb: typec: tipd: Update partner identity when power status was updated Sven Peter
2025-09-15 13:14 ` Heikki Krogerus
2025-09-14 12:56 ` [PATCH 09/11] usb: typec: tipd: Use read_power_status function in probe Sven Peter
2025-09-14 12:56 ` [PATCH 10/11] usb: typec: tipd: Read data status in probe and cache its value Sven Peter
2025-09-14 12:56 ` [PATCH 11/11] usb: typec: tipd: Handle mode transitions for CD321x Sven Peter
2025-09-14 13:29 ` Janne Grunau
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).