diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig
index d3bb803ac8406a79ff687508045d7ad876f5b959..3ee56a7e5b40f1003d791479845ca79a591f1c8e 100644
--- a/drivers/soc/imx/Kconfig
+++ b/drivers/soc/imx/Kconfig
@@ -19,6 +19,9 @@ config HAVE_IMX_SC
 config HAVE_IMX_SC_PD
 	bool
 
+config HAVE_IMX_GPC_PSCI
+	bool
+
 config HAVE_IMX_RPMSG
 	select RPMSG_VIRTIO
 	select RPMSG
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index 58103bec66b252e02a149757982e598b36c379f3..0b317ed9f6d3d9bbb75aa98960d46fae63564f15 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_HAVE_IMX_MU) += mu/
 obj-$(CONFIG_HAVE_IMX_SC) += sc/
 obj-$(CONFIG_HAVE_IMX_SC_PD) += pm-domains.o
 obj-$(CONFIG_HAVE_IMX8_SOC) += soc-imx8.o
+obj-$(CONFIG_HAVE_IMX_GPC_PSCI) += gpc-psci.o
diff --git a/drivers/soc/imx/gpc-psci.c b/drivers/soc/imx/gpc-psci.c
new file mode 100644
index 0000000000000000000000000000000000000000..d3867e18ee23f5853c4fe725e11532893518ba86
--- /dev/null
+++ b/drivers/soc/imx/gpc-psci.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_domain.h>
+#include <soc/imx/fsl_sip.h>
+
+#define GPC_MAX_IRQS		(4 * 32)
+
+struct imx_gpc_pm_domain {
+	const char name[30];
+	struct device *dev;
+	struct generic_pm_domain pd;
+	u32 gpc_domain_id;
+	struct clk **clks;
+	unsigned int num_clks;
+	struct regulator *reg;
+};
+
+enum imx_gpc_pm_domain_state {
+	GPC_PD_STATE_OFF,
+	GPC_PD_STATE_ON,
+};
+
+#define to_imx_gpc_pm_domain(_genpd) container_of(_genpd, struct imx_gpc_pm_domain, pd)
+
+static DEFINE_SPINLOCK(gpc_psci_lock);
+static DEFINE_MUTEX(gpc_pd_mutex);
+
+static void imx_gpc_psci_irq_unmask(struct irq_data *d)
+{
+	struct arm_smccc_res res;
+
+	spin_lock(&gpc_psci_lock);
+	arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_UNMASK, d->hwirq,
+		      0, 0, 0, 0, 0, &res);
+	spin_unlock(&gpc_psci_lock);
+
+	irq_chip_unmask_parent(d);
+}
+
+static void imx_gpc_psci_irq_mask(struct irq_data *d)
+{
+	struct arm_smccc_res res;
+
+	spin_lock(&gpc_psci_lock);
+	arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_MASK, d->hwirq,
+		      0, 0, 0, 0, 0, &res);
+	spin_unlock(&gpc_psci_lock);
+
+	irq_chip_mask_parent(d);
+}
+static int imx_gpc_psci_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+	struct arm_smccc_res res;
+
+	spin_lock(&gpc_psci_lock);
+	arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_SET_WAKE, d->hwirq,
+		      on, 0, 0, 0, 0, &res);
+	spin_unlock(&gpc_psci_lock);
+
+	return 0;
+}
+
+static int imx_gpc_psci_irq_set_affinity(struct irq_data *d,
+					 const struct cpumask *dest, bool force)
+{
+	/* parse the cpu of irq affinity */
+	struct arm_smccc_res res;
+	int cpu = cpumask_any_and(dest, cpu_online_mask);
+
+	irq_chip_set_affinity_parent(d, dest, force);
+
+	spin_lock(&gpc_psci_lock);
+	arm_smccc_smc(FSL_SIP_GPC, 0x4, d->hwirq,
+		      cpu, 0, 0, 0, 0, &res);
+	spin_unlock(&gpc_psci_lock);
+
+	return 0;
+}
+
+static struct irq_chip imx_gpc_psci_chip = {
+	.name			= "GPC-PSCI",
+	.irq_eoi		= irq_chip_eoi_parent,
+	.irq_mask		= imx_gpc_psci_irq_mask,
+	.irq_unmask		= imx_gpc_psci_irq_unmask,
+	.irq_retrigger		= irq_chip_retrigger_hierarchy,
+	.irq_set_wake		= imx_gpc_psci_irq_set_wake,
+	.irq_set_affinity	= imx_gpc_psci_irq_set_affinity,
+};
+
+static int imx_gpc_psci_domain_translate(struct irq_domain *d,
+				    struct irq_fwspec *fwspec,
+				    unsigned long *hwirq,
+				    unsigned int *type)
+{
+	if (is_of_node(fwspec->fwnode)) {
+		if (fwspec->param_count != 3)
+			return -EINVAL;
+
+		/* No PPI should point to this domain */
+		if (fwspec->param[0] != 0)
+			return -EINVAL;
+
+		*hwirq = fwspec->param[1];
+		*type = fwspec->param[2];
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int imx_gpc_psci_domain_alloc(struct irq_domain *domain,
+				  unsigned int irq,
+				  unsigned int nr_irqs, void *data)
+{
+	struct irq_fwspec *fwspec = data;
+	struct irq_fwspec parent_fwspec;
+	irq_hw_number_t hwirq;
+	int i;
+
+	if (fwspec->param_count != 3)
+		return -EINVAL;	/* Not GIC compliant */
+	if (fwspec->param[0] != 0)
+		return -EINVAL;	/* No PPI should point to this domain */
+
+	hwirq = fwspec->param[1];
+	if (hwirq >= GPC_MAX_IRQS)
+		return -EINVAL;	/* Can't deal with this */
+
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
+					      &imx_gpc_psci_chip, NULL);
+
+	parent_fwspec = *fwspec;
+	parent_fwspec.fwnode = domain->parent->fwnode;
+
+	return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
+					    &parent_fwspec);
+}
+
+static struct irq_domain_ops imx_gpc_psci_domain_ops = {
+	.translate = imx_gpc_psci_domain_translate,
+	.alloc	= imx_gpc_psci_domain_alloc,
+	.free	= irq_domain_free_irqs_common,
+};
+
+static int __init imx_gpc_psci_init(struct device_node *node,
+			       struct device_node *parent)
+{
+	struct irq_domain *parent_domain, *domain;
+
+	if (!parent) {
+		pr_err("%s: no parent, giving up\n", node->full_name);
+		return -ENODEV;
+	}
+
+	parent_domain = irq_find_host(parent);
+	if (!parent_domain) {
+		pr_err("%s: unable to obtain parent domain\n", node->full_name);
+		return -ENXIO;
+	}
+
+	domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
+					  node, &imx_gpc_psci_domain_ops,
+					  NULL);
+	if (!domain)
+		return -ENOMEM;
+
+	return 0;
+}
+IRQCHIP_DECLARE(imx_gpc_psci, "fsl,imx8mq-gpc", imx_gpc_psci_init);
+
+static int imx_gpc_pd_power_on(struct generic_pm_domain *domain)
+{
+	struct imx_gpc_pm_domain *pd = to_imx_gpc_pm_domain(domain);
+	struct arm_smccc_res res;
+	int index, ret = 0;
+
+	/* power on the external supply */
+	if (pd->reg) {
+		ret = regulator_enable(pd->reg);
+		if (ret) {
+			dev_warn(pd->dev, "failed to power up the reg%d\n", ret);
+			return ret;
+		}
+	}
+
+	/* enable the necessary clks needed by the power domain */
+	if (pd->num_clks) {
+		for (index = 0; index < pd->num_clks; index++)
+			clk_prepare_enable(pd->clks[index]);
+	}
+
+	mutex_lock(&gpc_pd_mutex);
+	arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_PM_DOMAIN, pd->gpc_domain_id,
+		      GPC_PD_STATE_ON, 0, 0, 0, 0, &res);
+	mutex_unlock(&gpc_pd_mutex);
+
+	return 0;
+}
+
+static int imx_gpc_pd_power_off(struct generic_pm_domain *domain)
+{
+	struct imx_gpc_pm_domain *pd = to_imx_gpc_pm_domain(domain);
+	struct arm_smccc_res res;
+	int index, ret = 0;
+
+	mutex_lock(&gpc_pd_mutex);
+	arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_PM_DOMAIN, pd->gpc_domain_id,
+		      GPC_PD_STATE_OFF, 0, 0, 0, 0, &res);
+	mutex_unlock(&gpc_pd_mutex);
+
+	/* power off the external supply */
+	if (pd->reg) {
+		ret = regulator_disable(pd->reg);
+		if (ret) {
+			dev_warn(pd->dev, "failed to power off the reg%d\n", ret);
+			return ret;
+		}
+	}
+
+	/* disable the necessary clks when power domain on finished */
+	if (pd->num_clks) {
+		for (index = 0; index < pd->num_clks; index++)
+			clk_disable_unprepare(pd->clks[index]);
+	}
+
+	return ret;
+};
+
+static int imx8m_pd_clk_init(struct device_node *np,
+			     struct imx_gpc_pm_domain *domain)
+{
+	struct property *pp;
+	struct clk **clks;
+	int index;
+
+	pp = of_find_property(np, "clocks", NULL);
+	if (pp)
+		domain->num_clks = pp->length / 8;
+	else
+		domain->num_clks = 0;
+
+	if (domain->num_clks) {
+		clks = kcalloc(domain->num_clks, sizeof(*clks), GFP_KERNEL);
+		if (!clks) {
+			domain->num_clks = 0;
+			domain->clks = NULL;
+			return -ENOMEM;
+		}
+
+		domain->clks = clks;
+	}
+
+	for (index = 0; index < domain->num_clks; index++) {
+		clks[index] = of_clk_get(np, index);
+		if (IS_ERR(clks[index])) {
+			for (index = 0; index < domain->num_clks; index++) {
+				if (!IS_ERR(clks[index]))
+					clk_put(clks[index]);
+			}
+
+			domain->num_clks = 0;
+			domain->clks = NULL;
+			kfree(clks);
+			pr_warn("imx8m domain clock init failed\n");
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+static int imx8m_add_subdomain(struct device_node *parent,
+			       struct generic_pm_domain *parent_pd)
+{
+	struct device_node *child_node;
+	struct imx_gpc_pm_domain *child_domain;
+	int ret = 0;
+
+	/* add each of the child domain of parent */
+	for_each_child_of_node(parent, child_node) {
+		if (!of_device_is_available(child_node))
+			continue;
+
+		child_domain = kzalloc(sizeof(*child_domain), GFP_KERNEL);
+		if (!child_domain)
+			return -ENOMEM;
+
+		ret = of_property_read_string(child_node, "domain-name",
+					      &child_domain->pd.name);
+		if (ret)
+			goto exit;
+
+		ret = of_property_read_u32(child_node, "domain-id",
+					   &child_domain->gpc_domain_id);
+		if (ret)
+			goto exit;
+
+		child_domain->pd.power_off = imx_gpc_pd_power_off;
+		child_domain->pd.power_on = imx_gpc_pd_power_on;
+		/* no reg for subdomains */
+		child_domain->reg = NULL;
+
+		imx8m_pd_clk_init(child_node, child_domain);
+
+		/* power domains as off at boot */
+		pm_genpd_init(&child_domain->pd, NULL, true);
+
+		/* add subdomain of parent power domain */
+		pm_genpd_add_subdomain(parent_pd, &child_domain->pd);
+
+		ret = of_genpd_add_provider_simple(child_node,
+						 &child_domain->pd);
+		if (ret)
+			pr_err("failed to add subdomain\n");
+	}
+
+	return 0;
+exit:
+	kfree(child_domain);
+	return ret;
+};
+
+static int imx_gpc_pm_domain_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct imx_gpc_pm_domain *imx_pm_domain;
+	int ret = 0;
+
+	if (!np) {
+		dev_err(dev, "power domain device tree node not found\n");
+		return -ENODEV;
+	}
+
+	imx_pm_domain = devm_kzalloc(dev, sizeof(*imx_pm_domain), GFP_KERNEL);
+	if (!imx_pm_domain)
+		return -ENOMEM;
+	imx_pm_domain->dev = dev;
+
+	ret = of_property_read_string(np, "domain-name", &imx_pm_domain->pd.name);
+	if (ret) {
+		dev_err(dev, "get domain name failed\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "domain-id", &imx_pm_domain->gpc_domain_id);
+	if (ret) {
+		dev_err(dev, "get domain id failed\n");
+		return -EINVAL;
+	}
+
+	imx_pm_domain->reg = devm_regulator_get_optional(dev, "power");
+	if (IS_ERR(imx_pm_domain->reg)) {
+		if (PTR_ERR(imx_pm_domain->reg) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		imx_pm_domain->reg = NULL;
+	}
+
+	imx8m_pd_clk_init(np, imx_pm_domain);
+
+	imx_pm_domain->pd.power_off = imx_gpc_pd_power_off;
+	imx_pm_domain->pd.power_on = imx_gpc_pd_power_on;
+	/* all power domains as off at boot */
+	pm_genpd_init(&imx_pm_domain->pd, NULL, true);
+
+	ret = of_genpd_add_provider_simple(np,
+				 &imx_pm_domain->pd);
+
+	/* add subdomain */
+	ret = imx8m_add_subdomain(np, &imx_pm_domain->pd);
+	if (ret)
+		dev_warn(dev, "please check the child power domain init\n");
+
+	return 0;
+}
+
+static const struct of_device_id imx_gpc_pm_domain_ids[] = {
+	{.compatible = "fsl,imx8mq-pm-domain"},
+	{.compatible = "fsl,imx8mm-pm-domain"},
+	{},
+};
+
+static struct platform_driver imx_gpc_pm_domain_driver = {
+	.driver = {
+		.name	= "imx8m_gpc_pm_domain",
+		.owner	= THIS_MODULE,
+		.of_match_table = imx_gpc_pm_domain_ids,
+	},
+	.probe = imx_gpc_pm_domain_probe,
+};
+
+module_platform_driver(imx_gpc_pm_domain_driver);
+
+MODULE_AUTHOR("NXP");
+MODULE_DESCRIPTION("NXP i.MX8M GPC power domain driver");
+MODULE_LICENSE("GPL v2");