usb: typec: ucsi: ccg: Remove run_isr flag
The "run_isr" flag is used for preventing the driver from
calling the interrupt service routine in its runtime resume
callback when the driver is expecting completion to a
command, but what that basically does is that it hides the
real problem. The real problem is that the controller is
allowed to suspend in the middle of command execution.
As a more appropriate fix for the problem, using autosuspend
delay time that matches UCSI_TIMEOUT_MS (5s). That prevents
the controller from suspending while still in the middle of
executing a command.
This fixes a potential deadlock. Both ccg_read() and
ccg_write() are called with the mutex already taken at least
from ccg_send_command(). In ccg_read() and ccg_write, the
mutex is only acquired so that run_isr flag can be set.
Fixes: f0e4cd948b
("usb: typec: ucsi: ccg: add runtime pm workaround")
Cc: stable@vger.kernel.org
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20191004100219.71152-2-heikki.krogerus@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
c9a2baa796
commit
8530e4e20e
|
@ -195,7 +195,6 @@ struct ucsi_ccg {
|
||||||
|
|
||||||
/* fw build with vendor information */
|
/* fw build with vendor information */
|
||||||
u16 fw_build;
|
u16 fw_build;
|
||||||
bool run_isr; /* flag to call ISR routine during resume */
|
|
||||||
struct work_struct pm_work;
|
struct work_struct pm_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -224,18 +223,6 @@ static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
||||||
if (quirks && quirks->max_read_len)
|
if (quirks && quirks->max_read_len)
|
||||||
max_read_len = quirks->max_read_len;
|
max_read_len = quirks->max_read_len;
|
||||||
|
|
||||||
if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
|
|
||||||
uc->fw_version <= CCG_OLD_FW_VERSION) {
|
|
||||||
mutex_lock(&uc->lock);
|
|
||||||
/*
|
|
||||||
* Do not schedule pm_work to run ISR in
|
|
||||||
* ucsi_ccg_runtime_resume() after pm_runtime_get_sync()
|
|
||||||
* since we are already in ISR path.
|
|
||||||
*/
|
|
||||||
uc->run_isr = false;
|
|
||||||
mutex_unlock(&uc->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
pm_runtime_get_sync(uc->dev);
|
pm_runtime_get_sync(uc->dev);
|
||||||
while (rem_len > 0) {
|
while (rem_len > 0) {
|
||||||
msgs[1].buf = &data[len - rem_len];
|
msgs[1].buf = &data[len - rem_len];
|
||||||
|
@ -278,18 +265,6 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
|
||||||
msgs[0].len = len + sizeof(rab);
|
msgs[0].len = len + sizeof(rab);
|
||||||
msgs[0].buf = buf;
|
msgs[0].buf = buf;
|
||||||
|
|
||||||
if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
|
|
||||||
uc->fw_version <= CCG_OLD_FW_VERSION) {
|
|
||||||
mutex_lock(&uc->lock);
|
|
||||||
/*
|
|
||||||
* Do not schedule pm_work to run ISR in
|
|
||||||
* ucsi_ccg_runtime_resume() after pm_runtime_get_sync()
|
|
||||||
* since we are already in ISR path.
|
|
||||||
*/
|
|
||||||
uc->run_isr = false;
|
|
||||||
mutex_unlock(&uc->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
pm_runtime_get_sync(uc->dev);
|
pm_runtime_get_sync(uc->dev);
|
||||||
status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
|
@ -1130,7 +1105,6 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
||||||
uc->ppm.sync = ucsi_ccg_sync;
|
uc->ppm.sync = ucsi_ccg_sync;
|
||||||
uc->dev = dev;
|
uc->dev = dev;
|
||||||
uc->client = client;
|
uc->client = client;
|
||||||
uc->run_isr = true;
|
|
||||||
mutex_init(&uc->lock);
|
mutex_init(&uc->lock);
|
||||||
INIT_WORK(&uc->work, ccg_update_firmware);
|
INIT_WORK(&uc->work, ccg_update_firmware);
|
||||||
INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
|
INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
|
||||||
|
@ -1188,6 +1162,8 @@ static int ucsi_ccg_probe(struct i2c_client *client,
|
||||||
|
|
||||||
pm_runtime_set_active(uc->dev);
|
pm_runtime_set_active(uc->dev);
|
||||||
pm_runtime_enable(uc->dev);
|
pm_runtime_enable(uc->dev);
|
||||||
|
pm_runtime_use_autosuspend(uc->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(uc->dev, 5000);
|
||||||
pm_runtime_idle(uc->dev);
|
pm_runtime_idle(uc->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1229,7 +1205,6 @@ static int ucsi_ccg_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct ucsi_ccg *uc = i2c_get_clientdata(client);
|
struct ucsi_ccg *uc = i2c_get_clientdata(client);
|
||||||
bool schedule = true;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
|
* Firmware version 3.1.10 or earlier, built for NVIDIA has known issue
|
||||||
|
@ -1237,17 +1212,8 @@ static int ucsi_ccg_runtime_resume(struct device *dev)
|
||||||
* Schedule a work to call ISR as a workaround.
|
* Schedule a work to call ISR as a workaround.
|
||||||
*/
|
*/
|
||||||
if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
|
if (uc->fw_build == CCG_FW_BUILD_NVIDIA &&
|
||||||
uc->fw_version <= CCG_OLD_FW_VERSION) {
|
uc->fw_version <= CCG_OLD_FW_VERSION)
|
||||||
mutex_lock(&uc->lock);
|
schedule_work(&uc->pm_work);
|
||||||
if (!uc->run_isr) {
|
|
||||||
uc->run_isr = true;
|
|
||||||
schedule = false;
|
|
||||||
}
|
|
||||||
mutex_unlock(&uc->lock);
|
|
||||||
|
|
||||||
if (schedule)
|
|
||||||
schedule_work(&uc->pm_work);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue