static void wcd9xxx_irq_sync_unlock(struct irq_data *data) { struct wcd9xxx *wcd9xxx = irq_data_get_irq_chip_data(data); int i; if (ARRAY_SIZE(wcd9xxx->irq_masks_cur) > WCD9XXX_NUM_IRQ_REGS || ARRAY_SIZE(wcd9xxx->irq_masks_cache) > WCD9XXX_NUM_IRQ_REGS) { pr_err("%s: Array Size out of bound\n", __func__); return; } for (i = 0; i < ARRAY_SIZE(wcd9xxx->irq_masks_cur); i++) { /* If there's been a change in the mask write it back * to the hardware. */ if (wcd9xxx->irq_masks_cur[i] != wcd9xxx->irq_masks_cache[i]) { wcd9xxx->irq_masks_cache[i] = wcd9xxx->irq_masks_cur[i]; wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MASK0 + i, wcd9xxx->irq_masks_cur[i]); } } mutex_unlock(&wcd9xxx->irq_lock); }
static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit) { if ((irqbit <= WCD9XXX_IRQ_MBHC_INSERTION) && (irqbit >= WCD9XXX_IRQ_MBHC_REMOVAL)) { wcd9xxx_nested_irq_lock(wcd9xxx); wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 + BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit)); if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02); handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit)); wcd9xxx_nested_irq_unlock(wcd9xxx); } else { wcd9xxx_nested_irq_lock(wcd9xxx); handle_nested_irq(phyirq_to_virq(wcd9xxx, irqbit)); wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_CLEAR0 + BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit)); if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MODE, 0x02); wcd9xxx_nested_irq_unlock(wcd9xxx); } }
int wcd9xxx_irq_init(struct wcd9xxx *wcd9xxx) { int i, ret; u8 irq_level[wcd9xxx_num_irq_regs(wcd9xxx)]; mutex_init(&wcd9xxx->irq_lock); mutex_init(&wcd9xxx->nested_irq_lock); wcd9xxx->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx); if (!wcd9xxx->irq) { pr_warn("%s: irq driver is not yet initialized\n", __func__); mutex_destroy(&wcd9xxx->irq_lock); mutex_destroy(&wcd9xxx->nested_irq_lock); return -EPROBE_DEFER; } pr_debug("%s: probed irq %d\n", __func__, wcd9xxx->irq); /* Setup downstream IRQs */ ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx); if (ret) { pr_err("%s: Failed to setup downstream IRQ\n", __func__); wcd9xxx_irq_put_upstream_irq(wcd9xxx); mutex_destroy(&wcd9xxx->irq_lock); mutex_destroy(&wcd9xxx->nested_irq_lock); return ret; } /* All other wcd9xxx interrupts are edge triggered */ wcd9xxx->irq_level_high[0] = true; /* mask all the interrupts */ memset(irq_level, 0, wcd9xxx_num_irq_regs(wcd9xxx)); for (i = 0; i < wcd9xxx->num_irqs; i++) { wcd9xxx->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); wcd9xxx->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); irq_level[BIT_BYTE(i)] |= wcd9xxx->irq_level_high[i] << (i % BITS_PER_BYTE); } for (i = 0; i < wcd9xxx_num_irq_regs(wcd9xxx); i++) { /* Initialize interrupt mask and level registers */ wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_LEVEL0 + i, irq_level[i]); wcd9xxx_reg_write(wcd9xxx, WCD9XXX_A_INTR_MASK0 + i, wcd9xxx->irq_masks_cur[i]); } ret = request_threaded_irq(wcd9xxx->irq, NULL, wcd9xxx_irq_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "wcd9xxx", wcd9xxx); if (ret != 0) dev_err(wcd9xxx->dev, "Failed to request IRQ %d: %d\n", wcd9xxx->irq, ret); else { ret = enable_irq_wake(wcd9xxx->irq); if (ret == 0) { ret = device_init_wakeup(wcd9xxx->dev, 1); if (ret) { dev_err(wcd9xxx->dev, "Failed to init device" "wakeup : %d\n", ret); disable_irq_wake(wcd9xxx->irq); } } else dev_err(wcd9xxx->dev, "Failed to set wake interrupt on" " IRQ %d: %d\n", wcd9xxx->irq, ret); if (ret) free_irq(wcd9xxx->irq, wcd9xxx); } if (ret) { pr_err("%s: Failed to init wcd9xxx irq\n", __func__); wcd9xxx_irq_put_upstream_irq(wcd9xxx); mutex_destroy(&wcd9xxx->irq_lock); mutex_destroy(&wcd9xxx->nested_irq_lock); } return ret; }
static irqreturn_t wcd9xxx_irq_thread(int irq, void *data) { int ret; int i; struct intr_data irqdata; char linebuf[128]; static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1); struct wcd9xxx_core_resource *wcd9xxx_res = data; int num_irq_regs = wcd9xxx_res->num_irq_regs; u8 status[num_irq_regs], status1[num_irq_regs]; if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) { dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n"); return IRQ_NONE; } if (!wcd9xxx_res->codec_bulk_read) { dev_err(wcd9xxx_res->dev, "%s: Codec Bulk Register read callback not supplied\n", __func__); goto err_disable_irq; } ret = wcd9xxx_res->codec_bulk_read(wcd9xxx_res, WCD9XXX_A_INTR_STATUS0, num_irq_regs, status); if (ret < 0) { dev_err(wcd9xxx_res->dev, "Failed to read interrupt status: %d\n", ret); goto err_disable_irq; } /* Apply masking */ for (i = 0; i < num_irq_regs; i++) status[i] &= ~wcd9xxx_res->irq_masks_cur[i]; memcpy(status1, status, sizeof(status1)); /* Find out which interrupt was triggered and call that interrupt's * handler function * * Since codec has only one hardware irq line which is shared by * codec's different internal interrupts, so it's possible master irq * handler dispatches multiple nested irq handlers after breaking * order. Dispatch interrupts in the order that is maintained by * the interrupt table. */ for (i = 0; i < wcd9xxx_res->intr_table_size; i++) { irqdata = wcd9xxx_res->intr_table[i]; if (status[BIT_BYTE(irqdata.intr_num)] & BYTE_BIT_MASK(irqdata.intr_num)) { wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata); status1[BIT_BYTE(irqdata.intr_num)] &= ~BYTE_BIT_MASK(irqdata.intr_num); } } /* * As a failsafe if unhandled irq is found, clear it to prevent * interrupt storm. * Note that we can say there was an unhandled irq only when no irq * handled by nested irq handler since Taiko supports qdsp as irqs' * destination for few irqs. Therefore driver shouldn't clear pending * irqs when few handled while few others not. */ if (unlikely(!memcmp(status, status1, sizeof(status)))) { if (__ratelimit(&ratelimit)) { pr_warn("%s: Unhandled irq found\n", __func__); hex_dump_to_buffer(status, sizeof(status), 16, 1, linebuf, sizeof(linebuf), false); pr_warn("%s: status0 : %s\n", __func__, linebuf); hex_dump_to_buffer(status1, sizeof(status1), 16, 1, linebuf, sizeof(linebuf), false); pr_warn("%s: status1 : %s\n", __func__, linebuf); } memset(status, 0xff, num_irq_regs); wcd9xxx_bulk_write(wcd9xxx_res, WCD9XXX_A_INTR_CLEAR0, num_irq_regs, status); if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) wcd9xxx_reg_write(wcd9xxx_res, WCD9XXX_A_INTR_MODE, 0x02); } wcd9xxx_unlock_sleep(wcd9xxx_res); return IRQ_HANDLED; err_disable_irq: dev_err(wcd9xxx_res->dev, "Disable irq %d\n", wcd9xxx_res->irq); disable_irq_wake(wcd9xxx_res->irq); disable_irq_nosync(wcd9xxx_res->irq); wcd9xxx_unlock_sleep(wcd9xxx_res); return IRQ_NONE; }