Commit 976e7e9b authored by Christian A. Ehrhardt's avatar Christian A. Ehrhardt Committed by Greg Kroah-Hartman
Browse files

acpi: typec: ucsi: Introduce a ->poll_cci method



For the ACPI backend of UCSI the UCSI "registers" are just a memory copy
of the register values in an opregion. The ACPI implementation in the
BIOS ensures that the opregion contents are synced to the embedded
controller and it ensures that the registers (in particular CCI) are
synced back to the opregion on notifications. While there is an ACPI call
that syncs the actual registers to the opregion there is rarely a need to
do this and on some ACPI implementations it actually breaks in various
interesting ways.

The only reason to force a sync from the embedded controller is to poll
CCI while notifications are disabled. Only the ucsi core knows if this
is the case and guessing based on the current command is suboptimal, i.e.
leading to the following spurious assertion splat:

WARNING: CPU: 3 PID: 76 at drivers/usb/typec/ucsi/ucsi.c:1388 ucsi_reset_ppm+0x1b4/0x1c0 [typec_ucsi]
CPU: 3 UID: 0 PID: 76 Comm: kworker/3:0 Not tainted 6.12.11-200.fc41.x86_64 #1
Hardware name: LENOVO 21D0/LNVNB161216, BIOS J6CN45WW 03/17/2023
Workqueue: events_long ucsi_init_work [typec_ucsi]
RIP: 0010:ucsi_reset_ppm+0x1b4/0x1c0 [typec_ucsi]
Call Trace:
 <TASK>
 ucsi_init_work+0x3c/0xac0 [typec_ucsi]
 process_one_work+0x179/0x330
 worker_thread+0x252/0x390
 kthread+0xd2/0x100
 ret_from_fork+0x34/0x50
 ret_from_fork_asm+0x1a/0x30
 </TASK>

Thus introduce a ->poll_cci() method that works like ->read_cci() with an
additional forced sync and document that this should be used when polling
with notifications disabled. For all other backends that presumably don't
have this issue use the same implementation for both methods.

Fixes: fa48d7e8 ("usb: typec: ucsi: Do not call ACPI _DSM method for UCSI read operations")
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarChristian A. Ehrhardt <lk@c--e.de>
Tested-by: default avatarFedor Pchelkin <boddah8794@gmail.com>
Signed-off-by: default avatarFedor Pchelkin <boddah8794@gmail.com>
Reviewed-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20250217105442.113486-2-boddah8794@gmail.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d6b82daf
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -1346,7 +1346,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)

	mutex_lock(&ucsi->ppm_lock);

	ret = ucsi->ops->read_cci(ucsi, &cci);
	ret = ucsi->ops->poll_cci(ucsi, &cci);
	if (ret < 0)
		goto out;

@@ -1364,7 +1364,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)

		tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
		do {
			ret = ucsi->ops->read_cci(ucsi, &cci);
			ret = ucsi->ops->poll_cci(ucsi, &cci);
			if (ret < 0)
				goto out;
			if (cci & UCSI_CCI_COMMAND_COMPLETE)
@@ -1393,7 +1393,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
		/* Give the PPM time to process a reset before reading CCI */
		msleep(20);

		ret = ucsi->ops->read_cci(ucsi, &cci);
		ret = ucsi->ops->poll_cci(ucsi, &cci);
		if (ret)
			goto out;

@@ -1929,8 +1929,8 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
	struct ucsi *ucsi;

	if (!ops ||
	    !ops->read_version || !ops->read_cci || !ops->read_message_in ||
	    !ops->sync_control || !ops->async_control)
	    !ops->read_version || !ops->read_cci || !ops->poll_cci ||
	    !ops->read_message_in || !ops->sync_control || !ops->async_control)
		return ERR_PTR(-EINVAL);

	ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ struct dentry;
 * struct ucsi_operations - UCSI I/O operations
 * @read_version: Read implemented UCSI version
 * @read_cci: Read CCI register
 * @poll_cci: Read CCI register while polling with notifications disabled
 * @read_message_in: Read message data from UCSI
 * @sync_control: Blocking control operation
 * @async_control: Non-blocking control operation
@@ -76,6 +77,7 @@ struct dentry;
struct ucsi_operations {
	int (*read_version)(struct ucsi *ucsi, u16 *version);
	int (*read_cci)(struct ucsi *ucsi, u32 *cci);
	int (*poll_cci)(struct ucsi *ucsi, u32 *cci);
	int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
	int (*sync_control)(struct ucsi *ucsi, u64 command);
	int (*async_control)(struct ucsi *ucsi, u64 command);
+14 −7
Original line number Diff line number Diff line
@@ -57,19 +57,24 @@ static int ucsi_acpi_read_version(struct ucsi *ucsi, u16 *version)
}

static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci)
{
	struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);

	memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));

	return 0;
}

static int ucsi_acpi_poll_cci(struct ucsi *ucsi, u32 *cci)
{
	struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
	int ret;

	if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
	ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
	if (ret)
		return ret;
	}

	memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));

	return 0;
	return ucsi_acpi_read_cci(ucsi, cci);
}

static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
@@ -94,6 +99,7 @@ static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
static const struct ucsi_operations ucsi_acpi_ops = {
	.read_version = ucsi_acpi_read_version,
	.read_cci = ucsi_acpi_read_cci,
	.poll_cci = ucsi_acpi_poll_cci,
	.read_message_in = ucsi_acpi_read_message_in,
	.sync_control = ucsi_sync_control_common,
	.async_control = ucsi_acpi_async_control
@@ -142,6 +148,7 @@ static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
static const struct ucsi_operations ucsi_gram_ops = {
	.read_version = ucsi_acpi_read_version,
	.read_cci = ucsi_acpi_read_cci,
	.poll_cci = ucsi_acpi_poll_cci,
	.read_message_in = ucsi_gram_read_message_in,
	.sync_control = ucsi_gram_sync_control,
	.async_control = ucsi_acpi_async_control
+1 −0
Original line number Diff line number Diff line
@@ -664,6 +664,7 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
static const struct ucsi_operations ucsi_ccg_ops = {
	.read_version = ucsi_ccg_read_version,
	.read_cci = ucsi_ccg_read_cci,
	.poll_cci = ucsi_ccg_read_cci,
	.read_message_in = ucsi_ccg_read_message_in,
	.sync_control = ucsi_ccg_sync_control,
	.async_control = ucsi_ccg_async_control,
+1 −0
Original line number Diff line number Diff line
@@ -206,6 +206,7 @@ static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con)
static const struct ucsi_operations pmic_glink_ucsi_ops = {
	.read_version = pmic_glink_ucsi_read_version,
	.read_cci = pmic_glink_ucsi_read_cci,
	.poll_cci = pmic_glink_ucsi_read_cci,
	.read_message_in = pmic_glink_ucsi_read_message_in,
	.sync_control = ucsi_sync_control_common,
	.async_control = pmic_glink_ucsi_async_control,
Loading