LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* PM / AVS: SVS: Introduce SVS engine
@ 2019-04-30 11:20 Roger Lu
  2019-04-30 11:20 ` [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings Roger Lu
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Roger Lu @ 2019-04-30 11:20 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Viresh Kumar, Stephen Boyd,
	Rafael J . Wysocki
  Cc: Mark Rutland, Nishanth Menon, Kevin Hilman, Roger Lu, devicetree,
	linux-arm-kernel, linux-mediatek, linux-kernel, linux-pm

The SVS (Smart Voltage Scaling) engine is a piece of hardware which is
used to calculate optimized voltage values of several power domains, e.g.
CPU/GPU/CCI, according to chip process corner, temperatures, and other
factors. Then DVFS driver could apply those optimized voltage values to
reduce power consumption.

Roger Lu (3):
  dt-bindings: soc: add mtk svs dt-bindings
  arm64: dts: mt8183: add svs device information
  PM / AVS: SVS: Introduce SVS engine

 .../devicetree/bindings/power/mtk-svs.txt     |   70 +
 arch/arm64/boot/dts/mediatek/mt8183-evb.dts   |   16 +
 arch/arm64/boot/dts/mediatek/mt8183.dtsi      |   46 +
 drivers/power/avs/Kconfig                     |   10 +
 drivers/power/avs/Makefile                    |    1 +
 drivers/power/avs/mtk_svs.c                   | 2084 +++++++++++++++++
 include/linux/power/mtk_svs.h                 |   23 +
 7 files changed, 2250 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/mtk-svs.txt
 create mode 100644 drivers/power/avs/mtk_svs.c
 create mode 100644 include/linux/power/mtk_svs.h

--
2.18.0



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

* [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings
  2019-04-30 11:20 PM / AVS: SVS: Introduce SVS engine Roger Lu
@ 2019-04-30 11:20 ` Roger Lu
  2019-04-30 20:31   ` Stephen Boyd
  2019-04-30 11:20 ` [RFC v1 2/3] arm64: dts: mt8183: add svs device information Roger Lu
  2019-04-30 11:20 ` [RFC v1 3/3] PM / AVS: SVS: Introduce SVS engine Roger Lu
  2 siblings, 1 reply; 11+ messages in thread
From: Roger Lu @ 2019-04-30 11:20 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Viresh Kumar, Stephen Boyd,
	Rafael J . Wysocki
  Cc: Mark Rutland, Nishanth Menon, Kevin Hilman, Roger Lu, devicetree,
	linux-arm-kernel, linux-mediatek, linux-kernel, linux-pm

Document the binding for enabling mtk svs on MediaTek SoC.

Signed-off-by: Roger Lu <roger.lu@mediatek.com>
---
 .../devicetree/bindings/power/mtk-svs.txt     | 70 +++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/mtk-svs.txt

diff --git a/Documentation/devicetree/bindings/power/mtk-svs.txt b/Documentation/devicetree/bindings/power/mtk-svs.txt
new file mode 100644
index 000000000000..355329db74ba
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/mtk-svs.txt
@@ -0,0 +1,70 @@
+* Mediatek Smart Voltage Scaling (MTK SVS)
+
+This describes the device tree binding for the MTK SVS controller
+which helps provide the optimized CPU/GPU/CCI voltages. This device also
+needs thermal data to calculate thermal slope for accurately compensate
+the voltages when temperature change.
+
+Required properties:
+- compatible:
+  - "mediatek,mt8183-svs" : For MT8183 family of SoCs
+- reg: Address range of the MTK SVS controller.
+- interrupts: IRQ for the MTK SVS controller.
+- clocks, clock-names: Clocks needed for the svs controller. required
+                       clocks are:
+		       "main_clk": Main clock needed for register access
+- nvmem-cells: Phandle to the calibration data provided by a nvmem device.
+- nvmem-cell-names: Should be "svs-calibration-data" and "calibration-data"
+- svs_xxx: Phandle of svs_bank device for controlling corresponding opp
+           table and power-domains.
+- vxxx-supply: Phandle to each regulator. vxxx can be "vcpu_little",
+	       "vcpu_big", "vcci" and "vgpu".
+
+Example:
+
+	svs: svs@1100b000 {
+		compatible = "mediatek,mt8183-svs";
+		reg = <0 0x1100b000 0 0x1000>;
+		interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW 0>;
+		clocks = <&infracfg CLK_INFRA_THERM>;
+		clock-names = "main_clk";
+		nvmem-cells = <&svs_calibration>, <&thermal_calibration>;
+		nvmem-cell-names = "svs-calibration-data", "calibration-data";
+
+		svs_cpu_little: svs_cpu_little {
+			compatible = "mediatek,mt8183-svs-cpu-little";
+			operating-points-v2 = <&cluster0_opp>;
+		};
+
+		svs_cpu_big: svs_cpu_big {
+			compatible = "mediatek,mt8183-svs-cpu-big";
+			operating-points-v2 = <&cluster1_opp>;
+		};
+
+		svs_cci: svs_cci {
+			compatible = "mediatek,mt8183-svs-cci";
+			operating-points-v2 = <&cluster2_opp>;
+		};
+
+		svs_gpu: svs_gpu {
+			compatible = "mediatek,mt8183-svs-gpu";
+			power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
+			operating-points-v2 = <&gpu_opp_table>;
+		};
+	};
+
+	&svs_cpu_little {
+		vcpu_little-supply = <&mt6358_vproc12_reg>;
+	};
+
+	&svs_cpu_big {
+		vcpu_big-supply = <&mt6358_vproc11_reg>;
+	};
+
+	&svs_cci {
+		vcci-supply = <&mt6358_vproc12_reg>;
+	};
+
+	&svs_gpu {
+		vgpu-spply = <&mt6358_vgpu_reg>;
+	};
-- 
2.18.0


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

* [RFC v1 2/3] arm64: dts: mt8183: add svs device information
  2019-04-30 11:20 PM / AVS: SVS: Introduce SVS engine Roger Lu
  2019-04-30 11:20 ` [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings Roger Lu
@ 2019-04-30 11:20 ` Roger Lu
  2019-04-30 11:20 ` [RFC v1 3/3] PM / AVS: SVS: Introduce SVS engine Roger Lu
  2 siblings, 0 replies; 11+ messages in thread
From: Roger Lu @ 2019-04-30 11:20 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Viresh Kumar, Stephen Boyd,
	Rafael J . Wysocki
  Cc: Mark Rutland, Nishanth Menon, Kevin Hilman, Roger Lu, devicetree,
	linux-arm-kernel, linux-mediatek, linux-kernel, linux-pm

add pmic/clock/irq/efuse setting in svs noce

Signed-off-by: Roger Lu <roger.lu@mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8183-evb.dts | 16 +++++++
 arch/arm64/boot/dts/mediatek/mt8183.dtsi    | 46 +++++++++++++++++++++
 2 files changed, 62 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8183-evb.dts b/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
index 9b525597e5ec..42bdf4f3c0bc 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
@@ -26,6 +26,22 @@
 	};
 };
 
+&svs_cpu_little {
+	vcpu_little-supply = <&mt6358_vproc12_reg>;
+};
+
+&svs_cpu_big {
+	vcpu_big-supply = <&mt6358_vproc11_reg>;
+};
+
+&svs_cci {
+	vcci-supply = <&mt6358_vproc12_reg>;
+};
+
+&svs_gpu {
+	vgpu-spply = <&mt6358_vgpu_reg>;
+};
+
 &uart0 {
 	status = "okay";
 };
diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index 75c4881bbe5e..0ae00cf39d41 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -299,12 +299,58 @@
 			status = "disabled";
 		};
 
+		svs: svs@1100b000 {
+			compatible = "mediatek,mt8183-svs";
+			reg = <0 0x1100b000 0 0x1000>;
+			interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW 0>;
+			clocks = <&infracfg CLK_INFRA_THERM>;
+			clock-names = "main_clk";
+			nvmem-cells = <&svs_calibration>,
+				      <&thermal_calibration>;
+			nvmem-cell-names = "svs-calibration-data",
+					   "calibration-data";
+
+			svs_cpu_little: svs_cpu_little {
+				compatible = "mediatek,mt8183-svs-cpu-little";
+				operating-points-v2 = <&cluster0_opp>;
+			};
+
+			svs_cpu_big: svs_cpu_big {
+				compatible = "mediatek,mt8183-svs-cpu-big";
+				operating-points-v2 = <&cluster1_opp>;
+			};
+
+			svs_cci: svs_cci {
+				compatible = "mediatek,mt8183-svs-cci";
+				operating-points-v2 = <&cluster2_opp>;
+			};
+
+			svs_gpu: svs_gpu {
+				compatible = "mediatek,mt8183-svs-gpu";
+				power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
+				operating-points-v2 = <&gpu_opp_table>;
+			};
+		};
+
 		audiosys: syscon@11220000 {
 			compatible = "mediatek,mt8183-audiosys", "syscon";
 			reg = <0 0x11220000 0 0x1000>;
 			#clock-cells = <1>;
 		};
 
+		efuse: efuse@11f10000 {
+			compatible = "mediatek,efuse";
+			reg = <0 0x11f10000 0 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			svs_calibration: calib@580 {
+				reg = <0x580 0x64>;
+			};
+			thermal_calibration: calib@180 {
+				reg = <0x180 0xc>;
+			};
+		};
+
 		mfgcfg: syscon@13000000 {
 			compatible = "mediatek,mt8183-mfgcfg", "syscon";
 			reg = <0 0x13000000 0 0x1000>;
-- 
2.18.0


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

* [RFC v1 3/3] PM / AVS: SVS: Introduce SVS engine
  2019-04-30 11:20 PM / AVS: SVS: Introduce SVS engine Roger Lu
  2019-04-30 11:20 ` [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings Roger Lu
  2019-04-30 11:20 ` [RFC v1 2/3] arm64: dts: mt8183: add svs device information Roger Lu
@ 2019-04-30 11:20 ` Roger Lu
  2 siblings, 0 replies; 11+ messages in thread
From: Roger Lu @ 2019-04-30 11:20 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Viresh Kumar, Stephen Boyd,
	Rafael J . Wysocki
  Cc: Mark Rutland, Nishanth Menon, Kevin Hilman, Roger Lu, devicetree,
	linux-arm-kernel, linux-mediatek, linux-kernel, linux-pm

The SVS (Smart Voltage Scaling) engine is a piece of hardware which is
used to calculate optimized voltage values of several power domains, e.g.
CPU/GPU/CCI, according to chip process corner, temperatures, and other
factors. Then DVFS driver could apply those optimized voltage values to
reduce power consumption.

Signed-off-by: Roger Lu <roger.lu@mediatek.com>
---
 drivers/power/avs/Kconfig     |   10 +
 drivers/power/avs/Makefile    |    1 +
 drivers/power/avs/mtk_svs.c   | 2084 +++++++++++++++++++++++++++++++++
 include/linux/power/mtk_svs.h |   23 +
 4 files changed, 2118 insertions(+)
 create mode 100644 drivers/power/avs/mtk_svs.c
 create mode 100644 include/linux/power/mtk_svs.h

diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig
index a67eeace6a89..da9baf5b5a03 100644
--- a/drivers/power/avs/Kconfig
+++ b/drivers/power/avs/Kconfig
@@ -18,3 +18,13 @@ config ROCKCHIP_IODOMAIN
           Say y here to enable support io domains on Rockchip SoCs. It is
           necessary for the io domain setting of the SoC to match the
           voltage supplied by the regulators.
+
+config MTK_SVS
+	bool "MediaTek Smart Voltage Scaling(SVS)"
+	depends on POWER_AVS && MTK_EFUSE
+	help
+	  The SVS engine is a piece of hardware which is used to calculate
+	  optimized voltage values of several power domains, e.g.
+	  CPU clusters/GPU/CCI, according to chip process corner, temperatures,
+	  and other factors. Then DVFS driver could apply those optimized voltage
+	  values to reduce power consumption.
diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile
index ba4c7bc69225..b487701cc4a6 100644
--- a/drivers/power/avs/Makefile
+++ b/drivers/power/avs/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_POWER_AVS_OMAP)		+= smartreflex.o
 obj-$(CONFIG_ROCKCHIP_IODOMAIN)		+= rockchip-io-domain.o
+obj-$(CONFIG_MTK_SVS)			+= mtk_svs.o
diff --git a/drivers/power/avs/mtk_svs.c b/drivers/power/avs/mtk_svs.c
new file mode 100644
index 000000000000..96a9331973a0
--- /dev/null
+++ b/drivers/power/avs/mtk_svs.c
@@ -0,0 +1,2084 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ */
+
+#define pr_fmt(fmt)	"[mtk_svs] " fmt
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_qos.h>
+#include <linux/pm_runtime.h>
+#include <linux/power/mtk_svs.h>
+#include <linux/proc_fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/thermal.h>
+#include <linux/uaccess.h>
+
+#define SVS_INIT01_VOLT_IGNORE		1
+#define SVS_INIT01_VOLT_INC_ONLY	2
+
+#define SVS_PHASE_INIT01		0
+#define SVS_PHASE_INIT02		1
+#define SVS_PHASE_MON			2
+#define SVS_PHASE_ERROR			3
+
+#define SVS_CPU_LITTLE			1
+#define SVS_CPU_BIG			2
+#define SVS_CCI				3
+#define SVS_GPU				4
+
+#define proc_fops_rw(name) \
+	static int name ## _proc_open(struct inode *inode,	\
+		struct file *file)				\
+	{							\
+		return single_open(file, name ## _proc_show,	\
+			PDE_DATA(inode));			\
+	}							\
+	static const struct file_operations name ## _proc_fops = {	\
+		.owner          = THIS_MODULE,				\
+		.open           = name ## _proc_open,			\
+		.read           = seq_read,				\
+		.llseek         = seq_lseek,				\
+		.release        = single_release,			\
+		.write          = name ## _proc_write,			\
+	}
+
+#define proc_fops_ro(name) \
+	static int name ## _proc_open(struct inode *inode,	\
+		struct file *file)				\
+	{							\
+		return single_open(file, name ## _proc_show,	\
+			PDE_DATA(inode));			\
+	}							\
+	static const struct file_operations name ## _proc_fops = {	\
+		.owner          = THIS_MODULE,				\
+		.open           = name ## _proc_open,			\
+		.read           = seq_read,				\
+		.llseek         = seq_lseek,				\
+		.release        = single_release,			\
+	}
+
+#define proc_entry(name)	{__stringify(name), &name ## _proc_fops}
+
+static DEFINE_SPINLOCK(mtk_svs_lock);
+struct mtk_svs;
+
+enum reg_index {
+	TEMPMONCTL0 = 0,
+	TEMPMONCTL1,
+	TEMPMONCTL2,
+	TEMPMONINT,
+	TEMPMONINTSTS,
+	TEMPMONIDET0,
+	TEMPMONIDET1,
+	TEMPMONIDET2,
+	TEMPH2NTHRE,
+	TEMPHTHRE,
+	TEMPCTHRE,
+	TEMPOFFSETH,
+	TEMPOFFSETL,
+	TEMPMSRCTL0,
+	TEMPMSRCTL1,
+	TEMPAHBPOLL,
+	TEMPAHBTO,
+	TEMPADCPNP0,
+	TEMPADCPNP1,
+	TEMPADCPNP2,
+	TEMPADCMUX,
+	TEMPADCEXT,
+	TEMPADCEXT1,
+	TEMPADCEN,
+	TEMPPNPMUXADDR,
+	TEMPADCMUXADDR,
+	TEMPADCEXTADDR,
+	TEMPADCEXT1ADDR,
+	TEMPADCENADDR,
+	TEMPADCVALIDADDR,
+	TEMPADCVOLTADDR,
+	TEMPRDCTRL,
+	TEMPADCVALIDMASK,
+	TEMPADCVOLTAGESHIFT,
+	TEMPADCWRITECTRL,
+	TEMPMSR0,
+	TEMPMSR1,
+	TEMPMSR2,
+	TEMPADCHADDR,
+	TEMPIMMD0,
+	TEMPIMMD1,
+	TEMPIMMD2,
+	TEMPMONIDET3,
+	TEMPADCPNP3,
+	TEMPMSR3,
+	TEMPIMMD3,
+	TEMPPROTCTL,
+	TEMPPROTTA,
+	TEMPPROTTB,
+	TEMPPROTTC,
+	TEMPSPARE0,
+	TEMPSPARE1,
+	TEMPSPARE2,
+	TEMPSPARE3,
+	TEMPMSR0_1,
+	TEMPMSR1_1,
+	TEMPMSR2_1,
+	TEMPMSR3_1,
+	DESCHAR,
+	TEMPCHAR,
+	DETCHAR,
+	AGECHAR,
+	DCCONFIG,
+	AGECONFIG,
+	FREQPCT30,
+	FREQPCT74,
+	LIMITVALS,
+	VBOOT,
+	DETWINDOW,
+	CONFIG,
+	TSCALCS,
+	RUNCONFIG,
+	SVSEN,
+	INIT2VALS,
+	DCVALUES,
+	AGEVALUES,
+	VOP30,
+	VOP74,
+	TEMP,
+	INTSTS,
+	INTSTSRAW,
+	INTEN,
+	CHKINT,
+	CHKSHIFT,
+	STATUS,
+	VDESIGN30,
+	VDESIGN74,
+	DVT30,
+	DVT74,
+	AGECOUNT,
+	SMSTATE0,
+	SMSTATE1,
+	CTL0,
+	DESDETSEC,
+	TEMPAGESEC,
+	CTRLSPARE0,
+	CTRLSPARE1,
+	CTRLSPARE2,
+	CTRLSPARE3,
+	CORESEL,
+	THERMINTST,
+	INTST,
+	THSTAGE0ST,
+	THSTAGE1ST,
+	THSTAGE2ST,
+	THAHBST0,
+	THAHBST1,
+	SPARE0,
+	SPARE1,
+	SPARE2,
+	SPARE3,
+	THSLPEVEB,
+	reg_num,
+};
+
+static const u32 svs_regs_v2[] = {
+	[TEMPMONCTL0]		= 0x000,
+	[TEMPMONCTL1]		= 0x004,
+	[TEMPMONCTL2]		= 0x008,
+	[TEMPMONINT]		= 0x00c,
+	[TEMPMONINTSTS]		= 0x010,
+	[TEMPMONIDET0]		= 0x014,
+	[TEMPMONIDET1]		= 0x018,
+	[TEMPMONIDET2]		= 0x01c,
+	[TEMPH2NTHRE]		= 0x024,
+	[TEMPHTHRE]		= 0x028,
+	[TEMPCTHRE]		= 0x02c,
+	[TEMPOFFSETH]		= 0x030,
+	[TEMPOFFSETL]		= 0x034,
+	[TEMPMSRCTL0]		= 0x038,
+	[TEMPMSRCTL1]		= 0x03c,
+	[TEMPAHBPOLL]		= 0x040,
+	[TEMPAHBTO]		= 0x044,
+	[TEMPADCPNP0]		= 0x048,
+	[TEMPADCPNP1]		= 0x04c,
+	[TEMPADCPNP2]		= 0x050,
+	[TEMPADCMUX]		= 0x054,
+	[TEMPADCEXT]		= 0x058,
+	[TEMPADCEXT1]		= 0x05c,
+	[TEMPADCEN]		= 0x060,
+	[TEMPPNPMUXADDR]	= 0x064,
+	[TEMPADCMUXADDR]	= 0x068,
+	[TEMPADCEXTADDR]	= 0x06c,
+	[TEMPADCEXT1ADDR]	= 0x070,
+	[TEMPADCENADDR]		= 0x074,
+	[TEMPADCVALIDADDR]	= 0x078,
+	[TEMPADCVOLTADDR]	= 0x07c,
+	[TEMPRDCTRL]		= 0x080,
+	[TEMPADCVALIDMASK]	= 0x084,
+	[TEMPADCVOLTAGESHIFT]	= 0x088,
+	[TEMPADCWRITECTRL]	= 0x08c,
+	[TEMPMSR0]		= 0x090,
+	[TEMPMSR1]		= 0x094,
+	[TEMPMSR2]		= 0x098,
+	[TEMPADCHADDR]		= 0x09c,
+	[TEMPIMMD0]		= 0x0a0,
+	[TEMPIMMD1]		= 0x0a4,
+	[TEMPIMMD2]		= 0x0a8,
+	[TEMPMONIDET3]		= 0x0b0,
+	[TEMPADCPNP3]		= 0x0b4,
+	[TEMPMSR3]		= 0x0b8,
+	[TEMPIMMD3]		= 0x0bc,
+	[TEMPPROTCTL]		= 0x0c0,
+	[TEMPPROTTA]		= 0x0c4,
+	[TEMPPROTTB]		= 0x0c8,
+	[TEMPPROTTC]		= 0x0cc,
+	[TEMPSPARE0]		= 0x0f0,
+	[TEMPSPARE1]		= 0x0f4,
+	[TEMPSPARE2]		= 0x0f8,
+	[TEMPSPARE3]		= 0x0fc,
+	[TEMPMSR0_1]		= 0x190,
+	[TEMPMSR1_1]		= 0x194,
+	[TEMPMSR2_1]		= 0x198,
+	[TEMPMSR3_1]		= 0x1b8,
+	[DESCHAR]		= 0xc00,
+	[TEMPCHAR]		= 0xc04,
+	[DETCHAR]		= 0xc08,
+	[AGECHAR]		= 0xc0c,
+	[DCCONFIG]		= 0xc10,
+	[AGECONFIG]		= 0xc14,
+	[FREQPCT30]		= 0xc18,
+	[FREQPCT74]		= 0xc1c,
+	[LIMITVALS]		= 0xc20,
+	[VBOOT]			= 0xc24,
+	[DETWINDOW]		= 0xc28,
+	[CONFIG]		= 0xc2c,
+	[TSCALCS]		= 0xc30,
+	[RUNCONFIG]		= 0xc34,
+	[SVSEN]			= 0xc38,
+	[INIT2VALS]		= 0xc3c,
+	[DCVALUES]		= 0xc40,
+	[AGEVALUES]		= 0xc44,
+	[VOP30]			= 0xc48,
+	[VOP74]			= 0xc4c,
+	[TEMP]			= 0xc50,
+	[INTSTS]		= 0xc54,
+	[INTSTSRAW]		= 0xc58,
+	[INTEN]			= 0xc5c,
+	[CHKINT]		= 0xc60,
+	[CHKSHIFT]		= 0xc64,
+	[STATUS]		= 0xc68,
+	[VDESIGN30]		= 0xc6c,
+	[VDESIGN74]		= 0xc70,
+	[DVT30]			= 0xc74,
+	[DVT74]			= 0xc78,
+	[AGECOUNT]		= 0xc7c,
+	[SMSTATE0]		= 0xc80,
+	[SMSTATE1]		= 0xc84,
+	[CTL0]			= 0xc88,
+	[DESDETSEC]		= 0xce0,
+	[TEMPAGESEC]		= 0xce4,
+	[CTRLSPARE0]		= 0xcf0,
+	[CTRLSPARE1]		= 0xcf4,
+	[CTRLSPARE2]		= 0xcf8,
+	[CTRLSPARE3]		= 0xcfc,
+	[CORESEL]		= 0xf00,
+	[THERMINTST]		= 0xf04,
+	[INTST]			= 0xf08,
+	[THSTAGE0ST]		= 0xf0c,
+	[THSTAGE1ST]		= 0xf10,
+	[THSTAGE2ST]		= 0xf14,
+	[THAHBST0]		= 0xf18,
+	[THAHBST1]		= 0xf1c,
+	[SPARE0]		= 0xf20,
+	[SPARE1]		= 0xf24,
+	[SPARE2]		= 0xf28,
+	[SPARE3]		= 0xf2c,
+	[THSLPEVEB]		= 0xf30,
+};
+
+struct thermal_parameter {
+	int adc_ge_t;
+	int adc_oe_t;
+	int ge;
+	int oe;
+	int gain;
+	int o_vtsabb;
+	int o_vtsmcu1;
+	int o_vtsmcu2;
+	int o_vtsmcu3;
+	int o_vtsmcu4;
+	int o_vtsmcu5;
+	int degc_cali;
+	int adc_cali_en_t;
+	int o_slope;
+	int o_slope_sign;
+	int ts_id;
+};
+
+struct svs_bank_ops {
+	void (*set_freqs_pct)(struct mtk_svs *svs);
+	void (*get_vops)(struct mtk_svs *svs);
+};
+
+struct svs_bank {
+	struct svs_bank_ops *ops;
+	struct completion init_completion;
+	struct device *dev;
+	struct regulator *buck;
+	struct mutex lock;	/* lock to protect update voltage process */
+	bool suspended;
+	bool mtcmos_request;
+	bool init01_support;
+	bool init02_support;
+	bool mon_mode_support;
+	s32 volt_offset;
+	u32 *opp_freqs;
+	u32 *freqs_pct;
+	u32 *opp_volts;
+	u32 *init02_volts;
+	u32 *volts;
+	u32 reg_data[3][reg_num];
+	u32 freq_base;
+	u32 vboot;
+	u32 volt_step;
+	u32 volt_base;
+	u32 init01_volt_flag;
+	u32 phase;
+	u32 vmax;
+	u32 vmin;
+	u32 bts;
+	u32 mts;
+	u32 bdes;
+	u32 mdes;
+	u32 mtdes;
+	u32 dcbdet;
+	u32 dcmdet;
+	u32 dthi;
+	u32 dtlo;
+	u32 det_window;
+	u32 det_max;
+	u32 age_config;
+	u32 age_voffset_in;
+	u32 agem;
+	u32 dc_config;
+	u32 dc_voffset_in;
+	u32 dvt_fixed;
+	u32 vco;
+	u32 chkshift;
+	u32 svs_temp;
+	u32 upper_temp_bound;
+	u32 lower_temp_bound;
+	u32 low_temp_threashold;
+	u32 low_temp_offset;
+	u32 coresel;
+	u32 opp_count;
+	u32 intst;
+	u32 systemclk_en;
+	u32 sw_id;
+	u32 hw_id;
+	u32 ctl0;
+	u8 *of_compatible;
+	u8 *name;
+	u8 *zone_name;
+	u8 *buck_name;
+};
+
+struct svs_platform {
+	struct svs_bank *banks;
+	int (*efuse_parsing)(struct mtk_svs *svs);
+	bool fake_efuse;
+	const u32 *regs;
+	u32 bank_num;
+	u32 efuse_num;
+	u32 efuse_check;
+	u32 thermal_efuse_num;
+	u8 *name;
+};
+
+struct mtk_svs {
+	const struct svs_platform *platform;
+	struct svs_bank *bank;
+	struct device *dev;
+	void __iomem *base;
+	struct clk *main_clk;
+	u32 *efuse;
+	u32 *thermal_efuse;
+};
+
+unsigned long claim_mtk_svs_lock(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mtk_svs_lock, flags);
+
+	return flags;
+}
+EXPORT_SYMBOL_GPL(claim_mtk_svs_lock);
+
+void release_mtk_svs_lock(unsigned long flags)
+{
+	spin_unlock_irqrestore(&mtk_svs_lock, flags);
+}
+EXPORT_SYMBOL_GPL(release_mtk_svs_lock);
+
+static u32 percent(u32 numerator, u32 denominator)
+{
+	u32 percent;
+
+	/* If not divide 1000, "numerator * 100" would be data overflow. */
+	numerator /= 1000;
+	denominator /= 1000;
+	percent = ((numerator * 100) + denominator - 1) / denominator;
+
+	return percent;
+}
+
+static u32 svs_readl(struct mtk_svs *svs, enum reg_index i)
+{
+	return readl(svs->base + svs->platform->regs[i]);
+}
+
+static void svs_writel(struct mtk_svs *svs, u32 val, enum reg_index i)
+{
+	writel(val, svs->base + svs->platform->regs[i]);
+}
+
+static void svs_switch_bank(struct mtk_svs *svs)
+{
+	struct svs_bank *svsb = svs->bank;
+
+	svs_writel(svs, svsb->coresel, CORESEL);
+}
+
+static u32 svs_volt_to_opp_volt(u32 svsb_volt,
+				u32 svsb_volt_step, u32 svsb_volt_base)
+{
+	u32 u_volt;
+
+	u_volt = (svsb_volt * svsb_volt_step) + svsb_volt_base;
+
+	return u_volt;
+}
+
+static int svs_get_zone_temperature(struct svs_bank *svsb, int *zone_temp)
+{
+	struct thermal_zone_device *tzd;
+	int ret;
+
+	tzd = thermal_zone_get_zone_by_name(svsb->zone_name);
+	ret = thermal_zone_get_temp(tzd, zone_temp);
+
+	return ret;
+}
+
+static int svs_set_volts(struct svs_bank *svsb, bool force_update)
+{
+	u32 i, svsb_volt, opp_volt, low_temp_offset = 0;
+	int zone_temp, ret;
+
+	mutex_lock(&svsb->lock);
+
+	/* If bank is suspended, it means init02 voltage is applied.
+	 * Don't need to update opp voltage anymore.
+	 */
+	if (svsb->suspended && !force_update) {
+		pr_notice("%s: bank is suspended\n", svsb->name);
+		mutex_unlock(&svsb->lock);
+		return -EPERM;
+	}
+
+	/* get thermal effect */
+	if (svsb->phase == SVS_PHASE_MON) {
+		if (svsb->svs_temp > svsb->upper_temp_bound &&
+		    svsb->svs_temp < svsb->lower_temp_bound) {
+			pr_err("%s: svs_temp is abnormal (0x%x)?\n",
+			       svsb->name, svsb->svs_temp);
+			mutex_unlock(&svsb->lock);
+			return -EINVAL;
+		}
+
+		ret = svs_get_zone_temperature(svsb, &zone_temp);
+		if (ret) {
+			pr_err("%s: cannot get zone \"%s\" temperature\n",
+			       svsb->name, svsb->zone_name);
+			pr_err("%s: add low_temp_offset = %u\n",
+			       svsb->name, svsb->low_temp_offset);
+			zone_temp = svsb->low_temp_threashold;
+		}
+
+		if (zone_temp <= svsb->low_temp_threashold)
+			low_temp_offset = svsb->low_temp_offset;
+	}
+
+	/* vmin <= svsb_volt (opp_volt) <= signed-off voltage */
+	for (i = 0; i < svsb->opp_count; i++) {
+		if (svsb->phase == SVS_PHASE_MON) {
+			svsb_volt = max((svsb->volts[i] + svsb->volt_offset +
+					 low_temp_offset), svsb->vmin);
+			opp_volt = svs_volt_to_opp_volt(svsb_volt,
+							svsb->volt_step,
+							svsb->volt_base);
+		} else if (svsb->phase == SVS_PHASE_INIT02) {
+			svsb_volt = max((svsb->init02_volts[i] +
+					 svsb->volt_offset), svsb->vmin);
+			opp_volt = svs_volt_to_opp_volt(svsb_volt,
+							svsb->volt_step,
+							svsb->volt_base);
+		} else if (svsb->phase == SVS_PHASE_ERROR) {
+			opp_volt = svsb->opp_volts[i];
+		} else {
+			pr_err("%s: unknown phase: %u?\n",
+			       svsb->name, svsb->phase);
+			mutex_unlock(&svsb->lock);
+			return -EINVAL;
+		}
+
+		opp_volt = min(opp_volt, svsb->opp_volts[i]);
+		ret = dev_pm_opp_adjust_voltage(svsb->dev, svsb->opp_freqs[i],
+						opp_volt);
+		if (ret) {
+			pr_err("%s: set voltage failed: %d\n", svsb->name, ret);
+			mutex_unlock(&svsb->lock);
+			return ret;
+		}
+	}
+
+	mutex_unlock(&svsb->lock);
+
+	return 0;
+}
+
+static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx)
+{
+	u32 vy;
+
+	if (v0 == v1 || f0 == f1)
+		return v0;
+
+	/* *100 to have decimal fraction factor, +99 for rounding up. */
+	vy = (v0 * 100) - ((((v0 - v1) * 100) / (f0 - f1)) * (f0 - fx));
+	vy = (vy + 99) / 100;
+
+	return vy;
+}
+
+static void svs_get_vops_v2(struct mtk_svs *svs)
+{
+	struct svs_bank *svsb = svs->bank;
+	u32 temp, i;
+
+	temp = svs_readl(svs, VOP30);
+	svsb->volts[6] = (temp >> 24) & 0xff;
+	svsb->volts[4] = (temp >> 16) & 0xff;
+	svsb->volts[2] = (temp >> 8)  & 0xff;
+	svsb->volts[0] = (temp & 0xff);
+
+	temp = svs_readl(svs, VOP74);
+	svsb->volts[14] = (temp >> 24) & 0xff;
+	svsb->volts[12] = (temp >> 16) & 0xff;
+	svsb->volts[10] = (temp >> 8)  & 0xff;
+	svsb->volts[8] = (temp & 0xff);
+
+	for (i = 0; i <= 7; i++) {
+		if (i < 7) {
+			svsb->volts[(i * 2) + 1] =
+				interpolate(svsb->freqs_pct[i * 2],
+					    svsb->freqs_pct[(i + 1) * 2],
+					    svsb->volts[i * 2],
+					    svsb->volts[(i + 1) * 2],
+					    svsb->freqs_pct[(i * 2) + 1]);
+		} else if (i == 7) {
+			svsb->volts[(i * 2) + 1] =
+				interpolate(svsb->freqs_pct[(i - 1) * 2],
+					    svsb->freqs_pct[i * 2],
+					    svsb->volts[(i - 1) * 2],
+					    svsb->volts[i * 2],
+					    svsb->freqs_pct[(i * 2) + 1]);
+		}
+	}
+}
+
+static void svs_set_freqs_pct_v2(struct mtk_svs *svs)
+{
+	struct svs_bank *svsb = svs->bank;
+
+	svs_writel(svs,
+		   ((svsb->freqs_pct[6] << 24) & 0xff000000) |
+		   ((svsb->freqs_pct[4] << 16) & 0xff0000) |
+		   ((svsb->freqs_pct[2] << 8) & 0xff00) |
+		   (svsb->freqs_pct[0] & 0xff),
+		   FREQPCT30);
+	svs_writel(svs,
+		   ((svsb->freqs_pct[14] << 24) & 0xff000000) |
+		   ((svsb->freqs_pct[12] << 16) & 0xff0000) |
+		   ((svsb->freqs_pct[10] << 8) & 0xff00) |
+		   ((svsb->freqs_pct[8]) & 0xff),
+		   FREQPCT74);
+}
+
+static void svs_set_phase(struct mtk_svs *svs, u32 target_phase)
+{
+	struct svs_bank *svsb = svs->bank;
+	u32 des_char, temp_char, det_char, limit_vals;
+	u32 init2vals, ts_calcs, val, filter, i;
+
+	svs_switch_bank(svs);
+
+	des_char = ((svsb->bdes << 8) & 0xff00) | (svsb->mdes & 0xff);
+	svs_writel(svs, des_char, DESCHAR);
+
+	temp_char = ((svsb->vco << 16) & 0xff0000) |
+		    ((svsb->mtdes << 8) & 0xff00) |
+		    (svsb->dvt_fixed & 0xff);
+	svs_writel(svs, temp_char, TEMPCHAR);
+
+	det_char = ((svsb->dcbdet << 8) & 0xff00) | (svsb->dcmdet & 0xff);
+	svs_writel(svs, det_char, DETCHAR);
+
+	svs_writel(svs, svsb->dc_config, DCCONFIG);
+	svs_writel(svs, svsb->age_config, AGECONFIG);
+
+	if (svsb->agem == 0x0) {
+		svs_writel(svs, 0x80000000, RUNCONFIG);
+	} else {
+		val = 0x0;
+
+		for (i = 0; i < 24; i += 2) {
+			filter = 0x3 << i;
+
+			if ((svsb->age_config & filter) == 0x0)
+				val |= (0x1 << i);
+			else
+				val |= (svsb->age_config & filter);
+		}
+		svs_writel(svs, val, RUNCONFIG);
+	}
+
+	svsb->ops->set_freqs_pct(svs);
+
+	limit_vals = ((svsb->vmax << 24) & 0xff000000) |
+		     ((svsb->vmin << 16) & 0xff0000) |
+		     ((svsb->dthi << 8) & 0xff00) |
+		     (svsb->dtlo & 0xff);
+	svs_writel(svs, limit_vals, LIMITVALS);
+	svs_writel(svs, (svsb->vboot & 0xff), VBOOT);
+	svs_writel(svs, (svsb->det_window & 0xffff), DETWINDOW);
+	svs_writel(svs, (svsb->det_max & 0xffff), CONFIG);
+
+	if (svsb->chkshift != 0)
+		svs_writel(svs, (svsb->chkshift & 0xff), CHKSHIFT);
+
+	if (svsb->ctl0 != 0)
+		svs_writel(svs, svsb->ctl0, CTL0);
+
+	svs_writel(svs, 0x00ffffff, INTSTS);
+
+	switch (target_phase) {
+	case SVS_PHASE_INIT01:
+		svs_writel(svs, 0x00005f01, INTEN);
+		svs_writel(svs, 0x00000001, SVSEN);
+		break;
+	case SVS_PHASE_INIT02:
+		svs_writel(svs, 0x00005f01, INTEN);
+		init2vals = ((svsb->age_voffset_in << 16) & 0xffff0000) |
+			    (svsb->dc_voffset_in & 0xffff);
+		svs_writel(svs, init2vals, INIT2VALS);
+		svs_writel(svs, 0x00000005, SVSEN);
+		break;
+	case SVS_PHASE_MON:
+		ts_calcs = ((svsb->bts << 12) & 0xfff000) | (svsb->mts & 0xfff);
+		svs_writel(svs, ts_calcs, TSCALCS);
+		svs_writel(svs, 0x00FF0000, INTEN);
+		svs_writel(svs, 0x00000002, SVSEN);
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+}
+
+static inline void svs_init01_isr_handler(struct mtk_svs *svs)
+{
+	struct svs_bank *svsb = svs->bank;
+	enum reg_index rg_i;
+
+	pr_notice("%s: %s: VDN74:0x%08x, VDN30:0x%08x, DCVALUES:0x%08x\n",
+		  svsb->name, __func__, svs_readl(svs, VDESIGN74),
+		  svs_readl(svs, VDESIGN30), svs_readl(svs, DCVALUES));
+
+	for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++)
+		svsb->reg_data[SVS_PHASE_INIT01][rg_i] = svs_readl(svs, rg_i);
+
+	svsb->dc_voffset_in = ~(svs_readl(svs, DCVALUES) & 0xffff) + 1;
+	if (svsb->init01_volt_flag == SVS_INIT01_VOLT_IGNORE)
+		svsb->dc_voffset_in = 0;
+	else if ((svsb->dc_voffset_in & 0x8000) &&
+		 (svsb->init01_volt_flag == SVS_INIT01_VOLT_INC_ONLY))
+		svsb->dc_voffset_in = 0;
+
+	svsb->age_voffset_in = svs_readl(svs, AGEVALUES) & 0xffff;
+
+	svs_writel(svs, 0x0, SVSEN);
+	svs_writel(svs, 0x1, INTSTS);
+
+	/* svs init01 clock gating */
+	svsb->coresel &= ~svsb->systemclk_en;
+
+	svsb->phase = SVS_PHASE_INIT01;
+	complete(&svsb->init_completion);
+}
+
+static inline void svs_init02_isr_handler(struct mtk_svs *svs)
+{
+	struct svs_bank *svsb = svs->bank;
+	enum reg_index rg_i;
+
+	pr_notice("%s: %s: VOP74:0x%08x, VOP30:0x%08x, DCVALUES:0x%08x\n",
+		  svsb->name, __func__, svs_readl(svs, VOP74),
+		  svs_readl(svs, VOP30), svs_readl(svs, DCVALUES));
+
+	for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++)
+		svsb->reg_data[SVS_PHASE_INIT02][rg_i] = svs_readl(svs, rg_i);
+
+	svsb->ops->get_vops(svs);
+	memcpy(svsb->init02_volts, svsb->volts, 4 * svsb->opp_count);
+	svsb->phase = SVS_PHASE_INIT02;
+
+	svs_writel(svs, 0x0, SVSEN);
+	svs_writel(svs, 0x1, INTSTS);
+
+	complete(&svsb->init_completion);
+}
+
+static inline void svs_mon_mode_isr_handler(struct mtk_svs *svs)
+{
+	struct svs_bank *svsb = svs->bank;
+	enum reg_index rg_i;
+
+	for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++)
+		svsb->reg_data[SVS_PHASE_MON][rg_i] = svs_readl(svs, rg_i);
+
+	svsb->svs_temp = svs_readl(svs, TEMP) & 0xff;
+
+	svsb->ops->get_vops(svs);
+	svsb->phase = SVS_PHASE_MON;
+
+	svs_writel(svs, 0x00ff0000, INTSTS);
+}
+
+static inline void svs_error_isr_handler(struct mtk_svs *svs)
+{
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb = svs->bank;
+	enum reg_index rg_i;
+
+	pr_err("%s(): %s(%s)", __func__, svsp->name, svsb->name);
+	pr_err("CORESEL(0x%x) = 0x%08x\n",
+	       svsp->regs[CORESEL], svs_readl(svs, CORESEL)),
+	pr_err("SVSEN(0x%x) = 0x%08x, INTSTS(0x%x) = 0x%08x\n",
+	       svsp->regs[SVSEN], svs_readl(svs, SVSEN),
+	       svsp->regs[INTSTS], svs_readl(svs, INTSTS));
+	pr_err("SMSTATE0(0x%x) = 0x%08x, SMSTATE1(0x%x) = 0x%08x\n",
+	       svsp->regs[SMSTATE0], svs_readl(svs, SMSTATE0),
+	       svsp->regs[SMSTATE1], svs_readl(svs, SMSTATE1));
+
+	for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++)
+		svsb->reg_data[SVS_PHASE_MON][rg_i] = svs_readl(svs, rg_i);
+
+	svsb->init01_support = false;
+	svsb->init02_support = false;
+	svsb->mon_mode_support = false;
+
+	if (svsb->phase == SVS_PHASE_MON)
+		svsb->phase = SVS_PHASE_INIT02;
+
+	svs_writel(svs, 0x0, SVSEN);
+	svs_writel(svs, 0x00ffffff, INTSTS);
+}
+
+static inline void svs_isr_handler(struct mtk_svs *svs)
+{
+	u32 intsts, svsen;
+
+	svs_switch_bank(svs);
+
+	intsts = svs_readl(svs, INTSTS);
+	svsen = svs_readl(svs, SVSEN);
+
+	if (intsts == 0x1 && ((svsen & 0x7) == 0x1))
+		svs_init01_isr_handler(svs);
+	else if ((intsts == 0x1) && ((svsen & 0x7) == 0x5))
+		svs_init02_isr_handler(svs);
+	else if ((intsts & 0x00ff0000) != 0x0)
+		svs_mon_mode_isr_handler(svs);
+	else
+		svs_error_isr_handler(svs);
+}
+
+static irqreturn_t svs_isr(int irq, void *data)
+{
+	struct mtk_svs *svs = (struct mtk_svs *)data;
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb = NULL;
+	unsigned long flags;
+	u32 idx;
+
+	flags = claim_mtk_svs_lock();
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		svs->bank = svsb;
+
+		if (svsb->suspended)
+			continue;
+		else if (svsb->intst & svs_readl(svs, INTST))
+			continue;
+
+		svs_isr_handler(svs);
+		break;
+	}
+	release_mtk_svs_lock(flags);
+
+	if (svsb->phase != SVS_PHASE_INIT01)
+		svs_set_volts(svsb, false);
+
+	return IRQ_HANDLED;
+}
+
+static void svs_mon_mode(struct mtk_svs *svs)
+{
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	unsigned long flags;
+	u32 idx;
+
+	flags = claim_mtk_svs_lock();
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		svs->bank = svsb;
+
+		if (!svsb->mon_mode_support)
+			continue;
+
+		svs_set_phase(svs, SVS_PHASE_MON);
+	}
+	release_mtk_svs_lock(flags);
+}
+
+static int svs_init02(struct mtk_svs *svs)
+{
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	unsigned long flags, time_left;
+	u32 idx;
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		svs->bank = svsb;
+
+		if (!svsb->init02_support)
+			continue;
+
+		reinit_completion(&svsb->init_completion);
+		flags = claim_mtk_svs_lock();
+		svs_set_phase(svs, SVS_PHASE_INIT02);
+		release_mtk_svs_lock(flags);
+		time_left =
+			wait_for_completion_timeout(&svsb->init_completion,
+						    msecs_to_jiffies(2000));
+		if (time_left == 0) {
+			pr_err("%s: init02 completion timeout\n", svsb->name);
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static int svs_init01(struct mtk_svs *svs)
+{
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	struct pm_qos_request qos_request = { {0} };
+	unsigned long flags, time_left;
+	bool search_done;
+	int ret = -EINVAL;
+	u32 opp_freqs, opp_vboot, buck_volt, idx, i;
+
+	/* Let CPUs leave idle-off state for initializing svs_init01. */
+	pm_qos_add_request(&qos_request, PM_QOS_CPU_DMA_LATENCY, 0);
+
+	/* Sometimes two svs_bank use the same buck.
+	 * Therefore, we set each svs_bank to vboot voltage first.
+	 */
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		search_done = false;
+
+		if (!svsb->init01_support)
+			continue;
+
+		ret = regulator_set_mode(svsb->buck, REGULATOR_MODE_FAST);
+		if (ret)
+			pr_notice("%s: fail to set fast mode: %d\n",
+				  svsb->name, ret);
+
+		if (svsb->mtcmos_request) {
+			ret = regulator_enable(svsb->buck);
+			if (ret) {
+				pr_err("%s: fail to enable %s power: %d\n",
+				       svsb->name, svsb->buck_name, ret);
+				goto err_init;
+			}
+
+			ret = dev_pm_domain_attach(svsb->dev, false);
+			if (ret) {
+				pr_err("%s: attach pm domain fail: %d\n",
+				       svsb->name, ret);
+				goto err_init;
+			}
+
+			pm_runtime_enable(svsb->dev);
+			ret = pm_runtime_get_sync(svsb->dev);
+			if (ret < 0) {
+				pr_err("%s: turn mtcmos on fail: %d\n",
+				       svsb->name, ret);
+				goto err_init;
+			}
+		}
+
+		/* Find the fastest freq that can be run at vboot and
+		 * fix to that freq until svs_init01 is done.
+		 */
+		opp_vboot = svs_volt_to_opp_volt(svsb->vboot,
+						 svsb->volt_step,
+						 svsb->volt_base);
+
+		for (i = 0; i < svsb->opp_count; i++) {
+			opp_freqs = svsb->opp_freqs[i];
+			if (!search_done && svsb->opp_volts[i] <= opp_vboot) {
+				ret = dev_pm_opp_adjust_voltage(svsb->dev,
+								opp_freqs,
+								opp_vboot);
+				if (ret) {
+					pr_err("%s: set voltage failed: %d\n",
+					       svsb->name, ret);
+					goto err_init;
+				}
+
+				search_done = true;
+			} else {
+				dev_pm_opp_disable(svsb->dev,
+						   svsb->opp_freqs[i]);
+			}
+		}
+	}
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		svs->bank = svsb;
+
+		if (!svsb->init01_support)
+			continue;
+
+		opp_vboot = svs_volt_to_opp_volt(svsb->vboot,
+						 svsb->volt_step,
+						 svsb->volt_base);
+
+		buck_volt = regulator_get_voltage(svsb->buck);
+		if (buck_volt != opp_vboot) {
+			pr_err("%s: buck voltage: %u, expected vboot: %u\n",
+			       svsb->name, buck_volt, opp_vboot);
+			ret = -EPERM;
+			goto err_init;
+		}
+
+		init_completion(&svsb->init_completion);
+		flags = claim_mtk_svs_lock();
+		svs_set_phase(svs, SVS_PHASE_INIT01);
+		release_mtk_svs_lock(flags);
+		time_left =
+			wait_for_completion_timeout(&svsb->init_completion,
+						    msecs_to_jiffies(2000));
+		if (time_left == 0) {
+			pr_err("%s: init01 completion timeout\n", svsb->name);
+			ret = -EBUSY;
+			goto err_init;
+		}
+	}
+
+err_init:
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+
+		if (!svsb->init01_support)
+			continue;
+
+		for (i = 0; i < svsb->opp_count; i++)
+			dev_pm_opp_enable(svsb->dev, svsb->opp_freqs[i]);
+
+		if (regulator_set_mode(svsb->buck, REGULATOR_MODE_NORMAL))
+			pr_notice("%s: fail to set normal mode: %d\n",
+				  svsb->name, ret);
+
+		if (svsb->mtcmos_request) {
+			if (pm_runtime_put_sync(svsb->dev))
+				pr_err("%s: turn mtcmos off fail: %d\n",
+				       svsb->name, ret);
+			pm_runtime_disable(svsb->dev);
+			dev_pm_domain_detach(svsb->dev, 0);
+			if (regulator_disable(svsb->buck))
+				pr_err("%s: fail to disable %s power: %d\n",
+				       svsb->name, svsb->buck_name, ret);
+		}
+	}
+
+	pm_qos_remove_request(&qos_request);
+
+	return ret;
+}
+
+static int svs_start(struct mtk_svs *svs)
+{
+	int ret;
+
+	ret = svs_init01(svs);
+	if (ret)
+		return ret;
+
+	ret = svs_init02(svs);
+	if (ret)
+		return ret;
+
+	svs_mon_mode(svs);
+
+	return ret;
+}
+
+static int svs_mt8183_efuse_parsing(struct mtk_svs *svs)
+{
+	const struct svs_platform *svsp = svs->platform;
+	struct thermal_parameter tp;
+	struct svs_bank *svsb;
+	bool mon_mode_support = true;
+	int format[6], x_roomt[6], tb_roomt;
+	u32 idx, i, ft_pgm, mts, temp0, temp1, temp2;
+
+	if (svsp->fake_efuse) {
+		svs->efuse[0] = 0x00110070;
+		svs->efuse[1] = 0xe7fcbb1b;
+		svs->efuse[2] = 0x478b47c7;
+		svs->efuse[3] = 0xabfbf757;
+		svs->efuse[4] = 0xe7fca4ed;
+		svs->efuse[5] = 0x47b84bc4;
+		svs->efuse[6] = 0xe7fcc3e9;
+		svs->efuse[7] = 0xe7fc9d0d;
+		svs->efuse[8] = 0x4bb84bf0;
+		svs->efuse[9] = 0xabfbdd79;
+		svs->efuse[16] = 0xe7fcb11c;
+		svs->efuse[17] = 0x47b847ff;
+		svs->efuse[18] = 0xabfbf557;
+
+		svs->thermal_efuse[0] = 0x02bf5c65;
+		svs->thermal_efuse[1] = 0x9ede715f;
+		svs->thermal_efuse[2] = 0xb0d9acc0;
+	}
+
+	/* svs efuse parsing */
+	ft_pgm = (svs->efuse[0] >> 4) & 0xf;
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		if (ft_pgm <= 1)
+			svsb->init01_volt_flag = SVS_INIT01_VOLT_IGNORE;
+
+		switch (svsb->sw_id) {
+		case SVS_CPU_LITTLE:
+			svsb->bdes = svs->efuse[16] & 0xff;
+			svsb->mdes = (svs->efuse[16] >> 8) & 0xff;
+			svsb->dcbdet = (svs->efuse[16] >> 16) & 0xff;
+			svsb->dcmdet = (svs->efuse[16] >> 24) & 0xff;
+			svsb->mtdes  = (svs->efuse[17] >> 16) & 0xff;
+
+			if (ft_pgm <= 3)
+				svsb->volt_offset += 10;
+			else
+				svsb->volt_offset += 2;
+			break;
+		case SVS_CPU_BIG:
+			svsb->bdes = svs->efuse[18] & 0xff;
+			svsb->mdes = (svs->efuse[18] >> 8) & 0xff;
+			svsb->dcbdet = (svs->efuse[18] >> 16) & 0xff;
+			svsb->dcmdet = (svs->efuse[18] >> 24) & 0xff;
+			svsb->mtdes  = svs->efuse[17] & 0xff;
+
+			if (ft_pgm <= 3)
+				svsb->volt_offset += 15;
+			else
+				svsb->volt_offset += 12;
+			break;
+		case SVS_CCI:
+			svsb->bdes = svs->efuse[4] & 0xff;
+			svsb->mdes = (svs->efuse[4] >> 8) & 0xff;
+			svsb->dcbdet = (svs->efuse[4] >> 16) & 0xff;
+			svsb->dcmdet = (svs->efuse[4] >> 24) & 0xff;
+			svsb->mtdes  = (svs->efuse[5] >> 16) & 0xff;
+
+			if (ft_pgm <= 3)
+				svsb->volt_offset += 10;
+			else
+				svsb->volt_offset += 2;
+			break;
+		case SVS_GPU:
+			svsb->bdes = svs->efuse[6] & 0xff;
+			svsb->mdes = (svs->efuse[6] >> 8) & 0xff;
+			svsb->dcbdet = (svs->efuse[6] >> 16) & 0xff;
+			svsb->dcmdet = (svs->efuse[6] >> 24) & 0xff;
+			svsb->mtdes  = svs->efuse[5] & 0xff;
+
+			if (ft_pgm >= 2) {
+				svsb->freq_base = 800000000; /* 800MHz */
+				svsb->dvt_fixed = 2;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	for (i = 0; i < svsp->efuse_num; i++) {
+		if (svs->efuse[i])
+			pr_notice("M_HW_RES%d: 0x%08x\n", i, svs->efuse[i]);
+	}
+
+	/* thermal efuse parsing */
+	if (!svs->thermal_efuse)
+		return 0;
+
+	tp.adc_ge_t = (svs->thermal_efuse[1] >> 22) & 0x3ff;
+	tp.adc_oe_t = (svs->thermal_efuse[1] >> 12) & 0x3ff;
+
+	tp.o_vtsmcu1 = (svs->thermal_efuse[0] >> 17) & 0x1ff;
+	tp.o_vtsmcu2 = (svs->thermal_efuse[0] >> 8) & 0x1ff;
+	tp.o_vtsmcu3 = svs->thermal_efuse[1] & 0x1ff;
+	tp.o_vtsmcu4 = (svs->thermal_efuse[2] >> 23) & 0x1ff;
+	tp.o_vtsmcu5 = (svs->thermal_efuse[2] >> 5) & 0x1ff;
+	tp.o_vtsabb = (svs->thermal_efuse[2] >> 14) & 0x1ff;
+
+	tp.degc_cali = (svs->thermal_efuse[0] >> 1) & 0x3f;
+	tp.adc_cali_en_t = svs->thermal_efuse[0] & BIT(0);
+	tp.o_slope_sign = (svs->thermal_efuse[0] >> 7) & BIT(0);
+
+	tp.ts_id = (svs->thermal_efuse[1] >> 9) & BIT(0);
+	tp.o_slope = (svs->thermal_efuse[0] >> 26) & 0x3f;
+
+	if (tp.adc_cali_en_t == 1) {
+		if (tp.ts_id == 0)
+			tp.o_slope = 0;
+
+		if ((tp.adc_ge_t < 265 || tp.adc_ge_t > 758) ||
+		    (tp.adc_oe_t < 265 || tp.adc_oe_t > 758) ||
+		    (tp.o_vtsmcu1 < -8 || tp.o_vtsmcu1 > 484) ||
+		    (tp.o_vtsmcu2 < -8 || tp.o_vtsmcu2 > 484) ||
+		    (tp.o_vtsmcu3 < -8 || tp.o_vtsmcu3 > 484) ||
+		    (tp.o_vtsmcu4 < -8 || tp.o_vtsmcu4 > 484) ||
+		    (tp.o_vtsmcu5 < -8 || tp.o_vtsmcu5 > 484) ||
+		    (tp.o_vtsabb < -8 || tp.o_vtsabb > 484) ||
+		    (tp.degc_cali < 1 || tp.degc_cali > 63)) {
+			pr_err("bad thermal efuse data. disable mon mode\n");
+			mon_mode_support = false;
+		}
+	} else {
+		pr_err("no thermal efuse data. disable mon mode\n");
+		mon_mode_support = false;
+	}
+
+	if (!mon_mode_support) {
+		for (i = 0; i < svsp->thermal_efuse_num; i++)
+			pr_err("thermal_efuse[%u] = 0x%08x\n",
+			       i, svs->thermal_efuse[i]);
+
+		for (idx = 0; idx < svsp->bank_num; idx++) {
+			svsb = &svsp->banks[idx];
+			svsb->mon_mode_support = false;
+		}
+
+		return 0;
+	}
+
+	tp.ge = ((tp.adc_ge_t - 512) * 10000) / 4096;
+	tp.oe = (tp.adc_oe_t - 512);
+	tp.gain = (10000 + tp.ge);
+
+	format[0] = (tp.o_vtsmcu1 + 3350 - tp.oe);
+	format[1] = (tp.o_vtsmcu2 + 3350 - tp.oe);
+	format[2] = (tp.o_vtsmcu3 + 3350 - tp.oe);
+	format[3] = (tp.o_vtsmcu4 + 3350 - tp.oe);
+	format[4] = (tp.o_vtsmcu5 + 3350 - tp.oe);
+	format[5] = (tp.o_vtsabb + 3350 - tp.oe);
+
+	for (i = 0; i < 6; i++)
+		x_roomt[i] = (((format[i] * 10000) / 4096) * 10000) / tp.gain;
+
+	temp0 = (10000 * 100000 / tp.gain) * 15 / 18;
+
+	if (tp.o_slope_sign == 0)
+		mts = (temp0 * 10) / (1534 + tp.o_slope * 10);
+	else
+		mts = (temp0 * 10) / (1534 - tp.o_slope * 10);
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		svsb->mts = mts;
+
+		switch (svsb->sw_id) {
+		case SVS_CPU_LITTLE:
+			tb_roomt = x_roomt[3];
+			break;
+		case SVS_CPU_BIG:
+			tb_roomt = x_roomt[4];
+			break;
+		case SVS_CCI:
+			tb_roomt = x_roomt[3];
+			break;
+		case SVS_GPU:
+			tb_roomt = x_roomt[1];
+			break;
+		default:
+			pr_err("unknown svsb_id = %u? disable svs\n",
+			       svsb->sw_id);
+			return -EINVAL;
+		}
+
+		temp0 = (tp.degc_cali * 10 / 2);
+		temp1 = ((10000 * 100000 / 4096 / tp.gain) *
+			 tp.oe + tb_roomt * 10) * 15 / 18;
+
+		if (tp.o_slope_sign == 0)
+			temp2 = temp1 * 100 / (1534 + tp.o_slope * 10);
+		else
+			temp2 = temp1 * 100 / (1534 - tp.o_slope * 10);
+
+		svsb->bts = (temp0 + temp2 - 250) * 4 / 10;
+	}
+
+	return 0;
+}
+
+static int svs_is_support(struct mtk_svs *svs)
+{
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	struct nvmem_cell *cell;
+	size_t len;
+	int ret;
+	u32 idx, i;
+
+	if (svsp->fake_efuse) {
+		len = svsp->efuse_num * 4;
+		svs->efuse = kzalloc(len, GFP_KERNEL);
+		if (!svs->efuse)
+			return -ENOMEM;
+
+		len = svsp->thermal_efuse_num * 4;
+		svs->thermal_efuse = kzalloc(len, GFP_KERNEL);
+		if (!svs->thermal_efuse)
+			return -ENOMEM;
+
+		goto svsp_efuse_parsing;
+	}
+
+	/* get svs efuse by nvmem */
+	cell = nvmem_cell_get(svs->dev, "svs-calibration-data");
+	if (IS_ERR(cell)) {
+		pr_err("no \"svs-calibration-data\" from dts? disable svs\n");
+		return PTR_ERR(cell);
+	}
+
+	svs->efuse = (u32 *)nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+	ret = (svs->efuse[svsp->efuse_check] == 0) ? -EPERM : 0;
+	if (ret) {
+		pr_err("no svs efuse. disable svs\n");
+		for (i = 0; i < svsp->efuse_num; i++)
+			pr_err("M_HW_RES%d: 0x%08x\n", i, svs->efuse[i]);
+		return ret;
+	}
+
+	/* get thermal efuse by nvmem */
+	cell = nvmem_cell_get(svs->dev, "calibration-data");
+	if (IS_ERR(cell)) {
+		pr_err("no \"calibration-data\" from dts? disable mon mode\n");
+		svs->thermal_efuse = NULL;
+		for (idx = 0; idx < svsp->bank_num; idx++) {
+			svsb = &svsp->banks[idx];
+			svsb->mon_mode_support = false;
+		}
+		goto svsp_efuse_parsing;
+	}
+
+	svs->thermal_efuse = (u32 *)nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+svsp_efuse_parsing:
+	ret = svsp->efuse_parsing(svs);
+
+	return ret;
+}
+
+static int svs_resource_setup(struct mtk_svs *svs)
+{
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	struct platform_device *pdev;
+	struct device_node *np = NULL;
+	struct dev_pm_opp *opp;
+	unsigned long freq;
+	int count, ret;
+	u32 idx, i;
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+
+		if (!svsb->init01_support)
+			continue;
+
+		switch (svsb->sw_id) {
+		case SVS_CPU_LITTLE:
+			svsb->name = "SVS_CPU_LITTLE";
+			break;
+		case SVS_CPU_BIG:
+			svsb->name = "SVS_CPU_BIG";
+			break;
+		case SVS_CCI:
+			svsb->name = "SVS_CCI";
+			break;
+		case SVS_GPU:
+			svsb->name = "SVS_GPU";
+			break;
+		default:
+			WARN_ON(1);
+			return -EINVAL;
+		}
+
+		/* Add svs_bank device for opp-table/mtcmos/buck control */
+		pdev = platform_device_alloc(svsb->name, 0);
+		if (!pdev) {
+			pr_err("%s: fail to alloc pdev for svs_bank\n",
+			       svsb->name);
+			return -ENOMEM;
+		}
+
+		for_each_child_of_node(svs->dev->of_node, np) {
+			if (of_device_is_compatible(np, svsb->of_compatible)) {
+				pdev->dev.of_node = np;
+				break;
+			}
+		}
+
+		ret = platform_device_add(pdev);
+		if (ret) {
+			pr_err("%s: fail to add svs_bank device: %d\n",
+			       svsb->name, ret);
+			return ret;
+		}
+
+		svsb->dev = &pdev->dev;
+		dev_set_drvdata(svsb->dev, svs);
+		ret = dev_pm_opp_of_add_table(svsb->dev);
+		if (ret) {
+			pr_err("%s: fail to add opp table: %d\n",
+			       svsb->name, ret);
+			return ret;
+		}
+
+		mutex_init(&svsb->lock);
+
+		svsb->buck = devm_regulator_get_optional(svsb->dev,
+							 svsb->buck_name);
+		if (IS_ERR(svsb->buck)) {
+			pr_err("%s: cannot get regulator \"%s-supply\"\n",
+			       svsb->name, svsb->buck_name);
+			return PTR_ERR(svsb->buck);
+		}
+
+		count = dev_pm_opp_get_opp_count(svsb->dev);
+		if (svsb->opp_count != count) {
+			pr_err("%s: opp_count not \"%u\" but get \"%d\"?\n",
+			       svsb->name, svsb->opp_count, count);
+			return count;
+		}
+
+		svsb->opp_volts = kmalloc(4 * svsb->opp_count,
+					  GFP_KERNEL);
+		if (IS_ERR(svsb->opp_volts)) {
+			pr_err("%s: fail kmalloc %u(bytes) to opp_volts\n",
+			       svsb->name, (4 * svsb->opp_count));
+			return PTR_ERR(svsb->opp_volts);
+		}
+
+		svsb->init02_volts = kmalloc(4 * svsb->opp_count,
+					     GFP_KERNEL);
+		if (IS_ERR(svsb->init02_volts)) {
+			pr_err("%s: fail kmalloc %u(bytes) to init02_volts\n",
+			       svsb->name, (4 * svsb->opp_count));
+			return PTR_ERR(svsb->init02_volts);
+		}
+
+		svsb->volts = kmalloc(4 * svsb->opp_count,
+				      GFP_KERNEL);
+		if (IS_ERR(svsb->volts)) {
+			pr_err("%s: fail kmalloc %u(bytes) to volts\n",
+			       svsb->name, (4 * svsb->opp_count));
+			return PTR_ERR(svsb->volts);
+		}
+
+		svsb->opp_freqs = kmalloc(4 * svsb->opp_count,
+					  GFP_KERNEL);
+		if (IS_ERR(svsb->opp_freqs)) {
+			pr_err("%s: fail kmalloc %u(bytes) to opp_freqs\n",
+			       svsb->name, (4 * svsb->opp_count));
+			return PTR_ERR(svsb->opp_freqs);
+		}
+
+		svsb->freqs_pct = kmalloc(4 * svsb->opp_count,
+					  GFP_KERNEL);
+		if (IS_ERR(svsb->freqs_pct)) {
+			pr_err("%s: fail kmalloc %u(bytes) to freqs_pct\n",
+			       svsb->name, (4 * svsb->opp_count));
+			return PTR_ERR(svsb->freqs_pct);
+		}
+
+		for (i = 0, freq = (u32)-1; i < svsb->opp_count; i++, freq--) {
+			opp = dev_pm_opp_find_freq_floor(svsb->dev, &freq);
+			if (IS_ERR(opp)) {
+				pr_err("%s: error opp entry!!, err = %ld\n",
+				       svsb->name, PTR_ERR(opp));
+				return PTR_ERR(opp);
+			}
+
+			svsb->opp_freqs[i] = freq;
+			svsb->opp_volts[i] = dev_pm_opp_get_voltage(opp);
+			svsb->freqs_pct[i] = percent(svsb->opp_freqs[i],
+						     svsb->freq_base) & 0xff;
+		}
+	}
+
+	return 0;
+}
+
+static int svs_suspend(struct device *dev)
+{
+	struct mtk_svs *svs = dev_get_drvdata(dev);
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	unsigned long flags;
+	u32 idx;
+
+	/* Wait if there is processing svs_isr(). Suspend all banks. */
+	flags = claim_mtk_svs_lock();
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		svs->bank = svsb;
+		svs_switch_bank(svs);
+		svs_writel(svs, 0x0, SVSEN);
+		svs_writel(svs, 0x00ffffff, INTSTS);
+		svsb->suspended = true;
+	}
+	release_mtk_svs_lock(flags);
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		if (svsb->phase == SVS_PHASE_MON) {
+			svsb->phase = SVS_PHASE_INIT02;
+			svs_set_volts(svsb, true);
+		}
+	}
+
+	clk_disable_unprepare(svs->main_clk);
+
+	return 0;
+}
+
+static int svs_resume(struct device *dev)
+{
+	struct mtk_svs *svs = dev_get_drvdata(dev);
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	int ret;
+	u32 idx;
+
+	ret = clk_prepare_enable(svs->main_clk);
+	if (ret)
+		pr_err("%s(): cannot enable main_clk\n", __func__);
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+		svsb->suspended = false;
+	}
+
+	ret = svs_init02(svs);
+	if (ret)
+		return ret;
+
+	svs_mon_mode(svs);
+
+	return 0;
+}
+
+static int svs_debug_proc_show(struct seq_file *m, void *v)
+{
+	struct svs_bank *svsb = (struct svs_bank *)m->private;
+
+	if (svsb->phase == SVS_PHASE_INIT01)
+		seq_puts(m, "init01\n");
+	else if (svsb->phase == SVS_PHASE_INIT02)
+		seq_puts(m, "init02\n");
+	else if (svsb->phase == SVS_PHASE_MON)
+		seq_puts(m, "mon mode\n");
+	else if (svsb->phase == SVS_PHASE_ERROR)
+		seq_puts(m, "disabled\n");
+	else
+		seq_puts(m, "unknown\n");
+
+	return 0;
+}
+
+static ssize_t svs_debug_proc_write(struct file *file,
+				    const char __user *buffer,
+				    size_t count, loff_t *pos)
+{
+	struct svs_bank *svsb = (struct svs_bank *)PDE_DATA(file_inode(file));
+	struct mtk_svs *svs = dev_get_drvdata(svsb->dev);
+	char *buf = (char *)__get_free_page(GFP_USER);
+	unsigned long flags;
+	int enabled, ret;
+
+	if (svsb->phase == SVS_PHASE_ERROR)
+		return count;
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (count >= PAGE_SIZE) {
+		free_page((unsigned long)buf);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(buf, buffer, count)) {
+		free_page((unsigned long)buf);
+		return -EFAULT;
+	}
+
+	buf[count] = '\0';
+
+	ret = kstrtoint(buf, 10, &enabled);
+	if (ret)
+		return ret;
+
+	if (!enabled) {
+		flags = claim_mtk_svs_lock();
+		svs->bank = svsb;
+
+		svsb->init01_support = false;
+		svsb->init02_support = false;
+		svsb->mon_mode_support = false;
+
+		svs_switch_bank(svs);
+		svs_writel(svs, 0x0, SVSEN);
+		svs_writel(svs, 0x00ffffff, INTSTS);
+		release_mtk_svs_lock(flags);
+	}
+
+	svsb->phase = SVS_PHASE_ERROR;
+	svs_set_volts(svsb, true);
+
+	return count;
+}
+
+proc_fops_rw(svs_debug);
+
+static int svs_dump_proc_show(struct seq_file *m, void *v)
+{
+	struct mtk_svs *svs = (struct mtk_svs *)m->private;
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	unsigned long svs_reg_addr;
+	u32 idx, i, j;
+
+	for (i = 0; i < svsp->efuse_num; i++) {
+		if (svs->efuse[i])
+			seq_printf(m, "M_HW_RES%d = 0x%08x\n",
+				   i, svs->efuse[i]);
+	}
+
+	for (i = 0; i < svsp->thermal_efuse_num; i++) {
+		if (svs->thermal_efuse[i])
+			seq_printf(m, "THERMAL_EFUSE%d = 0x%08x\n",
+				   i, svs->thermal_efuse[i]);
+	}
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+
+		if (!svsb->init01_support)
+			continue;
+
+		for (i = SVS_PHASE_INIT01; i <= SVS_PHASE_MON; i++) {
+			seq_printf(m, "bank number = %u\n", svsb->hw_id);
+
+			if (i < SVS_PHASE_MON)
+				seq_printf(m, "mode = init0%d\n", i + 1);
+			else
+				seq_puts(m, "mode = mon\n");
+
+			for (j = TEMPMONCTL0; j < reg_num; j++) {
+				svs_reg_addr = (unsigned long)(svs->base +
+							       svsp->regs[j]);
+				seq_printf(m, "0x%08lx = 0x%08x\n",
+					   svs_reg_addr, svsb->reg_data[i][j]);
+			}
+		}
+	}
+
+	return 0;
+}
+
+proc_fops_ro(svs_dump);
+
+static int svs_status_proc_show(struct seq_file *m, void *v)
+{
+	struct svs_bank *svsb = (struct svs_bank *)m->private;
+	struct dev_pm_opp *opp;
+	unsigned long freq;
+	int zone_temp, ret;
+	u32 i;
+
+	ret = svs_get_zone_temperature(svsb, &zone_temp);
+	if (ret)
+		seq_printf(m, "%s: cannot get zone \"%s\" temperature\n",
+			   svsb->name, svsb->zone_name);
+	else
+		seq_printf(m, "%s: temperature = %d\n", svsb->name, zone_temp);
+
+	for (i = 0, freq = (u32)-1; i < svsb->opp_count; i++, freq--) {
+		opp = dev_pm_opp_find_freq_floor(svsb->dev, &freq);
+		if (IS_ERR(opp)) {
+			seq_printf(m, "%s: error opp entry!!, err = %ld\n",
+				   svsb->name, PTR_ERR(opp));
+			return PTR_ERR(opp);
+		}
+
+		seq_printf(m, "opp_freqs[%02u]: %lu, volts[%02u]: %lu, ",
+			   i, freq, i, dev_pm_opp_get_voltage(opp));
+		seq_printf(m, "svsb_volts[%02u]: 0x%x, freqs_pct[%02u]: %u\n",
+			   i, svsb->volts[i], i, svsb->freqs_pct[i]);
+	}
+
+	return 0;
+}
+
+proc_fops_ro(svs_status);
+
+static int svs_volt_offset_proc_show(struct seq_file *m, void *v)
+{
+	struct svs_bank *svsb = (struct svs_bank *)m->private;
+
+	seq_printf(m, "%d\n", svsb->volt_offset);
+
+	return 0;
+}
+
+static ssize_t svs_volt_offset_proc_write(struct file *file,
+					  const char __user *buffer,
+					  size_t count, loff_t *pos)
+{
+	struct svs_bank *svsb = (struct svs_bank *)PDE_DATA(file_inode(file));
+	char *buf = (char *)__get_free_page(GFP_USER);
+	int ret, volt_offset;
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (count >= PAGE_SIZE) {
+		free_page((unsigned long)buf);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(buf, buffer, count)) {
+		free_page((unsigned long)buf);
+		return -EFAULT;
+	}
+
+	buf[count] = '\0';
+
+	if (!kstrtoint(buf, 10, &volt_offset)) {
+		svsb->volt_offset = volt_offset;
+		ret = svs_set_volts(svsb, true);
+		if (ret)
+			return ret;
+	}
+
+	return count;
+}
+
+proc_fops_rw(svs_volt_offset);
+
+static int svs_create_svs_procfs(struct mtk_svs *svs)
+{
+	const struct svs_platform *svsp = svs->platform;
+	struct svs_bank *svsb;
+	struct proc_dir_entry *svs_dir, *bank_dir;
+	u32 idx, i;
+
+	struct pentry {
+		const char *name;
+		const struct file_operations *fops;
+	};
+
+	struct pentry svs_entries[] = {
+		proc_entry(svs_dump),
+	};
+
+	struct pentry bank_entries[] = {
+		proc_entry(svs_debug),
+		proc_entry(svs_status),
+		proc_entry(svs_volt_offset),
+	};
+
+	svs_dir = proc_mkdir("svs", NULL);
+	if (!svs_dir) {
+		pr_err("mkdir /proc/svs failed\n");
+		return -EPERM;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(svs_entries); i++) {
+		if (!proc_create_data(svs_entries[i].name, 0664,
+				      svs_dir, svs_entries[i].fops, svs)) {
+			pr_err("create /proc/svs/%s failed\n",
+			       svs_entries[i].name);
+			return -EPERM;
+		}
+	}
+
+	for (idx = 0; idx < svsp->bank_num; idx++) {
+		svsb = &svsp->banks[idx];
+
+		if (!svsb->init01_support)
+			continue;
+
+		bank_dir = proc_mkdir(svsb->name, svs_dir);
+		if (!bank_dir) {
+			pr_err("mkdir /proc/svs/%s failed\n", svsb->name);
+			return -EPERM;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(bank_entries); i++) {
+			if (!proc_create_data(bank_entries[i].name, 0664,
+					      bank_dir, bank_entries[i].fops,
+					      svsb)) {
+				pr_err("create /proc/svs/%s/%s failed\n",
+				       svsb->name, bank_entries[i].name);
+				return -EPERM;
+			}
+		}
+	}
+
+	return 0;
+}
+
+struct svs_bank_ops svs_mt8183_banks_ops = {
+	.set_freqs_pct	= svs_set_freqs_pct_v2,
+	.get_vops	= svs_get_vops_v2,
+};
+
+struct svs_bank svs_mt8183_banks[4] = {
+	{
+		.of_compatible		= "mediatek,mt8183-svs-cpu-little",
+		.sw_id			= SVS_CPU_LITTLE,
+		.hw_id			= 0,
+		.ops			= &svs_mt8183_banks_ops,
+		.zone_name		= "tzts4",
+		.buck_name		= "vcpu_little",
+		.mtcmos_request		= false,
+		.init01_volt_flag	= SVS_INIT01_VOLT_INC_ONLY,
+		.init01_support		= true,
+		.init02_support		= true,
+		.mon_mode_support	= false,
+		.opp_count		= 16,
+		.freq_base		= 1989000000,
+		.vboot			= 0x30,
+		.volt_step		= 6250,
+		.volt_base		= 500000,
+		.volt_offset		= 0,
+		.vmax			= 0x64,
+		.vmin			= 0x10,
+		.dthi			= 0x1,
+		.dtlo			= 0xfe,
+		.det_window		= 0xa28,
+		.det_max		= 0xffff,
+		.age_config		= 0x555555,
+		.agem			= 0x0,
+		.dc_config		= 0x555555,
+		.dvt_fixed		= 0x7,
+		.vco			= 0x10,
+		.chkshift		= 0x77,
+		.upper_temp_bound	= 0x64,
+		.lower_temp_bound	= 0xb2,
+		.low_temp_threashold	= 25000,
+		.low_temp_offset	= 0,
+		.coresel		= 0x8fff0000,
+		.systemclk_en		= BIT(31),
+		.intst			= BIT(0),
+		.ctl0			= 0x00010001,
+	},
+	{
+		.of_compatible		= "mediatek,mt8183-svs-cpu-big",
+		.sw_id			= SVS_CPU_BIG,
+		.hw_id			= 1,
+		.ops			= &svs_mt8183_banks_ops,
+		.zone_name		= "tzts5",
+		.buck_name		= "vcpu_big",
+		.mtcmos_request		= false,
+		.init01_volt_flag	= SVS_INIT01_VOLT_INC_ONLY,
+		.init01_support		= true,
+		.init02_support		= true,
+		.mon_mode_support	= false,
+		.opp_count		= 16,
+		.freq_base		= 1989000000,
+		.vboot			= 0x30,
+		.volt_step		= 6250,
+		.volt_base		= 500000,
+		.volt_offset		= 0,
+		.vmax			= 0x58,
+		.vmin			= 0x10,
+		.dthi			= 0x1,
+		.dtlo			= 0xfe,
+		.det_window		= 0xa28,
+		.det_max		= 0xffff,
+		.age_config		= 0x555555,
+		.agem			= 0x0,
+		.dc_config		= 0x555555,
+		.dvt_fixed		= 0x7,
+		.vco			= 0x10,
+		.chkshift		= 0x77,
+		.upper_temp_bound	= 0x64,
+		.lower_temp_bound	= 0xb2,
+		.low_temp_threashold	= 25000,
+		.low_temp_offset	= 0,
+		.coresel		= 0x8fff0001,
+		.systemclk_en		= BIT(31),
+		.intst			= BIT(1),
+		.ctl0			= 0x00000001,
+	},
+	{
+		.of_compatible		= "mediatek,mt8183-svs-cci",
+		.sw_id			= SVS_CCI,
+		.hw_id			= 2,
+		.ops			= &svs_mt8183_banks_ops,
+		.zone_name		= "tzts4",
+		.buck_name		= "vcci",
+		.mtcmos_request		= false,
+		.init01_volt_flag	= SVS_INIT01_VOLT_INC_ONLY,
+		.init01_support		= true,
+		.init02_support		= true,
+		.mon_mode_support	= false,
+		.opp_count		= 16,
+		.freq_base		= 1196000000,
+		.vboot			= 0x30,
+		.volt_step		= 6250,
+		.volt_base		= 500000,
+		.volt_offset		= 0,
+		.vmax			= 0x64,
+		.vmin			= 0x10,
+		.dthi			= 0x1,
+		.dtlo			= 0xfe,
+		.det_window		= 0xa28,
+		.det_max		= 0xffff,
+		.age_config		= 0x555555,
+		.agem			= 0x0,
+		.dc_config		= 0x555555,
+		.dvt_fixed		= 0x7,
+		.vco			= 0x10,
+		.chkshift		= 0x77,
+		.upper_temp_bound	= 0x64,
+		.lower_temp_bound	= 0xb2,
+		.low_temp_threashold	= 25000,
+		.low_temp_offset	= 0,
+		.coresel		= 0x8fff0002,
+		.systemclk_en		= BIT(31),
+		.intst			= BIT(2),
+		.ctl0			= 0x00100003,
+	},
+	{
+		.of_compatible		= "mediatek,mt8183-svs-gpu",
+		.sw_id			= SVS_GPU,
+		.hw_id			= 3,
+		.ops			= &svs_mt8183_banks_ops,
+		.zone_name		= "tzts2",
+		.buck_name		= "vgpu",
+		.mtcmos_request		= true,
+		.init01_volt_flag	= SVS_INIT01_VOLT_INC_ONLY,
+		.init01_support		= true,
+		.init02_support		= true,
+		.mon_mode_support	= true,
+		.opp_count		= 16,
+		.freq_base		= 900000000,
+		.vboot			= 0x30,
+		.volt_step		= 6250,
+		.volt_base		= 500000,
+		.volt_offset		= 0,
+		.vmax			= 0x40,
+		.vmin			= 0x14,
+		.dthi			= 0x1,
+		.dtlo			= 0xfe,
+		.det_window		= 0xa28,
+		.det_max		= 0xffff,
+		.age_config		= 0x555555,
+		.agem			= 0x0,
+		.dc_config		= 0x555555,
+		.dvt_fixed		= 0x3,
+		.vco			= 0x10,
+		.chkshift		= 0x77,
+		.upper_temp_bound	= 0x64,
+		.lower_temp_bound	= 0xb2,
+		.low_temp_threashold	= 25000,
+		.low_temp_offset	= 3,
+		.coresel		= 0x8fff0003,
+		.systemclk_en		= BIT(31),
+		.intst			= BIT(3),
+		.ctl0			= 0x00050001,
+	},
+};
+
+static const struct svs_platform svs_mt8183_platform = {
+	.name		= "mt8183-svs",
+	.banks		= svs_mt8183_banks,
+	.efuse_parsing	= svs_mt8183_efuse_parsing,
+	.regs		= svs_regs_v2,
+	.fake_efuse	= false,
+	.bank_num	= 4,
+	.efuse_num	= 25,
+	.efuse_check	= 2,
+	.thermal_efuse_num = 3,
+};
+
+static const struct of_device_id mtk_svs_of_match[] = {
+	{
+		.compatible = "mediatek,mt8183-svs",
+		.data = &svs_mt8183_platform,
+	}, {
+		/* sentinel */
+	},
+};
+
+static int svs_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *of_dev_id;
+	struct mtk_svs *svs;
+	int ret;
+	u32 svs_irq;
+
+	svs = devm_kzalloc(&pdev->dev, sizeof(*svs), GFP_KERNEL);
+	if (IS_ERR(svs))
+		return PTR_ERR(svs);
+
+	svs->dev = &pdev->dev;
+	if (!svs->dev->of_node) {
+		pr_err("cannot find device node\n");
+		return -ENODEV;
+	}
+
+	svs->base = of_iomap(svs->dev->of_node, 0);
+	if (IS_ERR(svs->base)) {
+		pr_err("cannot find svs register base\n");
+		return PTR_ERR(svs->base);
+	}
+
+	svs->main_clk = devm_clk_get(svs->dev, "main_clk");
+	if (IS_ERR(svs->main_clk))
+		pr_err("failed to get clock: %ld\n", PTR_ERR(svs->main_clk));
+
+	ret = clk_prepare_enable(svs->main_clk);
+	if (ret) {
+		pr_err("cannot enable main_clk: %d\n", ret);
+		return ret;
+	}
+
+	of_dev_id = of_match_node(mtk_svs_of_match, svs->dev->of_node);
+	if (!of_dev_id || !of_dev_id->data)
+		return -EINVAL;
+
+	svs->platform = of_dev_id->data;
+	dev_set_drvdata(svs->dev, svs);
+
+	svs_irq = irq_of_parse_and_map(svs->dev->of_node, 0);
+	ret = devm_request_threaded_irq(svs->dev, svs_irq, NULL, svs_isr,
+					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+					"mtk-svs", svs);
+	if (ret) {
+		pr_err("register irq(%d) failed: %d\n", svs_irq, ret);
+		return ret;
+	}
+
+	ret = svs_is_support(svs);
+	if (ret)
+		return ret;
+
+	ret = svs_resource_setup(svs);
+	if (ret)
+		return ret;
+
+	ret = svs_start(svs);
+	if (ret)
+		return ret;
+
+	ret = svs_create_svs_procfs(svs);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct dev_pm_ops svs_pm_ops = {
+	.suspend	= svs_suspend,
+	.resume		= svs_resume,
+};
+
+static struct platform_driver svs_driver = {
+	.probe	= svs_probe,
+	.driver	= {
+		.name		= "mtk-svs",
+		.pm		= &svs_pm_ops,
+		.of_match_table	= of_match_ptr(mtk_svs_of_match),
+	},
+};
+
+static int __init svs_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&svs_driver);
+	if (ret) {
+		pr_err("svs platform driver register failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+late_initcall_sync(svs_init);
+
+MODULE_DESCRIPTION("MediaTek SVS Driver v1.0");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/power/mtk_svs.h b/include/linux/power/mtk_svs.h
new file mode 100644
index 000000000000..d5efca8d9dca
--- /dev/null
+++ b/include/linux/power/mtk_svs.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 MediaTek Inc.
+ */
+
+#ifndef __MTK_SVS_H__
+#define __MTK_SVS_H__
+
+#if defined(CONFIG_MTK_SVS)
+unsigned long claim_mtk_svs_lock(void);
+void release_mtk_svs_lock(unsigned long flags);
+#else
+static inline unsigned long claim_mtk_svs_lock(void)
+{
+	return 0;
+}
+
+static inline void release_mtk_svs_lock(unsigned long flags)
+{
+}
+#endif /* CONFIG_MTK_SVS */
+
+#endif /* __MTK_SVS_H__ */
-- 
2.18.0


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

* Re: [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings
  2019-04-30 11:20 ` [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings Roger Lu
@ 2019-04-30 20:31   ` Stephen Boyd
  2019-05-02  6:19     ` Roger Lu
  2019-05-02 21:06     ` Rob Herring
  0 siblings, 2 replies; 11+ messages in thread
From: Stephen Boyd @ 2019-04-30 20:31 UTC (permalink / raw)
  To: Rafael J . Wysocki, Matthias Brugger, Rob Herring, Roger Lu,
	Viresh Kumar
  Cc: Mark Rutland, Nishanth Menon, Kevin Hilman, Roger Lu, devicetree,
	linux-arm-kernel, linux-mediatek, linux-kernel, linux-pm

Quoting Roger Lu (2019-04-30 04:20:10)
> Document the binding for enabling mtk svs on MediaTek SoC.
> 
> Signed-off-by: Roger Lu <roger.lu@mediatek.com>
> ---
>  .../devicetree/bindings/power/mtk-svs.txt     | 70 +++++++++++++++++++
>  1 file changed, 70 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/mtk-svs.txt
> 
> diff --git a/Documentation/devicetree/bindings/power/mtk-svs.txt b/Documentation/devicetree/bindings/power/mtk-svs.txt
> new file mode 100644
> index 000000000000..355329db74ba
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/mtk-svs.txt
> @@ -0,0 +1,70 @@
> +* Mediatek Smart Voltage Scaling (MTK SVS)
> +
> +This describes the device tree binding for the MTK SVS controller
> +which helps provide the optimized CPU/GPU/CCI voltages. This device also
> +needs thermal data to calculate thermal slope for accurately compensate
> +the voltages when temperature change.
> +
> +Required properties:
> +- compatible:
> +  - "mediatek,mt8183-svs" : For MT8183 family of SoCs
> +- reg: Address range of the MTK SVS controller.
> +- interrupts: IRQ for the MTK SVS controller.
> +- clocks, clock-names: Clocks needed for the svs controller. required
> +                       clocks are:
> +                      "main_clk": Main clock needed for register access
> +- nvmem-cells: Phandle to the calibration data provided by a nvmem device.
> +- nvmem-cell-names: Should be "svs-calibration-data" and "calibration-data"
> +- svs_xxx: Phandle of svs_bank device for controlling corresponding opp

Properties shouldn't have underscores in them. Use dashes?

> +           table and power-domains.
> +- vxxx-supply: Phandle to each regulator. vxxx can be "vcpu_little",
> +              "vcpu_big", "vcci" and "vgpu".
> +
> +Example:
> +
> +       svs: svs@1100b000 {
> +               compatible = "mediatek,mt8183-svs";
> +               reg = <0 0x1100b000 0 0x1000>;
> +               interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW 0>;
> +               clocks = <&infracfg CLK_INFRA_THERM>;
> +               clock-names = "main_clk";
> +               nvmem-cells = <&svs_calibration>, <&thermal_calibration>;
> +               nvmem-cell-names = "svs-calibration-data", "calibration-data";
> +
> +               svs_cpu_little: svs_cpu_little {
> +                       compatible = "mediatek,mt8183-svs-cpu-little";
> +                       operating-points-v2 = <&cluster0_opp>;
> +               };
> +
> +               svs_cpu_big: svs_cpu_big {
> +                       compatible = "mediatek,mt8183-svs-cpu-big";
> +                       operating-points-v2 = <&cluster1_opp>;
> +               };
> +
> +               svs_cci: svs_cci {
> +                       compatible = "mediatek,mt8183-svs-cci";
> +                       operating-points-v2 = <&cluster2_opp>;
> +               };
> +
> +               svs_gpu: svs_gpu {
> +                       compatible = "mediatek,mt8183-svs-gpu";
> +                       power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> +                       operating-points-v2 = <&gpu_opp_table>;
> +               };

It looks like you need multiple OPPs for a single device, because it has
different independent power supplies it wants to associate the OPP
tables with? Why can't these OPP tables be attached to the devices that
use them, i.e. CPU, GPU, CCI, etc.? Seems odd that those devices don't
have OPP tables that this hardware block can look up somehow. Similarly,
the power domains should probably be part of the devices that are using
them and not these sub-nodes that are mirroring the other hardware
blocks in the system?

> +       };
> +
> +       &svs_cpu_little {

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

* Re: [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings
  2019-04-30 20:31   ` Stephen Boyd
@ 2019-05-02  6:19     ` Roger Lu
  2019-05-03 21:08       ` Stephen Boyd
  2019-05-02 21:06     ` Rob Herring
  1 sibling, 1 reply; 11+ messages in thread
From: Roger Lu @ 2019-05-02  6:19 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Rafael J . Wysocki, Matthias Brugger, Rob Herring, Viresh Kumar,
	Mark Rutland, Nishanth Menon, devicetree, linux-pm, Kevin Hilman,
	linux-kernel, linux-mediatek, linux-arm-kernel, Angus.Lin,
	Andy-YT.Liu

Dear Stephen,

Thanks for the review.

On Tue, 2019-04-30 at 13:31 -0700, Stephen Boyd wrote:
> Quoting Roger Lu (2019-04-30 04:20:10)
> > Document the binding for enabling mtk svs on MediaTek SoC.
> > 
> > Signed-off-by: Roger Lu <roger.lu@mediatek.com>
> > ---
> >  .../devicetree/bindings/power/mtk-svs.txt     | 70 +++++++++++++++++++
> >  1 file changed, 70 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/power/mtk-svs.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/power/mtk-svs.txt b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > new file mode 100644
> > index 000000000000..355329db74ba
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > @@ -0,0 +1,70 @@
> > +* Mediatek Smart Voltage Scaling (MTK SVS)
> > +
> > +This describes the device tree binding for the MTK SVS controller
> > +which helps provide the optimized CPU/GPU/CCI voltages. This device also
> > +needs thermal data to calculate thermal slope for accurately compensate
> > +the voltages when temperature change.
> > +
> > +Required properties:
> > +- compatible:
> > +  - "mediatek,mt8183-svs" : For MT8183 family of SoCs
> > +- reg: Address range of the MTK SVS controller.
> > +- interrupts: IRQ for the MTK SVS controller.
> > +- clocks, clock-names: Clocks needed for the svs controller. required
> > +                       clocks are:
> > +                      "main_clk": Main clock needed for register access
> > +- nvmem-cells: Phandle to the calibration data provided by a nvmem device.
> > +- nvmem-cell-names: Should be "svs-calibration-data" and "calibration-data"
> > +- svs_xxx: Phandle of svs_bank device for controlling corresponding opp
> 
> Properties shouldn't have underscores in them. Use dashes?
Ok. I'll use dashes.

> 
> > +           table and power-domains.
> > +- vxxx-supply: Phandle to each regulator. vxxx can be "vcpu_little",
> > +              "vcpu_big", "vcci" and "vgpu".
> > +
> > +Example:
> > +
> > +       svs: svs@1100b000 {
> > +               compatible = "mediatek,mt8183-svs";
> > +               reg = <0 0x1100b000 0 0x1000>;
> > +               interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW 0>;
> > +               clocks = <&infracfg CLK_INFRA_THERM>;
> > +               clock-names = "main_clk";
> > +               nvmem-cells = <&svs_calibration>, <&thermal_calibration>;
> > +               nvmem-cell-names = "svs-calibration-data", "calibration-data";
> > +
> > +               svs_cpu_little: svs_cpu_little {
> > +                       compatible = "mediatek,mt8183-svs-cpu-little";
> > +                       operating-points-v2 = <&cluster0_opp>;
> > +               };
> > +
> > +               svs_cpu_big: svs_cpu_big {
> > +                       compatible = "mediatek,mt8183-svs-cpu-big";
> > +                       operating-points-v2 = <&cluster1_opp>;
> > +               };
> > +
> > +               svs_cci: svs_cci {
> > +                       compatible = "mediatek,mt8183-svs-cci";
> > +                       operating-points-v2 = <&cluster2_opp>;
> > +               };
> > +
> > +               svs_gpu: svs_gpu {
> > +                       compatible = "mediatek,mt8183-svs-gpu";
> > +                       power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> > +                       operating-points-v2 = <&gpu_opp_table>;
> > +               };
> 
> It looks like you need multiple OPPs for a single device, because it has
> different independent power supplies it wants to associate the OPP
> tables with?
Yes. SVS has different controllers inside the hardware in order to
calculate and optimize different OPP table voltage part.

> Why can't these OPP tables be attached to the devices that
> use them, i.e. CPU, GPU, CCI, etc.? Seems odd that those devices don't
> have OPP tables that this hardware block can look up somehow.
Those OPP tables are attached by our DVFS node (please refers below
patch). SVS just shares with their OPP table and help optimize these OPP
tables' voltage part.

Add cpufreq DTS node to the mt8183 and mt8183-evb
https://patchwork.kernel.org/patch/10921675/


> Similarly,
> the power domains should probably be part of the devices that are using
> them and not these sub-nodes that are mirroring the other hardware
> blocks in the system?
Oh. There is a svs controller in GPU power-domain. We need to turn on
GPU power so that svs controller can work functionally. Therefore, we
add GPU power-domains in our svs_gpu sub-node.


> 
> > +       };
> > +
> > +       &svs_cpu_little {
> 
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek



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

* Re: [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings
  2019-04-30 20:31   ` Stephen Boyd
  2019-05-02  6:19     ` Roger Lu
@ 2019-05-02 21:06     ` Rob Herring
  1 sibling, 0 replies; 11+ messages in thread
From: Rob Herring @ 2019-05-02 21:06 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Rafael J . Wysocki, Matthias Brugger, Roger Lu, Viresh Kumar,
	Mark Rutland, Nishanth Menon, Kevin Hilman, devicetree,
	linux-arm-kernel, linux-mediatek, linux-kernel, linux-pm

On Tue, Apr 30, 2019 at 01:31:32PM -0700, Stephen Boyd wrote:
> Quoting Roger Lu (2019-04-30 04:20:10)
> > Document the binding for enabling mtk svs on MediaTek SoC.
> > 
> > Signed-off-by: Roger Lu <roger.lu@mediatek.com>
> > ---
> >  .../devicetree/bindings/power/mtk-svs.txt     | 70 +++++++++++++++++++
> >  1 file changed, 70 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/power/mtk-svs.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/power/mtk-svs.txt b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > new file mode 100644
> > index 000000000000..355329db74ba
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > @@ -0,0 +1,70 @@
> > +* Mediatek Smart Voltage Scaling (MTK SVS)
> > +
> > +This describes the device tree binding for the MTK SVS controller
> > +which helps provide the optimized CPU/GPU/CCI voltages. This device also
> > +needs thermal data to calculate thermal slope for accurately compensate
> > +the voltages when temperature change.
> > +
> > +Required properties:
> > +- compatible:
> > +  - "mediatek,mt8183-svs" : For MT8183 family of SoCs
> > +- reg: Address range of the MTK SVS controller.
> > +- interrupts: IRQ for the MTK SVS controller.
> > +- clocks, clock-names: Clocks needed for the svs controller. required
> > +                       clocks are:
> > +                      "main_clk": Main clock needed for register access
> > +- nvmem-cells: Phandle to the calibration data provided by a nvmem device.
> > +- nvmem-cell-names: Should be "svs-calibration-data" and "calibration-data"
> > +- svs_xxx: Phandle of svs_bank device for controlling corresponding opp
> 
> Properties shouldn't have underscores in them. Use dashes?

It's also a node, not a property.

> 
> > +           table and power-domains.
> > +- vxxx-supply: Phandle to each regulator. vxxx can be "vcpu_little",
> > +              "vcpu_big", "vcci" and "vgpu".

Just list each property instead of the indirection with 'xxx'. Though 
here to, these should be in the nodes actually getting the power.

> > +
> > +Example:
> > +
> > +       svs: svs@1100b000 {
> > +               compatible = "mediatek,mt8183-svs";
> > +               reg = <0 0x1100b000 0 0x1000>;
> > +               interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW 0>;
> > +               clocks = <&infracfg CLK_INFRA_THERM>;
> > +               clock-names = "main_clk";
> > +               nvmem-cells = <&svs_calibration>, <&thermal_calibration>;
> > +               nvmem-cell-names = "svs-calibration-data", "calibration-data";
> > +
> > +               svs_cpu_little: svs_cpu_little {
> > +                       compatible = "mediatek,mt8183-svs-cpu-little";

Not documented. Though I think the child nodes should be removed if you 
do as Stephen suggests below.

> > +                       operating-points-v2 = <&cluster0_opp>;
> > +               };
> > +
> > +               svs_cpu_big: svs_cpu_big {
> > +                       compatible = "mediatek,mt8183-svs-cpu-big";
> > +                       operating-points-v2 = <&cluster1_opp>;
> > +               };
> > +
> > +               svs_cci: svs_cci {
> > +                       compatible = "mediatek,mt8183-svs-cci";
> > +                       operating-points-v2 = <&cluster2_opp>;
> > +               };
> > +
> > +               svs_gpu: svs_gpu {
> > +                       compatible = "mediatek,mt8183-svs-gpu";
> > +                       power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> > +                       operating-points-v2 = <&gpu_opp_table>;
> > +               };
> 
> It looks like you need multiple OPPs for a single device, because it has
> different independent power supplies it wants to associate the OPP
> tables with? Why can't these OPP tables be attached to the devices that
> use them, i.e. CPU, GPU, CCI, etc.? Seems odd that those devices don't
> have OPP tables that this hardware block can look up somehow. Similarly,
> the power domains should probably be part of the devices that are using
> them and not these sub-nodes that are mirroring the other hardware
> blocks in the system?
> 
> > +       };
> > +
> > +       &svs_cpu_little {

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

* Re: [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings
  2019-05-02  6:19     ` Roger Lu
@ 2019-05-03 21:08       ` Stephen Boyd
  2019-05-07  7:50         ` Roger Lu
  0 siblings, 1 reply; 11+ messages in thread
From: Stephen Boyd @ 2019-05-03 21:08 UTC (permalink / raw)
  To: Roger Lu
  Cc: Rafael J . Wysocki, Matthias Brugger, Rob Herring, Viresh Kumar,
	Mark Rutland, Nishanth Menon, devicetree, linux-pm, Kevin Hilman,
	linux-kernel, linux-mediatek, linux-arm-kernel, Angus.Lin,
	Andy-YT.Liu

Quoting Roger Lu (2019-05-01 23:19:31)
> On Tue, 2019-04-30 at 13:31 -0700, Stephen Boyd wrote:
> > Quoting Roger Lu (2019-04-30 04:20:10)
> > > diff --git a/Documentation/devicetree/bindings/power/mtk-svs.txt b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > > new file mode 100644
> > > index 000000000000..355329db74ba
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/power/mtk-svs.txt
[..]
> > > +
> > > +               svs_gpu: svs_gpu {
> > > +                       compatible = "mediatek,mt8183-svs-gpu";
> > > +                       power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> > > +                       operating-points-v2 = <&gpu_opp_table>;
> > > +               };
> > 
> > It looks like you need multiple OPPs for a single device, because it has
> > different independent power supplies it wants to associate the OPP
> > tables with?
> Yes. SVS has different controllers inside the hardware in order to
> calculate and optimize different OPP table voltage part.

So is there more than one SVS register region that needs certain devices
to be powered on or at least have their power domain enabled so that the
SVS hardware can read the voltage and adjust accordingly? I should read
the driver I suppose.

> 
> > Why can't these OPP tables be attached to the devices that
> > use them, i.e. CPU, GPU, CCI, etc.? Seems odd that those devices don't
> > have OPP tables that this hardware block can look up somehow.
> Those OPP tables are attached by our DVFS node (please refers below
> patch). SVS just shares with their OPP table and help optimize these OPP
> tables' voltage part.
> 
> Add cpufreq DTS node to the mt8183 and mt8183-evb
> https://patchwork.kernel.org/patch/10921675/

Cool thanks for the pointer.

> 
> 
> > Similarly,
> > the power domains should probably be part of the devices that are using
> > them and not these sub-nodes that are mirroring the other hardware
> > blocks in the system?
> Oh. There is a svs controller in GPU power-domain. We need to turn on
> GPU power so that svs controller can work functionally. Therefore, we
> add GPU power-domains in our svs_gpu sub-node.
> 
> 

Sorry, I'm not really following what you're saying too closely. I think
I get it but it sounds complicated.

I'm mostly wondering if having properties like svs-gpu = <&gpu_node>,
and svs-cci = <&cci_node> would work for you. The idea would be to link
this hardware block to the nodes that it's going to adjust the OPPs of.
Once you have the node, use some sort of OPP API to get the OPP table
for a device_node and adjust it at runtime for the current OPP. It
sounds like it might be a little more complicated if the hardware goes
haywire when the device like GPU is powered down and the power domain is
shut off. Hopefully it isn't though, so that the driver can mostly sit
on top of the SVS hardware and poke OPP every once and a while when the
voltage needs to change, regardless of the power state of the device.


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

* Re: [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings
  2019-05-03 21:08       ` Stephen Boyd
@ 2019-05-07  7:50         ` Roger Lu
  2019-05-07 20:49           ` Stephen Boyd
  0 siblings, 1 reply; 11+ messages in thread
From: Roger Lu @ 2019-05-07  7:50 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Mark Rutland, Nishanth Menon, Angus.Lin, devicetree,
	Viresh Kumar, linux-pm, Rafael J . Wysocki, linux-kernel,
	Kevin Hilman, Rob Herring, linux-mediatek, Andy-YT.Liu,
	Matthias Brugger, linux-arm-kernel, HenryC.Chen

Dear Stephen,

Sorry for the late reply.

On Fri, 2019-05-03 at 14:08 -0700, Stephen Boyd wrote:
> Quoting Roger Lu (2019-05-01 23:19:31)
> > On Tue, 2019-04-30 at 13:31 -0700, Stephen Boyd wrote:
> > > Quoting Roger Lu (2019-04-30 04:20:10)
> > > > diff --git a/Documentation/devicetree/bindings/power/mtk-svs.txt b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > > > new file mode 100644
> > > > index 000000000000..355329db74ba
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/power/mtk-svs.txt
> [..]
> > > > +
> > > > +               svs_gpu: svs_gpu {
> > > > +                       compatible = "mediatek,mt8183-svs-gpu";
> > > > +                       power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> > > > +                       operating-points-v2 = <&gpu_opp_table>;
> > > > +               };
> > > 
> > > It looks like you need multiple OPPs for a single device, because it has
> > > different independent power supplies it wants to associate the OPP
> > > tables with?
> > Yes. SVS has different controllers inside the hardware in order to
> > calculate and optimize different OPP table voltage part.
> 
> So is there more than one SVS register region that needs certain devices
> to be powered on or at least have their power domain enabled so that the
> SVS hardware can read the voltage and adjust accordingly? I should read
> the driver I suppose.
No, basically, each SVS controller (aka SVS bank) only has one SVS
register region that needs to be powered on for the init.
In MT8183 SVS case, SVS has four controllers (banks). Each SVS bank
needs corresponding power domain to be on for its init.

#SVS bank corresponding power domain
svs_cpu_little: Needs CPU-A53 power on for init
svs_cpu_big: Needs CPU-A73 power on for init
svs_cci: Needs CPU-A53 power on for init
svs_gpu: Needs MFG_2D power on for init

P.S SVS driver will use pm_runtime_get_sync() to turn on power before
svs bank init and pm_runtime_put_sync() to turn off power power after
svs bank init.

> 
> > 
> > > Why can't these OPP tables be attached to the devices that
> > > use them, i.e. CPU, GPU, CCI, etc.? Seems odd that those devices don't
> > > have OPP tables that this hardware block can look up somehow.
> > Those OPP tables are attached by our DVFS node (please refers below
> > patch). SVS just shares with their OPP table and help optimize these OPP
> > tables' voltage part.
> > 
> > Add cpufreq DTS node to the mt8183 and mt8183-evb
> > https://patchwork.kernel.org/patch/10921675/
> 
> Cool thanks for the pointer.
> 
> > 
> > 
> > > Similarly,
> > > the power domains should probably be part of the devices that are using
> > > them and not these sub-nodes that are mirroring the other hardware
> > > blocks in the system?
> > Oh. There is a svs controller in GPU power-domain. We need to turn on
> > GPU power so that svs controller can work functionally. Therefore, we
> > add GPU power-domains in our svs_gpu sub-node.
> > 
> > 
> 
> Sorry, I'm not really following what you're saying too closely. I think
> I get it but it sounds complicated.
> 
> I'm mostly wondering if having properties like svs-gpu = <&gpu_node>,
> and svs-cci = <&cci_node> would work for you. The idea would be to link
> this hardware block to the nodes that it's going to adjust the OPPs of.
> Once you have the node, use some sort of OPP API to get the OPP table
> for a device_node and adjust it at runtime for the current OPP.
Yes, I understand your idea. Thank you. I share my design purpose and
the troubles I encountered when linking other hardware block.

#my design purpose
1. SVS bank doesn't need all the resources in other device node like
cci_node. Therefore, I model SVS sub-nodes to declare what svs bank
needs.

#troubles - linking other hardware block
1. I don't know how to get cpu devcie after we link CPU node
(svs_cpu_little = <cpu0>). I use "get_cpu_device(unsigned cpu)" in Linux
driver to attain cpuX device generally.
2. Our MT8183 has three gpu-related node as below, svs_gpu need the
reference of gpu (OPP table) & gpu_core2 (power-domain MFG_2D) to make
sure svs_gpu can init and update gpu OPP table. I don't know how to
refer two nodes by one property. Therefore, I model a svs_gpu to declare
what it needs.

gpu: mali@13040000 {
	...
	power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_CORE0>;
	operating-points-v2 = <&gpu_opp_table>;
	...
}

gpu_core1: mali_gpu_core1 {
	...
	power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_CORE1>;
};

gpu_core2: mali_gpu_core2 {
	...
	power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
};

P.S MT8183 GPU won't do upstream. So, there is no patchwork weblink to
refer.

> It sounds like it might be a little more complicated if the hardware goes
> haywire when the device like GPU is powered down and the power domain is
> shut off. Hopefully it isn't though, so that the driver can mostly sit
> on top of the SVS hardware and poke OPP every once and a while when the
> voltage needs to change, regardless of the power state of the device.

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



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

* Re: [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings
  2019-05-07  7:50         ` Roger Lu
@ 2019-05-07 20:49           ` Stephen Boyd
  2019-05-13  0:34             ` Roger Lu
  0 siblings, 1 reply; 11+ messages in thread
From: Stephen Boyd @ 2019-05-07 20:49 UTC (permalink / raw)
  To: Roger Lu
  Cc: Mark Rutland, Nishanth Menon, Angus.Lin, devicetree,
	Viresh Kumar, linux-pm, Rafael J . Wysocki, linux-kernel,
	Kevin Hilman, Rob Herring, linux-mediatek, Andy-YT.Liu,
	Matthias Brugger, linux-arm-kernel, HenryC.Chen

Quoting Roger Lu (2019-05-07 00:50:57)
> Dear Stephen,
> 
> Sorry for the late reply.
> 
> On Fri, 2019-05-03 at 14:08 -0700, Stephen Boyd wrote:
> > Quoting Roger Lu (2019-05-01 23:19:31)
> > > On Tue, 2019-04-30 at 13:31 -0700, Stephen Boyd wrote:
> > > > Quoting Roger Lu (2019-04-30 04:20:10)
> > > > > diff --git a/Documentation/devicetree/bindings/power/mtk-svs.txt b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > > > > new file mode 100644
> > > > > index 000000000000..355329db74ba
> > > > > --- /dev/null
> > > > > +++ b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > [..]
> > > > > +
> > > > > +               svs_gpu: svs_gpu {
> > > > > +                       compatible = "mediatek,mt8183-svs-gpu";
> > > > > +                       power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> > > > > +                       operating-points-v2 = <&gpu_opp_table>;
> > > > > +               };
> > > > 
> > > > It looks like you need multiple OPPs for a single device, because it has
> > > > different independent power supplies it wants to associate the OPP
> > > > tables with?
> > > Yes. SVS has different controllers inside the hardware in order to
> > > calculate and optimize different OPP table voltage part.
> > 
> > So is there more than one SVS register region that needs certain devices
> > to be powered on or at least have their power domain enabled so that the
> > SVS hardware can read the voltage and adjust accordingly? I should read
> > the driver I suppose.
> No, basically, each SVS controller (aka SVS bank) only has one SVS
> register region that needs to be powered on for the init.
> In MT8183 SVS case, SVS has four controllers (banks). Each SVS bank
> needs corresponding power domain to be on for its init.
> 
> #SVS bank corresponding power domain
> svs_cpu_little: Needs CPU-A53 power on for init
> svs_cpu_big: Needs CPU-A73 power on for init
> svs_cci: Needs CPU-A53 power on for init
> svs_gpu: Needs MFG_2D power on for init
> 
> P.S SVS driver will use pm_runtime_get_sync() to turn on power before
> svs bank init and pm_runtime_put_sync() to turn off power power after
> svs bank init.

Ok. How are you making sure that certain CPUs are powered on?

> 
> > 
> > > 
> > > > Why can't these OPP tables be attached to the devices that
> > > > use them, i.e. CPU, GPU, CCI, etc.? Seems odd that those devices don't
> > > > have OPP tables that this hardware block can look up somehow.
> > > Those OPP tables are attached by our DVFS node (please refers below
> > > patch). SVS just shares with their OPP table and help optimize these OPP
> > > tables' voltage part.
> > > 
> > > Add cpufreq DTS node to the mt8183 and mt8183-evb
> > > https://patchwork.kernel.org/patch/10921675/
> > 
> > Cool thanks for the pointer.
> > 
> > > 
> > > 
> > > > Similarly,
> > > > the power domains should probably be part of the devices that are using
> > > > them and not these sub-nodes that are mirroring the other hardware
> > > > blocks in the system?
> > > Oh. There is a svs controller in GPU power-domain. We need to turn on
> > > GPU power so that svs controller can work functionally. Therefore, we
> > > add GPU power-domains in our svs_gpu sub-node.
> > > 
> > > 
> > 
> > Sorry, I'm not really following what you're saying too closely. I think
> > I get it but it sounds complicated.
> > 
> > I'm mostly wondering if having properties like svs-gpu = <&gpu_node>,
> > and svs-cci = <&cci_node> would work for you. The idea would be to link
> > this hardware block to the nodes that it's going to adjust the OPPs of.
> > Once you have the node, use some sort of OPP API to get the OPP table
> > for a device_node and adjust it at runtime for the current OPP.
> Yes, I understand your idea. Thank you. I share my design purpose and
> the troubles I encountered when linking other hardware block.
> 
> #my design purpose
> 1. SVS bank doesn't need all the resources in other device node like
> cci_node. Therefore, I model SVS sub-nodes to declare what svs bank
> needs.

Do you mean that there are other properties in the cci_node that the SVS
hardware block doesn't use? That doesn't sound like a problem to me. I
view nodes in the SoC bus as all memory mapped IO devices and it sounds
like SVS is a hardware IP core that's off to the side in the system that
has some sensors that goes into various other IP blocks in the system.
It's correct to model the registers and interrupts, etc. as one node for
the one hardware block that's delivered by the hardware engineers.

> 
> #troubles - linking other hardware block
> 1. I don't know how to get cpu devcie after we link CPU node
> (svs_cpu_little = <cpu0>). I use "get_cpu_device(unsigned cpu)" in Linux
> driver to attain cpuX device generally.

This should probably be some sort of list property that points to all
the CPUs in the little and big clusters. Then the code can iterate
through the node pointers and look for an OPP table in any of them by
combining of_cpu_node_to_id() with get_cpu_device()?

> 2. Our MT8183 has three gpu-related node as below, svs_gpu need the
> reference of gpu (OPP table) & gpu_core2 (power-domain MFG_2D) to make
> sure svs_gpu can init and update gpu OPP table. I don't know how to
> refer two nodes by one property. Therefore, I model a svs_gpu to declare
> what it needs.
> 
> gpu: mali@13040000 {
>         ...
>         power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_CORE0>;
>         operating-points-v2 = <&gpu_opp_table>;
>         ...
> }
> 
> gpu_core1: mali_gpu_core1 {
>         ...
>         power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_CORE1>;
> };
> 
> gpu_core2: mali_gpu_core2 {
>         ...
>         power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> };

These three nodes should be combined into one node for the GPU. The
power domains will need to be referred to by name. Luckily we have
support for multiple power domains in the kernel now so this should
work. Let us know if it doesn't work for some reason.

> 
> P.S MT8183 GPU won't do upstream. So, there is no patchwork weblink to
> refer.

Sure.


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

* Re: [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings
  2019-05-07 20:49           ` Stephen Boyd
@ 2019-05-13  0:34             ` Roger Lu
  0 siblings, 0 replies; 11+ messages in thread
From: Roger Lu @ 2019-05-13  0:34 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Mark Rutland, Nishanth Menon, Angus.Lin, devicetree,
	Viresh Kumar, linux-pm, Rafael J . Wysocki, linux-kernel,
	Kevin Hilman, Rob Herring, linux-mediatek, Andy-YT.Liu,
	Matthias Brugger, linux-arm-kernel, HenryC.Chen, Nick.Fan,
	yt.lee


Dear Stephen,

Sorry for the late reply.

On Tue, 2019-05-07 at 13:49 -0700, Stephen Boyd wrote:
> Quoting Roger Lu (2019-05-07 00:50:57)
> > Dear Stephen,
> > 
> > Sorry for the late reply.
> > 
> > On Fri, 2019-05-03 at 14:08 -0700, Stephen Boyd wrote:
> > > Quoting Roger Lu (2019-05-01 23:19:31)
> > > > On Tue, 2019-04-30 at 13:31 -0700, Stephen Boyd wrote:
> > > > > Quoting Roger Lu (2019-04-30 04:20:10)
> > > > > > diff --git a/Documentation/devicetree/bindings/power/mtk-svs.txt b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > > > > > new file mode 100644
> > > > > > index 000000000000..355329db74ba
> > > > > > --- /dev/null
> > > > > > +++ b/Documentation/devicetree/bindings/power/mtk-svs.txt
> > > [..]
> > > > > > +
> > > > > > +               svs_gpu: svs_gpu {
> > > > > > +                       compatible = "mediatek,mt8183-svs-gpu";
> > > > > > +                       power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> > > > > > +                       operating-points-v2 = <&gpu_opp_table>;
> > > > > > +               };
> > > > > 
> > > > > It looks like you need multiple OPPs for a single device, because it has
> > > > > different independent power supplies it wants to associate the OPP
> > > > > tables with?
> > > > Yes. SVS has different controllers inside the hardware in order to
> > > > calculate and optimize different OPP table voltage part.
> > > 
> > > So is there more than one SVS register region that needs certain devices
> > > to be powered on or at least have their power domain enabled so that the
> > > SVS hardware can read the voltage and adjust accordingly? I should read
> > > the driver I suppose.
> > No, basically, each SVS controller (aka SVS bank) only has one SVS
> > register region that needs to be powered on for the init.
> > In MT8183 SVS case, SVS has four controllers (banks). Each SVS bank
> > needs corresponding power domain to be on for its init.
> > 
> > #SVS bank corresponding power domain
> > svs_cpu_little: Needs CPU-A53 power on for init
> > svs_cpu_big: Needs CPU-A73 power on for init
> > svs_cci: Needs CPU-A53 power on for init
> > svs_gpu: Needs MFG_2D power on for init
> > 
> > P.S SVS driver will use pm_runtime_get_sync() to turn on power before
> > svs bank init and pm_runtime_put_sync() to turn off power power after
> > svs bank init.
> 
> Ok. How are you making sure that certain CPUs are powered on?

Before SVS banks init, We add a qos request to prevent CPU from entering
idle for making sure CPU powers are on. Also, we'll remove this qos
request after SVS banks init are done.

pm_qos_add_request(&qos_request, PM_QOS_CPU_DMA_LATENCY, 0); //prevent CPU idle
pm_qos_remove_request(&qos_request); //release above request

> 
> > 
> > > 
> > > > 
> > > > > Why can't these OPP tables be attached to the devices that
> > > > > use them, i.e. CPU, GPU, CCI, etc.? Seems odd that those devices don't
> > > > > have OPP tables that this hardware block can look up somehow.
> > > > Those OPP tables are attached by our DVFS node (please refers below
> > > > patch). SVS just shares with their OPP table and help optimize these OPP
> > > > tables' voltage part.
> > > > 
> > > > Add cpufreq DTS node to the mt8183 and mt8183-evb
> > > > https://patchwork.kernel.org/patch/10921675/
> > > 
> > > Cool thanks for the pointer.
> > > 
> > > > 
> > > > 
> > > > > Similarly,
> > > > > the power domains should probably be part of the devices that are using
> > > > > them and not these sub-nodes that are mirroring the other hardware
> > > > > blocks in the system?
> > > > Oh. There is a svs controller in GPU power-domain. We need to turn on
> > > > GPU power so that svs controller can work functionally. Therefore, we
> > > > add GPU power-domains in our svs_gpu sub-node.
> > > > 
> > > > 
> > > 
> > > Sorry, I'm not really following what you're saying too closely. I think
> > > I get it but it sounds complicated.
> > > 
> > > I'm mostly wondering if having properties like svs-gpu = <&gpu_node>,
> > > and svs-cci = <&cci_node> would work for you. The idea would be to link
> > > this hardware block to the nodes that it's going to adjust the OPPs of.
> > > Once you have the node, use some sort of OPP API to get the OPP table
> > > for a device_node and adjust it at runtime for the current OPP.
> > Yes, I understand your idea. Thank you. I share my design purpose and
> > the troubles I encountered when linking other hardware block.
> > 
> > #my design purpose
> > 1. SVS bank doesn't need all the resources in other device node like
> > cci_node. Therefore, I model SVS sub-nodes to declare what svs bank
> > needs.
> 
> Do you mean that there are other properties in the cci_node that the SVS
> hardware block doesn't use? That doesn't sound like a problem to me. I
> view nodes in the SoC bus as all memory mapped IO devices and it sounds
> like SVS is a hardware IP core that's off to the side in the system that
> has some sensors that goes into various other IP blocks in the system.
> It's correct to model the registers and interrupts, etc. as one node for
> the one hardware block that's delivered by the hardware engineers.
> 
> > 
> > #troubles - linking other hardware block
> > 1. I don't know how to get cpu devcie after we link CPU node
> > (svs_cpu_little = <cpu0>). I use "get_cpu_device(unsigned cpu)" in Linux
> > driver to attain cpuX device generally.
> 
> This should probably be some sort of list property that points to all
> the CPUs in the little and big clusters. Then the code can iterate
> through the node pointers and look for an OPP table in any of them by
> combining of_cpu_node_to_id() with get_cpu_device()?

Okay. However, it becomes complicated when SVS banks link to other
hardware block to get desired OPP table and power-domains

1. We cannot guarantee the design decision of our SVS Hardware designer. They might put
controller in the hardware block whose dts node doesn't provide the OPP table and
power-domains that SVS bank wants.

#For example:
If there is a SVS bank that wants GPU OPP table but it needs two power-domains(GPU, Vencoder)
for init. Then, it becomes complicated and confusing when SVS sub-node tries to link many nodes.
Therefore, we want to add a sub-node to focus on what SVS bank requires.

2. SVS banks depend on other hardware's power only. All the SVS banks' control registers
are in SVS hardware. So, we think It's good that SVS sub-node describe what their bank requires.

3. We want SVS driver to have a generic way to attain device for using pm_runtime and OPP API.
If SVS banks link to CPU and other subsys device node. It means that SVS driver have to maintain
different topology in order to get CPU and other subsys device (e.g cci).

> 
> > 2. Our MT8183 has three gpu-related node as below, svs_gpu need the
> > reference of gpu (OPP table) & gpu_core2 (power-domain MFG_2D) to make
> > sure svs_gpu can init and update gpu OPP table. I don't know how to
> > refer two nodes by one property. Therefore, I model a svs_gpu to declare
> > what it needs.
> > 
> > gpu: mali@13040000 {
> >         ...
> >         power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_CORE0>;
> >         operating-points-v2 = <&gpu_opp_table>;
> >         ...
> > }
> > 
> > gpu_core1: mali_gpu_core1 {
> >         ...
> >         power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_CORE1>;
> > };
> > 
> > gpu_core2: mali_gpu_core2 {
> >         ...
> >         power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
> > };
> 
> These three nodes should be combined into one node for the GPU. The
> power domains will need to be referred to by name. Luckily we have
> support for multiple power domains in the kernel now so this should
> work. Let us know if it doesn't work for some reason.

Cools. I'll inform our GPU maintainer to check it. Thanks a lot.

> 
> > 
> > P.S MT8183 GPU won't do upstream. So, there is no patchwork weblink to
> > refer.
> 
> Sure.
> 





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

end of thread, other threads:[~2019-05-13  0:35 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-30 11:20 PM / AVS: SVS: Introduce SVS engine Roger Lu
2019-04-30 11:20 ` [RFC v1 1/3] dt-bindings: soc: add mtk svs dt-bindings Roger Lu
2019-04-30 20:31   ` Stephen Boyd
2019-05-02  6:19     ` Roger Lu
2019-05-03 21:08       ` Stephen Boyd
2019-05-07  7:50         ` Roger Lu
2019-05-07 20:49           ` Stephen Boyd
2019-05-13  0:34             ` Roger Lu
2019-05-02 21:06     ` Rob Herring
2019-04-30 11:20 ` [RFC v1 2/3] arm64: dts: mt8183: add svs device information Roger Lu
2019-04-30 11:20 ` [RFC v1 3/3] PM / AVS: SVS: Introduce SVS engine Roger Lu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).