x86: apl: Add PMC driver

Add a driver for the Apollo Lake SoC. It supports the basic operations and
can use device tree or of-platdata.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
diff --git a/drivers/power/acpi_pmc/acpi-pmc-uclass.c b/drivers/power/acpi_pmc/acpi-pmc-uclass.c
index 653c71b..d43de87 100644
--- a/drivers/power/acpi_pmc/acpi-pmc-uclass.c
+++ b/drivers/power/acpi_pmc/acpi-pmc-uclass.c
@@ -9,6 +9,9 @@
 #include <acpi_s3.h>
 #include <dm.h>
 #include <log.h>
+#ifdef CONFIG_X86
+#include <asm/intel_pinctrl.h>
+#endif
 #include <asm/io.h>
 #include <power/acpi_pmc.h>
 
@@ -34,6 +37,59 @@
 	TCO1_CNT_HLT			= 1 << 11,
 };
 
+#ifdef CONFIG_X86
+static int gpe0_shift(struct acpi_pmc_upriv *upriv, int regnum)
+{
+	return upriv->gpe0_dwx_shift_base + regnum * 4;
+}
+
+int pmc_gpe_init(struct udevice *dev)
+{
+	struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);
+	struct udevice *itss;
+	u32 *dw;
+	u32 gpio_cfg_mask;
+	u32 gpio_cfg;
+	int ret, i;
+	u32 mask;
+
+	if (device_get_uclass_id(dev) != UCLASS_ACPI_PMC)
+		return log_msg_ret("uclass", -EPROTONOSUPPORT);
+	dw = upriv->gpe0_dw;
+	mask = upriv->gpe0_dwx_mask;
+	gpio_cfg_mask = 0;
+	for (i = 0; i < upriv->gpe0_count; i++) {
+		gpio_cfg_mask |= mask << gpe0_shift(upriv, i);
+		if (dw[i] & ~mask)
+			return log_msg_ret("Base GPE0 value", -EINVAL);
+	}
+
+	/*
+	 * Route the GPIOs to the GPE0 block. Determine that all values
+	 * are different and if they aren't, use the reset values.
+	 */
+	if (dw[0] == dw[1] || dw[1] == dw[2]) {
+		log_info("PMC: Using default GPE route");
+		gpio_cfg = readl(upriv->gpe_cfg);
+		for (i = 0; i < upriv->gpe0_count; i++)
+			dw[i] = gpio_cfg >> gpe0_shift(upriv, i);
+	} else {
+		gpio_cfg = 0;
+		for (i = 0; i < upriv->gpe0_count; i++)
+			gpio_cfg |= dw[i] << gpe0_shift(upriv, i);
+		clrsetbits_le32(upriv->gpe_cfg, gpio_cfg_mask, gpio_cfg);
+	}
+
+	/* Set the routes in the GPIO communities as well */
+	ret = uclass_first_device_err(UCLASS_IRQ, &itss);
+	if (ret)
+		return log_msg_ret("Cannot find itss", ret);
+	pinctrl_route_gpe(itss, dw[0], dw[1], dw[2]);
+
+	return 0;
+}
+#endif /* CONFIG_X86 */
+
 static void pmc_fill_pm_reg_info(struct udevice *dev)
 {
 	struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);