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

Commit 2e298179 authored by Suman Tatiraju's avatar Suman Tatiraju
Browse files

msm: kgsl: Enable gfx rail retention voltage



When the gpu is sufficiently idle, set the gfx rail voltage to
retention to save power. Leave a vote on the MX rail to avoid
RPM transaction.

Change-Id: If6b49aee49d64bd57aa9c4cce153017af3f8eae6
Signed-off-by: default avatarSuman Tatiraju <sumant@codeaurora.org>
parent d9642cc2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ Optional Properties:
			   the chip identification read from the GPU hardware.
			   This is used to override faulty hardware readings.
- qcom,strtstp-sleepwake:  Boolean. Enables use of GPU SLUMBER instead of SLEEP for power savings
- qcom,gx-retention:  	   Boolean. Enables use of GX rail RETENTION voltage

- qcom,pm-qos-active-latency:
				Right after GPU wakes up from sleep, driver votes for
+8 −4
Original line number Diff line number Diff line
@@ -84,19 +84,22 @@
		/*
		 * Clocks = KGSL_CLK_CORE | KGSL_CLK_IFACE
		 * KGSL_CLK_RBBMTIMER | KGSL_CLK_MEM_IFACE
		 * KGSL_CLK_ALT_MEM_IFACE | KGSL_CLK_MEM
		 * KGSL_CLK_ALT_MEM_IFACE | KGSL_CLK_MEM |
		 * KGSL_CLK_MX
		 */
		qcom,clk-map = <0x000000DE>;
		qcom,clk-map = <0x000004DE>;

		clocks = <&clock_gpu clk_gpu_gx_gfx3d_clk>,
			<&clock_gpu clk_gpu_ahb_clk>,
			<&clock_gpu clk_gpu_gx_rbbmtimer_clk>,
			<&clock_gcc clk_gcc_bimc_gfx_clk>,
			<&clock_gcc clk_gcc_mmss_bimc_gfx_clk>,
			<&clock_mmss clk_mmss_mmagic_ahb_clk>;
			<&clock_mmss clk_mmss_mmagic_ahb_clk>,
			<&clock_gpu clk_gpu_mx_clk>;

		clock-names = "core_clk", "iface_clk", "rbbmtimer_clk",
			"mem_clk", "mem_iface_clk", "alt_mem_iface_clk";
			"mem_clk", "mem_iface_clk", "alt_mem_iface_clk",
			"mx_clk";

		/* Bus Scale Settings */
		qcom,gpubw-dev = <&gpubw>;
@@ -125,6 +128,7 @@
		vddcx-supply = <&gdsc_gpu>;
		vdd-supply = <&gdsc_gpu_gx>;

		qcom,gx-retention;
		/* Power levels */
		qcom,gpu-pwrlevels {
			#address-cells = <1>;
+98 −2
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#define KGSL_PWRFLAGS_CLK_ON   1
#define KGSL_PWRFLAGS_AXI_ON   2
#define KGSL_PWRFLAGS_IRQ_ON   3
#define KGSL_PWRFLAGS_RETENTION_ON  4

#define UPDATE_BUSY_VAL		1000000

@@ -97,6 +98,11 @@ static struct clk_pair clks[KGSL_MAX_CLKS] = {
	},
};

static struct clk_pair dummy_mx_clk = {
		.name = "mx_clk",
		.map = KGSL_CLK_MX,
};

static unsigned int ib_votes[KGSL_MAX_BUSLEVELS];
static int last_vote_buslevel;
static int max_vote_buslevel;
@@ -109,6 +115,7 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device,
				unsigned int state);
static void kgsl_pwrctrl_request_state(struct kgsl_device *device,
				unsigned int state);
static void kgsl_pwrctrl_retention_clk(struct kgsl_device *device, int state);

/**
 * _record_pwrevent() - Record the history of the new event
@@ -919,6 +926,9 @@ static void __force_on(struct kgsl_device *device, int flag, int on)
		case KGSL_PWRFLAGS_POWER_ON:
			kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_ON);
			break;
		case KGSL_PWRFLAGS_RETENTION_ON:
			kgsl_pwrctrl_retention_clk(device, KGSL_PWRFLAGS_ON);
			break;
		}
		set_bit(flag, &device->pwrctrl.ctrl_flags);
	} else {
@@ -1002,6 +1012,21 @@ static ssize_t kgsl_pwrctrl_force_rail_on_store(struct device *dev,
	return __force_on_store(dev, attr, buf, count, KGSL_PWRFLAGS_POWER_ON);
}

static ssize_t kgsl_pwrctrl_force_non_retention_on_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	return __force_on_show(dev, attr, buf, KGSL_PWRFLAGS_RETENTION_ON);
}

static ssize_t kgsl_pwrctrl_force_non_retention_on_store(struct device *dev,
					struct device_attribute *attr,
					const char *buf, size_t count)
{
	return __force_on_store(dev, attr, buf, count,
					KGSL_PWRFLAGS_RETENTION_ON);
}

static ssize_t kgsl_pwrctrl_bus_split_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
@@ -1163,6 +1188,9 @@ static DEVICE_ATTR(default_pwrlevel, 0644,
	kgsl_pwrctrl_default_pwrlevel_show,
	kgsl_pwrctrl_default_pwrlevel_store);
static DEVICE_ATTR(popp, 0644, kgsl_popp_show, kgsl_popp_store);
static DEVICE_ATTR(force_non_retention_on, 0644,
	kgsl_pwrctrl_force_non_retention_on_show,
	kgsl_pwrctrl_force_non_retention_on_store);

static const struct device_attribute *pwrctrl_attr_list[] = {
	&dev_attr_gpuclk,
@@ -1180,6 +1208,7 @@ static const struct device_attribute *pwrctrl_attr_list[] = {
	&dev_attr_force_clk_on,
	&dev_attr_force_bus_on,
	&dev_attr_force_rail_on,
	&dev_attr_force_non_retention_on,
	&dev_attr_bus_split,
	&dev_attr_default_pwrlevel,
	&dev_attr_popp,
@@ -1217,6 +1246,54 @@ void kgsl_pwrctrl_busy_time(struct kgsl_device *device, u64 time, u64 busy)
}
EXPORT_SYMBOL(kgsl_pwrctrl_busy_time);

static void kgsl_pwrctrl_retention_clk(struct kgsl_device *device, int state)
{
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	int i = 0;

	if (!(pwr->gx_retention) || test_bit(KGSL_PWRFLAGS_RETENTION_ON,
					&device->pwrctrl.ctrl_flags))
		return;

	if (state == KGSL_PWRFLAGS_OFF) {
		if (test_and_clear_bit(KGSL_PWRFLAGS_RETENTION_ON,
			&pwr->power_flags)) {
			trace_kgsl_retention_clk(device, state);
			/* prepare the mx clk to avoid RPM transactions*/
			clk_set_rate(pwr->dummy_mx_clk,
				pwr->pwrlevels
				[pwr->active_pwrlevel].
				gpu_freq);
			clk_prepare(pwr->dummy_mx_clk);
			/*
			 * Unprepare Gfx clocks to put Gfx rail to
			 * retention voltage.
			 */
			for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
				if (pwr->grp_clks[i])
					clk_unprepare(pwr->grp_clks[i]);
		}
	} else if (state == KGSL_PWRFLAGS_ON) {
		if (!test_and_set_bit(KGSL_PWRFLAGS_RETENTION_ON,
					&pwr->power_flags)) {
			trace_kgsl_retention_clk(device, state);
			/*
			 * Prepare Gfx clocks to put Gfx rail out
			 * of rentention
			 */
			for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
				if (pwr->grp_clks[i])
					clk_prepare(pwr->grp_clks[i]);

			/* unprepare the dummy mx clk*/
			clk_unprepare(pwr->dummy_mx_clk);
			clk_set_rate(pwr->dummy_mx_clk,
				pwr->pwrlevels[pwr->num_pwrlevels - 1].
				gpu_freq);
		}
	}
}

static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
					  int requested_state)
{
@@ -1469,6 +1546,19 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
		&pwr->deep_nap_timeout))
		pwr->deep_nap_timeout = HZ/50;

	pwr->gx_retention = of_property_read_bool(pdev->dev.of_node,
						"qcom,gx-retention");
	if (pwr->gx_retention) {
		clk = clk_get(&pdev->dev, dummy_mx_clk.name);
		if (IS_ERR(clk)) {
			pwr->gx_retention = 0;
			KGSL_PWR_ERR(device, "clk_get(%s) failed: %d\n",
				 dummy_mx_clk.name, (int)PTR_ERR(clk));
		} else
			pwr->dummy_mx_clk = clk;
	}
	pwr->power_flags = BIT(KGSL_PWRFLAGS_RETENTION_ON);

	if (pdata->num_levels > KGSL_MAX_PWRLEVELS ||
	    pdata->num_levels < 1) {
		KGSL_PWR_ERR(device, "invalid power level count: %d\n",
@@ -1557,8 +1647,6 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
		}
	}

	pwr->power_flags = 0;

	pwr->interval_timeout = pdata->idle_timeout;
	pwr->strtstp_sleepwake = pdata->strtstp_sleepwake;

@@ -1879,7 +1967,10 @@ static int _init(struct kgsl_device *device)
	int status = 0;
	switch (device->state) {
	case KGSL_STATE_NAP:
	case KGSL_STATE_DEEP_NAP:
	case KGSL_STATE_SLEEP:
		/* Get the device out of retention */
		kgsl_pwrctrl_retention_clk(device, KGSL_PWRFLAGS_ON);
		/* Force power on to do the stop */
		status = kgsl_pwrctrl_enable(device);
	case KGSL_STATE_ACTIVE:
@@ -1934,6 +2025,8 @@ static int _wake(struct kgsl_device *device)
	case KGSL_STATE_DEEP_NAP:
		pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
					device->pwrctrl.pm_qos_active_latency);
		/* Get the device out of retention */
		kgsl_pwrctrl_retention_clk(device, KGSL_PWRFLAGS_ON);
		/* fall through */
	case KGSL_STATE_NAP:
		/* Turn on the core clocks */
@@ -2058,6 +2151,7 @@ _deep_nap(struct kgsl_device *device)
		 * a deeper low power state. No other transition is permitted
		 */
	case KGSL_STATE_NAP:
		kgsl_pwrctrl_retention_clk(device, KGSL_PWRFLAGS_OFF);
		pm_qos_update_request(&device->pwrctrl.pm_qos_req_dma,
						PM_QOS_DEFAULT_VALUE);
		kgsl_pwrctrl_set_state(device, KGSL_STATE_DEEP_NAP);
@@ -2122,6 +2216,8 @@ _slumber(struct kgsl_device *device)
		}
		del_timer_sync(&device->pwrctrl.deep_nap_timer);
		kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
		/* Get the device out of retention */
		kgsl_pwrctrl_retention_clk(device, KGSL_PWRFLAGS_ON);
		/* make sure power is on to stop the device*/
		status = kgsl_pwrctrl_enable(device);
		device->ftbl->suspend_context(device);
+4 −0
Original line number Diff line number Diff line
@@ -89,6 +89,7 @@ struct kgsl_pwr_constraint {
 * struct kgsl_pwrctrl - Power control settings for a KGSL device
 * @interrupt_num - The interrupt number for the device
 * @grp_clks - Array of clocks structures that we control
 * @dummy_mx_clk - mx clock that is contolled during retention
 * @power_flags - Control flags for power
 * @pwrlevels - List of supported power levels
 * @active_pwrlevel - The currently active power level
@@ -130,11 +131,13 @@ struct kgsl_pwr_constraint {
 * @sysfs_pwr_limit - pointer to the sysfs limits node
 * @deep_nap_timer - Timer struct for entering deep nap
 * @deep_nap_timeout - Timeout for entering deep nap
 * @gx_retention - true if retention voltage is allowed
 */

struct kgsl_pwrctrl {
	int interrupt_num;
	struct clk *grp_clks[KGSL_MAX_CLKS];
	struct clk *dummy_mx_clk;
	unsigned long power_flags;
	unsigned long ctrl_flags;
	struct kgsl_pwrlevel pwrlevels[KGSL_MAX_PWRLEVELS];
@@ -178,6 +181,7 @@ struct kgsl_pwrctrl {
	struct kgsl_pwr_limit *sysfs_pwr_limit;
	struct timer_list deep_nap_timer;
	uint32_t deep_nap_timeout;
	bool gx_retention;
};

int kgsl_pwrctrl_init(struct kgsl_device *device);
+5 −0
Original line number Diff line number Diff line
@@ -221,6 +221,11 @@ DEFINE_EVENT(kgsl_pwr_template, kgsl_rail,
	TP_ARGS(device, on)
);

DEFINE_EVENT(kgsl_pwr_template, kgsl_retention_clk,
	TP_PROTO(struct kgsl_device *device, int on),
	TP_ARGS(device, on)
);

TRACE_EVENT(kgsl_clk,

	TP_PROTO(struct kgsl_device *device, unsigned int on,
Loading