diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index 5cccee583291..1cba1b189546 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -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 diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 1255022f2239..8c08415310db 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -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, diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c index c828275033d4..8caf31c97be8 100644 --- a/drivers/gpu/msm/kgsl_gmu.c +++ b/drivers/gpu/msm/kgsl_gmu.c @@ -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) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index c3c93899ba3a..30292f8a6d0b 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -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]); - return ret; - } + if (ret) { + 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); } diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 5e0ececdfd7b..b6761a57f256 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -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; diff --git a/drivers/gpu/msm/kgsl_rgmu.c b/drivers/gpu/msm/kgsl_rgmu.c index 885bfc162f53..af2b6b9483c4 100644 --- a/drivers/gpu/msm/kgsl_rgmu.c +++ b/drivers/gpu/msm/kgsl_rgmu.c @@ -13,6 +13,7 @@ #include "adreno.h" #include "kgsl_device.h" #include "kgsl_rgmu.h" +#include "kgsl_util.h" #define RGMU_CLK_FREQ 200000000 @@ -192,39 +193,16 @@ static int rgmu_enable_clks(struct kgsl_device *device) return 0; } -#define CX_GDSC_TIMEOUT 5000 /* ms */ -static void rgmu_disable_gdsc(struct kgsl_device *device) +static int rgmu_disable_gdsc(struct kgsl_device *device) { struct rgmu_device *rgmu = KGSL_RGMU_DEVICE(device); - int ret = 0; - unsigned long t; - if (IS_ERR_OR_NULL(rgmu->cx_gdsc)) - return; + /* Wait up to 5 seconds for the regulator to go off */ + if (kgsl_regulator_disable_wait(rgmu->cx_gdsc, 5000)) + return 0; - ret = regulator_disable(rgmu->cx_gdsc); - if (ret) { - dev_err(&rgmu->pdev->dev, - "Failed to disable CX gdsc:%d\n", ret); - return; - } - - /* - * After GX GDSC is off, CX GDSC must be off. - * Voting off alone from GPU driver cannot - * guarantee CX GDSC off. Polling with 5sec - * timeout to ensure CX GDSC is off. - */ - t = jiffies + msecs_to_jiffies(CX_GDSC_TIMEOUT); - do { - if (!regulator_is_enabled(rgmu->cx_gdsc)) - return; - usleep_range(10, 100); - - } while (!(time_after(jiffies, t))); - - if (regulator_is_enabled(rgmu->cx_gdsc)) - dev_err(&rgmu->pdev->dev, "RGMU CX gdsc off timeout\n"); + dev_err(&rgmu->pdev->dev, "RGMU CX gdsc off timeout\n"); + return -ETIMEDOUT; } static int rgmu_enable_gdsc(struct rgmu_device *rgmu) diff --git a/drivers/gpu/msm/kgsl_util.c b/drivers/gpu/msm/kgsl_util.c new file mode 100644 index 000000000000..5a0b4a1baa8e --- /dev/null +++ b/drivers/gpu/msm/kgsl_util.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include + +#include "kgsl_util.h" + +bool kgsl_regulator_disable_wait(struct regulator *reg, u32 timeout) +{ + ktime_t tout = ktime_add_us(ktime_get(), timeout * 1000); + + regulator_disable(reg); + + for (;;) { + if (!regulator_is_enabled(reg)) + return true; + + if (ktime_compare(ktime_get(), tout) > 0) + return (!regulator_is_enabled(reg)); + + usleep_range((100 >> 2) + 1, 100); + } +} diff --git a/drivers/gpu/msm/kgsl_util.h b/drivers/gpu/msm/kgsl_util.h new file mode 100644 index 000000000000..403481cbdf26 --- /dev/null +++ b/drivers/gpu/msm/kgsl_util.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#ifndef _KGSL_UTIL_H_ +#define _KGSL_UTIL_H_ + +struct regulator; + +/** + * kgsl_regulator_disable_wait - Disable a regulator and wait for it + * @reg: A &struct regulator handle + * @timeout: Time to wait (in milliseconds) + * + * Disable the regulator and wait @timeout milliseconds for it to enter the + * disabled state. + * + * Return: True if the regulator was disabled or false if it timed out + */ +bool kgsl_regulator_disable_wait(struct regulator *reg, u32 timeout); + +#endif