All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/7] Add Support for MediaTek MT8192 APU Power
@ 2021-10-23 10:13 ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

The MediaTek AI Processing Unit (APU) is a proprietary hardware
in the SoC to support AI operations.
The series is to create apusys in the SoC folder for developing
the related drivers. Add the apu clocks, basic apu nodes and the
power domain to provide the power controller of APU subsystem.

This series is based on MT8192 clock[1] and MT8193/PMIC[3][4] patches.
[1] https://patchwork.kernel.org/project/linux-mediatek/list/?series=521655
[2] https://patchwork.kernel.org/patch/12134935
[3] https://patchwork.kernel.org/patch/12140237

Change notes:
v2->v3:
  1) Rebase to 5.15-rc1
  2) remove the apu-mbox registers from syscon.
  3) update mtk-apu-pm to fix clock flow and move power status bit into
     platform data to support different hardware settings.

v1->v2:
  1) update expression "s/_/-/" in dt-bindings documents.
  2) drop apu power domain header file for mt8192.

v1: https://patchwork.kernel.org/project/linux-mediatek/list/?series=461999

Flora Fu (7):
  dt-bindings: clock: Add MT8192 APU clock bindings
  clk: mediatek: mt8192: Add APU clocks support
  dt-bindings: arm: mediatek: Add new document bindings for APU
  dt-bindings: soc: mediatek: apusys: Add new document for APU power
    domain
  soc: mediatek: apu: Add apusys and add apu power domain driver
  arm64: dts: mt8192: Add APU node
  arm64: dts: mt8192: Add APU power domain node

 .../arm/mediatek/mediatek,apusys.yaml         |  49 ++
 .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++
 arch/arm64/boot/dts/mediatek/mt8192-evb.dts   |   7 +
 arch/arm64/boot/dts/mediatek/mt8192.dtsi      |  39 ++
 drivers/clk/mediatek/clk-mt8192.c             |  91 +++
 drivers/soc/mediatek/Kconfig                  |  19 +
 drivers/soc/mediatek/Makefile                 |   1 +
 drivers/soc/mediatek/apusys/Makefile          |   2 +
 drivers/soc/mediatek/apusys/mtk-apu-pm.c      | 633 ++++++++++++++++++
 include/dt-bindings/clock/mt8192-clk.h        |  14 +-
 10 files changed, 997 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
 create mode 100644 drivers/soc/mediatek/apusys/Makefile
 create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c

-- 
2.18.0


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v3 0/7] Add Support for MediaTek MT8192 APU Power
@ 2021-10-23 10:13 ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

The MediaTek AI Processing Unit (APU) is a proprietary hardware
in the SoC to support AI operations.
The series is to create apusys in the SoC folder for developing
the related drivers. Add the apu clocks, basic apu nodes and the
power domain to provide the power controller of APU subsystem.

This series is based on MT8192 clock[1] and MT8193/PMIC[3][4] patches.
[1] https://patchwork.kernel.org/project/linux-mediatek/list/?series=521655
[2] https://patchwork.kernel.org/patch/12134935
[3] https://patchwork.kernel.org/patch/12140237

Change notes:
v2->v3:
  1) Rebase to 5.15-rc1
  2) remove the apu-mbox registers from syscon.
  3) update mtk-apu-pm to fix clock flow and move power status bit into
     platform data to support different hardware settings.

v1->v2:
  1) update expression "s/_/-/" in dt-bindings documents.
  2) drop apu power domain header file for mt8192.

v1: https://patchwork.kernel.org/project/linux-mediatek/list/?series=461999

Flora Fu (7):
  dt-bindings: clock: Add MT8192 APU clock bindings
  clk: mediatek: mt8192: Add APU clocks support
  dt-bindings: arm: mediatek: Add new document bindings for APU
  dt-bindings: soc: mediatek: apusys: Add new document for APU power
    domain
  soc: mediatek: apu: Add apusys and add apu power domain driver
  arm64: dts: mt8192: Add APU node
  arm64: dts: mt8192: Add APU power domain node

 .../arm/mediatek/mediatek,apusys.yaml         |  49 ++
 .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++
 arch/arm64/boot/dts/mediatek/mt8192-evb.dts   |   7 +
 arch/arm64/boot/dts/mediatek/mt8192.dtsi      |  39 ++
 drivers/clk/mediatek/clk-mt8192.c             |  91 +++
 drivers/soc/mediatek/Kconfig                  |  19 +
 drivers/soc/mediatek/Makefile                 |   1 +
 drivers/soc/mediatek/apusys/Makefile          |   2 +
 drivers/soc/mediatek/apusys/mtk-apu-pm.c      | 633 ++++++++++++++++++
 include/dt-bindings/clock/mt8192-clk.h        |  14 +-
 10 files changed, 997 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
 create mode 100644 drivers/soc/mediatek/apusys/Makefile
 create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c

-- 
2.18.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v3 0/7] Add Support for MediaTek MT8192 APU Power
@ 2021-10-23 10:13 ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

The MediaTek AI Processing Unit (APU) is a proprietary hardware
in the SoC to support AI operations.
The series is to create apusys in the SoC folder for developing
the related drivers. Add the apu clocks, basic apu nodes and the
power domain to provide the power controller of APU subsystem.

This series is based on MT8192 clock[1] and MT8193/PMIC[3][4] patches.
[1] https://patchwork.kernel.org/project/linux-mediatek/list/?series=521655
[2] https://patchwork.kernel.org/patch/12134935
[3] https://patchwork.kernel.org/patch/12140237

Change notes:
v2->v3:
  1) Rebase to 5.15-rc1
  2) remove the apu-mbox registers from syscon.
  3) update mtk-apu-pm to fix clock flow and move power status bit into
     platform data to support different hardware settings.

v1->v2:
  1) update expression "s/_/-/" in dt-bindings documents.
  2) drop apu power domain header file for mt8192.

v1: https://patchwork.kernel.org/project/linux-mediatek/list/?series=461999

Flora Fu (7):
  dt-bindings: clock: Add MT8192 APU clock bindings
  clk: mediatek: mt8192: Add APU clocks support
  dt-bindings: arm: mediatek: Add new document bindings for APU
  dt-bindings: soc: mediatek: apusys: Add new document for APU power
    domain
  soc: mediatek: apu: Add apusys and add apu power domain driver
  arm64: dts: mt8192: Add APU node
  arm64: dts: mt8192: Add APU power domain node

 .../arm/mediatek/mediatek,apusys.yaml         |  49 ++
 .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++
 arch/arm64/boot/dts/mediatek/mt8192-evb.dts   |   7 +
 arch/arm64/boot/dts/mediatek/mt8192.dtsi      |  39 ++
 drivers/clk/mediatek/clk-mt8192.c             |  91 +++
 drivers/soc/mediatek/Kconfig                  |  19 +
 drivers/soc/mediatek/Makefile                 |   1 +
 drivers/soc/mediatek/apusys/Makefile          |   2 +
 drivers/soc/mediatek/apusys/mtk-apu-pm.c      | 633 ++++++++++++++++++
 include/dt-bindings/clock/mt8192-clk.h        |  14 +-
 10 files changed, 997 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
 create mode 100644 drivers/soc/mediatek/apusys/Makefile
 create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c

-- 
2.18.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v3 1/7] dt-bindings: clock: Add MT8192 APU clock bindings
  2021-10-23 10:13 ` Flora Fu
  (?)
@ 2021-10-23 10:13   ` Flora Fu
  -1 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add clock bindings for APU on MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Stephen Boyd <sboyd@kernel.org>

---
 include/dt-bindings/clock/mt8192-clk.h | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/include/dt-bindings/clock/mt8192-clk.h b/include/dt-bindings/clock/mt8192-clk.h
index 5ab68f15a256..d5ac7b6c1d5c 100644
--- a/include/dt-bindings/clock/mt8192-clk.h
+++ b/include/dt-bindings/clock/mt8192-clk.h
@@ -164,7 +164,15 @@
 #define CLK_TOP_APLL12_DIV9		152
 #define CLK_TOP_SSUSB_TOP_REF		153
 #define CLK_TOP_SSUSB_PHY_REF		154
-#define CLK_TOP_NR_CLK			155
+#define CLK_TOP_DSP_SEL			155
+#define CLK_TOP_DSP1_SEL		156
+#define CLK_TOP_DSP1_NPUPLL_SEL		157
+#define CLK_TOP_DSP2_SEL		158
+#define CLK_TOP_DSP2_NPUPLL_SEL		159
+#define CLK_TOP_DSP5_SEL		160
+#define CLK_TOP_DSP5_APUPLL_SEL		161
+#define CLK_TOP_IPU_IF_SEL		162
+#define CLK_TOP_NR_CLK			163
 
 /* INFRACFG */
 
@@ -309,7 +317,9 @@
 #define CLK_APMIXED_APLL1		8
 #define CLK_APMIXED_APLL2		9
 #define CLK_APMIXED_MIPID26M		10
-#define CLK_APMIXED_NR_CLK		11
+#define CLK_APMIXED_APUPLL		11
+#define CLK_APMIXED_NPUPLL		12
+#define CLK_APMIXED_NR_CLK		13
 
 /* SCP_ADSP */
 
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 1/7] dt-bindings: clock: Add MT8192 APU clock bindings
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add clock bindings for APU on MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Stephen Boyd <sboyd@kernel.org>

---
 include/dt-bindings/clock/mt8192-clk.h | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/include/dt-bindings/clock/mt8192-clk.h b/include/dt-bindings/clock/mt8192-clk.h
index 5ab68f15a256..d5ac7b6c1d5c 100644
--- a/include/dt-bindings/clock/mt8192-clk.h
+++ b/include/dt-bindings/clock/mt8192-clk.h
@@ -164,7 +164,15 @@
 #define CLK_TOP_APLL12_DIV9		152
 #define CLK_TOP_SSUSB_TOP_REF		153
 #define CLK_TOP_SSUSB_PHY_REF		154
-#define CLK_TOP_NR_CLK			155
+#define CLK_TOP_DSP_SEL			155
+#define CLK_TOP_DSP1_SEL		156
+#define CLK_TOP_DSP1_NPUPLL_SEL		157
+#define CLK_TOP_DSP2_SEL		158
+#define CLK_TOP_DSP2_NPUPLL_SEL		159
+#define CLK_TOP_DSP5_SEL		160
+#define CLK_TOP_DSP5_APUPLL_SEL		161
+#define CLK_TOP_IPU_IF_SEL		162
+#define CLK_TOP_NR_CLK			163
 
 /* INFRACFG */
 
@@ -309,7 +317,9 @@
 #define CLK_APMIXED_APLL1		8
 #define CLK_APMIXED_APLL2		9
 #define CLK_APMIXED_MIPID26M		10
-#define CLK_APMIXED_NR_CLK		11
+#define CLK_APMIXED_APUPLL		11
+#define CLK_APMIXED_NPUPLL		12
+#define CLK_APMIXED_NR_CLK		13
 
 /* SCP_ADSP */
 
-- 
2.18.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 1/7] dt-bindings: clock: Add MT8192 APU clock bindings
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add clock bindings for APU on MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Stephen Boyd <sboyd@kernel.org>

---
 include/dt-bindings/clock/mt8192-clk.h | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/include/dt-bindings/clock/mt8192-clk.h b/include/dt-bindings/clock/mt8192-clk.h
index 5ab68f15a256..d5ac7b6c1d5c 100644
--- a/include/dt-bindings/clock/mt8192-clk.h
+++ b/include/dt-bindings/clock/mt8192-clk.h
@@ -164,7 +164,15 @@
 #define CLK_TOP_APLL12_DIV9		152
 #define CLK_TOP_SSUSB_TOP_REF		153
 #define CLK_TOP_SSUSB_PHY_REF		154
-#define CLK_TOP_NR_CLK			155
+#define CLK_TOP_DSP_SEL			155
+#define CLK_TOP_DSP1_SEL		156
+#define CLK_TOP_DSP1_NPUPLL_SEL		157
+#define CLK_TOP_DSP2_SEL		158
+#define CLK_TOP_DSP2_NPUPLL_SEL		159
+#define CLK_TOP_DSP5_SEL		160
+#define CLK_TOP_DSP5_APUPLL_SEL		161
+#define CLK_TOP_IPU_IF_SEL		162
+#define CLK_TOP_NR_CLK			163
 
 /* INFRACFG */
 
@@ -309,7 +317,9 @@
 #define CLK_APMIXED_APLL1		8
 #define CLK_APMIXED_APLL2		9
 #define CLK_APMIXED_MIPID26M		10
-#define CLK_APMIXED_NR_CLK		11
+#define CLK_APMIXED_APUPLL		11
+#define CLK_APMIXED_NPUPLL		12
+#define CLK_APMIXED_NR_CLK		13
 
 /* SCP_ADSP */
 
-- 
2.18.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 2/7] clk: mediatek: mt8192: Add APU clocks support
  2021-10-23 10:13 ` Flora Fu
  (?)
@ 2021-10-23 10:13   ` Flora Fu
  -1 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU clocks support on MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
Acked-by: Stephen Boyd <sboyd@kernel.org>

---
 drivers/clk/mediatek/clk-mt8192.c | 91 +++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/drivers/clk/mediatek/clk-mt8192.c b/drivers/clk/mediatek/clk-mt8192.c
index cbc7c6dbe0f4..7019caeb0358 100644
--- a/drivers/clk/mediatek/clk-mt8192.c
+++ b/drivers/clk/mediatek/clk-mt8192.c
@@ -244,6 +244,65 @@ static const char * const ccu_parents[] = {
 	"univpll_d6_d2"
 };
 
+static const char * const dsp_parents[] = {
+	"clk26m",
+	"univpll_d6_d2",
+	"univpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mmpll_d4",
+	"mainpll_d3",
+	"univpll_d3"
+};
+
+static const char * const dsp1_parents[] = {
+	"clk26m",
+	"npupll_ck",
+	"mainpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mainpll_d3",
+	"univpll_d3",
+	"apupll_ck"
+};
+
+static const char * const dsp1_npupll_parents[] = {
+	"dsp1_sel",
+	"npupll_ck"
+};
+
+static const char * const dsp2_parents[] = {
+	"clk26m",
+	"npupll_ck",
+	"mainpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mainpll_d3",
+	"univpll_d3",
+	"apupll_ck"
+};
+
+static const char * const dsp2_npupll_parents[] = {
+	"dsp2_sel",
+	"npupll_ck"
+};
+
+static const char * const dsp5_parents[] = {
+	"clk26m",
+	"apupll_ck",
+	"univpll_d4_d2",
+	"mainpll_d4",
+	"univpll_d4",
+	"mmpll_d4",
+	"mainpll_d3",
+	"univpll_d3"
+};
+
+static const char * const dsp5_apupll_parents[] = {
+	"dsp5_sel",
+	"apupll_ck"
+};
+
 static const char * const dsp7_parents[] = {
 	"clk26m",
 	"mainpll_d4_d2",
@@ -255,6 +314,17 @@ static const char * const dsp7_parents[] = {
 	"mmpll_d4"
 };
 
+static const char * const ipu_if_parents[] = {
+	"clk26m",
+	"univpll_d6_d2",
+	"mainpll_d4_d2",
+	"univpll_d4_d2",
+	"univpll_d5",
+	"mainpll_d4",
+	"tvdpll_ck",
+	"univpll_d4"
+};
+
 static const char * const mfg_ref_parents[] = {
 	"clk26m",
 	"clk26m",
@@ -734,9 +804,26 @@ static const struct mtk_mux top_mtk_muxes[] = {
 			     cam_parents, 0x030, 0x034, 0x038, 16, 4, 23, 0x004, 10),
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_CCU_SEL, "ccu_sel",
 			     ccu_parents, 0x030, 0x034, 0x038, 24, 4, 31, 0x004, 11),
+	/* CLK_CFG_3 */
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP_SEL, "dsp_sel",
+			     dsp_parents, 0x040, 0x044, 0x048, 0, 3, 7, 0x004, 12),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP1_SEL, "dsp1_sel",
+			     dsp1_parents, 0x040, 0x044, 0x048, 8, 3, 15, 0x004, 13),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP1_NPUPLL_SEL, "dsp1_npupll_sel",
+			     dsp1_npupll_parents, 0x040, 0x044, 0x048, 11, 1, -1, -1),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP2_SEL, "dsp2_sel",
+			     dsp2_parents, 0x040, 0x044, 0x048, 16, 3, 23, 0x004, 14),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP2_NPUPLL_SEL, "dsp2_npupll_sel",
+			     dsp2_npupll_parents, 0x040, 0x044, 0x048, 19, 1, -1, -1),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP5_SEL, "dsp5_sel",
+			     dsp5_parents, 0x040, 0x044, 0x048, 24, 3, 31, 0x004, 15),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP5_APUPLL_SEL, "dsp5_apupll_sel",
+			     dsp5_apupll_parents, 0x040, 0x044, 0x048, 27, 1, -1, -1),
 	/* CLK_CFG_4 */
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP7_SEL, "dsp7_sel",
 			     dsp7_parents, 0x050, 0x054, 0x058, 0, 3, 7, 0x004, 16),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_IPU_IF_SEL, "ipu_if_sel",
+			     ipu_if_parents, 0x050, 0x054, 0x058, 8, 3, 15, 0x004, 17),
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_REF_SEL, "mfg_ref_sel",
 			     mfg_ref_parents, 0x050, 0x054, 0x058, 16, 2, 23, 0x004, 18),
 	MUX_CLR_SET_UPD(CLK_TOP_MFG_PLL_SEL, "mfg_pll_sel",
@@ -1175,6 +1262,10 @@ static const struct mtk_pll_data plls[] = {
 	      0, 0, 32, 0x031c, 24, 0x0040, 0x000c, 0, 0x0320, 0),
 	PLL_B(CLK_APMIXED_APLL2, "apll2", 0x032c, 0x033c, 0x00000000,
 	      0, 0, 32, 0x0330, 24, 0, 0, 0, 0x0334, 0),
+	PLL_B(CLK_APMIXED_APUPLL, "apupll", 0x03a0, 0x03ac, 0xff000001,
+	      HAVE_RST_BAR, BIT(23), 22, 0x03a4, 24, 0, 0, 0, 0x03a4, 0),
+	PLL_B(CLK_APMIXED_NPUPLL, "npupll", 0x03b4, 0x03c0, 0x00000001,
+	      0, 0, 22, 0x03b8, 24, 0, 0, 0, 0x03b8, 0),
 };
 
 static struct clk_onecell_data *top_clk_data;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 2/7] clk: mediatek: mt8192: Add APU clocks support
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU clocks support on MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
Acked-by: Stephen Boyd <sboyd@kernel.org>

---
 drivers/clk/mediatek/clk-mt8192.c | 91 +++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/drivers/clk/mediatek/clk-mt8192.c b/drivers/clk/mediatek/clk-mt8192.c
index cbc7c6dbe0f4..7019caeb0358 100644
--- a/drivers/clk/mediatek/clk-mt8192.c
+++ b/drivers/clk/mediatek/clk-mt8192.c
@@ -244,6 +244,65 @@ static const char * const ccu_parents[] = {
 	"univpll_d6_d2"
 };
 
+static const char * const dsp_parents[] = {
+	"clk26m",
+	"univpll_d6_d2",
+	"univpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mmpll_d4",
+	"mainpll_d3",
+	"univpll_d3"
+};
+
+static const char * const dsp1_parents[] = {
+	"clk26m",
+	"npupll_ck",
+	"mainpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mainpll_d3",
+	"univpll_d3",
+	"apupll_ck"
+};
+
+static const char * const dsp1_npupll_parents[] = {
+	"dsp1_sel",
+	"npupll_ck"
+};
+
+static const char * const dsp2_parents[] = {
+	"clk26m",
+	"npupll_ck",
+	"mainpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mainpll_d3",
+	"univpll_d3",
+	"apupll_ck"
+};
+
+static const char * const dsp2_npupll_parents[] = {
+	"dsp2_sel",
+	"npupll_ck"
+};
+
+static const char * const dsp5_parents[] = {
+	"clk26m",
+	"apupll_ck",
+	"univpll_d4_d2",
+	"mainpll_d4",
+	"univpll_d4",
+	"mmpll_d4",
+	"mainpll_d3",
+	"univpll_d3"
+};
+
+static const char * const dsp5_apupll_parents[] = {
+	"dsp5_sel",
+	"apupll_ck"
+};
+
 static const char * const dsp7_parents[] = {
 	"clk26m",
 	"mainpll_d4_d2",
@@ -255,6 +314,17 @@ static const char * const dsp7_parents[] = {
 	"mmpll_d4"
 };
 
+static const char * const ipu_if_parents[] = {
+	"clk26m",
+	"univpll_d6_d2",
+	"mainpll_d4_d2",
+	"univpll_d4_d2",
+	"univpll_d5",
+	"mainpll_d4",
+	"tvdpll_ck",
+	"univpll_d4"
+};
+
 static const char * const mfg_ref_parents[] = {
 	"clk26m",
 	"clk26m",
@@ -734,9 +804,26 @@ static const struct mtk_mux top_mtk_muxes[] = {
 			     cam_parents, 0x030, 0x034, 0x038, 16, 4, 23, 0x004, 10),
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_CCU_SEL, "ccu_sel",
 			     ccu_parents, 0x030, 0x034, 0x038, 24, 4, 31, 0x004, 11),
+	/* CLK_CFG_3 */
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP_SEL, "dsp_sel",
+			     dsp_parents, 0x040, 0x044, 0x048, 0, 3, 7, 0x004, 12),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP1_SEL, "dsp1_sel",
+			     dsp1_parents, 0x040, 0x044, 0x048, 8, 3, 15, 0x004, 13),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP1_NPUPLL_SEL, "dsp1_npupll_sel",
+			     dsp1_npupll_parents, 0x040, 0x044, 0x048, 11, 1, -1, -1),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP2_SEL, "dsp2_sel",
+			     dsp2_parents, 0x040, 0x044, 0x048, 16, 3, 23, 0x004, 14),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP2_NPUPLL_SEL, "dsp2_npupll_sel",
+			     dsp2_npupll_parents, 0x040, 0x044, 0x048, 19, 1, -1, -1),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP5_SEL, "dsp5_sel",
+			     dsp5_parents, 0x040, 0x044, 0x048, 24, 3, 31, 0x004, 15),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP5_APUPLL_SEL, "dsp5_apupll_sel",
+			     dsp5_apupll_parents, 0x040, 0x044, 0x048, 27, 1, -1, -1),
 	/* CLK_CFG_4 */
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP7_SEL, "dsp7_sel",
 			     dsp7_parents, 0x050, 0x054, 0x058, 0, 3, 7, 0x004, 16),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_IPU_IF_SEL, "ipu_if_sel",
+			     ipu_if_parents, 0x050, 0x054, 0x058, 8, 3, 15, 0x004, 17),
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_REF_SEL, "mfg_ref_sel",
 			     mfg_ref_parents, 0x050, 0x054, 0x058, 16, 2, 23, 0x004, 18),
 	MUX_CLR_SET_UPD(CLK_TOP_MFG_PLL_SEL, "mfg_pll_sel",
@@ -1175,6 +1262,10 @@ static const struct mtk_pll_data plls[] = {
 	      0, 0, 32, 0x031c, 24, 0x0040, 0x000c, 0, 0x0320, 0),
 	PLL_B(CLK_APMIXED_APLL2, "apll2", 0x032c, 0x033c, 0x00000000,
 	      0, 0, 32, 0x0330, 24, 0, 0, 0, 0x0334, 0),
+	PLL_B(CLK_APMIXED_APUPLL, "apupll", 0x03a0, 0x03ac, 0xff000001,
+	      HAVE_RST_BAR, BIT(23), 22, 0x03a4, 24, 0, 0, 0, 0x03a4, 0),
+	PLL_B(CLK_APMIXED_NPUPLL, "npupll", 0x03b4, 0x03c0, 0x00000001,
+	      0, 0, 22, 0x03b8, 24, 0, 0, 0, 0x03b8, 0),
 };
 
 static struct clk_onecell_data *top_clk_data;
-- 
2.18.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 2/7] clk: mediatek: mt8192: Add APU clocks support
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU clocks support on MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
Acked-by: Stephen Boyd <sboyd@kernel.org>

---
 drivers/clk/mediatek/clk-mt8192.c | 91 +++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/drivers/clk/mediatek/clk-mt8192.c b/drivers/clk/mediatek/clk-mt8192.c
index cbc7c6dbe0f4..7019caeb0358 100644
--- a/drivers/clk/mediatek/clk-mt8192.c
+++ b/drivers/clk/mediatek/clk-mt8192.c
@@ -244,6 +244,65 @@ static const char * const ccu_parents[] = {
 	"univpll_d6_d2"
 };
 
+static const char * const dsp_parents[] = {
+	"clk26m",
+	"univpll_d6_d2",
+	"univpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mmpll_d4",
+	"mainpll_d3",
+	"univpll_d3"
+};
+
+static const char * const dsp1_parents[] = {
+	"clk26m",
+	"npupll_ck",
+	"mainpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mainpll_d3",
+	"univpll_d3",
+	"apupll_ck"
+};
+
+static const char * const dsp1_npupll_parents[] = {
+	"dsp1_sel",
+	"npupll_ck"
+};
+
+static const char * const dsp2_parents[] = {
+	"clk26m",
+	"npupll_ck",
+	"mainpll_d4_d2",
+	"univpll_d5",
+	"univpll_d4",
+	"mainpll_d3",
+	"univpll_d3",
+	"apupll_ck"
+};
+
+static const char * const dsp2_npupll_parents[] = {
+	"dsp2_sel",
+	"npupll_ck"
+};
+
+static const char * const dsp5_parents[] = {
+	"clk26m",
+	"apupll_ck",
+	"univpll_d4_d2",
+	"mainpll_d4",
+	"univpll_d4",
+	"mmpll_d4",
+	"mainpll_d3",
+	"univpll_d3"
+};
+
+static const char * const dsp5_apupll_parents[] = {
+	"dsp5_sel",
+	"apupll_ck"
+};
+
 static const char * const dsp7_parents[] = {
 	"clk26m",
 	"mainpll_d4_d2",
@@ -255,6 +314,17 @@ static const char * const dsp7_parents[] = {
 	"mmpll_d4"
 };
 
+static const char * const ipu_if_parents[] = {
+	"clk26m",
+	"univpll_d6_d2",
+	"mainpll_d4_d2",
+	"univpll_d4_d2",
+	"univpll_d5",
+	"mainpll_d4",
+	"tvdpll_ck",
+	"univpll_d4"
+};
+
 static const char * const mfg_ref_parents[] = {
 	"clk26m",
 	"clk26m",
@@ -734,9 +804,26 @@ static const struct mtk_mux top_mtk_muxes[] = {
 			     cam_parents, 0x030, 0x034, 0x038, 16, 4, 23, 0x004, 10),
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_CCU_SEL, "ccu_sel",
 			     ccu_parents, 0x030, 0x034, 0x038, 24, 4, 31, 0x004, 11),
+	/* CLK_CFG_3 */
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP_SEL, "dsp_sel",
+			     dsp_parents, 0x040, 0x044, 0x048, 0, 3, 7, 0x004, 12),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP1_SEL, "dsp1_sel",
+			     dsp1_parents, 0x040, 0x044, 0x048, 8, 3, 15, 0x004, 13),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP1_NPUPLL_SEL, "dsp1_npupll_sel",
+			     dsp1_npupll_parents, 0x040, 0x044, 0x048, 11, 1, -1, -1),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP2_SEL, "dsp2_sel",
+			     dsp2_parents, 0x040, 0x044, 0x048, 16, 3, 23, 0x004, 14),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP2_NPUPLL_SEL, "dsp2_npupll_sel",
+			     dsp2_npupll_parents, 0x040, 0x044, 0x048, 19, 1, -1, -1),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP5_SEL, "dsp5_sel",
+			     dsp5_parents, 0x040, 0x044, 0x048, 24, 3, 31, 0x004, 15),
+	MUX_CLR_SET_UPD(CLK_TOP_DSP5_APUPLL_SEL, "dsp5_apupll_sel",
+			     dsp5_apupll_parents, 0x040, 0x044, 0x048, 27, 1, -1, -1),
 	/* CLK_CFG_4 */
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_DSP7_SEL, "dsp7_sel",
 			     dsp7_parents, 0x050, 0x054, 0x058, 0, 3, 7, 0x004, 16),
+	MUX_GATE_CLR_SET_UPD(CLK_TOP_IPU_IF_SEL, "ipu_if_sel",
+			     ipu_if_parents, 0x050, 0x054, 0x058, 8, 3, 15, 0x004, 17),
 	MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_REF_SEL, "mfg_ref_sel",
 			     mfg_ref_parents, 0x050, 0x054, 0x058, 16, 2, 23, 0x004, 18),
 	MUX_CLR_SET_UPD(CLK_TOP_MFG_PLL_SEL, "mfg_pll_sel",
@@ -1175,6 +1262,10 @@ static const struct mtk_pll_data plls[] = {
 	      0, 0, 32, 0x031c, 24, 0x0040, 0x000c, 0, 0x0320, 0),
 	PLL_B(CLK_APMIXED_APLL2, "apll2", 0x032c, 0x033c, 0x00000000,
 	      0, 0, 32, 0x0330, 24, 0, 0, 0, 0x0334, 0),
+	PLL_B(CLK_APMIXED_APUPLL, "apupll", 0x03a0, 0x03ac, 0xff000001,
+	      HAVE_RST_BAR, BIT(23), 22, 0x03a4, 24, 0, 0, 0, 0x03a4, 0),
+	PLL_B(CLK_APMIXED_NPUPLL, "npupll", 0x03b4, 0x03c0, 0x00000001,
+	      0, 0, 22, 0x03b8, 24, 0, 0, 0, 0x03b8, 0),
 };
 
 static struct clk_onecell_data *top_clk_data;
-- 
2.18.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 3/7] dt-bindings: arm: mediatek: Add new document bindings for APU
  2021-10-23 10:13 ` Flora Fu
  (?)
@ 2021-10-23 10:13   ` Flora Fu
  -1 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Document the apusys bindings.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 .../arm/mediatek/mediatek,apusys.yaml         | 49 +++++++++++++++++++
 1 file changed, 49 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml

diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
new file mode 100644
index 000000000000..0f08a6c79f53
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/mediatek/mediatek,apusys.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek APUSYS Controller
+
+maintainers:
+  - Flora Fu <flora.fu@mediatek.com>
+
+description:
+  The Mediatek apusys controller provides functional configurations and clocks
+  to the system.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - mediatek,mt8192-apu-conn
+          - mediatek,mt8192-apu-vcore
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  '#clock-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    apu_conn: apu_conn@19020000 {
+        compatible = "mediatek,mt8192-apu-conn", "syscon";
+        reg = <0x19020000 0x1000>;
+        #clock-cells = <1>;
+    };
+
+  - |
+    apu_vcore: apu_vcore@19029000 {
+        compatible = "mediatek,mt8192-apu-vcore", "syscon";
+        reg = <0x19029000 0x1000>;
+        #clock-cells = <1>;
+    };
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 3/7] dt-bindings: arm: mediatek: Add new document bindings for APU
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Document the apusys bindings.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 .../arm/mediatek/mediatek,apusys.yaml         | 49 +++++++++++++++++++
 1 file changed, 49 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml

diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
new file mode 100644
index 000000000000..0f08a6c79f53
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/mediatek/mediatek,apusys.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek APUSYS Controller
+
+maintainers:
+  - Flora Fu <flora.fu@mediatek.com>
+
+description:
+  The Mediatek apusys controller provides functional configurations and clocks
+  to the system.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - mediatek,mt8192-apu-conn
+          - mediatek,mt8192-apu-vcore
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  '#clock-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    apu_conn: apu_conn@19020000 {
+        compatible = "mediatek,mt8192-apu-conn", "syscon";
+        reg = <0x19020000 0x1000>;
+        #clock-cells = <1>;
+    };
+
+  - |
+    apu_vcore: apu_vcore@19029000 {
+        compatible = "mediatek,mt8192-apu-vcore", "syscon";
+        reg = <0x19029000 0x1000>;
+        #clock-cells = <1>;
+    };
-- 
2.18.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 3/7] dt-bindings: arm: mediatek: Add new document bindings for APU
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Document the apusys bindings.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 .../arm/mediatek/mediatek,apusys.yaml         | 49 +++++++++++++++++++
 1 file changed, 49 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml

diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
new file mode 100644
index 000000000000..0f08a6c79f53
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/mediatek/mediatek,apusys.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek APUSYS Controller
+
+maintainers:
+  - Flora Fu <flora.fu@mediatek.com>
+
+description:
+  The Mediatek apusys controller provides functional configurations and clocks
+  to the system.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - mediatek,mt8192-apu-conn
+          - mediatek,mt8192-apu-vcore
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  '#clock-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    apu_conn: apu_conn@19020000 {
+        compatible = "mediatek,mt8192-apu-conn", "syscon";
+        reg = <0x19020000 0x1000>;
+        #clock-cells = <1>;
+    };
+
+  - |
+    apu_vcore: apu_vcore@19029000 {
+        compatible = "mediatek,mt8192-apu-vcore", "syscon";
+        reg = <0x19029000 0x1000>;
+        #clock-cells = <1>;
+    };
-- 
2.18.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 4/7] dt-bindings: soc: mediatek: apusys: Add new document for APU power domain
  2021-10-23 10:13 ` Flora Fu
  (?)
@ 2021-10-23 10:13   ` Flora Fu
  -1 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Document the bindings for APU power domain on MediaTek SoC.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++++++++++++++++
 1 file changed, 144 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml

diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
new file mode 100644
index 000000000000..5530a8be88ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# # Copyright 2021 MediaTek Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/mediatek/mediatek,apu-pm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek APU Power Domains
+
+maintainers:
+  - Flora Fu <flora.fu@mediatek.com>
+
+description: |
+  Mediatek AI Process Unit (APU) include support for power domains which can be
+  powered up/down by software.
+  APU subsys belonging to a power domain should contain a 'power-domains'
+  property that is a phandle for apuspm node representing the domain.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - mediatek,mt8192-apu-pm
+      - const: syscon
+
+  reg:
+    description: Address range of the APU power domain controller.
+    maxItems: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  '#power-domain-cells':
+    const: 1
+
+  vsram-supply:
+    description: apu sram regulator supply.
+
+  mediatek,scpsys:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys register range.
+
+  mediatek,apu-conn:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu conn register range.
+
+  mediatek,apu-conn1:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu conn1 register range.
+
+  mediatek,apu-vcore:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu vcore register range.
+
+patternProperties:
+  "^power-domain@[0-9a-f]+$":
+    type: object
+    description: |
+      Represents the power domains within the power controller node as
+      documented in Documentation/devicetree/bindings/power/power-domain.yaml.
+
+    properties:
+      reg:
+        description: Power domain index.
+        maxItems: 1
+
+      '#power-domain-cells':
+        description: |
+          Must be 0 for nodes representing a single PM domain and 1 for nodes
+          providing multiple PM.
+
+      clocks:
+        description: |
+          List of phandles of clocks list. Specify by order according to
+          power-up sequence.
+
+      clock-names:
+        description: |
+          List of names of clocks. Specify by order according to power-up
+          sequence.
+
+      assigned-clocks:
+        maxItems: 2
+
+      assigned-clock-parents:
+        maxItems: 2
+
+      domain-supply:
+        description: domain regulator supply.
+
+    required:
+      - reg
+      - '#power-domain-cells'
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - '#power-domain-cells'
+  - vsram-supply
+  - mediatek,scpsys
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/mt8192-clk.h>
+    apuspm: power-domain@190f0000 {
+        compatible = "mediatek,mt8192-apu-pm", "syscon";
+        reg = <0x190f0000 0x1000>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        #power-domain-cells = <1>;
+        vsram-supply = <&mt6359_vsram_md_ldo_reg>;
+        mediatek,scpsys = <&scpsys>;
+        mediatek,apu-conn = <&apu_conn>;
+        mediatek,apu-vcore = <&apu_vcore>;
+
+        apu_top: power-domain@0 {
+            reg = <0>;
+            #power-domain-cells = <0>;
+            clocks = <&topckgen CLK_TOP_DSP_SEL>,
+                     <&topckgen CLK_TOP_IPU_IF_SEL>,
+                     <&clk26m>,
+                     <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+            clock-names = "clk_top_conn",
+                          "clk_top_ipu_if",
+                          "clk_off",
+                          "clk_on_default";
+            assigned-clocks = <&topckgen CLK_TOP_DSP_SEL>,
+                              <&topckgen CLK_TOP_IPU_IF_SEL>;
+            assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_D6_D2>,
+                                     <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+            domain-supply = <&mt6359_vproc1_buck_reg>;
+        };
+    };
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 4/7] dt-bindings: soc: mediatek: apusys: Add new document for APU power domain
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Document the bindings for APU power domain on MediaTek SoC.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++++++++++++++++
 1 file changed, 144 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml

diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
new file mode 100644
index 000000000000..5530a8be88ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# # Copyright 2021 MediaTek Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/mediatek/mediatek,apu-pm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek APU Power Domains
+
+maintainers:
+  - Flora Fu <flora.fu@mediatek.com>
+
+description: |
+  Mediatek AI Process Unit (APU) include support for power domains which can be
+  powered up/down by software.
+  APU subsys belonging to a power domain should contain a 'power-domains'
+  property that is a phandle for apuspm node representing the domain.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - mediatek,mt8192-apu-pm
+      - const: syscon
+
+  reg:
+    description: Address range of the APU power domain controller.
+    maxItems: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  '#power-domain-cells':
+    const: 1
+
+  vsram-supply:
+    description: apu sram regulator supply.
+
+  mediatek,scpsys:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys register range.
+
+  mediatek,apu-conn:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu conn register range.
+
+  mediatek,apu-conn1:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu conn1 register range.
+
+  mediatek,apu-vcore:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu vcore register range.
+
+patternProperties:
+  "^power-domain@[0-9a-f]+$":
+    type: object
+    description: |
+      Represents the power domains within the power controller node as
+      documented in Documentation/devicetree/bindings/power/power-domain.yaml.
+
+    properties:
+      reg:
+        description: Power domain index.
+        maxItems: 1
+
+      '#power-domain-cells':
+        description: |
+          Must be 0 for nodes representing a single PM domain and 1 for nodes
+          providing multiple PM.
+
+      clocks:
+        description: |
+          List of phandles of clocks list. Specify by order according to
+          power-up sequence.
+
+      clock-names:
+        description: |
+          List of names of clocks. Specify by order according to power-up
+          sequence.
+
+      assigned-clocks:
+        maxItems: 2
+
+      assigned-clock-parents:
+        maxItems: 2
+
+      domain-supply:
+        description: domain regulator supply.
+
+    required:
+      - reg
+      - '#power-domain-cells'
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - '#power-domain-cells'
+  - vsram-supply
+  - mediatek,scpsys
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/mt8192-clk.h>
+    apuspm: power-domain@190f0000 {
+        compatible = "mediatek,mt8192-apu-pm", "syscon";
+        reg = <0x190f0000 0x1000>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        #power-domain-cells = <1>;
+        vsram-supply = <&mt6359_vsram_md_ldo_reg>;
+        mediatek,scpsys = <&scpsys>;
+        mediatek,apu-conn = <&apu_conn>;
+        mediatek,apu-vcore = <&apu_vcore>;
+
+        apu_top: power-domain@0 {
+            reg = <0>;
+            #power-domain-cells = <0>;
+            clocks = <&topckgen CLK_TOP_DSP_SEL>,
+                     <&topckgen CLK_TOP_IPU_IF_SEL>,
+                     <&clk26m>,
+                     <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+            clock-names = "clk_top_conn",
+                          "clk_top_ipu_if",
+                          "clk_off",
+                          "clk_on_default";
+            assigned-clocks = <&topckgen CLK_TOP_DSP_SEL>,
+                              <&topckgen CLK_TOP_IPU_IF_SEL>;
+            assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_D6_D2>,
+                                     <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+            domain-supply = <&mt6359_vproc1_buck_reg>;
+        };
+    };
-- 
2.18.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 4/7] dt-bindings: soc: mediatek: apusys: Add new document for APU power domain
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Document the bindings for APU power domain on MediaTek SoC.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++++++++++++++++
 1 file changed, 144 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml

diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
new file mode 100644
index 000000000000..5530a8be88ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# # Copyright 2021 MediaTek Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/mediatek/mediatek,apu-pm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek APU Power Domains
+
+maintainers:
+  - Flora Fu <flora.fu@mediatek.com>
+
+description: |
+  Mediatek AI Process Unit (APU) include support for power domains which can be
+  powered up/down by software.
+  APU subsys belonging to a power domain should contain a 'power-domains'
+  property that is a phandle for apuspm node representing the domain.
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - mediatek,mt8192-apu-pm
+      - const: syscon
+
+  reg:
+    description: Address range of the APU power domain controller.
+    maxItems: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  '#power-domain-cells':
+    const: 1
+
+  vsram-supply:
+    description: apu sram regulator supply.
+
+  mediatek,scpsys:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys register range.
+
+  mediatek,apu-conn:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu conn register range.
+
+  mediatek,apu-conn1:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu conn1 register range.
+
+  mediatek,apu-vcore:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      phandle to the device containing the scpsys apu vcore register range.
+
+patternProperties:
+  "^power-domain@[0-9a-f]+$":
+    type: object
+    description: |
+      Represents the power domains within the power controller node as
+      documented in Documentation/devicetree/bindings/power/power-domain.yaml.
+
+    properties:
+      reg:
+        description: Power domain index.
+        maxItems: 1
+
+      '#power-domain-cells':
+        description: |
+          Must be 0 for nodes representing a single PM domain and 1 for nodes
+          providing multiple PM.
+
+      clocks:
+        description: |
+          List of phandles of clocks list. Specify by order according to
+          power-up sequence.
+
+      clock-names:
+        description: |
+          List of names of clocks. Specify by order according to power-up
+          sequence.
+
+      assigned-clocks:
+        maxItems: 2
+
+      assigned-clock-parents:
+        maxItems: 2
+
+      domain-supply:
+        description: domain regulator supply.
+
+    required:
+      - reg
+      - '#power-domain-cells'
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - '#power-domain-cells'
+  - vsram-supply
+  - mediatek,scpsys
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/mt8192-clk.h>
+    apuspm: power-domain@190f0000 {
+        compatible = "mediatek,mt8192-apu-pm", "syscon";
+        reg = <0x190f0000 0x1000>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+        #power-domain-cells = <1>;
+        vsram-supply = <&mt6359_vsram_md_ldo_reg>;
+        mediatek,scpsys = <&scpsys>;
+        mediatek,apu-conn = <&apu_conn>;
+        mediatek,apu-vcore = <&apu_vcore>;
+
+        apu_top: power-domain@0 {
+            reg = <0>;
+            #power-domain-cells = <0>;
+            clocks = <&topckgen CLK_TOP_DSP_SEL>,
+                     <&topckgen CLK_TOP_IPU_IF_SEL>,
+                     <&clk26m>,
+                     <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+            clock-names = "clk_top_conn",
+                          "clk_top_ipu_if",
+                          "clk_off",
+                          "clk_on_default";
+            assigned-clocks = <&topckgen CLK_TOP_DSP_SEL>,
+                              <&topckgen CLK_TOP_IPU_IF_SEL>;
+            assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_D6_D2>,
+                                     <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+            domain-supply = <&mt6359_vproc1_buck_reg>;
+        };
+    };
-- 
2.18.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver
  2021-10-23 10:13 ` Flora Fu
  (?)
@ 2021-10-23 10:13   ` Flora Fu
  -1 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add the apusys in soc.
Add driver for apu power domains.
APU power domain shall be enabled before accessing the
internal sub modules.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 drivers/soc/mediatek/Kconfig             |  19 +
 drivers/soc/mediatek/Makefile            |   1 +
 drivers/soc/mediatek/apusys/Makefile     |   2 +
 drivers/soc/mediatek/apusys/mtk-apu-pm.c | 633 +++++++++++++++++++++++
 4 files changed, 655 insertions(+)
 create mode 100644 drivers/soc/mediatek/apusys/Makefile
 create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c

diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index fdd8bc08569e..d9bac2710494 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -5,6 +5,25 @@
 menu "MediaTek SoC drivers"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
 
+config MTK_APUSYS
+	bool "MediaTek APUSYS Support"
+	select REGMAP
+	help
+	  Say yes here to add support for the MediaTek AI Processing Unit
+	  Subsystem (APUSYS).
+	  The APUSYS is a proprietary hardware in SoC to support AI
+	  operations.
+
+config MTK_APU_PM
+	bool "MediaTek APU power management support"
+	select REGMAP
+	select PM_GENERIC_DOMAINS if PM
+	help
+	  Say yes here to add support for power management control
+	  to Mediatek AI Processing Unit Subsystem (APUSYS).
+	  APU power domain shall be enabled before accessing the
+	  internal sub modules.
+
 config MTK_CMDQ
 	tristate "MediaTek CMDQ Support"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
index 90270f8114ed..e46e7a3c21e7 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_MTK_APUSYS) += apusys/
 obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
 obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
 obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile
new file mode 100644
index 000000000000..8821c0f0b7b7
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
diff --git a/drivers/soc/mediatek/apusys/mtk-apu-pm.c b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
new file mode 100644
index 000000000000..828aa9eb6b37
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_clk.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define APU_PD_IPUIF_HW_CG		BIT(0)
+#define APU_PD_RPC_AUTO_BUCK		BIT(1)
+#define APU_PD_CAPS(_pd, _x)		((_pd)->data->caps & (_x))
+
+#define MTK_POLL_DELAY_US		10
+#define MTK_POLL_TIMEOUT		USEC_PER_SEC
+
+/* rpc_top_con*/
+#define SLEEP_REQ		BIT(0)
+#define APU_BUCK_ELS_EN		BIT(3)
+
+/*conn_clr, conn1_clr, vcore_clr */
+#define CG_CLR	(0xFFFFFFFF)
+
+/* mt8192 rpc_sw_type */
+#define MT8192_RPC_SW_TYPE0	(0x200)
+#define MT8192_RPC_SW_TYPE1	(0x210)
+#define MT8192_RPC_SW_TYPE2	(0x220)
+#define MT8192_RPC_SW_TYPE3	(0x230)
+#define MT8192_RPC_SW_TYPE4	(0x240)
+#define MT8192_RPC_SW_TYPE6	(0x260)
+#define MT8192_RPC_SW_TYPE7	(0x270)
+
+/* rpc_sw_type*/
+static const struct reg_sequence mt8192_rpc_sw_type[] = {
+	{ MT8192_RPC_SW_TYPE0, 0xFF },
+	{ MT8192_RPC_SW_TYPE2, 0x7 },
+	{ MT8192_RPC_SW_TYPE3, 0x7 },
+	{ MT8192_RPC_SW_TYPE6, 0x3 },
+};
+
+struct apu_top_domain {
+	u32 spm_ext_buck_iso;
+	u32 spm_ext_buck_iso_mask;
+	u32 spm_cross_wake_m01;
+	u32 wake_apu;
+	u32 spm_other_pwr;
+	u32 pwr_status;
+	u32 conn_clr;
+	u32 conn1_clr;
+	u32 vcore_clr;
+	u32 rpc_top_con;
+	u32 rpc_top_con_init_mask;
+	u32 rpc_top_sel;
+	u32 rpc_top_intf_pwr_rdy;
+	u32 pwr_rdy;
+	const struct reg_sequence *rpc_sw_type;
+	int rpc_sw_ntype;
+};
+
+static struct apu_top_domain mt8192_top_reg = {
+	.spm_ext_buck_iso = 0x39C,
+	.spm_ext_buck_iso_mask = 0x21,
+	.spm_cross_wake_m01 = 0x670,
+	.wake_apu = BIT(0),
+	.spm_other_pwr = 0x178,
+	.pwr_status = BIT(5),
+	.conn_clr = 0x008,
+	.vcore_clr = 0x008,
+	.rpc_top_con = 0x000,
+	.rpc_top_con_init_mask = 0x49E,
+	.rpc_top_sel = 0x004,
+	.rpc_top_intf_pwr_rdy = 0x044,
+	.pwr_rdy = BIT(0),
+	.rpc_sw_type = mt8192_rpc_sw_type,
+	.rpc_sw_ntype = ARRAY_SIZE(mt8192_rpc_sw_type),
+};
+
+struct apusys {
+	struct device *dev;
+	struct regmap *scpsys;
+	struct regmap *conn;
+	struct regmap *conn1;
+	struct regmap *vcore;
+	struct regmap *rpc;
+	struct regulator *vsram_supply;
+	const struct apu_pm_data *data;
+	struct genpd_onecell_data pd_data;
+	struct generic_pm_domain *domains[];
+};
+
+struct apu_domain {
+	struct generic_pm_domain genpd;
+	const struct apu_domain_data *data;
+	struct apusys *apusys;
+	struct regulator *domain_supply;
+	int num_clks;
+	struct clk_bulk_data *clks;
+	struct clk *clk_top_conn;
+	struct clk *clk_top_ipu_if;
+	struct clk *clk_off;
+	struct clk *clk_on_def;
+};
+
+struct apu_domain_data {
+	int domain_idx;
+	char *name;
+	struct apu_top_domain *topd;
+	u8 caps;
+};
+
+struct apu_pm_data {
+	const struct apu_domain_data *domains_data;
+	int num_domains;
+};
+
+#define to_apu_domain(gpd) container_of(gpd, struct apu_domain, genpd)
+
+static int apu_top_init_hw(struct apu_domain *pd)
+{
+	struct apusys *apusys = pd->apusys;
+	int ret;
+
+	/*
+	 * set memory type to PD or sleep group
+	 * sw_type register for each memory group, set to PD mode default
+	 */
+	if (pd->data->topd->rpc_sw_ntype) {
+		ret = regmap_register_patch(apusys->rpc,
+					    pd->data->topd->rpc_sw_type,
+					    pd->data->topd->rpc_sw_ntype);
+		if (ret < 0) {
+			dev_err(apusys->dev, "Failed to rpc patch: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/* mask RPC IRQ and bypass WFI */
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
+			pd->data->topd->rpc_top_con_init_mask);
+
+	if (APU_PD_CAPS(pd, APU_PD_RPC_AUTO_BUCK))
+		regmap_set_bits(apusys->rpc,
+				pd->data->topd->rpc_top_con, APU_BUCK_ELS_EN);
+
+	return 0;
+}
+
+static const struct apu_domain_data apu_domain_data_mt8192[] = {
+	{
+		.domain_idx = 0,
+		.name = "apu-top",
+		.caps = APU_PD_IPUIF_HW_CG,
+		.topd = &mt8192_top_reg,
+	}
+};
+
+static const struct apu_pm_data mt8192_apu_pm_data = {
+	.domains_data = apu_domain_data_mt8192,
+	.num_domains = ARRAY_SIZE(apu_domain_data_mt8192),
+};
+
+static int apu_top_power_on(struct generic_pm_domain *genpd)
+{
+	struct apu_domain *pd = to_apu_domain(genpd);
+	struct apusys *apusys = pd->apusys;
+	int ret, tmp;
+
+	if (apusys->vsram_supply) {
+		ret = regulator_enable(apusys->vsram_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pd->domain_supply) {
+		ret = regulator_enable(pd->domain_supply);
+		if (ret < 0)
+			goto err_regulator;
+	}
+
+	regmap_clear_bits(apusys->scpsys, pd->data->topd->spm_ext_buck_iso,
+			  pd->data->topd->spm_ext_buck_iso_mask);
+
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
+			pd->data->topd->rpc_top_con_init_mask);
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		ret = clk_prepare_enable(pd->clk_top_conn);
+		if (ret) {
+			dev_err(apusys->dev, "Failed enable top_conn clk\n");
+			goto err_clk;
+		}
+
+		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_on_def);
+		if (ret) {
+			dev_err(apusys->dev, "Failed set ipu_if mux\n");
+			goto err_clk;
+		}
+
+		/* The ipu_if clock is gatting by HW. Only enable once. */
+		if (!__clk_is_enabled(pd->clk_top_ipu_if)) {
+			ret = clk_prepare_enable(pd->clk_top_ipu_if);
+			if (ret) {
+				dev_err(apusys->dev, "Failed enable ipu_if\n");
+				goto err_clk;
+			}
+		}
+	} else {
+		ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
+		if (ret)
+			goto err_clk;
+	}
+
+	regmap_set_bits(apusys->scpsys,
+			pd->data->topd->spm_cross_wake_m01,
+			pd->data->topd->wake_apu);
+
+	ret = regmap_read_poll_timeout(apusys->scpsys,
+				       pd->data->topd->spm_other_pwr,
+				       tmp,
+				       (tmp & pd->data->topd->pwr_status) ==
+				       pd->data->topd->pwr_status,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top on wait SPM PWR_ACK != 0\n");
+		goto err_clk;
+	}
+
+	ret = regmap_read_poll_timeout(apusys->rpc,
+				       pd->data->topd->rpc_top_intf_pwr_rdy,
+				       tmp, (tmp & pd->data->topd->pwr_rdy) ==
+				       pd->data->topd->pwr_rdy,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top on wait RPC PWR_RDY != 0\n");
+		goto err_clk;
+	}
+
+	if (apusys->vcore)
+		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
+
+	if (apusys->conn)
+		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
+
+	if (apusys->conn1)
+		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
+
+	return 0;
+
+err_clk:
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_disable_unprepare(pd->clk_top_conn);
+		clk_disable_unprepare(pd->clk_top_ipu_if);
+	} else {
+		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
+	}
+	if (pd->domain_supply)
+		ret = regulator_disable(pd->domain_supply);
+err_regulator:
+	if (apusys->vsram_supply)
+		ret = regulator_disable(apusys->vsram_supply);
+
+	return ret;
+}
+
+static int apu_top_power_off(struct generic_pm_domain *genpd)
+{
+	struct apu_domain *pd = to_apu_domain(genpd);
+	struct apusys *apusys = pd->apusys;
+	int ret, tmp;
+
+	if (apusys->vcore)
+		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
+
+	if (apusys->conn)
+		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
+
+	if (apusys->conn1)
+		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
+
+	regmap_clear_bits(apusys->scpsys,
+			  pd->data->topd->spm_cross_wake_m01,
+			  pd->data->topd->wake_apu);
+
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_con, SLEEP_REQ);
+
+	ret = regmap_read_poll_timeout(apusys->rpc,
+				       pd->data->topd->rpc_top_intf_pwr_rdy,
+				       tmp,
+				       (tmp & pd->data->topd->pwr_rdy) == 0x0,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top off wait RPC PWR_RDY != 0\n");
+		return ret;
+	}
+
+	ret = regmap_read_poll_timeout(apusys->scpsys,
+				       pd->data->topd->spm_other_pwr, tmp,
+				       (tmp & pd->data->topd->pwr_status) == 0x0,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top off wait SPM PWR_ACK != 0\n");
+		return ret;
+	}
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_disable_unprepare(pd->clk_top_conn);
+		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_off);
+		if (ret) {
+			dev_err(apusys->dev, "Failed set ipu_if rate\n");
+			return ret;
+		}
+	} else {
+		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
+	}
+
+	regmap_set_bits(apusys->scpsys,
+			pd->data->topd->spm_ext_buck_iso,
+			pd->data->topd->spm_ext_buck_iso_mask);
+
+	if (apusys->vsram_supply) {
+		ret = regulator_disable(apusys->vsram_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pd->domain_supply) {
+		ret = regulator_disable(pd->domain_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct generic_pm_domain *apu_add_one_domain(struct apusys *apusys,
+						    struct device_node *node)
+{
+	const struct apu_domain_data *domain_data;
+	struct apu_domain *pd;
+	int ret;
+	u32 id;
+	int i;
+	struct clk *clk;
+
+	ret = of_property_read_u32(node, "reg", &id);
+	if (ret) {
+		dev_dbg(apusys->dev, "%pOF: invalid reg: %d\n", node, ret);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (id >= apusys->data->num_domains) {
+		dev_dbg(apusys->dev, "%pOF: invalid id %d\n", node, id);
+		return ERR_PTR(-EINVAL);
+	}
+
+	domain_data = &apusys->data->domains_data[id];
+
+	pd = devm_kzalloc(apusys->dev, sizeof(*pd), GFP_KERNEL);
+	if (!pd)
+		return ERR_PTR(-ENOMEM);
+
+	pd->data   = domain_data;
+	pd->apusys = apusys;
+
+	pd->domain_supply = devm_regulator_get_optional(apusys->dev, "domain");
+	if (IS_ERR(pd->domain_supply))
+		pd->domain_supply = NULL;
+
+	/*
+	 * For HW using ipu_if, the clock is switched to 26M
+	 * when power down top domain and switch to default clock rate
+	 * before power on.
+	 */
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		pd->clk_top_conn = of_clk_get_by_name(node, "clk_top_conn");
+		if (IS_ERR(pd->clk_top_conn)) {
+			dev_err(apusys->dev, "Fail to get clk_top_conn clock\n");
+			ret = PTR_ERR(pd->clk_top_conn);
+			goto err_put_clocks;
+		}
+
+		pd->clk_top_ipu_if = of_clk_get_by_name(node, "clk_top_ipu_if");
+		if (IS_ERR(pd->clk_top_ipu_if)) {
+			dev_err(apusys->dev, "Fail to get clk_top_ipu_if clock\n");
+			ret = PTR_ERR(pd->clk_top_ipu_if);
+			goto err_put_clocks;
+		}
+
+		pd->clk_off = of_clk_get_by_name(node, "clk_off");
+		if (IS_ERR(pd->clk_off)) {
+			dev_err(apusys->dev, "Fail to get clk_off clock\n");
+			ret = PTR_ERR(pd->clk_off);
+			goto err_put_clocks;
+		}
+
+		pd->clk_on_def = of_clk_get_by_name(node, "clk_on_default");
+		if (IS_ERR(pd->clk_on_def)) {
+			dev_err(apusys->dev, "Fail to get clk_on_default clock\n");
+			ret = PTR_ERR(pd->clk_on_def);
+			goto err_put_clocks;
+		}
+	} else {
+		pd->num_clks = of_clk_get_parent_count(node);
+		if (pd->num_clks > 0) {
+			pd->clks = devm_kcalloc(apusys->dev, pd->num_clks,
+						sizeof(*pd->clks), GFP_KERNEL);
+			if (!pd->clks)
+				return ERR_PTR(-ENOMEM);
+		}
+
+		for (i = 0; i < pd->num_clks; i++) {
+			clk = of_clk_get(node, i);
+			if (IS_ERR(clk)) {
+				ret = PTR_ERR(clk);
+				dev_dbg(apusys->dev,
+					"%pOF: failed to get clk at index %d: %d\n",
+					node, i, ret);
+				goto err_put_clocks;
+			}
+			pd->clks[i].clk = clk;
+		}
+	}
+
+	if (apusys->domains[id]) {
+		ret = -EINVAL;
+		dev_err(apusys->dev, "domain id %d already exists\n", id);
+		goto err_put_clocks;
+	}
+
+	/* set rpc hw init status */
+	ret = apu_top_init_hw(pd);
+	if (ret < 0) {
+		dev_dbg(apusys->dev, "top init fail ret = %d\n", ret);
+		goto err_put_clocks;
+	}
+
+	if (!pd->data->name)
+		pd->genpd.name = node->name;
+	else
+		pd->genpd.name = pd->data->name;
+	pd->genpd.power_off = apu_top_power_off;
+	pd->genpd.power_on = apu_top_power_on;
+
+	/*
+	 * Initially turn on all domains to make the domains usable
+	 * with !CONFIG_PM and to get the hardware in sync with the
+	 * software.  The unused domains will be switched off during
+	 * late_init time.
+	 */
+	ret = pd->genpd.power_on(&pd->genpd);
+	if (ret < 0) {
+		dev_dbg(apusys->dev, "%pOF: power on domain fail: %d\n",
+			node, ret);
+		goto err_put_clocks;
+	}
+
+	pm_genpd_init(&pd->genpd, NULL, false);
+
+	apusys->domains[id] = &pd->genpd;
+
+	return apusys->pd_data.domains[id];
+
+err_put_clocks:
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_put(pd->clk_top_conn);
+		clk_put(pd->clk_top_ipu_if);
+		clk_put(pd->clk_off);
+		clk_put(pd->clk_on_def);
+	} else {
+		clk_bulk_put(pd->num_clks, pd->clks);
+	}
+	return ERR_PTR(ret);
+}
+
+static void apu_remove_one_domain(struct apu_domain *pd)
+{
+	int ret;
+
+	if (pd->genpd.power_off)
+		pd->genpd.power_off(&pd->genpd);
+
+	/*
+	 * We're in the error cleanup already, so we only complain,
+	 * but won't emit another error on top of the original one.
+	 */
+	ret = pm_genpd_remove(&pd->genpd);
+	if (ret < 0)
+		dev_dbg(pd->apusys->dev,
+			"Remove domain '%s' : %d - state may be inconsistent\n",
+			pd->genpd.name, ret);
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_put(pd->clk_top_conn);
+		clk_put(pd->clk_top_ipu_if);
+		clk_put(pd->clk_off);
+		clk_put(pd->clk_on_def);
+	} else {
+		clk_bulk_put(pd->num_clks, pd->clks);
+	}
+}
+
+static void apu_domain_cleanup(struct apusys *apusys)
+{
+	struct generic_pm_domain *genpd;
+	struct apu_domain *pd;
+	int i;
+
+	for (i = apusys->pd_data.num_domains - 1; i >= 0; i--) {
+		genpd = apusys->pd_data.domains[i];
+		if (genpd) {
+			pd = to_apu_domain(genpd);
+			apu_remove_one_domain(pd);
+		}
+	}
+}
+
+static const struct of_device_id apu_pm_of_match[] = {
+	{
+		.compatible = "mediatek,mt8192-apu-pm",
+		.data = &mt8192_apu_pm_data,
+	},
+	{ }
+};
+
+static int apu_pm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	const struct apu_pm_data *data;
+	struct device_node *node;
+	struct apusys *apusys;
+	int ret;
+
+	data = of_device_get_match_data(&pdev->dev);
+	if (!data) {
+		dev_dbg(dev, "no power domain data\n");
+		return -EINVAL;
+	}
+
+	apusys = devm_kzalloc(dev,
+			      struct_size(apusys, domains, data->num_domains),
+			      GFP_KERNEL);
+	if (!apusys)
+		return -ENOMEM;
+
+	apusys->dev = dev;
+	apusys->data = data;
+	apusys->pd_data.domains = apusys->domains;
+	apusys->pd_data.num_domains = data->num_domains;
+
+	apusys->vsram_supply = devm_regulator_get_optional(dev, "vsram");
+	if (IS_ERR(apusys->vsram_supply)) {
+		ret = PTR_ERR(apusys->vsram_supply);
+		if (ret != -EPROBE_DEFER) {
+			dev_err(dev, "vsram_supply fail, ret=%d", ret);
+			apusys->vsram_supply = NULL;
+		}
+		return ret;
+	}
+
+	/* rpc */
+	apusys->rpc = syscon_node_to_regmap(np);
+	if (IS_ERR(apusys->rpc)) {
+		dev_err(dev, "Unable to get rpc\n");
+		return PTR_ERR(apusys->rpc);
+	}
+
+	/* scpsys */
+	apusys->scpsys = syscon_regmap_lookup_by_phandle(np, "mediatek,scpsys");
+	if (IS_ERR(apusys->scpsys)) {
+		dev_err(dev, "Unable to get scpsys\n");
+		return PTR_ERR(apusys->scpsys);
+	}
+
+	/* apusys conn */
+	apusys->conn = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn");
+	if (IS_ERR(apusys->conn))
+		dev_info(dev, "No optional phandle apu-conn\n");
+
+	/* apusys conn1 */
+	apusys->conn1 = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn1");
+	if (IS_ERR(apusys->conn1))
+		dev_info(dev, "No optional phandle apu-conn1\n");
+
+	/* apusys vcore */
+	apusys->vcore = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-vcore");
+	if (IS_ERR(apusys->vcore))
+		dev_info(dev, "No optional phandle apu-vcore\n");
+
+	for_each_available_child_of_node(np, node) {
+		struct generic_pm_domain *domain;
+
+		domain = apu_add_one_domain(apusys, node);
+		if (IS_ERR(domain)) {
+			ret = PTR_ERR(domain);
+			of_node_put(node);
+			goto err_cleanup_domains;
+		}
+	}
+
+	ret = of_genpd_add_provider_onecell(np, &apusys->pd_data);
+	if (ret) {
+		dev_dbg(dev, "failed to add provider: %d\n", ret);
+		goto err_cleanup_domains;
+	}
+
+	return 0;
+
+err_cleanup_domains:
+	apu_domain_cleanup(apusys);
+	return ret;
+}
+
+static struct platform_driver apu_pm_driver = {
+	.probe = apu_pm_probe,
+	.driver = {
+		.name = "mtk-apu-pm",
+		.suppress_bind_attrs = true,
+		.of_match_table = apu_pm_of_match,
+	},
+};
+builtin_platform_driver(apu_pm_driver);
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add the apusys in soc.
Add driver for apu power domains.
APU power domain shall be enabled before accessing the
internal sub modules.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 drivers/soc/mediatek/Kconfig             |  19 +
 drivers/soc/mediatek/Makefile            |   1 +
 drivers/soc/mediatek/apusys/Makefile     |   2 +
 drivers/soc/mediatek/apusys/mtk-apu-pm.c | 633 +++++++++++++++++++++++
 4 files changed, 655 insertions(+)
 create mode 100644 drivers/soc/mediatek/apusys/Makefile
 create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c

diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index fdd8bc08569e..d9bac2710494 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -5,6 +5,25 @@
 menu "MediaTek SoC drivers"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
 
+config MTK_APUSYS
+	bool "MediaTek APUSYS Support"
+	select REGMAP
+	help
+	  Say yes here to add support for the MediaTek AI Processing Unit
+	  Subsystem (APUSYS).
+	  The APUSYS is a proprietary hardware in SoC to support AI
+	  operations.
+
+config MTK_APU_PM
+	bool "MediaTek APU power management support"
+	select REGMAP
+	select PM_GENERIC_DOMAINS if PM
+	help
+	  Say yes here to add support for power management control
+	  to Mediatek AI Processing Unit Subsystem (APUSYS).
+	  APU power domain shall be enabled before accessing the
+	  internal sub modules.
+
 config MTK_CMDQ
 	tristate "MediaTek CMDQ Support"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
index 90270f8114ed..e46e7a3c21e7 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_MTK_APUSYS) += apusys/
 obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
 obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
 obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile
new file mode 100644
index 000000000000..8821c0f0b7b7
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
diff --git a/drivers/soc/mediatek/apusys/mtk-apu-pm.c b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
new file mode 100644
index 000000000000..828aa9eb6b37
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_clk.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define APU_PD_IPUIF_HW_CG		BIT(0)
+#define APU_PD_RPC_AUTO_BUCK		BIT(1)
+#define APU_PD_CAPS(_pd, _x)		((_pd)->data->caps & (_x))
+
+#define MTK_POLL_DELAY_US		10
+#define MTK_POLL_TIMEOUT		USEC_PER_SEC
+
+/* rpc_top_con*/
+#define SLEEP_REQ		BIT(0)
+#define APU_BUCK_ELS_EN		BIT(3)
+
+/*conn_clr, conn1_clr, vcore_clr */
+#define CG_CLR	(0xFFFFFFFF)
+
+/* mt8192 rpc_sw_type */
+#define MT8192_RPC_SW_TYPE0	(0x200)
+#define MT8192_RPC_SW_TYPE1	(0x210)
+#define MT8192_RPC_SW_TYPE2	(0x220)
+#define MT8192_RPC_SW_TYPE3	(0x230)
+#define MT8192_RPC_SW_TYPE4	(0x240)
+#define MT8192_RPC_SW_TYPE6	(0x260)
+#define MT8192_RPC_SW_TYPE7	(0x270)
+
+/* rpc_sw_type*/
+static const struct reg_sequence mt8192_rpc_sw_type[] = {
+	{ MT8192_RPC_SW_TYPE0, 0xFF },
+	{ MT8192_RPC_SW_TYPE2, 0x7 },
+	{ MT8192_RPC_SW_TYPE3, 0x7 },
+	{ MT8192_RPC_SW_TYPE6, 0x3 },
+};
+
+struct apu_top_domain {
+	u32 spm_ext_buck_iso;
+	u32 spm_ext_buck_iso_mask;
+	u32 spm_cross_wake_m01;
+	u32 wake_apu;
+	u32 spm_other_pwr;
+	u32 pwr_status;
+	u32 conn_clr;
+	u32 conn1_clr;
+	u32 vcore_clr;
+	u32 rpc_top_con;
+	u32 rpc_top_con_init_mask;
+	u32 rpc_top_sel;
+	u32 rpc_top_intf_pwr_rdy;
+	u32 pwr_rdy;
+	const struct reg_sequence *rpc_sw_type;
+	int rpc_sw_ntype;
+};
+
+static struct apu_top_domain mt8192_top_reg = {
+	.spm_ext_buck_iso = 0x39C,
+	.spm_ext_buck_iso_mask = 0x21,
+	.spm_cross_wake_m01 = 0x670,
+	.wake_apu = BIT(0),
+	.spm_other_pwr = 0x178,
+	.pwr_status = BIT(5),
+	.conn_clr = 0x008,
+	.vcore_clr = 0x008,
+	.rpc_top_con = 0x000,
+	.rpc_top_con_init_mask = 0x49E,
+	.rpc_top_sel = 0x004,
+	.rpc_top_intf_pwr_rdy = 0x044,
+	.pwr_rdy = BIT(0),
+	.rpc_sw_type = mt8192_rpc_sw_type,
+	.rpc_sw_ntype = ARRAY_SIZE(mt8192_rpc_sw_type),
+};
+
+struct apusys {
+	struct device *dev;
+	struct regmap *scpsys;
+	struct regmap *conn;
+	struct regmap *conn1;
+	struct regmap *vcore;
+	struct regmap *rpc;
+	struct regulator *vsram_supply;
+	const struct apu_pm_data *data;
+	struct genpd_onecell_data pd_data;
+	struct generic_pm_domain *domains[];
+};
+
+struct apu_domain {
+	struct generic_pm_domain genpd;
+	const struct apu_domain_data *data;
+	struct apusys *apusys;
+	struct regulator *domain_supply;
+	int num_clks;
+	struct clk_bulk_data *clks;
+	struct clk *clk_top_conn;
+	struct clk *clk_top_ipu_if;
+	struct clk *clk_off;
+	struct clk *clk_on_def;
+};
+
+struct apu_domain_data {
+	int domain_idx;
+	char *name;
+	struct apu_top_domain *topd;
+	u8 caps;
+};
+
+struct apu_pm_data {
+	const struct apu_domain_data *domains_data;
+	int num_domains;
+};
+
+#define to_apu_domain(gpd) container_of(gpd, struct apu_domain, genpd)
+
+static int apu_top_init_hw(struct apu_domain *pd)
+{
+	struct apusys *apusys = pd->apusys;
+	int ret;
+
+	/*
+	 * set memory type to PD or sleep group
+	 * sw_type register for each memory group, set to PD mode default
+	 */
+	if (pd->data->topd->rpc_sw_ntype) {
+		ret = regmap_register_patch(apusys->rpc,
+					    pd->data->topd->rpc_sw_type,
+					    pd->data->topd->rpc_sw_ntype);
+		if (ret < 0) {
+			dev_err(apusys->dev, "Failed to rpc patch: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/* mask RPC IRQ and bypass WFI */
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
+			pd->data->topd->rpc_top_con_init_mask);
+
+	if (APU_PD_CAPS(pd, APU_PD_RPC_AUTO_BUCK))
+		regmap_set_bits(apusys->rpc,
+				pd->data->topd->rpc_top_con, APU_BUCK_ELS_EN);
+
+	return 0;
+}
+
+static const struct apu_domain_data apu_domain_data_mt8192[] = {
+	{
+		.domain_idx = 0,
+		.name = "apu-top",
+		.caps = APU_PD_IPUIF_HW_CG,
+		.topd = &mt8192_top_reg,
+	}
+};
+
+static const struct apu_pm_data mt8192_apu_pm_data = {
+	.domains_data = apu_domain_data_mt8192,
+	.num_domains = ARRAY_SIZE(apu_domain_data_mt8192),
+};
+
+static int apu_top_power_on(struct generic_pm_domain *genpd)
+{
+	struct apu_domain *pd = to_apu_domain(genpd);
+	struct apusys *apusys = pd->apusys;
+	int ret, tmp;
+
+	if (apusys->vsram_supply) {
+		ret = regulator_enable(apusys->vsram_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pd->domain_supply) {
+		ret = regulator_enable(pd->domain_supply);
+		if (ret < 0)
+			goto err_regulator;
+	}
+
+	regmap_clear_bits(apusys->scpsys, pd->data->topd->spm_ext_buck_iso,
+			  pd->data->topd->spm_ext_buck_iso_mask);
+
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
+			pd->data->topd->rpc_top_con_init_mask);
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		ret = clk_prepare_enable(pd->clk_top_conn);
+		if (ret) {
+			dev_err(apusys->dev, "Failed enable top_conn clk\n");
+			goto err_clk;
+		}
+
+		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_on_def);
+		if (ret) {
+			dev_err(apusys->dev, "Failed set ipu_if mux\n");
+			goto err_clk;
+		}
+
+		/* The ipu_if clock is gatting by HW. Only enable once. */
+		if (!__clk_is_enabled(pd->clk_top_ipu_if)) {
+			ret = clk_prepare_enable(pd->clk_top_ipu_if);
+			if (ret) {
+				dev_err(apusys->dev, "Failed enable ipu_if\n");
+				goto err_clk;
+			}
+		}
+	} else {
+		ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
+		if (ret)
+			goto err_clk;
+	}
+
+	regmap_set_bits(apusys->scpsys,
+			pd->data->topd->spm_cross_wake_m01,
+			pd->data->topd->wake_apu);
+
+	ret = regmap_read_poll_timeout(apusys->scpsys,
+				       pd->data->topd->spm_other_pwr,
+				       tmp,
+				       (tmp & pd->data->topd->pwr_status) ==
+				       pd->data->topd->pwr_status,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top on wait SPM PWR_ACK != 0\n");
+		goto err_clk;
+	}
+
+	ret = regmap_read_poll_timeout(apusys->rpc,
+				       pd->data->topd->rpc_top_intf_pwr_rdy,
+				       tmp, (tmp & pd->data->topd->pwr_rdy) ==
+				       pd->data->topd->pwr_rdy,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top on wait RPC PWR_RDY != 0\n");
+		goto err_clk;
+	}
+
+	if (apusys->vcore)
+		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
+
+	if (apusys->conn)
+		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
+
+	if (apusys->conn1)
+		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
+
+	return 0;
+
+err_clk:
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_disable_unprepare(pd->clk_top_conn);
+		clk_disable_unprepare(pd->clk_top_ipu_if);
+	} else {
+		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
+	}
+	if (pd->domain_supply)
+		ret = regulator_disable(pd->domain_supply);
+err_regulator:
+	if (apusys->vsram_supply)
+		ret = regulator_disable(apusys->vsram_supply);
+
+	return ret;
+}
+
+static int apu_top_power_off(struct generic_pm_domain *genpd)
+{
+	struct apu_domain *pd = to_apu_domain(genpd);
+	struct apusys *apusys = pd->apusys;
+	int ret, tmp;
+
+	if (apusys->vcore)
+		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
+
+	if (apusys->conn)
+		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
+
+	if (apusys->conn1)
+		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
+
+	regmap_clear_bits(apusys->scpsys,
+			  pd->data->topd->spm_cross_wake_m01,
+			  pd->data->topd->wake_apu);
+
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_con, SLEEP_REQ);
+
+	ret = regmap_read_poll_timeout(apusys->rpc,
+				       pd->data->topd->rpc_top_intf_pwr_rdy,
+				       tmp,
+				       (tmp & pd->data->topd->pwr_rdy) == 0x0,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top off wait RPC PWR_RDY != 0\n");
+		return ret;
+	}
+
+	ret = regmap_read_poll_timeout(apusys->scpsys,
+				       pd->data->topd->spm_other_pwr, tmp,
+				       (tmp & pd->data->topd->pwr_status) == 0x0,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top off wait SPM PWR_ACK != 0\n");
+		return ret;
+	}
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_disable_unprepare(pd->clk_top_conn);
+		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_off);
+		if (ret) {
+			dev_err(apusys->dev, "Failed set ipu_if rate\n");
+			return ret;
+		}
+	} else {
+		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
+	}
+
+	regmap_set_bits(apusys->scpsys,
+			pd->data->topd->spm_ext_buck_iso,
+			pd->data->topd->spm_ext_buck_iso_mask);
+
+	if (apusys->vsram_supply) {
+		ret = regulator_disable(apusys->vsram_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pd->domain_supply) {
+		ret = regulator_disable(pd->domain_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct generic_pm_domain *apu_add_one_domain(struct apusys *apusys,
+						    struct device_node *node)
+{
+	const struct apu_domain_data *domain_data;
+	struct apu_domain *pd;
+	int ret;
+	u32 id;
+	int i;
+	struct clk *clk;
+
+	ret = of_property_read_u32(node, "reg", &id);
+	if (ret) {
+		dev_dbg(apusys->dev, "%pOF: invalid reg: %d\n", node, ret);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (id >= apusys->data->num_domains) {
+		dev_dbg(apusys->dev, "%pOF: invalid id %d\n", node, id);
+		return ERR_PTR(-EINVAL);
+	}
+
+	domain_data = &apusys->data->domains_data[id];
+
+	pd = devm_kzalloc(apusys->dev, sizeof(*pd), GFP_KERNEL);
+	if (!pd)
+		return ERR_PTR(-ENOMEM);
+
+	pd->data   = domain_data;
+	pd->apusys = apusys;
+
+	pd->domain_supply = devm_regulator_get_optional(apusys->dev, "domain");
+	if (IS_ERR(pd->domain_supply))
+		pd->domain_supply = NULL;
+
+	/*
+	 * For HW using ipu_if, the clock is switched to 26M
+	 * when power down top domain and switch to default clock rate
+	 * before power on.
+	 */
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		pd->clk_top_conn = of_clk_get_by_name(node, "clk_top_conn");
+		if (IS_ERR(pd->clk_top_conn)) {
+			dev_err(apusys->dev, "Fail to get clk_top_conn clock\n");
+			ret = PTR_ERR(pd->clk_top_conn);
+			goto err_put_clocks;
+		}
+
+		pd->clk_top_ipu_if = of_clk_get_by_name(node, "clk_top_ipu_if");
+		if (IS_ERR(pd->clk_top_ipu_if)) {
+			dev_err(apusys->dev, "Fail to get clk_top_ipu_if clock\n");
+			ret = PTR_ERR(pd->clk_top_ipu_if);
+			goto err_put_clocks;
+		}
+
+		pd->clk_off = of_clk_get_by_name(node, "clk_off");
+		if (IS_ERR(pd->clk_off)) {
+			dev_err(apusys->dev, "Fail to get clk_off clock\n");
+			ret = PTR_ERR(pd->clk_off);
+			goto err_put_clocks;
+		}
+
+		pd->clk_on_def = of_clk_get_by_name(node, "clk_on_default");
+		if (IS_ERR(pd->clk_on_def)) {
+			dev_err(apusys->dev, "Fail to get clk_on_default clock\n");
+			ret = PTR_ERR(pd->clk_on_def);
+			goto err_put_clocks;
+		}
+	} else {
+		pd->num_clks = of_clk_get_parent_count(node);
+		if (pd->num_clks > 0) {
+			pd->clks = devm_kcalloc(apusys->dev, pd->num_clks,
+						sizeof(*pd->clks), GFP_KERNEL);
+			if (!pd->clks)
+				return ERR_PTR(-ENOMEM);
+		}
+
+		for (i = 0; i < pd->num_clks; i++) {
+			clk = of_clk_get(node, i);
+			if (IS_ERR(clk)) {
+				ret = PTR_ERR(clk);
+				dev_dbg(apusys->dev,
+					"%pOF: failed to get clk at index %d: %d\n",
+					node, i, ret);
+				goto err_put_clocks;
+			}
+			pd->clks[i].clk = clk;
+		}
+	}
+
+	if (apusys->domains[id]) {
+		ret = -EINVAL;
+		dev_err(apusys->dev, "domain id %d already exists\n", id);
+		goto err_put_clocks;
+	}
+
+	/* set rpc hw init status */
+	ret = apu_top_init_hw(pd);
+	if (ret < 0) {
+		dev_dbg(apusys->dev, "top init fail ret = %d\n", ret);
+		goto err_put_clocks;
+	}
+
+	if (!pd->data->name)
+		pd->genpd.name = node->name;
+	else
+		pd->genpd.name = pd->data->name;
+	pd->genpd.power_off = apu_top_power_off;
+	pd->genpd.power_on = apu_top_power_on;
+
+	/*
+	 * Initially turn on all domains to make the domains usable
+	 * with !CONFIG_PM and to get the hardware in sync with the
+	 * software.  The unused domains will be switched off during
+	 * late_init time.
+	 */
+	ret = pd->genpd.power_on(&pd->genpd);
+	if (ret < 0) {
+		dev_dbg(apusys->dev, "%pOF: power on domain fail: %d\n",
+			node, ret);
+		goto err_put_clocks;
+	}
+
+	pm_genpd_init(&pd->genpd, NULL, false);
+
+	apusys->domains[id] = &pd->genpd;
+
+	return apusys->pd_data.domains[id];
+
+err_put_clocks:
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_put(pd->clk_top_conn);
+		clk_put(pd->clk_top_ipu_if);
+		clk_put(pd->clk_off);
+		clk_put(pd->clk_on_def);
+	} else {
+		clk_bulk_put(pd->num_clks, pd->clks);
+	}
+	return ERR_PTR(ret);
+}
+
+static void apu_remove_one_domain(struct apu_domain *pd)
+{
+	int ret;
+
+	if (pd->genpd.power_off)
+		pd->genpd.power_off(&pd->genpd);
+
+	/*
+	 * We're in the error cleanup already, so we only complain,
+	 * but won't emit another error on top of the original one.
+	 */
+	ret = pm_genpd_remove(&pd->genpd);
+	if (ret < 0)
+		dev_dbg(pd->apusys->dev,
+			"Remove domain '%s' : %d - state may be inconsistent\n",
+			pd->genpd.name, ret);
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_put(pd->clk_top_conn);
+		clk_put(pd->clk_top_ipu_if);
+		clk_put(pd->clk_off);
+		clk_put(pd->clk_on_def);
+	} else {
+		clk_bulk_put(pd->num_clks, pd->clks);
+	}
+}
+
+static void apu_domain_cleanup(struct apusys *apusys)
+{
+	struct generic_pm_domain *genpd;
+	struct apu_domain *pd;
+	int i;
+
+	for (i = apusys->pd_data.num_domains - 1; i >= 0; i--) {
+		genpd = apusys->pd_data.domains[i];
+		if (genpd) {
+			pd = to_apu_domain(genpd);
+			apu_remove_one_domain(pd);
+		}
+	}
+}
+
+static const struct of_device_id apu_pm_of_match[] = {
+	{
+		.compatible = "mediatek,mt8192-apu-pm",
+		.data = &mt8192_apu_pm_data,
+	},
+	{ }
+};
+
+static int apu_pm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	const struct apu_pm_data *data;
+	struct device_node *node;
+	struct apusys *apusys;
+	int ret;
+
+	data = of_device_get_match_data(&pdev->dev);
+	if (!data) {
+		dev_dbg(dev, "no power domain data\n");
+		return -EINVAL;
+	}
+
+	apusys = devm_kzalloc(dev,
+			      struct_size(apusys, domains, data->num_domains),
+			      GFP_KERNEL);
+	if (!apusys)
+		return -ENOMEM;
+
+	apusys->dev = dev;
+	apusys->data = data;
+	apusys->pd_data.domains = apusys->domains;
+	apusys->pd_data.num_domains = data->num_domains;
+
+	apusys->vsram_supply = devm_regulator_get_optional(dev, "vsram");
+	if (IS_ERR(apusys->vsram_supply)) {
+		ret = PTR_ERR(apusys->vsram_supply);
+		if (ret != -EPROBE_DEFER) {
+			dev_err(dev, "vsram_supply fail, ret=%d", ret);
+			apusys->vsram_supply = NULL;
+		}
+		return ret;
+	}
+
+	/* rpc */
+	apusys->rpc = syscon_node_to_regmap(np);
+	if (IS_ERR(apusys->rpc)) {
+		dev_err(dev, "Unable to get rpc\n");
+		return PTR_ERR(apusys->rpc);
+	}
+
+	/* scpsys */
+	apusys->scpsys = syscon_regmap_lookup_by_phandle(np, "mediatek,scpsys");
+	if (IS_ERR(apusys->scpsys)) {
+		dev_err(dev, "Unable to get scpsys\n");
+		return PTR_ERR(apusys->scpsys);
+	}
+
+	/* apusys conn */
+	apusys->conn = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn");
+	if (IS_ERR(apusys->conn))
+		dev_info(dev, "No optional phandle apu-conn\n");
+
+	/* apusys conn1 */
+	apusys->conn1 = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn1");
+	if (IS_ERR(apusys->conn1))
+		dev_info(dev, "No optional phandle apu-conn1\n");
+
+	/* apusys vcore */
+	apusys->vcore = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-vcore");
+	if (IS_ERR(apusys->vcore))
+		dev_info(dev, "No optional phandle apu-vcore\n");
+
+	for_each_available_child_of_node(np, node) {
+		struct generic_pm_domain *domain;
+
+		domain = apu_add_one_domain(apusys, node);
+		if (IS_ERR(domain)) {
+			ret = PTR_ERR(domain);
+			of_node_put(node);
+			goto err_cleanup_domains;
+		}
+	}
+
+	ret = of_genpd_add_provider_onecell(np, &apusys->pd_data);
+	if (ret) {
+		dev_dbg(dev, "failed to add provider: %d\n", ret);
+		goto err_cleanup_domains;
+	}
+
+	return 0;
+
+err_cleanup_domains:
+	apu_domain_cleanup(apusys);
+	return ret;
+}
+
+static struct platform_driver apu_pm_driver = {
+	.probe = apu_pm_probe,
+	.driver = {
+		.name = "mtk-apu-pm",
+		.suppress_bind_attrs = true,
+		.of_match_table = apu_pm_of_match,
+	},
+};
+builtin_platform_driver(apu_pm_driver);
-- 
2.18.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add the apusys in soc.
Add driver for apu power domains.
APU power domain shall be enabled before accessing the
internal sub modules.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 drivers/soc/mediatek/Kconfig             |  19 +
 drivers/soc/mediatek/Makefile            |   1 +
 drivers/soc/mediatek/apusys/Makefile     |   2 +
 drivers/soc/mediatek/apusys/mtk-apu-pm.c | 633 +++++++++++++++++++++++
 4 files changed, 655 insertions(+)
 create mode 100644 drivers/soc/mediatek/apusys/Makefile
 create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c

diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index fdd8bc08569e..d9bac2710494 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -5,6 +5,25 @@
 menu "MediaTek SoC drivers"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
 
+config MTK_APUSYS
+	bool "MediaTek APUSYS Support"
+	select REGMAP
+	help
+	  Say yes here to add support for the MediaTek AI Processing Unit
+	  Subsystem (APUSYS).
+	  The APUSYS is a proprietary hardware in SoC to support AI
+	  operations.
+
+config MTK_APU_PM
+	bool "MediaTek APU power management support"
+	select REGMAP
+	select PM_GENERIC_DOMAINS if PM
+	help
+	  Say yes here to add support for power management control
+	  to Mediatek AI Processing Unit Subsystem (APUSYS).
+	  APU power domain shall be enabled before accessing the
+	  internal sub modules.
+
 config MTK_CMDQ
 	tristate "MediaTek CMDQ Support"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
index 90270f8114ed..e46e7a3c21e7 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_MTK_APUSYS) += apusys/
 obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
 obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
 obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile
new file mode 100644
index 000000000000..8821c0f0b7b7
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
diff --git a/drivers/soc/mediatek/apusys/mtk-apu-pm.c b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
new file mode 100644
index 000000000000..828aa9eb6b37
--- /dev/null
+++ b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_clk.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define APU_PD_IPUIF_HW_CG		BIT(0)
+#define APU_PD_RPC_AUTO_BUCK		BIT(1)
+#define APU_PD_CAPS(_pd, _x)		((_pd)->data->caps & (_x))
+
+#define MTK_POLL_DELAY_US		10
+#define MTK_POLL_TIMEOUT		USEC_PER_SEC
+
+/* rpc_top_con*/
+#define SLEEP_REQ		BIT(0)
+#define APU_BUCK_ELS_EN		BIT(3)
+
+/*conn_clr, conn1_clr, vcore_clr */
+#define CG_CLR	(0xFFFFFFFF)
+
+/* mt8192 rpc_sw_type */
+#define MT8192_RPC_SW_TYPE0	(0x200)
+#define MT8192_RPC_SW_TYPE1	(0x210)
+#define MT8192_RPC_SW_TYPE2	(0x220)
+#define MT8192_RPC_SW_TYPE3	(0x230)
+#define MT8192_RPC_SW_TYPE4	(0x240)
+#define MT8192_RPC_SW_TYPE6	(0x260)
+#define MT8192_RPC_SW_TYPE7	(0x270)
+
+/* rpc_sw_type*/
+static const struct reg_sequence mt8192_rpc_sw_type[] = {
+	{ MT8192_RPC_SW_TYPE0, 0xFF },
+	{ MT8192_RPC_SW_TYPE2, 0x7 },
+	{ MT8192_RPC_SW_TYPE3, 0x7 },
+	{ MT8192_RPC_SW_TYPE6, 0x3 },
+};
+
+struct apu_top_domain {
+	u32 spm_ext_buck_iso;
+	u32 spm_ext_buck_iso_mask;
+	u32 spm_cross_wake_m01;
+	u32 wake_apu;
+	u32 spm_other_pwr;
+	u32 pwr_status;
+	u32 conn_clr;
+	u32 conn1_clr;
+	u32 vcore_clr;
+	u32 rpc_top_con;
+	u32 rpc_top_con_init_mask;
+	u32 rpc_top_sel;
+	u32 rpc_top_intf_pwr_rdy;
+	u32 pwr_rdy;
+	const struct reg_sequence *rpc_sw_type;
+	int rpc_sw_ntype;
+};
+
+static struct apu_top_domain mt8192_top_reg = {
+	.spm_ext_buck_iso = 0x39C,
+	.spm_ext_buck_iso_mask = 0x21,
+	.spm_cross_wake_m01 = 0x670,
+	.wake_apu = BIT(0),
+	.spm_other_pwr = 0x178,
+	.pwr_status = BIT(5),
+	.conn_clr = 0x008,
+	.vcore_clr = 0x008,
+	.rpc_top_con = 0x000,
+	.rpc_top_con_init_mask = 0x49E,
+	.rpc_top_sel = 0x004,
+	.rpc_top_intf_pwr_rdy = 0x044,
+	.pwr_rdy = BIT(0),
+	.rpc_sw_type = mt8192_rpc_sw_type,
+	.rpc_sw_ntype = ARRAY_SIZE(mt8192_rpc_sw_type),
+};
+
+struct apusys {
+	struct device *dev;
+	struct regmap *scpsys;
+	struct regmap *conn;
+	struct regmap *conn1;
+	struct regmap *vcore;
+	struct regmap *rpc;
+	struct regulator *vsram_supply;
+	const struct apu_pm_data *data;
+	struct genpd_onecell_data pd_data;
+	struct generic_pm_domain *domains[];
+};
+
+struct apu_domain {
+	struct generic_pm_domain genpd;
+	const struct apu_domain_data *data;
+	struct apusys *apusys;
+	struct regulator *domain_supply;
+	int num_clks;
+	struct clk_bulk_data *clks;
+	struct clk *clk_top_conn;
+	struct clk *clk_top_ipu_if;
+	struct clk *clk_off;
+	struct clk *clk_on_def;
+};
+
+struct apu_domain_data {
+	int domain_idx;
+	char *name;
+	struct apu_top_domain *topd;
+	u8 caps;
+};
+
+struct apu_pm_data {
+	const struct apu_domain_data *domains_data;
+	int num_domains;
+};
+
+#define to_apu_domain(gpd) container_of(gpd, struct apu_domain, genpd)
+
+static int apu_top_init_hw(struct apu_domain *pd)
+{
+	struct apusys *apusys = pd->apusys;
+	int ret;
+
+	/*
+	 * set memory type to PD or sleep group
+	 * sw_type register for each memory group, set to PD mode default
+	 */
+	if (pd->data->topd->rpc_sw_ntype) {
+		ret = regmap_register_patch(apusys->rpc,
+					    pd->data->topd->rpc_sw_type,
+					    pd->data->topd->rpc_sw_ntype);
+		if (ret < 0) {
+			dev_err(apusys->dev, "Failed to rpc patch: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/* mask RPC IRQ and bypass WFI */
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
+			pd->data->topd->rpc_top_con_init_mask);
+
+	if (APU_PD_CAPS(pd, APU_PD_RPC_AUTO_BUCK))
+		regmap_set_bits(apusys->rpc,
+				pd->data->topd->rpc_top_con, APU_BUCK_ELS_EN);
+
+	return 0;
+}
+
+static const struct apu_domain_data apu_domain_data_mt8192[] = {
+	{
+		.domain_idx = 0,
+		.name = "apu-top",
+		.caps = APU_PD_IPUIF_HW_CG,
+		.topd = &mt8192_top_reg,
+	}
+};
+
+static const struct apu_pm_data mt8192_apu_pm_data = {
+	.domains_data = apu_domain_data_mt8192,
+	.num_domains = ARRAY_SIZE(apu_domain_data_mt8192),
+};
+
+static int apu_top_power_on(struct generic_pm_domain *genpd)
+{
+	struct apu_domain *pd = to_apu_domain(genpd);
+	struct apusys *apusys = pd->apusys;
+	int ret, tmp;
+
+	if (apusys->vsram_supply) {
+		ret = regulator_enable(apusys->vsram_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pd->domain_supply) {
+		ret = regulator_enable(pd->domain_supply);
+		if (ret < 0)
+			goto err_regulator;
+	}
+
+	regmap_clear_bits(apusys->scpsys, pd->data->topd->spm_ext_buck_iso,
+			  pd->data->topd->spm_ext_buck_iso_mask);
+
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
+			pd->data->topd->rpc_top_con_init_mask);
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		ret = clk_prepare_enable(pd->clk_top_conn);
+		if (ret) {
+			dev_err(apusys->dev, "Failed enable top_conn clk\n");
+			goto err_clk;
+		}
+
+		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_on_def);
+		if (ret) {
+			dev_err(apusys->dev, "Failed set ipu_if mux\n");
+			goto err_clk;
+		}
+
+		/* The ipu_if clock is gatting by HW. Only enable once. */
+		if (!__clk_is_enabled(pd->clk_top_ipu_if)) {
+			ret = clk_prepare_enable(pd->clk_top_ipu_if);
+			if (ret) {
+				dev_err(apusys->dev, "Failed enable ipu_if\n");
+				goto err_clk;
+			}
+		}
+	} else {
+		ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
+		if (ret)
+			goto err_clk;
+	}
+
+	regmap_set_bits(apusys->scpsys,
+			pd->data->topd->spm_cross_wake_m01,
+			pd->data->topd->wake_apu);
+
+	ret = regmap_read_poll_timeout(apusys->scpsys,
+				       pd->data->topd->spm_other_pwr,
+				       tmp,
+				       (tmp & pd->data->topd->pwr_status) ==
+				       pd->data->topd->pwr_status,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top on wait SPM PWR_ACK != 0\n");
+		goto err_clk;
+	}
+
+	ret = regmap_read_poll_timeout(apusys->rpc,
+				       pd->data->topd->rpc_top_intf_pwr_rdy,
+				       tmp, (tmp & pd->data->topd->pwr_rdy) ==
+				       pd->data->topd->pwr_rdy,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top on wait RPC PWR_RDY != 0\n");
+		goto err_clk;
+	}
+
+	if (apusys->vcore)
+		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
+
+	if (apusys->conn)
+		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
+
+	if (apusys->conn1)
+		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
+
+	return 0;
+
+err_clk:
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_disable_unprepare(pd->clk_top_conn);
+		clk_disable_unprepare(pd->clk_top_ipu_if);
+	} else {
+		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
+	}
+	if (pd->domain_supply)
+		ret = regulator_disable(pd->domain_supply);
+err_regulator:
+	if (apusys->vsram_supply)
+		ret = regulator_disable(apusys->vsram_supply);
+
+	return ret;
+}
+
+static int apu_top_power_off(struct generic_pm_domain *genpd)
+{
+	struct apu_domain *pd = to_apu_domain(genpd);
+	struct apusys *apusys = pd->apusys;
+	int ret, tmp;
+
+	if (apusys->vcore)
+		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
+
+	if (apusys->conn)
+		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
+
+	if (apusys->conn1)
+		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
+
+	regmap_clear_bits(apusys->scpsys,
+			  pd->data->topd->spm_cross_wake_m01,
+			  pd->data->topd->wake_apu);
+
+	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_con, SLEEP_REQ);
+
+	ret = regmap_read_poll_timeout(apusys->rpc,
+				       pd->data->topd->rpc_top_intf_pwr_rdy,
+				       tmp,
+				       (tmp & pd->data->topd->pwr_rdy) == 0x0,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top off wait RPC PWR_RDY != 0\n");
+		return ret;
+	}
+
+	ret = regmap_read_poll_timeout(apusys->scpsys,
+				       pd->data->topd->spm_other_pwr, tmp,
+				       (tmp & pd->data->topd->pwr_status) == 0x0,
+				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	if (ret < 0) {
+		dev_err(apusys->dev, "apu top off wait SPM PWR_ACK != 0\n");
+		return ret;
+	}
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_disable_unprepare(pd->clk_top_conn);
+		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_off);
+		if (ret) {
+			dev_err(apusys->dev, "Failed set ipu_if rate\n");
+			return ret;
+		}
+	} else {
+		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
+	}
+
+	regmap_set_bits(apusys->scpsys,
+			pd->data->topd->spm_ext_buck_iso,
+			pd->data->topd->spm_ext_buck_iso_mask);
+
+	if (apusys->vsram_supply) {
+		ret = regulator_disable(apusys->vsram_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pd->domain_supply) {
+		ret = regulator_disable(pd->domain_supply);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct generic_pm_domain *apu_add_one_domain(struct apusys *apusys,
+						    struct device_node *node)
+{
+	const struct apu_domain_data *domain_data;
+	struct apu_domain *pd;
+	int ret;
+	u32 id;
+	int i;
+	struct clk *clk;
+
+	ret = of_property_read_u32(node, "reg", &id);
+	if (ret) {
+		dev_dbg(apusys->dev, "%pOF: invalid reg: %d\n", node, ret);
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (id >= apusys->data->num_domains) {
+		dev_dbg(apusys->dev, "%pOF: invalid id %d\n", node, id);
+		return ERR_PTR(-EINVAL);
+	}
+
+	domain_data = &apusys->data->domains_data[id];
+
+	pd = devm_kzalloc(apusys->dev, sizeof(*pd), GFP_KERNEL);
+	if (!pd)
+		return ERR_PTR(-ENOMEM);
+
+	pd->data   = domain_data;
+	pd->apusys = apusys;
+
+	pd->domain_supply = devm_regulator_get_optional(apusys->dev, "domain");
+	if (IS_ERR(pd->domain_supply))
+		pd->domain_supply = NULL;
+
+	/*
+	 * For HW using ipu_if, the clock is switched to 26M
+	 * when power down top domain and switch to default clock rate
+	 * before power on.
+	 */
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		pd->clk_top_conn = of_clk_get_by_name(node, "clk_top_conn");
+		if (IS_ERR(pd->clk_top_conn)) {
+			dev_err(apusys->dev, "Fail to get clk_top_conn clock\n");
+			ret = PTR_ERR(pd->clk_top_conn);
+			goto err_put_clocks;
+		}
+
+		pd->clk_top_ipu_if = of_clk_get_by_name(node, "clk_top_ipu_if");
+		if (IS_ERR(pd->clk_top_ipu_if)) {
+			dev_err(apusys->dev, "Fail to get clk_top_ipu_if clock\n");
+			ret = PTR_ERR(pd->clk_top_ipu_if);
+			goto err_put_clocks;
+		}
+
+		pd->clk_off = of_clk_get_by_name(node, "clk_off");
+		if (IS_ERR(pd->clk_off)) {
+			dev_err(apusys->dev, "Fail to get clk_off clock\n");
+			ret = PTR_ERR(pd->clk_off);
+			goto err_put_clocks;
+		}
+
+		pd->clk_on_def = of_clk_get_by_name(node, "clk_on_default");
+		if (IS_ERR(pd->clk_on_def)) {
+			dev_err(apusys->dev, "Fail to get clk_on_default clock\n");
+			ret = PTR_ERR(pd->clk_on_def);
+			goto err_put_clocks;
+		}
+	} else {
+		pd->num_clks = of_clk_get_parent_count(node);
+		if (pd->num_clks > 0) {
+			pd->clks = devm_kcalloc(apusys->dev, pd->num_clks,
+						sizeof(*pd->clks), GFP_KERNEL);
+			if (!pd->clks)
+				return ERR_PTR(-ENOMEM);
+		}
+
+		for (i = 0; i < pd->num_clks; i++) {
+			clk = of_clk_get(node, i);
+			if (IS_ERR(clk)) {
+				ret = PTR_ERR(clk);
+				dev_dbg(apusys->dev,
+					"%pOF: failed to get clk at index %d: %d\n",
+					node, i, ret);
+				goto err_put_clocks;
+			}
+			pd->clks[i].clk = clk;
+		}
+	}
+
+	if (apusys->domains[id]) {
+		ret = -EINVAL;
+		dev_err(apusys->dev, "domain id %d already exists\n", id);
+		goto err_put_clocks;
+	}
+
+	/* set rpc hw init status */
+	ret = apu_top_init_hw(pd);
+	if (ret < 0) {
+		dev_dbg(apusys->dev, "top init fail ret = %d\n", ret);
+		goto err_put_clocks;
+	}
+
+	if (!pd->data->name)
+		pd->genpd.name = node->name;
+	else
+		pd->genpd.name = pd->data->name;
+	pd->genpd.power_off = apu_top_power_off;
+	pd->genpd.power_on = apu_top_power_on;
+
+	/*
+	 * Initially turn on all domains to make the domains usable
+	 * with !CONFIG_PM and to get the hardware in sync with the
+	 * software.  The unused domains will be switched off during
+	 * late_init time.
+	 */
+	ret = pd->genpd.power_on(&pd->genpd);
+	if (ret < 0) {
+		dev_dbg(apusys->dev, "%pOF: power on domain fail: %d\n",
+			node, ret);
+		goto err_put_clocks;
+	}
+
+	pm_genpd_init(&pd->genpd, NULL, false);
+
+	apusys->domains[id] = &pd->genpd;
+
+	return apusys->pd_data.domains[id];
+
+err_put_clocks:
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_put(pd->clk_top_conn);
+		clk_put(pd->clk_top_ipu_if);
+		clk_put(pd->clk_off);
+		clk_put(pd->clk_on_def);
+	} else {
+		clk_bulk_put(pd->num_clks, pd->clks);
+	}
+	return ERR_PTR(ret);
+}
+
+static void apu_remove_one_domain(struct apu_domain *pd)
+{
+	int ret;
+
+	if (pd->genpd.power_off)
+		pd->genpd.power_off(&pd->genpd);
+
+	/*
+	 * We're in the error cleanup already, so we only complain,
+	 * but won't emit another error on top of the original one.
+	 */
+	ret = pm_genpd_remove(&pd->genpd);
+	if (ret < 0)
+		dev_dbg(pd->apusys->dev,
+			"Remove domain '%s' : %d - state may be inconsistent\n",
+			pd->genpd.name, ret);
+
+	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
+		clk_put(pd->clk_top_conn);
+		clk_put(pd->clk_top_ipu_if);
+		clk_put(pd->clk_off);
+		clk_put(pd->clk_on_def);
+	} else {
+		clk_bulk_put(pd->num_clks, pd->clks);
+	}
+}
+
+static void apu_domain_cleanup(struct apusys *apusys)
+{
+	struct generic_pm_domain *genpd;
+	struct apu_domain *pd;
+	int i;
+
+	for (i = apusys->pd_data.num_domains - 1; i >= 0; i--) {
+		genpd = apusys->pd_data.domains[i];
+		if (genpd) {
+			pd = to_apu_domain(genpd);
+			apu_remove_one_domain(pd);
+		}
+	}
+}
+
+static const struct of_device_id apu_pm_of_match[] = {
+	{
+		.compatible = "mediatek,mt8192-apu-pm",
+		.data = &mt8192_apu_pm_data,
+	},
+	{ }
+};
+
+static int apu_pm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	const struct apu_pm_data *data;
+	struct device_node *node;
+	struct apusys *apusys;
+	int ret;
+
+	data = of_device_get_match_data(&pdev->dev);
+	if (!data) {
+		dev_dbg(dev, "no power domain data\n");
+		return -EINVAL;
+	}
+
+	apusys = devm_kzalloc(dev,
+			      struct_size(apusys, domains, data->num_domains),
+			      GFP_KERNEL);
+	if (!apusys)
+		return -ENOMEM;
+
+	apusys->dev = dev;
+	apusys->data = data;
+	apusys->pd_data.domains = apusys->domains;
+	apusys->pd_data.num_domains = data->num_domains;
+
+	apusys->vsram_supply = devm_regulator_get_optional(dev, "vsram");
+	if (IS_ERR(apusys->vsram_supply)) {
+		ret = PTR_ERR(apusys->vsram_supply);
+		if (ret != -EPROBE_DEFER) {
+			dev_err(dev, "vsram_supply fail, ret=%d", ret);
+			apusys->vsram_supply = NULL;
+		}
+		return ret;
+	}
+
+	/* rpc */
+	apusys->rpc = syscon_node_to_regmap(np);
+	if (IS_ERR(apusys->rpc)) {
+		dev_err(dev, "Unable to get rpc\n");
+		return PTR_ERR(apusys->rpc);
+	}
+
+	/* scpsys */
+	apusys->scpsys = syscon_regmap_lookup_by_phandle(np, "mediatek,scpsys");
+	if (IS_ERR(apusys->scpsys)) {
+		dev_err(dev, "Unable to get scpsys\n");
+		return PTR_ERR(apusys->scpsys);
+	}
+
+	/* apusys conn */
+	apusys->conn = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn");
+	if (IS_ERR(apusys->conn))
+		dev_info(dev, "No optional phandle apu-conn\n");
+
+	/* apusys conn1 */
+	apusys->conn1 = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn1");
+	if (IS_ERR(apusys->conn1))
+		dev_info(dev, "No optional phandle apu-conn1\n");
+
+	/* apusys vcore */
+	apusys->vcore = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-vcore");
+	if (IS_ERR(apusys->vcore))
+		dev_info(dev, "No optional phandle apu-vcore\n");
+
+	for_each_available_child_of_node(np, node) {
+		struct generic_pm_domain *domain;
+
+		domain = apu_add_one_domain(apusys, node);
+		if (IS_ERR(domain)) {
+			ret = PTR_ERR(domain);
+			of_node_put(node);
+			goto err_cleanup_domains;
+		}
+	}
+
+	ret = of_genpd_add_provider_onecell(np, &apusys->pd_data);
+	if (ret) {
+		dev_dbg(dev, "failed to add provider: %d\n", ret);
+		goto err_cleanup_domains;
+	}
+
+	return 0;
+
+err_cleanup_domains:
+	apu_domain_cleanup(apusys);
+	return ret;
+}
+
+static struct platform_driver apu_pm_driver = {
+	.probe = apu_pm_probe,
+	.driver = {
+		.name = "mtk-apu-pm",
+		.suppress_bind_attrs = true,
+		.of_match_table = apu_pm_of_match,
+	},
+};
+builtin_platform_driver(apu_pm_driver);
-- 
2.18.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 6/7] arm64: dts: mt8192: Add APU node
  2021-10-23 10:13 ` Flora Fu
  (?)
@ 2021-10-23 10:13   ` Flora Fu
  -1 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU node to MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8192.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
index c2ba5280a7a2..72bbc3b4abf9 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -916,6 +916,18 @@
 			#clock-cells = <1>;
 		};
 
+		apu_conn: apu_conn@19020000 {
+			compatible = "mediatek,mt8192-apu-conn", "syscon";
+			reg = <0 0x19020000 0 0x1000>;
+			#clock-cells = <1>;
+		};
+
+		apu_vcore: apu_vcore@19029000 {
+			compatible = "mediatek,mt8192-apu-vcore", "syscon";
+			reg = <0 0x19029000 0 0x1000>;
+			#clock-cells = <1>;
+		};
+
 		camsys: clock-controller@1a000000 {
 			compatible = "mediatek,mt8192-camsys";
 			reg = <0 0x1a000000 0 0x1000>;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 6/7] arm64: dts: mt8192: Add APU node
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU node to MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8192.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
index c2ba5280a7a2..72bbc3b4abf9 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -916,6 +916,18 @@
 			#clock-cells = <1>;
 		};
 
+		apu_conn: apu_conn@19020000 {
+			compatible = "mediatek,mt8192-apu-conn", "syscon";
+			reg = <0 0x19020000 0 0x1000>;
+			#clock-cells = <1>;
+		};
+
+		apu_vcore: apu_vcore@19029000 {
+			compatible = "mediatek,mt8192-apu-vcore", "syscon";
+			reg = <0 0x19029000 0 0x1000>;
+			#clock-cells = <1>;
+		};
+
 		camsys: clock-controller@1a000000 {
 			compatible = "mediatek,mt8192-camsys";
 			reg = <0 0x1a000000 0 0x1000>;
-- 
2.18.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 6/7] arm64: dts: mt8192: Add APU node
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU node to MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8192.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
index c2ba5280a7a2..72bbc3b4abf9 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -916,6 +916,18 @@
 			#clock-cells = <1>;
 		};
 
+		apu_conn: apu_conn@19020000 {
+			compatible = "mediatek,mt8192-apu-conn", "syscon";
+			reg = <0 0x19020000 0 0x1000>;
+			#clock-cells = <1>;
+		};
+
+		apu_vcore: apu_vcore@19029000 {
+			compatible = "mediatek,mt8192-apu-vcore", "syscon";
+			reg = <0 0x19029000 0 0x1000>;
+			#clock-cells = <1>;
+		};
+
 		camsys: clock-controller@1a000000 {
 			compatible = "mediatek,mt8192-camsys";
 			reg = <0 0x1a000000 0 0x1000>;
-- 
2.18.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 7/7] arm64: dts: mt8192: Add APU power domain node
  2021-10-23 10:13 ` Flora Fu
  (?)
@ 2021-10-23 10:13   ` Flora Fu
  -1 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU power domain node to MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
Note:
This patch depends on mt8192/mt6359 dts patches which haven't yet been accepted.
This series is based on MT8192 clock[1][2] and MT8193/PMIC[3][4] patches.
[1] https://patchwork.kernel.org/project/linux-mediatek/list/?series=521655
[2] https://patchwork.kernel.org/patch/12134935
[3] https://patchwork.kernel.org/patch/12140237
---
 arch/arm64/boot/dts/mediatek/mt8192-evb.dts |  7 ++++++
 arch/arm64/boot/dts/mediatek/mt8192.dtsi    | 27 +++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192-evb.dts b/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
index 808be492e970..5d9e108e41f5 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
@@ -28,3 +28,10 @@
 &uart0 {
 	status = "okay";
 };
+
+&apuspm {
+	vsram-supply = <&mt6359_vsram_md_ldo_reg>;
+	apu_top: power-domain@0 {
+		domain-supply = <&mt6359_vproc1_buck_reg>;
+	};
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
index 72bbc3b4abf9..7014082637b0 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -928,6 +928,33 @@
 			#clock-cells = <1>;
 		};
 
+		apuspm: power-domain@190f0000 {
+			compatible = "mediatek,mt8192-apu-pm", "syscon";
+			reg = <0 0x190f0000 0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			#power-domain-cells = <1>;
+			mediatek,scpsys = <&scpsys>;
+			mediatek,apu-conn = <&apu_conn>;
+			mediatek,apu-vcore = <&apu_vcore>;
+			apu_top: power-domain@0 {
+				reg = <0>;
+				#power-domain-cells = <0>;
+				clocks = <&topckgen CLK_TOP_DSP_SEL>,
+					 <&topckgen CLK_TOP_IPU_IF_SEL>,
+					 <&clk26m>,
+					 <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+				clock-names = "clk_top_conn",
+					      "clk_top_ipu_if",
+					      "clk_off",
+					      "clk_on_default";
+				assigned-clocks = <&topckgen CLK_TOP_DSP_SEL>,
+						  <&topckgen CLK_TOP_IPU_IF_SEL>;
+				assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_D6_D2>,
+							 <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+			};
+		};
+
 		camsys: clock-controller@1a000000 {
 			compatible = "mediatek,mt8192-camsys";
 			reg = <0 0x1a000000 0 0x1000>;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 7/7] arm64: dts: mt8192: Add APU power domain node
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU power domain node to MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
Note:
This patch depends on mt8192/mt6359 dts patches which haven't yet been accepted.
This series is based on MT8192 clock[1][2] and MT8193/PMIC[3][4] patches.
[1] https://patchwork.kernel.org/project/linux-mediatek/list/?series=521655
[2] https://patchwork.kernel.org/patch/12134935
[3] https://patchwork.kernel.org/patch/12140237
---
 arch/arm64/boot/dts/mediatek/mt8192-evb.dts |  7 ++++++
 arch/arm64/boot/dts/mediatek/mt8192.dtsi    | 27 +++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192-evb.dts b/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
index 808be492e970..5d9e108e41f5 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
@@ -28,3 +28,10 @@
 &uart0 {
 	status = "okay";
 };
+
+&apuspm {
+	vsram-supply = <&mt6359_vsram_md_ldo_reg>;
+	apu_top: power-domain@0 {
+		domain-supply = <&mt6359_vproc1_buck_reg>;
+	};
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
index 72bbc3b4abf9..7014082637b0 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -928,6 +928,33 @@
 			#clock-cells = <1>;
 		};
 
+		apuspm: power-domain@190f0000 {
+			compatible = "mediatek,mt8192-apu-pm", "syscon";
+			reg = <0 0x190f0000 0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			#power-domain-cells = <1>;
+			mediatek,scpsys = <&scpsys>;
+			mediatek,apu-conn = <&apu_conn>;
+			mediatek,apu-vcore = <&apu_vcore>;
+			apu_top: power-domain@0 {
+				reg = <0>;
+				#power-domain-cells = <0>;
+				clocks = <&topckgen CLK_TOP_DSP_SEL>,
+					 <&topckgen CLK_TOP_IPU_IF_SEL>,
+					 <&clk26m>,
+					 <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+				clock-names = "clk_top_conn",
+					      "clk_top_ipu_if",
+					      "clk_off",
+					      "clk_on_default";
+				assigned-clocks = <&topckgen CLK_TOP_DSP_SEL>,
+						  <&topckgen CLK_TOP_IPU_IF_SEL>;
+				assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_D6_D2>,
+							 <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+			};
+		};
+
 		camsys: clock-controller@1a000000 {
 			compatible = "mediatek,mt8192-camsys";
 			reg = <0 0x1a000000 0 0x1000>;
-- 
2.18.0


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH v3 7/7] arm64: dts: mt8192: Add APU power domain node
@ 2021-10-23 10:13   ` Flora Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora Fu @ 2021-10-23 10:13 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Flora Fu, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Add APU power domain node to MT8192.

Signed-off-by: Flora Fu <flora.fu@mediatek.com>
---
Note:
This patch depends on mt8192/mt6359 dts patches which haven't yet been accepted.
This series is based on MT8192 clock[1][2] and MT8193/PMIC[3][4] patches.
[1] https://patchwork.kernel.org/project/linux-mediatek/list/?series=521655
[2] https://patchwork.kernel.org/patch/12134935
[3] https://patchwork.kernel.org/patch/12140237
---
 arch/arm64/boot/dts/mediatek/mt8192-evb.dts |  7 ++++++
 arch/arm64/boot/dts/mediatek/mt8192.dtsi    | 27 +++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8192-evb.dts b/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
index 808be492e970..5d9e108e41f5 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8192-evb.dts
@@ -28,3 +28,10 @@
 &uart0 {
 	status = "okay";
 };
+
+&apuspm {
+	vsram-supply = <&mt6359_vsram_md_ldo_reg>;
+	apu_top: power-domain@0 {
+		domain-supply = <&mt6359_vproc1_buck_reg>;
+	};
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt8192.dtsi b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
index 72bbc3b4abf9..7014082637b0 100644
--- a/arch/arm64/boot/dts/mediatek/mt8192.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8192.dtsi
@@ -928,6 +928,33 @@
 			#clock-cells = <1>;
 		};
 
+		apuspm: power-domain@190f0000 {
+			compatible = "mediatek,mt8192-apu-pm", "syscon";
+			reg = <0 0x190f0000 0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			#power-domain-cells = <1>;
+			mediatek,scpsys = <&scpsys>;
+			mediatek,apu-conn = <&apu_conn>;
+			mediatek,apu-vcore = <&apu_vcore>;
+			apu_top: power-domain@0 {
+				reg = <0>;
+				#power-domain-cells = <0>;
+				clocks = <&topckgen CLK_TOP_DSP_SEL>,
+					 <&topckgen CLK_TOP_IPU_IF_SEL>,
+					 <&clk26m>,
+					 <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+				clock-names = "clk_top_conn",
+					      "clk_top_ipu_if",
+					      "clk_off",
+					      "clk_on_default";
+				assigned-clocks = <&topckgen CLK_TOP_DSP_SEL>,
+						  <&topckgen CLK_TOP_IPU_IF_SEL>;
+				assigned-clock-parents = <&topckgen CLK_TOP_UNIVPLL_D6_D2>,
+							 <&topckgen CLK_TOP_UNIVPLL_D6_D2>;
+			};
+		};
+
 		camsys: clock-controller@1a000000 {
 			compatible = "mediatek,mt8192-camsys";
 			reg = <0 0x1a000000 0 0x1000>;
-- 
2.18.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver
  2021-10-23 10:13   ` Flora Fu
  (?)
@ 2021-10-26  9:57     ` AngeloGioacchino Del Regno
  -1 siblings, 0 replies; 35+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-10-26  9:57 UTC (permalink / raw)
  To: Flora Fu, Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Il 23/10/21 12:13, Flora Fu ha scritto:
> Add the apusys in soc.
> Add driver for apu power domains.
> APU power domain shall be enabled before accessing the
> internal sub modules.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>

Hello,
thanks for the patch! However, there are a few things to improve...

> ---
>   drivers/soc/mediatek/Kconfig             |  19 +
>   drivers/soc/mediatek/Makefile            |   1 +
>   drivers/soc/mediatek/apusys/Makefile     |   2 +
>   drivers/soc/mediatek/apusys/mtk-apu-pm.c | 633 +++++++++++++++++++++++
>   4 files changed, 655 insertions(+)
>   create mode 100644 drivers/soc/mediatek/apusys/Makefile
>   create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c
> 
> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> index fdd8bc08569e..d9bac2710494 100644
> --- a/drivers/soc/mediatek/Kconfig
> +++ b/drivers/soc/mediatek/Kconfig
> @@ -5,6 +5,25 @@
>   menu "MediaTek SoC drivers"
>   	depends on ARCH_MEDIATEK || COMPILE_TEST
>   
> +config MTK_APUSYS
> +	bool "MediaTek APUSYS Support"
> +	select REGMAP
> +	help
> +	  Say yes here to add support for the MediaTek AI Processing Unit
> +	  Subsystem (APUSYS).
> +	  The APUSYS is a proprietary hardware in SoC to support AI
> +	  operations.
> +

If these config entries are for files in subfolder apusys/, then you should move

them to drivers/soc/mediatek/apusys/Kconfig and make sure that said Kconfig will

contain a "if MTK_APUSYS" as to include the subconfigs only if the parent one is

declared =y.

> +config MTK_APU_PM
> +	bool "MediaTek APU power management support"

Can we use tristate here? This doesn't look like being a boot-critical driver,
so it can as well be loaded after rootfs init.
Permitting to compile this as a module will also shrink the kernel image size
and that's important when you build a generic kernel image, and that's for both
the MediaTek SoCs case (older SoCs don't need this driver) and for others.

> +	select REGMAP
> +	select PM_GENERIC_DOMAINS if PM
> +	help
> +	  Say yes here to add support for power management control
> +	  to Mediatek AI Processing Unit Subsystem (APUSYS).
> +	  APU power domain shall be enabled before accessing the
> +	  internal sub modules.
> +
>   config MTK_CMDQ
>   	tristate "MediaTek CMDQ Support"
>   	depends on ARCH_MEDIATEK || COMPILE_TEST
> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> index 90270f8114ed..e46e7a3c21e7 100644
> --- a/drivers/soc/mediatek/Makefile
> +++ b/drivers/soc/mediatek/Makefile
> @@ -1,4 +1,5 @@
>   # SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_MTK_APUSYS) += apusys/
>   obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
>   obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
>   obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
> diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile
> new file mode 100644
> index 000000000000..8821c0f0b7b7
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
> diff --git a/drivers/soc/mediatek/apusys/mtk-apu-pm.c b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> new file mode 100644
> index 000000000000..828aa9eb6b37
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> @@ -0,0 +1,633 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_clk.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define APU_PD_IPUIF_HW_CG		BIT(0)
> +#define APU_PD_RPC_AUTO_BUCK		BIT(1)
> +#define APU_PD_CAPS(_pd, _x)		((_pd)->data->caps & (_x))
> +
> +#define MTK_POLL_DELAY_US		10
> +#define MTK_POLL_TIMEOUT		USEC_PER_SEC
> +
> +/* rpc_top_con*/
> +#define SLEEP_REQ		BIT(0)
> +#define APU_BUCK_ELS_EN		BIT(3)
> +
> +/*conn_clr, conn1_clr, vcore_clr */
> +#define CG_CLR	(0xFFFFFFFF)
> +
> +/* mt8192 rpc_sw_type */
> +#define MT8192_RPC_SW_TYPE0	(0x200)
> +#define MT8192_RPC_SW_TYPE1	(0x210)
> +#define MT8192_RPC_SW_TYPE2	(0x220)
> +#define MT8192_RPC_SW_TYPE3	(0x230)
> +#define MT8192_RPC_SW_TYPE4	(0x240)
> +#define MT8192_RPC_SW_TYPE6	(0x260)
> +#define MT8192_RPC_SW_TYPE7	(0x270)

What about...

/* mt8192 rpc_sw_type (0..7) */
#define MT8192_RPC_SW_TYPE(x)	(0x200 + (x * 0x10))

> +
> +/* rpc_sw_type*/
> +static const struct reg_sequence mt8192_rpc_sw_type[] = {
> +	{ MT8192_RPC_SW_TYPE0, 0xFF },
> +	{ MT8192_RPC_SW_TYPE2, 0x7 },
> +	{ MT8192_RPC_SW_TYPE3, 0x7 },
> +	{ MT8192_RPC_SW_TYPE6, 0x3 },
> +};
> +
> +struct apu_top_domain {
> +	u32 spm_ext_buck_iso;
> +	u32 spm_ext_buck_iso_mask;
> +	u32 spm_cross_wake_m01;
> +	u32 wake_apu;
> +	u32 spm_other_pwr;
> +	u32 pwr_status;
> +	u32 conn_clr;
> +	u32 conn1_clr;
> +	u32 vcore_clr;
> +	u32 rpc_top_con;
> +	u32 rpc_top_con_init_mask;
> +	u32 rpc_top_sel;
> +	u32 rpc_top_intf_pwr_rdy;
> +	u32 pwr_rdy;
> +	const struct reg_sequence *rpc_sw_type;
> +	int rpc_sw_ntype;

For the sake of clarity and readability, I would name this one num_rpc_sw_type
or num_rpc_sw, which also keeps naming consistency across this file, since later
in this file, you're doing the same with domains_data.

> +};
> +
> +static struct apu_top_domain mt8192_top_reg = {
> +	.spm_ext_buck_iso = 0x39C,
> +	.spm_ext_buck_iso_mask = 0x21,
> +	.spm_cross_wake_m01 = 0x670,
> +	.wake_apu = BIT(0),
> +	.spm_other_pwr = 0x178,
> +	.pwr_status = BIT(5),
> +	.conn_clr = 0x008,

Please drop leading zeroes. 0x8 here.

> +	.vcore_clr = 0x008,

0x8

> +	.rpc_top_con = 0x000,

0

> +	.rpc_top_con_init_mask = 0x49E,
> +	.rpc_top_sel = 0x004,

0x4

> +	.rpc_top_intf_pwr_rdy = 0x044,

0x44

> +	.pwr_rdy = BIT(0),
> +	.rpc_sw_type = mt8192_rpc_sw_type,
> +	.rpc_sw_ntype = ARRAY_SIZE(mt8192_rpc_sw_type),
> +};
> +
> +struct apusys {
> +	struct device *dev;
> +	struct regmap *scpsys;
> +	struct regmap *conn;
> +	struct regmap *conn1;
> +	struct regmap *vcore;
> +	struct regmap *rpc;
> +	struct regulator *vsram_supply;
> +	const struct apu_pm_data *data;
> +	struct genpd_onecell_data pd_data;
> +	struct generic_pm_domain *domains[];
> +};
> +
> +struct apu_domain {
> +	struct generic_pm_domain genpd;
> +	const struct apu_domain_data *data;
> +	struct apusys *apusys;
> +	struct regulator *domain_supply;
> +	int num_clks;
> +	struct clk_bulk_data *clks;
> +	struct clk *clk_top_conn;
> +	struct clk *clk_top_ipu_if;
> +	struct clk *clk_off;
> +	struct clk *clk_on_def;
> +};
> +
> +struct apu_domain_data {
> +	int domain_idx;
> +	char *name;
> +	struct apu_top_domain *topd;
> +	u8 caps;
> +};
> +
> +struct apu_pm_data {
> +	const struct apu_domain_data *domains_data;
> +	int num_domains;
> +};
> +
> +#define to_apu_domain(gpd) container_of(gpd, struct apu_domain, genpd)
> +
> +static int apu_top_init_hw(struct apu_domain *pd)
> +{
> +	struct apusys *apusys = pd->apusys;
> +	int ret;
> +
> +	/*
> +	 * set memory type to PD or sleep group
> +	 * sw_type register for each memory group, set to PD mode default
> +	 */
> +	if (pd->data->topd->rpc_sw_ntype) {
> +		ret = regmap_register_patch(apusys->rpc,
> +					    pd->data->topd->rpc_sw_type,
> +					    pd->data->topd->rpc_sw_ntype);
> +		if (ret < 0) {
> +			dev_err(apusys->dev, "Failed to rpc patch: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	/* mask RPC IRQ and bypass WFI */
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> +			pd->data->topd->rpc_top_con_init_mask);

This function may return a failure.. and this is not a performance-critical case,
nor a case in which it's 100% granted that this won't fail: you're calling this
at initialization phase (when you're adding the domain), so if there's anything
that would make this fail, this is definitely the right place to check for that.

Please check the return value.

> +
> +	if (APU_PD_CAPS(pd, APU_PD_RPC_AUTO_BUCK))
> +		regmap_set_bits(apusys->rpc,
> +				pd->data->topd->rpc_top_con, APU_BUCK_ELS_EN);
> +
> +	return 0;
> +}
> +
> +static const struct apu_domain_data apu_domain_data_mt8192[] = {
> +	{
> +		.domain_idx = 0,
> +		.name = "apu-top",
> +		.caps = APU_PD_IPUIF_HW_CG,
> +		.topd = &mt8192_top_reg,
> +	}
> +};
> +
> +static const struct apu_pm_data mt8192_apu_pm_data = {
> +	.domains_data = apu_domain_data_mt8192,
> +	.num_domains = ARRAY_SIZE(apu_domain_data_mt8192),
> +};
> +
> +static int apu_top_power_on(struct generic_pm_domain *genpd)
> +{
> +	struct apu_domain *pd = to_apu_domain(genpd);
> +	struct apusys *apusys = pd->apusys;
> +	int ret, tmp;
> +
> +	if (apusys->vsram_supply) {
> +		ret = regulator_enable(apusys->vsram_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pd->domain_supply) {
> +		ret = regulator_enable(pd->domain_supply);
> +		if (ret < 0)
> +			goto err_regulator;
> +	}
> +
> +	regmap_clear_bits(apusys->scpsys, pd->data->topd->spm_ext_buck_iso,
> +			  pd->data->topd->spm_ext_buck_iso_mask);
> +
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> +			pd->data->topd->rpc_top_con_init_mask);
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		ret = clk_prepare_enable(pd->clk_top_conn);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed enable top_conn clk\n");
> +			goto err_clk;
> +		}
> +
> +		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_on_def);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed set ipu_if mux\n");
> +			goto err_clk;
> +		}
> +
> +		/* The ipu_if clock is gatting by HW. Only enable once. */

typo: gating

> +		if (!__clk_is_enabled(pd->clk_top_ipu_if)) {
> +			ret = clk_prepare_enable(pd->clk_top_ipu_if);
> +			if (ret) {
> +				dev_err(apusys->dev, "Failed enable ipu_if\n");
> +				goto err_clk;
> +			}
> +		}
> +	} else {
> +		ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
> +		if (ret)
> +			goto err_clk;
> +	}
> +
> +	regmap_set_bits(apusys->scpsys,
> +			pd->data->topd->spm_cross_wake_m01,
> +			pd->data->topd->wake_apu);
> +
> +	ret = regmap_read_poll_timeout(apusys->scpsys,
> +				       pd->data->topd->spm_other_pwr,
> +				       tmp,
> +				       (tmp & pd->data->topd->pwr_status) ==
> +				       pd->data->topd->pwr_status,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top on wait SPM PWR_ACK != 0\n");
> +		goto err_clk;
> +	}
> +
> +	ret = regmap_read_poll_timeout(apusys->rpc,
> +				       pd->data->topd->rpc_top_intf_pwr_rdy,
> +				       tmp, (tmp & pd->data->topd->pwr_rdy) ==
> +				       pd->data->topd->pwr_rdy,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top on wait RPC PWR_RDY != 0\n");
> +		goto err_clk;
> +	}
> +
> +	if (apusys->vcore)
> +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
> +
> +	if (apusys->conn)
> +		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
> +
> +	if (apusys->conn1)
> +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
> +
> +	return 0;
> +
> +err_clk:
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_disable_unprepare(pd->clk_top_conn);
> +		clk_disable_unprepare(pd->clk_top_ipu_if);
> +	} else {
> +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> +	}
> +	if (pd->domain_supply)
> +		ret = regulator_disable(pd->domain_supply);
> +err_regulator:
> +	if (apusys->vsram_supply)
> +		ret = regulator_disable(apusys->vsram_supply);
> +
> +	return ret;
> +}
> +
> +static int apu_top_power_off(struct generic_pm_domain *genpd)
> +{
> +	struct apu_domain *pd = to_apu_domain(genpd);
> +	struct apusys *apusys = pd->apusys;
> +	int ret, tmp;
> +
> +	if (apusys->vcore)
> +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
> +
> +	if (apusys->conn)
> +		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
> +
> +	if (apusys->conn1)
> +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
> +
> +	regmap_clear_bits(apusys->scpsys,
> +			  pd->data->topd->spm_cross_wake_m01,
> +			  pd->data->topd->wake_apu);
> +
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_con, SLEEP_REQ);
> +
> +	ret = regmap_read_poll_timeout(apusys->rpc,
> +				       pd->data->topd->rpc_top_intf_pwr_rdy,
> +				       tmp,
> +				       (tmp & pd->data->topd->pwr_rdy) == 0x0,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top off wait RPC PWR_RDY != 0\n");
> +		return ret;
> +	}
> +
> +	ret = regmap_read_poll_timeout(apusys->scpsys,
> +				       pd->data->topd->spm_other_pwr, tmp,
> +				       (tmp & pd->data->topd->pwr_status) == 0x0,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top off wait SPM PWR_ACK != 0\n");
> +		return ret;
> +	}
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_disable_unprepare(pd->clk_top_conn);
> +		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_off);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed set ipu_if rate\n");
> +			return ret;
> +		}
> +	} else {
> +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> +	}
> +
> +	regmap_set_bits(apusys->scpsys,
> +			pd->data->topd->spm_ext_buck_iso,
> +			pd->data->topd->spm_ext_buck_iso_mask);
> +
> +	if (apusys->vsram_supply) {
> +		ret = regulator_disable(apusys->vsram_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pd->domain_supply) {
> +		ret = regulator_disable(pd->domain_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct generic_pm_domain *apu_add_one_domain(struct apusys *apusys,
> +						    struct device_node *node)
> +{
> +	const struct apu_domain_data *domain_data;
> +	struct apu_domain *pd;
> +	int ret;
> +	u32 id;
> +	int i;
> +	struct clk *clk;
> +
> +	ret = of_property_read_u32(node, "reg", &id);
> +	if (ret) {
> +		dev_dbg(apusys->dev, "%pOF: invalid reg: %d\n", node, ret);
> +		return ERR_PTR(-EINVAL);

Just use the error value in 'ret', instead of forcing -EINVAL here.

> +	}
> +
> +	if (id >= apusys->data->num_domains) {
> +		dev_dbg(apusys->dev, "%pOF: invalid id %d\n", node, id);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	domain_data = &apusys->data->domains_data[id];
> +
> +	pd = devm_kzalloc(apusys->dev, sizeof(*pd), GFP_KERNEL);
> +	if (!pd)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pd->data   = domain_data;
> +	pd->apusys = apusys;
> +
> +	pd->domain_supply = devm_regulator_get_optional(apusys->dev, "domain");

This may return -EPROBE_DEFER: in that case, you don't want to ignore the supply,
but defer probing this driver until said supply becomes available so, if that
happens, you should return.

> +	if (IS_ERR(pd->domain_supply))
> +		pd->domain_supply = NULL;
> +
> +	/*
> +	 * For HW using ipu_if, the clock is switched to 26M
> +	 * when power down top domain and switch to default clock rate
> +	 * before power on.
> +	 */
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		pd->clk_top_conn = of_clk_get_by_name(node, "clk_top_conn");
> +		if (IS_ERR(pd->clk_top_conn)) {
> +			dev_err(apusys->dev, "Fail to get clk_top_conn clock\n");
> +			ret = PTR_ERR(pd->clk_top_conn);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_top_ipu_if = of_clk_get_by_name(node, "clk_top_ipu_if");
> +		if (IS_ERR(pd->clk_top_ipu_if)) {
> +			dev_err(apusys->dev, "Fail to get clk_top_ipu_if clock\n");
> +			ret = PTR_ERR(pd->clk_top_ipu_if);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_off = of_clk_get_by_name(node, "clk_off");
> +		if (IS_ERR(pd->clk_off)) {
> +			dev_err(apusys->dev, "Fail to get clk_off clock\n");
> +			ret = PTR_ERR(pd->clk_off);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_on_def = of_clk_get_by_name(node, "clk_on_default");
> +		if (IS_ERR(pd->clk_on_def)) {
> +			dev_err(apusys->dev, "Fail to get clk_on_default clock\n");
> +			ret = PTR_ERR(pd->clk_on_def);
> +			goto err_put_clocks;
> +		}
> +	} else {
> +		pd->num_clks = of_clk_get_parent_count(node);
> +		if (pd->num_clks > 0) {
> +			pd->clks = devm_kcalloc(apusys->dev, pd->num_clks,
> +						sizeof(*pd->clks), GFP_KERNEL);
> +			if (!pd->clks)
> +				return ERR_PTR(-ENOMEM);
> +		}
> +
> +		for (i = 0; i < pd->num_clks; i++) {
> +			clk = of_clk_get(node, i);
> +			if (IS_ERR(clk)) {
> +				ret = PTR_ERR(clk);
> +				dev_dbg(apusys->dev,
> +					"%pOF: failed to get clk at index %d: %d\n",
> +					node, i, ret);
> +				goto err_put_clocks;
> +			}
> +			pd->clks[i].clk = clk;
> +		}
> +	}
> +
> +	if (apusys->domains[id]) {
> +		ret = -EINVAL;

Error: X already exists -> -EEXIST

> +		dev_err(apusys->dev, "domain id %d already exists\n", id);
> +		goto err_put_clocks;
> +	}
> +
> +	/* set rpc hw init status */
> +	ret = apu_top_init_hw(pd);
> +	if (ret < 0) {
> +		dev_dbg(apusys->dev, "top init fail ret = %d\n", ret);
> +		goto err_put_clocks;
> +	}
> +
> +	if (!pd->data->name)
> +		pd->genpd.name = node->name;
> +	else
> +		pd->genpd.name = pd->data->name;
> +	pd->genpd.power_off = apu_top_power_off;
> +	pd->genpd.power_on = apu_top_power_on;
> +
> +	/*
> +	 * Initially turn on all domains to make the domains usable
> +	 * with !CONFIG_PM and to get the hardware in sync with the
> +	 * software.  The unused domains will be switched off during
> +	 * late_init time.
> +	 */
> +	ret = pd->genpd.power_on(&pd->genpd);
> +	if (ret < 0) {
> +		dev_dbg(apusys->dev, "%pOF: power on domain fail: %d\n",
> +			node, ret);
> +		goto err_put_clocks;
> +	}
> +
> +	pm_genpd_init(&pd->genpd, NULL, false);

This function may return failure: please check the return value.

> +
> +	apusys->domains[id] = &pd->genpd;
> +
> +	return apusys->pd_data.domains[id];
> +
> +err_put_clocks:
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_put(pd->clk_top_conn);
> +		clk_put(pd->clk_top_ipu_if);
> +		clk_put(pd->clk_off);
> +		clk_put(pd->clk_on_def);
> +	} else {
> +		clk_bulk_put(pd->num_clks, pd->clks);
> +	}
> +	return ERR_PTR(ret);
> +}
> +
> +static void apu_remove_one_domain(struct apu_domain *pd)
> +{
> +	int ret;
> +
> +	if (pd->genpd.power_off)
> +		pd->genpd.power_off(&pd->genpd);
> +
> +	/*
> +	 * We're in the error cleanup already, so we only complain,
> +	 * but won't emit another error on top of the original one.
> +	 */
> +	ret = pm_genpd_remove(&pd->genpd);
> +	if (ret < 0)
> +		dev_dbg(pd->apusys->dev,
> +			"Remove domain '%s' : %d - state may be inconsistent\n",
> +			pd->genpd.name, ret);
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_put(pd->clk_top_conn);
> +		clk_put(pd->clk_top_ipu_if);
> +		clk_put(pd->clk_off);
> +		clk_put(pd->clk_on_def);
> +	} else {
> +		clk_bulk_put(pd->num_clks, pd->clks);
> +	}
> +}
> +
> +static void apu_domain_cleanup(struct apusys *apusys)
> +{
> +	struct generic_pm_domain *genpd;
> +	struct apu_domain *pd;
> +	int i;
> +
> +	for (i = apusys->pd_data.num_domains - 1; i >= 0; i--) {
> +		genpd = apusys->pd_data.domains[i];
> +		if (genpd) {
> +			pd = to_apu_domain(genpd);
> +			apu_remove_one_domain(pd);
> +		}
> +	}
> +}
> +
> +static const struct of_device_id apu_pm_of_match[] = {
> +	{
> +		.compatible = "mediatek,mt8192-apu-pm",
> +		.data = &mt8192_apu_pm_data,
> +	},
> +	{ }
> +};
> +
> +static int apu_pm_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	const struct apu_pm_data *data;
> +	struct device_node *node;
> +	struct apusys *apusys;
> +	int ret;
> +
> +	data = of_device_get_match_data(&pdev->dev);
> +	if (!data) {
> +		dev_dbg(dev, "no power domain data\n");
> +		return -EINVAL;
> +	}
> +
> +	apusys = devm_kzalloc(dev,
> +			      struct_size(apusys, domains, data->num_domains),
> +			      GFP_KERNEL);
> +	if (!apusys)
> +		return -ENOMEM;
> +
> +	apusys->dev = dev;
> +	apusys->data = data;
> +	apusys->pd_data.domains = apusys->domains;
> +	apusys->pd_data.num_domains = data->num_domains;
> +
> +	apusys->vsram_supply = devm_regulator_get_optional(dev, "vsram");
> +	if (IS_ERR(apusys->vsram_supply)) {
> +		ret = PTR_ERR(apusys->vsram_supply);
> +		if (ret != -EPROBE_DEFER) {
> +			dev_err(dev, "vsram_supply fail, ret=%d", ret);
> +			apusys->vsram_supply = NULL;
> +		}
> +		return ret;
> +	}
> +
> +	/* rpc */
> +	apusys->rpc = syscon_node_to_regmap(np);
> +	if (IS_ERR(apusys->rpc)) {
> +		dev_err(dev, "Unable to get rpc\n");
> +		return PTR_ERR(apusys->rpc);
> +	}
> +
> +	/* scpsys */
> +	apusys->scpsys = syscon_regmap_lookup_by_phandle(np, "mediatek,scpsys");
> +	if (IS_ERR(apusys->scpsys)) {
> +		dev_err(dev, "Unable to get scpsys\n");
> +		return PTR_ERR(apusys->scpsys);
> +	}
> +
> +	/* apusys conn */
> +	apusys->conn = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn");
> +	if (IS_ERR(apusys->conn))
> +		dev_info(dev, "No optional phandle apu-conn\n");

I take it as if some platforms may be expected to not have any optional phandle
for that (and for the others): in such case, this would result in being a useless
message. Please use dev_dbg() here.

> +
> +	/* apusys conn1 */
> +	apusys->conn1 = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn1");
> +	if (IS_ERR(apusys->conn1))
> +		dev_info(dev, "No optional phandle apu-conn1\n");
> +
> +	/* apusys vcore */
> +	apusys->vcore = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-vcore");
> +	if (IS_ERR(apusys->vcore))
> +		dev_info(dev, "No optional phandle apu-vcore\n");
> +
> +	for_each_available_child_of_node(np, node) {
> +		struct generic_pm_domain *domain;
> +
> +		domain = apu_add_one_domain(apusys, node);
> +		if (IS_ERR(domain)) {
> +			ret = PTR_ERR(domain);
> +			of_node_put(node);
> +			goto err_cleanup_domains;
> +		}
> +	}
> +
> +	ret = of_genpd_add_provider_onecell(np, &apusys->pd_data);
> +	if (ret) {
> +		dev_dbg(dev, "failed to add provider: %d\n", ret);
> +		goto err_cleanup_domains;
> +	}
> +
> +	return 0;
> +
> +err_cleanup_domains:
> +	apu_domain_cleanup(apusys);
> +	return ret;
> +}
> +
> +static struct platform_driver apu_pm_driver = {
> +	.probe = apu_pm_probe,

In this driver, you already have the means to remove the domains and cleanup the
state... it would probably be trivial to add a .remove handler.

What do you think about that?
Also, is there be any reason for which removing this driver during runtime
would be unsafe?

In my opinion, it would be beneficial to have a remove handler also in the
development usecase, as you'd be able to just recompile this module, remove and
reinsert it to test new changes, without the need of copying an entire new
kernel image and rebooting the device.

> +	.driver = {
> +		.name = "mtk-apu-pm",
> +		.suppress_bind_attrs = true,
> +		.of_match_table = apu_pm_of_match,
> +	},
> +};
> +builtin_platform_driver(apu_pm_driver);
> 

MODULE_LICENSE() is missing! Please add it.
Also, as said in the Kconfig comment, this driver can probably be a module, in
which case... module_platform_driver(apu_pm_driver);

Thanks,
- Angelo


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver
@ 2021-10-26  9:57     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 35+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-10-26  9:57 UTC (permalink / raw)
  To: Flora Fu, Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Il 23/10/21 12:13, Flora Fu ha scritto:
> Add the apusys in soc.
> Add driver for apu power domains.
> APU power domain shall be enabled before accessing the
> internal sub modules.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>

Hello,
thanks for the patch! However, there are a few things to improve...

> ---
>   drivers/soc/mediatek/Kconfig             |  19 +
>   drivers/soc/mediatek/Makefile            |   1 +
>   drivers/soc/mediatek/apusys/Makefile     |   2 +
>   drivers/soc/mediatek/apusys/mtk-apu-pm.c | 633 +++++++++++++++++++++++
>   4 files changed, 655 insertions(+)
>   create mode 100644 drivers/soc/mediatek/apusys/Makefile
>   create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c
> 
> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> index fdd8bc08569e..d9bac2710494 100644
> --- a/drivers/soc/mediatek/Kconfig
> +++ b/drivers/soc/mediatek/Kconfig
> @@ -5,6 +5,25 @@
>   menu "MediaTek SoC drivers"
>   	depends on ARCH_MEDIATEK || COMPILE_TEST
>   
> +config MTK_APUSYS
> +	bool "MediaTek APUSYS Support"
> +	select REGMAP
> +	help
> +	  Say yes here to add support for the MediaTek AI Processing Unit
> +	  Subsystem (APUSYS).
> +	  The APUSYS is a proprietary hardware in SoC to support AI
> +	  operations.
> +

If these config entries are for files in subfolder apusys/, then you should move

them to drivers/soc/mediatek/apusys/Kconfig and make sure that said Kconfig will

contain a "if MTK_APUSYS" as to include the subconfigs only if the parent one is

declared =y.

> +config MTK_APU_PM
> +	bool "MediaTek APU power management support"

Can we use tristate here? This doesn't look like being a boot-critical driver,
so it can as well be loaded after rootfs init.
Permitting to compile this as a module will also shrink the kernel image size
and that's important when you build a generic kernel image, and that's for both
the MediaTek SoCs case (older SoCs don't need this driver) and for others.

> +	select REGMAP
> +	select PM_GENERIC_DOMAINS if PM
> +	help
> +	  Say yes here to add support for power management control
> +	  to Mediatek AI Processing Unit Subsystem (APUSYS).
> +	  APU power domain shall be enabled before accessing the
> +	  internal sub modules.
> +
>   config MTK_CMDQ
>   	tristate "MediaTek CMDQ Support"
>   	depends on ARCH_MEDIATEK || COMPILE_TEST
> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> index 90270f8114ed..e46e7a3c21e7 100644
> --- a/drivers/soc/mediatek/Makefile
> +++ b/drivers/soc/mediatek/Makefile
> @@ -1,4 +1,5 @@
>   # SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_MTK_APUSYS) += apusys/
>   obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
>   obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
>   obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
> diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile
> new file mode 100644
> index 000000000000..8821c0f0b7b7
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
> diff --git a/drivers/soc/mediatek/apusys/mtk-apu-pm.c b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> new file mode 100644
> index 000000000000..828aa9eb6b37
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> @@ -0,0 +1,633 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_clk.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define APU_PD_IPUIF_HW_CG		BIT(0)
> +#define APU_PD_RPC_AUTO_BUCK		BIT(1)
> +#define APU_PD_CAPS(_pd, _x)		((_pd)->data->caps & (_x))
> +
> +#define MTK_POLL_DELAY_US		10
> +#define MTK_POLL_TIMEOUT		USEC_PER_SEC
> +
> +/* rpc_top_con*/
> +#define SLEEP_REQ		BIT(0)
> +#define APU_BUCK_ELS_EN		BIT(3)
> +
> +/*conn_clr, conn1_clr, vcore_clr */
> +#define CG_CLR	(0xFFFFFFFF)
> +
> +/* mt8192 rpc_sw_type */
> +#define MT8192_RPC_SW_TYPE0	(0x200)
> +#define MT8192_RPC_SW_TYPE1	(0x210)
> +#define MT8192_RPC_SW_TYPE2	(0x220)
> +#define MT8192_RPC_SW_TYPE3	(0x230)
> +#define MT8192_RPC_SW_TYPE4	(0x240)
> +#define MT8192_RPC_SW_TYPE6	(0x260)
> +#define MT8192_RPC_SW_TYPE7	(0x270)

What about...

/* mt8192 rpc_sw_type (0..7) */
#define MT8192_RPC_SW_TYPE(x)	(0x200 + (x * 0x10))

> +
> +/* rpc_sw_type*/
> +static const struct reg_sequence mt8192_rpc_sw_type[] = {
> +	{ MT8192_RPC_SW_TYPE0, 0xFF },
> +	{ MT8192_RPC_SW_TYPE2, 0x7 },
> +	{ MT8192_RPC_SW_TYPE3, 0x7 },
> +	{ MT8192_RPC_SW_TYPE6, 0x3 },
> +};
> +
> +struct apu_top_domain {
> +	u32 spm_ext_buck_iso;
> +	u32 spm_ext_buck_iso_mask;
> +	u32 spm_cross_wake_m01;
> +	u32 wake_apu;
> +	u32 spm_other_pwr;
> +	u32 pwr_status;
> +	u32 conn_clr;
> +	u32 conn1_clr;
> +	u32 vcore_clr;
> +	u32 rpc_top_con;
> +	u32 rpc_top_con_init_mask;
> +	u32 rpc_top_sel;
> +	u32 rpc_top_intf_pwr_rdy;
> +	u32 pwr_rdy;
> +	const struct reg_sequence *rpc_sw_type;
> +	int rpc_sw_ntype;

For the sake of clarity and readability, I would name this one num_rpc_sw_type
or num_rpc_sw, which also keeps naming consistency across this file, since later
in this file, you're doing the same with domains_data.

> +};
> +
> +static struct apu_top_domain mt8192_top_reg = {
> +	.spm_ext_buck_iso = 0x39C,
> +	.spm_ext_buck_iso_mask = 0x21,
> +	.spm_cross_wake_m01 = 0x670,
> +	.wake_apu = BIT(0),
> +	.spm_other_pwr = 0x178,
> +	.pwr_status = BIT(5),
> +	.conn_clr = 0x008,

Please drop leading zeroes. 0x8 here.

> +	.vcore_clr = 0x008,

0x8

> +	.rpc_top_con = 0x000,

0

> +	.rpc_top_con_init_mask = 0x49E,
> +	.rpc_top_sel = 0x004,

0x4

> +	.rpc_top_intf_pwr_rdy = 0x044,

0x44

> +	.pwr_rdy = BIT(0),
> +	.rpc_sw_type = mt8192_rpc_sw_type,
> +	.rpc_sw_ntype = ARRAY_SIZE(mt8192_rpc_sw_type),
> +};
> +
> +struct apusys {
> +	struct device *dev;
> +	struct regmap *scpsys;
> +	struct regmap *conn;
> +	struct regmap *conn1;
> +	struct regmap *vcore;
> +	struct regmap *rpc;
> +	struct regulator *vsram_supply;
> +	const struct apu_pm_data *data;
> +	struct genpd_onecell_data pd_data;
> +	struct generic_pm_domain *domains[];
> +};
> +
> +struct apu_domain {
> +	struct generic_pm_domain genpd;
> +	const struct apu_domain_data *data;
> +	struct apusys *apusys;
> +	struct regulator *domain_supply;
> +	int num_clks;
> +	struct clk_bulk_data *clks;
> +	struct clk *clk_top_conn;
> +	struct clk *clk_top_ipu_if;
> +	struct clk *clk_off;
> +	struct clk *clk_on_def;
> +};
> +
> +struct apu_domain_data {
> +	int domain_idx;
> +	char *name;
> +	struct apu_top_domain *topd;
> +	u8 caps;
> +};
> +
> +struct apu_pm_data {
> +	const struct apu_domain_data *domains_data;
> +	int num_domains;
> +};
> +
> +#define to_apu_domain(gpd) container_of(gpd, struct apu_domain, genpd)
> +
> +static int apu_top_init_hw(struct apu_domain *pd)
> +{
> +	struct apusys *apusys = pd->apusys;
> +	int ret;
> +
> +	/*
> +	 * set memory type to PD or sleep group
> +	 * sw_type register for each memory group, set to PD mode default
> +	 */
> +	if (pd->data->topd->rpc_sw_ntype) {
> +		ret = regmap_register_patch(apusys->rpc,
> +					    pd->data->topd->rpc_sw_type,
> +					    pd->data->topd->rpc_sw_ntype);
> +		if (ret < 0) {
> +			dev_err(apusys->dev, "Failed to rpc patch: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	/* mask RPC IRQ and bypass WFI */
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> +			pd->data->topd->rpc_top_con_init_mask);

This function may return a failure.. and this is not a performance-critical case,
nor a case in which it's 100% granted that this won't fail: you're calling this
at initialization phase (when you're adding the domain), so if there's anything
that would make this fail, this is definitely the right place to check for that.

Please check the return value.

> +
> +	if (APU_PD_CAPS(pd, APU_PD_RPC_AUTO_BUCK))
> +		regmap_set_bits(apusys->rpc,
> +				pd->data->topd->rpc_top_con, APU_BUCK_ELS_EN);
> +
> +	return 0;
> +}
> +
> +static const struct apu_domain_data apu_domain_data_mt8192[] = {
> +	{
> +		.domain_idx = 0,
> +		.name = "apu-top",
> +		.caps = APU_PD_IPUIF_HW_CG,
> +		.topd = &mt8192_top_reg,
> +	}
> +};
> +
> +static const struct apu_pm_data mt8192_apu_pm_data = {
> +	.domains_data = apu_domain_data_mt8192,
> +	.num_domains = ARRAY_SIZE(apu_domain_data_mt8192),
> +};
> +
> +static int apu_top_power_on(struct generic_pm_domain *genpd)
> +{
> +	struct apu_domain *pd = to_apu_domain(genpd);
> +	struct apusys *apusys = pd->apusys;
> +	int ret, tmp;
> +
> +	if (apusys->vsram_supply) {
> +		ret = regulator_enable(apusys->vsram_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pd->domain_supply) {
> +		ret = regulator_enable(pd->domain_supply);
> +		if (ret < 0)
> +			goto err_regulator;
> +	}
> +
> +	regmap_clear_bits(apusys->scpsys, pd->data->topd->spm_ext_buck_iso,
> +			  pd->data->topd->spm_ext_buck_iso_mask);
> +
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> +			pd->data->topd->rpc_top_con_init_mask);
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		ret = clk_prepare_enable(pd->clk_top_conn);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed enable top_conn clk\n");
> +			goto err_clk;
> +		}
> +
> +		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_on_def);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed set ipu_if mux\n");
> +			goto err_clk;
> +		}
> +
> +		/* The ipu_if clock is gatting by HW. Only enable once. */

typo: gating

> +		if (!__clk_is_enabled(pd->clk_top_ipu_if)) {
> +			ret = clk_prepare_enable(pd->clk_top_ipu_if);
> +			if (ret) {
> +				dev_err(apusys->dev, "Failed enable ipu_if\n");
> +				goto err_clk;
> +			}
> +		}
> +	} else {
> +		ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
> +		if (ret)
> +			goto err_clk;
> +	}
> +
> +	regmap_set_bits(apusys->scpsys,
> +			pd->data->topd->spm_cross_wake_m01,
> +			pd->data->topd->wake_apu);
> +
> +	ret = regmap_read_poll_timeout(apusys->scpsys,
> +				       pd->data->topd->spm_other_pwr,
> +				       tmp,
> +				       (tmp & pd->data->topd->pwr_status) ==
> +				       pd->data->topd->pwr_status,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top on wait SPM PWR_ACK != 0\n");
> +		goto err_clk;
> +	}
> +
> +	ret = regmap_read_poll_timeout(apusys->rpc,
> +				       pd->data->topd->rpc_top_intf_pwr_rdy,
> +				       tmp, (tmp & pd->data->topd->pwr_rdy) ==
> +				       pd->data->topd->pwr_rdy,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top on wait RPC PWR_RDY != 0\n");
> +		goto err_clk;
> +	}
> +
> +	if (apusys->vcore)
> +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
> +
> +	if (apusys->conn)
> +		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
> +
> +	if (apusys->conn1)
> +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
> +
> +	return 0;
> +
> +err_clk:
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_disable_unprepare(pd->clk_top_conn);
> +		clk_disable_unprepare(pd->clk_top_ipu_if);
> +	} else {
> +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> +	}
> +	if (pd->domain_supply)
> +		ret = regulator_disable(pd->domain_supply);
> +err_regulator:
> +	if (apusys->vsram_supply)
> +		ret = regulator_disable(apusys->vsram_supply);
> +
> +	return ret;
> +}
> +
> +static int apu_top_power_off(struct generic_pm_domain *genpd)
> +{
> +	struct apu_domain *pd = to_apu_domain(genpd);
> +	struct apusys *apusys = pd->apusys;
> +	int ret, tmp;
> +
> +	if (apusys->vcore)
> +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
> +
> +	if (apusys->conn)
> +		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
> +
> +	if (apusys->conn1)
> +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
> +
> +	regmap_clear_bits(apusys->scpsys,
> +			  pd->data->topd->spm_cross_wake_m01,
> +			  pd->data->topd->wake_apu);
> +
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_con, SLEEP_REQ);
> +
> +	ret = regmap_read_poll_timeout(apusys->rpc,
> +				       pd->data->topd->rpc_top_intf_pwr_rdy,
> +				       tmp,
> +				       (tmp & pd->data->topd->pwr_rdy) == 0x0,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top off wait RPC PWR_RDY != 0\n");
> +		return ret;
> +	}
> +
> +	ret = regmap_read_poll_timeout(apusys->scpsys,
> +				       pd->data->topd->spm_other_pwr, tmp,
> +				       (tmp & pd->data->topd->pwr_status) == 0x0,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top off wait SPM PWR_ACK != 0\n");
> +		return ret;
> +	}
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_disable_unprepare(pd->clk_top_conn);
> +		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_off);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed set ipu_if rate\n");
> +			return ret;
> +		}
> +	} else {
> +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> +	}
> +
> +	regmap_set_bits(apusys->scpsys,
> +			pd->data->topd->spm_ext_buck_iso,
> +			pd->data->topd->spm_ext_buck_iso_mask);
> +
> +	if (apusys->vsram_supply) {
> +		ret = regulator_disable(apusys->vsram_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pd->domain_supply) {
> +		ret = regulator_disable(pd->domain_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct generic_pm_domain *apu_add_one_domain(struct apusys *apusys,
> +						    struct device_node *node)
> +{
> +	const struct apu_domain_data *domain_data;
> +	struct apu_domain *pd;
> +	int ret;
> +	u32 id;
> +	int i;
> +	struct clk *clk;
> +
> +	ret = of_property_read_u32(node, "reg", &id);
> +	if (ret) {
> +		dev_dbg(apusys->dev, "%pOF: invalid reg: %d\n", node, ret);
> +		return ERR_PTR(-EINVAL);

Just use the error value in 'ret', instead of forcing -EINVAL here.

> +	}
> +
> +	if (id >= apusys->data->num_domains) {
> +		dev_dbg(apusys->dev, "%pOF: invalid id %d\n", node, id);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	domain_data = &apusys->data->domains_data[id];
> +
> +	pd = devm_kzalloc(apusys->dev, sizeof(*pd), GFP_KERNEL);
> +	if (!pd)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pd->data   = domain_data;
> +	pd->apusys = apusys;
> +
> +	pd->domain_supply = devm_regulator_get_optional(apusys->dev, "domain");

This may return -EPROBE_DEFER: in that case, you don't want to ignore the supply,
but defer probing this driver until said supply becomes available so, if that
happens, you should return.

> +	if (IS_ERR(pd->domain_supply))
> +		pd->domain_supply = NULL;
> +
> +	/*
> +	 * For HW using ipu_if, the clock is switched to 26M
> +	 * when power down top domain and switch to default clock rate
> +	 * before power on.
> +	 */
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		pd->clk_top_conn = of_clk_get_by_name(node, "clk_top_conn");
> +		if (IS_ERR(pd->clk_top_conn)) {
> +			dev_err(apusys->dev, "Fail to get clk_top_conn clock\n");
> +			ret = PTR_ERR(pd->clk_top_conn);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_top_ipu_if = of_clk_get_by_name(node, "clk_top_ipu_if");
> +		if (IS_ERR(pd->clk_top_ipu_if)) {
> +			dev_err(apusys->dev, "Fail to get clk_top_ipu_if clock\n");
> +			ret = PTR_ERR(pd->clk_top_ipu_if);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_off = of_clk_get_by_name(node, "clk_off");
> +		if (IS_ERR(pd->clk_off)) {
> +			dev_err(apusys->dev, "Fail to get clk_off clock\n");
> +			ret = PTR_ERR(pd->clk_off);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_on_def = of_clk_get_by_name(node, "clk_on_default");
> +		if (IS_ERR(pd->clk_on_def)) {
> +			dev_err(apusys->dev, "Fail to get clk_on_default clock\n");
> +			ret = PTR_ERR(pd->clk_on_def);
> +			goto err_put_clocks;
> +		}
> +	} else {
> +		pd->num_clks = of_clk_get_parent_count(node);
> +		if (pd->num_clks > 0) {
> +			pd->clks = devm_kcalloc(apusys->dev, pd->num_clks,
> +						sizeof(*pd->clks), GFP_KERNEL);
> +			if (!pd->clks)
> +				return ERR_PTR(-ENOMEM);
> +		}
> +
> +		for (i = 0; i < pd->num_clks; i++) {
> +			clk = of_clk_get(node, i);
> +			if (IS_ERR(clk)) {
> +				ret = PTR_ERR(clk);
> +				dev_dbg(apusys->dev,
> +					"%pOF: failed to get clk at index %d: %d\n",
> +					node, i, ret);
> +				goto err_put_clocks;
> +			}
> +			pd->clks[i].clk = clk;
> +		}
> +	}
> +
> +	if (apusys->domains[id]) {
> +		ret = -EINVAL;

Error: X already exists -> -EEXIST

> +		dev_err(apusys->dev, "domain id %d already exists\n", id);
> +		goto err_put_clocks;
> +	}
> +
> +	/* set rpc hw init status */
> +	ret = apu_top_init_hw(pd);
> +	if (ret < 0) {
> +		dev_dbg(apusys->dev, "top init fail ret = %d\n", ret);
> +		goto err_put_clocks;
> +	}
> +
> +	if (!pd->data->name)
> +		pd->genpd.name = node->name;
> +	else
> +		pd->genpd.name = pd->data->name;
> +	pd->genpd.power_off = apu_top_power_off;
> +	pd->genpd.power_on = apu_top_power_on;
> +
> +	/*
> +	 * Initially turn on all domains to make the domains usable
> +	 * with !CONFIG_PM and to get the hardware in sync with the
> +	 * software.  The unused domains will be switched off during
> +	 * late_init time.
> +	 */
> +	ret = pd->genpd.power_on(&pd->genpd);
> +	if (ret < 0) {
> +		dev_dbg(apusys->dev, "%pOF: power on domain fail: %d\n",
> +			node, ret);
> +		goto err_put_clocks;
> +	}
> +
> +	pm_genpd_init(&pd->genpd, NULL, false);

This function may return failure: please check the return value.

> +
> +	apusys->domains[id] = &pd->genpd;
> +
> +	return apusys->pd_data.domains[id];
> +
> +err_put_clocks:
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_put(pd->clk_top_conn);
> +		clk_put(pd->clk_top_ipu_if);
> +		clk_put(pd->clk_off);
> +		clk_put(pd->clk_on_def);
> +	} else {
> +		clk_bulk_put(pd->num_clks, pd->clks);
> +	}
> +	return ERR_PTR(ret);
> +}
> +
> +static void apu_remove_one_domain(struct apu_domain *pd)
> +{
> +	int ret;
> +
> +	if (pd->genpd.power_off)
> +		pd->genpd.power_off(&pd->genpd);
> +
> +	/*
> +	 * We're in the error cleanup already, so we only complain,
> +	 * but won't emit another error on top of the original one.
> +	 */
> +	ret = pm_genpd_remove(&pd->genpd);
> +	if (ret < 0)
> +		dev_dbg(pd->apusys->dev,
> +			"Remove domain '%s' : %d - state may be inconsistent\n",
> +			pd->genpd.name, ret);
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_put(pd->clk_top_conn);
> +		clk_put(pd->clk_top_ipu_if);
> +		clk_put(pd->clk_off);
> +		clk_put(pd->clk_on_def);
> +	} else {
> +		clk_bulk_put(pd->num_clks, pd->clks);
> +	}
> +}
> +
> +static void apu_domain_cleanup(struct apusys *apusys)
> +{
> +	struct generic_pm_domain *genpd;
> +	struct apu_domain *pd;
> +	int i;
> +
> +	for (i = apusys->pd_data.num_domains - 1; i >= 0; i--) {
> +		genpd = apusys->pd_data.domains[i];
> +		if (genpd) {
> +			pd = to_apu_domain(genpd);
> +			apu_remove_one_domain(pd);
> +		}
> +	}
> +}
> +
> +static const struct of_device_id apu_pm_of_match[] = {
> +	{
> +		.compatible = "mediatek,mt8192-apu-pm",
> +		.data = &mt8192_apu_pm_data,
> +	},
> +	{ }
> +};
> +
> +static int apu_pm_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	const struct apu_pm_data *data;
> +	struct device_node *node;
> +	struct apusys *apusys;
> +	int ret;
> +
> +	data = of_device_get_match_data(&pdev->dev);
> +	if (!data) {
> +		dev_dbg(dev, "no power domain data\n");
> +		return -EINVAL;
> +	}
> +
> +	apusys = devm_kzalloc(dev,
> +			      struct_size(apusys, domains, data->num_domains),
> +			      GFP_KERNEL);
> +	if (!apusys)
> +		return -ENOMEM;
> +
> +	apusys->dev = dev;
> +	apusys->data = data;
> +	apusys->pd_data.domains = apusys->domains;
> +	apusys->pd_data.num_domains = data->num_domains;
> +
> +	apusys->vsram_supply = devm_regulator_get_optional(dev, "vsram");
> +	if (IS_ERR(apusys->vsram_supply)) {
> +		ret = PTR_ERR(apusys->vsram_supply);
> +		if (ret != -EPROBE_DEFER) {
> +			dev_err(dev, "vsram_supply fail, ret=%d", ret);
> +			apusys->vsram_supply = NULL;
> +		}
> +		return ret;
> +	}
> +
> +	/* rpc */
> +	apusys->rpc = syscon_node_to_regmap(np);
> +	if (IS_ERR(apusys->rpc)) {
> +		dev_err(dev, "Unable to get rpc\n");
> +		return PTR_ERR(apusys->rpc);
> +	}
> +
> +	/* scpsys */
> +	apusys->scpsys = syscon_regmap_lookup_by_phandle(np, "mediatek,scpsys");
> +	if (IS_ERR(apusys->scpsys)) {
> +		dev_err(dev, "Unable to get scpsys\n");
> +		return PTR_ERR(apusys->scpsys);
> +	}
> +
> +	/* apusys conn */
> +	apusys->conn = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn");
> +	if (IS_ERR(apusys->conn))
> +		dev_info(dev, "No optional phandle apu-conn\n");

I take it as if some platforms may be expected to not have any optional phandle
for that (and for the others): in such case, this would result in being a useless
message. Please use dev_dbg() here.

> +
> +	/* apusys conn1 */
> +	apusys->conn1 = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn1");
> +	if (IS_ERR(apusys->conn1))
> +		dev_info(dev, "No optional phandle apu-conn1\n");
> +
> +	/* apusys vcore */
> +	apusys->vcore = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-vcore");
> +	if (IS_ERR(apusys->vcore))
> +		dev_info(dev, "No optional phandle apu-vcore\n");
> +
> +	for_each_available_child_of_node(np, node) {
> +		struct generic_pm_domain *domain;
> +
> +		domain = apu_add_one_domain(apusys, node);
> +		if (IS_ERR(domain)) {
> +			ret = PTR_ERR(domain);
> +			of_node_put(node);
> +			goto err_cleanup_domains;
> +		}
> +	}
> +
> +	ret = of_genpd_add_provider_onecell(np, &apusys->pd_data);
> +	if (ret) {
> +		dev_dbg(dev, "failed to add provider: %d\n", ret);
> +		goto err_cleanup_domains;
> +	}
> +
> +	return 0;
> +
> +err_cleanup_domains:
> +	apu_domain_cleanup(apusys);
> +	return ret;
> +}
> +
> +static struct platform_driver apu_pm_driver = {
> +	.probe = apu_pm_probe,

In this driver, you already have the means to remove the domains and cleanup the
state... it would probably be trivial to add a .remove handler.

What do you think about that?
Also, is there be any reason for which removing this driver during runtime
would be unsafe?

In my opinion, it would be beneficial to have a remove handler also in the
development usecase, as you'd be able to just recompile this module, remove and
reinsert it to test new changes, without the need of copying an entire new
kernel image and rebooting the device.

> +	.driver = {
> +		.name = "mtk-apu-pm",
> +		.suppress_bind_attrs = true,
> +		.of_match_table = apu_pm_of_match,
> +	},
> +};
> +builtin_platform_driver(apu_pm_driver);
> 

MODULE_LICENSE() is missing! Please add it.
Also, as said in the Kconfig comment, this driver can probably be a module, in
which case... module_platform_driver(apu_pm_driver);

Thanks,
- Angelo


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver
@ 2021-10-26  9:57     ` AngeloGioacchino Del Regno
  0 siblings, 0 replies; 35+ messages in thread
From: AngeloGioacchino Del Regno @ 2021-10-26  9:57 UTC (permalink / raw)
  To: Flora Fu, Rob Herring, Matthias Brugger, Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

Il 23/10/21 12:13, Flora Fu ha scritto:
> Add the apusys in soc.
> Add driver for apu power domains.
> APU power domain shall be enabled before accessing the
> internal sub modules.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>

Hello,
thanks for the patch! However, there are a few things to improve...

> ---
>   drivers/soc/mediatek/Kconfig             |  19 +
>   drivers/soc/mediatek/Makefile            |   1 +
>   drivers/soc/mediatek/apusys/Makefile     |   2 +
>   drivers/soc/mediatek/apusys/mtk-apu-pm.c | 633 +++++++++++++++++++++++
>   4 files changed, 655 insertions(+)
>   create mode 100644 drivers/soc/mediatek/apusys/Makefile
>   create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c
> 
> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> index fdd8bc08569e..d9bac2710494 100644
> --- a/drivers/soc/mediatek/Kconfig
> +++ b/drivers/soc/mediatek/Kconfig
> @@ -5,6 +5,25 @@
>   menu "MediaTek SoC drivers"
>   	depends on ARCH_MEDIATEK || COMPILE_TEST
>   
> +config MTK_APUSYS
> +	bool "MediaTek APUSYS Support"
> +	select REGMAP
> +	help
> +	  Say yes here to add support for the MediaTek AI Processing Unit
> +	  Subsystem (APUSYS).
> +	  The APUSYS is a proprietary hardware in SoC to support AI
> +	  operations.
> +

If these config entries are for files in subfolder apusys/, then you should move

them to drivers/soc/mediatek/apusys/Kconfig and make sure that said Kconfig will

contain a "if MTK_APUSYS" as to include the subconfigs only if the parent one is

declared =y.

> +config MTK_APU_PM
> +	bool "MediaTek APU power management support"

Can we use tristate here? This doesn't look like being a boot-critical driver,
so it can as well be loaded after rootfs init.
Permitting to compile this as a module will also shrink the kernel image size
and that's important when you build a generic kernel image, and that's for both
the MediaTek SoCs case (older SoCs don't need this driver) and for others.

> +	select REGMAP
> +	select PM_GENERIC_DOMAINS if PM
> +	help
> +	  Say yes here to add support for power management control
> +	  to Mediatek AI Processing Unit Subsystem (APUSYS).
> +	  APU power domain shall be enabled before accessing the
> +	  internal sub modules.
> +
>   config MTK_CMDQ
>   	tristate "MediaTek CMDQ Support"
>   	depends on ARCH_MEDIATEK || COMPILE_TEST
> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> index 90270f8114ed..e46e7a3c21e7 100644
> --- a/drivers/soc/mediatek/Makefile
> +++ b/drivers/soc/mediatek/Makefile
> @@ -1,4 +1,5 @@
>   # SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_MTK_APUSYS) += apusys/
>   obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
>   obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
>   obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
> diff --git a/drivers/soc/mediatek/apusys/Makefile b/drivers/soc/mediatek/apusys/Makefile
> new file mode 100644
> index 000000000000..8821c0f0b7b7
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/Makefile
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
> diff --git a/drivers/soc/mediatek/apusys/mtk-apu-pm.c b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> new file mode 100644
> index 000000000000..828aa9eb6b37
> --- /dev/null
> +++ b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> @@ -0,0 +1,633 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_clk.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define APU_PD_IPUIF_HW_CG		BIT(0)
> +#define APU_PD_RPC_AUTO_BUCK		BIT(1)
> +#define APU_PD_CAPS(_pd, _x)		((_pd)->data->caps & (_x))
> +
> +#define MTK_POLL_DELAY_US		10
> +#define MTK_POLL_TIMEOUT		USEC_PER_SEC
> +
> +/* rpc_top_con*/
> +#define SLEEP_REQ		BIT(0)
> +#define APU_BUCK_ELS_EN		BIT(3)
> +
> +/*conn_clr, conn1_clr, vcore_clr */
> +#define CG_CLR	(0xFFFFFFFF)
> +
> +/* mt8192 rpc_sw_type */
> +#define MT8192_RPC_SW_TYPE0	(0x200)
> +#define MT8192_RPC_SW_TYPE1	(0x210)
> +#define MT8192_RPC_SW_TYPE2	(0x220)
> +#define MT8192_RPC_SW_TYPE3	(0x230)
> +#define MT8192_RPC_SW_TYPE4	(0x240)
> +#define MT8192_RPC_SW_TYPE6	(0x260)
> +#define MT8192_RPC_SW_TYPE7	(0x270)

What about...

/* mt8192 rpc_sw_type (0..7) */
#define MT8192_RPC_SW_TYPE(x)	(0x200 + (x * 0x10))

> +
> +/* rpc_sw_type*/
> +static const struct reg_sequence mt8192_rpc_sw_type[] = {
> +	{ MT8192_RPC_SW_TYPE0, 0xFF },
> +	{ MT8192_RPC_SW_TYPE2, 0x7 },
> +	{ MT8192_RPC_SW_TYPE3, 0x7 },
> +	{ MT8192_RPC_SW_TYPE6, 0x3 },
> +};
> +
> +struct apu_top_domain {
> +	u32 spm_ext_buck_iso;
> +	u32 spm_ext_buck_iso_mask;
> +	u32 spm_cross_wake_m01;
> +	u32 wake_apu;
> +	u32 spm_other_pwr;
> +	u32 pwr_status;
> +	u32 conn_clr;
> +	u32 conn1_clr;
> +	u32 vcore_clr;
> +	u32 rpc_top_con;
> +	u32 rpc_top_con_init_mask;
> +	u32 rpc_top_sel;
> +	u32 rpc_top_intf_pwr_rdy;
> +	u32 pwr_rdy;
> +	const struct reg_sequence *rpc_sw_type;
> +	int rpc_sw_ntype;

For the sake of clarity and readability, I would name this one num_rpc_sw_type
or num_rpc_sw, which also keeps naming consistency across this file, since later
in this file, you're doing the same with domains_data.

> +};
> +
> +static struct apu_top_domain mt8192_top_reg = {
> +	.spm_ext_buck_iso = 0x39C,
> +	.spm_ext_buck_iso_mask = 0x21,
> +	.spm_cross_wake_m01 = 0x670,
> +	.wake_apu = BIT(0),
> +	.spm_other_pwr = 0x178,
> +	.pwr_status = BIT(5),
> +	.conn_clr = 0x008,

Please drop leading zeroes. 0x8 here.

> +	.vcore_clr = 0x008,

0x8

> +	.rpc_top_con = 0x000,

0

> +	.rpc_top_con_init_mask = 0x49E,
> +	.rpc_top_sel = 0x004,

0x4

> +	.rpc_top_intf_pwr_rdy = 0x044,

0x44

> +	.pwr_rdy = BIT(0),
> +	.rpc_sw_type = mt8192_rpc_sw_type,
> +	.rpc_sw_ntype = ARRAY_SIZE(mt8192_rpc_sw_type),
> +};
> +
> +struct apusys {
> +	struct device *dev;
> +	struct regmap *scpsys;
> +	struct regmap *conn;
> +	struct regmap *conn1;
> +	struct regmap *vcore;
> +	struct regmap *rpc;
> +	struct regulator *vsram_supply;
> +	const struct apu_pm_data *data;
> +	struct genpd_onecell_data pd_data;
> +	struct generic_pm_domain *domains[];
> +};
> +
> +struct apu_domain {
> +	struct generic_pm_domain genpd;
> +	const struct apu_domain_data *data;
> +	struct apusys *apusys;
> +	struct regulator *domain_supply;
> +	int num_clks;
> +	struct clk_bulk_data *clks;
> +	struct clk *clk_top_conn;
> +	struct clk *clk_top_ipu_if;
> +	struct clk *clk_off;
> +	struct clk *clk_on_def;
> +};
> +
> +struct apu_domain_data {
> +	int domain_idx;
> +	char *name;
> +	struct apu_top_domain *topd;
> +	u8 caps;
> +};
> +
> +struct apu_pm_data {
> +	const struct apu_domain_data *domains_data;
> +	int num_domains;
> +};
> +
> +#define to_apu_domain(gpd) container_of(gpd, struct apu_domain, genpd)
> +
> +static int apu_top_init_hw(struct apu_domain *pd)
> +{
> +	struct apusys *apusys = pd->apusys;
> +	int ret;
> +
> +	/*
> +	 * set memory type to PD or sleep group
> +	 * sw_type register for each memory group, set to PD mode default
> +	 */
> +	if (pd->data->topd->rpc_sw_ntype) {
> +		ret = regmap_register_patch(apusys->rpc,
> +					    pd->data->topd->rpc_sw_type,
> +					    pd->data->topd->rpc_sw_ntype);
> +		if (ret < 0) {
> +			dev_err(apusys->dev, "Failed to rpc patch: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	/* mask RPC IRQ and bypass WFI */
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> +			pd->data->topd->rpc_top_con_init_mask);

This function may return a failure.. and this is not a performance-critical case,
nor a case in which it's 100% granted that this won't fail: you're calling this
at initialization phase (when you're adding the domain), so if there's anything
that would make this fail, this is definitely the right place to check for that.

Please check the return value.

> +
> +	if (APU_PD_CAPS(pd, APU_PD_RPC_AUTO_BUCK))
> +		regmap_set_bits(apusys->rpc,
> +				pd->data->topd->rpc_top_con, APU_BUCK_ELS_EN);
> +
> +	return 0;
> +}
> +
> +static const struct apu_domain_data apu_domain_data_mt8192[] = {
> +	{
> +		.domain_idx = 0,
> +		.name = "apu-top",
> +		.caps = APU_PD_IPUIF_HW_CG,
> +		.topd = &mt8192_top_reg,
> +	}
> +};
> +
> +static const struct apu_pm_data mt8192_apu_pm_data = {
> +	.domains_data = apu_domain_data_mt8192,
> +	.num_domains = ARRAY_SIZE(apu_domain_data_mt8192),
> +};
> +
> +static int apu_top_power_on(struct generic_pm_domain *genpd)
> +{
> +	struct apu_domain *pd = to_apu_domain(genpd);
> +	struct apusys *apusys = pd->apusys;
> +	int ret, tmp;
> +
> +	if (apusys->vsram_supply) {
> +		ret = regulator_enable(apusys->vsram_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pd->domain_supply) {
> +		ret = regulator_enable(pd->domain_supply);
> +		if (ret < 0)
> +			goto err_regulator;
> +	}
> +
> +	regmap_clear_bits(apusys->scpsys, pd->data->topd->spm_ext_buck_iso,
> +			  pd->data->topd->spm_ext_buck_iso_mask);
> +
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> +			pd->data->topd->rpc_top_con_init_mask);
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		ret = clk_prepare_enable(pd->clk_top_conn);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed enable top_conn clk\n");
> +			goto err_clk;
> +		}
> +
> +		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_on_def);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed set ipu_if mux\n");
> +			goto err_clk;
> +		}
> +
> +		/* The ipu_if clock is gatting by HW. Only enable once. */

typo: gating

> +		if (!__clk_is_enabled(pd->clk_top_ipu_if)) {
> +			ret = clk_prepare_enable(pd->clk_top_ipu_if);
> +			if (ret) {
> +				dev_err(apusys->dev, "Failed enable ipu_if\n");
> +				goto err_clk;
> +			}
> +		}
> +	} else {
> +		ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
> +		if (ret)
> +			goto err_clk;
> +	}
> +
> +	regmap_set_bits(apusys->scpsys,
> +			pd->data->topd->spm_cross_wake_m01,
> +			pd->data->topd->wake_apu);
> +
> +	ret = regmap_read_poll_timeout(apusys->scpsys,
> +				       pd->data->topd->spm_other_pwr,
> +				       tmp,
> +				       (tmp & pd->data->topd->pwr_status) ==
> +				       pd->data->topd->pwr_status,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top on wait SPM PWR_ACK != 0\n");
> +		goto err_clk;
> +	}
> +
> +	ret = regmap_read_poll_timeout(apusys->rpc,
> +				       pd->data->topd->rpc_top_intf_pwr_rdy,
> +				       tmp, (tmp & pd->data->topd->pwr_rdy) ==
> +				       pd->data->topd->pwr_rdy,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top on wait RPC PWR_RDY != 0\n");
> +		goto err_clk;
> +	}
> +
> +	if (apusys->vcore)
> +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
> +
> +	if (apusys->conn)
> +		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
> +
> +	if (apusys->conn1)
> +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
> +
> +	return 0;
> +
> +err_clk:
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_disable_unprepare(pd->clk_top_conn);
> +		clk_disable_unprepare(pd->clk_top_ipu_if);
> +	} else {
> +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> +	}
> +	if (pd->domain_supply)
> +		ret = regulator_disable(pd->domain_supply);
> +err_regulator:
> +	if (apusys->vsram_supply)
> +		ret = regulator_disable(apusys->vsram_supply);
> +
> +	return ret;
> +}
> +
> +static int apu_top_power_off(struct generic_pm_domain *genpd)
> +{
> +	struct apu_domain *pd = to_apu_domain(genpd);
> +	struct apusys *apusys = pd->apusys;
> +	int ret, tmp;
> +
> +	if (apusys->vcore)
> +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr, CG_CLR);
> +
> +	if (apusys->conn)
> +		regmap_write(apusys->conn, pd->data->topd->conn_clr, CG_CLR);
> +
> +	if (apusys->conn1)
> +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr, CG_CLR);
> +
> +	regmap_clear_bits(apusys->scpsys,
> +			  pd->data->topd->spm_cross_wake_m01,
> +			  pd->data->topd->wake_apu);
> +
> +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_con, SLEEP_REQ);
> +
> +	ret = regmap_read_poll_timeout(apusys->rpc,
> +				       pd->data->topd->rpc_top_intf_pwr_rdy,
> +				       tmp,
> +				       (tmp & pd->data->topd->pwr_rdy) == 0x0,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top off wait RPC PWR_RDY != 0\n");
> +		return ret;
> +	}
> +
> +	ret = regmap_read_poll_timeout(apusys->scpsys,
> +				       pd->data->topd->spm_other_pwr, tmp,
> +				       (tmp & pd->data->topd->pwr_status) == 0x0,
> +				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
> +	if (ret < 0) {
> +		dev_err(apusys->dev, "apu top off wait SPM PWR_ACK != 0\n");
> +		return ret;
> +	}
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_disable_unprepare(pd->clk_top_conn);
> +		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_off);
> +		if (ret) {
> +			dev_err(apusys->dev, "Failed set ipu_if rate\n");
> +			return ret;
> +		}
> +	} else {
> +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> +	}
> +
> +	regmap_set_bits(apusys->scpsys,
> +			pd->data->topd->spm_ext_buck_iso,
> +			pd->data->topd->spm_ext_buck_iso_mask);
> +
> +	if (apusys->vsram_supply) {
> +		ret = regulator_disable(apusys->vsram_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	if (pd->domain_supply) {
> +		ret = regulator_disable(pd->domain_supply);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct generic_pm_domain *apu_add_one_domain(struct apusys *apusys,
> +						    struct device_node *node)
> +{
> +	const struct apu_domain_data *domain_data;
> +	struct apu_domain *pd;
> +	int ret;
> +	u32 id;
> +	int i;
> +	struct clk *clk;
> +
> +	ret = of_property_read_u32(node, "reg", &id);
> +	if (ret) {
> +		dev_dbg(apusys->dev, "%pOF: invalid reg: %d\n", node, ret);
> +		return ERR_PTR(-EINVAL);

Just use the error value in 'ret', instead of forcing -EINVAL here.

> +	}
> +
> +	if (id >= apusys->data->num_domains) {
> +		dev_dbg(apusys->dev, "%pOF: invalid id %d\n", node, id);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	domain_data = &apusys->data->domains_data[id];
> +
> +	pd = devm_kzalloc(apusys->dev, sizeof(*pd), GFP_KERNEL);
> +	if (!pd)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pd->data   = domain_data;
> +	pd->apusys = apusys;
> +
> +	pd->domain_supply = devm_regulator_get_optional(apusys->dev, "domain");

This may return -EPROBE_DEFER: in that case, you don't want to ignore the supply,
but defer probing this driver until said supply becomes available so, if that
happens, you should return.

> +	if (IS_ERR(pd->domain_supply))
> +		pd->domain_supply = NULL;
> +
> +	/*
> +	 * For HW using ipu_if, the clock is switched to 26M
> +	 * when power down top domain and switch to default clock rate
> +	 * before power on.
> +	 */
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		pd->clk_top_conn = of_clk_get_by_name(node, "clk_top_conn");
> +		if (IS_ERR(pd->clk_top_conn)) {
> +			dev_err(apusys->dev, "Fail to get clk_top_conn clock\n");
> +			ret = PTR_ERR(pd->clk_top_conn);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_top_ipu_if = of_clk_get_by_name(node, "clk_top_ipu_if");
> +		if (IS_ERR(pd->clk_top_ipu_if)) {
> +			dev_err(apusys->dev, "Fail to get clk_top_ipu_if clock\n");
> +			ret = PTR_ERR(pd->clk_top_ipu_if);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_off = of_clk_get_by_name(node, "clk_off");
> +		if (IS_ERR(pd->clk_off)) {
> +			dev_err(apusys->dev, "Fail to get clk_off clock\n");
> +			ret = PTR_ERR(pd->clk_off);
> +			goto err_put_clocks;
> +		}
> +
> +		pd->clk_on_def = of_clk_get_by_name(node, "clk_on_default");
> +		if (IS_ERR(pd->clk_on_def)) {
> +			dev_err(apusys->dev, "Fail to get clk_on_default clock\n");
> +			ret = PTR_ERR(pd->clk_on_def);
> +			goto err_put_clocks;
> +		}
> +	} else {
> +		pd->num_clks = of_clk_get_parent_count(node);
> +		if (pd->num_clks > 0) {
> +			pd->clks = devm_kcalloc(apusys->dev, pd->num_clks,
> +						sizeof(*pd->clks), GFP_KERNEL);
> +			if (!pd->clks)
> +				return ERR_PTR(-ENOMEM);
> +		}
> +
> +		for (i = 0; i < pd->num_clks; i++) {
> +			clk = of_clk_get(node, i);
> +			if (IS_ERR(clk)) {
> +				ret = PTR_ERR(clk);
> +				dev_dbg(apusys->dev,
> +					"%pOF: failed to get clk at index %d: %d\n",
> +					node, i, ret);
> +				goto err_put_clocks;
> +			}
> +			pd->clks[i].clk = clk;
> +		}
> +	}
> +
> +	if (apusys->domains[id]) {
> +		ret = -EINVAL;

Error: X already exists -> -EEXIST

> +		dev_err(apusys->dev, "domain id %d already exists\n", id);
> +		goto err_put_clocks;
> +	}
> +
> +	/* set rpc hw init status */
> +	ret = apu_top_init_hw(pd);
> +	if (ret < 0) {
> +		dev_dbg(apusys->dev, "top init fail ret = %d\n", ret);
> +		goto err_put_clocks;
> +	}
> +
> +	if (!pd->data->name)
> +		pd->genpd.name = node->name;
> +	else
> +		pd->genpd.name = pd->data->name;
> +	pd->genpd.power_off = apu_top_power_off;
> +	pd->genpd.power_on = apu_top_power_on;
> +
> +	/*
> +	 * Initially turn on all domains to make the domains usable
> +	 * with !CONFIG_PM and to get the hardware in sync with the
> +	 * software.  The unused domains will be switched off during
> +	 * late_init time.
> +	 */
> +	ret = pd->genpd.power_on(&pd->genpd);
> +	if (ret < 0) {
> +		dev_dbg(apusys->dev, "%pOF: power on domain fail: %d\n",
> +			node, ret);
> +		goto err_put_clocks;
> +	}
> +
> +	pm_genpd_init(&pd->genpd, NULL, false);

This function may return failure: please check the return value.

> +
> +	apusys->domains[id] = &pd->genpd;
> +
> +	return apusys->pd_data.domains[id];
> +
> +err_put_clocks:
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_put(pd->clk_top_conn);
> +		clk_put(pd->clk_top_ipu_if);
> +		clk_put(pd->clk_off);
> +		clk_put(pd->clk_on_def);
> +	} else {
> +		clk_bulk_put(pd->num_clks, pd->clks);
> +	}
> +	return ERR_PTR(ret);
> +}
> +
> +static void apu_remove_one_domain(struct apu_domain *pd)
> +{
> +	int ret;
> +
> +	if (pd->genpd.power_off)
> +		pd->genpd.power_off(&pd->genpd);
> +
> +	/*
> +	 * We're in the error cleanup already, so we only complain,
> +	 * but won't emit another error on top of the original one.
> +	 */
> +	ret = pm_genpd_remove(&pd->genpd);
> +	if (ret < 0)
> +		dev_dbg(pd->apusys->dev,
> +			"Remove domain '%s' : %d - state may be inconsistent\n",
> +			pd->genpd.name, ret);
> +
> +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> +		clk_put(pd->clk_top_conn);
> +		clk_put(pd->clk_top_ipu_if);
> +		clk_put(pd->clk_off);
> +		clk_put(pd->clk_on_def);
> +	} else {
> +		clk_bulk_put(pd->num_clks, pd->clks);
> +	}
> +}
> +
> +static void apu_domain_cleanup(struct apusys *apusys)
> +{
> +	struct generic_pm_domain *genpd;
> +	struct apu_domain *pd;
> +	int i;
> +
> +	for (i = apusys->pd_data.num_domains - 1; i >= 0; i--) {
> +		genpd = apusys->pd_data.domains[i];
> +		if (genpd) {
> +			pd = to_apu_domain(genpd);
> +			apu_remove_one_domain(pd);
> +		}
> +	}
> +}
> +
> +static const struct of_device_id apu_pm_of_match[] = {
> +	{
> +		.compatible = "mediatek,mt8192-apu-pm",
> +		.data = &mt8192_apu_pm_data,
> +	},
> +	{ }
> +};
> +
> +static int apu_pm_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	const struct apu_pm_data *data;
> +	struct device_node *node;
> +	struct apusys *apusys;
> +	int ret;
> +
> +	data = of_device_get_match_data(&pdev->dev);
> +	if (!data) {
> +		dev_dbg(dev, "no power domain data\n");
> +		return -EINVAL;
> +	}
> +
> +	apusys = devm_kzalloc(dev,
> +			      struct_size(apusys, domains, data->num_domains),
> +			      GFP_KERNEL);
> +	if (!apusys)
> +		return -ENOMEM;
> +
> +	apusys->dev = dev;
> +	apusys->data = data;
> +	apusys->pd_data.domains = apusys->domains;
> +	apusys->pd_data.num_domains = data->num_domains;
> +
> +	apusys->vsram_supply = devm_regulator_get_optional(dev, "vsram");
> +	if (IS_ERR(apusys->vsram_supply)) {
> +		ret = PTR_ERR(apusys->vsram_supply);
> +		if (ret != -EPROBE_DEFER) {
> +			dev_err(dev, "vsram_supply fail, ret=%d", ret);
> +			apusys->vsram_supply = NULL;
> +		}
> +		return ret;
> +	}
> +
> +	/* rpc */
> +	apusys->rpc = syscon_node_to_regmap(np);
> +	if (IS_ERR(apusys->rpc)) {
> +		dev_err(dev, "Unable to get rpc\n");
> +		return PTR_ERR(apusys->rpc);
> +	}
> +
> +	/* scpsys */
> +	apusys->scpsys = syscon_regmap_lookup_by_phandle(np, "mediatek,scpsys");
> +	if (IS_ERR(apusys->scpsys)) {
> +		dev_err(dev, "Unable to get scpsys\n");
> +		return PTR_ERR(apusys->scpsys);
> +	}
> +
> +	/* apusys conn */
> +	apusys->conn = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn");
> +	if (IS_ERR(apusys->conn))
> +		dev_info(dev, "No optional phandle apu-conn\n");

I take it as if some platforms may be expected to not have any optional phandle
for that (and for the others): in such case, this would result in being a useless
message. Please use dev_dbg() here.

> +
> +	/* apusys conn1 */
> +	apusys->conn1 = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-conn1");
> +	if (IS_ERR(apusys->conn1))
> +		dev_info(dev, "No optional phandle apu-conn1\n");
> +
> +	/* apusys vcore */
> +	apusys->vcore = syscon_regmap_lookup_by_phandle_optional(np, "mediatek,apu-vcore");
> +	if (IS_ERR(apusys->vcore))
> +		dev_info(dev, "No optional phandle apu-vcore\n");
> +
> +	for_each_available_child_of_node(np, node) {
> +		struct generic_pm_domain *domain;
> +
> +		domain = apu_add_one_domain(apusys, node);
> +		if (IS_ERR(domain)) {
> +			ret = PTR_ERR(domain);
> +			of_node_put(node);
> +			goto err_cleanup_domains;
> +		}
> +	}
> +
> +	ret = of_genpd_add_provider_onecell(np, &apusys->pd_data);
> +	if (ret) {
> +		dev_dbg(dev, "failed to add provider: %d\n", ret);
> +		goto err_cleanup_domains;
> +	}
> +
> +	return 0;
> +
> +err_cleanup_domains:
> +	apu_domain_cleanup(apusys);
> +	return ret;
> +}
> +
> +static struct platform_driver apu_pm_driver = {
> +	.probe = apu_pm_probe,

In this driver, you already have the means to remove the domains and cleanup the
state... it would probably be trivial to add a .remove handler.

What do you think about that?
Also, is there be any reason for which removing this driver during runtime
would be unsafe?

In my opinion, it would be beneficial to have a remove handler also in the
development usecase, as you'd be able to just recompile this module, remove and
reinsert it to test new changes, without the need of copying an entire new
kernel image and rebooting the device.

> +	.driver = {
> +		.name = "mtk-apu-pm",
> +		.suppress_bind_attrs = true,
> +		.of_match_table = apu_pm_of_match,
> +	},
> +};
> +builtin_platform_driver(apu_pm_driver);
> 

MODULE_LICENSE() is missing! Please add it.
Also, as said in the Kconfig comment, this driver can probably be a module, in
which case... module_platform_driver(apu_pm_driver);

Thanks,
- Angelo


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 3/7] dt-bindings: arm: mediatek: Add new document bindings for APU
  2021-10-23 10:13   ` Flora Fu
  (?)
@ 2021-10-27 21:12     ` Rob Herring
  -1 siblings, 0 replies; 35+ messages in thread
From: Rob Herring @ 2021-10-27 21:12 UTC (permalink / raw)
  To: Flora Fu
  Cc: Mark Brown, Ikjoon Jang, linux-arm-kernel, Michael Turquette,
	linux-clk, Chun-Jie Chen, devicetree, linux-mediatek,
	Stephen Boyd, linux-kernel, Rob Herring, Matthias Brugger,
	Liam Girdwood

On Sat, 23 Oct 2021 18:13:30 +0800, Flora Fu wrote:
> Document the apusys bindings.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>  .../arm/mediatek/mediatek,apusys.yaml         | 49 +++++++++++++++++++
>  1 file changed, 49 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 3/7] dt-bindings: arm: mediatek: Add new document bindings for APU
@ 2021-10-27 21:12     ` Rob Herring
  0 siblings, 0 replies; 35+ messages in thread
From: Rob Herring @ 2021-10-27 21:12 UTC (permalink / raw)
  To: Flora Fu
  Cc: Mark Brown, Ikjoon Jang, linux-arm-kernel, Michael Turquette,
	linux-clk, Chun-Jie Chen, devicetree, linux-mediatek,
	Stephen Boyd, linux-kernel, Rob Herring, Matthias Brugger,
	Liam Girdwood

On Sat, 23 Oct 2021 18:13:30 +0800, Flora Fu wrote:
> Document the apusys bindings.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>  .../arm/mediatek/mediatek,apusys.yaml         | 49 +++++++++++++++++++
>  1 file changed, 49 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 3/7] dt-bindings: arm: mediatek: Add new document bindings for APU
@ 2021-10-27 21:12     ` Rob Herring
  0 siblings, 0 replies; 35+ messages in thread
From: Rob Herring @ 2021-10-27 21:12 UTC (permalink / raw)
  To: Flora Fu
  Cc: Mark Brown, Ikjoon Jang, linux-arm-kernel, Michael Turquette,
	linux-clk, Chun-Jie Chen, devicetree, linux-mediatek,
	Stephen Boyd, linux-kernel, Rob Herring, Matthias Brugger,
	Liam Girdwood

On Sat, 23 Oct 2021 18:13:30 +0800, Flora Fu wrote:
> Document the apusys bindings.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>  .../arm/mediatek/mediatek,apusys.yaml         | 49 +++++++++++++++++++
>  1 file changed, 49 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apusys.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 4/7] dt-bindings: soc: mediatek: apusys: Add new document for APU power domain
  2021-10-23 10:13   ` Flora Fu
  (?)
@ 2021-10-27 21:14     ` Rob Herring
  -1 siblings, 0 replies; 35+ messages in thread
From: Rob Herring @ 2021-10-27 21:14 UTC (permalink / raw)
  To: Flora Fu
  Cc: Ikjoon Jang, devicetree, Liam Girdwood, linux-kernel, linux-clk,
	linux-arm-kernel, linux-mediatek, Mark Brown, Stephen Boyd,
	Rob Herring, Michael Turquette, Chun-Jie Chen, Matthias Brugger

On Sat, 23 Oct 2021 18:13:31 +0800, Flora Fu wrote:
> Document the bindings for APU power domain on MediaTek SoC.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>  .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++++++++++++++++
>  1 file changed, 144 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 4/7] dt-bindings: soc: mediatek: apusys: Add new document for APU power domain
@ 2021-10-27 21:14     ` Rob Herring
  0 siblings, 0 replies; 35+ messages in thread
From: Rob Herring @ 2021-10-27 21:14 UTC (permalink / raw)
  To: Flora Fu
  Cc: Ikjoon Jang, devicetree, Liam Girdwood, linux-kernel, linux-clk,
	linux-arm-kernel, linux-mediatek, Mark Brown, Stephen Boyd,
	Rob Herring, Michael Turquette, Chun-Jie Chen, Matthias Brugger

On Sat, 23 Oct 2021 18:13:31 +0800, Flora Fu wrote:
> Document the bindings for APU power domain on MediaTek SoC.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>  .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++++++++++++++++
>  1 file changed, 144 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 4/7] dt-bindings: soc: mediatek: apusys: Add new document for APU power domain
@ 2021-10-27 21:14     ` Rob Herring
  0 siblings, 0 replies; 35+ messages in thread
From: Rob Herring @ 2021-10-27 21:14 UTC (permalink / raw)
  To: Flora Fu
  Cc: Ikjoon Jang, devicetree, Liam Girdwood, linux-kernel, linux-clk,
	linux-arm-kernel, linux-mediatek, Mark Brown, Stephen Boyd,
	Rob Herring, Michael Turquette, Chun-Jie Chen, Matthias Brugger

On Sat, 23 Oct 2021 18:13:31 +0800, Flora Fu wrote:
> Document the bindings for APU power domain on MediaTek SoC.
> 
> Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> ---
>  .../soc/mediatek/mediatek,apu-pm.yaml         | 144 ++++++++++++++++++
>  1 file changed, 144 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/soc/mediatek/mediatek,apu-pm.yaml
> 

Reviewed-by: Rob Herring <robh@kernel.org>

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver
  2021-10-26  9:57     ` AngeloGioacchino Del Regno
@ 2021-11-25  6:41       ` Flora.Fu
  -1 siblings, 0 replies; 35+ messages in thread
From: Flora.Fu @ 2021-11-25  6:41 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Rob Herring, Matthias Brugger,
	Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

On Tue, 2021-10-26 at 11:57 +0200, AngeloGioacchino Del Regno wrote:
> Il 23/10/21 12:13, Flora Fu ha scritto:
> > Add the apusys in soc.
> > Add driver for apu power domains.
> > APU power domain shall be enabled before accessing the
> > internal sub modules.
> > 
> > Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> 
> Hello,
> thanks for the patch! However, there are a few things to improve...

Hi,
Thanks for the comments, I will update the driver into a tristate
module and fix related coding defects in the next submission.
> 
> > ---
> >   drivers/soc/mediatek/Kconfig             |  19 +
> >   drivers/soc/mediatek/Makefile            |   1 +
> >   drivers/soc/mediatek/apusys/Makefile     |   2 +
> >   drivers/soc/mediatek/apusys/mtk-apu-pm.c | 633
> > +++++++++++++++++++++++
> >   4 files changed, 655 insertions(+)
> >   create mode 100644 drivers/soc/mediatek/apusys/Makefile
> >   create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c
> > 
> > diff --git a/drivers/soc/mediatek/Kconfig
> > b/drivers/soc/mediatek/Kconfig
> > index fdd8bc08569e..d9bac2710494 100644
> > --- a/drivers/soc/mediatek/Kconfig
> > +++ b/drivers/soc/mediatek/Kconfig
> > @@ -5,6 +5,25 @@
> >   menu "MediaTek SoC drivers"
> >   	depends on ARCH_MEDIATEK || COMPILE_TEST
> >   
> > +config MTK_APUSYS
> > +	bool "MediaTek APUSYS Support"
> > +	select REGMAP
> > +	help
> > +	  Say yes here to add support for the MediaTek AI Processing
> > Unit
> > +	  Subsystem (APUSYS).
> > +	  The APUSYS is a proprietary hardware in SoC to support AI
> > +	  operations.
> > +
> 
> If these config entries are for files in subfolder apusys/, then you
> should move
> 
> them to drivers/soc/mediatek/apusys/Kconfig and make sure that said
> Kconfig will
> 
> contain a "if MTK_APUSYS" as to include the subconfigs only if the
> parent one is
> 
> declared =y.
> 
> > +config MTK_APU_PM
> > +	bool "MediaTek APU power management support"
> 
> Can we use tristate here? This doesn't look like being a boot-
> critical driver,
> so it can as well be loaded after rootfs init.
> Permitting to compile this as a module will also shrink the kernel
> image size
> and that's important when you build a generic kernel image, and
> that's for both
> the MediaTek SoCs case (older SoCs don't need this driver) and for
> others.
> 
> > +	select REGMAP
> > +	select PM_GENERIC_DOMAINS if PM
> > +	help
> > +	  Say yes here to add support for power management control
> > +	  to Mediatek AI Processing Unit Subsystem (APUSYS).
> > +	  APU power domain shall be enabled before accessing the
> > +	  internal sub modules.
> > +
> >   config MTK_CMDQ
> >   	tristate "MediaTek CMDQ Support"
> >   	depends on ARCH_MEDIATEK || COMPILE_TEST
> > diff --git a/drivers/soc/mediatek/Makefile
> > b/drivers/soc/mediatek/Makefile
> > index 90270f8114ed..e46e7a3c21e7 100644
> > --- a/drivers/soc/mediatek/Makefile
> > +++ b/drivers/soc/mediatek/Makefile
> > @@ -1,4 +1,5 @@
> >   # SPDX-License-Identifier: GPL-2.0-only
> > +obj-$(CONFIG_MTK_APUSYS) += apusys/
> >   obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
> >   obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
> >   obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
> > diff --git a/drivers/soc/mediatek/apusys/Makefile
> > b/drivers/soc/mediatek/apusys/Makefile
> > new file mode 100644
> > index 000000000000..8821c0f0b7b7
> > --- /dev/null
> > +++ b/drivers/soc/mediatek/apusys/Makefile
> > @@ -0,0 +1,2 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
> > diff --git a/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> > b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> > new file mode 100644
> > index 000000000000..828aa9eb6b37
> > --- /dev/null
> > +++ b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> > @@ -0,0 +1,633 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (c) 2021 MediaTek Inc.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/clk-provider.h>
> > +#include <linux/init.h>
> > +#include <linux/io.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_clk.h>
> > +#include <linux/of_device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_domain.h>
> > +#include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#define APU_PD_IPUIF_HW_CG		BIT(0)
> > +#define APU_PD_RPC_AUTO_BUCK		BIT(1)
> > +#define APU_PD_CAPS(_pd, _x)		((_pd)->data->caps &
> > (_x))
> > +
> > +#define MTK_POLL_DELAY_US		10
> > +#define MTK_POLL_TIMEOUT		USEC_PER_SEC
> > +
> > +/* rpc_top_con*/
> > +#define SLEEP_REQ		BIT(0)
> > +#define APU_BUCK_ELS_EN		BIT(3)
> > +
> > +/*conn_clr, conn1_clr, vcore_clr */
> > +#define CG_CLR	(0xFFFFFFFF)
> > +
> > +/* mt8192 rpc_sw_type */
> > +#define MT8192_RPC_SW_TYPE0	(0x200)
> > +#define MT8192_RPC_SW_TYPE1	(0x210)
> > +#define MT8192_RPC_SW_TYPE2	(0x220)
> > +#define MT8192_RPC_SW_TYPE3	(0x230)
> > +#define MT8192_RPC_SW_TYPE4	(0x240)
> > +#define MT8192_RPC_SW_TYPE6	(0x260)
> > +#define MT8192_RPC_SW_TYPE7	(0x270)
> 
> What about...
> 
> /* mt8192 rpc_sw_type (0..7) */
> #define MT8192_RPC_SW_TYPE(x)	(0x200 + (x * 0x10))
> 
> > +
> > +/* rpc_sw_type*/
> > +static const struct reg_sequence mt8192_rpc_sw_type[] = {
> > +	{ MT8192_RPC_SW_TYPE0, 0xFF },
> > +	{ MT8192_RPC_SW_TYPE2, 0x7 },
> > +	{ MT8192_RPC_SW_TYPE3, 0x7 },
> > +	{ MT8192_RPC_SW_TYPE6, 0x3 },
> > +};
> > +
> > +struct apu_top_domain {
> > +	u32 spm_ext_buck_iso;
> > +	u32 spm_ext_buck_iso_mask;
> > +	u32 spm_cross_wake_m01;
> > +	u32 wake_apu;
> > +	u32 spm_other_pwr;
> > +	u32 pwr_status;
> > +	u32 conn_clr;
> > +	u32 conn1_clr;
> > +	u32 vcore_clr;
> > +	u32 rpc_top_con;
> > +	u32 rpc_top_con_init_mask;
> > +	u32 rpc_top_sel;
> > +	u32 rpc_top_intf_pwr_rdy;
> > +	u32 pwr_rdy;
> > +	const struct reg_sequence *rpc_sw_type;
> > +	int rpc_sw_ntype;
> 
> For the sake of clarity and readability, I would name this one
> num_rpc_sw_type
> or num_rpc_sw, which also keeps naming consistency across this file,
> since later
> in this file, you're doing the same with domains_data.
> 
> > +};
> > +
> > +static struct apu_top_domain mt8192_top_reg = {
> > +	.spm_ext_buck_iso = 0x39C,
> > +	.spm_ext_buck_iso_mask = 0x21,
> > +	.spm_cross_wake_m01 = 0x670,
> > +	.wake_apu = BIT(0),
> > +	.spm_other_pwr = 0x178,
> > +	.pwr_status = BIT(5),
> > +	.conn_clr = 0x008,
> 
> Please drop leading zeroes. 0x8 here.
> 
> > +	.vcore_clr = 0x008,
> 
> 0x8
> 
> > +	.rpc_top_con = 0x000,
> 
> 0
> 
> > +	.rpc_top_con_init_mask = 0x49E,
> > +	.rpc_top_sel = 0x004,
> 
> 0x4
> 
> > +	.rpc_top_intf_pwr_rdy = 0x044,
> 
> 0x44
> 
> > +	.pwr_rdy = BIT(0),
> > +	.rpc_sw_type = mt8192_rpc_sw_type,
> > +	.rpc_sw_ntype = ARRAY_SIZE(mt8192_rpc_sw_type),
> > +};
> > +
> > +struct apusys {
> > +	struct device *dev;
> > +	struct regmap *scpsys;
> > +	struct regmap *conn;
> > +	struct regmap *conn1;
> > +	struct regmap *vcore;
> > +	struct regmap *rpc;
> > +	struct regulator *vsram_supply;
> > +	const struct apu_pm_data *data;
> > +	struct genpd_onecell_data pd_data;
> > +	struct generic_pm_domain *domains[];
> > +};
> > +
> > +struct apu_domain {
> > +	struct generic_pm_domain genpd;
> > +	const struct apu_domain_data *data;
> > +	struct apusys *apusys;
> > +	struct regulator *domain_supply;
> > +	int num_clks;
> > +	struct clk_bulk_data *clks;
> > +	struct clk *clk_top_conn;
> > +	struct clk *clk_top_ipu_if;
> > +	struct clk *clk_off;
> > +	struct clk *clk_on_def;
> > +};
> > +
> > +struct apu_domain_data {
> > +	int domain_idx;
> > +	char *name;
> > +	struct apu_top_domain *topd;
> > +	u8 caps;
> > +};
> > +
> > +struct apu_pm_data {
> > +	const struct apu_domain_data *domains_data;
> > +	int num_domains;
> > +};
> > +
> > +#define to_apu_domain(gpd) container_of(gpd, struct apu_domain,
> > genpd)
> > +
> > +static int apu_top_init_hw(struct apu_domain *pd)
> > +{
> > +	struct apusys *apusys = pd->apusys;
> > +	int ret;
> > +
> > +	/*
> > +	 * set memory type to PD or sleep group
> > +	 * sw_type register for each memory group, set to PD mode
> > default
> > +	 */
> > +	if (pd->data->topd->rpc_sw_ntype) {
> > +		ret = regmap_register_patch(apusys->rpc,
> > +					    pd->data->topd-
> > >rpc_sw_type,
> > +					    pd->data->topd-
> > >rpc_sw_ntype);
> > +		if (ret < 0) {
> > +			dev_err(apusys->dev, "Failed to rpc patch:
> > %d\n", ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	/* mask RPC IRQ and bypass WFI */
> > +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> > +			pd->data->topd->rpc_top_con_init_mask);
> 
> This function may return a failure.. and this is not a performance-
> critical case,
> nor a case in which it's 100% granted that this won't fail: you're
> calling this
> at initialization phase (when you're adding the domain), so if
> there's anything
> that would make this fail, this is definitely the right place to
> check for that.
> 
> Please check the return value.
> 
> > +
> > +	if (APU_PD_CAPS(pd, APU_PD_RPC_AUTO_BUCK))
> > +		regmap_set_bits(apusys->rpc,
> > +				pd->data->topd->rpc_top_con,
> > APU_BUCK_ELS_EN);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct apu_domain_data apu_domain_data_mt8192[] = {
> > +	{
> > +		.domain_idx = 0,
> > +		.name = "apu-top",
> > +		.caps = APU_PD_IPUIF_HW_CG,
> > +		.topd = &mt8192_top_reg,
> > +	}
> > +};
> > +
> > +static const struct apu_pm_data mt8192_apu_pm_data = {
> > +	.domains_data = apu_domain_data_mt8192,
> > +	.num_domains = ARRAY_SIZE(apu_domain_data_mt8192),
> > +};
> > +
> > +static int apu_top_power_on(struct generic_pm_domain *genpd)
> > +{
> > +	struct apu_domain *pd = to_apu_domain(genpd);
> > +	struct apusys *apusys = pd->apusys;
> > +	int ret, tmp;
> > +
> > +	if (apusys->vsram_supply) {
> > +		ret = regulator_enable(apusys->vsram_supply);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	if (pd->domain_supply) {
> > +		ret = regulator_enable(pd->domain_supply);
> > +		if (ret < 0)
> > +			goto err_regulator;
> > +	}
> > +
> > +	regmap_clear_bits(apusys->scpsys, pd->data->topd-
> > >spm_ext_buck_iso,
> > +			  pd->data->topd->spm_ext_buck_iso_mask);
> > +
> > +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> > +			pd->data->topd->rpc_top_con_init_mask);
> > +
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		ret = clk_prepare_enable(pd->clk_top_conn);
> > +		if (ret) {
> > +			dev_err(apusys->dev, "Failed enable top_conn
> > clk\n");
> > +			goto err_clk;
> > +		}
> > +
> > +		ret = clk_set_parent(pd->clk_top_ipu_if, pd-
> > >clk_on_def);
> > +		if (ret) {
> > +			dev_err(apusys->dev, "Failed set ipu_if
> > mux\n");
> > +			goto err_clk;
> > +		}
> > +
> > +		/* The ipu_if clock is gatting by HW. Only enable once.
> > */
> 
> typo: gating
> 
> > +		if (!__clk_is_enabled(pd->clk_top_ipu_if)) {
> > +			ret = clk_prepare_enable(pd->clk_top_ipu_if);
> > +			if (ret) {
> > +				dev_err(apusys->dev, "Failed enable
> > ipu_if\n");
> > +				goto err_clk;
> > +			}
> > +		}
> > +	} else {
> > +		ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
> > +		if (ret)
> > +			goto err_clk;
> > +	}
> > +
> > +	regmap_set_bits(apusys->scpsys,
> > +			pd->data->topd->spm_cross_wake_m01,
> > +			pd->data->topd->wake_apu);
> > +
> > +	ret = regmap_read_poll_timeout(apusys->scpsys,
> > +				       pd->data->topd->spm_other_pwr,
> > +				       tmp,
> > +				       (tmp & pd->data->topd-
> > >pwr_status) ==
> > +				       pd->data->topd->pwr_status,
> > +				       MTK_POLL_DELAY_US,
> > MTK_POLL_TIMEOUT);
> > +	if (ret < 0) {
> > +		dev_err(apusys->dev, "apu top on wait SPM PWR_ACK !=
> > 0\n");
> > +		goto err_clk;
> > +	}
> > +
> > +	ret = regmap_read_poll_timeout(apusys->rpc,
> > +				       pd->data->topd-
> > >rpc_top_intf_pwr_rdy,
> > +				       tmp, (tmp & pd->data->topd-
> > >pwr_rdy) ==
> > +				       pd->data->topd->pwr_rdy,
> > +				       MTK_POLL_DELAY_US,
> > MTK_POLL_TIMEOUT);
> > +	if (ret < 0) {
> > +		dev_err(apusys->dev, "apu top on wait RPC PWR_RDY !=
> > 0\n");
> > +		goto err_clk;
> > +	}
> > +
> > +	if (apusys->vcore)
> > +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr,
> > CG_CLR);
> > +
> > +	if (apusys->conn)
> > +		regmap_write(apusys->conn, pd->data->topd->conn_clr,
> > CG_CLR);
> > +
> > +	if (apusys->conn1)
> > +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr,
> > CG_CLR);
> > +
> > +	return 0;
> > +
> > +err_clk:
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		clk_disable_unprepare(pd->clk_top_conn);
> > +		clk_disable_unprepare(pd->clk_top_ipu_if);
> > +	} else {
> > +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> > +	}
> > +	if (pd->domain_supply)
> > +		ret = regulator_disable(pd->domain_supply);
> > +err_regulator:
> > +	if (apusys->vsram_supply)
> > +		ret = regulator_disable(apusys->vsram_supply);
> > +
> > +	return ret;
> > +}
> > +
> > +static int apu_top_power_off(struct generic_pm_domain *genpd)
> > +{
> > +	struct apu_domain *pd = to_apu_domain(genpd);
> > +	struct apusys *apusys = pd->apusys;
> > +	int ret, tmp;
> > +
> > +	if (apusys->vcore)
> > +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr,
> > CG_CLR);
> > +
> > +	if (apusys->conn)
> > +		regmap_write(apusys->conn, pd->data->topd->conn_clr,
> > CG_CLR);
> > +
> > +	if (apusys->conn1)
> > +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr,
> > CG_CLR);
> > +
> > +	regmap_clear_bits(apusys->scpsys,
> > +			  pd->data->topd->spm_cross_wake_m01,
> > +			  pd->data->topd->wake_apu);
> > +
> > +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_con,
> > SLEEP_REQ);
> > +
> > +	ret = regmap_read_poll_timeout(apusys->rpc,
> > +				       pd->data->topd-
> > >rpc_top_intf_pwr_rdy,
> > +				       tmp,
> > +				       (tmp & pd->data->topd->pwr_rdy)
> > == 0x0,
> > +				       MTK_POLL_DELAY_US,
> > MTK_POLL_TIMEOUT);
> > +	if (ret < 0) {
> > +		dev_err(apusys->dev, "apu top off wait RPC PWR_RDY !=
> > 0\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = regmap_read_poll_timeout(apusys->scpsys,
> > +				       pd->data->topd->spm_other_pwr,
> > tmp,
> > +				       (tmp & pd->data->topd-
> > >pwr_status) == 0x0,
> > +				       MTK_POLL_DELAY_US,
> > MTK_POLL_TIMEOUT);
> > +	if (ret < 0) {
> > +		dev_err(apusys->dev, "apu top off wait SPM PWR_ACK !=
> > 0\n");
> > +		return ret;
> > +	}
> > +
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		clk_disable_unprepare(pd->clk_top_conn);
> > +		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_off);
> > +		if (ret) {
> > +			dev_err(apusys->dev, "Failed set ipu_if
> > rate\n");
> > +			return ret;
> > +		}
> > +	} else {
> > +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> > +	}
> > +
> > +	regmap_set_bits(apusys->scpsys,
> > +			pd->data->topd->spm_ext_buck_iso,
> > +			pd->data->topd->spm_ext_buck_iso_mask);
> > +
> > +	if (apusys->vsram_supply) {
> > +		ret = regulator_disable(apusys->vsram_supply);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	if (pd->domain_supply) {
> > +		ret = regulator_disable(pd->domain_supply);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct generic_pm_domain *apu_add_one_domain(struct apusys
> > *apusys,
> > +						    struct device_node
> > *node)
> > +{
> > +	const struct apu_domain_data *domain_data;
> > +	struct apu_domain *pd;
> > +	int ret;
> > +	u32 id;
> > +	int i;
> > +	struct clk *clk;
> > +
> > +	ret = of_property_read_u32(node, "reg", &id);
> > +	if (ret) {
> > +		dev_dbg(apusys->dev, "%pOF: invalid reg: %d\n", node,
> > ret);
> > +		return ERR_PTR(-EINVAL);
> 
> Just use the error value in 'ret', instead of forcing -EINVAL here.
> 
> > +	}
> > +
> > +	if (id >= apusys->data->num_domains) {
> > +		dev_dbg(apusys->dev, "%pOF: invalid id %d\n", node,
> > id);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	domain_data = &apusys->data->domains_data[id];
> > +
> > +	pd = devm_kzalloc(apusys->dev, sizeof(*pd), GFP_KERNEL);
> > +	if (!pd)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	pd->data   = domain_data;
> > +	pd->apusys = apusys;
> > +
> > +	pd->domain_supply = devm_regulator_get_optional(apusys->dev,
> > "domain");
> 
> This may return -EPROBE_DEFER: in that case, you don't want to ignore
> the supply,
> but defer probing this driver until said supply becomes available so,
> if that
> happens, you should return.
> 
> > +	if (IS_ERR(pd->domain_supply))
> > +		pd->domain_supply = NULL;
> > +
> > +	/*
> > +	 * For HW using ipu_if, the clock is switched to 26M
> > +	 * when power down top domain and switch to default clock rate
> > +	 * before power on.
> > +	 */
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		pd->clk_top_conn = of_clk_get_by_name(node,
> > "clk_top_conn");
> > +		if (IS_ERR(pd->clk_top_conn)) {
> > +			dev_err(apusys->dev, "Fail to get clk_top_conn
> > clock\n");
> > +			ret = PTR_ERR(pd->clk_top_conn);
> > +			goto err_put_clocks;
> > +		}
> > +
> > +		pd->clk_top_ipu_if = of_clk_get_by_name(node,
> > "clk_top_ipu_if");
> > +		if (IS_ERR(pd->clk_top_ipu_if)) {
> > +			dev_err(apusys->dev, "Fail to get
> > clk_top_ipu_if clock\n");
> > +			ret = PTR_ERR(pd->clk_top_ipu_if);
> > +			goto err_put_clocks;
> > +		}
> > +
> > +		pd->clk_off = of_clk_get_by_name(node, "clk_off");
> > +		if (IS_ERR(pd->clk_off)) {
> > +			dev_err(apusys->dev, "Fail to get clk_off
> > clock\n");
> > +			ret = PTR_ERR(pd->clk_off);
> > +			goto err_put_clocks;
> > +		}
> > +
> > +		pd->clk_on_def = of_clk_get_by_name(node,
> > "clk_on_default");
> > +		if (IS_ERR(pd->clk_on_def)) {
> > +			dev_err(apusys->dev, "Fail to get
> > clk_on_default clock\n");
> > +			ret = PTR_ERR(pd->clk_on_def);
> > +			goto err_put_clocks;
> > +		}
> > +	} else {
> > +		pd->num_clks = of_clk_get_parent_count(node);
> > +		if (pd->num_clks > 0) {
> > +			pd->clks = devm_kcalloc(apusys->dev, pd-
> > >num_clks,
> > +						sizeof(*pd->clks),
> > GFP_KERNEL);
> > +			if (!pd->clks)
> > +				return ERR_PTR(-ENOMEM);
> > +		}
> > +
> > +		for (i = 0; i < pd->num_clks; i++) {
> > +			clk = of_clk_get(node, i);
> > +			if (IS_ERR(clk)) {
> > +				ret = PTR_ERR(clk);
> > +				dev_dbg(apusys->dev,
> > +					"%pOF: failed to get clk at
> > index %d: %d\n",
> > +					node, i, ret);
> > +				goto err_put_clocks;
> > +			}
> > +			pd->clks[i].clk = clk;
> > +		}
> > +	}
> > +
> > +	if (apusys->domains[id]) {
> > +		ret = -EINVAL;
> 
> Error: X already exists -> -EEXIST
> 
> > +		dev_err(apusys->dev, "domain id %d already exists\n",
> > id);
> > +		goto err_put_clocks;
> > +	}
> > +
> > +	/* set rpc hw init status */
> > +	ret = apu_top_init_hw(pd);
> > +	if (ret < 0) {
> > +		dev_dbg(apusys->dev, "top init fail ret = %d\n", ret);
> > +		goto err_put_clocks;
> > +	}
> > +
> > +	if (!pd->data->name)
> > +		pd->genpd.name = node->name;
> > +	else
> > +		pd->genpd.name = pd->data->name;
> > +	pd->genpd.power_off = apu_top_power_off;
> > +	pd->genpd.power_on = apu_top_power_on;
> > +
> > +	/*
> > +	 * Initially turn on all domains to make the domains usable
> > +	 * with !CONFIG_PM and to get the hardware in sync with the
> > +	 * software.  The unused domains will be switched off during
> > +	 * late_init time.
> > +	 */
> > +	ret = pd->genpd.power_on(&pd->genpd);
> > +	if (ret < 0) {
> > +		dev_dbg(apusys->dev, "%pOF: power on domain fail:
> > %d\n",
> > +			node, ret);
> > +		goto err_put_clocks;
> > +	}
> > +
> > +	pm_genpd_init(&pd->genpd, NULL, false);
> 
> This function may return failure: please check the return value.
> 
> > +
> > +	apusys->domains[id] = &pd->genpd;
> > +
> > +	return apusys->pd_data.domains[id];
> > +
> > +err_put_clocks:
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		clk_put(pd->clk_top_conn);
> > +		clk_put(pd->clk_top_ipu_if);
> > +		clk_put(pd->clk_off);
> > +		clk_put(pd->clk_on_def);
> > +	} else {
> > +		clk_bulk_put(pd->num_clks, pd->clks);
> > +	}
> > +	return ERR_PTR(ret);
> > +}
> > +
> > +static void apu_remove_one_domain(struct apu_domain *pd)
> > +{
> > +	int ret;
> > +
> > +	if (pd->genpd.power_off)
> > +		pd->genpd.power_off(&pd->genpd);
> > +
> > +	/*
> > +	 * We're in the error cleanup already, so we only complain,
> > +	 * but won't emit another error on top of the original one.
> > +	 */
> > +	ret = pm_genpd_remove(&pd->genpd);
> > +	if (ret < 0)
> > +		dev_dbg(pd->apusys->dev,
> > +			"Remove domain '%s' : %d - state may be
> > inconsistent\n",
> > +			pd->genpd.name, ret);
> > +
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		clk_put(pd->clk_top_conn);
> > +		clk_put(pd->clk_top_ipu_if);
> > +		clk_put(pd->clk_off);
> > +		clk_put(pd->clk_on_def);
> > +	} else {
> > +		clk_bulk_put(pd->num_clks, pd->clks);
> > +	}
> > +}
> > +
> > +static void apu_domain_cleanup(struct apusys *apusys)
> > +{
> > +	struct generic_pm_domain *genpd;
> > +	struct apu_domain *pd;
> > +	int i;
> > +
> > +	for (i = apusys->pd_data.num_domains - 1; i >= 0; i--) {
> > +		genpd = apusys->pd_data.domains[i];
> > +		if (genpd) {
> > +			pd = to_apu_domain(genpd);
> > +			apu_remove_one_domain(pd);
> > +		}
> > +	}
> > +}
> > +
> > +static const struct of_device_id apu_pm_of_match[] = {
> > +	{
> > +		.compatible = "mediatek,mt8192-apu-pm",
> > +		.data = &mt8192_apu_pm_data,
> > +	},
> > +	{ }
> > +};
> > +
> > +static int apu_pm_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	const struct apu_pm_data *data;
> > +	struct device_node *node;
> > +	struct apusys *apusys;
> > +	int ret;
> > +
> > +	data = of_device_get_match_data(&pdev->dev);
> > +	if (!data) {
> > +		dev_dbg(dev, "no power domain data\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	apusys = devm_kzalloc(dev,
> > +			      struct_size(apusys, domains, data-
> > >num_domains),
> > +			      GFP_KERNEL);
> > +	if (!apusys)
> > +		return -ENOMEM;
> > +
> > +	apusys->dev = dev;
> > +	apusys->data = data;
> > +	apusys->pd_data.domains = apusys->domains;
> > +	apusys->pd_data.num_domains = data->num_domains;
> > +
> > +	apusys->vsram_supply = devm_regulator_get_optional(dev,
> > "vsram");
> > +	if (IS_ERR(apusys->vsram_supply)) {
> > +		ret = PTR_ERR(apusys->vsram_supply);
> > +		if (ret != -EPROBE_DEFER) {
> > +			dev_err(dev, "vsram_supply fail, ret=%d", ret);
> > +			apusys->vsram_supply = NULL;
> > +		}
> > +		return ret;
> > +	}
> > +
> > +	/* rpc */
> > +	apusys->rpc = syscon_node_to_regmap(np);
> > +	if (IS_ERR(apusys->rpc)) {
> > +		dev_err(dev, "Unable to get rpc\n");
> > +		return PTR_ERR(apusys->rpc);
> > +	}
> > +
> > +	/* scpsys */
> > +	apusys->scpsys = syscon_regmap_lookup_by_phandle(np,
> > "mediatek,scpsys");
> > +	if (IS_ERR(apusys->scpsys)) {
> > +		dev_err(dev, "Unable to get scpsys\n");
> > +		return PTR_ERR(apusys->scpsys);
> > +	}
> > +
> > +	/* apusys conn */
> > +	apusys->conn = syscon_regmap_lookup_by_phandle_optional(np,
> > "mediatek,apu-conn");
> > +	if (IS_ERR(apusys->conn))
> > +		dev_info(dev, "No optional phandle apu-conn\n");
> 
> I take it as if some platforms may be expected to not have any
> optional phandle
> for that (and for the others): in such case, this would result in
> being a useless
> message. Please use dev_dbg() here.
> 
> > +
> > +	/* apusys conn1 */
> > +	apusys->conn1 = syscon_regmap_lookup_by_phandle_optional(np,
> > "mediatek,apu-conn1");
> > +	if (IS_ERR(apusys->conn1))
> > +		dev_info(dev, "No optional phandle apu-conn1\n");
> > +
> > +	/* apusys vcore */
> > +	apusys->vcore = syscon_regmap_lookup_by_phandle_optional(np,
> > "mediatek,apu-vcore");
> > +	if (IS_ERR(apusys->vcore))
> > +		dev_info(dev, "No optional phandle apu-vcore\n");
> > +
> > +	for_each_available_child_of_node(np, node) {
> > +		struct generic_pm_domain *domain;
> > +
> > +		domain = apu_add_one_domain(apusys, node);
> > +		if (IS_ERR(domain)) {
> > +			ret = PTR_ERR(domain);
> > +			of_node_put(node);
> > +			goto err_cleanup_domains;
> > +		}
> > +	}
> > +
> > +	ret = of_genpd_add_provider_onecell(np, &apusys->pd_data);
> > +	if (ret) {
> > +		dev_dbg(dev, "failed to add provider: %d\n", ret);
> > +		goto err_cleanup_domains;
> > +	}
> > +
> > +	return 0;
> > +
> > +err_cleanup_domains:
> > +	apu_domain_cleanup(apusys);
> > +	return ret;
> > +}
> > +
> > +static struct platform_driver apu_pm_driver = {
> > +	.probe = apu_pm_probe,
> 
> In this driver, you already have the means to remove the domains and
> cleanup the
> state... it would probably be trivial to add a .remove handler.
> 
> What do you think about that?
> Also, is there be any reason for which removing this driver during
> runtime
> would be unsafe?
> 
> In my opinion, it would be beneficial to have a remove handler also
> in the
> development usecase, as you'd be able to just recompile this module,
> remove and
> reinsert it to test new changes, without the need of copying an
> entire new
> kernel image and rebooting the device.
> 
> > +	.driver = {
> > +		.name = "mtk-apu-pm",
> > +		.suppress_bind_attrs = true,
> > +		.of_match_table = apu_pm_of_match,
> > +	},
> > +};
> > +builtin_platform_driver(apu_pm_driver);
> > 
> 
> MODULE_LICENSE() is missing! Please add it.
> Also, as said in the Kconfig comment, this driver can probably be a
> module, in
> which case... module_platform_driver(apu_pm_driver);
> 
> Thanks,
> - Angelo
> 


_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver
@ 2021-11-25  6:41       ` Flora.Fu
  0 siblings, 0 replies; 35+ messages in thread
From: Flora.Fu @ 2021-11-25  6:41 UTC (permalink / raw)
  To: AngeloGioacchino Del Regno, Rob Herring, Matthias Brugger,
	Michael Turquette, Stephen Boyd
  Cc: Liam Girdwood, Mark Brown, Ikjoon Jang, Chun-Jie Chen,
	devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
	linux-clk

On Tue, 2021-10-26 at 11:57 +0200, AngeloGioacchino Del Regno wrote:
> Il 23/10/21 12:13, Flora Fu ha scritto:
> > Add the apusys in soc.
> > Add driver for apu power domains.
> > APU power domain shall be enabled before accessing the
> > internal sub modules.
> > 
> > Signed-off-by: Flora Fu <flora.fu@mediatek.com>
> 
> Hello,
> thanks for the patch! However, there are a few things to improve...

Hi,
Thanks for the comments, I will update the driver into a tristate
module and fix related coding defects in the next submission.
> 
> > ---
> >   drivers/soc/mediatek/Kconfig             |  19 +
> >   drivers/soc/mediatek/Makefile            |   1 +
> >   drivers/soc/mediatek/apusys/Makefile     |   2 +
> >   drivers/soc/mediatek/apusys/mtk-apu-pm.c | 633
> > +++++++++++++++++++++++
> >   4 files changed, 655 insertions(+)
> >   create mode 100644 drivers/soc/mediatek/apusys/Makefile
> >   create mode 100644 drivers/soc/mediatek/apusys/mtk-apu-pm.c
> > 
> > diff --git a/drivers/soc/mediatek/Kconfig
> > b/drivers/soc/mediatek/Kconfig
> > index fdd8bc08569e..d9bac2710494 100644
> > --- a/drivers/soc/mediatek/Kconfig
> > +++ b/drivers/soc/mediatek/Kconfig
> > @@ -5,6 +5,25 @@
> >   menu "MediaTek SoC drivers"
> >   	depends on ARCH_MEDIATEK || COMPILE_TEST
> >   
> > +config MTK_APUSYS
> > +	bool "MediaTek APUSYS Support"
> > +	select REGMAP
> > +	help
> > +	  Say yes here to add support for the MediaTek AI Processing
> > Unit
> > +	  Subsystem (APUSYS).
> > +	  The APUSYS is a proprietary hardware in SoC to support AI
> > +	  operations.
> > +
> 
> If these config entries are for files in subfolder apusys/, then you
> should move
> 
> them to drivers/soc/mediatek/apusys/Kconfig and make sure that said
> Kconfig will
> 
> contain a "if MTK_APUSYS" as to include the subconfigs only if the
> parent one is
> 
> declared =y.
> 
> > +config MTK_APU_PM
> > +	bool "MediaTek APU power management support"
> 
> Can we use tristate here? This doesn't look like being a boot-
> critical driver,
> so it can as well be loaded after rootfs init.
> Permitting to compile this as a module will also shrink the kernel
> image size
> and that's important when you build a generic kernel image, and
> that's for both
> the MediaTek SoCs case (older SoCs don't need this driver) and for
> others.
> 
> > +	select REGMAP
> > +	select PM_GENERIC_DOMAINS if PM
> > +	help
> > +	  Say yes here to add support for power management control
> > +	  to Mediatek AI Processing Unit Subsystem (APUSYS).
> > +	  APU power domain shall be enabled before accessing the
> > +	  internal sub modules.
> > +
> >   config MTK_CMDQ
> >   	tristate "MediaTek CMDQ Support"
> >   	depends on ARCH_MEDIATEK || COMPILE_TEST
> > diff --git a/drivers/soc/mediatek/Makefile
> > b/drivers/soc/mediatek/Makefile
> > index 90270f8114ed..e46e7a3c21e7 100644
> > --- a/drivers/soc/mediatek/Makefile
> > +++ b/drivers/soc/mediatek/Makefile
> > @@ -1,4 +1,5 @@
> >   # SPDX-License-Identifier: GPL-2.0-only
> > +obj-$(CONFIG_MTK_APUSYS) += apusys/
> >   obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq-helper.o
> >   obj-$(CONFIG_MTK_DEVAPC) += mtk-devapc.o
> >   obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
> > diff --git a/drivers/soc/mediatek/apusys/Makefile
> > b/drivers/soc/mediatek/apusys/Makefile
> > new file mode 100644
> > index 000000000000..8821c0f0b7b7
> > --- /dev/null
> > +++ b/drivers/soc/mediatek/apusys/Makefile
> > @@ -0,0 +1,2 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +obj-$(CONFIG_MTK_APU_PM) += mtk-apu-pm.o
> > diff --git a/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> > b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> > new file mode 100644
> > index 000000000000..828aa9eb6b37
> > --- /dev/null
> > +++ b/drivers/soc/mediatek/apusys/mtk-apu-pm.c
> > @@ -0,0 +1,633 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (c) 2021 MediaTek Inc.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/clk-provider.h>
> > +#include <linux/init.h>
> > +#include <linux/io.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_clk.h>
> > +#include <linux/of_device.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_domain.h>
> > +#include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#define APU_PD_IPUIF_HW_CG		BIT(0)
> > +#define APU_PD_RPC_AUTO_BUCK		BIT(1)
> > +#define APU_PD_CAPS(_pd, _x)		((_pd)->data->caps &
> > (_x))
> > +
> > +#define MTK_POLL_DELAY_US		10
> > +#define MTK_POLL_TIMEOUT		USEC_PER_SEC
> > +
> > +/* rpc_top_con*/
> > +#define SLEEP_REQ		BIT(0)
> > +#define APU_BUCK_ELS_EN		BIT(3)
> > +
> > +/*conn_clr, conn1_clr, vcore_clr */
> > +#define CG_CLR	(0xFFFFFFFF)
> > +
> > +/* mt8192 rpc_sw_type */
> > +#define MT8192_RPC_SW_TYPE0	(0x200)
> > +#define MT8192_RPC_SW_TYPE1	(0x210)
> > +#define MT8192_RPC_SW_TYPE2	(0x220)
> > +#define MT8192_RPC_SW_TYPE3	(0x230)
> > +#define MT8192_RPC_SW_TYPE4	(0x240)
> > +#define MT8192_RPC_SW_TYPE6	(0x260)
> > +#define MT8192_RPC_SW_TYPE7	(0x270)
> 
> What about...
> 
> /* mt8192 rpc_sw_type (0..7) */
> #define MT8192_RPC_SW_TYPE(x)	(0x200 + (x * 0x10))
> 
> > +
> > +/* rpc_sw_type*/
> > +static const struct reg_sequence mt8192_rpc_sw_type[] = {
> > +	{ MT8192_RPC_SW_TYPE0, 0xFF },
> > +	{ MT8192_RPC_SW_TYPE2, 0x7 },
> > +	{ MT8192_RPC_SW_TYPE3, 0x7 },
> > +	{ MT8192_RPC_SW_TYPE6, 0x3 },
> > +};
> > +
> > +struct apu_top_domain {
> > +	u32 spm_ext_buck_iso;
> > +	u32 spm_ext_buck_iso_mask;
> > +	u32 spm_cross_wake_m01;
> > +	u32 wake_apu;
> > +	u32 spm_other_pwr;
> > +	u32 pwr_status;
> > +	u32 conn_clr;
> > +	u32 conn1_clr;
> > +	u32 vcore_clr;
> > +	u32 rpc_top_con;
> > +	u32 rpc_top_con_init_mask;
> > +	u32 rpc_top_sel;
> > +	u32 rpc_top_intf_pwr_rdy;
> > +	u32 pwr_rdy;
> > +	const struct reg_sequence *rpc_sw_type;
> > +	int rpc_sw_ntype;
> 
> For the sake of clarity and readability, I would name this one
> num_rpc_sw_type
> or num_rpc_sw, which also keeps naming consistency across this file,
> since later
> in this file, you're doing the same with domains_data.
> 
> > +};
> > +
> > +static struct apu_top_domain mt8192_top_reg = {
> > +	.spm_ext_buck_iso = 0x39C,
> > +	.spm_ext_buck_iso_mask = 0x21,
> > +	.spm_cross_wake_m01 = 0x670,
> > +	.wake_apu = BIT(0),
> > +	.spm_other_pwr = 0x178,
> > +	.pwr_status = BIT(5),
> > +	.conn_clr = 0x008,
> 
> Please drop leading zeroes. 0x8 here.
> 
> > +	.vcore_clr = 0x008,
> 
> 0x8
> 
> > +	.rpc_top_con = 0x000,
> 
> 0
> 
> > +	.rpc_top_con_init_mask = 0x49E,
> > +	.rpc_top_sel = 0x004,
> 
> 0x4
> 
> > +	.rpc_top_intf_pwr_rdy = 0x044,
> 
> 0x44
> 
> > +	.pwr_rdy = BIT(0),
> > +	.rpc_sw_type = mt8192_rpc_sw_type,
> > +	.rpc_sw_ntype = ARRAY_SIZE(mt8192_rpc_sw_type),
> > +};
> > +
> > +struct apusys {
> > +	struct device *dev;
> > +	struct regmap *scpsys;
> > +	struct regmap *conn;
> > +	struct regmap *conn1;
> > +	struct regmap *vcore;
> > +	struct regmap *rpc;
> > +	struct regulator *vsram_supply;
> > +	const struct apu_pm_data *data;
> > +	struct genpd_onecell_data pd_data;
> > +	struct generic_pm_domain *domains[];
> > +};
> > +
> > +struct apu_domain {
> > +	struct generic_pm_domain genpd;
> > +	const struct apu_domain_data *data;
> > +	struct apusys *apusys;
> > +	struct regulator *domain_supply;
> > +	int num_clks;
> > +	struct clk_bulk_data *clks;
> > +	struct clk *clk_top_conn;
> > +	struct clk *clk_top_ipu_if;
> > +	struct clk *clk_off;
> > +	struct clk *clk_on_def;
> > +};
> > +
> > +struct apu_domain_data {
> > +	int domain_idx;
> > +	char *name;
> > +	struct apu_top_domain *topd;
> > +	u8 caps;
> > +};
> > +
> > +struct apu_pm_data {
> > +	const struct apu_domain_data *domains_data;
> > +	int num_domains;
> > +};
> > +
> > +#define to_apu_domain(gpd) container_of(gpd, struct apu_domain,
> > genpd)
> > +
> > +static int apu_top_init_hw(struct apu_domain *pd)
> > +{
> > +	struct apusys *apusys = pd->apusys;
> > +	int ret;
> > +
> > +	/*
> > +	 * set memory type to PD or sleep group
> > +	 * sw_type register for each memory group, set to PD mode
> > default
> > +	 */
> > +	if (pd->data->topd->rpc_sw_ntype) {
> > +		ret = regmap_register_patch(apusys->rpc,
> > +					    pd->data->topd-
> > >rpc_sw_type,
> > +					    pd->data->topd-
> > >rpc_sw_ntype);
> > +		if (ret < 0) {
> > +			dev_err(apusys->dev, "Failed to rpc patch:
> > %d\n", ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	/* mask RPC IRQ and bypass WFI */
> > +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> > +			pd->data->topd->rpc_top_con_init_mask);
> 
> This function may return a failure.. and this is not a performance-
> critical case,
> nor a case in which it's 100% granted that this won't fail: you're
> calling this
> at initialization phase (when you're adding the domain), so if
> there's anything
> that would make this fail, this is definitely the right place to
> check for that.
> 
> Please check the return value.
> 
> > +
> > +	if (APU_PD_CAPS(pd, APU_PD_RPC_AUTO_BUCK))
> > +		regmap_set_bits(apusys->rpc,
> > +				pd->data->topd->rpc_top_con,
> > APU_BUCK_ELS_EN);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct apu_domain_data apu_domain_data_mt8192[] = {
> > +	{
> > +		.domain_idx = 0,
> > +		.name = "apu-top",
> > +		.caps = APU_PD_IPUIF_HW_CG,
> > +		.topd = &mt8192_top_reg,
> > +	}
> > +};
> > +
> > +static const struct apu_pm_data mt8192_apu_pm_data = {
> > +	.domains_data = apu_domain_data_mt8192,
> > +	.num_domains = ARRAY_SIZE(apu_domain_data_mt8192),
> > +};
> > +
> > +static int apu_top_power_on(struct generic_pm_domain *genpd)
> > +{
> > +	struct apu_domain *pd = to_apu_domain(genpd);
> > +	struct apusys *apusys = pd->apusys;
> > +	int ret, tmp;
> > +
> > +	if (apusys->vsram_supply) {
> > +		ret = regulator_enable(apusys->vsram_supply);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	if (pd->domain_supply) {
> > +		ret = regulator_enable(pd->domain_supply);
> > +		if (ret < 0)
> > +			goto err_regulator;
> > +	}
> > +
> > +	regmap_clear_bits(apusys->scpsys, pd->data->topd-
> > >spm_ext_buck_iso,
> > +			  pd->data->topd->spm_ext_buck_iso_mask);
> > +
> > +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_sel,
> > +			pd->data->topd->rpc_top_con_init_mask);
> > +
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		ret = clk_prepare_enable(pd->clk_top_conn);
> > +		if (ret) {
> > +			dev_err(apusys->dev, "Failed enable top_conn
> > clk\n");
> > +			goto err_clk;
> > +		}
> > +
> > +		ret = clk_set_parent(pd->clk_top_ipu_if, pd-
> > >clk_on_def);
> > +		if (ret) {
> > +			dev_err(apusys->dev, "Failed set ipu_if
> > mux\n");
> > +			goto err_clk;
> > +		}
> > +
> > +		/* The ipu_if clock is gatting by HW. Only enable once.
> > */
> 
> typo: gating
> 
> > +		if (!__clk_is_enabled(pd->clk_top_ipu_if)) {
> > +			ret = clk_prepare_enable(pd->clk_top_ipu_if);
> > +			if (ret) {
> > +				dev_err(apusys->dev, "Failed enable
> > ipu_if\n");
> > +				goto err_clk;
> > +			}
> > +		}
> > +	} else {
> > +		ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks);
> > +		if (ret)
> > +			goto err_clk;
> > +	}
> > +
> > +	regmap_set_bits(apusys->scpsys,
> > +			pd->data->topd->spm_cross_wake_m01,
> > +			pd->data->topd->wake_apu);
> > +
> > +	ret = regmap_read_poll_timeout(apusys->scpsys,
> > +				       pd->data->topd->spm_other_pwr,
> > +				       tmp,
> > +				       (tmp & pd->data->topd-
> > >pwr_status) ==
> > +				       pd->data->topd->pwr_status,
> > +				       MTK_POLL_DELAY_US,
> > MTK_POLL_TIMEOUT);
> > +	if (ret < 0) {
> > +		dev_err(apusys->dev, "apu top on wait SPM PWR_ACK !=
> > 0\n");
> > +		goto err_clk;
> > +	}
> > +
> > +	ret = regmap_read_poll_timeout(apusys->rpc,
> > +				       pd->data->topd-
> > >rpc_top_intf_pwr_rdy,
> > +				       tmp, (tmp & pd->data->topd-
> > >pwr_rdy) ==
> > +				       pd->data->topd->pwr_rdy,
> > +				       MTK_POLL_DELAY_US,
> > MTK_POLL_TIMEOUT);
> > +	if (ret < 0) {
> > +		dev_err(apusys->dev, "apu top on wait RPC PWR_RDY !=
> > 0\n");
> > +		goto err_clk;
> > +	}
> > +
> > +	if (apusys->vcore)
> > +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr,
> > CG_CLR);
> > +
> > +	if (apusys->conn)
> > +		regmap_write(apusys->conn, pd->data->topd->conn_clr,
> > CG_CLR);
> > +
> > +	if (apusys->conn1)
> > +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr,
> > CG_CLR);
> > +
> > +	return 0;
> > +
> > +err_clk:
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		clk_disable_unprepare(pd->clk_top_conn);
> > +		clk_disable_unprepare(pd->clk_top_ipu_if);
> > +	} else {
> > +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> > +	}
> > +	if (pd->domain_supply)
> > +		ret = regulator_disable(pd->domain_supply);
> > +err_regulator:
> > +	if (apusys->vsram_supply)
> > +		ret = regulator_disable(apusys->vsram_supply);
> > +
> > +	return ret;
> > +}
> > +
> > +static int apu_top_power_off(struct generic_pm_domain *genpd)
> > +{
> > +	struct apu_domain *pd = to_apu_domain(genpd);
> > +	struct apusys *apusys = pd->apusys;
> > +	int ret, tmp;
> > +
> > +	if (apusys->vcore)
> > +		regmap_write(apusys->vcore, pd->data->topd->vcore_clr,
> > CG_CLR);
> > +
> > +	if (apusys->conn)
> > +		regmap_write(apusys->conn, pd->data->topd->conn_clr,
> > CG_CLR);
> > +
> > +	if (apusys->conn1)
> > +		regmap_write(apusys->conn1, pd->data->topd->conn1_clr,
> > CG_CLR);
> > +
> > +	regmap_clear_bits(apusys->scpsys,
> > +			  pd->data->topd->spm_cross_wake_m01,
> > +			  pd->data->topd->wake_apu);
> > +
> > +	regmap_set_bits(apusys->rpc, pd->data->topd->rpc_top_con,
> > SLEEP_REQ);
> > +
> > +	ret = regmap_read_poll_timeout(apusys->rpc,
> > +				       pd->data->topd-
> > >rpc_top_intf_pwr_rdy,
> > +				       tmp,
> > +				       (tmp & pd->data->topd->pwr_rdy)
> > == 0x0,
> > +				       MTK_POLL_DELAY_US,
> > MTK_POLL_TIMEOUT);
> > +	if (ret < 0) {
> > +		dev_err(apusys->dev, "apu top off wait RPC PWR_RDY !=
> > 0\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = regmap_read_poll_timeout(apusys->scpsys,
> > +				       pd->data->topd->spm_other_pwr,
> > tmp,
> > +				       (tmp & pd->data->topd-
> > >pwr_status) == 0x0,
> > +				       MTK_POLL_DELAY_US,
> > MTK_POLL_TIMEOUT);
> > +	if (ret < 0) {
> > +		dev_err(apusys->dev, "apu top off wait SPM PWR_ACK !=
> > 0\n");
> > +		return ret;
> > +	}
> > +
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		clk_disable_unprepare(pd->clk_top_conn);
> > +		ret = clk_set_parent(pd->clk_top_ipu_if, pd->clk_off);
> > +		if (ret) {
> > +			dev_err(apusys->dev, "Failed set ipu_if
> > rate\n");
> > +			return ret;
> > +		}
> > +	} else {
> > +		clk_bulk_disable_unprepare(pd->num_clks, pd->clks);
> > +	}
> > +
> > +	regmap_set_bits(apusys->scpsys,
> > +			pd->data->topd->spm_ext_buck_iso,
> > +			pd->data->topd->spm_ext_buck_iso_mask);
> > +
> > +	if (apusys->vsram_supply) {
> > +		ret = regulator_disable(apusys->vsram_supply);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	if (pd->domain_supply) {
> > +		ret = regulator_disable(pd->domain_supply);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct generic_pm_domain *apu_add_one_domain(struct apusys
> > *apusys,
> > +						    struct device_node
> > *node)
> > +{
> > +	const struct apu_domain_data *domain_data;
> > +	struct apu_domain *pd;
> > +	int ret;
> > +	u32 id;
> > +	int i;
> > +	struct clk *clk;
> > +
> > +	ret = of_property_read_u32(node, "reg", &id);
> > +	if (ret) {
> > +		dev_dbg(apusys->dev, "%pOF: invalid reg: %d\n", node,
> > ret);
> > +		return ERR_PTR(-EINVAL);
> 
> Just use the error value in 'ret', instead of forcing -EINVAL here.
> 
> > +	}
> > +
> > +	if (id >= apusys->data->num_domains) {
> > +		dev_dbg(apusys->dev, "%pOF: invalid id %d\n", node,
> > id);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	domain_data = &apusys->data->domains_data[id];
> > +
> > +	pd = devm_kzalloc(apusys->dev, sizeof(*pd), GFP_KERNEL);
> > +	if (!pd)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	pd->data   = domain_data;
> > +	pd->apusys = apusys;
> > +
> > +	pd->domain_supply = devm_regulator_get_optional(apusys->dev,
> > "domain");
> 
> This may return -EPROBE_DEFER: in that case, you don't want to ignore
> the supply,
> but defer probing this driver until said supply becomes available so,
> if that
> happens, you should return.
> 
> > +	if (IS_ERR(pd->domain_supply))
> > +		pd->domain_supply = NULL;
> > +
> > +	/*
> > +	 * For HW using ipu_if, the clock is switched to 26M
> > +	 * when power down top domain and switch to default clock rate
> > +	 * before power on.
> > +	 */
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		pd->clk_top_conn = of_clk_get_by_name(node,
> > "clk_top_conn");
> > +		if (IS_ERR(pd->clk_top_conn)) {
> > +			dev_err(apusys->dev, "Fail to get clk_top_conn
> > clock\n");
> > +			ret = PTR_ERR(pd->clk_top_conn);
> > +			goto err_put_clocks;
> > +		}
> > +
> > +		pd->clk_top_ipu_if = of_clk_get_by_name(node,
> > "clk_top_ipu_if");
> > +		if (IS_ERR(pd->clk_top_ipu_if)) {
> > +			dev_err(apusys->dev, "Fail to get
> > clk_top_ipu_if clock\n");
> > +			ret = PTR_ERR(pd->clk_top_ipu_if);
> > +			goto err_put_clocks;
> > +		}
> > +
> > +		pd->clk_off = of_clk_get_by_name(node, "clk_off");
> > +		if (IS_ERR(pd->clk_off)) {
> > +			dev_err(apusys->dev, "Fail to get clk_off
> > clock\n");
> > +			ret = PTR_ERR(pd->clk_off);
> > +			goto err_put_clocks;
> > +		}
> > +
> > +		pd->clk_on_def = of_clk_get_by_name(node,
> > "clk_on_default");
> > +		if (IS_ERR(pd->clk_on_def)) {
> > +			dev_err(apusys->dev, "Fail to get
> > clk_on_default clock\n");
> > +			ret = PTR_ERR(pd->clk_on_def);
> > +			goto err_put_clocks;
> > +		}
> > +	} else {
> > +		pd->num_clks = of_clk_get_parent_count(node);
> > +		if (pd->num_clks > 0) {
> > +			pd->clks = devm_kcalloc(apusys->dev, pd-
> > >num_clks,
> > +						sizeof(*pd->clks),
> > GFP_KERNEL);
> > +			if (!pd->clks)
> > +				return ERR_PTR(-ENOMEM);
> > +		}
> > +
> > +		for (i = 0; i < pd->num_clks; i++) {
> > +			clk = of_clk_get(node, i);
> > +			if (IS_ERR(clk)) {
> > +				ret = PTR_ERR(clk);
> > +				dev_dbg(apusys->dev,
> > +					"%pOF: failed to get clk at
> > index %d: %d\n",
> > +					node, i, ret);
> > +				goto err_put_clocks;
> > +			}
> > +			pd->clks[i].clk = clk;
> > +		}
> > +	}
> > +
> > +	if (apusys->domains[id]) {
> > +		ret = -EINVAL;
> 
> Error: X already exists -> -EEXIST
> 
> > +		dev_err(apusys->dev, "domain id %d already exists\n",
> > id);
> > +		goto err_put_clocks;
> > +	}
> > +
> > +	/* set rpc hw init status */
> > +	ret = apu_top_init_hw(pd);
> > +	if (ret < 0) {
> > +		dev_dbg(apusys->dev, "top init fail ret = %d\n", ret);
> > +		goto err_put_clocks;
> > +	}
> > +
> > +	if (!pd->data->name)
> > +		pd->genpd.name = node->name;
> > +	else
> > +		pd->genpd.name = pd->data->name;
> > +	pd->genpd.power_off = apu_top_power_off;
> > +	pd->genpd.power_on = apu_top_power_on;
> > +
> > +	/*
> > +	 * Initially turn on all domains to make the domains usable
> > +	 * with !CONFIG_PM and to get the hardware in sync with the
> > +	 * software.  The unused domains will be switched off during
> > +	 * late_init time.
> > +	 */
> > +	ret = pd->genpd.power_on(&pd->genpd);
> > +	if (ret < 0) {
> > +		dev_dbg(apusys->dev, "%pOF: power on domain fail:
> > %d\n",
> > +			node, ret);
> > +		goto err_put_clocks;
> > +	}
> > +
> > +	pm_genpd_init(&pd->genpd, NULL, false);
> 
> This function may return failure: please check the return value.
> 
> > +
> > +	apusys->domains[id] = &pd->genpd;
> > +
> > +	return apusys->pd_data.domains[id];
> > +
> > +err_put_clocks:
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		clk_put(pd->clk_top_conn);
> > +		clk_put(pd->clk_top_ipu_if);
> > +		clk_put(pd->clk_off);
> > +		clk_put(pd->clk_on_def);
> > +	} else {
> > +		clk_bulk_put(pd->num_clks, pd->clks);
> > +	}
> > +	return ERR_PTR(ret);
> > +}
> > +
> > +static void apu_remove_one_domain(struct apu_domain *pd)
> > +{
> > +	int ret;
> > +
> > +	if (pd->genpd.power_off)
> > +		pd->genpd.power_off(&pd->genpd);
> > +
> > +	/*
> > +	 * We're in the error cleanup already, so we only complain,
> > +	 * but won't emit another error on top of the original one.
> > +	 */
> > +	ret = pm_genpd_remove(&pd->genpd);
> > +	if (ret < 0)
> > +		dev_dbg(pd->apusys->dev,
> > +			"Remove domain '%s' : %d - state may be
> > inconsistent\n",
> > +			pd->genpd.name, ret);
> > +
> > +	if (APU_PD_CAPS(pd, APU_PD_IPUIF_HW_CG)) {
> > +		clk_put(pd->clk_top_conn);
> > +		clk_put(pd->clk_top_ipu_if);
> > +		clk_put(pd->clk_off);
> > +		clk_put(pd->clk_on_def);
> > +	} else {
> > +		clk_bulk_put(pd->num_clks, pd->clks);
> > +	}
> > +}
> > +
> > +static void apu_domain_cleanup(struct apusys *apusys)
> > +{
> > +	struct generic_pm_domain *genpd;
> > +	struct apu_domain *pd;
> > +	int i;
> > +
> > +	for (i = apusys->pd_data.num_domains - 1; i >= 0; i--) {
> > +		genpd = apusys->pd_data.domains[i];
> > +		if (genpd) {
> > +			pd = to_apu_domain(genpd);
> > +			apu_remove_one_domain(pd);
> > +		}
> > +	}
> > +}
> > +
> > +static const struct of_device_id apu_pm_of_match[] = {
> > +	{
> > +		.compatible = "mediatek,mt8192-apu-pm",
> > +		.data = &mt8192_apu_pm_data,
> > +	},
> > +	{ }
> > +};
> > +
> > +static int apu_pm_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct device_node *np = dev->of_node;
> > +	const struct apu_pm_data *data;
> > +	struct device_node *node;
> > +	struct apusys *apusys;
> > +	int ret;
> > +
> > +	data = of_device_get_match_data(&pdev->dev);
> > +	if (!data) {
> > +		dev_dbg(dev, "no power domain data\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	apusys = devm_kzalloc(dev,
> > +			      struct_size(apusys, domains, data-
> > >num_domains),
> > +			      GFP_KERNEL);
> > +	if (!apusys)
> > +		return -ENOMEM;
> > +
> > +	apusys->dev = dev;
> > +	apusys->data = data;
> > +	apusys->pd_data.domains = apusys->domains;
> > +	apusys->pd_data.num_domains = data->num_domains;
> > +
> > +	apusys->vsram_supply = devm_regulator_get_optional(dev,
> > "vsram");
> > +	if (IS_ERR(apusys->vsram_supply)) {
> > +		ret = PTR_ERR(apusys->vsram_supply);
> > +		if (ret != -EPROBE_DEFER) {
> > +			dev_err(dev, "vsram_supply fail, ret=%d", ret);
> > +			apusys->vsram_supply = NULL;
> > +		}
> > +		return ret;
> > +	}
> > +
> > +	/* rpc */
> > +	apusys->rpc = syscon_node_to_regmap(np);
> > +	if (IS_ERR(apusys->rpc)) {
> > +		dev_err(dev, "Unable to get rpc\n");
> > +		return PTR_ERR(apusys->rpc);
> > +	}
> > +
> > +	/* scpsys */
> > +	apusys->scpsys = syscon_regmap_lookup_by_phandle(np,
> > "mediatek,scpsys");
> > +	if (IS_ERR(apusys->scpsys)) {
> > +		dev_err(dev, "Unable to get scpsys\n");
> > +		return PTR_ERR(apusys->scpsys);
> > +	}
> > +
> > +	/* apusys conn */
> > +	apusys->conn = syscon_regmap_lookup_by_phandle_optional(np,
> > "mediatek,apu-conn");
> > +	if (IS_ERR(apusys->conn))
> > +		dev_info(dev, "No optional phandle apu-conn\n");
> 
> I take it as if some platforms may be expected to not have any
> optional phandle
> for that (and for the others): in such case, this would result in
> being a useless
> message. Please use dev_dbg() here.
> 
> > +
> > +	/* apusys conn1 */
> > +	apusys->conn1 = syscon_regmap_lookup_by_phandle_optional(np,
> > "mediatek,apu-conn1");
> > +	if (IS_ERR(apusys->conn1))
> > +		dev_info(dev, "No optional phandle apu-conn1\n");
> > +
> > +	/* apusys vcore */
> > +	apusys->vcore = syscon_regmap_lookup_by_phandle_optional(np,
> > "mediatek,apu-vcore");
> > +	if (IS_ERR(apusys->vcore))
> > +		dev_info(dev, "No optional phandle apu-vcore\n");
> > +
> > +	for_each_available_child_of_node(np, node) {
> > +		struct generic_pm_domain *domain;
> > +
> > +		domain = apu_add_one_domain(apusys, node);
> > +		if (IS_ERR(domain)) {
> > +			ret = PTR_ERR(domain);
> > +			of_node_put(node);
> > +			goto err_cleanup_domains;
> > +		}
> > +	}
> > +
> > +	ret = of_genpd_add_provider_onecell(np, &apusys->pd_data);
> > +	if (ret) {
> > +		dev_dbg(dev, "failed to add provider: %d\n", ret);
> > +		goto err_cleanup_domains;
> > +	}
> > +
> > +	return 0;
> > +
> > +err_cleanup_domains:
> > +	apu_domain_cleanup(apusys);
> > +	return ret;
> > +}
> > +
> > +static struct platform_driver apu_pm_driver = {
> > +	.probe = apu_pm_probe,
> 
> In this driver, you already have the means to remove the domains and
> cleanup the
> state... it would probably be trivial to add a .remove handler.
> 
> What do you think about that?
> Also, is there be any reason for which removing this driver during
> runtime
> would be unsafe?
> 
> In my opinion, it would be beneficial to have a remove handler also
> in the
> development usecase, as you'd be able to just recompile this module,
> remove and
> reinsert it to test new changes, without the need of copying an
> entire new
> kernel image and rebooting the device.
> 
> > +	.driver = {
> > +		.name = "mtk-apu-pm",
> > +		.suppress_bind_attrs = true,
> > +		.of_match_table = apu_pm_of_match,
> > +	},
> > +};
> > +builtin_platform_driver(apu_pm_driver);
> > 
> 
> MODULE_LICENSE() is missing! Please add it.
> Also, as said in the Kconfig comment, this driver can probably be a
> module, in
> which case... module_platform_driver(apu_pm_driver);
> 
> Thanks,
> - Angelo
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2021-11-25  6:43 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-23 10:13 [PATCH v3 0/7] Add Support for MediaTek MT8192 APU Power Flora Fu
2021-10-23 10:13 ` Flora Fu
2021-10-23 10:13 ` Flora Fu
2021-10-23 10:13 ` [PATCH v3 1/7] dt-bindings: clock: Add MT8192 APU clock bindings Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13 ` [PATCH v3 2/7] clk: mediatek: mt8192: Add APU clocks support Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13 ` [PATCH v3 3/7] dt-bindings: arm: mediatek: Add new document bindings for APU Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-27 21:12   ` Rob Herring
2021-10-27 21:12     ` Rob Herring
2021-10-27 21:12     ` Rob Herring
2021-10-23 10:13 ` [PATCH v3 4/7] dt-bindings: soc: mediatek: apusys: Add new document for APU power domain Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-27 21:14   ` Rob Herring
2021-10-27 21:14     ` Rob Herring
2021-10-27 21:14     ` Rob Herring
2021-10-23 10:13 ` [PATCH v3 5/7] soc: mediatek: apu: Add apusys and add apu power domain driver Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-26  9:57   ` AngeloGioacchino Del Regno
2021-10-26  9:57     ` AngeloGioacchino Del Regno
2021-10-26  9:57     ` AngeloGioacchino Del Regno
2021-11-25  6:41     ` Flora.Fu
2021-11-25  6:41       ` Flora.Fu
2021-10-23 10:13 ` [PATCH v3 6/7] arm64: dts: mt8192: Add APU node Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13 ` [PATCH v3 7/7] arm64: dts: mt8192: Add APU power domain node Flora Fu
2021-10-23 10:13   ` Flora Fu
2021-10-23 10:13   ` Flora Fu

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.