// SPDX-License-Identifier: GPL-2.0-or-later /* * twl6030-irq.c - TWL6030 irq support * * Copyright (C) 2005-2009 Texas Instruments, Inc. * * Modifications to defer interrupt handling to a kernel thread: * Copyright (C) 2006 MontaVista Software, Inc. * * Based on tlv320aic23.c: * Copyright (c) by Kai Svahn <kai.svahn@nokia.com> * * Code cleanup and modifications to IRQ handler. * by syed khasim <x0khasim@ti.com> * * TWL6030 specific code and IRQ handling changes by * Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com> * Balaji T K <balajitk@ti.com>
*/
/* * TWL6030 (unlike its predecessors, which had two level interrupt handling) * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C. * It exposes status bits saying who has raised an interrupt. There are * three mask registers that corresponds to these status registers, that * enables/disables these interrupts. * * We set up IRQs starting at a platform-specified base. An interrupt map table, * specifies mapping between interrupt number and the associated module.
*/ #define TWL6030_NR_IRQS 20
staticint twl6030_interrupt_mapping[24] = {
PWR_INTR_OFFSET, /* Bit 0 PWRON */
PWR_INTR_OFFSET, /* Bit 1 RPWRON */
PWR_INTR_OFFSET, /* Bit 2 BAT_VLOW */
RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */
RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */
HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */
SMPSLDO_INTR_OFFSET, /* Bit 6 VXXX_SHORT */
SMPSLDO_INTR_OFFSET, /* Bit 7 VMMC_SHORT */
SMPSLDO_INTR_OFFSET, /* Bit 8 VUSIM_SHORT */
BATDETECT_INTR_OFFSET, /* Bit 9 BAT */
SIMDETECT_INTR_OFFSET, /* Bit 10 SIM */
MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */
RSV_INTR_OFFSET, /* Bit 12 Reserved */
MADC_INTR_OFFSET, /* Bit 13 GPADC_RT_EOC */
MADC_INTR_OFFSET, /* Bit 14 GPADC_SW_EOC */
GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */
USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */
USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */
USBOTG_INTR_OFFSET, /* Bit 18 ID */
USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */
CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */
CHARGERFAULT_INTR_OFFSET, /* Bit 21 EXT_CHRG */
CHARGERFAULT_INTR_OFFSET, /* Bit 22 INT_CHRG */
RSV_INTR_OFFSET, /* Bit 23 Reserved */
};
staticint twl6032_interrupt_mapping[24] = {
PWR_INTR_OFFSET, /* Bit 0 PWRON */
PWR_INTR_OFFSET, /* Bit 1 RPWRON */
PWR_INTR_OFFSET, /* Bit 2 SYS_VLOW */
RTC_INTR_OFFSET, /* Bit 3 RTC_ALARM */
RTC_INTR_OFFSET, /* Bit 4 RTC_PERIOD */
HOTDIE_INTR_OFFSET, /* Bit 5 HOT_DIE */
SMPSLDO_INTR_OFFSET, /* Bit 6 VXXX_SHORT */
PWR_INTR_OFFSET, /* Bit 7 SPDURATION */
PWR_INTR_OFFSET, /* Bit 8 WATCHDOG */
BATDETECT_INTR_OFFSET, /* Bit 9 BAT */
SIMDETECT_INTR_OFFSET, /* Bit 10 SIM */
MMCDETECT_INTR_OFFSET, /* Bit 11 MMC */
MADC_INTR_OFFSET, /* Bit 12 GPADC_RT_EOC */
MADC_INTR_OFFSET, /* Bit 13 GPADC_SW_EOC */
GASGAUGE_INTR_OFFSET, /* Bit 14 CC_EOC */
GASGAUGE_INTR_OFFSET, /* Bit 15 CC_AUTOCAL */
USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */
USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */
USBOTG_INTR_OFFSET, /* Bit 18 ID */
USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */
CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */
CHARGERFAULT_INTR_OFFSET, /* Bit 21 EXT_CHRG */
CHARGERFAULT_INTR_OFFSET, /* Bit 22 INT_CHRG */
RSV_INTR_OFFSET, /* Bit 23 Reserved */
};
switch (pm_event) { case PM_SUSPEND_PREPARE:
chained_wakeups = atomic_read(&pdata->wakeirqs);
if (chained_wakeups && !pdata->irq_wake_enabled) { if (enable_irq_wake(pdata->twl_irq))
pr_err("twl6030 IRQ wake enable failed\n"); else
pdata->irq_wake_enabled = true;
} elseif (!chained_wakeups && pdata->irq_wake_enabled) {
disable_irq_wake(pdata->twl_irq);
pdata->irq_wake_enabled = false;
}
disable_irq(pdata->twl_irq); break;
case PM_POST_SUSPEND:
enable_irq(pdata->twl_irq); break;
default: break;
}
return NOTIFY_DONE;
}
/* * Threaded irq handler for the twl6030 interrupt. * We query the interrupt controller in the twl6030 to determine * which module is generating the interrupt request and call * handle_nested_irq for that module.
*/ static irqreturn_t twl6030_irq_thread(int irq, void *data)
{ int i, ret; union {
u8 bytes[4];
__le32 int_sts;
} sts;
u32 int_sts; /* sts.int_sts converted to CPU endianness */ struct twl6030_irq *pdata = data;
/* read INT_STS_A, B and C in one shot using a burst read */
ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes, REG_INT_STS_A, 3); if (ret) {
pr_warn("twl6030_irq: I2C error %d reading PIH ISR\n", ret); return IRQ_HANDLED;
}
sts.bytes[3] = 0; /* Only 24 bits are valid*/
/* * Since VBUS status bit is not reliable for VBUS disconnect * use CHARGER VBUS detection status bit instead.
*/ if (sts.bytes[2] & 0x10)
sts.bytes[2] |= 0x08;
int_sts = le32_to_cpu(sts.int_sts); for (i = 0; int_sts; int_sts >>= 1, i++) if (int_sts & 0x1) { int module_irq =
irq_find_mapping(pdata->irq_domain,
pdata->irq_mapping_tbl[i]); if (module_irq)
handle_nested_irq(module_irq); else
pr_err("twl6030_irq: Unmapped PIH ISR %u detected\n",
i);
pr_debug("twl6030_irq: PIH ISR %u, virq%u\n",
i, module_irq);
}
/* * NOTE: * Simulation confirms that documentation is wrong w.r.t the * interrupt status clear operation. A single *byte* write to * any one of STS_A to STS_C register results in all three * STS registers being reset. Since it does not matter which * value is written, all three registers are cleared on a * single byte write, so we just use 0x0 to clear.
*/
ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A); if (ret)
pr_warn("twl6030_irq: I2C error in clearing PIH ISR\n");
twl6030_irq = devm_kzalloc(dev, sizeof(*twl6030_irq), GFP_KERNEL); if (!twl6030_irq) return -ENOMEM;
mask[0] = 0xFF;
mask[1] = 0xFF;
mask[2] = 0xFF;
/* mask all int lines */
status = twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3); /* mask all int sts */
status |= twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3); /* clear INT_STS_A,B,C */
status |= twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);
void twl6030_exit_irq(void)
{ if (twl6030_irq && twl6030_irq->twl_irq) {
unregister_pm_notifier(&twl6030_irq->pm_nb);
free_irq(twl6030_irq->twl_irq, NULL); /* * TODO: IRQ domain and allocated nested IRQ descriptors * should be freed somehow here. Now It can't be done, because * child devices will not be deleted during removing of * TWL Core driver and they will still contain allocated * virt IRQs in their Resources tables. * The same prevents us from using devm_request_threaded_irq() * in this module.
*/
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.