* [PATCH v1 1/5] dt-bindings: spmi: Add MediaTek MT8196 SPMI 2 Arbiter/Controllers
2025-06-23 12:00 [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus AngeloGioacchino Del Regno
@ 2025-06-23 12:00 ` AngeloGioacchino Del Regno
2025-06-27 20:07 ` Rob Herring
2025-06-23 12:00 ` [PATCH v1 2/5] spmi: mtk-pmif: Add multi-bus support for SPMI 2.0 AngeloGioacchino Del Regno
` (4 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: AngeloGioacchino Del Regno @ 2025-06-23 12:00 UTC (permalink / raw)
To: sboyd
Cc: robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
hsin-hsiung.wang, linux-kernel, devicetree, linux-arm-kernel,
linux-mediatek, kernel
Document the MT8196 SPMI 2.0 Controller with a new schema.
This is a MIPI SPMI 2.0 compliant IP, composed of a main arbiter
and two SPMI master controllers with Request Capable Slave (RCS)
support.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
.../bindings/spmi/mediatek,mt8196-spmi.yaml | 138 ++++++++++++++++++
1 file changed, 138 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
diff --git a/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml b/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
new file mode 100644
index 000000000000..d7eb63e81a5c
--- /dev/null
+++ b/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
@@ -0,0 +1,138 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spmi/mediatek,mt8196-spmi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT8196 SPMI 2.0 Controller
+
+maintainers:
+ - Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>
+ - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+ The MediaTek MT8196 SoC features a SPMI version 2.0 compliant controller,
+ with internal wrapping arbitration logic to allow for multiple on-chip
+ devices to control up to two SPMI buses.
+ The main arbiter also acts as an interrupt controller, arbitering also
+ the interrupts coming from SPMI-connected devices into each of the nested
+ interrupt controllers from any of the present SPMI buses.
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - mediatek,mt8196-spmi
+ - items:
+ - enum:
+ - mediatek,mt6991-spmi
+ - const: mediatek,mt8196-spmi
+
+ ranges: true
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 1
+
+patternProperties:
+ "^spmi@[a-f0-9]+$":
+ type: object
+ $ref: /schemas/spmi/spmi.yaml
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ items:
+ - description: controller interface registers
+ - description: spmi master controller registers
+
+ reg-names:
+ items:
+ - const: pmif
+ - const: spmimst
+
+ clocks:
+ items:
+ - description: controller interface system clock
+ - description: controller interface timer clock
+ - description: spmi controller master clock
+
+ clock-names:
+ items:
+ - const: pmif_sys_ck
+ - const: pmif_tmr_ck
+ - const: spmimst_clk_mux
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ const: rcs
+
+ interrupt-controller: true
+
+ "#interrupt-cells":
+ const: 3
+ description: |
+ cell 1: slave ID for the requested interrupt (0-15)
+ cell 2: the requested peripheral interrupt (0-7)
+ cell 3: interrupt flags indicating level-sense information,
+ as defined in dt-bindings/interrupt-controller/irq.h
+ required:
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - interrupts
+ - interrupt-names
+ - interrupt-controller
+ - "#interrupt-cells"
+
+required:
+ - compatible
+ - ranges
+ - '#address-cells'
+ - '#size-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ spmi-arbiter@1c018000 {
+ compatible = "mediatek,mt8196-spmi";
+ ranges = <0 0 0x1c018000 0x4900>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ spmi@0 {
+ reg = <0 0x900>, <0x4800 0x100>;
+ reg-names = "pmif", "spmimst";
+ interrupts-extended = <&pio 292 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "rcs";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ clocks = <&pmif_sys>, <&pmif_tmr>, <&spmi_mst>;
+ clock-names = "pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux";
+ };
+
+ spmi@2000 {
+ reg = <0x2000 0x900>, <0x4000 0x100>;
+ reg-names = "pmif", "spmimst";
+ interrupts-extended = <&pio 291 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "rcs";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ clocks = <&pmif_sys>, <&pmif_tmr>, <&spmi_mst>;
+ clock-names = "pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux";
+ };
+ };
+ };
+...
--
2.49.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v1 1/5] dt-bindings: spmi: Add MediaTek MT8196 SPMI 2 Arbiter/Controllers
2025-06-23 12:00 ` [PATCH v1 1/5] dt-bindings: spmi: Add MediaTek MT8196 SPMI 2 Arbiter/Controllers AngeloGioacchino Del Regno
@ 2025-06-27 20:07 ` Rob Herring
2025-06-30 12:13 ` AngeloGioacchino Del Regno
0 siblings, 1 reply; 9+ messages in thread
From: Rob Herring @ 2025-06-27 20:07 UTC (permalink / raw)
To: AngeloGioacchino Del Regno
Cc: sboyd, krzk+dt, conor+dt, matthias.bgg, hsin-hsiung.wang,
linux-kernel, devicetree, linux-arm-kernel, linux-mediatek,
kernel
On Mon, Jun 23, 2025 at 02:00:43PM +0200, AngeloGioacchino Del Regno wrote:
> Document the MT8196 SPMI 2.0 Controller with a new schema.
> This is a MIPI SPMI 2.0 compliant IP, composed of a main arbiter
> and two SPMI master controllers with Request Capable Slave (RCS)
> support.
>
> Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> ---
> .../bindings/spmi/mediatek,mt8196-spmi.yaml | 138 ++++++++++++++++++
> 1 file changed, 138 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
>
> diff --git a/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml b/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
> new file mode 100644
> index 000000000000..d7eb63e81a5c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
> @@ -0,0 +1,138 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/spmi/mediatek,mt8196-spmi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: MediaTek MT8196 SPMI 2.0 Controller
> +
> +maintainers:
> + - Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>
> + - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
> +
> +description:
> + The MediaTek MT8196 SoC features a SPMI version 2.0 compliant controller,
> + with internal wrapping arbitration logic to allow for multiple on-chip
> + devices to control up to two SPMI buses.
> + The main arbiter also acts as an interrupt controller, arbitering also
> + the interrupts coming from SPMI-connected devices into each of the nested
> + interrupt controllers from any of the present SPMI buses.
> +
> +properties:
> + compatible:
> + oneOf:
> + - enum:
> + - mediatek,mt8196-spmi
> + - items:
> + - enum:
> + - mediatek,mt6991-spmi
> + - const: mediatek,mt8196-spmi
> +
> + ranges: true
> +
> + '#address-cells':
> + const: 1
> +
> + '#size-cells':
> + const: 1
> +
> +patternProperties:
> + "^spmi@[a-f0-9]+$":
> + type: object
> + $ref: /schemas/spmi/spmi.yaml
> + unevaluatedProperties: false
> +
> + properties:
> + reg:
> + items:
> + - description: controller interface registers
> + - description: spmi master controller registers
> +
> + reg-names:
> + items:
> + - const: pmif
> + - const: spmimst
> +
> + clocks:
> + items:
> + - description: controller interface system clock
> + - description: controller interface timer clock
> + - description: spmi controller master clock
> +
> + clock-names:
> + items:
> + - const: pmif_sys_ck
> + - const: pmif_tmr_ck
> + - const: spmimst_clk_mux
> +
> + interrupts:
> + maxItems: 1
> +
> + interrupt-names:
> + const: rcs
> +
> + interrupt-controller: true
> +
> + "#interrupt-cells":
> + const: 3
> + description: |
> + cell 1: slave ID for the requested interrupt (0-15)
> + cell 2: the requested peripheral interrupt (0-7)
> + cell 3: interrupt flags indicating level-sense information,
> + as defined in dt-bindings/interrupt-controller/irq.h
> + required:
> + - reg
> + - reg-names
> + - clocks
> + - clock-names
> + - interrupts
> + - interrupt-names
> + - interrupt-controller
> + - "#interrupt-cells"
> +
> +required:
> + - compatible
> + - ranges
> + - '#address-cells'
> + - '#size-cells'
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + spmi-arbiter@1c018000 {
> + compatible = "mediatek,mt8196-spmi";
> + ranges = <0 0 0x1c018000 0x4900>;
> + #address-cells = <1>;
> + #size-cells = <1>;
> +
> + spmi@0 {
> + reg = <0 0x900>, <0x4800 0x100>;
> + reg-names = "pmif", "spmimst";
> + interrupts-extended = <&pio 292 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-names = "rcs";
> + interrupt-controller;
Indentation error.
Otherwise,
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
> + #interrupt-cells = <3>;
> + clocks = <&pmif_sys>, <&pmif_tmr>, <&spmi_mst>;
> + clock-names = "pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux";
> + };
> +
> + spmi@2000 {
> + reg = <0x2000 0x900>, <0x4000 0x100>;
> + reg-names = "pmif", "spmimst";
> + interrupts-extended = <&pio 291 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-names = "rcs";
> + interrupt-controller;
> + #interrupt-cells = <3>;
> + clocks = <&pmif_sys>, <&pmif_tmr>, <&spmi_mst>;
> + clock-names = "pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux";
> + };
> + };
> + };
> +...
> --
> 2.49.0
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v1 1/5] dt-bindings: spmi: Add MediaTek MT8196 SPMI 2 Arbiter/Controllers
2025-06-27 20:07 ` Rob Herring
@ 2025-06-30 12:13 ` AngeloGioacchino Del Regno
0 siblings, 0 replies; 9+ messages in thread
From: AngeloGioacchino Del Regno @ 2025-06-30 12:13 UTC (permalink / raw)
To: Rob Herring
Cc: sboyd, krzk+dt, conor+dt, matthias.bgg, hsin-hsiung.wang,
linux-kernel, devicetree, linux-arm-kernel, linux-mediatek,
kernel
Il 27/06/25 22:07, Rob Herring ha scritto:
> On Mon, Jun 23, 2025 at 02:00:43PM +0200, AngeloGioacchino Del Regno wrote:
>> Document the MT8196 SPMI 2.0 Controller with a new schema.
>> This is a MIPI SPMI 2.0 compliant IP, composed of a main arbiter
>> and two SPMI master controllers with Request Capable Slave (RCS)
>> support.
>>
>> Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
>> ---
>> .../bindings/spmi/mediatek,mt8196-spmi.yaml | 138 ++++++++++++++++++
>> 1 file changed, 138 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml b/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
>> new file mode 100644
>> index 000000000000..d7eb63e81a5c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
>> @@ -0,0 +1,138 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/spmi/mediatek,mt8196-spmi.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: MediaTek MT8196 SPMI 2.0 Controller
>> +
>> +maintainers:
>> + - Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>
>> + - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
>> +
>> +description:
>> + The MediaTek MT8196 SoC features a SPMI version 2.0 compliant controller,
>> + with internal wrapping arbitration logic to allow for multiple on-chip
>> + devices to control up to two SPMI buses.
>> + The main arbiter also acts as an interrupt controller, arbitering also
>> + the interrupts coming from SPMI-connected devices into each of the nested
>> + interrupt controllers from any of the present SPMI buses.
>> +
>> +properties:
>> + compatible:
>> + oneOf:
>> + - enum:
>> + - mediatek,mt8196-spmi
>> + - items:
>> + - enum:
>> + - mediatek,mt6991-spmi
>> + - const: mediatek,mt8196-spmi
>> +
>> + ranges: true
>> +
>> + '#address-cells':
>> + const: 1
>> +
>> + '#size-cells':
>> + const: 1
>> +
>> +patternProperties:
>> + "^spmi@[a-f0-9]+$":
>> + type: object
>> + $ref: /schemas/spmi/spmi.yaml
>> + unevaluatedProperties: false
>> +
>> + properties:
>> + reg:
>> + items:
>> + - description: controller interface registers
>> + - description: spmi master controller registers
>> +
>> + reg-names:
>> + items:
>> + - const: pmif
>> + - const: spmimst
>> +
>> + clocks:
>> + items:
>> + - description: controller interface system clock
>> + - description: controller interface timer clock
>> + - description: spmi controller master clock
>> +
>> + clock-names:
>> + items:
>> + - const: pmif_sys_ck
>> + - const: pmif_tmr_ck
>> + - const: spmimst_clk_mux
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + interrupt-names:
>> + const: rcs
>> +
>> + interrupt-controller: true
>> +
>> + "#interrupt-cells":
>> + const: 3
>> + description: |
>> + cell 1: slave ID for the requested interrupt (0-15)
>> + cell 2: the requested peripheral interrupt (0-7)
>> + cell 3: interrupt flags indicating level-sense information,
>> + as defined in dt-bindings/interrupt-controller/irq.h
>> + required:
>> + - reg
>> + - reg-names
>> + - clocks
>> + - clock-names
>> + - interrupts
>> + - interrupt-names
>> + - interrupt-controller
>> + - "#interrupt-cells"
>> +
>> +required:
>> + - compatible
>> + - ranges
>> + - '#address-cells'
>> + - '#size-cells'
>> +
>> +additionalProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +
>> + soc {
>> + #address-cells = <2>;
>> + #size-cells = <2>;
>> +
>> + spmi-arbiter@1c018000 {
>> + compatible = "mediatek,mt8196-spmi";
>> + ranges = <0 0 0x1c018000 0x4900>;
>> + #address-cells = <1>;
>> + #size-cells = <1>;
>> +
>> + spmi@0 {
>> + reg = <0 0x900>, <0x4800 0x100>;
>> + reg-names = "pmif", "spmimst";
>> + interrupts-extended = <&pio 292 IRQ_TYPE_LEVEL_HIGH>;
>> + interrupt-names = "rcs";
>> + interrupt-controller;
>
> Indentation error.
>
> Otherwise,
>
> Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
>
Oh sorry for that.
If there's no "complaint" on any other patch of this series, I wonder if this
could be fixed while applying?
Thanks a lot,
Angelo
>> + #interrupt-cells = <3>;
>> + clocks = <&pmif_sys>, <&pmif_tmr>, <&spmi_mst>;
>> + clock-names = "pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux";
>> + };
>> +
>> + spmi@2000 {
>> + reg = <0x2000 0x900>, <0x4000 0x100>;
>> + reg-names = "pmif", "spmimst";
>> + interrupts-extended = <&pio 291 IRQ_TYPE_LEVEL_HIGH>;
>> + interrupt-names = "rcs";
>> + interrupt-controller;
>> + #interrupt-cells = <3>;
>> + clocks = <&pmif_sys>, <&pmif_tmr>, <&spmi_mst>;
>> + clock-names = "pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux";
>> + };
>> + };
>> + };
>> +...
>> --
>> 2.49.0
>>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 2/5] spmi: mtk-pmif: Add multi-bus support for SPMI 2.0
2025-06-23 12:00 [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus AngeloGioacchino Del Regno
2025-06-23 12:00 ` [PATCH v1 1/5] dt-bindings: spmi: Add MediaTek MT8196 SPMI 2 Arbiter/Controllers AngeloGioacchino Del Regno
@ 2025-06-23 12:00 ` AngeloGioacchino Del Regno
2025-06-23 12:00 ` [PATCH v1 3/5] spmi: mtk-pmif: Keep spinlock until read is fully done AngeloGioacchino Del Regno
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: AngeloGioacchino Del Regno @ 2025-06-23 12:00 UTC (permalink / raw)
To: sboyd
Cc: robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
hsin-hsiung.wang, linux-kernel, devicetree, linux-arm-kernel,
linux-mediatek, kernel
In preparation for adding support for MT8196/MT6991 SoCs having
multiple SPMI busses, move the bus specific parameters into a new
pmif_bus structure and keep the SoC-specific data in the already
existing struct pmif, and add means to register multiple SPMI
controllers.
While this needs a different devicetree node structure, where each
of the controllers are in subnodes of a main SPMI node, and where
each has its own resources (iospaces and clocks), support for the
legacy single-controller was retained and doesn't require any DT
change in the currently supported SoCs.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/spmi/spmi-mtk-pmif.c | 235 +++++++++++++++++++++++------------
1 file changed, 157 insertions(+), 78 deletions(-)
diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index 160d36f7d238..68f458587c67 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2021 MediaTek Inc.
+// Copyright (c) 2025 Collabora Ltd
+// AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
#include <linux/clk.h>
#include <linux/iopoll.h>
@@ -25,6 +27,7 @@
#define PMIF_CHAN_OFFSET 0x5
+#define PMIF_MAX_BUSES 2
#define PMIF_MAX_CLKS 3
#define SPMI_OP_ST_BUSY 1
@@ -41,16 +44,22 @@ struct pmif_data {
const u32 *regs;
const u32 *spmimst_regs;
u32 soc_chan;
+ u32 num_spmi_buses;
};
-struct pmif {
+struct pmif_bus {
void __iomem *base;
void __iomem *spmimst_base;
- struct ch_reg chan;
+ struct spmi_controller *ctrl;
struct clk_bulk_data clks[PMIF_MAX_CLKS];
size_t nclks;
+ raw_spinlock_t lock;
+};
+
+struct pmif {
+ struct pmif_bus bus[PMIF_MAX_BUSES];
+ struct ch_reg chan;
const struct pmif_data *data;
- raw_spinlock_t lock;
};
static const char * const pmif_clock_names[] = {
@@ -262,33 +271,41 @@ static const u32 mt8195_spmi_regs[] = {
[SPMI_MST_DBG] = 0x00FC,
};
-static u32 pmif_readl(struct pmif *arb, enum pmif_regs reg)
+static inline struct pmif *to_mtk_pmif(struct spmi_controller *ctrl)
+{
+ return dev_get_drvdata(ctrl->dev.parent);
+}
+
+static u32 pmif_readl(struct pmif *arb, struct pmif_bus *pbus, enum pmif_regs reg)
{
- return readl(arb->base + arb->data->regs[reg]);
+ return readl(pbus->base + arb->data->regs[reg]);
}
-static void pmif_writel(struct pmif *arb, u32 val, enum pmif_regs reg)
+static void pmif_writel(struct pmif *arb, struct pmif_bus *pbus,
+ u32 val, enum pmif_regs reg)
{
- writel(val, arb->base + arb->data->regs[reg]);
+ writel(val, pbus->base + arb->data->regs[reg]);
}
-static void mtk_spmi_writel(struct pmif *arb, u32 val, enum spmi_regs reg)
+static void mtk_spmi_writel(struct pmif *arb, struct pmif_bus *pbus,
+ u32 val, enum spmi_regs reg)
{
- writel(val, arb->spmimst_base + arb->data->spmimst_regs[reg]);
+ writel(val, pbus->spmimst_base + arb->data->spmimst_regs[reg]);
}
-static bool pmif_is_fsm_vldclr(struct pmif *arb)
+static bool pmif_is_fsm_vldclr(struct pmif *arb, struct pmif_bus *pbus)
{
u32 reg_rdata;
- reg_rdata = pmif_readl(arb, arb->chan.ch_sta);
+ reg_rdata = pmif_readl(arb, pbus, arb->chan.ch_sta);
return GET_SWINF(reg_rdata) == SWINF_WFVLDCLR;
}
static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
{
- struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+ struct pmif_bus *pbus = spmi_controller_get_drvdata(ctrl);
+ struct pmif *arb = to_mtk_pmif(ctrl);
u32 rdata, cmd;
int ret;
@@ -298,8 +315,8 @@ static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
cmd = opc - SPMI_CMD_RESET;
- mtk_spmi_writel(arb, (cmd << 0x4) | sid, SPMI_OP_ST_CTRL);
- ret = readl_poll_timeout_atomic(arb->spmimst_base + arb->data->spmimst_regs[SPMI_OP_ST_STA],
+ mtk_spmi_writel(arb, pbus, (cmd << 0x4) | sid, SPMI_OP_ST_CTRL);
+ ret = readl_poll_timeout_atomic(pbus->spmimst_base + arb->data->spmimst_regs[SPMI_OP_ST_STA],
rdata, (rdata & SPMI_OP_ST_BUSY) == SPMI_OP_ST_BUSY,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0)
@@ -311,7 +328,8 @@ static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
u16 addr, u8 *buf, size_t len)
{
- struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+ struct pmif_bus *pbus = spmi_controller_get_drvdata(ctrl);
+ struct pmif *arb = to_mtk_pmif(ctrl);
struct ch_reg *inf_reg;
int ret;
u32 data, cmd;
@@ -336,31 +354,31 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
else
return -EINVAL;
- raw_spin_lock_irqsave(&arb->lock, flags);
+ raw_spin_lock_irqsave(&pbus->lock, flags);
/* Wait for Software Interface FSM state to be IDLE. */
inf_reg = &arb->chan;
- ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
+ ret = readl_poll_timeout_atomic(pbus->base + arb->data->regs[inf_reg->ch_sta],
data, GET_SWINF(data) == SWINF_IDLE,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0) {
/* set channel ready if the data has transferred */
- if (pmif_is_fsm_vldclr(arb))
- pmif_writel(arb, 1, inf_reg->ch_rdy);
- raw_spin_unlock_irqrestore(&arb->lock, flags);
+ if (pmif_is_fsm_vldclr(arb, pbus))
+ pmif_writel(arb, pbus, 1, inf_reg->ch_rdy);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
return ret;
}
/* Send the command. */
cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr;
- pmif_writel(arb, cmd, inf_reg->ch_send);
- raw_spin_unlock_irqrestore(&arb->lock, flags);
+ pmif_writel(arb, pbus, cmd, inf_reg->ch_send);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
/*
* Wait for Software Interface FSM state to be WFVLDCLR,
* read the data and clear the valid flag.
*/
- ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
+ ret = readl_poll_timeout_atomic(pbus->base + arb->data->regs[inf_reg->ch_sta],
data, GET_SWINF(data) == SWINF_WFVLDCLR,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0) {
@@ -368,9 +386,9 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
return ret;
}
- data = pmif_readl(arb, inf_reg->rdata);
+ data = pmif_readl(arb, pbus, inf_reg->rdata);
memcpy(buf, &data, len);
- pmif_writel(arb, 1, inf_reg->ch_rdy);
+ pmif_writel(arb, pbus, 1, inf_reg->ch_rdy);
return 0;
}
@@ -378,7 +396,8 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
u16 addr, const u8 *buf, size_t len)
{
- struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+ struct pmif_bus *pbus = spmi_controller_get_drvdata(ctrl);
+ struct pmif *arb = to_mtk_pmif(ctrl);
struct ch_reg *inf_reg;
int ret;
u32 data, wdata, cmd;
@@ -409,27 +428,27 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
/* Set the write data. */
memcpy(&wdata, buf, len);
- raw_spin_lock_irqsave(&arb->lock, flags);
+ raw_spin_lock_irqsave(&pbus->lock, flags);
/* Wait for Software Interface FSM state to be IDLE. */
inf_reg = &arb->chan;
- ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta],
+ ret = readl_poll_timeout_atomic(pbus->base + arb->data->regs[inf_reg->ch_sta],
data, GET_SWINF(data) == SWINF_IDLE,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0) {
/* set channel ready if the data has transferred */
- if (pmif_is_fsm_vldclr(arb))
- pmif_writel(arb, 1, inf_reg->ch_rdy);
- raw_spin_unlock_irqrestore(&arb->lock, flags);
+ if (pmif_is_fsm_vldclr(arb, pbus))
+ pmif_writel(arb, pbus, 1, inf_reg->ch_rdy);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n");
return ret;
}
- pmif_writel(arb, wdata, inf_reg->wdata);
+ pmif_writel(arb, pbus, wdata, inf_reg->wdata);
/* Send the command. */
cmd = (opc << 30) | BIT(29) | (sid << 24) | ((len - 1) << 16) | addr;
- pmif_writel(arb, cmd, inf_reg->ch_send);
- raw_spin_unlock_irqrestore(&arb->lock, flags);
+ pmif_writel(arb, pbus, cmd, inf_reg->ch_send);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
return 0;
}
@@ -446,84 +465,143 @@ static const struct pmif_data mt8195_pmif_arb = {
.soc_chan = 2,
};
-static int mtk_spmi_probe(struct platform_device *pdev)
+static int mtk_spmi_bus_probe(struct platform_device *pdev,
+ struct device_node *node,
+ const struct pmif_data *pdata,
+ struct pmif_bus *pbus)
{
- struct pmif *arb;
struct spmi_controller *ctrl;
- int err, i;
- u32 chan_offset;
+ int err, idx, bus_id, i;
+
+ if (pdata->num_spmi_buses > 1)
+ bus_id = of_alias_get_id(node, "spmi");
+ else
+ bus_id = 0;
+
+ if (bus_id < 0)
+ return dev_err_probe(&pdev->dev, bus_id,
+ "Cannot find SPMI Bus alias ID\n");
- ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*arb));
+ ctrl = devm_spmi_controller_alloc(&pdev->dev, sizeof(*pbus));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
- arb = spmi_controller_get_drvdata(ctrl);
- arb->data = device_get_match_data(&pdev->dev);
- if (!arb->data) {
- dev_err(&pdev->dev, "Cannot get drv_data\n");
+ pbus = spmi_controller_get_drvdata(ctrl);
+ pbus->ctrl = ctrl;
+
+ idx = of_property_match_string(node, "reg-names", "pmif");
+ if (idx < 0)
return -EINVAL;
- }
- arb->base = devm_platform_ioremap_resource_byname(pdev, "pmif");
- if (IS_ERR(arb->base))
- return PTR_ERR(arb->base);
+ pbus->base = devm_of_iomap(&pdev->dev, node, idx, NULL);
+ if (IS_ERR(pbus->base))
+ return PTR_ERR(pbus->base);
- arb->spmimst_base = devm_platform_ioremap_resource_byname(pdev, "spmimst");
- if (IS_ERR(arb->spmimst_base))
- return PTR_ERR(arb->spmimst_base);
+ idx = of_property_match_string(node, "reg-names", "spmimst");
+ if (idx < 0)
+ return -EINVAL;
- arb->nclks = ARRAY_SIZE(pmif_clock_names);
- for (i = 0; i < arb->nclks; i++)
- arb->clks[i].id = pmif_clock_names[i];
+ pbus->spmimst_base = devm_of_iomap(&pdev->dev, node, idx, NULL);
+ if (IS_ERR(pbus->spmimst_base))
+ return PTR_ERR(pbus->spmimst_base);
- err = clk_bulk_get(&pdev->dev, arb->nclks, arb->clks);
- if (err) {
- dev_err(&pdev->dev, "Failed to get clocks: %d\n", err);
- return err;
+ pbus->nclks = ARRAY_SIZE(pmif_clock_names);
+ for (i = 0; i < pbus->nclks; i++) {
+ pbus->clks[i].id = pmif_clock_names[i];
+ pbus->clks[i].clk = of_clk_get_by_name(node, pbus->clks[i].id);
+ if (IS_ERR(pbus->clks[i].clk))
+ return PTR_ERR(pbus->clks[i].clk);
}
- err = clk_bulk_prepare_enable(arb->nclks, arb->clks);
- if (err) {
- dev_err(&pdev->dev, "Failed to enable clocks: %d\n", err);
+ err = clk_bulk_prepare_enable(pbus->nclks, pbus->clks);
+ if (err)
goto err_put_clks;
- }
ctrl->cmd = pmif_arb_cmd;
ctrl->read_cmd = pmif_spmi_read_cmd;
ctrl->write_cmd = pmif_spmi_write_cmd;
+ ctrl->dev.of_node = node;
+ dev_set_name(&ctrl->dev, "spmi-%d", bus_id);
- chan_offset = PMIF_CHAN_OFFSET * arb->data->soc_chan;
- arb->chan.ch_sta = PMIF_SWINF_0_STA + chan_offset;
- arb->chan.wdata = PMIF_SWINF_0_WDATA_31_0 + chan_offset;
- arb->chan.rdata = PMIF_SWINF_0_RDATA_31_0 + chan_offset;
- arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset;
- arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset;
-
- raw_spin_lock_init(&arb->lock);
-
- platform_set_drvdata(pdev, ctrl);
+ raw_spin_lock_init(&pbus->lock);
err = spmi_controller_add(ctrl);
if (err)
goto err_domain_remove;
+ pbus->ctrl = ctrl;
+
return 0;
err_domain_remove:
- clk_bulk_disable_unprepare(arb->nclks, arb->clks);
+ clk_bulk_disable_unprepare(pbus->nclks, pbus->clks);
err_put_clks:
- clk_bulk_put(arb->nclks, arb->clks);
+ clk_bulk_put(pbus->nclks, pbus->clks);
return err;
}
+static int mtk_spmi_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct pmif *arb;
+ u32 chan_offset;
+ u8 cur_bus = 0;
+ int ret;
+
+ arb = devm_kzalloc(&pdev->dev, sizeof(*arb), GFP_KERNEL);
+ if (!arb)
+ return -ENOMEM;
+
+ arb->data = device_get_match_data(&pdev->dev);
+ if (!arb->data) {
+ dev_err(&pdev->dev, "Cannot get drv_data\n");
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, arb);
+
+ if (!arb->data->num_spmi_buses) {
+ ret = mtk_spmi_bus_probe(pdev, node, arb->data, &arb->bus[cur_bus]);
+ if (ret)
+ return ret;
+ } else {
+ for_each_available_child_of_node_scoped(node, child) {
+ if (!of_node_name_eq(child, "spmi"))
+ continue;
+
+ ret = mtk_spmi_bus_probe(pdev, child, arb->data,
+ &arb->bus[cur_bus]);
+ if (ret)
+ return ret;
+ cur_bus++;
+ }
+ }
+
+ chan_offset = PMIF_CHAN_OFFSET * arb->data->soc_chan;
+ arb->chan.ch_sta = PMIF_SWINF_0_STA + chan_offset;
+ arb->chan.wdata = PMIF_SWINF_0_WDATA_31_0 + chan_offset;
+ arb->chan.rdata = PMIF_SWINF_0_RDATA_31_0 + chan_offset;
+ arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset;
+ arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset;
+
+ return 0;
+}
+
static void mtk_spmi_remove(struct platform_device *pdev)
{
- struct spmi_controller *ctrl = platform_get_drvdata(pdev);
- struct pmif *arb = spmi_controller_get_drvdata(ctrl);
+ struct pmif *arb = platform_get_drvdata(pdev);
+ int i;
- spmi_controller_remove(ctrl);
- clk_bulk_disable_unprepare(arb->nclks, arb->clks);
- clk_bulk_put(arb->nclks, arb->clks);
+ for (i = 0; i < PMIF_MAX_BUSES; i++) {
+ struct pmif_bus *pbus = &arb->bus[i];
+
+ if (!pbus->ctrl)
+ continue;
+
+ spmi_controller_remove(pbus->ctrl);
+ clk_bulk_disable_unprepare(pbus->nclks, pbus->clks);
+ clk_bulk_put(pbus->nclks, pbus->clks);
+ }
}
static const struct of_device_id mtk_spmi_match_table[] = {
@@ -549,6 +627,7 @@ static struct platform_driver mtk_spmi_driver = {
};
module_platform_driver(mtk_spmi_driver);
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_AUTHOR("Hsin-Hsiung Wang <hsin-hsiung.wang@mediatek.com>");
MODULE_DESCRIPTION("MediaTek SPMI Driver");
MODULE_LICENSE("GPL");
--
2.49.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v1 3/5] spmi: mtk-pmif: Keep spinlock until read is fully done
2025-06-23 12:00 [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus AngeloGioacchino Del Regno
2025-06-23 12:00 ` [PATCH v1 1/5] dt-bindings: spmi: Add MediaTek MT8196 SPMI 2 Arbiter/Controllers AngeloGioacchino Del Regno
2025-06-23 12:00 ` [PATCH v1 2/5] spmi: mtk-pmif: Add multi-bus support for SPMI 2.0 AngeloGioacchino Del Regno
@ 2025-06-23 12:00 ` AngeloGioacchino Del Regno
2025-06-23 12:00 ` [PATCH v1 4/5] spmi: mtk-pmif: Implement Request Capable Slave (RCS) interrupt AngeloGioacchino Del Regno
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: AngeloGioacchino Del Regno @ 2025-06-23 12:00 UTC (permalink / raw)
To: sboyd
Cc: robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
hsin-hsiung.wang, linux-kernel, devicetree, linux-arm-kernel,
linux-mediatek, kernel
Move the spin unlocking to after reading the contents of the
PMIF_SWINF_(x)_RDATA_31_0 register in pmif_spmi_read_cmd():
since this is the only register that we can read to get the
data from all of the arbitered busses, a concurrent request
for reading (especially on a busy arbiter) will show a race
condition and a unexpected or corrupted value may be read.
Doing the entire read sequence while spin locked guarantees
that concurrent access to the arbiter doesn't happen.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/spmi/spmi-mtk-pmif.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index 68f458587c67..9f416b231ab8 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -22,7 +22,7 @@
#define PMIF_CMD_EXT_REG 2
#define PMIF_CMD_EXT_REG_LONG 3
-#define PMIF_DELAY_US 10
+#define PMIF_DELAY_US 2
#define PMIF_TIMEOUT_US (10 * 1000)
#define PMIF_CHAN_OFFSET 0x5
@@ -372,7 +372,6 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
/* Send the command. */
cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr;
pmif_writel(arb, pbus, cmd, inf_reg->ch_send);
- raw_spin_unlock_irqrestore(&pbus->lock, flags);
/*
* Wait for Software Interface FSM state to be WFVLDCLR,
@@ -382,13 +381,16 @@ static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
data, GET_SWINF(data) == SWINF_WFVLDCLR,
PMIF_DELAY_US, PMIF_TIMEOUT_US);
if (ret < 0) {
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
dev_err(&ctrl->dev, "failed to wait for SWINF_WFVLDCLR\n");
return ret;
}
data = pmif_readl(arb, pbus, inf_reg->rdata);
- memcpy(buf, &data, len);
pmif_writel(arb, pbus, 1, inf_reg->ch_rdy);
+ raw_spin_unlock_irqrestore(&pbus->lock, flags);
+
+ memcpy(buf, &data, len);
return 0;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v1 4/5] spmi: mtk-pmif: Implement Request Capable Slave (RCS) interrupt
2025-06-23 12:00 [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus AngeloGioacchino Del Regno
` (2 preceding siblings ...)
2025-06-23 12:00 ` [PATCH v1 3/5] spmi: mtk-pmif: Keep spinlock until read is fully done AngeloGioacchino Del Regno
@ 2025-06-23 12:00 ` AngeloGioacchino Del Regno
2025-06-23 12:00 ` [PATCH v1 5/5] spmi: mtk-pmif: Add support for MT8196 SPMI Controller AngeloGioacchino Del Regno
2025-06-24 14:51 ` [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus Nícolas F. R. A. Prado
5 siblings, 0 replies; 9+ messages in thread
From: AngeloGioacchino Del Regno @ 2025-06-23 12:00 UTC (permalink / raw)
To: sboyd
Cc: robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
hsin-hsiung.wang, linux-kernel, devicetree, linux-arm-kernel,
linux-mediatek, kernel
Add support for the per-bus RCS interrupt by adding a new linear
irqdomain and its irqchip.
The SPMI controller will raise an interrupt when any of the SPMI
connected devices' irq needs attention (whenever any interrupt
fires on any SID) in one of four registers, where each register
holds four sets of four bits of information about a SID interrupt.
This controller's RCS interrupt status knowledge is limited to the
address of the SID that raised an interrupt, but does not have any
details about the devices irq numbers: as this may change with a
future SPMI controller IP version, the devicetree is meant to hold
three cells, where the first one is the SPMI SID interrupt number,
the second one is a device interrupt number, and the third one is
the irq sense type.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/spmi/spmi-mtk-pmif.c | 231 ++++++++++++++++++++++++++++++++++-
1 file changed, 226 insertions(+), 5 deletions(-)
diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index 9f416b231ab8..ad7b0cc9cdaa 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -5,12 +5,17 @@
// AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
#include <linux/clk.h>
+#include <linux/interrupt.h>
#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/spmi.h>
+#include <linux/irqchip/chained_irq.h>
#define SWINF_IDLE 0x00
#define SWINF_WFVLDCLR 0x06
@@ -26,6 +31,7 @@
#define PMIF_TIMEOUT_US (10 * 1000)
#define PMIF_CHAN_OFFSET 0x5
+#define PMIF_RCS_IRQ_MASK GENMASK(7, 0)
#define PMIF_MAX_BUSES 2
#define PMIF_MAX_CLKS 3
@@ -44,6 +50,7 @@ struct pmif_data {
const u32 *regs;
const u32 *spmimst_regs;
u32 soc_chan;
+ u8 spmi_ver;
u32 num_spmi_buses;
};
@@ -51,8 +58,14 @@ struct pmif_bus {
void __iomem *base;
void __iomem *spmimst_base;
struct spmi_controller *ctrl;
+ struct irq_domain *dom;
+ int irq;
struct clk_bulk_data clks[PMIF_MAX_CLKS];
size_t nclks;
+ struct mutex rcs_lock;
+ u8 irq_min_sid;
+ u8 irq_max_sid;
+ u16 irq_en;
raw_spinlock_t lock;
};
@@ -287,6 +300,11 @@ static void pmif_writel(struct pmif *arb, struct pmif_bus *pbus,
writel(val, pbus->base + arb->data->regs[reg]);
}
+static u32 mtk_spmi_readl(struct pmif *arb, struct pmif_bus *pbus, enum spmi_regs reg)
+{
+ return readl(pbus->spmimst_base + arb->data->spmimst_regs[reg]);
+}
+
static void mtk_spmi_writel(struct pmif *arb, struct pmif_bus *pbus,
u32 val, enum spmi_regs reg)
{
@@ -455,6 +473,157 @@ static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
return 0;
}
+static void mtk_spmi_handle_chained_irq(struct irq_desc *desc)
+{
+ struct pmif_bus *pbus = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct pmif *arb = to_mtk_pmif(pbus->ctrl);
+ u8 regidx_min, regidx_max;
+ bool irq_handled = false;
+ unsigned int i;
+
+ regidx_min = pbus->irq_min_sid / 4;
+ regidx_min += SPMI_SLV_3_0_EINT;
+
+ regidx_max = pbus->irq_max_sid / 4;
+ regidx_min += SPMI_SLV_3_0_EINT;
+
+ chained_irq_enter(chip, desc);
+
+ for (i = regidx_min; i <= regidx_max; i++) {
+ u32 val = mtk_spmi_readl(arb, pbus, i);
+
+ while (val) {
+ u8 bit = __ffs(val);
+ u8 bank = bit / 7;
+ u8 sid = ((i - SPMI_SLV_3_0_EINT) * 4) + bank;
+
+ val &= ~(PMIF_RCS_IRQ_MASK << (8 * bank));
+
+ /* Check if IRQs for this SID are enabled */
+ if (!(pbus->irq_en & BIT(sid)))
+ continue;
+
+ generic_handle_domain_irq(pbus->dom, sid);
+ irq_handled = true;
+ }
+ }
+
+ if (!irq_handled)
+ handle_bad_irq(desc);
+
+ chained_irq_exit(chip, desc);
+}
+
+static void mtk_spmi_rcs_irq_ack(struct irq_data *d)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+ struct pmif *arb = to_mtk_pmif(pbus->ctrl);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
+ unsigned int reg, shift;
+
+ /* There are four interrupts (8 bits each) per register */
+ reg = SPMI_SLV_3_0_EINT + d->hwirq / 4;
+ shift = (irq % 4) * 8;
+
+ mtk_spmi_writel(arb, pbus, PMIF_RCS_IRQ_MASK << shift, reg);
+}
+
+static void mtk_spmi_rcs_irq_lock(struct irq_data *d)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+
+ mutex_lock(&pbus->rcs_lock);
+}
+
+static void mtk_spmi_rcs_irq_sync_unlock(struct irq_data *d)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+
+ mutex_unlock(&pbus->rcs_lock);
+}
+
+static void mtk_spmi_rcs_irq_enable(struct irq_data *d)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
+
+ pbus->irq_en |= BIT(irq);
+}
+
+static void mtk_spmi_rcs_irq_disable(struct irq_data *d)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
+
+ pbus->irq_en &= ~BIT(irq);
+}
+
+static int mtk_spmi_rcs_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+ struct pmif_bus *pbus = irq_data_get_irq_chip_data(d);
+
+ return irq_set_irq_wake(pbus->irq, on);
+}
+
+static const struct irq_chip mtk_spmi_rcs_irq_chip = {
+ .name = "spmi_rcs",
+ .irq_ack = mtk_spmi_rcs_irq_ack,
+ .irq_bus_lock = mtk_spmi_rcs_irq_lock,
+ .irq_bus_sync_unlock = mtk_spmi_rcs_irq_sync_unlock,
+ .irq_enable = mtk_spmi_rcs_irq_enable,
+ .irq_disable = mtk_spmi_rcs_irq_disable,
+ .irq_set_wake = mtk_spmi_rcs_irq_set_wake,
+};
+
+static int mtk_spmi_rcs_irq_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct pmif_bus *pbus = d->host_data;
+
+ irq_set_chip_data(virq, pbus);
+ irq_set_chip_and_handler(virq, &mtk_spmi_rcs_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static int mtk_spmi_rcs_irq_xlate(struct irq_domain *d, struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ struct pmif_bus *pbus = d->host_data;
+ struct device *dev = &pbus->ctrl->dev;
+ struct irq_fwspec fwspec;
+
+ of_phandle_args_to_fwspec(ctrlr, intspec, intsize, &fwspec);
+ if (WARN_ON(fwspec.param_count < 3))
+ return -EINVAL;
+
+ /*
+ * The IRQ number in intspec[1] is ignored on purpose here!
+ *
+ * The controller only has knowledge of which SID raised an interrupt
+ * and the type of irq, but doesn't know about any device irq number,
+ * hence that must be read from the SPMI device's registers.
+ */
+ *out_hwirq = intspec[0];
+ *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+
+ if (pbus->irq_min_sid > intspec[0])
+ pbus->irq_min_sid = intspec[0];
+
+ if (pbus->irq_max_sid < intspec[0])
+ pbus->irq_max_sid = intspec[0];
+
+ dev_dbg(dev, "Found SPMI IRQ %u (map: 0x%lx)\n", intspec[0], *out_hwirq);
+ return 0;
+}
+
+static const struct irq_domain_ops mtk_spmi_rcs_irq_domain_ops = {
+ .map = mtk_spmi_rcs_irq_map,
+ .xlate = mtk_spmi_rcs_irq_xlate,
+};
+
static const struct pmif_data mt6873_pmif_arb = {
.regs = mt6873_regs,
.spmimst_regs = mt6873_spmi_regs,
@@ -467,6 +636,45 @@ static const struct pmif_data mt8195_pmif_arb = {
.soc_chan = 2,
};
+static int mtk_spmi_irq_init(struct device_node *node,
+ const struct pmif_data *pdata,
+ struct pmif_bus *pbus)
+{
+ struct pmif *arb = to_mtk_pmif(pbus->ctrl);
+ unsigned int i;
+
+ /* No interrupts required for SPMI 1.x controller */
+ if (pdata->spmi_ver < 2) {
+ pbus->dom = NULL;
+ return 0;
+ }
+
+ pbus->irq = of_irq_get_byname(node, "rcs");
+ if (pbus->irq <= 0)
+ return pbus->irq ? : -ENXIO;
+
+ mutex_init(&pbus->rcs_lock);
+
+ pbus->dom = irq_domain_add_tree(node, &mtk_spmi_rcs_irq_domain_ops, pbus);
+ if (!pbus->dom)
+ return -ENOMEM;
+
+ /* Clear possible unhandled interrupts coming from bootloader SPMI init */
+ for (i = SPMI_SLV_3_0_EINT; i <= SPMI_SLV_F_C_EINT; i++)
+ mtk_spmi_writel(arb, pbus, GENMASK(31, 0), i);
+
+ return 0;
+}
+
+static void mtk_spmi_irq_remove(struct pmif_bus *pbus)
+{
+ if (!pbus->dom)
+ return;
+
+ irq_set_chained_handler_and_data(pbus->irq, NULL, NULL);
+ irq_domain_remove(pbus->dom);
+}
+
static int mtk_spmi_bus_probe(struct platform_device *pdev,
struct device_node *node,
const struct pmif_data *pdata,
@@ -512,12 +720,21 @@ static int mtk_spmi_bus_probe(struct platform_device *pdev,
pbus->clks[i].id = pmif_clock_names[i];
pbus->clks[i].clk = of_clk_get_by_name(node, pbus->clks[i].id);
if (IS_ERR(pbus->clks[i].clk))
- return PTR_ERR(pbus->clks[i].clk);
+ return dev_err_probe(&pdev->dev, PTR_ERR(pbus->clks[i].clk),
+ "Failed to get clocks\n");
}
err = clk_bulk_prepare_enable(pbus->nclks, pbus->clks);
- if (err)
+ if (err) {
+ dev_err_probe(&pdev->dev, err, "Failed to enable clocks\n");
goto err_put_clks;
+ }
+
+ err = mtk_spmi_irq_init(node, pdata, pbus);
+ if (err) {
+ dev_err_probe(&pdev->dev, err, "Cannot initialize SPMI IRQs\n");
+ goto err_disable_clks;
+ }
ctrl->cmd = pmif_arb_cmd;
ctrl->read_cmd = pmif_spmi_read_cmd;
@@ -529,13 +746,16 @@ static int mtk_spmi_bus_probe(struct platform_device *pdev,
err = spmi_controller_add(ctrl);
if (err)
- goto err_domain_remove;
+ goto err_remove_irq;
- pbus->ctrl = ctrl;
+ if (pbus->dom)
+ irq_set_chained_handler_and_data(pbus->irq, mtk_spmi_handle_chained_irq, pbus);
return 0;
-err_domain_remove:
+err_remove_irq:
+ mtk_spmi_irq_remove(pbus);
+err_disable_clks:
clk_bulk_disable_unprepare(pbus->nclks, pbus->clks);
err_put_clks:
clk_bulk_put(pbus->nclks, pbus->clks);
@@ -600,6 +820,7 @@ static void mtk_spmi_remove(struct platform_device *pdev)
if (!pbus->ctrl)
continue;
+ mtk_spmi_irq_remove(pbus);
spmi_controller_remove(pbus->ctrl);
clk_bulk_disable_unprepare(pbus->nclks, pbus->clks);
clk_bulk_put(pbus->nclks, pbus->clks);
--
2.49.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v1 5/5] spmi: mtk-pmif: Add support for MT8196 SPMI Controller
2025-06-23 12:00 [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus AngeloGioacchino Del Regno
` (3 preceding siblings ...)
2025-06-23 12:00 ` [PATCH v1 4/5] spmi: mtk-pmif: Implement Request Capable Slave (RCS) interrupt AngeloGioacchino Del Regno
@ 2025-06-23 12:00 ` AngeloGioacchino Del Regno
2025-06-24 14:51 ` [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus Nícolas F. R. A. Prado
5 siblings, 0 replies; 9+ messages in thread
From: AngeloGioacchino Del Regno @ 2025-06-23 12:00 UTC (permalink / raw)
To: sboyd
Cc: robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
hsin-hsiung.wang, linux-kernel, devicetree, linux-arm-kernel,
linux-mediatek, kernel
Add support for the SPMI controller found in the MT8196 SoC:
this supports SPMI 2.0 and features two SPMI buses.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/spmi/spmi-mtk-pmif.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c
index ad7b0cc9cdaa..719753651234 100644
--- a/drivers/spmi/spmi-mtk-pmif.c
+++ b/drivers/spmi/spmi-mtk-pmif.c
@@ -636,6 +636,14 @@ static const struct pmif_data mt8195_pmif_arb = {
.soc_chan = 2,
};
+static const struct pmif_data mt8196_pmif_arb = {
+ .regs = mt8195_regs,
+ .spmimst_regs = mt8195_spmi_regs,
+ .soc_chan = 2,
+ .spmi_ver = 2,
+ .num_spmi_buses = 2,
+};
+
static int mtk_spmi_irq_init(struct device_node *node,
const struct pmif_data *pdata,
struct pmif_bus *pbus)
@@ -834,6 +842,9 @@ static const struct of_device_id mtk_spmi_match_table[] = {
}, {
.compatible = "mediatek,mt8195-spmi",
.data = &mt8195_pmif_arb,
+ }, {
+ .compatible = "mediatek,mt8196-spmi",
+ .data = &mt8196_pmif_arb,
}, {
/* sentinel */
},
--
2.49.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus
2025-06-23 12:00 [PATCH v1 0/5] SPMI: MediaTek: Add support for multi-bus AngeloGioacchino Del Regno
` (4 preceding siblings ...)
2025-06-23 12:00 ` [PATCH v1 5/5] spmi: mtk-pmif: Add support for MT8196 SPMI Controller AngeloGioacchino Del Regno
@ 2025-06-24 14:51 ` Nícolas F. R. A. Prado
5 siblings, 0 replies; 9+ messages in thread
From: Nícolas F. R. A. Prado @ 2025-06-24 14:51 UTC (permalink / raw)
To: AngeloGioacchino Del Regno, sboyd
Cc: robh, krzk+dt, conor+dt, matthias.bgg, hsin-hsiung.wang,
linux-kernel, devicetree, linux-arm-kernel, linux-mediatek,
kernel
On Mon, 2025-06-23 at 14:00 +0200, AngeloGioacchino Del Regno wrote:
> This series adds basic support for multi-bus (multi-master) SPMI
> controllers, as found in the MediaTek MT8196 Chromebook SoC and
> the MediaTek MT6991 Dimensity 9400 Smartphone SoC, including RCS
> interrupt handling and per-bus registration.
>
> AngeloGioacchino Del Regno (5):
> dt-bindings: spmi: Add MediaTek MT8196 SPMI 2 Arbiter/Controllers
> spmi: mtk-pmif: Add multi-bus support for SPMI 2.0
> spmi: mtk-pmif: Keep spinlock until read is fully done
> spmi: mtk-pmif: Implement Request Capable Slave (RCS) interrupt
> spmi: mtk-pmif: Add support for MT8196 SPMI Controller
>
> .../bindings/spmi/mediatek,mt8196-spmi.yaml | 138 +++++
> drivers/spmi/spmi-mtk-pmif.c | 471 +++++++++++++++-
> --
> 2 files changed, 530 insertions(+), 79 deletions(-)
> create mode 100644
> Documentation/devicetree/bindings/spmi/mediatek,mt8196-spmi.yaml
>
For the whole series:
Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
(as I internally reviewed it before submission)
--
Thanks,
Nícolas
^ permalink raw reply [flat|nested] 9+ messages in thread