591 lines
18 KiB
Diff
591 lines
18 KiB
Diff
From efe4392e390ad7d3709a3f928f462c1c74b04947 Mon Sep 17 00:00:00 2001
|
|
From: gengzonglin <gengzonglin@eswincomputing.com>
|
|
Date: Wed, 17 Jul 2024 17:15:31 +0800
|
|
Subject: [PATCH 105/219] feat(pmdomain): Add power domain framework support.
|
|
|
|
Changelogs:
|
|
1.Add PMU drive.
|
|
2.Temporarily force power on all power domains in DTS.
|
|
|
|
Signed-off-by: gengzonglin <gengzonglin@eswincomputing.com>
|
|
---
|
|
.../dts/eswin/eswin-win2030-die0-soc.dtsi | 23 +-
|
|
drivers/pmdomain/Makefile | 1 +
|
|
drivers/pmdomain/eswin/Makefile | 6 +
|
|
drivers/pmdomain/eswin/eic770x-pmu.c | 406 ++++++++++++++++++
|
|
include/dt-bindings/power/eswin,eic770x-pmu.h | 33 ++
|
|
5 files changed, 462 insertions(+), 7 deletions(-)
|
|
create mode 100644 drivers/pmdomain/eswin/Makefile
|
|
create mode 100644 drivers/pmdomain/eswin/eic770x-pmu.c
|
|
create mode 100644 include/dt-bindings/power/eswin,eic770x-pmu.h
|
|
|
|
diff --git a/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi b/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
|
|
index 79a64e2abaf3..fea1f577fddc 100644
|
|
--- a/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
|
|
+++ b/arch/riscv/boot/dts/eswin/eswin-win2030-die0-soc.dtsi
|
|
@@ -30,6 +30,7 @@
|
|
#include <dt-bindings/clock/win2030-clock.h>
|
|
#include <dt-bindings/i2c/i2c.h>
|
|
#include <dt-bindings/interconnect/eswin,win2030.h>
|
|
+#include <dt-bindings/power/eswin,eic770x-pmu.h>
|
|
|
|
/ {
|
|
compatible = "sifive,hifive-unmatched-a00", "sifive,fu740-c000","sifive,fu740";
|
|
@@ -294,12 +295,14 @@ d0_pmu: power-controller@51808000 {
|
|
#address-cells = <1>;
|
|
#size-cells = <0>;
|
|
compatible = "eswin,win2030-pmu-controller";
|
|
+ #power-domain-cells = <1>;
|
|
reg = <0x0 0x51808000 0x0 0x8000>;
|
|
numa-node-id = <0>;
|
|
d0_pmu_pcie: win2030-pmu-controller-port@0 {
|
|
compatible = "eswin,win2030-pmu-controller-port";
|
|
+ id = <EIC770X_PD_PCIE>;
|
|
reg_base = <0x0>;
|
|
- power_status = <1>;
|
|
+ power_status = <2>;
|
|
power_delay = <6 6 3 3>;
|
|
clock_delay = <4 2 2 2>;
|
|
reset_delay = <2 4 2 2>;
|
|
@@ -309,8 +312,9 @@ d0_pmu_pcie: win2030-pmu-controller-port@0 {
|
|
};
|
|
d0_pmu_dsp1: win2030-pmu-controller-port@40 {
|
|
compatible = "eswin,win2030-pmu-controller-port";
|
|
+ id = <EIC770X_PD_DSP1>;
|
|
reg_base = <0x40>;
|
|
- power_status = <1>;
|
|
+ power_status = <2>;
|
|
power_delay = <6 6 3 3>;
|
|
clock_delay = <4 2 2 2>;
|
|
reset_delay = <2 4 2 2>;
|
|
@@ -320,8 +324,9 @@ d0_pmu_dsp1: win2030-pmu-controller-port@40 {
|
|
};
|
|
d0_pmu_vi: win2030-pmu-controller-port@80 {
|
|
compatible = "eswin,win2030-pmu-controller-port";
|
|
+ id = <EIC770X_PD_VI>;
|
|
reg_base = <0x80>;
|
|
- power_status = <1>;
|
|
+ power_status = <2>;
|
|
power_delay = <6 6 3 3>;
|
|
clock_delay = <4 2 2 2>;
|
|
reset_delay = <2 4 2 2>;
|
|
@@ -331,8 +336,9 @@ d0_pmu_vi: win2030-pmu-controller-port@80 {
|
|
};
|
|
d0_pmu_vo: win2030-pmu-controller-port@c0 {
|
|
compatible = "eswin,win2030-pmu-controller-port";
|
|
+ id = <EIC770X_PD_VO>;
|
|
reg_base = <0xc0>;
|
|
- power_status = <1>;
|
|
+ power_status = <2>;
|
|
power_delay = <6 6 3 3>;
|
|
clock_delay = <4 2 2 2>;
|
|
reset_delay = <2 4 2 2>;
|
|
@@ -341,8 +347,9 @@ d0_pmu_vo: win2030-pmu-controller-port@c0 {
|
|
};
|
|
d0_pmu_codec: win2030-pmu-controller-port@140 {
|
|
compatible = "eswin,win2030-pmu-controller-port";
|
|
+ id = <EIC770X_PD_CODEC>;
|
|
reg_base = <0x140>;
|
|
- power_status = <1>;
|
|
+ power_status = <2>;
|
|
power_delay = <6 6 3 3>;
|
|
clock_delay = <4 2 2 2>;
|
|
reset_delay = <2 4 2 2>;
|
|
@@ -352,8 +359,9 @@ d0_pmu_codec: win2030-pmu-controller-port@140 {
|
|
};
|
|
d0_pmu_dsp2: win2030-pmu-controller-port@200 {
|
|
compatible = "eswin,win2030-pmu-controller-port";
|
|
+ id = <EIC770X_PD_DSP2>;
|
|
reg_base = <0x200>;
|
|
- power_status = <1>;
|
|
+ power_status = <2>;
|
|
power_delay = <6 6 3 3>;
|
|
clock_delay = <4 2 2 2>;
|
|
reset_delay = <2 4 2 2>;
|
|
@@ -363,8 +371,9 @@ d0_pmu_dsp2: win2030-pmu-controller-port@200 {
|
|
};
|
|
d0_pmu_dsp3: win2030-pmu-controller-port@240 {
|
|
compatible = "eswin,win2030-pmu-controller-port";
|
|
+ id = <EIC770X_PD_DSP3>;
|
|
reg_base = <0x240>;
|
|
- power_status = <1>;
|
|
+ power_status = <2>;
|
|
power_delay = <6 6 3 3>;
|
|
clock_delay = <4 2 2 2>;
|
|
reset_delay = <2 4 2 2>;
|
|
diff --git a/drivers/pmdomain/Makefile b/drivers/pmdomain/Makefile
|
|
index 666753676e5c..49aed0580d25 100644
|
|
--- a/drivers/pmdomain/Makefile
|
|
+++ b/drivers/pmdomain/Makefile
|
|
@@ -15,3 +15,4 @@ obj-y += sunxi/
|
|
obj-y += tegra/
|
|
obj-y += ti/
|
|
obj-y += xilinx/
|
|
+obj-y += eswin/
|
|
diff --git a/drivers/pmdomain/eswin/Makefile b/drivers/pmdomain/eswin/Makefile
|
|
new file mode 100644
|
|
index 000000000000..dcaed5c2aa79
|
|
--- /dev/null
|
|
+++ b/drivers/pmdomain/eswin/Makefile
|
|
@@ -0,0 +1,6 @@
|
|
+# SPDX-License-Identifier: GPL-2.0
|
|
+#
|
|
+# Makefile for ESWIN EIC77XX pmu driver
|
|
+#
|
|
+# obj-$(CONFIG_EIC77XX_PMU) += eic77xx-pmu.o
|
|
+obj-y += eic770x-pmu.o
|
|
diff --git a/drivers/pmdomain/eswin/eic770x-pmu.c b/drivers/pmdomain/eswin/eic770x-pmu.c
|
|
new file mode 100644
|
|
index 000000000000..da8a09d21d8b
|
|
--- /dev/null
|
|
+++ b/drivers/pmdomain/eswin/eic770x-pmu.c
|
|
@@ -0,0 +1,406 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * ESWIN PMU Driver
|
|
+ *
|
|
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
|
|
+ * SPDX-License-Identifier: GPL-2.0
|
|
+ *
|
|
+ * 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, version 2.
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ * Authors: gengzonglin <gengzonglin@eswincomputing.com>
|
|
+ */
|
|
+
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/iopoll.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/eswin-win2030-sid-cfg.h>
|
|
+#include <linux/pm_domain.h>
|
|
+#include <dt-bindings/power/eswin,eic770x-pmu.h>
|
|
+
|
|
+#define PD_CTRL 0x0 // override
|
|
+#define PD_SW_COLLAPSE 0x4 // sw collapse en
|
|
+#define PD_SW_PWR 0x8 // power switch ack & power switch en
|
|
+#define PD_SW_RESET 0xC // reg reset
|
|
+#define PD_SW_ISO 0x10 // sw clamp io
|
|
+#define PD_SW_CLK_DISABLE 0x14 // sw clk disable
|
|
+#define PD_PWR_DLY 0x18 // sw clk disable
|
|
+#define PD_CLK_DLY 0x1c // sw clk disable
|
|
+#define PD_RST_DLY 0x20 // sw clk disable
|
|
+#define PD_CLAMP_DLY 0x24 // sw clk disable
|
|
+#define PD_DEBUG 0x28 // status
|
|
+
|
|
+#define PD_STATUS_MASK 0x03 // STATE 3'b011 power on, 3'b000 power off
|
|
+
|
|
+#define eic770x_PMU_TIMEOUT_US 500
|
|
+
|
|
+struct pmu_device_power_delay {
|
|
+ unsigned int dly_en_up; /* wait time after power-on */
|
|
+ unsigned int dly_en_down; /* delay time of the req signal */
|
|
+ unsigned int en_up_div_exp; /* */
|
|
+ unsigned int en_down_div_exp; /* */
|
|
+};
|
|
+
|
|
+struct pmu_device_clock_delay {
|
|
+ unsigned int disable_div_exp;
|
|
+ unsigned int nodisable_div_exp;
|
|
+ unsigned int dly_disable_clk;
|
|
+ unsigned int dly_nodisble_clk;
|
|
+};
|
|
+
|
|
+struct pmu_device_reset_delay {
|
|
+ unsigned int dly_deassert_ares;
|
|
+ unsigned int dly_assert_ares;
|
|
+ unsigned int deassert_div_exp;
|
|
+ unsigned int assert_div_exp;
|
|
+};
|
|
+
|
|
+struct pmu_device_clamp_delay {
|
|
+ unsigned int dly_unclamp_io;
|
|
+ unsigned int dly_clamp_io;
|
|
+ unsigned int unclamp_div_exp;
|
|
+ unsigned int clamp_div_exp;
|
|
+};
|
|
+
|
|
+struct eic770x_domain_info
|
|
+{
|
|
+ const char *name;
|
|
+ void __iomem *reg_base;
|
|
+ struct device_node *of_node;
|
|
+ unsigned int status;
|
|
+ struct pmu_device_power_delay power_dly;
|
|
+ struct pmu_device_clock_delay clk_dly;
|
|
+ struct pmu_device_reset_delay reset_dly;
|
|
+ struct pmu_device_clamp_delay clamp_dly;
|
|
+};
|
|
+
|
|
+struct eic770x_pmu {
|
|
+ struct device *dev;
|
|
+ void __iomem *base;
|
|
+ struct eic770x_domain_info *domain_info;
|
|
+ unsigned int num_domains;
|
|
+ struct generic_pm_domain **genpd;
|
|
+ struct genpd_onecell_data genpd_data;
|
|
+};
|
|
+
|
|
+struct eic770x_pmu_dev {
|
|
+ struct eic770x_domain_info *domain_info;
|
|
+ struct eic770x_pmu *pmu;
|
|
+ struct generic_pm_domain genpd;
|
|
+};
|
|
+
|
|
+static int eic770x_pmu_get_domain_state(struct eic770x_pmu_dev *pmd, bool *is_on)
|
|
+{
|
|
+ *is_on = false;
|
|
+
|
|
+ if(PD_STATUS_MASK & ioread32(pmd->domain_info->reg_base + PD_DEBUG)) {
|
|
+ *is_on = true;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int eic770x_pmu_set_domain_state(struct eic770x_pmu_dev *pmd, bool off)
|
|
+{
|
|
+ iowrite32(0x0, pmd->domain_info->reg_base + PD_CTRL); // power domain cntr use hardware
|
|
+ iowrite32(off, pmd->domain_info->reg_base + PD_SW_COLLAPSE); // collapse 1:power off 0:power on
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int eic770x_pmu_domain_on(struct generic_pm_domain *genpd)
|
|
+{
|
|
+ struct eic770x_pmu_dev *pmd = container_of(genpd,
|
|
+ struct eic770x_pmu_dev, genpd);
|
|
+ struct eic770x_pmu *pmu = pmd->pmu;
|
|
+ struct device_node *node = pmd->domain_info->of_node;
|
|
+ bool is_on;
|
|
+ int ret;
|
|
+ u32 val;
|
|
+
|
|
+ eic770x_pmu_get_domain_state(pmd, &is_on);
|
|
+ if (is_on == true) {
|
|
+ dev_info(pmu->dev, "pm domain [%s] was already in power on state.\n",
|
|
+ pmd->genpd.name);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ dev_info(pmu->dev, "The %s enters power-on process.\n", pmd->genpd.name);
|
|
+
|
|
+ eic770x_pmu_set_domain_state(pmd, false); //true: power off.
|
|
+ ret = readl_poll_timeout_atomic(pmd->domain_info->reg_base + PD_DEBUG,
|
|
+ val, (val & PD_STATUS_MASK),
|
|
+ 1, eic770x_PMU_TIMEOUT_US);
|
|
+ if (ret) {
|
|
+ dev_err(pmu->dev, "%s: failed to power on\n",
|
|
+ pmd->genpd.name);
|
|
+ return -ETIMEDOUT;
|
|
+ }
|
|
+
|
|
+ if (!of_property_read_u32(node, "tbus", &val)) {
|
|
+ win2030_tbu_power_by_dev_and_node(pmu->dev, node, true); // tbu power on
|
|
+ dev_info(pmu->dev, "%s power on tbu.\n", pmd->genpd.name);
|
|
+ }
|
|
+
|
|
+ dev_info(pmu->dev, "The %s ends power-on process.\n", pmd->genpd.name);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int eic770x_pmu_domain_off(struct generic_pm_domain *genpd)
|
|
+{
|
|
+ struct eic770x_pmu_dev *pmd = container_of(genpd,
|
|
+ struct eic770x_pmu_dev, genpd);
|
|
+ struct eic770x_pmu *pmu = pmd->pmu;
|
|
+ struct device_node *node = pmd->domain_info->of_node;
|
|
+ bool is_on = false;
|
|
+ int ret;
|
|
+ u32 val;
|
|
+
|
|
+ eic770x_pmu_get_domain_state(pmd, &is_on);
|
|
+ if (is_on == false) {
|
|
+ dev_info(pmu->dev, "pm domain [%s] was already in power off state.\n",
|
|
+ pmd->genpd.name);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ dev_info(pmu->dev, "The %s enters power off process.\n", pmd->genpd.name);
|
|
+
|
|
+ if (!of_property_read_u32(node, "tbus", &val)) {
|
|
+ win2030_tbu_power_by_dev_and_node(pmu->dev, node, false); // tbu power off
|
|
+ dev_info(pmu->dev, "%s power off tbu.\n", pmd->genpd.name);
|
|
+ }
|
|
+
|
|
+ eic770x_pmu_set_domain_state(pmd, true); //true: power off.
|
|
+ ret = readl_poll_timeout_atomic(pmd->domain_info->reg_base + PD_DEBUG,
|
|
+ val, !(val & PD_STATUS_MASK),
|
|
+ 1, eic770x_PMU_TIMEOUT_US);
|
|
+ if (ret) {
|
|
+ dev_err(pmu->dev, "%s: failed to power off\n",
|
|
+ pmd->genpd.name);
|
|
+ return -ETIMEDOUT;
|
|
+ }
|
|
+ dev_info(pmu->dev, "The %s ends power off process.\n", pmd->genpd.name);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int eic770x_pmu_init_domain(struct eic770x_pmu *pmu, int index)
|
|
+{
|
|
+ struct eic770x_pmu_dev *pmd;
|
|
+ int ret;
|
|
+ bool is_on = false;
|
|
+
|
|
+ pmd = devm_kzalloc(pmu->dev, sizeof(*pmd), GFP_KERNEL);
|
|
+ if (!pmd)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ pmd->domain_info = &pmu->domain_info[index];
|
|
+ pmd->pmu = pmu;
|
|
+
|
|
+ pmd->genpd.name = pmd->domain_info->name;
|
|
+
|
|
+ if(pmd->domain_info->status == 2) {
|
|
+ pmd->genpd.flags = GENPD_FLAG_ALWAYS_ON;
|
|
+ }
|
|
+
|
|
+ if(pmd->domain_info->status == 0) {
|
|
+ eic770x_pmu_domain_off(&pmd->genpd);
|
|
+ }
|
|
+ else {
|
|
+ eic770x_pmu_domain_on(&pmd->genpd);
|
|
+ }
|
|
+
|
|
+ ret = eic770x_pmu_get_domain_state(pmd, &is_on);
|
|
+ if (ret)
|
|
+ dev_warn(pmu->dev, "unable to get current state for %s\n",
|
|
+ pmd->genpd.name);
|
|
+
|
|
+ pmd->genpd.power_on = eic770x_pmu_domain_on;
|
|
+ pmd->genpd.power_off = eic770x_pmu_domain_off;
|
|
+ pm_genpd_init(&pmd->genpd, NULL, !is_on);
|
|
+
|
|
+ pmu->genpd_data.domains[index] = &pmd->genpd;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int eic770x_pmu_add_domain(struct device *dev, struct eic770x_pmu *pmu)
|
|
+{
|
|
+ struct eic770x_domain_info *pd_info;
|
|
+ struct device_node *node;
|
|
+ int id, nval, num_domains;
|
|
+ int ret;
|
|
+ unsigned int val[32];
|
|
+
|
|
+ num_domains = device_get_child_node_count(dev);
|
|
+ if (num_domains == 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ pmu->domain_info = devm_kcalloc(dev, num_domains,
|
|
+ sizeof(*pd_info),
|
|
+ GFP_KERNEL);
|
|
+ if (!pmu->domain_info)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ num_domains = 0;
|
|
+ for_each_child_of_node(dev->of_node, node)
|
|
+ {
|
|
+ ret = of_property_read_u32(node, "id", &id);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "Failed to parse pmu id.\n");
|
|
+ continue;
|
|
+ }
|
|
+ if(id > EIC770X_PD_DSP3) {
|
|
+ dev_err(dev, "pmu id %d out of range.\n", id);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ pd_info = &pmu->domain_info[id];
|
|
+ pd_info->of_node = node;
|
|
+ unsigned int val_u32;
|
|
+ of_property_read_u32(node, "reg_base", &val_u32);
|
|
+ pd_info->reg_base = pmu->base + val_u32;
|
|
+
|
|
+ of_property_read_string(node, "label", &pd_info->name);
|
|
+ of_property_read_u32(node, "power_status", &pd_info->status);
|
|
+
|
|
+ nval = of_property_read_u32_array(node, "power_delay", &val[0], 4);
|
|
+ if(!nval ){
|
|
+ pd_info->power_dly.dly_en_up = val[0];
|
|
+ pd_info->power_dly.dly_en_down = val[1];
|
|
+ pd_info->power_dly.en_up_div_exp = val[2];
|
|
+ pd_info->power_dly.en_down_div_exp = val[3];
|
|
+ val_u32 = (pd_info->power_dly.dly_en_up & 0xff) |
|
|
+ ((pd_info->power_dly.dly_en_down & 0xff) << 8) |
|
|
+ ((pd_info->power_dly.en_up_div_exp & 0xf) << 16) |
|
|
+ ((pd_info->power_dly.en_down_div_exp & 0xf) << 20);
|
|
+ iowrite32( val_u32, pd_info->reg_base + PD_PWR_DLY);
|
|
+ }
|
|
+
|
|
+ nval = of_property_read_u32_array(node, "clock_delay", &val[0], 4);
|
|
+ if(!nval){
|
|
+ pd_info->clk_dly.dly_nodisble_clk = val[0];
|
|
+ pd_info->clk_dly.dly_disable_clk = val[1];
|
|
+ pd_info->clk_dly.nodisable_div_exp = val[2];
|
|
+ pd_info->clk_dly.disable_div_exp = val[3];
|
|
+ val_u32 = (pd_info->clk_dly.dly_nodisble_clk & 0xff) |
|
|
+ ((pd_info->clk_dly.dly_disable_clk & 0xff) << 8) |
|
|
+ ((pd_info->clk_dly.nodisable_div_exp & 0xf) << 16) |
|
|
+ ((pd_info->clk_dly.disable_div_exp & 0xf) << 20);
|
|
+ iowrite32( val_u32, pd_info->reg_base + PD_CLK_DLY);
|
|
+ }
|
|
+
|
|
+ nval = of_property_read_u32_array(node, "reset_delay", &val[0], 4);
|
|
+ if(!nval){
|
|
+ pd_info->reset_dly.dly_assert_ares = val[0];
|
|
+ pd_info->reset_dly.dly_deassert_ares = val[1];
|
|
+ pd_info->reset_dly.assert_div_exp = val[2];
|
|
+ pd_info->reset_dly.deassert_div_exp = val[3];
|
|
+ val_u32 = (pd_info->reset_dly.dly_assert_ares & 0xff) |
|
|
+ ((pd_info->reset_dly.dly_deassert_ares & 0xff) << 8) |
|
|
+ ((pd_info->reset_dly.assert_div_exp & 0xf) << 16) |
|
|
+ ((pd_info->reset_dly.deassert_div_exp & 0xf) << 20);
|
|
+ iowrite32( val_u32, pd_info->reg_base + PD_RST_DLY);
|
|
+ }
|
|
+
|
|
+ nval = of_property_read_u32_array(node, "clamp_delay", &val[0], 4);
|
|
+ if(!nval){
|
|
+ pd_info->clamp_dly.dly_unclamp_io = val[0];
|
|
+ pd_info->clamp_dly.dly_clamp_io = val[1];
|
|
+ pd_info->clamp_dly.unclamp_div_exp = val[2];
|
|
+ pd_info->clamp_dly.clamp_div_exp = val[3];
|
|
+ val_u32 = (pd_info->clamp_dly.dly_unclamp_io & 0xff) |
|
|
+ ((pd_info->clamp_dly.dly_clamp_io & 0xff) << 8) |
|
|
+ ((pd_info->clamp_dly.unclamp_div_exp & 0xf) << 16) |
|
|
+ ((pd_info->clamp_dly.clamp_div_exp & 0xf) << 20);
|
|
+ iowrite32( val_u32, pd_info->reg_base + PD_CLAMP_DLY);
|
|
+ }
|
|
+
|
|
+ num_domains++;
|
|
+ }
|
|
+ pmu->num_domains = num_domains;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int eic770x_pmu_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct device_node *np = dev->of_node;
|
|
+ struct eic770x_pmu *pmu;
|
|
+ unsigned int i;
|
|
+ int ret;
|
|
+
|
|
+ dev_info(dev, "start registe power domains\n");
|
|
+
|
|
+ pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL);
|
|
+ if (!pmu)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ pmu->base = devm_platform_ioremap_resource(pdev, 0);
|
|
+ if (IS_ERR(pmu->base))
|
|
+ return PTR_ERR(pmu->base);
|
|
+
|
|
+ eic770x_pmu_add_domain(dev, pmu);
|
|
+
|
|
+ pmu->genpd = devm_kcalloc(dev, pmu->num_domains,
|
|
+ sizeof(struct generic_pm_domain *),
|
|
+ GFP_KERNEL);
|
|
+ if (!pmu->genpd)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ pmu->dev = dev;
|
|
+ pmu->genpd_data.domains = pmu->genpd;
|
|
+ pmu->genpd_data.num_domains = pmu->num_domains;
|
|
+
|
|
+ for (i = 0; i < pmu->num_domains; i++) {
|
|
+ ret = eic770x_pmu_init_domain(pmu, i);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "failed to initialize power domain\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "failed to register genpd driver: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dev_info(dev, "registered %u power domains\n", i);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static const struct of_device_id eic770x_pmu_of_match[] = {
|
|
+ {
|
|
+ .compatible = "eswin,win2030-pmu-controller",
|
|
+ }, {
|
|
+ /* sentinel */
|
|
+ }
|
|
+};
|
|
+
|
|
+static struct platform_driver eic770x_pmu_driver = {
|
|
+ .probe = eic770x_pmu_probe,
|
|
+ .driver = {
|
|
+ .name = "eswin-pmu",
|
|
+ .of_match_table = eic770x_pmu_of_match,
|
|
+ },
|
|
+};
|
|
+builtin_platform_driver(eic770x_pmu_driver);
|
|
+
|
|
+MODULE_AUTHOR("Geng Zonglin <gengzonglin@eswincomputing.com>");
|
|
+MODULE_DESCRIPTION("ESWIN eic770x PMU Driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/include/dt-bindings/power/eswin,eic770x-pmu.h b/include/dt-bindings/power/eswin,eic770x-pmu.h
|
|
new file mode 100644
|
|
index 000000000000..aca28e5543fe
|
|
--- /dev/null
|
|
+++ b/include/dt-bindings/power/eswin,eic770x-pmu.h
|
|
@@ -0,0 +1,33 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * ESWIN PMU Driver
|
|
+ *
|
|
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
|
|
+ * SPDX-License-Identifier: GPL-2.0
|
|
+ *
|
|
+ * 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, version 2.
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
+ *
|
|
+ * Authors: gengzonglin <gengzonglin@eswincomputing.com>
|
|
+ */
|
|
+
|
|
+#ifndef __DT_BINDINGS_EIC770X_POWER_H__
|
|
+#define __DT_BINDINGS_EIC770X_POWER_H__
|
|
+
|
|
+#define EIC770X_PD_PCIE 0
|
|
+#define EIC770X_PD_DSP1 1
|
|
+#define EIC770X_PD_VI 2
|
|
+#define EIC770X_PD_VO 3
|
|
+#define EIC770X_PD_CODEC 4
|
|
+#define EIC770X_PD_DSP2 5
|
|
+#define EIC770X_PD_DSP3 6
|
|
+#endif
|
|
--
|
|
2.47.0
|
|
|