diff mbox series

[v2,1/2] i2c: i801: Fix resume bug

Message ID 20200901152221.3cea0048@endymion
State New
Headers show
Series [v2,1/2] i2c: i801: Fix resume bug | expand

Commit Message

Jean Delvare Sept. 1, 2020, 1:22 p.m. UTC
From: Volker Rümelin <vr_qemu@t-online.de>

On suspend the original host configuration gets restored. The
resume routine has to undo this, otherwise the SMBus master
may be left in disabled state or in i2c mode.

[JD: Rebased on v5.8, moved the write into i801_setup_hstcfg.]

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
Signed-off-by: Jean Delvare <jdelvare@suse.de>
---
I noticed this bug in a QEMU x86_64 q35 VM booted with OVMF. OVMF
doesn't inititialize the SMBus master. After 1s of SMBus inactivity
autosuspend disables the SMBus master. To reproduce please note QEMU's
ICH9 SMBus emulation does not handle interrupts and it's necessary
to pass the parameter disable_features=0x10 to the i2c_i801 driver.

Changes since v1:
 * Move the write into i801_setup_hstcfg() itself (suggested by Wolfram
   Sang).
 * Pass struct i801_priv * as the parameter to i801_setup_hstcfg(). It
   includes everything we need.

 drivers/i2c/busses/i2c-i801.c |   21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

Comments

Jean Delvare Sept. 3, 2020, 5:17 p.m. UTC | #1
On Tue, 1 Sep 2020 15:22:21 +0200, Jean Delvare wrote:
> From: Volker Rümelin <vr_qemu@t-online.de>
> 
> On suspend the original host configuration gets restored. The
> resume routine has to undo this, otherwise the SMBus master
> may be left in disabled state or in i2c mode.
> 
> [JD: Rebased on v5.8, moved the write into i801_setup_hstcfg.]
> 
> Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
> Signed-off-by: Jean Delvare <jdelvare@suse.de>

Oh and by the way this deserves a Cc: stable@, methinks.
Wolfram Sang Sept. 10, 2020, 7:09 a.m. UTC | #2
Hi Volker, hi Jean,

On Sun, Sep 06, 2020 at 10:00:50AM +0200, Volker Rümelin wrote:
> Hi Jean,
> 
> with these two patches the code in i2c-i801.c looks really good.
> 
> But there is an issue with the reproducer.

I am not familiar with the HW; do we want these two patches here or does
the issue below need to be solved first? And if we want them, is it
still stable material?

Regards,

   Wolfram

> 
> > I noticed this bug in a QEMU x86_64 q35 VM booted with OVMF. OVMF
> > doesn't inititialize the SMBus master. After 1s of SMBus inactivity
> > autosuspend disables the SMBus master. To reproduce please note QEMU's
> > ICH9 SMBus emulation does not handle interrupts and it's necessary
> > to pass the parameter disable_features=0x10 to the i2c_i801 driver.
> 
> Since commit a9c8088c7988e "i2c: i801: Don't restore config
> registers on runtime PM" the reproducer doesn't work anymore.
> This is because commit a9c8088c7988e works as intended and the
> pm->runtime_* callbacks no longer call i801_suspend() and
> i801_resume().
> 
> But there is more. With the SMBus master in runtime suspended state
> the direct-complete mechanism skips the calls to the pm->suspend
> and pm->resume callbacks on system suspend/resume. I am convinced
> in nearly all cases this disables the fix from commit a5aaea37858fb
> "i2c-i801: Restore the device state before leaving".
> 
> At the moment I see two ways to fix this problem. One way is to
> revert a9c8088c7988e "i2c: i801: Don't restore config registers
> on runtime PM", the other is to set the driver flag
> DPM_FLAG_NO_DIRECT_COMPLETE in i801_probe(). I tested both, but I
> can't decide which way is better.
> 
> With best regards,
> Volker
>
Wolfram Sang Sept. 14, 2020, 7:03 a.m. UTC | #3
On Tue, Sep 01, 2020 at 03:28:37PM +0200, Jean Delvare wrote:
> We don't actually need to derive the PCI device from the device
> structure, as we already have a pointer to it in our private data
> structure.
> 
> Signed-off-by: Jean Delvare <jdelvare@suse.de>

To reduce dependencies, this is also applied to for-current, thanks!
diff mbox series

Patch

--- linux-5.8.orig/drivers/i2c/busses/i2c-i801.c	2020-08-31 15:09:29.221616330 +0200
+++ linux-5.8/drivers/i2c/busses/i2c-i801.c	2020-09-01 12:42:46.003491616 +0200
@@ -1709,6 +1709,16 @@  static inline int i801_acpi_probe(struct
 static inline void i801_acpi_remove(struct i801_priv *priv) { }
 #endif
 
+static unsigned char i801_setup_hstcfg(struct i801_priv *priv)
+{
+	unsigned char hstcfg = priv->original_hstcfg;
+
+	hstcfg &= ~SMBHSTCFG_I2C_EN;	/* SMBus timing */
+	hstcfg |= SMBHSTCFG_HST_EN;
+	pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hstcfg);
+	return hstcfg;
+}
+
 static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
 	unsigned char temp;
@@ -1830,14 +1840,10 @@  static int i801_probe(struct pci_dev *de
 		return err;
 	}
 
-	pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &temp);
-	priv->original_hstcfg = temp;
-	temp &= ~SMBHSTCFG_I2C_EN;	/* SMBus timing */
-	if (!(temp & SMBHSTCFG_HST_EN)) {
+	pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &priv->original_hstcfg);
+	temp = i801_setup_hstcfg(priv);
+	if (!(priv->original_hstcfg & SMBHSTCFG_HST_EN))
 		dev_info(&dev->dev, "Enabling SMBus device\n");
-		temp |= SMBHSTCFG_HST_EN;
-	}
-	pci_write_config_byte(priv->pci_dev, SMBHSTCFG, temp);
 
 	if (temp & SMBHSTCFG_SMB_SMI_EN) {
 		dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n");
@@ -1963,6 +1969,7 @@  static int i801_resume(struct device *de
 {
 	struct i801_priv *priv = dev_get_drvdata(dev);
 
+	i801_setup_hstcfg(priv);
 	i801_enable_host_notify(&priv->adapter);
 
 	return 0;