summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Zhang <benzh@chromium.org>2019-06-18 19:45:55 -0400
committerMark Brown <broonie@kernel.org>2019-06-19 07:46:44 -0400
commitdf9091e9d3f4500bc6fb15f5d2a1c2614f67004c (patch)
treea41e8bc8b26d6b913c51d193ce94af6ce6e07362
parent4f7b018b55db0361718161e1471d8b7a16fdfa47 (diff)
ASoC: rt5677: handle concurrent interrupts
The rt5677 driver writes to the IRQ control register within the IRQ handler in order to flip the polarity of the interrupts that have been signalled. If an interrupt fires in the interval between the regmap_read and the regmap_write, it will not trigger a new call to rt5677_irq. Add a bounded loop to rt5677_irq that keeps checking interrupts until none are seen, so that any interrupts that are signalled in that interval are correctly handled. Signed-off-by: Ben Zhang <benzh@chromium.org> Signed-off-by: Fletcher Woodruff <fletcherw@chromium.org> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/codecs/rt5677.c71
1 files changed, 45 insertions, 26 deletions
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index b5ae61ff87af..202af7135f07 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5072,38 +5072,57 @@ static const struct rt5677_irq_desc rt5677_irq_descs[] = {
5072static irqreturn_t rt5677_irq(int unused, void *data) 5072static irqreturn_t rt5677_irq(int unused, void *data)
5073{ 5073{
5074 struct rt5677_priv *rt5677 = data; 5074 struct rt5677_priv *rt5677 = data;
5075 int ret = 0, i, reg_irq, virq; 5075 int ret = 0, loop, i, reg_irq, virq;
5076 bool irq_fired = false; 5076 bool irq_fired = false;
5077 5077
5078 mutex_lock(&rt5677->irq_lock); 5078 mutex_lock(&rt5677->irq_lock);
5079 /* Read interrupt status */
5080 ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, &reg_irq);
5081 if (ret) {
5082 dev_err(rt5677->dev, "failed reading IRQ status: %d\n", ret);
5083 goto exit;
5084 }
5085 5079
5086 for (i = 0; i < RT5677_IRQ_NUM; i++) { 5080 /*
5087 if (reg_irq & rt5677_irq_descs[i].status_mask) { 5081 * Loop to handle interrupts until the last i2c read shows no pending
5088 irq_fired = true; 5082 * irqs. The interrupt line is shared by multiple interrupt sources.
5089 virq = irq_find_mapping(rt5677->domain, i); 5083 * After the regmap_read() below, a new interrupt source line may
5090 if (virq) 5084 * become high before the regmap_write() finishes, so there isn't a
5091 handle_nested_irq(virq); 5085 * rising edge on the shared interrupt line for the new interrupt. Thus,
5092 5086 * the loop is needed to avoid missing irqs.
5093 /* Clear the interrupt by flipping the polarity of the 5087 *
5094 * interrupt source line that fired 5088 * A safeguard of 20 loops is used to avoid hanging in the irq handler
5095 */ 5089 * if there is something wrong with the interrupt status update. The
5096 reg_irq ^= rt5677_irq_descs[i].polarity_mask; 5090 * interrupt sources here are audio jack plug/unplug events which
5091 * shouldn't happen at a high frequency for a long period of time.
5092 * Empirically, more than 3 loops have never been seen.
5093 */
5094 for (loop = 0; loop < 20; loop++) {
5095 /* Read interrupt status */
5096 ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, &reg_irq);
5097 if (ret) {
5098 dev_err(rt5677->dev, "failed reading IRQ status: %d\n",
5099 ret);
5100 goto exit;
5097 } 5101 }
5098 }
5099 5102
5100 if (!irq_fired) 5103 irq_fired = false;
5101 goto exit; 5104 for (i = 0; i < RT5677_IRQ_NUM; i++) {
5102 5105 if (reg_irq & rt5677_irq_descs[i].status_mask) {
5103 ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq); 5106 irq_fired = true;
5104 if (ret) { 5107 virq = irq_find_mapping(rt5677->domain, i);
5105 dev_err(rt5677->dev, "failed updating IRQ status: %d\n", ret); 5108 if (virq)
5106 goto exit; 5109 handle_nested_irq(virq);
5110
5111 /* Clear the interrupt by flipping the polarity
5112 * of the interrupt source line that fired
5113 */
5114 reg_irq ^= rt5677_irq_descs[i].polarity_mask;
5115 }
5116 }
5117 if (!irq_fired)
5118 goto exit;
5119
5120 ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
5121 if (ret) {
5122 dev_err(rt5677->dev, "failed updating IRQ status: %d\n",
5123 ret);
5124 goto exit;
5125 }
5107 } 5126 }
5108exit: 5127exit:
5109 mutex_unlock(&rt5677->irq_lock); 5128 mutex_unlock(&rt5677->irq_lock);