/* * This is the driver for Qualcomm MPM (MSM Power Manager) interrupt controller, * which is commonly found on Qualcomm SoCs built on the RPM architecture. * Sitting in always-on domain, MPM monitors the wakeup interrupts when SoC is * asleep, and wakes up the AP when one of those interrupts occurs. This driver * doesn't directly access physical MPM registers though. Instead, the access * is bridged via a piece of internal memory (SRAM) that is accessible to both * AP and RPM. This piece of memory is called 'vMPM' in the driver. * * When SoC is awake, the vMPM is owned by AP and the register setup by this * driver all happens on vMPM. When AP is about to get power collapsed, the * driver sends a mailbox notification to RPM, which will take over the vMPM * ownership and dump vMPM into physical MPM registers. On wakeup, AP is woken * up by a MPM pin/interrupt, and RPM will copy STATUS registers into vMPM. * Then AP start owning vMPM again. * * vMPM register map: * * 31 0 * +--------------------------------+ * | TIMER0 | 0x00 * +--------------------------------+ * | TIMER1 | 0x04 * +--------------------------------+ * | ENABLE0 | 0x08 * +--------------------------------+ * | ... | ... * +--------------------------------+ * | ENABLEn | * +--------------------------------+ * | FALLING_EDGE0 | * +--------------------------------+ * | ... | * +--------------------------------+ * | STATUSn | * +--------------------------------+ * * n = DIV_ROUND_UP(pin_cnt, 32) *
*/
/* Triggered by RPM when system resumes from deep sleep */ static irqreturn_t qcom_mpm_handler(int irq, void *dev_id)
{ struct qcom_mpm_priv *priv = dev_id; unsignedlong enable, pending;
irqreturn_t ret = IRQ_NONE; unsignedlong flags; int i, j;
for (i = 0; i < priv->reg_stride; i++) {
raw_spin_lock_irqsave(&priv->lock, flags);
enable = qcom_mpm_read(priv, MPM_REG_ENABLE, i);
pending = qcom_mpm_read(priv, MPM_REG_STATUS, i);
pending &= enable;
raw_spin_unlock_irqrestore(&priv->lock, flags);
/* If we have a handle to an RPM message ram partition, use it. */
msgram_np = of_parse_phandle(np, "qcom,rpm-msg-ram", 0); if (msgram_np) {
ret = of_address_to_resource(msgram_np, 0, &res); if (ret) {
of_node_put(msgram_np); return ret;
}
/* Don't use devm_ioremap_resource, as we're accessing a shared region. */
priv->base = devm_ioremap(dev, res.start, resource_size(&res));
of_node_put(msgram_np); if (!priv->base) return -ENOMEM;
} else { /* Otherwise, fall back to simple MMIO. */
priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base);
}
for (i = 0; i < priv->reg_stride; i++) {
qcom_mpm_write(priv, MPM_REG_ENABLE, i, 0);
qcom_mpm_write(priv, MPM_REG_FALLING_EDGE, i, 0);
qcom_mpm_write(priv, MPM_REG_RISING_EDGE, i, 0);
qcom_mpm_write(priv, MPM_REG_POLARITY, i, 0);
qcom_mpm_write(priv, MPM_REG_STATUS, i, 0);
}
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
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.