diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
index 12998b0..9dc6670 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -1,3 +1,3 @@
-obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
+obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o mtk-scpsys-ext.o
 obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
 obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
diff --git a/drivers/soc/mediatek/mtk-infracfg.c b/drivers/soc/mediatek/mtk-infracfg.c
index 958861c..11eadf8 100644
--- a/drivers/soc/mediatek/mtk-infracfg.c
+++ b/drivers/soc/mediatek/mtk-infracfg.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015 Pengutronix, Sascha Hauer <kernel@pengutronix.de>
  *
@@ -15,6 +16,7 @@
 #include <linux/jiffies.h>
 #include <linux/regmap.h>
 #include <linux/soc/mediatek/infracfg.h>
+#include <linux/soc/mediatek/scpsys-ext.h>
 #include <asm/processor.h>
 
 #define MTK_POLL_DELAY_US   10
@@ -26,62 +28,176 @@
 #define INFRA_TOPAXI_PROTECTEN_CLR	0x0264
 
 /**
- * mtk_infracfg_set_bus_protection - enable bus protection
- * @regmap: The infracfg regmap
- * @mask: The mask containing the protection bits to be enabled.
- * @reg_update: The boolean flag determines to set the protection bits
- *              by regmap_update_bits with enable register(PROTECTEN) or
- *              by regmap_write with set register(PROTECTEN_SET).
+ * mtk_generic_set_cmd - enable bus protection with set register
+ * @regmap: The bus protect regmap
+ * @set_ofs: The set register offset to set corresponding bit to 1.
+ * @sta_ofs: The status register offset to show bus protect enable/disable.
+ * @mask: The mask containing the protection bits to be disabled.
  *
  * This function enables the bus protection bits for disabled power
  * domains so that the system does not hang when some unit accesses the
  * bus while in power down.
  */
-int mtk_infracfg_set_bus_protection(struct regmap *infracfg, u32 mask,
-		bool reg_update)
+int mtk_generic_set_cmd(struct regmap *regmap, u32 set_ofs,
+			u32 sta_ofs, u32 mask)
 {
 	u32 val;
 	int ret;
 
-	if (reg_update)
-		regmap_update_bits(infracfg, INFRA_TOPAXI_PROTECTEN, mask,
-				mask);
-	else
-		regmap_write(infracfg, INFRA_TOPAXI_PROTECTEN_SET, mask);
+	regmap_write(regmap, set_ofs, mask);
 
-	ret = regmap_read_poll_timeout(infracfg, INFRA_TOPAXI_PROTECTSTA1,
-				       val, (val & mask) == mask,
-				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	ret = regmap_read_poll_timeout(regmap, sta_ofs, val,
+				       (val & mask) == mask,
+				       MTK_POLL_DELAY_US,
+				       MTK_POLL_TIMEOUT);
 
 	return ret;
 }
 
 /**
- * mtk_infracfg_clear_bus_protection - disable bus protection
- * @regmap: The infracfg regmap
+ * mtk_generic_clr_cmd - disable bus protection  with clr register
+ * @regmap: The bus protect regmap
+ * @clr_ofs: The clr register offset to clear corresponding bit to 0.
+ * @sta_ofs: The status register offset to show bus protect enable/disable.
  * @mask: The mask containing the protection bits to be disabled.
- * @reg_update: The boolean flag determines to clear the protection bits
- *              by regmap_update_bits with enable register(PROTECTEN) or
- *              by regmap_write with clear register(PROTECTEN_CLR).
  *
  * This function disables the bus protection bits previously enabled with
- * mtk_infracfg_set_bus_protection.
+ * mtk_set_bus_protection.
  */
 
-int mtk_infracfg_clear_bus_protection(struct regmap *infracfg, u32 mask,
-		bool reg_update)
+int mtk_generic_clr_cmd(struct regmap *regmap, u32 clr_ofs,
+			u32 sta_ofs, u32 mask)
 {
 	int ret;
 	u32 val;
 
-	if (reg_update)
-		regmap_update_bits(infracfg, INFRA_TOPAXI_PROTECTEN, mask, 0);
-	else
-		regmap_write(infracfg, INFRA_TOPAXI_PROTECTEN_CLR, mask);
+	regmap_write(regmap, clr_ofs, mask);
 
-	ret = regmap_read_poll_timeout(infracfg, INFRA_TOPAXI_PROTECTSTA1,
-				       val, !(val & mask),
-				       MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+	ret = regmap_read_poll_timeout(regmap, sta_ofs, val,
+				       !(val & mask),
+				       MTK_POLL_DELAY_US,
+				       MTK_POLL_TIMEOUT);
+	return ret;
+}
+
+/**
+ * mtk_generic_enable_cmd - enable bus protection with upd register
+ * @regmap: The bus protect regmap
+ * @upd_ofs: The update register offset to directly rewrite value to
+ *              corresponding bit.
+ * @sta_ofs: The status register offset to show bus protect enable/disable.
+ * @mask: The mask containing the protection bits to be disabled.
+ *
+ * This function enables the bus protection bits for disabled power
+ * domains so that the system does not hang when some unit accesses the
+ * bus while in power down.
+ */
+int mtk_generic_enable_cmd(struct regmap *regmap, u32 upd_ofs,
+			   u32 sta_ofs, u32 mask)
+{
+	u32 val;
+	int ret;
+
+	regmap_update_bits(regmap, upd_ofs, mask, mask);
 
+	ret = regmap_read_poll_timeout(regmap, sta_ofs, val,
+				       (val & mask) == mask,
+				       MTK_POLL_DELAY_US,
+				       MTK_POLL_TIMEOUT);
 	return ret;
 }
+
+/**
+ * mtk_generic_disable_cmd - disable bus protection with updd register
+ * @regmap: The bus protect regmap
+ * @upd_ofs: The update register offset to directly rewrite value to
+ *              corresponding bit.
+ * @sta_ofs: The status register offset to show bus protect enable/disable.
+ * @mask: The mask containing the protection bits to be disabled.
+ *
+ * This function disables the bus protection bits previously enabled with
+ * mtk_set_bus_protection.
+ */
+
+int mtk_generic_disable_cmd(struct regmap *regmap, u32 upd_ofs,
+			    u32 sta_ofs, u32 mask)
+{
+	int ret;
+	u32 val;
+
+	regmap_update_bits(regmap, upd_ofs, mask, 0);
+
+	ret = regmap_read_poll_timeout(regmap, sta_ofs,
+				       val, !(val & mask),
+				       MTK_POLL_DELAY_US,
+				       MTK_POLL_TIMEOUT);
+	return ret;
+}
+
+/**
+ * mtk_set_bus_protection - enable bus protection
+ * @infracfg: The bus protect regmap, default use infracfg
+ * @mask: The mask containing the protection bits to be enabled.
+ *
+ * This function enables the bus protection bits for disabled power
+ * domains so that the system does not hang when some unit accesses the
+ * bus while in power down.
+ */
+int mtk_infracfg_set_bus_protection(struct regmap *infracfg, u32 mask)
+{
+	return mtk_generic_set_cmd(infracfg,
+				   INFRA_TOPAXI_PROTECTEN_SET,
+				   INFRA_TOPAXI_PROTECTSTA1,
+				   mask);
+}
+
+/**
+ * mtk_clear_bus_protection - disable bus protection
+ * @infracfg: The bus protect regmap, default use infracfg
+ * @mask: The mask containing the protection bits to be disabled.
+ *
+ * This function disables the bus protection bits previously enabled with
+ * mtk_set_bus_protection.
+ */
+
+int mtk_infracfg_clear_bus_protection(struct regmap *infracfg, u32 mask)
+{
+	return mtk_generic_clr_cmd(infracfg,
+				   INFRA_TOPAXI_PROTECTEN_CLR,
+				   INFRA_TOPAXI_PROTECTSTA1,
+				   mask);
+}
+
+/**
+ * mtk_infracfg_enable_bus_protection - enable bus protection
+ * @infracfg: The bus protect regmap, default use infracfg
+ * @mask: The mask containing the protection bits to be disabled.
+ *
+ * This function enables the bus protection bits for disabled power
+ * domains so that the system does not hang when some unit accesses the
+ * bus while in power down.
+ */
+int mtk_infracfg_enable_bus_protection(struct regmap *infracfg, u32 mask)
+{
+	return mtk_generic_enable_cmd(infracfg,
+				      INFRA_TOPAXI_PROTECTEN,
+				      INFRA_TOPAXI_PROTECTSTA1,
+				      mask);
+}
+
+/**
+ * mtk_infracfg_disable_bus_protection - disable bus protection
+ * @infracfg: The bus protect regmap, default use infracfg
+ * @mask: The mask containing the protection bits to be disabled.
+ *
+ * This function disables the bus protection bits previously enabled with
+ * mtk_infracfg_set_bus_protection.
+ */
+
+int mtk_infracfg_disable_bus_protection(struct regmap *infracfg, u32 mask)
+{
+	return mtk_generic_disable_cmd(infracfg,
+				       INFRA_TOPAXI_PROTECTEN,
+				       INFRA_TOPAXI_PROTECTSTA1,
+				       mask);
+}
diff --git a/drivers/soc/mediatek/mtk-scpsys-ext.c b/drivers/soc/mediatek/mtk-scpsys-ext.c
new file mode 100644
index 0000000..965e64d
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-scpsys-ext.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: Owen Chen <owen.chen@mediatek.com>
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/soc/mediatek/infracfg.h>
+#include <linux/soc/mediatek/scpsys-ext.h>
+
+
+#define MAX_CLKS		10
+#define INFRA			"infracfg"
+#define SMIC			"smi_comm"
+
+static LIST_HEAD(ext_clk_map_list);
+static LIST_HEAD(ext_attr_map_list);
+
+static struct regmap *infracfg;
+static struct regmap *smi_comm;
+
+enum regmap_type {
+	IFR_TYPE,
+	SMI_TYPE,
+	MAX_REGMAP_TYPE,
+};
+
+/**
+ * struct ext_reg_ctrl - set multiple register for bus protect
+ * @regmap: The bus protect regmap, 1: infracfg, 2: other master regmap
+ *                  such as SMI.
+ * @set_ofs: The set register offset to set corresponding bit to 1.
+ * @clr_ofs: The clr register offset to clear corresponding bit to 0.
+ * @sta_ofs: The status register offset to show bus protect enable/disable.
+ */
+struct ext_reg_ctrl {
+	enum regmap_type type;
+	u32 set_ofs;
+	u32 clr_ofs;
+	u32 sta_ofs;
+};
+
+/**
+ * struct ext_clk_ctrl - enable multiple clks for bus protect
+ * @clk: The clk need to enable before pwr on/bus protect.
+ * @scpd_n: The name present the scpsys domain where the clks belongs to.
+ * @clk_list: The list node linked to ext_clk_map_list.
+ */
+struct ext_clk_ctrl {
+	struct clk *clk;
+	const char *scpd_n;
+	struct list_head clk_list;
+};
+
+struct bus_mask_ops {
+	int	(*set)(struct regmap *regmap, u32 set_ofs,
+		       u32 sta_ofs, u32 mask);
+	int	(*release)(struct regmap *regmap, u32 clr_ofs,
+			   u32 sta_ofs, u32 mask);
+};
+
+static struct scpsys_ext_attr *__get_attr_parent(const char *parent_n)
+{
+	struct scpsys_ext_attr *attr;
+
+	if (!parent_n)
+		return ERR_PTR(-EINVAL);
+
+	list_for_each_entry(attr, &ext_attr_map_list, attr_list) {
+		if (attr->scpd_n && !strcmp(parent_n, attr->scpd_n))
+			return attr;
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
+int bus_ctrl_set_release(struct scpsys_ext_attr *attr, bool set)
+{
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < MAX_STEP_NUM && attr->mask[i].mask; i++) {
+		struct ext_reg_ctrl *rc = attr->mask[i].regs;
+		struct regmap *regmap;
+
+		if (rc->type == IFR_TYPE)
+			regmap = infracfg;
+		else if (rc->type == SMI_TYPE)
+			regmap = smi_comm;
+		else
+			return -EINVAL;
+
+		if (set)
+			ret = attr->mask[i].ops->set(regmap,
+						rc->set_ofs,
+						rc->sta_ofs,
+						attr->mask[i].mask);
+		else
+			ret = attr->mask[i].ops->release(regmap,
+						rc->clr_ofs,
+						rc->sta_ofs,
+						attr->mask[i].mask);
+	}
+
+	return ret;
+}
+
+int bus_ctrl_set(struct scpsys_ext_attr *attr)
+{
+	return bus_ctrl_set_release(attr, CMD_ENABLE);
+}
+
+int bus_ctrl_release(struct scpsys_ext_attr *attr)
+{
+	return bus_ctrl_set_release(attr, CMD_DISABLE);
+}
+
+int bus_clk_enable_disable(struct scpsys_ext_attr *attr, bool enable)
+{
+	int i = 0;
+	int ret = 0;
+	struct ext_clk_ctrl *cc;
+	struct clk *clk[MAX_CLKS];
+
+	list_for_each_entry(cc, &ext_clk_map_list, clk_list) {
+		if (!strcmp(cc->scpd_n, attr->scpd_n)) {
+			if (enable)
+				ret = clk_prepare_enable(cc->clk);
+			else
+				clk_disable_unprepare(cc->clk);
+
+			if (ret) {
+				pr_err("Failed to  %s %s\n",
+				       enable ? "enable" : "disable",
+				       __clk_get_name(cc->clk));
+				goto err;
+			} else {
+				clk[i] = cc->clk;
+				i++;
+			}
+		}
+	}
+
+	return ret;
+
+err:
+	for (--i; i >= 0; i--)
+		if (enable)
+			clk_disable_unprepare(clk[i]);
+		else
+			clk_prepare_enable(clk[i]);
+	return ret;
+}
+
+int bus_clk_enable(struct scpsys_ext_attr *attr)
+{
+	struct scpsys_ext_attr *attr_p;
+	int ret = 0;
+
+	attr_p = __get_attr_parent(attr->parent_n);
+	if (!IS_ERR(attr_p)) {
+		ret = bus_clk_enable_disable(attr_p, CMD_ENABLE);
+		if (ret)
+			return ret;
+	}
+
+	return bus_clk_enable_disable(attr, CMD_ENABLE);
+}
+
+int bus_clk_disable(struct scpsys_ext_attr *attr)
+{
+	struct scpsys_ext_attr *attr_p;
+	int ret = 0;
+
+	ret = bus_clk_enable_disable(attr, CMD_DISABLE);
+	if (ret)
+		return ret;
+
+	attr_p = __get_attr_parent(attr->parent_n);
+	if (!IS_ERR(attr_p))
+		ret = bus_clk_enable_disable(attr_p, CMD_DISABLE);
+
+	return ret;
+}
+
+const struct bus_mask_ops bus_mask_set_clr_ctrl = {
+	.set = &mtk_generic_set_cmd,
+	.release = &mtk_generic_clr_cmd,
+};
+
+const struct bus_ext_ops ext_bus_ctrl = {
+	.enable = &bus_ctrl_set,
+	.disable = &bus_ctrl_release,
+};
+
+const struct bus_ext_ops ext_cg_ctrl = {
+	.enable = &bus_clk_enable,
+	.disable = &bus_clk_disable,
+};
+
+/*
+ * scpsys bus driver init
+ */
+struct regmap *syscon_regmap_lookup_by_phandle_idx(struct device_node *np,
+						   const char *property,
+						   int index)
+{
+	struct device_node *syscon_np;
+	struct regmap *regmap;
+
+	if (property)
+		syscon_np = of_parse_phandle(np, property, index);
+	else
+		syscon_np = np;
+
+	if (!syscon_np)
+		return ERR_PTR(-ENODEV);
+
+	regmap = syscon_node_to_regmap(syscon_np);
+	of_node_put(syscon_np);
+
+	return regmap;
+}
+
+int scpsys_ext_regmap_init(struct platform_device *pdev)
+{
+	infracfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+						   INFRA);
+	if (IS_ERR(infracfg)) {
+		dev_err(&pdev->dev,
+			"Cannot find bus infracfg controller: %ld\n",
+			PTR_ERR(infracfg));
+		return PTR_ERR(infracfg);
+	}
+
+	smi_comm = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+						   SMIC);
+	if (IS_ERR(smi_comm)) {
+		dev_err(&pdev->dev,
+			"Cannot find bus smi_comm controller: %ld\n",
+			PTR_ERR(smi_comm));
+		return PTR_ERR(smi_comm);
+	}
+
+	return 0;
+}
+
+static int add_clk_to_list(struct platform_device *pdev,
+			   const char *name,
+			   const char *scpd_n)
+{
+	struct clk *clk;
+	struct ext_clk_ctrl *cc;
+
+	clk = devm_clk_get(&pdev->dev, name);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Failed add clk %ld\n", PTR_ERR(clk));
+		return PTR_ERR(clk);
+	}
+
+	cc = kzalloc(sizeof(*cc), GFP_KERNEL);
+	cc->clk = clk;
+	cc->scpd_n = kstrdup(scpd_n, GFP_KERNEL);
+
+	list_add(&cc->clk_list, &ext_clk_map_list);
+
+	return 0;
+}
+
+static int add_cg_to_list(struct platform_device *pdev)
+{
+	int i = 0;
+
+	struct device_node *node = pdev->dev.of_node;
+
+	if (!node) {
+		dev_err(&pdev->dev, "Cannot find topcksys node: %ld\n",
+			PTR_ERR(node));
+		return PTR_ERR(node);
+	}
+
+	do {
+		const char *ck_name;
+		char *temp_str;
+		char *tok[2] = {NULL};
+		int cg_idx = 0;
+		int idx = 0;
+		int ret = 0;
+
+		ret = of_property_read_string_index(node, "clock-names", i,
+						    &ck_name);
+		if (ret < 0)
+			break;
+
+		temp_str = kmalloc_array(strlen(ck_name), sizeof(char),
+					 GFP_KERNEL | __GFP_ZERO);
+		memcpy(temp_str, ck_name, strlen(ck_name));
+		temp_str[strlen(ck_name)] = '\0';
+		do {
+			tok[idx] = strsep(&temp_str, "-");
+			idx++;
+		} while (temp_str);
+
+		if (idx == 2) {
+			if (kstrtouint(tok[1], 10, &cg_idx))
+				return -EINVAL;
+
+			if (add_clk_to_list(pdev, ck_name, tok[0]))
+				return -EINVAL;
+		}
+		kfree(temp_str);
+		i++;
+	} while (1);
+
+	return 0;
+}
+
+int scpsys_ext_clk_init(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	ret = add_cg_to_list(pdev);
+	if (ret)
+		goto err;
+
+err:
+	return ret;
+}
+
+int scpsys_ext_attr_init(const struct scpsys_ext_data *data)
+{
+	int i, count = 0;
+
+	for (i = 0; i < data->num_attr; i++) {
+		struct scpsys_ext_attr *node = data->attr + i;
+
+		if (!node)
+			continue;
+
+		list_add(&node->attr_list, &ext_attr_map_list);
+		count++;
+	}
+
+	if (!count)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct of_device_id of_scpsys_ext_match_tbl[] = {
+	{
+		/* sentinel */
+	}
+};
+
+struct scpsys_ext_data *scpsys_ext_init(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct scpsys_ext_data *data;
+	int ret;
+
+	match = of_match_device(of_scpsys_ext_match_tbl, &pdev->dev);
+
+	if (!match) {
+		dev_err(&pdev->dev, "no match\n");
+		return ERR_CAST(match);
+	}
+
+	data = (struct scpsys_ext_data *)match->data;
+	if (IS_ERR(data)) {
+		dev_err(&pdev->dev, "no match scpext data\n");
+		return ERR_CAST(data);
+	}
+
+	ret = scpsys_ext_attr_init(data);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Failed to init bus attr: %d\n",
+			ret);
+		return ERR_PTR(ret);
+	}
+
+	ret = scpsys_ext_regmap_init(pdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Failed to init bus register: %d\n",
+			ret);
+		return ERR_PTR(ret);
+	}
+
+	ret = scpsys_ext_clk_init(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to init bus clks: %d\n",
+			ret);
+		return ERR_PTR(ret);
+	}
+
+	return data;
+}
diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c
index 4bb6c7a..03df2d6 100644
--- a/drivers/soc/mediatek/mtk-scpsys.c
+++ b/drivers/soc/mediatek/mtk-scpsys.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2015 Pengutronix, Sascha Hauer <kernel@pengutronix.de>
  *
@@ -20,6 +21,7 @@
 #include <linux/pm_domain.h>
 #include <linux/regulator/consumer.h>
 #include <linux/soc/mediatek/infracfg.h>
+#include <linux/soc/mediatek/scpsys-ext.h>
 
 #include <dt-bindings/power/mt2701-power.h>
 #include <dt-bindings/power/mt2712-power.h>
@@ -117,6 +119,15 @@ enum clk_id {
 
 #define MAX_CLKS	3
 
+/**
+ * struct scp_domain_data - scp domain data for power on/off flow
+ * @name: The domain name.
+ * @sta_mask: The mask for power on/off status bit.
+ * @ctl_offs: The offset for main power control register.
+ * @sram_pdn_bits: The mask for sram power control bits.
+ * @sram_pdn_ack_bits The mask for sram power control acked bits.
+ * @caps: The flag for active wake-up action.
+ */
 struct scp_domain_data {
 	const char *name;
 	u32 sta_mask;
@@ -150,7 +161,7 @@ struct scp {
 	void __iomem *base;
 	struct regmap *infracfg;
 	struct scp_ctrl_reg ctrl_reg;
-	bool bus_prot_reg_update;
+	struct scpsys_ext_data *ext_data;
 };
 
 struct scp_subdomain {
@@ -164,7 +175,6 @@ struct scp_soc_data {
 	const struct scp_subdomain *subdomains;
 	int num_subdomains;
 	const struct scp_ctrl_reg regs;
-	bool bus_prot_reg_update;
 };
 
 static int scpsys_domain_is_on(struct scp_domain *scpd)
@@ -236,6 +246,31 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
 	val |= PWR_RST_B_BIT;
 	writel(val, ctl_addr);
 
+	if (!IS_ERR(scp->ext_data)) {
+		struct scpsys_ext_attr *attr;
+
+		attr = scp->ext_data->get_attr(scpd->data->name);
+		if (!IS_ERR(attr) && attr->cg_ops) {
+			ret = attr->cg_ops->enable(attr);
+			if (ret)
+				goto err_ext_clk;
+		}
+	}
+
+	val &= ~scpd->data->sram_pdn_bits;
+	writel(val, ctl_addr);
+
+	if (!IS_ERR(scp->ext_data)) {
+		struct scpsys_ext_attr *attr;
+
+		attr = scp->ext_data->get_attr(scpd->data->name);
+		if (!IS_ERR(attr) && attr->cg_ops) {
+			ret = attr->cg_ops->enable(attr);
+			if (ret)
+				goto err_ext_clk;
+		}
+	}
+
 	val &= ~scpd->data->sram_pdn_bits;
 	writel(val, ctl_addr);
 
@@ -247,25 +282,65 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
 		 * applied here.
 		 */
 		usleep_range(12000, 12100);
-
 	} else {
 		ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == 0,
 					 MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
 		if (ret < 0)
-			goto err_pwr_ack;
+			goto err_sram;
 	}
 
 	if (scpd->data->bus_prot_mask) {
 		ret = mtk_infracfg_clear_bus_protection(scp->infracfg,
-				scpd->data->bus_prot_mask,
-				scp->bus_prot_reg_update);
+				scpd->data->bus_prot_mask);
 		if (ret)
-			goto err_pwr_ack;
+			goto err_sram;
+	}
+
+	if (!IS_ERR(scp->ext_data)) {
+		struct scpsys_ext_attr *attr;
+
+		attr = scp->ext_data->get_attr(scpd->data->name);
+		if (!IS_ERR(attr) && attr->bus_ops) {
+			ret = attr->bus_ops->disable(attr);
+			if (ret)
+				goto err_sram;
+		}
+	}
+
+	if (!IS_ERR(scp->ext_data)) {
+		struct scpsys_ext_attr *attr;
+
+		attr = scp->ext_data->get_attr(scpd->data->name);
+		if (!IS_ERR(attr) && attr->cg_ops) {
+			ret = attr->cg_ops->disable(attr);
+			if (ret)
+				goto err_sram;
+		}
 	}
 
 	return 0;
 
+err_sram:
+	val = readl(ctl_addr);
+	val |= scpd->data->sram_pdn_bits;
+	writel(val, ctl_addr);
+err_ext_clk:
+	val = readl(ctl_addr);
+	val |= PWR_ISO_BIT;
+	writel(val, ctl_addr);
+
+	val &= ~PWR_RST_B_BIT;
+	writel(val, ctl_addr);
+
+	val |= PWR_CLK_DIS_BIT;
+	writel(val, ctl_addr);
 err_pwr_ack:
+	val &= ~PWR_ON_BIT;
+	writel(val, ctl_addr);
+
+	val &= ~PWR_ON_2ND_BIT;
+	writel(val, ctl_addr);
+
 	for (i = MAX_CLKS - 1; i >= 0; i--) {
 		if (scpd->clk[i])
 			clk_disable_unprepare(scpd->clk[i]);
@@ -274,8 +349,6 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
 	if (scpd->supply)
 		regulator_disable(scpd->supply);
 
-	dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name);
-
 	return ret;
 }
 
@@ -289,14 +362,35 @@ static int scpsys_power_off(struct generic_pm_domain *genpd)
 	int ret, tmp;
 	int i;
 
+	if (!IS_ERR(scp->ext_data)) {
+		struct scpsys_ext_attr *attr;
+
+		attr = scp->ext_data->get_attr(scpd->data->name);
+		if (!IS_ERR(attr) && attr->cg_ops) {
+			ret = attr->cg_ops->enable(attr);
+			if (ret)
+				goto out;
+		}
+	}
+
 	if (scpd->data->bus_prot_mask) {
 		ret = mtk_infracfg_set_bus_protection(scp->infracfg,
-				scpd->data->bus_prot_mask,
-				scp->bus_prot_reg_update);
+				scpd->data->bus_prot_mask);
 		if (ret)
 			goto out;
 	}
 
+	if (!IS_ERR(scp->ext_data)) {
+		struct scpsys_ext_attr *attr;
+
+		attr = scp->ext_data->get_attr(scpd->data->name);
+		if (!IS_ERR(attr) && attr->bus_ops) {
+			ret = attr->bus_ops->enable(attr);
+			if (ret)
+				goto out;
+		}
+	}
+
 	val = readl(ctl_addr);
 	val |= scpd->data->sram_pdn_bits;
 	writel(val, ctl_addr);
@@ -307,6 +401,17 @@ static int scpsys_power_off(struct generic_pm_domain *genpd)
 	if (ret < 0)
 		goto out;
 
+	if (!IS_ERR(scp->ext_data)) {
+		struct scpsys_ext_attr *attr;
+
+		attr = scp->ext_data->get_attr(scpd->data->name);
+		if (!IS_ERR(attr) && attr->cg_ops) {
+			ret = attr->cg_ops->disable(attr);
+			if (ret)
+				goto out;
+		}
+	}
+
 	val |= PWR_ISO_BIT;
 	writel(val, ctl_addr);
 
@@ -337,8 +442,6 @@ static int scpsys_power_off(struct generic_pm_domain *genpd)
 	return 0;
 
 out:
-	dev_err(scp->dev, "Failed to power off domain %s\n", genpd->name);
-
 	return ret;
 }
 
@@ -352,8 +455,7 @@ static void init_clks(struct platform_device *pdev, struct clk **clk)
 
 static struct scp *init_scp(struct platform_device *pdev,
 			const struct scp_domain_data *scp_domain_data, int num,
-			const struct scp_ctrl_reg *scp_ctrl_reg,
-			bool bus_prot_reg_update)
+			const struct scp_ctrl_reg *scp_ctrl_reg)
 {
 	struct genpd_onecell_data *pd_data;
 	struct resource *res;
@@ -367,11 +469,10 @@ static struct scp *init_scp(struct platform_device *pdev,
 
 	scp->ctrl_reg.pwr_sta_offs = scp_ctrl_reg->pwr_sta_offs;
 	scp->ctrl_reg.pwr_sta2nd_offs = scp_ctrl_reg->pwr_sta2nd_offs;
-
-	scp->bus_prot_reg_update = bus_prot_reg_update;
-
 	scp->dev = &pdev->dev;
 
+	scp->ext_data = scpsys_ext_init(pdev);
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	scp->base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(scp->base))
@@ -1021,7 +1122,6 @@ static void mtk_register_power_domains(struct platform_device *pdev,
 		.pwr_sta_offs = SPM_PWR_STATUS,
 		.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND
 	},
-	.bus_prot_reg_update = true,
 };
 
 static const struct scp_soc_data mt2712_data = {
@@ -1033,7 +1133,6 @@ static void mtk_register_power_domains(struct platform_device *pdev,
 		.pwr_sta_offs = SPM_PWR_STATUS,
 		.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND
 	},
-	.bus_prot_reg_update = false,
 };
 
 static const struct scp_soc_data mt6765_data = {
@@ -1056,7 +1155,6 @@ static void mtk_register_power_domains(struct platform_device *pdev,
 		.pwr_sta_offs = SPM_PWR_STATUS_MT6797,
 		.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6797
 	},
-	.bus_prot_reg_update = true,
 };
 
 static const struct scp_soc_data mt7622_data = {
@@ -1066,7 +1164,6 @@ static void mtk_register_power_domains(struct platform_device *pdev,
 		.pwr_sta_offs = SPM_PWR_STATUS,
 		.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND
 	},
-	.bus_prot_reg_update = true,
 };
 
 static const struct scp_soc_data mt7623a_data = {
@@ -1076,7 +1173,6 @@ static void mtk_register_power_domains(struct platform_device *pdev,
 		.pwr_sta_offs = SPM_PWR_STATUS,
 		.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND
 	},
-	.bus_prot_reg_update = true,
 };
 
 static const struct scp_soc_data mt8173_data = {
@@ -1088,7 +1184,6 @@ static void mtk_register_power_domains(struct platform_device *pdev,
 		.pwr_sta_offs = SPM_PWR_STATUS,
 		.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND
 	},
-	.bus_prot_reg_update = true,
 };
 
 /*
@@ -1132,8 +1227,8 @@ static int scpsys_probe(struct platform_device *pdev)
 
 	soc = of_device_get_match_data(&pdev->dev);
 
-	scp = init_scp(pdev, soc->domains, soc->num_domains, &soc->regs,
-			soc->bus_prot_reg_update);
+	scp = init_scp(pdev, soc->domains, soc->num_domains,
+		       &soc->regs);
 	if (IS_ERR(scp))
 		return PTR_ERR(scp);
 
diff --git a/include/linux/soc/mediatek/infracfg.h b/include/linux/soc/mediatek/infracfg.h
index fd25f01..bfad082 100644
--- a/include/linux/soc/mediatek/infracfg.h
+++ b/include/linux/soc/mediatek/infracfg.h
@@ -32,8 +32,9 @@
 #define MT7622_TOP_AXI_PROT_EN_WB		(BIT(2) | BIT(6) | \
 						 BIT(7) | BIT(8))
 
-int mtk_infracfg_set_bus_protection(struct regmap *infracfg, u32 mask,
-		bool reg_update);
-int mtk_infracfg_clear_bus_protection(struct regmap *infracfg, u32 mask,
-		bool reg_update);
+int mtk_infracfg_set_bus_protection(struct regmap *infracfg, u32 mask);
+int mtk_infracfg_clear_bus_protection(struct regmap *infracfg, u32 mask);
+int mtk_infracfg_enable_bus_protection(struct regmap *infracfg, u32 mask);
+int mtk_infracfg_disable_bus_protection(struct regmap *infracfg, u32 mask);
+
 #endif /* __SOC_MEDIATEK_INFRACFG_H */
diff --git a/include/linux/soc/mediatek/scpsys-ext.h b/include/linux/soc/mediatek/scpsys-ext.h
new file mode 100644
index 0000000..99b5ff1
--- /dev/null
+++ b/include/linux/soc/mediatek/scpsys-ext.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __SOC_MEDIATEK_SCPSYS_EXT_H
+#define __SOC_MEDIATEK_SCPSYS_EXT_H
+
+#include <linux/platform_device.h>
+
+#define CMD_ENABLE	1
+#define CMD_DISABLE	0
+
+#define MAX_STEP_NUM	4
+
+/**
+ * struct bus_mask - set mask and corresponding operation for bus protect
+ * @regs: The register set of bus register control, including set/clr/sta.
+ * @mask: The mask set for bus protect.
+ * @flag: The flag to idetify which operation we take for bus protect.
+ */
+struct bus_mask {
+	struct ext_reg_ctrl *regs;
+	u32 mask;
+	const struct bus_mask_ops *ops;
+};
+
+/**
+ * struct scpsys_ext_attr - extended attribute for bus protect and further
+ *                                           operand.
+ *
+ * @scpd_n: The name present the scpsys domain where the clks belongs to.
+ * @mask: The mask set for bus protect.
+ * @bus_ops: The operation we take for bus protect.
+ * @cg_ops: The operation we take for cg on/off.
+ * @attr_list: The list node linked to ext_attr_map_list.
+ */
+struct scpsys_ext_attr {
+	const char *scpd_n;
+	struct bus_mask mask[MAX_STEP_NUM];
+	const char *parent_n;
+	const struct bus_ext_ops *bus_ops;
+	const struct bus_ext_ops *cg_ops;
+
+	struct list_head attr_list;
+};
+
+struct scpsys_ext_data {
+	struct scpsys_ext_attr *attr;
+	u8 num_attr;
+	struct scpsys_ext_attr * (*get_attr)(const char *scpd_n);
+};
+
+struct bus_ext_ops {
+	int	(*enable)(struct scpsys_ext_attr *attr);
+	int	(*disable)(struct scpsys_ext_attr *attr);
+};
+
+int mtk_generic_set_cmd(struct regmap *regmap, u32 set_ofs,
+			u32 sta_ofs, u32 mask);
+int mtk_generic_clr_cmd(struct regmap *regmap, u32 clr_ofs,
+			u32 sta_ofs, u32 mask);
+int mtk_generic_enable_cmd(struct regmap *regmap, u32 upd_ofs,
+			   u32 sta_ofs, u32 mask);
+int mtk_generic_disable_cmd(struct regmap *regmap, u32 upd_ofs,
+			    u32 sta_ofs, u32 mask);
+
+struct scpsys_ext_data *scpsys_ext_init(struct platform_device *pdev);
+
+#endif /* __SOC_MEDIATEK_SCPSYS_EXT_H */
