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

Commit ce6cda63 authored by Jordan Crouse's avatar Jordan Crouse
Browse files

msm: kgsl: Cleanup GPU regulators



We only ever have at most two regulators, and sometimes only one.
We don't need a complex set of lists and loops when only two pointers
will do.  Furthermore, explicitly forcing the regulators into the pwrctrl
struct helps ensure that they get enabled and disabled in the correct
order.

To assist, we add a new helper function called kgsl_regulator_disable_wait
that consolidates code in the driver used to wait for regulators to finish
disabling.

Change-Id: Ic0dedbad3aaa9c5d2f75dd33ee4e368eceb91d34
Signed-off-by: default avatarJordan Crouse <jcrouse@codeaurora.org>
parent c1cc28e6
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -18,7 +18,8 @@ msm_kgsl-y = \
	kgsl_rgmu.o \
	kgsl_sharedmem.o \
	kgsl_snapshot.o \
	kgsl_trace.o
	kgsl_trace.o \
	kgsl_util.o

msm_kgsl-$(CONFIG_COMPAT) += kgsl_compat.o
msm_kgsl-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o
+12 −41
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include "adreno_iommu.h"
#include "adreno_trace.h"
#include "kgsl_trace.h"
#include "kgsl_util.h"

/* Include the master list of GPU cores that are supported */
#include "adreno-gpulist.h"
@@ -1779,21 +1780,17 @@ static int adreno_init(struct kgsl_device *device)

static bool regulators_left_on(struct kgsl_device *device)
{
	int i;
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;

	if (gmu_core_gpmu_isenabled(device))
		return false;

	for (i = 0; i < KGSL_MAX_REGULATORS; i++) {
		struct kgsl_regulator *regulator =
			&device->pwrctrl.regulators[i];

		if (IS_ERR_OR_NULL(regulator->reg))
			break;

		if (regulator_is_enabled(regulator->reg))
	if (!IS_ERR(pwr->cx_gdsc))
		if (regulator_is_enabled(pwr->cx_gdsc))
			return true;
	}

	if (!IS_ERR(pwr->gx_gdsc))
		return regulator_is_enabled(pwr->gx_gdsc);

	return false;
}
@@ -3709,41 +3706,15 @@ static void adreno_clk_set_options(struct kgsl_device *device, const char *name,
			ADRENO_DEVICE(device), name, clk, on);
}

static void _regulator_disable(struct device *dev,
		struct kgsl_regulator *regulator, unsigned int timeout)
{
	unsigned long wait_time;

	if (IS_ERR_OR_NULL(regulator->reg))
		return;

	regulator_disable(regulator->reg);

	wait_time = jiffies + msecs_to_jiffies(timeout);

	/* Poll for regulator status to ensure it's OFF */
	while (!time_after(jiffies, wait_time)) {
		if (!regulator_is_enabled(regulator->reg))
			return;
		usleep_range(10, 100);
	}

	if (!regulator_is_enabled(regulator->reg))
		return;

	dev_err(dev, "regulator '%s' disable timed out\n", regulator->name);
}

static void adreno_regulator_disable_poll(struct kgsl_device *device)
{
	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	int i;
	unsigned int timeout =
		ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_IOMMU_SYNC) ? 200 : 5000;

	for (i = KGSL_MAX_REGULATORS - 1; i >= 0; i--)
		_regulator_disable(device->dev, &pwr->regulators[i], timeout);
	if (!kgsl_regulator_disable_wait(pwr->gx_gdsc, 200))
		dev_err(device->dev, "Regulator vdd is stuck on\n");

	if (!kgsl_regulator_disable_wait(pwr->cx_gdsc, 200))
		dev_err(device->dev, "Regulator vddcx is stuck on\n");
}

static void adreno_gpu_model(struct kgsl_device *device, char *str,
+4 −26
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include "adreno.h"
#include "kgsl_device.h"
#include "kgsl_gmu.h"
#include "kgsl_util.h"

struct gmu_iommu_context {
	const char *name;
@@ -1415,41 +1416,18 @@ static int gmu_enable_gdsc(struct gmu_device *gmu)
	return ret;
}

#define CX_GDSC_TIMEOUT	5000	/* ms */
static int gmu_disable_gdsc(struct gmu_device *gmu)
static void gmu_disable_gdsc(struct gmu_device *gmu)
{
	int ret;
	unsigned long t;

	if (IS_ERR_OR_NULL(gmu->cx_gdsc))
		return 0;

	ret = regulator_disable(gmu->cx_gdsc);
	if (ret) {
		dev_err(&gmu->pdev->dev,
			"Failed to disable GMU CX gdsc, error %d\n", ret);
		return ret;
	}

	/*
	 * After GX GDSC is off, CX GDSC must be off
	 * Voting off alone from GPU driver cannot
	 * Guarantee CX GDSC off. Polling with 5s
	 * timeout to ensure
	 */
	t = jiffies + msecs_to_jiffies(CX_GDSC_TIMEOUT);
	do {
		if (!regulator_is_enabled(gmu->cx_gdsc))
			return 0;
		usleep_range(10, 100);

	} while (!(time_after(jiffies, t)));

	if (!regulator_is_enabled(gmu->cx_gdsc))
		return 0;
	if (kgsl_regulator_disable_wait(gmu->cx_gdsc, 5000))
		return;

	dev_err(&gmu->pdev->dev, "GMU CX gdsc off timeout\n");
	return -ETIMEDOUT;
}

static int gmu_suspend(struct kgsl_device *device)
+21 −109
Original line number Diff line number Diff line
@@ -1393,43 +1393,38 @@ static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state)
	}
}

static int _regulator_enable(struct kgsl_device *device,
		struct kgsl_regulator *regulator)
static int enable_regulator(struct device *dev, struct regulator *regulator,
		const char *name)
{
	int ret;

	if (IS_ERR_OR_NULL(regulator->reg))
	if (IS_ERR(regulator))
		return 0;

	ret = regulator_enable(regulator->reg);
	ret = regulator_enable(regulator);
	if (ret)
		dev_err(device->dev,
			     "Failed to enable regulator '%s': %d\n",
			     regulator->name, ret);
		dev_err(dev, "Unable to enable regulator %s: %d\n", name, ret);
	return ret;
}

static void _regulator_disable(struct kgsl_regulator *regulator)
static int enable_regulators(struct kgsl_device *device)
{
	if (!IS_ERR_OR_NULL(regulator->reg))
		regulator_disable(regulator->reg);
}
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	int ret;

static int _enable_regulators(struct kgsl_device *device,
		struct kgsl_pwrctrl *pwr)
{
	int i;
	if (test_and_set_bit(KGSL_PWRFLAGS_POWER_ON, &pwr->power_flags))
		return 0;

	for (i = 0; i < KGSL_MAX_REGULATORS; i++) {
		int ret = _regulator_enable(device, &pwr->regulators[i]);
	ret = enable_regulator(device->dev, pwr->cx_gdsc, "vddcx");
	if (!ret)
		ret = enable_regulator(device->dev, pwr->gx_gdsc, "vdd");

	if (ret) {
			for (i = i - 1; i >= 0; i--)
				_regulator_disable(&pwr->regulators[i]);
		clear_bit(KGSL_PWRFLAGS_POWER_ON, &pwr->power_flags);
		return ret;
	}
	}

	trace_kgsl_rail(device, KGSL_PWRFLAGS_POWER_ON);
	return 0;
}

@@ -1454,18 +1449,8 @@ static int kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state)
			trace_kgsl_rail(device, state);
			device->ftbl->regulator_disable_poll(device);
		}
	} else if (state == KGSL_PWRFLAGS_ON) {
		if (!test_and_set_bit(KGSL_PWRFLAGS_POWER_ON,
			&pwr->power_flags)) {
			status = _enable_regulators(device, pwr);

			if (status)
				clear_bit(KGSL_PWRFLAGS_POWER_ON,
					&pwr->power_flags);
			else
				trace_kgsl_rail(device, state);
		}
	}
	} else if (state == KGSL_PWRFLAGS_ON)
		status = enable_regulators(device);

	return status;
}
@@ -1503,66 +1488,6 @@ static void kgsl_pwrctrl_vbif_init(struct kgsl_device *device)
}
#endif

static int _get_regulator(struct kgsl_device *device,
		struct kgsl_regulator *regulator, const char *str)
{
	regulator->reg = devm_regulator_get(&device->pdev->dev, str);
	if (IS_ERR(regulator->reg)) {
		int ret = PTR_ERR(regulator->reg);

		dev_err(&device->pdev->dev,
			"Couldn't get regulator: %s (%d)\n", str, ret);
		return ret;
	}

	strlcpy(regulator->name, str, sizeof(regulator->name));
	return 0;
}

static int get_legacy_regulators(struct kgsl_device *device)
{
	struct device *dev = &device->pdev->dev;
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	int ret;

	ret = _get_regulator(device, &pwr->regulators[0], "vdd");

	/* Use vddcx only on targets that have it. */
	if (ret == 0 && of_find_property(dev->of_node, "vddcx-supply", NULL))
		ret = _get_regulator(device, &pwr->regulators[1], "vddcx");

	return ret;
}

static int get_regulators(struct kgsl_device *device)
{
	struct device *dev = &device->pdev->dev;
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
	int index = 0;
	const char *name;
	struct property *prop;

	if (!of_find_property(dev->of_node, "regulator-names", NULL))
		return get_legacy_regulators(device);

	of_property_for_each_string(dev->of_node,
		"regulator-names", prop, name) {
		int ret;

		if (index == KGSL_MAX_REGULATORS) {
			dev_err(dev, "Too many regulators defined\n");
			return -ENOMEM;
		}

		ret = _get_regulator(device, &pwr->regulators[index], name);
		if (ret)
			return ret;
		index++;
	}

	return 0;
}

static int _get_clocks(struct kgsl_device *device)
{
	struct device *dev = &device->pdev->dev;
@@ -1664,14 +1589,6 @@ static int kgsl_pwrctrl_clk_set_rate(struct clk *grp_clk, unsigned int freq,
	return ret;
}

static inline void _close_regulators(struct kgsl_pwrctrl *pwr)
{
	int i;

	for (i = 0; i < KGSL_MAX_REGULATORS; i++)
		pwr->regulators[i].reg = NULL;
}

static inline void _close_clks(struct kgsl_device *device)
{
	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
@@ -1809,9 +1726,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)

	_isense_clk_set_rate(pwr, pwr->num_pwrlevels - 1);

	result = get_regulators(device);
	if (result)
		goto error_cleanup_regulators;
	pwr->cx_gdsc = devm_regulator_get(&device->pdev->dev, "vddcx");
	pwr->gx_gdsc = devm_regulator_get(&device->pdev->dev, "vdd");

	pwr->power_flags = 0;

@@ -1869,8 +1785,6 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)

error_disable_pm:
	pm_runtime_disable(&pdev->dev);
error_cleanup_regulators:
	_close_regulators(pwr);
error_cleanup_clks:
	_close_clks(device);
	kfree(levels);
@@ -1889,8 +1803,6 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)

	pm_runtime_disable(&device->pdev->dev);

	_close_regulators(pwr);

	_close_clks(device);
}

+4 −8
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@
#define KGSL_PWR_ON	0xFFFF

#define KGSL_MAX_CLKS 17
#define KGSL_MAX_REGULATORS 2

#define KGSL_MAX_PWRLEVELS 10

@@ -70,11 +69,6 @@ struct kgsl_pwrlevel {
	unsigned int acd_level;
};

struct kgsl_regulator {
	struct regulator *reg;
	char name[8];
};

/**
 * struct kgsl_pwrctrl - Power control settings for a KGSL device
 * @interrupt_num - The interrupt number for the device
@@ -93,7 +87,6 @@ struct kgsl_regulator {
 * @throttle_mask - LM throttle mask
 * @interval_timeout - timeout in jiffies to be idle before a power event
 * @clock_times - Each GPU frequency's accumulated active time in us
 * @regulators - array of pointers to kgsl_regulator structs
 * @pcl - bus scale identifier
 * @irq_name - resource name for the IRQ
 * @clk_stats - structure of clock statistics
@@ -115,6 +108,10 @@ struct kgsl_pwrctrl {
	int interrupt_num;
	struct clk *grp_clks[KGSL_MAX_CLKS];
	struct clk *gpu_bimc_int_clk;
	/** @cx_gdsc: Pointer to the CX domain regulator if applicable */
	struct regulator *cx_gdsc;
	/** @gx_gdsc: Pointer to the GX domain regulator if applicable */
	struct regulator *gx_gdsc;
	int isense_clk_indx;
	int isense_clk_on_level;
	unsigned long power_flags;
@@ -133,7 +130,6 @@ struct kgsl_pwrctrl {
	unsigned int throttle_mask;
	unsigned long interval_timeout;
	u64 clock_times[KGSL_MAX_PWRLEVELS];
	struct kgsl_regulator regulators[KGSL_MAX_REGULATORS];
	uint32_t pcl;
	const char *irq_name;
	struct kgsl_clk_stats clk_stats;
Loading