Hello Jean, On 05/09/2019 14:57, Jean Delvare wrote: > If this is of any value to you, I tried implementing it in i2c-i801 a > few days ago. I can't really test it though as I don't have any device > which triggers an alert on my system, but I am sharing it with you if > you want to give it a try. You would still need to write the code in > the slave driver. I'm going to test your patch (I actually made very similar one couple of days ago), but I have a couple of comments to the below version: > --- > drivers/i2c/busses/i2c-i801.c | 77 +++++++++++++++++++++++++++++++++++++----- > 1 file changed, 69 insertions(+), 8 deletions(-) > > --- linux-5.2.orig/drivers/i2c/busses/i2c-i801.c 2019-08-28 15:58:52.725828215 +0200 > +++ linux-5.2/drivers/i2c/busses/i2c-i801.c 2019-08-28 16:50:09.212696037 +0200 > @@ -196,6 +196,7 @@ > > /* Host Notify Command register bits */ > #define SMBSLVCMD_HST_NTFY_INTREN BIT(0) > +#define SMBSLVCMD_SMBALERT_DIS BIT(2) > > #define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \ > SMBHSTSTS_DEV_ERR) > @@ -281,6 +282,10 @@ struct i801_priv { > */ > bool acpi_reserved; > struct mutex acpi_lock; > + > + /* SMBus alert */ > + struct i2c_smbus_alert_setup alert_data; > + struct i2c_client *ara; > }; > > #define FEATURE_SMBUS_PEC BIT(0) > @@ -289,6 +294,7 @@ struct i801_priv { > #define FEATURE_I2C_BLOCK_READ BIT(3) > #define FEATURE_IRQ BIT(4) > #define FEATURE_HOST_NOTIFY BIT(5) > +#define FEATURE_SMBUS_ALERT BIT(6) > /* Not really a feature, but it's convenient to handle it as such */ > #define FEATURE_IDF BIT(15) > #define FEATURE_TCO_SPT BIT(16) > @@ -301,6 +307,7 @@ static const char *i801_feature_names[] > "I2C block read", > "Interrupt", > "SMBus Host Notify", > + "SMBus Alert", > }; > > static unsigned int disable_features; > @@ -310,7 +317,8 @@ MODULE_PARM_DESC(disable_features, "Disa > "\t\t 0x02 disable the block buffer\n" > "\t\t 0x08 disable the I2C block read functionality\n" > "\t\t 0x10 don't use interrupts\n" > - "\t\t 0x20 disable SMBus Host Notify "); > + "\t\t 0x20 disable SMBus Host Notify\n" > + "\t\t 0x40 disable SMBus Alert "); > > /* Make sure the SMBus host is ready to start transmitting. > Return 0 if it is, -EBUSY if it is not. */ > @@ -620,8 +628,24 @@ static irqreturn_t i801_host_notify_isr( > return IRQ_HANDLED; > } > > +static irqreturn_t i801_smbus_alert_isr(struct i801_priv *priv) > +{ > + struct i2c_client *ara = priv->ara; > + > + if (ara) { > + dev_dbg(&ara->dev, "SMBus alert received\n"); > + i2c_handle_smbus_alert(ara); > + } else > + dev_dbg(&priv->adapter.dev, > + "SMBus alert received but no ARA client!\n"); > + > + /* clear SMBus Alert bit and return */ > + outb_p(SMBHSTSTS_SMBALERT_STS, SMBHSTSTS(priv)); > + return IRQ_HANDLED; > +} > + > /* > - * There are three kinds of interrupts: > + * There are four kinds of interrupts: > * > * 1) i801 signals transaction completion with one of these interrupts: > * INTR - Success > @@ -635,6 +659,8 @@ static irqreturn_t i801_host_notify_isr( > * occurs for each byte of a byte-by-byte to prepare the next byte. > * > * 3) Host Notify interrupts > + * > + * 4) SMBus Alert interrupts > */ > static irqreturn_t i801_isr(int irq, void *dev_id) > { > @@ -653,6 +679,12 @@ static irqreturn_t i801_isr(int irq, voi > return i801_host_notify_isr(priv); > } > > + if (priv->features & FEATURE_SMBUS_ALERT) { > + status = inb_p(SMBHSTSTS(priv)); > + if (status & SMBHSTSTS_SMBALERT_STS) > + return i801_smbus_alert_isr(priv); > + } > + > status = inb_p(SMBHSTSTS(priv)); > if (status & SMBHSTSTS_BYTE_DONE) > i801_isr_byte_done(priv); > @@ -1006,9 +1038,35 @@ static void i801_enable_host_notify(stru > outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); > } > > -static void i801_disable_host_notify(struct i801_priv *priv) > +static void i801_enable_smbus_alert(struct i2c_adapter *adapter) > { > - if (!(priv->features & FEATURE_HOST_NOTIFY)) > + struct i801_priv *priv = i2c_get_adapdata(adapter); > + > + if (!(priv->features & FEATURE_SMBUS_ALERT)) > + return; > + > + priv->ara = i2c_setup_smbus_alert(adapter, &priv->alert_data); > + if (!priv->ara) { > + dev_warn(&adapter->dev, "Failed to register ARA client\n"); > + > + /* Disable SMBus Alert interrupts */ > + if (!(SMBSLVCMD_SMBALERT_DIS & priv->original_slvcmd)) > + outb_p(SMBSLVCMD_SMBALERT_DIS | priv->original_slvcmd, > + SMBSLVCMD(priv)); > + return; > + } > + > + if (SMBSLVCMD_SMBALERT_DIS & priv->original_slvcmd) > + outb_p(~SMBSLVCMD_SMBALERT_DIS & priv->original_slvcmd, > + SMBSLVCMD(priv)); > + > + /* Clear SMBus Alert bit to allow a new notification */ > + outb_p(SMBHSTSTS_SMBALERT_STS, SMBHSTSTS(priv)); I'd rather handle the pending ALERT# not to lose the ALERTS from the boot time for instance. This clearing of the pending bit here doesn't help anyway, please refer below: > +} > + > +static void i801_restore_slvcmd(struct i801_priv *priv) > +{ > + if (!(priv->features & (FEATURE_HOST_NOTIFY | FEATURE_SMBUS_ALERT))) > return; > > outb_p(priv->original_slvcmd, SMBSLVCMD(priv)); > @@ -1823,8 +1881,8 @@ static int i801_probe(struct pci_dev *de > outb_p(inb_p(SMBAUXCTL(priv)) & > ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); > > - /* Remember original Host Notify setting */ > - if (priv->features & FEATURE_HOST_NOTIFY) I'd propose to disable the ALERT# for the time of setting up ARA here... > + /* Remember original Host Notify and SMBus Alert setting */ > + if (priv->features & (FEATURE_HOST_NOTIFY | FEATURE_SMBUS_ALERT)) > priv->original_slvcmd = inb_p(SMBSLVCMD(priv)); > > /* Default timeout in interrupt mode: 200 ms */ Because between these two hunks we enable the interrupts and we will jump into IRQ handler without ARA being set up. > @@ -1875,6 +1933,7 @@ static int i801_probe(struct pci_dev *de > } > > i801_enable_host_notify(&priv->adapter); > + i801_enable_smbus_alert(&priv->adapter); ... and re-enable ALERT# here again... > i801_probe_optional_slaves(priv); > /* We ignore errors - multiplexing is optional */ > @@ -1897,8 +1956,10 @@ static void i801_remove(struct pci_dev * > pm_runtime_forbid(&dev->dev); > pm_runtime_get_noresume(&dev->dev); > > - i801_disable_host_notify(priv); > + i801_restore_slvcmd(priv); > i801_del_mux(priv); > + if (priv->ara) > + i2c_unregister_device(priv->ara); > i2c_del_adapter(&priv->adapter); > i801_acpi_remove(priv); > pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); > @@ -1916,7 +1977,7 @@ static void i801_shutdown(struct pci_dev > struct i801_priv *priv = pci_get_drvdata(dev); > > /* Restore config registers to avoid hard hang on some systems */ > - i801_disable_host_notify(priv); > + i801_restore_slvcmd(priv); > pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); > } I can do some tests to prove my assumption regarding pending ALERT#. Or do you agree with the above and I should test only modified version with disable-registerARA-re-enable setup? What are your thoughts? -- Best regards, Alexander Sverdlin.