Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0653f67 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'v4.7-rockchip-drivers-2' of...

Merge tag 'v4.7-rockchip-drivers-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip into next/drivers

Merge "Rockchip driver updates for v4.7 - part2" from Heiko Stübner:

Ability to save and restore the power-domain quality of service
settings that get lost and reset on power-domain power cycles.

* tag 'v4.7-rockchip-drivers-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip:
  soc: rockchip: power-domain: support qos save and restore
  dt-bindings: modify document of Rockchip power domains
parents 036dae83 074c6a42
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,15 @@ Required properties for power domain sub nodes:
	"include/dt-bindings/power/rk3399-power.h" - for RK3399 type power domain.
	"include/dt-bindings/power/rk3399-power.h" - for RK3399 type power domain.
- clocks (optional): phandles to clocks which need to be enabled while power domain
- clocks (optional): phandles to clocks which need to be enabled while power domain
	switches state.
	switches state.
- pm_qos (optional): phandles to qos blocks which need to be saved and restored
	while power domain switches state.

Qos Example:

	qos_gpu: qos_gpu@ffaf0000 {
		compatible ="syscon";
		reg = <0x0 0xffaf0000 0x0 0x20>;
	};


Example:
Example:


@@ -32,6 +41,7 @@ Example:
		pd_gpu {
		pd_gpu {
			reg = <RK3288_PD_GPU>;
			reg = <RK3288_PD_GPU>;
			clocks = <&cru ACLK_GPU>;
			clocks = <&cru ACLK_GPU>;
			pm_qos = <&qos_gpu>;
		};
		};
	};
	};


+102 −3
Original line number Original line Diff line number Diff line
@@ -46,10 +46,20 @@ struct rockchip_pmu_info {
	const struct rockchip_domain_info *domain_info;
	const struct rockchip_domain_info *domain_info;
};
};


#define MAX_QOS_REGS_NUM	5
#define QOS_PRIORITY		0x08
#define QOS_MODE		0x0c
#define QOS_BANDWIDTH		0x10
#define QOS_SATURATION		0x14
#define QOS_EXTCONTROL		0x18

struct rockchip_pm_domain {
struct rockchip_pm_domain {
	struct generic_pm_domain genpd;
	struct generic_pm_domain genpd;
	const struct rockchip_domain_info *info;
	const struct rockchip_domain_info *info;
	struct rockchip_pmu *pmu;
	struct rockchip_pmu *pmu;
	int num_qos;
	struct regmap **qos_regmap;
	u32 *qos_save_regs[MAX_QOS_REGS_NUM];
	int num_clks;
	int num_clks;
	struct clk *clks[];
	struct clk *clks[];
};
};
@@ -118,6 +128,55 @@ static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
	return 0;
	return 0;
}
}


static int rockchip_pmu_save_qos(struct rockchip_pm_domain *pd)
{
	int i;

	for (i = 0; i < pd->num_qos; i++) {
		regmap_read(pd->qos_regmap[i],
			    QOS_PRIORITY,
			    &pd->qos_save_regs[0][i]);
		regmap_read(pd->qos_regmap[i],
			    QOS_MODE,
			    &pd->qos_save_regs[1][i]);
		regmap_read(pd->qos_regmap[i],
			    QOS_BANDWIDTH,
			    &pd->qos_save_regs[2][i]);
		regmap_read(pd->qos_regmap[i],
			    QOS_SATURATION,
			    &pd->qos_save_regs[3][i]);
		regmap_read(pd->qos_regmap[i],
			    QOS_EXTCONTROL,
			    &pd->qos_save_regs[4][i]);
	}
	return 0;
}

static int rockchip_pmu_restore_qos(struct rockchip_pm_domain *pd)
{
	int i;

	for (i = 0; i < pd->num_qos; i++) {
		regmap_write(pd->qos_regmap[i],
			     QOS_PRIORITY,
			     pd->qos_save_regs[0][i]);
		regmap_write(pd->qos_regmap[i],
			     QOS_MODE,
			     pd->qos_save_regs[1][i]);
		regmap_write(pd->qos_regmap[i],
			     QOS_BANDWIDTH,
			     pd->qos_save_regs[2][i]);
		regmap_write(pd->qos_regmap[i],
			     QOS_SATURATION,
			     pd->qos_save_regs[3][i]);
		regmap_write(pd->qos_regmap[i],
			     QOS_EXTCONTROL,
			     pd->qos_save_regs[4][i]);
	}

	return 0;
}

static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
{
{
	struct rockchip_pmu *pmu = pd->pmu;
	struct rockchip_pmu *pmu = pd->pmu;
@@ -161,7 +220,7 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
			clk_enable(pd->clks[i]);
			clk_enable(pd->clks[i]);


		if (!power_on) {
		if (!power_on) {
			/* FIXME: add code to save AXI_QOS */
			rockchip_pmu_save_qos(pd);


			/* if powering down, idle request to NIU first */
			/* if powering down, idle request to NIU first */
			rockchip_pmu_set_idle_request(pd, true);
			rockchip_pmu_set_idle_request(pd, true);
@@ -173,7 +232,7 @@ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
			/* if powering up, leave idle mode */
			/* if powering up, leave idle mode */
			rockchip_pmu_set_idle_request(pd, false);
			rockchip_pmu_set_idle_request(pd, false);


			/* FIXME: add code to restore AXI_QOS */
			rockchip_pmu_restore_qos(pd);
		}
		}


		for (i = pd->num_clks - 1; i >= 0; i--)
		for (i = pd->num_clks - 1; i >= 0; i--)
@@ -241,9 +300,10 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
{
{
	const struct rockchip_domain_info *pd_info;
	const struct rockchip_domain_info *pd_info;
	struct rockchip_pm_domain *pd;
	struct rockchip_pm_domain *pd;
	struct device_node *qos_node;
	struct clk *clk;
	struct clk *clk;
	int clk_cnt;
	int clk_cnt;
	int i;
	int i, j;
	u32 id;
	u32 id;
	int error;
	int error;


@@ -303,6 +363,45 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
			clk, node->name);
			clk, node->name);
	}
	}


	pd->num_qos = of_count_phandle_with_args(node, "pm_qos",
						 NULL);

	if (pd->num_qos > 0) {
		pd->qos_regmap = devm_kcalloc(pmu->dev, pd->num_qos,
					      sizeof(*pd->qos_regmap),
					      GFP_KERNEL);
		if (!pd->qos_regmap) {
			error = -ENOMEM;
			goto err_out;
		}

		for (j = 0; j < MAX_QOS_REGS_NUM; j++) {
			pd->qos_save_regs[j] = devm_kcalloc(pmu->dev,
							    pd->num_qos,
							    sizeof(u32),
							    GFP_KERNEL);
			if (!pd->qos_save_regs[j]) {
				error = -ENOMEM;
				goto err_out;
			}
		}

		for (j = 0; j < pd->num_qos; j++) {
			qos_node = of_parse_phandle(node, "pm_qos", j);
			if (!qos_node) {
				error = -ENODEV;
				goto err_out;
			}
			pd->qos_regmap[j] = syscon_node_to_regmap(qos_node);
			if (IS_ERR(pd->qos_regmap[j])) {
				error = -ENODEV;
				of_node_put(qos_node);
				goto err_out;
			}
			of_node_put(qos_node);
		}
	}

	error = rockchip_pd_power(pd, true);
	error = rockchip_pd_power(pd, true);
	if (error) {
	if (error) {
		dev_err(pmu->dev,
		dev_err(pmu->dev,