From nobody Mon Jun 8 08:28:04 2026 Received: from outbound7.mail.transip.nl (outbound7.mail.transip.nl [136.144.136.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4A6B82F549F; Sun, 31 May 2026 04:09:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780200561; cv=none; b=qm6IK3MVZ0eqWe2zWWe/U7BlkkW03T6Qc88lYaic6Kh6oW4gS4rRz/fFnLuExywadsQXF4yysoczzhssaG3ZG9JIWQtKDWwbgYDwj1hABxObK+N2tAqjxzJXFI1fo1CISgRJFVb0rrpHHS3x2JHjDp2RIrTd7DUT0Y4cBMwFW5E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780200561; c=relaxed/simple; bh=uwlX8PMjADCDQEuq4aieXUonap0uly45GgLHHNp7B8s=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eDFqwhyoKZ1qpB9lvqZNY5QhQIQn485WsRcsAu+JnXXof6ZK8QOflUC/vV7BDPpDSE4+eUhQPIm9aPAP3YUH+KjA/MI4Lx9JuKXrsxaMsE1bOKWuxRJmkpFoZZXm8KwWN6VfQdR3Wi5lHcJSsKZhrbDqgTx++/5IwUxve5tSLY8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=sT9FdjdF; arc=none smtp.client-ip=136.144.136.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="sT9FdjdF" Received: from submission1.mail.transip.nl (unknown [10.100.4.70]) by outbound7.mail.transip.nl (Postfix) with ESMTP id 4gSkB16LSlzQvvWt; Sun, 31 May 2026 06:09:13 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission1.mail.transip.nl (Postfix) with ESMTPA id 4gSkB108KYzJjhXv; Sun, 31 May 2026 06:09:13 +0200 (CEST) From: Herman van Hazendonk To: Bjorn Andersson , Clark Williams , Conor Dooley , devicetree@vger.kernel.org, Konrad Dybcio , Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-rt-devel@lists.linux.dev, Rob Herring , Sebastian Andrzej Siewior , Steven Rostedt , Thomas Gleixner , van Hazendonk Subject: [PATCH v2 1/2] dt-bindings: interrupt-controller: qcom: add msm8660-mpm Date: Sun, 31 May 2026 06:09:10 +0200 Message-ID: <4cca687dba2d1c341613a13d3e4b4a156b0d5531.1780195817.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: ClueGetter at submission1.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780200553; h=from:subject:to:references: in-reply-to:date:mime-version; bh=IOnVW/3WZHj8cYeEeQr7f0EyPtew8KoJY81FvzBRJSs=; b=sT9FdjdFDQe4uB/WL6B6ovQc6Vy5evEw/otAMlxYkJeeo2gKJPLVNoORpXJa7VW23j+VyI AlQg0evTYG+cUzlp2eKkvPyfv7W5bKgjfVkRg56kGsUIqde02ScWYcAFQloXvE4IJ9qKnh f4kg/ckLxNqq145ClZFkP/r49OJNVzk5NwfTauA6Kz41YMV60NL/tHDfQXA1R0+X9rXsP6 RNCU3yeEU1F7naZUllYP6LR7kUJUxIYqASE5dAZ7n7/fmDz0vMresjxqgBVrE4gDG35Lhy 3lGbhngyzvDdvs8mASztVhM2fvsX79aXRKx1aCevzHzUvkRyEirCD2Jhpk+KNA== X-Report-Abuse-To: abuse@transip.nl Content-Type: text/plain; charset="utf-8" Add the binding for the Modem Power Manager (MPM) interrupt controller on the MSM8x60 family (MSM8260/MSM8660/APQ8060). The MPM is a small wake-capable interrupt aggregator that lets the SoC stay in low-power states while a small set of GIC SPIs continues to be monitored and can wake the system on an edge. The binding describes its register region (in the RPM syscon), the GIC SPI it raises on a wake event, the GIC mailbox it shares with the RPM, and the per-pin wake-source mapping table. Signed-off-by: Herman van Hazendonk --- .../qcom,msm8660-mpm.yaml | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/= qcom,msm8660-mpm.yaml diff --git a/Documentation/devicetree/bindings/interrupt-controller/qcom,ms= m8660-mpm.yaml b/Documentation/devicetree/bindings/interrupt-controller/qco= m,msm8660-mpm.yaml new file mode 100644 index 000000000000..1288e83d75d6 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/qcom,msm8660-m= pm.yaml @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/qcom,msm8660-mpm.y= aml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8x60 MPM wakeup interrupt controller + +maintainers: + - Herman van Hazendonk + +description: | + The MSM Power Manager (MPM) on the MSM8x60 family (MSM8260/MSM8660/ + APQ8060) is an always-on hardware block that keeps a set of wake + interrupts alive while the application processor is powered down. + + The vMPM (virtual MPM) registers live INSIDE the RPM control block: + the request window (ENABLE, DETECT_CTL, POLARITY, CLEAR) starts at + RPM_BASE + 0x9d8 and the status window starts 0x420 above the + request window. The reg property points at this vMPM sub-region; + the qcom,rpm-msm8660 driver maps the surrounding RPM control area + for its own use, so the two mappings overlap but neither claims + exclusive ownership of the other's range. + + Notification of new wake-source configuration is delivered via the + qcom-apcs-ipc mailbox (writing GCC + 0x008 bit 1). + + This binding is MSM8x60-specific. Newer Qualcomm SoCs (sm6375, etc.) + use the qcom,mpm binding which assumes a dedicated MPM SRAM region + and an IPCC mailbox - neither holds on MSM8x60. The driver lives at + drivers/irqchip/irq-msm8660-mpm.c. + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + const: qcom,msm8660-mpm + + reg: + maxItems: 1 + description: | + vMPM register window inside the RPM control block. Must cover + the request registers at offset 0 of the window and the status + registers at offset 0x420 of the window. A length of 0x440 is + sufficient for the four-word request banks and the status bank. + + interrupts: + maxItems: 1 + description: + IPC IRQ raised by MPM when one of the enabled wake sources fires. + On MSM8x60 this is wired to GIC SPI 2. + + mboxes: + maxItems: 1 + description: + Mailbox channel used to notify MPM that the vMPM request + registers have been updated. On MSM8x60 this is the + qcom-apcs-ipc mailbox channel 1 (writes GCC + 0x008 bit 1). + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + description: + First cell is the MPM pin number / parent GIC SPI; second cell + is the trigger type. + + qcom,mpm-pin-count: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Total number of MPM pins exposed. + + qcom,mpm-pin-map: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: MPM pin number + - description: GIC SPI number this pin maps to + description: | + List of (MPM-pin, GIC-SPI) tuples for wake sources that have a + corresponding GIC IRQ. Consumers route their interrupts through + the MPM hierarchical irqdomain to take advantage of these + mappings. + + Raw wake pins like SDC3/4 DATx (pins 21-24) are NOT listed + here - they have no GIC SPI mapping and are accessed through + the msm8660_mpm_set_pin_wake() / msm8660_mpm_enable_pin() C API + by consumer drivers. + +required: + - compatible + - reg + - interrupts + - mboxes + - interrupt-controller + - '#interrupt-cells' + - qcom,mpm-pin-count + +additionalProperties: false + +examples: + - | + #include + + soc { + #address-cells =3D <1>; + #size-cells =3D <1>; + + msm8660_mpm: interrupt-controller@1049d8 { + compatible =3D "qcom,msm8660-mpm"; + reg =3D <0x001049d8 0x440>; + interrupts =3D ; + mboxes =3D <&gcc_ipc 1>; + interrupt-controller; + #interrupt-cells =3D <2>; + interrupt-parent =3D <&intc>; + qcom,mpm-pin-count =3D <64>; + qcom,mpm-pin-map =3D <25 100>, + <26 50>, + <27 79>; + }; + }; --=20 2.43.0 From nobody Mon Jun 8 08:28:04 2026 Received: from outbound9.mail.transip.nl (outbound9.mail.transip.nl [136.144.136.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9169B3019D8; Sun, 31 May 2026 04:09:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.144.136.11 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780200562; cv=none; b=u2wvfHzVJ2wX/nXaLh0i3a9pDlwgCxEflBto/VUNEFJ0wR1+iqF5VA//IZBNlqnIzt8LcKF8Af613nAXlnEegYnPbwqqUn0J/SecNPjUIY6HsgUei8i0rkpRNIkBmwPbD9PFzmiHA70DJenxI+bEfDvGcAOggDFDoavP2KYfr6A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780200562; c=relaxed/simple; bh=BIFYJKjy8Y89tf19+LrYGrHSks73m00/Gv/0bqPQFY8=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Mo6RR1/9bg4mOEdjTKjD1BbsoanBdmzExOXzjVTe4KvHDFuIWXefAV0SrgJ/i+CluKlTQmTrDE1YfxyxYA5mHCaEF+eOzVhXR2ySR4D3SpKDkJVxug4wwfZVfrsuxuvLLKv4PWLz7iLT3IKkwE0t80Yb76iNFekpuTg/aNy/Rng= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org; spf=pass smtp.mailfrom=herrie.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b=AcCzrxuz; arc=none smtp.client-ip=136.144.136.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=herrie.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=herrie.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=herrie.org header.i=@herrie.org header.b="AcCzrxuz" Received: from submission1.mail.transip.nl (unknown [10.100.4.70]) by outbound9.mail.transip.nl (Postfix) with ESMTP id 4gSkB260XNzTPNcL; Sun, 31 May 2026 06:09:14 +0200 (CEST) Received: from herrie-desktop.. (180-93-184-31.ftth.glasoperator.nl [31.184.93.180]) by submission1.mail.transip.nl (Postfix) with ESMTPA id 4gSkB16fbnzJjhXw; Sun, 31 May 2026 06:09:13 +0200 (CEST) From: Herman van Hazendonk To: Bjorn Andersson , Clark Williams , Conor Dooley , devicetree@vger.kernel.org, Konrad Dybcio , Krzysztof Kozlowski , linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-rt-devel@lists.linux.dev, Rob Herring , Sebastian Andrzej Siewior , Steven Rostedt , Thomas Gleixner , van Hazendonk Subject: [PATCH v2 2/2] irqchip: add MSM8x60 MPM wakeup interrupt controller driver Date: Sun, 31 May 2026 06:09:11 +0200 Message-ID: <0133fce127c7507bbb907d0258ad69ac8d753218.1780195817.git.github.com@herrie.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: ClueGetter at submission1.mail.transip.nl DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; s=transip-a; d=herrie.org; t=1780200554; h=from:subject:to:references: in-reply-to:date:mime-version; bh=RTI3fgU4New0i1U66mKPQVvliSJSx1EP7SEuCqZWb84=; b=AcCzrxuzs4e7sXD1Q6pqDfwGFIeMYuXRw9pX72hKuVPWTXG3eGOdypjh16vt8KmzAwUEKY qxSsePkyIXfLeaeQZvxO6GqRDlWcM/ddYjkaetzMvCxcAoJkYrkeMEeNP7L+4SisFx4yPF XbA39iDhev+gjFLPHbqWrJYDIjAGio2OLCjEhaCuFzTPx8ACybPhzGM9TvvV1cykdRmPHr 5EsGbVyJoiNtnflmkvR1cudJ4em1ptxwPcOU1gqwr0S1lGFmA/HofTC8VWvTR4///MRZaO 6x4DFGq0sTte+nbQ2hspciRv0Mra5aAtxEKw8dnhbu2pWtqxUl76kw2lnAdoKw== X-Report-Abuse-To: abuse@transip.nl Content-Type: text/plain; charset="utf-8" Add a driver for the MSM Power Manager (MPM) on the MSM8x60 family (MSM8260/MSM8660/APQ8060). The MPM is a small wake-source controller implemented in the always-on power domain that latches edge-triggered interrupts during APPS power collapse and signals the RPM to wake the APPS back up. The driver: - implements an irqchip that wraps a parent GIC SPI line and overrides ->irq_set_wake() to enable the MPM mirror of the same interrupt; - exposes msm8660_mpm_set_pin_wake() etc. for consumers (e.g. mmci's SDC4 SDIO wake) that do not flow through irq_set_wake() directly; - communicates with the RPM via SMSM/syscon to apply the wake enable/clear bits and request a power collapse vote. Used on the HP TouchPad (Tenderloin) for SDIO-attached Wi-Fi and panel touch wake-from-suspend. Signed-off-by: Herman van Hazendonk --- drivers/irqchip/Kconfig | 23 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-msm8660-mpm.c | 740 ++++++++++++++++++++++++++++++ include/soc/qcom/msm8660-mpm.h | 83 ++++ 4 files changed, 847 insertions(+) create mode 100644 drivers/irqchip/irq-msm8660-mpm.c create mode 100644 include/soc/qcom/msm8660-mpm.h diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index e755a2a05209..35598a56ac79 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -525,6 +525,29 @@ config QCOM_MPM MSM Power Manager driver to manage and configure wakeup IRQs for Qualcomm Technologies Inc (QTI) mobile chips. =20 +config QCOM_MSM8660_MPM + bool "MSM8x60 MPM wakeup interrupt controller" + depends on ARCH_QCOM + depends on MFD_SYSCON + depends on MAILBOX + select IRQ_DOMAIN_HIERARCHY + default y + help + Platform driver for the MSM Power Manager (MPM) wakeup interrupt + controller on the MSM8x60 family (MSM8260/MSM8660/APQ8060). The vMPM re= gisters live inside + the RPM control block, which makes the generic QCOM_MPM driver + unusable here (it assumes a dedicated MPM SRAM region, an IPCC + mailbox, and uses IRQCHIP_DECLARE early-init that races platform + device creation). + + This driver replicates the legacy 2.6.35-palm arch/arm/mach-msm/ + mpm.c mechanism as a regular platform driver, accessing the vMPM + registers via a syscon phandle to the RPM block and signaling the + MPM via the qcom-apcs-ipc mailbox (writing to GCC + 0x008 bit 1). + + Required for cpuidle deep-sleep states (SPC/PC) and for + WoWLAN-style wake-from-suspend on MSM8x60 SoCs. + config CSKY_MPINTC bool depends on CSKY diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 26aa3b6ec99f..3e64591f0f5b 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_MESON_IRQ_GPIO) +=3D irq-meson-gpio.o obj-$(CONFIG_GOLDFISH_PIC) +=3D irq-goldfish-pic.o obj-$(CONFIG_QCOM_PDC) +=3D qcom-pdc.o obj-$(CONFIG_QCOM_MPM) +=3D irq-qcom-mpm.o +obj-$(CONFIG_QCOM_MSM8660_MPM) +=3D irq-msm8660-mpm.o obj-$(CONFIG_CSKY_MPINTC) +=3D irq-csky-mpintc.o obj-$(CONFIG_CSKY_APB_INTC) +=3D irq-csky-apb-intc.o obj-$(CONFIG_RISCV_INTC) +=3D irq-riscv-intc.o diff --git a/drivers/irqchip/irq-msm8660-mpm.c b/drivers/irqchip/irq-msm866= 0-mpm.c new file mode 100644 index 000000000000..b8394d9c076d --- /dev/null +++ b/drivers/irqchip/irq-msm8660-mpm.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MSM8x60 family (MSM8260/MSM8660/APQ8060) MPM (MSM Power Manager) wakeup= interrupt controller + * + * The MPM is an always-on hardware block that keeps a small set of wake + * sources alive while the application processor is powered down for + * cpuidle Power Collapse or suspend-to-RAM. On MSM8x60 the + * vMPM (virtual MPM) registers live INSIDE the RPM's 4 KB control block + * at: + * + * request (control) regs: RPM_BASE + 0x9d8 (ENABLE, DETECT_CTL, + * POLARITY, CLEAR) + * status (pending) regs: RPM_BASE + 0xdf8 (=3D=3D 0x9d8 + 0x420) + * + * The mainline qcom-mpm driver (drivers/irqchip/irq-qcom-mpm.c) is + * fundamentally incompatible with this layout: + * - it assumes a dedicated MPM SRAM region separate from RPM; + * - it assumes a mailbox controller (IPCC) for wake notification; + * - it uses IRQCHIP_DECLARE which runs before platform devices exist, + * so of_find_device_by_node() returns NULL and the init silently + * hangs. + * + * This driver replicates the 2.6.35-palm `arch/arm/mach-msm/mpm.c` + * mechanism as a regular platform driver: probes after platform + * infrastructure is ready, ioremaps the vMPM sub-region of the RPM + * control block (the qcom,rpm driver maps the surrounding area for + * its own use; the two mappings overlap and neither claims exclusive + * ownership), and uses the qcom-apcs-ipc mailbox for wake notification + * (writing to GCC + 0x008 bit 1). + * + * Register access is done with readl_relaxed/writel_relaxed rather than + * via the RPM syscon regmap. The IRQ core invokes our mask/unmask/ + * set_type/set_wake callbacks with the irq_desc's raw_spinlock_t held, + * and syscon regmaps use a sleepable spinlock_t which on PREEMPT_RT + * would deadlock under that raw lock. Direct MMIO is also what every + * other SoC irqchip (qcom-pdc, gic-v3, ...) does. + * + * Two consumer interfaces: + * + * 1. Hierarchical irqdomain: for MPM pins that map to GIC SPIs (USB, + * HDMI, ...). Consumers wire their interrupts through this + * controller via interrupts-extended and the kernel manages + * enable / mask / set_type / set_wake via the IRQ subsystem. + * + * 2. Raw-pin API: for MPM pins that do NOT correspond to a GIC IRQ + * (SDC3_DAT1=3D21, SDC3_DAT3=3D22, SDC4_DAT1=3D23, SDC4_DAT3=3D24). + * These are physical wake-signal lines monitored by MPM + * directly. Consumers (mmci for SDC4 wake) call + * msm8660_mpm_set_pin_wake() etc. The consumer API establishes + * a device_link from consumer to producer so the MPM device + * cannot disappear while a consumer holds a handle. + * + * Copyright (c) 2026 Herman van Hazendonk + * Copyright (c) 2010-2012, The Linux Foundation (legacy mpm.c reference) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * vMPM register offsets (relative to the start of the ioremap'd window + * =3D RPM base + 0x9d8). Each register is two 32-bit slots because MPM + * exposes 64 wake pins. + */ +#define MSM8660_MPM_REG_ENABLE 0x00 +#define MSM8660_MPM_REG_DETECT_CTL 0x08 +#define MSM8660_MPM_REG_POLARITY 0x10 +#define MSM8660_MPM_REG_CLEAR 0x18 + +/* Status registers at +0x420 from vMPM base (=3D=3D RPM + 0xdf8). */ +#define MSM8660_MPM_STATUS_OFFSET 0x420 + +#define MSM8660_MPM_PIN_COUNT 64 +#define MSM8660_MPM_REG_WIDTH 2 + +struct msm8660_mpm_pin { + int pin; + int hwirq; +}; + +struct msm8660_mpm { + struct device *dev; + void __iomem *base; + struct irq_domain *domain; + struct msm8660_mpm_pin *pin_map; + unsigned int pin_map_count; + int parent_irq; + struct mbox_client mbox_client; + struct mbox_chan *mbox_chan; +}; + +/* + * Singleton - there is only one MPM instance per SoC. msm8660_mpm_get() + * returns this. Updates are serialised through the binding lifecycle so + * a plain pointer is sufficient. + */ +static struct msm8660_mpm *msm8660_mpm_global; + +static u32 msm8660_mpm_read(struct msm8660_mpm *mpm, unsigned int reg) +{ + return readl_relaxed(mpm->base + reg); +} + +static void msm8660_mpm_write(struct msm8660_mpm *mpm, unsigned int reg, + u32 val) +{ + writel_relaxed(val, mpm->base + reg); +} + +/* + * Doorbell the RPM after touching the vMPM request registers. Without + * this the RPM keeps using its last cached copy of the enable/detect/ + * polarity state and our configuration changes have no effect. + * + * Called from raw_spinlock_t-held contexts (irq_chip mask/unmask/ + * set_type/set_wake), so the mailbox driver must accept that. The + * qcom-apcs-ipc mailbox just does a writel into the IPC trigger + * register; it is safe under a raw lock. + */ +static void msm8660_mpm_doorbell(struct msm8660_mpm *mpm) +{ + int ret; + + if (!mpm->mbox_chan) + return; + + ret =3D mbox_send_message(mpm->mbox_chan, NULL); + if (ret < 0) + dev_warn_ratelimited(mpm->dev, + "RPM doorbell failed: %d\n", ret); +} + +static int msm8660_mpm_pin_to_hwirq(struct msm8660_mpm *mpm, int pin) +{ + int i; + + for (i =3D 0; i < mpm->pin_map_count; i++) { + if (mpm->pin_map[i].pin =3D=3D pin) + return mpm->pin_map[i].hwirq; + } + return -ENOENT; +} + +/* + * IPC handler: MPM fires this IRQ when one or more enabled wake pins + * have pending activity. Read pending status, CLEAR the pending bits + * BEFORE dispatching the per-pin handlers so a fresh edge that arrives + * during dispatch cannot be wiped out by a later CLEAR write, then + * replay each pending pin through the irqdomain. + */ +static irqreturn_t msm8660_mpm_irq(int irq, void *data) +{ + struct msm8660_mpm *mpm =3D data; + unsigned long pending[MSM8660_MPM_REG_WIDTH]; + unsigned long enable[MSM8660_MPM_REG_WIDTH]; + int i, j; + + for (i =3D 0; i < MSM8660_MPM_REG_WIDTH; i++) { + pending[i] =3D msm8660_mpm_read(mpm, + MSM8660_MPM_STATUS_OFFSET + i * 4); + enable[i] =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_ENABLE + i * 4); + pending[i] &=3D enable[i]; + + /* + * Clear before dispatching: a new edge latched on this pin + * after this point will set the pending bit again and we + * will service it on the next IPC IRQ. Clearing AFTER the + * handler would race with that new latch and silently lose + * the new edge. + */ + if (pending[i]) + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_CLEAR + i * 4, pending[i]); + } + + for (i =3D 0; i < MSM8660_MPM_REG_WIDTH; i++) { + unsigned long bits =3D pending[i]; + + for_each_set_bit(j, &bits, 32) { + int pin =3D i * 32 + j; + int hwirq =3D msm8660_mpm_pin_to_hwirq(mpm, pin); + + if (hwirq >=3D 0) { + dev_dbg(mpm->dev, "wake pin %d -> hwirq %d\n", + pin, hwirq); + generic_handle_domain_irq(mpm->domain, hwirq); + } + } + } + + return IRQ_HANDLED; +} + +/* + * Resolve an irq_data hwirq back to its MPM pin number, or -1 if the + * pin is not in the qcom,mpm-pin-map (which would mean a consumer + * bound to a hwirq that has no MPM mapping). + */ +static int msm8660_mpm_hwirq_to_pin(struct msm8660_mpm *mpm, unsigned int = hwirq) +{ + int i; + + for (i =3D 0; i < mpm->pin_map_count; i++) { + if (mpm->pin_map[i].hwirq =3D=3D hwirq) + return mpm->pin_map[i].pin; + } + return -1; +} + +static void msm8660_mpm_enable_hwirq(struct irq_data *d, bool enable) +{ + struct msm8660_mpm *mpm =3D irq_data_get_irq_chip_data(d); + int pin; + u32 val, mask; + + pin =3D msm8660_mpm_hwirq_to_pin(mpm, d->hwirq); + if (pin < 0) + return; + + mask =3D BIT(pin % 32); + val =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_ENABLE + (pin / 32) * 4); + if (enable) + val |=3D mask; + else + val &=3D ~mask; + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_ENABLE + (pin / 32) * 4, val); + + msm8660_mpm_doorbell(mpm); +} + +static void msm8660_mpm_mask_irq(struct irq_data *d) +{ + msm8660_mpm_enable_hwirq(d, false); + irq_chip_mask_parent(d); +} + +static void msm8660_mpm_unmask_irq(struct irq_data *d) +{ + msm8660_mpm_enable_hwirq(d, true); + irq_chip_unmask_parent(d); +} + +static int msm8660_mpm_set_type(struct irq_data *d, unsigned int type) +{ + struct msm8660_mpm *mpm =3D irq_data_get_irq_chip_data(d); + u32 detect, polarity, mask; + int pin; + + pin =3D msm8660_mpm_hwirq_to_pin(mpm, d->hwirq); + if (pin < 0) + return -ENOENT; + + mask =3D BIT(pin % 32); + detect =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4); + polarity =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_POLARITY + (pin / 32) * 4); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + detect |=3D mask; + polarity |=3D mask; + break; + case IRQ_TYPE_EDGE_FALLING: + detect |=3D mask; + polarity &=3D ~mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + detect &=3D ~mask; + polarity |=3D mask; + break; + case IRQ_TYPE_LEVEL_LOW: + detect &=3D ~mask; + polarity &=3D ~mask; + break; + default: + return -EINVAL; + } + + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4, detect); + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_POLARITY + (pin / 32) * 4, polarity); + + msm8660_mpm_doorbell(mpm); + + return irq_chip_set_type_parent(d, type); +} + +/* + * Toggle MPM monitoring of the pin and propagate the wake request to + * the parent GIC so it also stays alive during power-collapse. + * + * Without this callback the generic IRQ core would either silently + * succeed (with IRQCHIP_SKIP_SET_WAKE) or fail outright, and neither + * the MPM nor the GIC would actually be programmed to wake the system. + */ +static int msm8660_mpm_set_wake(struct irq_data *d, unsigned int on) +{ + struct msm8660_mpm *mpm =3D irq_data_get_irq_chip_data(d); + int pin; + + pin =3D msm8660_mpm_hwirq_to_pin(mpm, d->hwirq); + if (pin < 0) + return -ENOENT; + + msm8660_mpm_enable_hwirq(d, !!on); + + return irq_chip_set_wake_parent(d, on); +} + +static struct irq_chip msm8660_mpm_chip =3D { + .name =3D "msm8660-mpm", + .irq_mask =3D msm8660_mpm_mask_irq, + .irq_unmask =3D msm8660_mpm_unmask_irq, + .irq_set_type =3D msm8660_mpm_set_type, + .irq_set_wake =3D msm8660_mpm_set_wake, + .irq_eoi =3D irq_chip_eoi_parent, + .irq_set_affinity =3D irq_chip_set_affinity_parent, + .flags =3D IRQCHIP_MASK_ON_SUSPEND, +}; + +static int msm8660_mpm_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *data) +{ + struct msm8660_mpm *mpm =3D domain->host_data; + struct irq_fwspec *fwspec =3D data; + struct irq_fwspec parent_fwspec; + irq_hw_number_t hwirq; + int i, ret; + + if (fwspec->param_count !=3D 2) + return -EINVAL; + + hwirq =3D fwspec->param[0]; + + for (i =3D 0; i < nr_irqs; i++) + irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, + &msm8660_mpm_chip, mpm); + + parent_fwspec.fwnode =3D domain->parent->fwnode; + parent_fwspec.param_count =3D 3; + parent_fwspec.param[0] =3D 0; + parent_fwspec.param[1] =3D hwirq; + parent_fwspec.param[2] =3D fwspec->param[1]; + + ret =3D irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, + &parent_fwspec); + if (ret) { + dev_err(mpm->dev, "irq_domain_alloc_irqs_parent failed: %d\n", + ret); + return ret; + } + + return 0; +} + +static int msm8660_mpm_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, unsigned int *type) +{ + if (fwspec->param_count !=3D 2) + return -EINVAL; + + *hwirq =3D fwspec->param[0]; + *type =3D fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + return 0; +} + +static const struct irq_domain_ops msm8660_mpm_domain_ops =3D { + .translate =3D msm8660_mpm_translate, + .alloc =3D msm8660_mpm_domain_alloc, + .free =3D irq_domain_free_irqs_common, +}; + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * Raw-pin consumer API + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + */ + +/** + * msm8660_mpm_get() - acquire a handle to the MPM for raw-pin use + * @consumer: device of the consumer driver + * @np: optional device-tree node containing a phandle reference + * @propname: optional property name for that phandle (e.g. "qcom,mpm") + * + * Returns the singleton MPM handle, ERR_PTR(-EPROBE_DEFER) if the MPM + * driver has not finished probing yet, or ERR_PTR(-ENOENT) if @np is + * given and the phandle does not resolve. + * + * On success this also establishes a consumer-supplier device_link so + * the MPM device cannot be unbound while the consumer holds the + * handle. The link is auto-removed when @consumer is unbound. + */ +struct msm8660_mpm *msm8660_mpm_get(struct device *consumer, + struct device_node *np, + const char *propname) +{ + struct device_node *mpm_np; + struct device_link *link; + + if (!msm8660_mpm_global) + return ERR_PTR(-EPROBE_DEFER); + + if (np && propname) { + mpm_np =3D of_parse_phandle(np, propname, 0); + if (!mpm_np) + return ERR_PTR(-ENOENT); + of_node_put(mpm_np); + } + + if (!consumer) + return msm8660_mpm_global; + + link =3D device_link_add(consumer, msm8660_mpm_global->dev, + DL_FLAG_AUTOREMOVE_CONSUMER); + if (!link) { + dev_warn(consumer, "failed to link to MPM, deferring\n"); + return ERR_PTR(-EPROBE_DEFER); + } + + return msm8660_mpm_global; +} +EXPORT_SYMBOL_GPL(msm8660_mpm_get); + +/** + * msm8660_mpm_enable_pin() - enable/disable MPM monitoring of a pin + * @mpm: handle from msm8660_mpm_get() + * @pin: MPM pin index (0..MSM8660_MPM_PIN_COUNT-1) + * @enable: true to monitor, false to ignore + * + * Programs the ENABLE register directly. Intended for "raw" wake pins + * (SDC3_DAT1=3D21, SDC3_DAT3=3D22, SDC4_DAT1=3D23, SDC4_DAT3=3D24) that h= ave no + * GIC IRQ mapping. For pins that DO have a GIC mapping (in + * qcom,mpm-pin-map), use the irqdomain path instead. + */ +int msm8660_mpm_enable_pin(struct msm8660_mpm *mpm, unsigned int pin, + bool enable) +{ + u32 val; + + if (!mpm || pin >=3D MSM8660_MPM_PIN_COUNT) + return -EINVAL; + + val =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_ENABLE + (pin / 32) * 4); + if (enable) + val |=3D BIT(pin % 32); + else + val &=3D ~BIT(pin % 32); + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_ENABLE + (pin / 32) * 4, val); + + msm8660_mpm_doorbell(mpm); + + return 0; +} +EXPORT_SYMBOL_GPL(msm8660_mpm_enable_pin); + +/** + * msm8660_mpm_set_pin_wake() - mark a pin as wake-capable + * @mpm: handle + * @pin: MPM pin index + * @on: true to allow this pin to wake the system, false to clear. + * + * Equivalent to msm8660_mpm_enable_pin() on MSM8660 - the hardware has + * a single ENABLE register, not separate enable + wake masks. The name + * is kept for API parity with the legacy mpm.h interface so consumers + * can express the wake-source intent explicitly. + */ +int msm8660_mpm_set_pin_wake(struct msm8660_mpm *mpm, unsigned int pin, + bool on) +{ + return msm8660_mpm_enable_pin(mpm, pin, on); +} +EXPORT_SYMBOL_GPL(msm8660_mpm_set_pin_wake); + +/** + * msm8660_mpm_set_pin_type() - set trigger type for a raw MPM pin + * @mpm: handle + * @pin: MPM pin index + * @flow_type: standard IRQ_TYPE_* constants + * + * On MSM8660 the trigger config is split across DETECT_CTL (edge vs + * level) and POLARITY (rising/high vs falling/low). + */ +int msm8660_mpm_set_pin_type(struct msm8660_mpm *mpm, unsigned int pin, + unsigned int flow_type) +{ + u32 detect, polarity; + bool edge, polarity_high; + + if (!mpm || pin >=3D MSM8660_MPM_PIN_COUNT) + return -EINVAL; + + edge =3D !!(flow_type & IRQ_TYPE_EDGE_BOTH); + polarity_high =3D !!(flow_type & (IRQ_TYPE_EDGE_RISING | + IRQ_TYPE_LEVEL_HIGH)); + + detect =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4); + polarity =3D msm8660_mpm_read(mpm, + MSM8660_MPM_REG_POLARITY + (pin / 32) * 4); + + if (edge) + detect |=3D BIT(pin % 32); + else + detect &=3D ~BIT(pin % 32); + + if (polarity_high) + polarity |=3D BIT(pin % 32); + else + polarity &=3D ~BIT(pin % 32); + + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_DETECT_CTL + (pin / 32) * 4, detect); + msm8660_mpm_write(mpm, + MSM8660_MPM_REG_POLARITY + (pin / 32) * 4, polarity); + + msm8660_mpm_doorbell(mpm); + + return 0; +} +EXPORT_SYMBOL_GPL(msm8660_mpm_set_pin_type); + +/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * Platform driver + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + */ + +static int msm8660_mpm_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct device_node *np =3D dev->of_node; + struct msm8660_mpm *mpm; + struct irq_domain *parent_domain; + struct device_node *parent_np; + struct resource *res; + int ret, i; + + if (msm8660_mpm_global) + return dev_err_probe(dev, -EBUSY, + "only one MPM instance is supported\n"); + + mpm =3D devm_kzalloc(dev, sizeof(*mpm), GFP_KERNEL); + if (!mpm) + return -ENOMEM; + + mpm->dev =3D dev; + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return dev_err_probe(dev, -ENODEV, "missing reg property\n"); + + /* + * Use a non-exclusive mapping: the qcom,rpm driver maps the + * surrounding RPM control block via its own platform resource, + * and our vMPM sub-region overlaps that mapping. devm_ioremap() + * does not call request_mem_region() so there is no conflict. + */ + mpm->base =3D devm_ioremap(dev, res->start, resource_size(res)); + if (!mpm->base) + return dev_err_probe(dev, -ENOMEM, + "failed to ioremap vMPM at %pR\n", res); + + /* + * Parse pin map (IRQ-mapped wake pins; raw pins like SDC4_DAT1 + * are not listed here - they are accessed via the pin-API). + */ + ret =3D of_property_count_u32_elems(np, "qcom,mpm-pin-map"); + if (ret < 0 || ret % 2) + return dev_err_probe(dev, -EINVAL, + "invalid qcom,mpm-pin-map\n"); + + mpm->pin_map_count =3D ret / 2; + mpm->pin_map =3D devm_kcalloc(dev, mpm->pin_map_count, + sizeof(*mpm->pin_map), GFP_KERNEL); + if (!mpm->pin_map) + return -ENOMEM; + + for (i =3D 0; i < mpm->pin_map_count; i++) { + u32 pin, hwirq; + + of_property_read_u32_index(np, "qcom,mpm-pin-map", + i * 2, &pin); + of_property_read_u32_index(np, "qcom,mpm-pin-map", + i * 2 + 1, &hwirq); + + if (pin >=3D MSM8660_MPM_PIN_COUNT) + return dev_err_probe(dev, -EINVAL, + "qcom,mpm-pin-map entry %d: pin %u >=3D %u\n", + i, pin, MSM8660_MPM_PIN_COUNT); + + mpm->pin_map[i].pin =3D pin; + mpm->pin_map[i].hwirq =3D hwirq; + + dev_dbg(dev, "pin map: pin %u -> hwirq %u\n", pin, hwirq); + } + + parent_np =3D of_irq_find_parent(np); + if (!parent_np) + return dev_err_probe(dev, -ENODEV, + "failed to find parent interrupt controller\n"); + + parent_domain =3D irq_find_host(parent_np); + of_node_put(parent_np); + if (!parent_domain) + return dev_err_probe(dev, -ENODEV, + "failed to find parent IRQ domain\n"); + + mpm->domain =3D irq_domain_create_hierarchy(parent_domain, 0, + MSM8660_MPM_PIN_COUNT, + of_fwnode_handle(np), + &msm8660_mpm_domain_ops, + mpm); + if (!mpm->domain) + return dev_err_probe(dev, -ENOMEM, + "failed to create IRQ domain\n"); + + mpm->parent_irq =3D platform_get_irq(pdev, 0); + if (mpm->parent_irq < 0) { + ret =3D mpm->parent_irq; + goto err_remove_domain; + } + + /* + * Mailbox channel for poking MPM to re-read its config. Get this + * BEFORE registering our IRQ handler so that doorbell-from-IRQ + * never sees a partially-initialised channel pointer. + */ + mpm->mbox_client.dev =3D dev; + mpm->mbox_client.knows_txdone =3D true; + mpm->mbox_chan =3D mbox_request_channel(&mpm->mbox_client, 0); + if (IS_ERR(mpm->mbox_chan)) { + ret =3D PTR_ERR(mpm->mbox_chan); + mpm->mbox_chan =3D NULL; + if (ret =3D=3D -EPROBE_DEFER) + goto err_remove_domain; + dev_warn(dev, "no mailbox channel: %d (continuing without RPM doorbell)\= n", + ret); + } + + /* + * Register the parent IRQ last and use plain request_irq() (not + * devm_*) so we can free it explicitly in ->remove() before + * irq_domain_remove(). With devm_request_irq() the handler + * outlives irq_domain_remove() and a wake event arriving in the + * removal window would dereference a freed domain pointer. + */ + ret =3D request_irq(mpm->parent_irq, msm8660_mpm_irq, + IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, + "msm8660-mpm", mpm); + if (ret) { + dev_err(dev, "failed to request IRQ %d: %d\n", + mpm->parent_irq, ret); + goto err_free_mbox; + } + + platform_set_drvdata(pdev, mpm); + msm8660_mpm_global =3D mpm; + + dev_info(dev, "ready: %d pin mappings, irq=3D%d\n", + mpm->pin_map_count, mpm->parent_irq); + + return 0; + +err_free_mbox: + if (mpm->mbox_chan) + mbox_free_channel(mpm->mbox_chan); +err_remove_domain: + irq_domain_remove(mpm->domain); + return ret; +} + +static void msm8660_mpm_remove(struct platform_device *pdev) +{ + struct msm8660_mpm *mpm =3D platform_get_drvdata(pdev); + + /* + * Tear down in strict reverse order: drop the singleton so new + * consumers cannot grab a handle, free the IRQ so the handler + * cannot fire again, free the mailbox channel, then remove the + * domain. Consumer device_links established in msm8660_mpm_get() + * prevent the parent device from being unbound while a consumer + * still holds a handle. + */ + msm8660_mpm_global =3D NULL; + free_irq(mpm->parent_irq, mpm); + if (mpm->mbox_chan) + mbox_free_channel(mpm->mbox_chan); + irq_domain_remove(mpm->domain); +} + +static const struct of_device_id msm8660_mpm_of_match[] =3D { + { .compatible =3D "qcom,msm8660-mpm" }, + {} +}; +MODULE_DEVICE_TABLE(of, msm8660_mpm_of_match); + +static struct platform_driver msm8660_mpm_driver =3D { + .probe =3D msm8660_mpm_probe, + .remove =3D msm8660_mpm_remove, + .driver =3D { + .name =3D "msm8660-mpm", + .of_match_table =3D msm8660_mpm_of_match, + }, +}; + +static int __init msm8660_mpm_init(void) +{ + return platform_driver_register(&msm8660_mpm_driver); +} +subsys_initcall(msm8660_mpm_init); + +static void __exit msm8660_mpm_exit(void) +{ + platform_driver_unregister(&msm8660_mpm_driver); +} +module_exit(msm8660_mpm_exit); + +MODULE_DESCRIPTION("Qualcomm MSM8x60 MPM wakeup interrupt controller"); +MODULE_LICENSE("GPL"); diff --git a/include/soc/qcom/msm8660-mpm.h b/include/soc/qcom/msm8660-mpm.h new file mode 100644 index 000000000000..27f3f22d57b4 --- /dev/null +++ b/include/soc/qcom/msm8660-mpm.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Qualcomm MSM8x60 family (MSM8260/MSM8660/APQ8060) MPM wake-source consu= mer interface. + * + * The MPM driver lives at drivers/irqchip/irq-msm8660-mpm.c. It exposes + * TWO interfaces: + * + * 1. Hierarchical irqdomain (preferred). For wake sources that map to + * GIC SPIs (USB1_HS, HDMI, ...). Consumers wire their IRQ through + * the MPM via `interrupts-extended =3D <&msm8660_mpm ...>` in DT and + * the IRQ subsystem manages enable / mask / set_type via the + * irqdomain alloc path. No explicit C API call needed. + * + * 2. Raw-pin API (this header). For wake sources that do NOT have a + * GIC IRQ mapping: SDC3_DAT1=3D21, SDC3_DAT3=3D22, SDC4_DAT1=3D23, + * SDC4_DAT3=3D24. These are physical wake-signal lines that MPM + * monitors directly. mmci (for SDC4 WiFi wake) obtains a handle + * via msm8660_mpm_get() and uses the helpers below. + */ + +#ifndef __SOC_QCOM_MSM8660_MPM_H__ +#define __SOC_QCOM_MSM8660_MPM_H__ + +#include +#include +#include + +#define MSM8660_MPM_NR_PINS 64 + +/* Well-known wake-source pin indices (from the legacy 2.6.35-palm tree). = */ +#define MSM8660_MPM_PIN_SDC3_DAT1 21 +#define MSM8660_MPM_PIN_SDC3_DAT3 22 +#define MSM8660_MPM_PIN_SDC4_DAT1 23 +#define MSM8660_MPM_PIN_SDC4_DAT3 24 + +struct device; +struct device_node; +struct msm8660_mpm; + +#if IS_ENABLED(CONFIG_QCOM_MSM8660_MPM) + +struct msm8660_mpm *msm8660_mpm_get(struct device *consumer, + struct device_node *np, + const char *propname); + +int msm8660_mpm_set_pin_wake(struct msm8660_mpm *mpm, unsigned int pin, + bool on); +int msm8660_mpm_enable_pin(struct msm8660_mpm *mpm, unsigned int pin, + bool enable); +int msm8660_mpm_set_pin_type(struct msm8660_mpm *mpm, unsigned int pin, + unsigned int flow_type); + +#else /* !CONFIG_QCOM_MSM8660_MPM */ + +static inline struct msm8660_mpm * +msm8660_mpm_get(struct device *consumer, struct device_node *np, + const char *propname) +{ + return ERR_PTR(-ENODEV); +} + +static inline int msm8660_mpm_set_pin_wake(struct msm8660_mpm *mpm, + unsigned int pin, bool on) +{ + return -ENODEV; +} + +static inline int msm8660_mpm_enable_pin(struct msm8660_mpm *mpm, + unsigned int pin, bool enable) +{ + return -ENODEV; +} + +static inline int msm8660_mpm_set_pin_type(struct msm8660_mpm *mpm, + unsigned int pin, + unsigned int flow_type) +{ + return -ENODEV; +} + +#endif /* CONFIG_QCOM_MSM8660_MPM */ + +#endif /* __SOC_QCOM_MSM8660_MPM_H__ */ --=20 2.43.0