Re: [PATCH] i801_smbus: clear SMBALERT status bit and disable SMBALERT interrupt

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.




[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux