Skip to content

Commit 85e654c

Browse files
bebarinojwrdegoede
authored andcommitted
platform/x86: intel_scu_ipc: Fail IPC send if still busy
It's possible for interrupts to get significantly delayed to the point that callers of intel_scu_ipc_dev_command() and friends can call the function once, hit a timeout, and call it again while the interrupt still hasn't been processed. This driver will get seriously confused if the interrupt is finally processed after the second IPC has been sent with ipc_command(). It won't know which IPC has been completed. This could be quite disastrous if calling code assumes something has happened upon return from intel_scu_ipc_dev_simple_command() when it actually hasn't. Let's avoid this scenario by simply returning -EBUSY in this case. Hopefully higher layers will know to back off or fail gracefully when this happens. It's all highly unlikely anyway, but it's better to be correct here as we have no way to know which IPC the status register is telling us about if we send a second IPC while the previous IPC is still processing. Cc: Prashant Malani <[email protected]> Cc: Kuppuswamy Sathyanarayanan <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Reviewed-by: Mika Westerberg <[email protected]> Fixes: ed12f29 ("ipc: Added support for IPC interrupt mode") Signed-off-by: Stephen Boyd <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Ilpo Järvinen <[email protected]> Reviewed-by: Hans de Goede <[email protected]> Signed-off-by: Hans de Goede <[email protected]>
1 parent efce785 commit 85e654c

File tree

1 file changed

+28
-12
lines changed

1 file changed

+28
-12
lines changed

drivers/platform/x86/intel_scu_ipc.c

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,24 @@ static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
265265
return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
266266
}
267267

268+
static struct intel_scu_ipc_dev *intel_scu_ipc_get(struct intel_scu_ipc_dev *scu)
269+
{
270+
u8 status;
271+
272+
if (!scu)
273+
scu = ipcdev;
274+
if (!scu)
275+
return ERR_PTR(-ENODEV);
276+
277+
status = ipc_read_status(scu);
278+
if (status & IPC_STATUS_BUSY) {
279+
dev_dbg(&scu->dev, "device is busy\n");
280+
return ERR_PTR(-EBUSY);
281+
}
282+
283+
return scu;
284+
}
285+
268286
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
269287
static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
270288
u32 count, u32 op, u32 id)
@@ -278,11 +296,10 @@ static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
278296
memset(cbuf, 0, sizeof(cbuf));
279297

280298
mutex_lock(&ipclock);
281-
if (!scu)
282-
scu = ipcdev;
283-
if (!scu) {
299+
scu = intel_scu_ipc_get(scu);
300+
if (IS_ERR(scu)) {
284301
mutex_unlock(&ipclock);
285-
return -ENODEV;
302+
return PTR_ERR(scu);
286303
}
287304

288305
for (nc = 0; nc < count; nc++, offset += 2) {
@@ -437,12 +454,12 @@ int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
437454
int err;
438455

439456
mutex_lock(&ipclock);
440-
if (!scu)
441-
scu = ipcdev;
442-
if (!scu) {
457+
scu = intel_scu_ipc_get(scu);
458+
if (IS_ERR(scu)) {
443459
mutex_unlock(&ipclock);
444-
return -ENODEV;
460+
return PTR_ERR(scu);
445461
}
462+
446463
cmdval = sub << 12 | cmd;
447464
ipc_command(scu, cmdval);
448465
err = intel_scu_ipc_check_status(scu);
@@ -482,11 +499,10 @@ int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
482499
return -EINVAL;
483500

484501
mutex_lock(&ipclock);
485-
if (!scu)
486-
scu = ipcdev;
487-
if (!scu) {
502+
scu = intel_scu_ipc_get(scu);
503+
if (IS_ERR(scu)) {
488504
mutex_unlock(&ipclock);
489-
return -ENODEV;
505+
return PTR_ERR(scu);
490506
}
491507

492508
memcpy(inbuf, in, inlen);

0 commit comments

Comments
 (0)