From 894a4385f694b8d207e6a2c45eabaafe787a6eb4 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Fri, 5 Mar 2021 10:53:25 -0800 Subject: [PATCH 1/2] ucsi: ucsi_glink: Move debug log in ucsi_qti_glink_write() Currently we print a debug log in ucsi_qti_glink_write() only after it succeeds with an ACK from PPM. To debug some timeout issues, it is helpful to see the log printed before writing and waiting for the ACK. So, move it. Change-Id: I811bc7647030d247f7082092e73d31c90944e43b Signed-off-by: Subbaraman Narayanamurthy --- drivers/usb/typec/ucsi/ucsi_glink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index d7ae5abb1102..bf17db5121f1 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "UCSI: %s: " fmt, __func__ @@ -285,6 +285,9 @@ static int ucsi_qti_glink_write(struct ucsi_dev *udev, unsigned int offset, reinit_completion(&udev->sync_write_ack); } + ucsi_log(sync ? "sync_write:" : "async_write:", offset, + (u8 *)val, val_len); + rc = pmic_glink_write(udev->client, &ucsi_buf, sizeof(ucsi_buf)); if (rc < 0) { @@ -314,9 +317,6 @@ static int ucsi_qti_glink_write(struct ucsi_dev *udev, unsigned int offset, } } - ucsi_log(sync ? "sync_write:" : "async_write:", offset, - (u8 *)val, val_len); - if (((u8 *)val)[0] == UCSI_GET_CONNECTOR_STATUS) { mutex_lock(&udev->notify_lock); set_bit(CONN_STAT_REQD, &udev->cmd_requested_flags); From 4129360b43063a4c1fa74f1abfb2c9034057e0b1 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Tue, 6 Apr 2021 18:39:48 -0700 Subject: [PATCH 2/2] usb: typec: ucsi_glink: Fix a NULL pointer dereference During a SSR/PDR scenario, when pmic_glink state is down, ucsi_glink driver unregisters from the UCSI framework thereby setting udev->ucsi to NULL. Typically, we don't expect any notification coming from the charger firmware that runs on a remote subsystem until pmic_glink state is up and ucsi_glink driver registers again with the UCSI framework. In the case, if ucsi_glink driver receives a notification before UCSI driver finishes the registration, then don't handle it. Change-Id: Ieb792ca6026344462c7eae4d40aef87ae061ae96 Signed-off-by: Subbaraman Narayanamurthy --- drivers/usb/typec/ucsi/ucsi_glink.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index bf17db5121f1..1df57ee1dfa0 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -77,6 +77,7 @@ struct ucsi_dev { struct mutex read_lock; struct mutex write_lock; struct mutex notify_lock; + struct mutex state_lock; struct ucsi_read_buf_resp_msg rx_buf; unsigned long flags; atomic_t rx_valid; @@ -201,6 +202,19 @@ static int handle_ucsi_notify(struct ucsi_dev *udev, void *data, size_t len) return -EINVAL; } + mutex_lock(&udev->state_lock); + if (atomic_read(&udev->state) == PMIC_GLINK_STATE_DOWN) { + pr_err("glink state is down\n"); + mutex_unlock(&udev->state_lock); + return -ENOTCONN; + } + + if (!udev->ucsi) { + pr_err("ucsi is NULL\n"); + mutex_unlock(&udev->state_lock); + return -ENODEV; + } + msg_ptr = data; cci = msg_ptr->notification; ucsi_log("notify:", UCSI_CCI, (u8 *)&cci, sizeof(cci)); @@ -221,6 +235,7 @@ static int handle_ucsi_notify(struct ucsi_dev *udev, void *data, size_t len) if (con && con->ucsi) ucsi_connector_change(udev->ucsi, con_num); } + mutex_unlock(&udev->state_lock); return 0; } @@ -485,11 +500,13 @@ static int ucsi_setup(struct ucsi_dev *udev) return -EINVAL; } + mutex_lock(&udev->state_lock); udev->ucsi = ucsi_create(udev->dev, &ucsi_qti_ops); if (IS_ERR(udev->ucsi)) { rc = PTR_ERR(udev->ucsi); dev_err(udev->dev, "ucsi_create failed rc=%d\n", rc); udev->ucsi = NULL; + mutex_unlock(&udev->state_lock); return rc; } @@ -500,9 +517,11 @@ static int ucsi_setup(struct ucsi_dev *udev) dev_err(udev->dev, "ucsi_register failed rc=%d\n", rc); ucsi_destroy(udev->ucsi); udev->ucsi = NULL; + mutex_unlock(&udev->state_lock); return rc; } + mutex_unlock(&udev->state_lock); return 0; } @@ -520,12 +539,14 @@ static void ucsi_qti_state_cb(void *priv, enum pmic_glink_state state) dev_dbg(udev->dev, "state: %d\n", state); + mutex_lock(&udev->state_lock); atomic_set(&udev->state, state); switch (state) { case PMIC_GLINK_STATE_DOWN: if (!udev->ucsi) { dev_err(udev->dev, "ucsi is NULL\n"); + mutex_unlock(&udev->state_lock); return; } @@ -539,6 +560,7 @@ static void ucsi_qti_state_cb(void *priv, enum pmic_glink_state state) default: break; } + mutex_unlock(&udev->state_lock); } static int ucsi_probe(struct platform_device *pdev) @@ -557,6 +579,7 @@ static int ucsi_probe(struct platform_device *pdev) mutex_init(&udev->read_lock); mutex_init(&udev->write_lock); mutex_init(&udev->notify_lock); + mutex_init(&udev->state_lock); init_completion(&udev->read_ack); init_completion(&udev->write_ack); init_completion(&udev->sync_write_ack);