imx8mp: power-domain: Add PCIe support

Add support for GPCv2 power domains and clock handling for PCIe and
PCIe PHY.

Tested-by: Tim Harvey <tharvey@gateworks.com> #imx8mp-venice*
Tested-by: Adam Ford <aford173@gmail.com> #imx8mp-beacon-kit
Reviewed-by: Marek Vasut <marex@denx.de>
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
diff --git a/drivers/power/domain/imx8mp-hsiomix.c b/drivers/power/domain/imx8mp-hsiomix.c
index e2d772c..d3b0c11 100644
--- a/drivers/power/domain/imx8mp-hsiomix.c
+++ b/drivers/power/domain/imx8mp-hsiomix.c
@@ -16,48 +16,85 @@
 #define GPR_REG0		0x0
 #define  PCIE_CLOCK_MODULE_EN	BIT(0)
 #define  USB_CLOCK_MODULE_EN	BIT(1)
+#define  PCIE_PHY_APB_RST	BIT(4)
+#define  PCIE_PHY_INIT_RST	BIT(5)
 
 struct imx8mp_hsiomix_priv {
 	void __iomem *base;
 	struct clk clk_usb;
+	struct clk clk_pcie;
 	struct power_domain pd_bus;
 	struct power_domain pd_usb;
+	struct power_domain pd_pcie;
 	struct power_domain pd_usb_phy1;
 	struct power_domain pd_usb_phy2;
+	struct power_domain pd_pcie_phy;
 };
 
-static int imx8mp_hsiomix_on(struct power_domain *power_domain)
+static int imx8mp_hsiomix_set(struct power_domain *power_domain, bool power_on)
 {
 	struct udevice *dev = power_domain->dev;
 	struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev);
-	struct power_domain *domain;
+	struct power_domain *domain = NULL;
+	struct clk *clk = NULL;
+	u32 gpr_reg0_bits = 0;
 	int ret;
 
-	ret = power_domain_on(&priv->pd_bus);
-	if (ret)
-		return ret;
-
-	if (power_domain->id == IMX8MP_HSIOBLK_PD_USB) {
+	switch (power_domain->id) {
+	case IMX8MP_HSIOBLK_PD_USB:
 		domain = &priv->pd_usb;
-	} else if (power_domain->id == IMX8MP_HSIOBLK_PD_USB_PHY1) {
+		clk = &priv->clk_usb;
+		gpr_reg0_bits |= USB_CLOCK_MODULE_EN;
+		break;
+	case IMX8MP_HSIOBLK_PD_USB_PHY1:
 		domain = &priv->pd_usb_phy1;
-	} else if (power_domain->id == IMX8MP_HSIOBLK_PD_USB_PHY2) {
+		break;
+	case IMX8MP_HSIOBLK_PD_USB_PHY2:
 		domain = &priv->pd_usb_phy2;
-	} else {
-		ret = -EINVAL;
-		goto err_pd;
+		break;
+	case IMX8MP_HSIOBLK_PD_PCIE:
+		domain = &priv->pd_pcie;
+		clk = &priv->clk_pcie;
+		gpr_reg0_bits |= PCIE_CLOCK_MODULE_EN;
+		break;
+	case IMX8MP_HSIOBLK_PD_PCIE_PHY:
+		domain = &priv->pd_pcie_phy;
+		/* Bits to deassert PCIe PHY reset */
+		gpr_reg0_bits |= PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST;
+		break;
+	default:
+		dev_err(dev, "unknown power domain id: %ld\n",
+			power_domain->id);
+		return -EINVAL;
 	}
 
-	ret = power_domain_on(domain);
-	if (ret)
-		goto err_pd;
+	if (power_on) {
+		ret = power_domain_on(&priv->pd_bus);
+		if (ret)
+			return ret;
 
-	ret = clk_enable(&priv->clk_usb);
-	if (ret)
-		goto err_clk;
+		ret = power_domain_on(domain);
+		if (ret)
+			goto err_pd;
 
-	if (power_domain->id == IMX8MP_HSIOBLK_PD_USB)
-		setbits_le32(priv->base + GPR_REG0, USB_CLOCK_MODULE_EN);
+		if (clk) {
+			ret = clk_enable(clk);
+			if (ret)
+				goto err_clk;
+		}
+
+		if (gpr_reg0_bits)
+			setbits_le32(priv->base + GPR_REG0, gpr_reg0_bits);
+	} else {
+		if (gpr_reg0_bits)
+			clrbits_le32(priv->base + GPR_REG0, gpr_reg0_bits);
+
+		if (clk)
+			clk_disable(clk);
+
+		power_domain_off(domain);
+		power_domain_off(&priv->pd_bus);
+	}
 
 	return 0;
 
@@ -68,26 +105,14 @@
 	return ret;
 }
 
-static int imx8mp_hsiomix_off(struct power_domain *power_domain)
+static int imx8mp_hsiomix_on(struct power_domain *power_domain)
 {
-	struct udevice *dev = power_domain->dev;
-	struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev);
-
-	if (power_domain->id == IMX8MP_HSIOBLK_PD_USB)
-		clrbits_le32(priv->base + GPR_REG0, USB_CLOCK_MODULE_EN);
-
-	clk_disable(&priv->clk_usb);
-
-	if (power_domain->id == IMX8MP_HSIOBLK_PD_USB)
-		power_domain_off(&priv->pd_usb);
-	else if (power_domain->id == IMX8MP_HSIOBLK_PD_USB_PHY1)
-		power_domain_off(&priv->pd_usb_phy1);
-	else if (power_domain->id == IMX8MP_HSIOBLK_PD_USB_PHY2)
-		power_domain_off(&priv->pd_usb_phy2);
-
-	power_domain_off(&priv->pd_bus);
+	return imx8mp_hsiomix_set(power_domain, true);
+}
 
-	return 0;
+static int imx8mp_hsiomix_off(struct power_domain *power_domain)
+{
+	return imx8mp_hsiomix_set(power_domain, false);
 }
 
 static int imx8mp_hsiomix_of_xlate(struct power_domain *power_domain,
@@ -109,6 +134,10 @@
 	if (ret < 0)
 		return ret;
 
+	ret = clk_get_by_name(dev, "pcie", &priv->clk_pcie);
+	if (ret < 0)
+		return ret;
+
 	ret = power_domain_get_by_name(dev, &priv->pd_bus, "bus");
 	if (ret < 0)
 		return ret;
@@ -125,8 +154,20 @@
 	if (ret < 0)
 		goto err_pd_usb_phy2;
 
+	ret = power_domain_get_by_name(dev, &priv->pd_pcie, "pcie");
+	if (ret < 0)
+		goto err_pd_pcie;
+
+	ret = power_domain_get_by_name(dev, &priv->pd_pcie_phy, "pcie-phy");
+	if (ret < 0)
+		goto err_pd_pcie_phy;
+
 	return 0;
 
+err_pd_pcie_phy:
+	power_domain_free(&priv->pd_pcie);
+err_pd_pcie:
+	power_domain_free(&priv->pd_usb_phy2);
 err_pd_usb_phy2:
 	power_domain_free(&priv->pd_usb_phy1);
 err_pd_usb_phy1: