Skip to content

Commit df787d8

Browse files
Christian A. Ehrhardtgregkh
Christian A. Ehrhardt
authored andcommitted
usb: ucsi_acpi: Quirk to ack a connector change ack cmd
[ Upstream commit f3be347 ] The PPM on some Dell laptops seems to expect that the ACK_CC_CI command to clear the connector change notification is in turn followed by another ACK_CC_CI to acknowledge the ACK_CC_CI command itself. This is in violation of the UCSI spec that states: "The only notification that is not acknowledged by the OPM is the command completion notification for the ACK_CC_CI or the PPM_RESET command." Add a quirk to send this ack anyway. Apply the quirk to all Dell systems. On the first command that acks a connector change send a dummy command to determine if it runs into a timeout. Only activate the quirk if it does. This ensure that we do not break Dell systems that do not need the quirk. Signed-off-by: "Christian A. Ehrhardt" <[email protected]> Reviewed-by: Heikki Krogerus <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 334b4a4 commit df787d8

File tree

1 file changed

+68
-3
lines changed

1 file changed

+68
-3
lines changed

drivers/usb/typec/ucsi/ucsi_acpi.c

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ struct ucsi_acpi {
2525
unsigned long flags;
2626
guid_t guid;
2727
u64 cmd;
28+
bool dell_quirk_probed;
29+
bool dell_quirk_active;
2830
};
2931

3032
static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
@@ -126,12 +128,73 @@ static const struct ucsi_operations ucsi_zenbook_ops = {
126128
.async_write = ucsi_acpi_async_write
127129
};
128130

129-
static const struct dmi_system_id zenbook_dmi_id[] = {
131+
/*
132+
* Some Dell laptops expect that an ACK command with the
133+
* UCSI_ACK_CONNECTOR_CHANGE bit set is followed by a (separate)
134+
* ACK command that only has the UCSI_ACK_COMMAND_COMPLETE bit set.
135+
* If this is not done events are not delivered to OSPM and
136+
* subsequent commands will timeout.
137+
*/
138+
static int
139+
ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset,
140+
const void *val, size_t val_len)
141+
{
142+
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
143+
u64 cmd = *(u64 *)val, ack = 0;
144+
int ret;
145+
146+
if (UCSI_COMMAND(cmd) == UCSI_ACK_CC_CI &&
147+
cmd & UCSI_ACK_CONNECTOR_CHANGE)
148+
ack = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE;
149+
150+
ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len);
151+
if (ret != 0)
152+
return ret;
153+
if (ack == 0)
154+
return ret;
155+
156+
if (!ua->dell_quirk_probed) {
157+
ua->dell_quirk_probed = true;
158+
159+
cmd = UCSI_GET_CAPABILITY;
160+
ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd,
161+
sizeof(cmd));
162+
if (ret == 0)
163+
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL,
164+
&ack, sizeof(ack));
165+
if (ret != -ETIMEDOUT)
166+
return ret;
167+
168+
ua->dell_quirk_active = true;
169+
dev_err(ua->dev, "Firmware bug: Additional ACK required after ACKing a connector change.\n");
170+
dev_err(ua->dev, "Firmware bug: Enabling workaround\n");
171+
}
172+
173+
if (!ua->dell_quirk_active)
174+
return ret;
175+
176+
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &ack, sizeof(ack));
177+
}
178+
179+
static const struct ucsi_operations ucsi_dell_ops = {
180+
.read = ucsi_acpi_read,
181+
.sync_write = ucsi_dell_sync_write,
182+
.async_write = ucsi_acpi_async_write
183+
};
184+
185+
static const struct dmi_system_id ucsi_acpi_quirks[] = {
130186
{
131187
.matches = {
132188
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
133189
DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
134190
},
191+
.driver_data = (void *)&ucsi_zenbook_ops,
192+
},
193+
{
194+
.matches = {
195+
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
196+
},
197+
.driver_data = (void *)&ucsi_dell_ops,
135198
},
136199
{ }
137200
};
@@ -160,6 +223,7 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
160223
{
161224
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
162225
const struct ucsi_operations *ops = &ucsi_acpi_ops;
226+
const struct dmi_system_id *id;
163227
struct ucsi_acpi *ua;
164228
struct resource *res;
165229
acpi_status status;
@@ -189,8 +253,9 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
189253
init_completion(&ua->complete);
190254
ua->dev = &pdev->dev;
191255

192-
if (dmi_check_system(zenbook_dmi_id))
193-
ops = &ucsi_zenbook_ops;
256+
id = dmi_first_match(ucsi_acpi_quirks);
257+
if (id)
258+
ops = id->driver_data;
194259

195260
ua->ucsi = ucsi_create(&pdev->dev, ops);
196261
if (IS_ERR(ua->ucsi))

0 commit comments

Comments
 (0)