[9610] drivers: soc: introduce exynos_pd
authoryi jaeuk <ju.yi@samsung.com>
Wed, 9 May 2018 08:10:19 +0000 (17:10 +0900)
committerChungwoo Park <cww.park@samsung.com>
Mon, 21 May 2018 08:26:15 +0000 (17:26 +0900)
Change-Id: Ia5ae988037a35136d60c7310e725f05d720f76fa
Signed-off-by: yi jaeuk <ju.yi@samsung.com>
arch/arm64/boot/dts/exynos/exynos9610-pm-domains.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/exynos/exynos9610.dtsi
drivers/soc/samsung/Kconfig
drivers/soc/samsung/Makefile
drivers/soc/samsung/exynos-pd-dbg.c [new file with mode: 0644]
drivers/soc/samsung/exynos-pd.c [new file with mode: 0644]
include/dt-bindings/power/exynos-power.h [new file with mode: 0644]
include/linux/apm-exynos.h [new file with mode: 0644]
include/soc/samsung/exynos-pd.h [new file with mode: 0644]

diff --git a/arch/arm64/boot/dts/exynos/exynos9610-pm-domains.dtsi b/arch/arm64/boot/dts/exynos/exynos9610-pm-domains.dtsi
new file mode 100644 (file)
index 0000000..9601d63
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * SAMSUNG EXYNOS9810 SoC PM Domains device tree source
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * SAMSUNG EXYNOS9810 SoC PM domains device nodes are listed in this file.
+ * EXYNOS9810 based board files can include this file and provide
+ * values for board specfic bindings.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <dt-bindings/power/exynos-power.h>
+
+/ {
+       pd_cam: pd-cam@11864020 {
+               compatible = "samsung,exynos-pd";
+               reg = <0x0 0x11864020 0x20>;
+               cal_id = <0xB1380000>;
+               need_smc = <0x14510204>;
+               status = "okay";
+       };
+
+       pd_isp: pd-isp@11864080 {
+               compatible = "samsung,exynos-pd";
+               reg = <0x0 0x11864080 0x20>;
+               cal_id = <0xB1380001>;
+               need_smc = <0x14710204>;
+               status = "okay";
+               parent = <&pd_cam>;
+       };
+
+       pd_vipx1: pd-vipx1@118640e0 {
+               compatible = "samsung,exynos-pd";
+               reg = <0x0 0x118640e0 0x20>;
+               cal_id = <0xB1380002>;
+               need_smc = <0x10C10204>;
+               status = "okay";
+       };
+
+       pd_vipx2: pd-vipx2@118640e0 {
+               compatible = "samsung,exynos-pd";
+               reg = <0x0 0x118640e0 0x20>;
+               cal_id = <0xB1380003>;
+               need_smc = <0x10e10204>;
+               status = "okay";
+               parent = <&pd_vipx1>;
+       };
+
+       pd_g2d: pd-g2d@11864040 {
+               compatible = "samsung,exynos-pd";
+               reg = <0x0 0x11864040 0x20>;
+               cal_id = <0xB1380004>;
+               need_smc = <0x12e10204>;
+               status = "okay";
+       };
+
+       pd_g3d: pd-g3d@11864060 {
+               compatible = "samsung,exynos-pd";
+               reg = <0x0 0x11864060 0x20>;
+               cal_id = <0xB1380005>;
+               need_smc = <0x11410204>;
+               status = "okay";
+       };
+
+       pd_dispaud: pd-dispaud@11864000 {
+               compatible = "samsung,exynos-pd";
+               reg = <0x0 0x11864000 0x20>;
+               cal_id = <0xB1380006>;
+               need_smc = <0x14810204>;
+               status = "okay";
+               power-down-ok = <PD_OK_AUD>;
+       };
+
+       pd_mfc: pd-mfc@118640a0 {
+               compatible = "samsung,exynos-pd";
+               reg = <0x0 0x118640a0 0x20>;
+               cal_id = <0xB1380007>;
+               need_smc = <0x12C10204>;
+               status = "okay";
+       };
+
+       dbgdev-pd-cam {
+               compatible = "samsung,exynos-pd-dbg";
+               samsung,power-domain = <&pd_cam>;
+       };
+
+       dbgdev-pd-isp {
+               compatible = "samsung,exynos-pd-dbg";
+               samsung,power-domain = <&pd_isp>;
+       };
+
+       dbgdev-pd-vipx1 {
+               compatible = "samsung,exynos-pd-dbg";
+               samsung,power-domain = <&pd_vipx1>;
+       };
+
+       dbgdev-pd-vipx2 {
+               compatible = "samsung,exynos-pd-dbg";
+               samsung,power-domain = <&pd_vipx2>;
+       };
+
+       dbgdev-pd-g2d {
+               compatible = "samsung,exynos-pd-dbg";
+               samsung,power-domain = <&pd_g2d>;
+       };
+
+       dbgdev-pd-g3d {
+               compatible = "samsung,exynos-pd-dbg";
+               samsung,power-domain = <&pd_g3d>;
+       };
+
+       dbgdev-pd-dispaud {
+               compatible = "samsung,exynos-pd-dbg";
+               samsung,power-domain = <&pd_dispaud>;
+       };
+
+       dbgdev-pd-mfc {
+               compatible = "samsung,exynos-pd-dbg";
+               samsung,power-domain = <&pd_mfc>;
+       };
+
+};
index f89b523cd90119147c5d350fc0889410769a31e4..9de3646e1256ad95613f2e77c6fa4da05f113ea0 100644 (file)
@@ -21,6 +21,7 @@
 #include <dt-bindings/ufs/ufs.h>
 #include "exynos9610-sysmmu.dtsi"
 #include <dt-bindings/soc/samsung/exynos9610-dm.h>
+#include "exynos9610-pm-domains.dtsi"
 
 / {
        compatible = "samsung,armv8", "samsung,exynos9610";
index 0893705834a221268119473009141b757edb1131..9cb1a39bdc513a67eec294f8c21548373965cc51 100644 (file)
@@ -104,4 +104,10 @@ config EXYNOS_DVFS_MANAGER
          Enable DVFS Manager for Exynos SoC.
          This module controls constraint between each DVFS domains.
 
+config EXYNOS_PD
+       bool "Exynos PM domain Support"
+               depends on ARCH_EXYNOS
+               depends on PM
+               select PM_GENERIC_DOMAINS
+
 endif
index e77f64c8ff2b49bb8d4b48ad9e9fe425b17b3465..acca1607e296df0ff6e979ae78f740bfbb002c92 100644 (file)
@@ -14,7 +14,6 @@ obj-$(CONFIG_ARCH_EXYNOS)     += exynos-fsys0-tcxo.o
 
 obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS)   += exynos3250-pmu.o exynos4-pmu.o \
                                        exynos5250-pmu.o exynos5420-pmu.o
-obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
 
 obj-$(CONFIG_EXYNOS_CHIPID)    += exynos-chipid.o
 
@@ -29,3 +28,7 @@ obj-$(CONFIG_EXYNOS_REBOOT)     += exynos-reboot.o
 obj-$(CONFIG_EXYNOS_DEBUG)      += debug/
 # DVFS
 obj-$(CONFIG_EXYNOS_DVFS_MANAGER)      += exynos-dm.o
+
+# PD
+obj-$(CONFIG_EXYNOS_PD)        += exynos-pd.o exynos-pd-dbg.o
+obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
diff --git a/drivers/soc/samsung/exynos-pd-dbg.c b/drivers/soc/samsung/exynos-pd-dbg.c
new file mode 100644 (file)
index 0000000..f36421e
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * Exynos PM domain debugfs support.
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+
+#include <soc/samsung/exynos-pd.h>
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *exynos_pd_dbg_root;
+
+static int exynos_pd_dbg_long_test(struct device *dev)
+{
+       int ret, i;
+
+       pr_info("%s %s: test start.\n", EXYNOS_PD_DBG_PREFIX, __func__);
+
+       if (pm_runtime_enabled(dev) && pm_runtime_active(dev)) {
+               ret = pm_runtime_put_sync(dev);
+               if (ret) {
+                       pr_err("%s %s: put sync failed.\n",
+                                       EXYNOS_PD_DBG_PREFIX, __func__);
+                       return ret;
+               }
+       }
+
+       for (i = 0; i < 100; i++) {
+               ret = pm_runtime_get_sync(dev);
+               if (ret) {
+                       pr_err("%s %s: get sync failed.\n",
+                                       EXYNOS_PD_DBG_PREFIX, __func__);
+                       return ret;
+               }
+               mdelay(50);
+               ret = pm_runtime_put_sync(dev);
+               if (ret) {
+                       pr_err("%s %s: put sync failed.\n",
+                                       EXYNOS_PD_DBG_PREFIX, __func__);
+                       return ret;
+               }
+               mdelay(50);
+       }
+
+       pr_info("%s %s: test done.\n", EXYNOS_PD_DBG_PREFIX, __func__);
+
+       return ret;
+}
+
+static struct generic_pm_domain *exynos_pd_dbg_dev_to_genpd(struct device *dev)
+{
+       if (IS_ERR_OR_NULL(dev->pm_domain))
+               return ERR_PTR(-EINVAL);
+
+       return pd_to_genpd(dev->pm_domain);
+}
+
+static void exynos_pd_dbg_summary_show(struct generic_pm_domain *genpd)
+{
+       static const char * const gpd_status_lookup[] = {
+               [GPD_STATE_ACTIVE] = "on",
+               [GPD_STATE_POWER_OFF] = "off"
+       };
+       static const char * const rpm_status_lookup[] = {
+               [RPM_ACTIVE] = "active",
+               [RPM_RESUMING] = "resuming",
+               [RPM_SUSPENDED] = "suspended",
+               [RPM_SUSPENDING] = "suspending"
+       };
+       const char *p = "";
+       struct pm_domain_data *pm_data;
+       struct gpd_link *link;
+
+       mutex_lock(&genpd->lock);
+
+       if (genpd->status >= ARRAY_SIZE(gpd_status_lookup)) {
+               pr_err("%s invalid GPD_STATUS\n", EXYNOS_PD_DBG_PREFIX);
+               mutex_unlock(&genpd->lock);
+               return ;
+       }
+
+       pr_info("[GENPD] : %-30s [GPD_STATUS] : %-15s\n",
+                       genpd->name, gpd_status_lookup[genpd->status]);
+
+       list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
+               if (pm_data->dev->power.runtime_error)
+                       p = "error";
+               else if (pm_data->dev->power.disable_depth)
+                       p = "unsupported";
+               else if (pm_data->dev->power.runtime_status < ARRAY_SIZE(rpm_status_lookup))
+                       p = rpm_status_lookup[pm_data->dev->power.runtime_status];
+               else
+                       WARN_ON(1);
+
+               pr_info("\t[DEV] : %-30s [RPM_STATUS] : %-15s\n",
+                                       dev_name(pm_data->dev), p);
+       }
+
+       list_for_each_entry(link, &genpd->master_links, master_node)
+               exynos_pd_dbg_summary_show(link->slave);
+
+       mutex_unlock(&genpd->lock);
+}
+
+static ssize_t exynos_pd_dbg_read(struct file *file, char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct device *dev = file->private_data;
+       struct generic_pm_domain *genpd = exynos_pd_dbg_dev_to_genpd(dev);
+
+       exynos_pd_dbg_summary_show(genpd);
+
+       return 0;
+}
+
+static ssize_t exynos_pd_dbg_write(struct file *file, const char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct device *dev = file->private_data;
+       char buf[32];
+       size_t buf_size;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       switch (buf[0]) {
+       case '0':
+               if (pm_runtime_put_sync(dev))
+                       pr_err("%s %s: put sync failed.\n",
+                                       EXYNOS_PD_DBG_PREFIX, __func__);
+               break;
+       case '1':
+               if (pm_runtime_get_sync(dev))
+                       pr_err("%s %s: get sync failed.\n",
+                                       EXYNOS_PD_DBG_PREFIX, __func__);
+               break;
+       case 'c':
+               exynos_pd_dbg_long_test(dev);
+               break;
+       default:
+               pr_err("%s %s: Invalid input ['0'|'1'|'c']\n",
+                               EXYNOS_PD_DBG_PREFIX, __func__);
+               break;
+       }
+
+       return count;
+}
+
+static const struct file_operations exynos_pd_dbg_fops = {
+       .open = simple_open,
+       .read = exynos_pd_dbg_read,
+       .write = exynos_pd_dbg_write,
+       .llseek = default_llseek,
+};
+#endif
+
+static int exynos_pd_dbg_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct exynos_pd_dbg_info *dbg_info;
+
+       dbg_info = kzalloc(sizeof(struct exynos_pd_dbg_info), GFP_KERNEL);
+       if (!dbg_info) {
+               pr_err("%s %s: could not allocate mem for dbg_info\n",
+                               EXYNOS_PD_DBG_PREFIX, __func__);
+               ret = -ENOMEM;
+               goto err_dbg_info;
+       }
+       dbg_info->dev = &pdev->dev;
+#ifdef CONFIG_DEBUG_FS
+       if (!exynos_pd_dbg_root) {
+               exynos_pd_dbg_root = debugfs_create_dir("exynos-pd", NULL);
+               if (!exynos_pd_dbg_root) {
+                       pr_err("%s %s: could not create debugfs dir\n",
+                                       EXYNOS_PD_DBG_PREFIX, __func__);
+                       ret = -ENOMEM;
+                       goto err_dbgfs_root;
+               }
+       }
+
+       dbg_info->fops = exynos_pd_dbg_fops;
+       dbg_info->d = debugfs_create_file(dev_name(&pdev->dev), 0644,
+                       exynos_pd_dbg_root, dbg_info->dev, &dbg_info->fops);
+       if (!dbg_info->d) {
+               pr_err("%s %s: could not creatd debugfs file\n",
+                               EXYNOS_PD_DBG_PREFIX, __func__);
+               ret = -ENOMEM;
+               goto err_dbgfs_pd;
+       }
+#endif
+       platform_set_drvdata(pdev, dbg_info);
+
+       pm_runtime_enable(&pdev->dev);
+
+       ret = pm_runtime_get_sync(&pdev->dev);
+       if (ret) {
+               pr_err("%s %s: get_sync of %s failed.\n",
+                       EXYNOS_PD_DBG_PREFIX, __func__, dev_name(&pdev->dev));
+               goto err_get_sync;
+       }
+
+       ret = pm_runtime_put_sync(&pdev->dev);
+       if (ret) {
+               pr_err("%s %s: put sync of %s failed.\n",
+                       EXYNOS_PD_DBG_PREFIX, __func__, dev_name(&pdev->dev));
+               goto err_put_sync;
+       }
+
+       return 0;
+
+err_get_sync:
+err_put_sync:
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove_recursive(dbg_info->d);
+err_dbgfs_pd:
+       debugfs_remove_recursive(exynos_pd_dbg_root);
+err_dbgfs_root:
+#endif
+       kfree(dbg_info);
+err_dbg_info:
+       return ret;
+}
+
+static int exynos_pd_dbg_remove(struct platform_device *pdev)
+{
+       struct exynos_pd_dbg_info *dbg_info = platform_get_drvdata(pdev);
+       struct device *dev = dbg_info->dev;
+
+       if (pm_runtime_enabled(dev) && pm_runtime_active(dev))
+               pm_runtime_put_sync(dev);
+
+       pm_runtime_disable(dev);
+
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove_recursive(dbg_info->d);
+       debugfs_remove_recursive(exynos_pd_dbg_root);
+#endif
+       kfree(dbg_info);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static int exynos_pd_dbg_runtime_suspend(struct device *dev)
+{
+       pr_info("%s %s's Runtime_Suspend\n",
+                       EXYNOS_PD_DBG_PREFIX, dev_name(dev));
+       return 0;
+}
+
+static int exynos_pd_dbg_runtime_resume(struct device *dev)
+{
+       pr_info("%s %s's Runtime_Resume\n",
+                       EXYNOS_PD_DBG_PREFIX, dev_name(dev));
+       return 0;
+}
+
+static struct dev_pm_ops exynos_pd_dbg_pm_ops = {
+       SET_RUNTIME_PM_OPS(exynos_pd_dbg_runtime_suspend,
+                       exynos_pd_dbg_runtime_resume,
+                       NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_pd_dbg_match[] = {
+       {
+               .compatible = "samsung,exynos-pd-dbg",
+       },
+       {},
+};
+#endif
+
+static struct platform_driver exynos_pd_dbg_drv = {
+       .probe          = exynos_pd_dbg_probe,
+       .remove         = exynos_pd_dbg_remove,
+       .driver         = {
+               .name   = "exynos_pd_dbg",
+               .owner  = THIS_MODULE,
+               .pm     = &exynos_pd_dbg_pm_ops,
+#ifdef CONFIG_OF
+               .of_match_table = exynos_pd_dbg_match,
+#endif
+       },
+};
+
+static int __init exynos_pd_dbg_init(void)
+{
+       return platform_driver_register(&exynos_pd_dbg_drv);
+}
+late_initcall(exynos_pd_dbg_init);
diff --git a/drivers/soc/samsung/exynos-pd.c b/drivers/soc/samsung/exynos-pd.c
new file mode 100644 (file)
index 0000000..997a7fa
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * Exynos PM domain support for PMUCAL 3.0 interface.
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Implementation of Exynos specific power domain control which is used in
+ * conjunction with runtime-pm.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <soc/samsung/exynos-pd.h>
+#include <soc/samsung/bts.h>
+#include <soc/samsung/cal-if.h>
+#include <linux/apm-exynos.h>
+struct exynos_pm_domain *exynos_pd_lookup_name(const char *domain_name)
+{
+       struct exynos_pm_domain *exypd = NULL;
+       struct device_node *np;
+
+       if (IS_ERR_OR_NULL(domain_name))
+               return NULL;
+
+       for_each_compatible_node(np, NULL, "samsung,exynos-pd") {
+               struct platform_device *pdev;
+               struct exynos_pm_domain *pd;
+
+               if (of_device_is_available(np)) {
+                       pdev = of_find_device_by_node(np);
+                       if (!pdev)
+                               continue;
+                       pd = platform_get_drvdata(pdev);
+                       if (!strcmp(pd->name, domain_name)) {
+                               exypd = pd;
+                               break;
+                       }
+               }
+       }
+       return exypd;
+}
+EXPORT_SYMBOL(exynos_pd_lookup_name);
+
+static int exynos_pd_status(struct exynos_pm_domain *pd)
+{
+       int status;
+
+       if (unlikely(!pd))
+               return -EINVAL;
+
+       mutex_lock(&pd->access_lock);
+       status = cal_pd_status(pd->cal_pdid);
+       mutex_unlock(&pd->access_lock);
+
+       return status;
+}
+
+/* Power domain on sequence.
+ * on_pre, on_post functions are registered as notification handler at CAL code.
+ */
+static void exynos_pd_power_on_pre(struct exynos_pm_domain *pd)
+{
+       exynos_update_ip_idle_status(pd->idle_ip_index, 0);
+
+       if (pd->devfreq_index >= 0) {
+               exynos_bts_scitoken_setting(true);
+       }
+}
+
+static void exynos_pd_power_on_post(struct exynos_pm_domain *pd)
+{
+#if defined(CONFIG_EXYNOS_BCM)
+       if(cal_pd_status(pd->cal_pdid) && pd->bcm)
+               bcm_pd_sync(pd->bcm, true);
+#endif
+}
+
+static void exynos_pd_power_off_pre(struct exynos_pm_domain *pd)
+{
+#ifdef CONFIG_EXYNOS_CL_DVFS_G3D
+       if (!strcmp(pd->name, "pd-g3d")) {
+               exynos_g3d_power_down_noti_apm();
+       }
+#endif /* CONFIG_EXYNOS_CL_DVFS_G3D */
+#if defined(CONFIG_EXYNOS_BCM)
+       if(cal_pd_status(pd->cal_pdid) && pd->bcm)
+               bcm_pd_sync(pd->bcm, false);
+#endif
+}
+
+static void exynos_pd_power_off_post(struct exynos_pm_domain *pd)
+{
+       exynos_update_ip_idle_status(pd->idle_ip_index, 1);
+
+       if (pd->devfreq_index >= 0) {
+               exynos_bts_scitoken_setting(false);
+       }
+}
+
+static void exynos_pd_prepare_forced_off(struct exynos_pm_domain *pd)
+{
+}
+
+static int exynos_pd_power_on(struct generic_pm_domain *genpd)
+{
+       struct exynos_pm_domain *pd = container_of(genpd, struct exynos_pm_domain, genpd);
+       int ret = 0;
+
+       mutex_lock(&pd->access_lock);
+
+       DEBUG_PRINT_INFO("%s(%s)+\n", __func__, pd->name);
+
+       if (unlikely(!pd->pd_control)) {
+               pr_debug(EXYNOS_PD_PREFIX "%s is Logical sub power domain, dose not have to power on control\n", pd->name);
+               goto acc_unlock;
+       }
+
+       if (pd->power_down_skipped) {
+               pr_info(EXYNOS_PD_PREFIX "%s power-on is skipped.\n", pd->name);
+               goto acc_unlock;
+       }
+
+       exynos_pd_power_on_pre(pd);
+
+       ret = pd->pd_control(pd->cal_pdid, 1);
+       if (ret) {
+               pr_err(EXYNOS_PD_PREFIX "%s cannot be powered on\n", pd->name);
+               exynos_pd_power_off_post(pd);
+               ret = -EAGAIN;
+               goto acc_unlock;
+       }
+
+       exynos_pd_power_on_post(pd);
+
+acc_unlock:
+       DEBUG_PRINT_INFO("%s(%s)-, ret = %d\n", __func__, pd->name, ret);
+       mutex_unlock(&pd->access_lock);
+
+       return ret;
+}
+
+static int exynos_pd_power_off(struct generic_pm_domain *genpd)
+{
+       struct exynos_pm_domain *pd = container_of(genpd, struct exynos_pm_domain, genpd);
+       int ret = 0;
+
+       mutex_lock(&pd->access_lock);
+
+       DEBUG_PRINT_INFO("%s(%s)+\n", __func__, pd->name);
+
+       if (unlikely(!pd->pd_control)) {
+               pr_debug(EXYNOS_PD_PREFIX "%s is Logical sub power domain, dose not have to power off control\n", genpd->name);
+               goto acc_unlock;
+       }
+
+       if (pd->power_down_ok && !pd->power_down_ok()) {
+               pr_info(EXYNOS_PD_PREFIX "%s power-off is skipped.\n", pd->name);
+               pd->power_down_skipped = true;
+               goto acc_unlock;
+       }
+
+       exynos_pd_power_off_pre(pd);
+
+       ret = pd->pd_control(pd->cal_pdid, 0);
+       if (unlikely(ret)) {
+               if (ret == -4) {
+                       pr_err(EXYNOS_PD_PREFIX "Timed out during %s  power off! -> forced power off\n", genpd->name);
+                       exynos_pd_prepare_forced_off(pd);
+                       ret = pd->pd_control(pd->cal_pdid, 0);
+                       if (unlikely(ret)) {
+                               pr_err(EXYNOS_PD_PREFIX "%s occur error at power off!\n", genpd->name);
+                               goto acc_unlock;
+                       }
+               } else {
+                       pr_err(EXYNOS_PD_PREFIX "%s occur error at power off!\n", genpd->name);
+                       goto acc_unlock;
+               }
+       }
+
+       exynos_pd_power_off_post(pd);
+       pd->power_down_skipped = false;
+
+acc_unlock:
+       DEBUG_PRINT_INFO("%s(%s)-, ret = %d\n", __func__, pd->name, ret);
+       mutex_unlock(&pd->access_lock);
+
+       return ret;
+}
+
+#ifdef CONFIG_OF
+/**
+ *  of_get_devfreq_sync_volt_idx - check devfreq sync voltage idx
+ *
+ *  Returns the index if the "devfreq-sync-voltage" is described in DT pd node,
+ *  -ENOENT otherwise.
+ */
+static int of_get_devfreq_sync_volt_idx(const struct device_node *device)
+{
+       int ret;
+       u32 val;
+
+       ret = of_property_read_u32(device, "devfreq-sync-voltage", &val);
+       if (ret)
+               return -ENOENT;
+
+       return val;
+}
+
+static bool exynos_pd_power_down_ok_aud(void)
+{
+#ifdef CONFIG_SND_SOC_SAMSUNG_ABOX
+#ifdef CONFIG_SOC_EMULATOR8895
+       return false;
+#else
+       return !abox_is_on();
+#endif
+#else
+       return true;
+#endif
+}
+
+static bool exynos_pd_power_down_ok_vts(void)
+{
+#ifdef CONFIG_SND_SOC_SAMSUNG_VTS
+       return !vts_is_on();
+#else
+       return true;
+#endif
+}
+
+static void of_get_power_down_ok(struct exynos_pm_domain *pd)
+{
+       int ret;
+       u32 val;
+       struct device_node *device = pd->of_node;
+
+       ret = of_property_read_u32(device, "power-down-ok", &val);
+       if (ret)
+               return ;
+       else {
+               switch (val) {
+               case PD_OK_AUD:
+                       pd->power_down_ok = exynos_pd_power_down_ok_aud;
+                       break;
+               case PD_OK_VTS:
+                       pd->power_down_ok = exynos_pd_power_down_ok_vts;
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void exynos_pd_genpd_init(struct exynos_pm_domain *pd, int state)
+{
+       pd->genpd.name = pd->name;
+       pd->genpd.power_off = exynos_pd_power_off;
+       pd->genpd.power_on = exynos_pd_power_on;
+
+       /* pd power on/off latency is less than 1ms */
+       pd->genpd.states[0].power_on_latency_ns = 1000000;
+       pd->genpd.states[0].power_off_latency_ns = 1000000;
+
+       pm_genpd_init(&pd->genpd, NULL, state ? false : true);
+}
+
+/* exynos_pd_show_power_domain - show current power domain status.
+ *
+ * read the status of power domain and show it.
+ */
+static void exynos_pd_show_power_domain(void)
+{
+       struct device_node *np;
+       for_each_compatible_node(np, NULL, "samsung,exynos-pd") {
+               struct platform_device *pdev;
+               struct exynos_pm_domain *pd;
+
+               if (of_device_is_available(np)) {
+                       pdev = of_find_device_by_node(np);
+                       if (!pdev)
+                               continue;
+                       pd = platform_get_drvdata(pdev);
+                       pr_info("   %-9s - %-3s\n", pd->genpd.name,
+                                       cal_pd_status(pd->cal_pdid) ? "on" : "off");
+               } else
+                       pr_info("   %-9s - %s\n", np->name, "on,  always");
+       }
+
+       return;
+}
+
+static __init int exynos_pd_dt_parse(void)
+{
+       struct platform_device *pdev = NULL;
+       struct device_node *np = NULL;
+       int ret = 0;
+
+       for_each_compatible_node(np, NULL, "samsung,exynos-pd") {
+               struct exynos_pm_domain *pd;
+               struct device_node *children;
+               int initial_state;
+
+               /* skip unmanaged power domain */
+               if (!of_device_is_available(np))
+                       continue;
+
+               pdev = of_find_device_by_node(np);
+
+               pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+               if (!pd) {
+                       pr_err(EXYNOS_PD_PREFIX "%s: failed to allocate memory for domain\n",
+                                       __func__);
+                       return -ENOMEM;
+               }
+
+               /* init exynos_pm_domain's members  */
+               pd->name = kstrdup(np->name, GFP_KERNEL);
+               ret = of_property_read_u32(np, "cal_id", (u32 *)&pd->cal_pdid);
+               if (ret) {
+                       pr_err(EXYNOS_PD_PREFIX "%s: failed to get cal_pdid  from of %s\n",
+                                       __func__, pd->name);
+                       return -ENODEV;
+               }
+               pd->of_node = np;
+               pd->pd_control = cal_pd_control;
+               pd->check_status = exynos_pd_status;
+               pd->devfreq_index = of_get_devfreq_sync_volt_idx(pd->of_node);
+               of_get_power_down_ok(pd);
+               pd->power_down_skipped = false;
+
+               ret = of_property_read_u32(np, "need_smc", (u32 *)&pd->need_smc);
+               if (ret) {
+                       pd->need_smc = 0x0;
+               } else {
+                       cal_pd_set_smc_id(pd->cal_pdid, pd->need_smc);
+                       pr_info(EXYNOS_PD_PREFIX "%s: %s read need_smc 0x%x successfully.!\n",
+                                       __func__, pd->name, pd->need_smc);
+               }
+               initial_state = cal_pd_status(pd->cal_pdid);
+               if (initial_state == -1) {
+                       pr_err(EXYNOS_PD_PREFIX "%s: %s is in unknown state\n",
+                                       __func__, pd->name);
+                       return -EINVAL;
+               }
+
+               pd->idle_ip_index = exynos_get_idle_ip_index(pd->name);
+
+               mutex_init(&pd->access_lock);
+               platform_set_drvdata(pdev, pd);
+
+               exynos_pd_genpd_init(pd, initial_state);
+               of_genpd_add_provider_simple(np, &pd->genpd);
+
+               /* add LOGICAL sub-domain
+                * It is not assumed that there is REAL sub-domain.
+                * Power on/off functions are not defined here.
+                */
+               for_each_child_of_node(np, children) {
+                       struct exynos_pm_domain *sub_pd;
+                       struct platform_device *sub_pdev;
+
+                       sub_pd = kzalloc(sizeof(*sub_pd), GFP_KERNEL);
+                       if (!sub_pd) {
+                               pr_err("%s %s: failed to allocate memory for power domain\n",
+                                               EXYNOS_PD_PREFIX, __func__);
+                               return -ENOMEM;
+                       }
+
+                       sub_pd->name = kstrdup(children->name, GFP_KERNEL);
+                       sub_pd->of_node = children;
+
+                       /* Logical sub-domain does not have to power on/off control*/
+                       sub_pd->pd_control = NULL;
+
+                       sub_pd->devfreq_index = of_get_devfreq_sync_volt_idx(sub_pd->of_node);
+
+                       /* kernel does not create sub-domain pdev. */
+                       sub_pdev = of_find_device_by_node(children);
+                       if (!sub_pdev)
+                               sub_pdev = of_platform_device_create(children, NULL, &pdev->dev);
+                       if (!sub_pdev) {
+                               pr_err(EXYNOS_PD_PREFIX "sub domain allocation failed: %s\n", children->name);
+                               continue;
+                       }
+
+                       mutex_init(&sub_pd->access_lock);
+                       platform_set_drvdata(sub_pdev, sub_pd);
+
+                       exynos_pd_genpd_init(sub_pd, initial_state);
+                       of_genpd_add_provider_simple(children, &sub_pd->genpd);
+
+                       if (pm_genpd_add_subdomain(&pd->genpd, &sub_pd->genpd))
+                               pr_err("%s %s can't add subdomain %s\n",
+                                       EXYNOS_PD_PREFIX, pd->genpd.name, sub_pd->genpd.name);
+                       else
+                               pr_info(EXYNOS_PD_PREFIX "%s has a new logical child %s.\n",
+                                               pd->genpd.name, sub_pd->genpd.name);
+               }
+       }
+
+       /* EXCEPTION: add physical sub-pd to master pd using device tree */
+       for_each_compatible_node(np, NULL, "samsung,exynos-pd") {
+               struct exynos_pm_domain *parent_pd, *child_pd;
+               struct device_node *parent;
+               struct platform_device *parent_pd_pdev, *child_pd_pdev;
+               int i;
+
+               /* skip unmanaged power domain */
+               if (!of_device_is_available(np))
+                       continue;
+
+               /* child_pd_pdev should have value. */
+               child_pd_pdev = of_find_device_by_node(np);
+               child_pd = platform_get_drvdata(child_pd_pdev);
+
+               /* search parents in device tree */
+               for (i = 0; i < MAX_PARENT_POWER_DOMAIN; i++) {
+                       /* find parent node if available */
+                       parent = of_parse_phandle(np, "parent", i);
+                       if (!parent)
+                               break;
+
+                       /* display error when parent is unmanaged. */
+                       if (!of_device_is_available(parent)) {
+                               pr_err(EXYNOS_PD_PREFIX "%s is not managed by runtime pm.\n", parent->name);
+                               continue;
+                       }
+
+                       /* parent_pd_pdev should have value. */
+                       parent_pd_pdev = of_find_device_by_node(parent);
+                       parent_pd = platform_get_drvdata(parent_pd_pdev);
+
+                       if (pm_genpd_add_subdomain(&parent_pd->genpd, &child_pd->genpd))
+                               pr_err(EXYNOS_PD_PREFIX "%s cannot add subdomain %s\n",
+                                               parent_pd->name, child_pd->name);
+                       else
+                               pr_info(EXYNOS_PD_PREFIX "%s has a new child %s.\n",
+                                               parent_pd->name, child_pd->name);
+               }
+       }
+
+       return 0;
+}
+#endif /* CONFIG_OF */
+
+static int __init exynos_pd_init(void)
+{
+       int ret;
+#ifdef CONFIG_OF
+       if (of_have_populated_dt()) {
+               ret = exynos_pd_dt_parse();
+               if (ret) {
+                       pr_err(EXYNOS_PD_PREFIX "dt parse failed.\n");
+                       return ret;
+               }
+
+               pr_info("%s PM Domain Initialize\n", EXYNOS_PD_PREFIX);
+               /* show information of power domain registration */
+               exynos_pd_show_power_domain();
+
+               return 0;
+       }
+#endif
+       pr_err(EXYNOS_PD_PREFIX "PM Domain works along with Device Tree\n");
+       return -EPERM;
+}
+subsys_initcall(exynos_pd_init);
diff --git a/include/dt-bindings/power/exynos-power.h b/include/dt-bindings/power/exynos-power.h
new file mode 100644 (file)
index 0000000..47d0523
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * This header provides constants for Exynos PM Domain bindings.
+ *
+ * Copyright (C) 2016 Samsung Electronics Co., Ltd.
+ *     Jeonghoon Jang <jnghn.jang@samsung.com>
+ *
+ * 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.
+ */
+
+#ifndef _DT_BINDINGS_POWER_EXYNOS_POWER_H
+#define _DT_BINDINGS_POWER_EXYNOS_POWER_H
+
+#define PD_OK_AUD              0x1
+#define PD_OK_VTS              0x2
+
+#endif
diff --git a/include/linux/apm-exynos.h b/include/linux/apm-exynos.h
new file mode 100644 (file)
index 0000000..21ec51c
--- /dev/null
@@ -0,0 +1,226 @@
+/* arch/arm64/mach-exynos/include/mach/apm-exynos.h
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *                                               http://www.samsung.com
+ *
+ * APM register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ASM_ARCH_REGS_APM_H
+#define __ASM_ARCH_REGS_APM_H __FILE__
+
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+
+/* Margin related variables */
+#define MARGIN_0MV                             (0)
+#define MARGIN_6_25MV                          (1)
+#define MARGIN_12_5MV                          (2)
+#define MARGIN_18_75MV                         (3)
+#define MARGIN_25MV                            (4)
+#define MARGIN_31_25MV                         (5)
+#define MARGIN_37_5MV                          (6)
+#define MARGIN_43_75MV                         (7)
+#define MARGIN_50MV                            (8)
+#define MARGIN_56_25MV                         (9)
+#define MARGIN_62_5MV                          (0xA)
+#define MARGIN_68_75MV                         (0xB)
+#define MARGIN_75MV                            (0xC)
+#define MARGIN_81_25MV                         (0xD)
+#define MARGIN_87_5MV                          (0xE)
+#define MARGIN_93_75MV                         (0xF)
+
+/* PERIOD related variables */
+#define PERIOD_1MS                             (0)
+#define PERIOD_5MS                             (1)
+
+/* APM Protocol related variables */
+/* Notifier variables */
+#define APM_READY                              (0x0001)
+#define APM_SLEEP                              (0x0002)
+#define APM_TIMEOUT                            (0x0003)
+#define CL_ENABLE                              (0x0004)
+#define CL_DISABLE                             (0x0005)
+
+/* Shift variables */
+#define CL_DVFS_SHIFT                          (29)
+#define COMMAND_SHIFT                          (27)
+#define PM_SECTION_SHIFT                       (26)
+#define MASK_SHIFT                             (25)
+#define INIT_MODE_SHIFT                                (22)
+#define ASV_MODE_SHIFT                         (21)
+#define CL_ALL_STOP_SHIFT                      (30)
+#define CL_ALL_START_SHIFT                     (31)
+#define MULTI_BYTE_SHIFT                       (16)
+#define CL_DOMAIN_SHIFT                                (14)
+
+#define MULTI_BYTE_CNT_SHIFT                   (16)
+#define ATLAS_SHIFT                            (0)
+#define APOLLO_SHIFT                           (4)
+#define G3D_SHIFT                              (8)
+#define MIF_SHIFT                              (12)
+#define PERIOD_SHIFT                           (16)
+#define BYTE_SHIFT                             (8)
+#define BYTE_MASK                              (0xFF)
+#define WRITE_MODE                             (0)
+#define READ_MODE                              (1)
+#define NONE                                   (2)
+#define TX_INTERRUPT_ENABLE                    (1)
+#define MASK                                   (1)
+#define BYTE_4                                 (4)
+#define INIT_SET                               (1)
+#define ASV_SET                                        (1)
+#define DEBUG_COUNT                            (10)
+
+/* Mask variables */
+#define CL_DVFS_MASK                           (1)
+#define COMMAND_MASK                           (0x3)
+#define MULTI_BYTE_MASK                                (0xF)
+#define CL_DVFS                                        (CL_DVFS_MASK << CL_DVFS_SHIFT)
+#define CL_DVFS_OFF                            (0)
+#define COMMAND                                        (COMMAND_MASK << COMMAND_SHIFT)
+#define MULTI_BYTE                             (MULTI_BYTE_MASK << MULTI_BYTE_SHIFT)
+
+/* Error variable */
+#define APM_RET_SUCESS                         (0xa)
+#define APM_GPIO_ERR                           (0xFFFFFFFF)
+#define PMIC_NO_ACK_ERR                                (0xEEEEEEEE)
+#define ERR_TIMEOUT                            (1)
+#define ERR_RETRY                              (2)
+#define ERR_OUT                                        (3)
+#define RETRY_ERR                              (-0xFF)
+
+/* apm related variables */
+#define MSG_LEN                                        (5)
+#define G3D_LV_OFFSET                          (0)
+#define MBOX_LEN                               (4)
+#define TIMEOUT                                        (500)           /* timeout 500 msec */
+#define TX                                     (0)
+#define RX                                     (1)
+#define HSI2C_MODE                             (0)
+#define APM_MODE                               (1)
+#define APM_TIMOUT                             (2)
+
+#define CL_ON                                  (1)
+#define CL_OFF                                 (0)
+
+#define APM_OFF                                        (0)
+#define APM_ON                                 (1)
+#define APM_WFI_TIMEOUT                                (2)
+
+#define EXYNOS_PMU_CORTEXM3_APM_CONFIGURATION                           (0x2500)
+#define EXYNOS_PMU_CORTEXM3_APM_STATUS                                  (0x2504)
+#define EXYNOS_PMU_CORTEXM3_APM_OPTION                                  (0x2508)
+#define EXYNOS_PMU_CORTEXM3_APM_DURATION0                               (0x2510)
+#define EXYNOS_PMU_CORTEXM3_APM_DURATION1                               (0x2514)
+#define EXYNOS_PMU_CORTEXM3_APM_DURATION2                               (0x2518)
+#define EXYNOS_PMU_CORTEXM3_APM_DURATION3                               (0x251C)
+
+/* CORTEX M3 */
+#define ENABLE_APM                      (0x1 << 15)
+#define APM_STATUS_MASK                 (0x1)
+#define STANDBYWFI                      (28)
+#define STANDBYWFI_MASK                 (0x1)
+#define APM_LOCAL_PWR_CFG_RUN           (0x1 << 0)
+#define APM_LOCAL_PWR_CFG_RESET         (~(0x1 << 0))
+
+struct apm_ops {
+       int (*apm_update_bits) (unsigned int type, unsigned int reg,
+                                       unsigned int mask, unsigned int value);
+       int (*apm_write) (unsigned int type, unsigned int reg, unsigned int value);
+       int (*apm_bulk_write) (unsigned int type, unsigned char reg,
+                                       unsigned char *buf, unsigned int count);
+       int (*apm_read) (unsigned int type, unsigned int reg, unsigned int *val);
+       int (*apm_bulk_read) (unsigned int type, unsigned char reg,
+                               unsigned char *buf, unsigned int count);
+};
+
+struct cl_ops {
+       void (*apm_reset) (void);
+       void (*apm_power_up) (void);
+       void (*apm_power_down) (void);
+       int (*cl_dvfs_setup) (unsigned int atlas_cl_limit, unsigned int apollo_cl_limit,
+                                       unsigned int g3d_cl_limit, unsigned int mif_cl_limit, unsigned int cl_period);
+       int (*cl_dvfs_start) (unsigned int cl_domain);
+       int (*cl_dvfs_stop) (unsigned int cl_domain, unsigned int level);
+       int (*cl_dvfs_enable) (void);
+       int (*cl_dvfs_disable) (void);
+       int (*g3d_power_on) (void);
+       int (*g3d_power_down) (void);
+       int (*enter_wfi) (void);
+};
+
+struct debug_data {
+       u32 buf[DEBUG_COUNT][6];
+       s64 time[DEBUG_COUNT];
+       char* name[DEBUG_COUNT];
+       unsigned int cnt;
+#ifdef CONFIG_EXYNOS_APM_VOLTAGE_DEBUG
+       u32 vol[DEBUG_COUNT][4];
+       u32 atl_value;
+       u32 apo_value;
+       u32 g3d_value;
+       u32 mif_value;
+#endif
+};
+
+struct cl_init_data {
+       u32 atlas_margin;
+       u32 apollo_margin;
+       u32 g3d_margin;
+       u32 mif_margin;
+       u32 period;
+       u32 cl_status;
+       u32 apm_status;
+};
+
+extern void cl_dvfs_lock(void);
+extern void cl_dvfs_unlock(void);
+extern int cm3_status_open(struct inode *inode, struct file *file);
+extern struct apm_ops exynos_apm_function_ops;
+
+extern int register_apm_notifier(struct notifier_block *nb);
+extern int unregister_apm_notifier(struct notifier_block *nb);
+extern int apm_notifier_call_chain(unsigned long val);
+extern void exynos_apm_reset_release(void);
+extern void exynos_apm_power_up(void);
+extern void exynos_apm_power_down(void);
+extern int exynos_cl_dvfs_setup(unsigned int atlas_cl_limit, unsigned int apollo_cl_limit, unsigned int g3d_cl_limit,
+                                                                       unsigned int mif_cl_limit, unsigned int cl_period);
+extern int exynos_cl_dvfs_start(unsigned int cl_domain);
+extern int exynos_cl_dvfs_stop(unsigned int cl_domain, unsigned int level);
+extern int exynos_cl_dvfs_mode_enable(void);
+extern int exynos_cl_dvfs_mode_disable(void);
+extern int exynos_g3d_power_on_noti_apm(void);
+extern int exynos_g3d_power_down_noti_apm(void);
+extern int exynos_apm_enter_wfi(void);
+extern int exynos_apm_update_bits(unsigned int type, unsigned int reg, unsigned int mask, unsigned int value);
+extern int exynos_apm_write(unsigned int type, unsigned int address, unsigned int value);
+extern int exynos_apm_bulk_write(unsigned int type, unsigned char reg, unsigned char *buf, unsigned int count);
+extern int exynos_apm_read(unsigned int type, unsigned int reg, unsigned int *val);
+extern int exynos_apm_bulk_read(unsigned int type, unsigned char reg, unsigned char *buf, unsigned int count);
+
+#define exynos7890_cl_dvfs_start(a)            exynos7420_cl_dvfs_start(a)
+#define exynos7890_cl_dvfs_stop(a, b)          exynos7420_cl_dvfs_stop(a, b)
+#define exynos7890_cl_dvfs_mode_enable()       exynos7420_cl_dvfs_mode_enable()
+#define exynos7890_cl_dvfs_mode_disable()      exynos7420_cl_dvfs_mode_disable()
+#define exynos7890_g3d_power_on_noti_apm()     exynos7420_g3d_power_on_noti_apm()
+#define exynos7890_g3d_power_down_noti_apm()   exynos7420_g3d_power_down_noti_apm()
+#define exynos7890_apm_enter_wfi()             exynos7420_apm_enter_wfi()
+#define exynos7890_apm_update_bits(a, b, c, d) exynos7420_apm_update_bits(a, b, c, d)
+#define exynos7890_apm_write(a, b, c)          exynos7420_apm_write(a, b, c)
+#define exynos7890_apm_bulk_write(a, b, c, d)  exynos7420_apm_bulk_write(a, b, c, d)
+#define exynos7890_apm_read(a, b, c)           exynos7420_apm_read(a, b, c)
+#define exynos7890_apm_bulk_read(a, b, c, d)   exynos7420_apm_bulk_read(a, b, c, d)
+
+unsigned int exynos_cortexm3_pmu_read(unsigned int offset);
+void exynos_cortexm3_pmu_write(unsigned int val, unsigned int offset);
+unsigned int exynos_mailbox_reg_read(unsigned int offset);
+void exynos_mailbox_reg_write(unsigned int val, unsigned int offset);
+#endif
diff --git a/include/soc/samsung/exynos-pd.h b/include/soc/samsung/exynos-pd.h
new file mode 100644 (file)
index 0000000..51247ca
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Exynos PM domain support.
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com
+ *
+ * Implementation of Exynos specific power domain control which is used in
+ * conjunction with runtime-pm. Support for both device-tree and non-device-tree
+ * based power domain support is included.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __EXYNOS_PD_H
+#define __EXYNOS_PD_H __FILE__
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+
+#include <linux/mfd/samsung/core.h>
+#include <soc/samsung/bcm.h>
+
+#include <soc/samsung/exynos-powermode.h>
+#include <soc/samsung/exynos-devfreq.h>
+#include <dt-bindings/power/exynos-power.h>
+
+#define EXYNOS_PD_PREFIX       "EXYNOS-PD: "
+#define EXYNOS_PD_DBG_PREFIX   "EXYNOS-PD-DBG: "
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+#endif
+
+#ifdef CONFIG_EXYNOS_PM_DOMAIN_DEBUG
+#define DEBUG_PRINT_INFO(fmt, ...) printk(PM_DOMAIN_PREFIX pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define DEBUG_PRINT_INFO(fmt, ...)
+#endif
+
+/* In Exynos, the number of MAX_POWER_DOMAIN is less than 15 */
+#define MAX_PARENT_POWER_DOMAIN        15
+
+struct exynos_pm_domain;
+
+struct exynos_pm_domain {
+       struct generic_pm_domain genpd;
+       char *name;
+       unsigned int cal_pdid;
+       struct device_node *of_node;
+       int (*pd_control)(unsigned int cal_id, int on);
+       int (*check_status)(struct exynos_pm_domain *pd);
+       bool (*power_down_ok)(void);
+       unsigned int bts;
+       int devfreq_index;
+       struct mutex access_lock;
+       int idle_ip_index;
+#if defined(CONFIG_EXYNOS_BCM)
+       struct bcm_info *bcm;
+#endif
+       bool power_down_skipped;
+       unsigned int need_smc;
+};
+
+struct exynos_pd_dbg_info {
+       struct device *dev;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *d;
+       struct file_operations fops;
+#endif
+};
+
+#ifdef CONFIG_EXYNOS_PD
+struct exynos_pm_domain *exynos_pd_lookup_name(const char *domain_name);
+#else
+static inline struct exynos_pm_domain *exynos_pd_lookup_name(const char *domain_name)
+{
+       return NULL;
+}
+#endif
+
+#ifdef CONFIG_SND_SOC_SAMSUNG_VTS
+extern bool vts_is_on(void);
+#endif
+#ifdef CONFIG_SND_SOC_SAMSUNG_ABOX
+extern bool abox_is_on(void);
+#endif
+
+#endif /* __EXYNOS_PD_H */