[v2,16/19] ASoC: tlv320aic31xx: Add short circuit detection support
diff mbox

Message ID 20171129213300.20021-17-afd@ti.com
State New
Headers show

Commit Message

Andrew F. Davis Nov. 29, 2017, 9:32 p.m. UTC
These devices support detecting and reporting short circuits across
the output stages. Add support for reporting these issue. Do this
by registering an interrupt if available and enabling this error
to trigger that interrupt in the device.

Signed-off-by: Andrew F. Davis <afd@ti.com>
---
 sound/soc/codecs/tlv320aic31xx.c | 42 ++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/tlv320aic31xx.h | 16 +++++++++++++++
 2 files changed, 58 insertions(+)

Comments

Mark Brown Dec. 1, 2017, 1:39 p.m. UTC | #1
On Wed, Nov 29, 2017 at 03:32:57PM -0600, Andrew F. Davis wrote:

> +static irqreturn_t aic31xx_irq(int irq, void *data)
> +{
> +	struct aic31xx_priv *aic31xx = (struct aic31xx_priv *)data;

There is no need to cast away from void, doing so can only mask bugs.

> +	ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
> +	if (ret) {
> +		dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
> +		return IRQ_NONE;
> +	}
> +
> +	if (value & AIC31XX_HPLSCDETECT)
> +		dev_err(dev, "Short circuit on Left output is detected\n");
> +	if (value & AIC31XX_HPRSCDETECT)
> +		dev_err(dev, "Short circuit on Right output is detected\n");
> +
> +	return IRQ_HANDLED;

This will report the interrupt as handled even if we didn't see an
interrupt we understand which will break shared interrupt lines.  At the
very least we should log other interrupt sources numerically.

> +	if (aic31xx->irq > 0) {
> +		regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
> +				   AIC31XX_GPIO1_FUNC_MASK,
> +				   AIC31XX_GPIO1_INT1 <<
> +				   AIC31XX_GPIO1_FUNC_SHIFT);

Is the interrupt only available on GPIO1?
Andrew F. Davis Dec. 1, 2017, 3:32 p.m. UTC | #2
On 12/01/2017 07:39 AM, Mark Brown wrote:
> On Wed, Nov 29, 2017 at 03:32:57PM -0600, Andrew F. Davis wrote:
> 
>> +static irqreturn_t aic31xx_irq(int irq, void *data)
>> +{
>> +	struct aic31xx_priv *aic31xx = (struct aic31xx_priv *)data;
> 
> There is no need to cast away from void, doing so can only mask bugs.
> 
>> +	ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
>> +		return IRQ_NONE;
>> +	}
>> +
>> +	if (value & AIC31XX_HPLSCDETECT)
>> +		dev_err(dev, "Short circuit on Left output is detected\n");
>> +	if (value & AIC31XX_HPRSCDETECT)
>> +		dev_err(dev, "Short circuit on Right output is detected\n");
>> +
>> +	return IRQ_HANDLED;
> 
> This will report the interrupt as handled even if we didn't see an
> interrupt we understand which will break shared interrupt lines.  At the
> very least we should log other interrupt sources numerically.
> 

Okay, I think I can make that work by checking if no bits are set in the
interrupt regs and returning early if not, IRQ_NONE.

>> +	if (aic31xx->irq > 0) {
>> +		regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
>> +				   AIC31XX_GPIO1_FUNC_MASK,
>> +				   AIC31XX_GPIO1_INT1 <<
>> +				   AIC31XX_GPIO1_FUNC_SHIFT);
> 
> Is the interrupt only available on GPIO1?
> 

Some devices can route this to GPIO2 IIRC.

I'm not sure how that would be supported, I think we would need to add
interrupt names to DT so users could specify which gpio they wired their
IRQ lines to.

interrupt = <&host 23>;
interrupt-name = "gpio2";

or similar?
Mark Brown Dec. 1, 2017, 3:57 p.m. UTC | #3
On Fri, Dec 01, 2017 at 09:32:12AM -0600, Andrew F. Davis wrote:
> On 12/01/2017 07:39 AM, Mark Brown wrote:

> > Is the interrupt only available on GPIO1?

> Some devices can route this to GPIO2 IIRC.

> I'm not sure how that would be supported, I think we would need to add
> interrupt names to DT so users could specify which gpio they wired their
> IRQ lines to.

> interrupt = <&host 23>;
> interrupt-name = "gpio2";

> or similar?

You could also use pinctrl an require the user to mux the interrupt in
whatever fashion makes sense for their device.
Andrew F. Davis Dec. 6, 2017, 5:15 p.m. UTC | #4
On 12/01/2017 09:57 AM, Mark Brown wrote:
> On Fri, Dec 01, 2017 at 09:32:12AM -0600, Andrew F. Davis wrote:
>> On 12/01/2017 07:39 AM, Mark Brown wrote:
> 
>>> Is the interrupt only available on GPIO1?
> 
>> Some devices can route this to GPIO2 IIRC.
> 
>> I'm not sure how that would be supported, I think we would need to add
>> interrupt names to DT so users could specify which gpio they wired their
>> IRQ lines to.
> 
>> interrupt = <&host 23>;
>> interrupt-name = "gpio2";
> 
>> or similar?
> 
> You could also use pinctrl an require the user to mux the interrupt in
> whatever fashion makes sense for their device.
> 

If done at that layer then no change is needed in the driver right? We
just request and use the IRQ passed to us from i2c data.
Andrew F. Davis Dec. 6, 2017, 8:22 p.m. UTC | #5
On 12/01/2017 09:32 AM, Andrew F. Davis wrote:
> On 12/01/2017 07:39 AM, Mark Brown wrote:
>> On Wed, Nov 29, 2017 at 03:32:57PM -0600, Andrew F. Davis wrote:
>>
>>> +static irqreturn_t aic31xx_irq(int irq, void *data)
>>> +{
>>> +	struct aic31xx_priv *aic31xx = (struct aic31xx_priv *)data;
>>
>> There is no need to cast away from void, doing so can only mask bugs.
>>
>>> +	ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
>>> +	if (ret) {
>>> +		dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
>>> +		return IRQ_NONE;
>>> +	}
>>> +
>>> +	if (value & AIC31XX_HPLSCDETECT)
>>> +		dev_err(dev, "Short circuit on Left output is detected\n");
>>> +	if (value & AIC31XX_HPRSCDETECT)
>>> +		dev_err(dev, "Short circuit on Right output is detected\n");
>>> +
>>> +	return IRQ_HANDLED;
>>
>> This will report the interrupt as handled even if we didn't see an
>> interrupt we understand which will break shared interrupt lines.  At the
>> very least we should log other interrupt sources numerically.
>>
> 
> Okay, I think I can make that work by checking if no bits are set in the
> interrupt regs and returning early if not, IRQ_NONE.
> 

This turned out to be more difficult than I expected, plus if I do
handle an interrupt it doesn't mean the other device did not right? So
this wouldn't fix shared lines as far as I can tell, but I don't
register it as shared so this should be fine.

As for your other suggestion of "log other interrupt sources
numerically", could you explain this or point to an example of what you
mean?

>>> +	if (aic31xx->irq > 0) {
>>> +		regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
>>> +				   AIC31XX_GPIO1_FUNC_MASK,
>>> +				   AIC31XX_GPIO1_INT1 <<
>>> +				   AIC31XX_GPIO1_FUNC_SHIFT);
>>
>> Is the interrupt only available on GPIO1?
>>
> 
> Some devices can route this to GPIO2 IIRC.
> 
> I'm not sure how that would be supported, I think we would need to add
> interrupt names to DT so users could specify which gpio they wired their
> IRQ lines to.
> 
> interrupt = <&host 23>;
> interrupt-name = "gpio2";
> 
> or similar?
>
Mark Brown Dec. 7, 2017, 12:03 p.m. UTC | #6
On Wed, Dec 06, 2017 at 02:22:39PM -0600, Andrew F. Davis wrote:
> On 12/01/2017 09:32 AM, Andrew F. Davis wrote:

> >> This will report the interrupt as handled even if we didn't see an
> >> interrupt we understand which will break shared interrupt lines.  At the
> >> very least we should log other interrupt sources numerically.

> > Okay, I think I can make that work by checking if no bits are set in the
> > interrupt regs and returning early if not, IRQ_NONE.

> This turned out to be more difficult than I expected, plus if I do
> handle an interrupt it doesn't mean the other device did not right? So
> this wouldn't fix shared lines as far as I can tell, but I don't
> register it as shared so this should be fine.

It'll mean that we don't offer the interrupt to anything else sharing
the line.

> As for your other suggestion of "log other interrupt sources
> numerically", could you explain this or point to an example of what you
> mean?

Just print out the bits that were set.
Andrew F. Davis Dec. 7, 2017, 3:37 p.m. UTC | #7
On 12/07/2017 06:03 AM, Mark Brown wrote:
> On Wed, Dec 06, 2017 at 02:22:39PM -0600, Andrew F. Davis wrote:
>> On 12/01/2017 09:32 AM, Andrew F. Davis wrote:
> 
>>>> This will report the interrupt as handled even if we didn't see an
>>>> interrupt we understand which will break shared interrupt lines.  At the
>>>> very least we should log other interrupt sources numerically.
> 
>>> Okay, I think I can make that work by checking if no bits are set in the
>>> interrupt regs and returning early if not, IRQ_NONE.
> 
>> This turned out to be more difficult than I expected, plus if I do
>> handle an interrupt it doesn't mean the other device did not right? So
>> this wouldn't fix shared lines as far as I can tell, but I don't
>> register it as shared so this should be fine.
> 
> It'll mean that we don't offer the interrupt to anything else sharing
> the line.
> 
>> As for your other suggestion of "log other interrupt sources
>> numerically", could you explain this or point to an example of what you
>> mean?
> 
> Just print out the bits that were set.
> 

I don't see anyone else doing this, what would that solve? Maybe I still
don't get what you mean here. :(
Mark Brown Dec. 7, 2017, 5:23 p.m. UTC | #8
On Thu, Dec 07, 2017 at 09:37:36AM -0600, Andrew F. Davis wrote:
> On 12/07/2017 06:03 AM, Mark Brown wrote:

> >> As for your other suggestion of "log other interrupt sources
> >> numerically", could you explain this or point to an example of what you
> >> mean?

> > Just print out the bits that were set.

> I don't see anyone else doing this, what would that solve? Maybe I still
> don't get what you mean here. :(

A good proportion of devices require explicit acks for interrupts and
only ack things they handled so don't have this issue, and otherwise
it's common to just handle all the interrupts that the device might
fire.  The goal is to not silently eat interrupts if the device starts
interrupting for some reason.

Patch
diff mbox

diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 77ae8f36a943..fd3109366377 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -164,6 +164,7 @@  struct aic31xx_priv {
 	unsigned int sysclk;
 	u8 p_div;
 	int rate_div_line;
+	int irq;
 };
 
 struct aic31xx_rate_divs {
@@ -1258,6 +1259,27 @@  static const struct acpi_device_id aic31xx_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match);
 #endif
 
+static irqreturn_t aic31xx_irq(int irq, void *data)
+{
+	struct aic31xx_priv *aic31xx = (struct aic31xx_priv *)data;
+	struct device *dev = aic31xx->dev;
+	unsigned int value;
+	int ret;
+
+	ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value);
+	if (ret) {
+		dev_err(dev, "Failed to read interrupt mask: %d\n", ret);
+		return IRQ_NONE;
+	}
+
+	if (value & AIC31XX_HPLSCDETECT)
+		dev_err(dev, "Short circuit on Left output is detected\n");
+	if (value & AIC31XX_HPRSCDETECT)
+		dev_err(dev, "Short circuit on Right output is detected\n");
+
+	return IRQ_HANDLED;
+}
+
 static int aic31xx_i2c_probe(struct i2c_client *i2c,
 			     const struct i2c_device_id *id)
 {
@@ -1280,6 +1302,7 @@  static int aic31xx_i2c_probe(struct i2c_client *i2c,
 		return ret;
 	}
 	aic31xx->dev = &i2c->dev;
+	aic31xx->irq = i2c->irq;
 
 	aic31xx->codec_type = id->driver_data;
 
@@ -1318,6 +1341,25 @@  static int aic31xx_i2c_probe(struct i2c_client *i2c,
 		return ret;
 	}
 
+	if (aic31xx->irq > 0) {
+		regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
+				   AIC31XX_GPIO1_FUNC_MASK,
+				   AIC31XX_GPIO1_INT1 <<
+				   AIC31XX_GPIO1_FUNC_SHIFT);
+
+		regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL,
+			     AIC31XX_SC);
+
+		ret = devm_request_threaded_irq(aic31xx->dev, aic31xx->irq,
+						NULL, aic31xx_irq,
+						IRQF_ONESHOT, "aic31xx-irq",
+						aic31xx);
+		if (ret) {
+			dev_err(aic31xx->dev, "Unable to request IRQ\n");
+			return ret;
+		}
+	}
+
 	if (aic31xx->codec_type & DAC31XX_BIT)
 		return snd_soc_register_codec(&i2c->dev,
 				&soc_codec_driver_aic31xx,
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index ab94e6a0c742..9dc85b6f6ad3 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -184,6 +184,22 @@  enum aic31xx_type {
 #define AIC31XX_SC			BIT(3)
 #define AIC31XX_ENGINE			BIT(2)
 
+/* AIC31XX_GPIO1 */
+#define AIC31XX_GPIO1_FUNC_MASK		GENMASK(5, 2)
+#define AIC31XX_GPIO1_FUNC_SHIFT	2
+#define AIC31XX_GPIO1_DISABLED		0x00
+#define AIC31XX_GPIO1_INPUT		0x01
+#define AIC31XX_GPIO1_GPI		0x02
+#define AIC31XX_GPIO1_GPO		0x03
+#define AIC31XX_GPIO1_CLKOUT		0x04
+#define AIC31XX_GPIO1_INT1		0x05
+#define AIC31XX_GPIO1_INT2		0x06
+#define AIC31XX_GPIO1_ADC_WCLK		0x07
+#define AIC31XX_GPIO1_SBCLK		0x08
+#define AIC31XX_GPIO1_SWCLK		0x09
+#define AIC31XX_GPIO1_ADC_MOD_CLK	0x10
+#define AIC31XX_GPIO1_SDOUT		0x11
+
 /* AIC31XX_DACSETUP */
 #define AIC31XX_SOFTSTEP_MASK		GENMASK(1, 0)