162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015, 2017-2018, 2022, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitops.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/jiffies.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/ktime.h> 1362306a36Sopenharmony_ci#include <linux/pm_domain.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1662306a36Sopenharmony_ci#include <linux/reset-controller.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include "gdsc.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define PWR_ON_MASK BIT(31) 2162306a36Sopenharmony_ci#define EN_REST_WAIT_MASK GENMASK_ULL(23, 20) 2262306a36Sopenharmony_ci#define EN_FEW_WAIT_MASK GENMASK_ULL(19, 16) 2362306a36Sopenharmony_ci#define CLK_DIS_WAIT_MASK GENMASK_ULL(15, 12) 2462306a36Sopenharmony_ci#define SW_OVERRIDE_MASK BIT(2) 2562306a36Sopenharmony_ci#define HW_CONTROL_MASK BIT(1) 2662306a36Sopenharmony_ci#define SW_COLLAPSE_MASK BIT(0) 2762306a36Sopenharmony_ci#define GMEM_CLAMP_IO_MASK BIT(0) 2862306a36Sopenharmony_ci#define GMEM_RESET_MASK BIT(4) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* CFG_GDSCR */ 3162306a36Sopenharmony_ci#define GDSC_POWER_UP_COMPLETE BIT(16) 3262306a36Sopenharmony_ci#define GDSC_POWER_DOWN_COMPLETE BIT(15) 3362306a36Sopenharmony_ci#define GDSC_RETAIN_FF_ENABLE BIT(11) 3462306a36Sopenharmony_ci#define CFG_GDSCR_OFFSET 0x4 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */ 3762306a36Sopenharmony_ci#define EN_REST_WAIT_VAL 0x2 3862306a36Sopenharmony_ci#define EN_FEW_WAIT_VAL 0x8 3962306a36Sopenharmony_ci#define CLK_DIS_WAIT_VAL 0x2 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Transition delay shifts */ 4262306a36Sopenharmony_ci#define EN_REST_WAIT_SHIFT 20 4362306a36Sopenharmony_ci#define EN_FEW_WAIT_SHIFT 16 4462306a36Sopenharmony_ci#define CLK_DIS_WAIT_SHIFT 12 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define RETAIN_MEM BIT(14) 4762306a36Sopenharmony_ci#define RETAIN_PERIPH BIT(13) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define STATUS_POLL_TIMEOUT_US 1500 5062306a36Sopenharmony_ci#define TIMEOUT_US 500 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define domain_to_gdsc(domain) container_of(domain, struct gdsc, pd) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cienum gdsc_status { 5562306a36Sopenharmony_ci GDSC_OFF, 5662306a36Sopenharmony_ci GDSC_ON 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Returns 1 if GDSC status is status, 0 if not, and < 0 on error */ 6062306a36Sopenharmony_cistatic int gdsc_check_status(struct gdsc *sc, enum gdsc_status status) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci unsigned int reg; 6362306a36Sopenharmony_ci u32 val; 6462306a36Sopenharmony_ci int ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (sc->flags & POLL_CFG_GDSCR) 6762306a36Sopenharmony_ci reg = sc->gdscr + CFG_GDSCR_OFFSET; 6862306a36Sopenharmony_ci else if (sc->gds_hw_ctrl) 6962306a36Sopenharmony_ci reg = sc->gds_hw_ctrl; 7062306a36Sopenharmony_ci else 7162306a36Sopenharmony_ci reg = sc->gdscr; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = regmap_read(sc->regmap, reg, &val); 7462306a36Sopenharmony_ci if (ret) 7562306a36Sopenharmony_ci return ret; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (sc->flags & POLL_CFG_GDSCR) { 7862306a36Sopenharmony_ci switch (status) { 7962306a36Sopenharmony_ci case GDSC_ON: 8062306a36Sopenharmony_ci return !!(val & GDSC_POWER_UP_COMPLETE); 8162306a36Sopenharmony_ci case GDSC_OFF: 8262306a36Sopenharmony_ci return !!(val & GDSC_POWER_DOWN_COMPLETE); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (status) { 8762306a36Sopenharmony_ci case GDSC_ON: 8862306a36Sopenharmony_ci return !!(val & PWR_ON_MASK); 8962306a36Sopenharmony_ci case GDSC_OFF: 9062306a36Sopenharmony_ci return !(val & PWR_ON_MASK); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return -EINVAL; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int gdsc_hwctrl(struct gdsc *sc, bool en) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci u32 val = en ? HW_CONTROL_MASK : 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return regmap_update_bits(sc->regmap, sc->gdscr, HW_CONTROL_MASK, val); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int gdsc_poll_status(struct gdsc *sc, enum gdsc_status status) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci ktime_t start; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci start = ktime_get(); 10862306a36Sopenharmony_ci do { 10962306a36Sopenharmony_ci if (gdsc_check_status(sc, status)) 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci } while (ktime_us_delta(ktime_get(), start) < STATUS_POLL_TIMEOUT_US); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (gdsc_check_status(sc, status)) 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return -ETIMEDOUT; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int gdsc_update_collapse_bit(struct gdsc *sc, bool val) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u32 reg, mask; 12262306a36Sopenharmony_ci int ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (sc->collapse_mask) { 12562306a36Sopenharmony_ci reg = sc->collapse_ctrl; 12662306a36Sopenharmony_ci mask = sc->collapse_mask; 12762306a36Sopenharmony_ci } else { 12862306a36Sopenharmony_ci reg = sc->gdscr; 12962306a36Sopenharmony_ci mask = SW_COLLAPSE_MASK; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ret = regmap_update_bits(sc->regmap, reg, mask, val ? mask : 0); 13362306a36Sopenharmony_ci if (ret) 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status, 14062306a36Sopenharmony_ci bool wait) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (status == GDSC_ON && sc->rsupply) { 14562306a36Sopenharmony_ci ret = regulator_enable(sc->rsupply); 14662306a36Sopenharmony_ci if (ret < 0) 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ret = gdsc_update_collapse_bit(sc, status == GDSC_OFF); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* If disabling votable gdscs, don't poll on status */ 15362306a36Sopenharmony_ci if ((sc->flags & VOTABLE) && status == GDSC_OFF && !wait) { 15462306a36Sopenharmony_ci /* 15562306a36Sopenharmony_ci * Add a short delay here to ensure that an enable 15662306a36Sopenharmony_ci * right after it was disabled does not put it in an 15762306a36Sopenharmony_ci * unknown state 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci udelay(TIMEOUT_US); 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (sc->gds_hw_ctrl) { 16462306a36Sopenharmony_ci /* 16562306a36Sopenharmony_ci * The gds hw controller asserts/de-asserts the status bit soon 16662306a36Sopenharmony_ci * after it receives a power on/off request from a master. 16762306a36Sopenharmony_ci * The controller then takes around 8 xo cycles to start its 16862306a36Sopenharmony_ci * internal state machine and update the status bit. During 16962306a36Sopenharmony_ci * this time, the status bit does not reflect the true status 17062306a36Sopenharmony_ci * of the core. 17162306a36Sopenharmony_ci * Add a delay of 1 us between writing to the SW_COLLAPSE bit 17262306a36Sopenharmony_ci * and polling the status bit. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci udelay(1); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = gdsc_poll_status(sc, status); 17862306a36Sopenharmony_ci WARN(ret, "%s status stuck at 'o%s'", sc->pd.name, status ? "ff" : "n"); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (!ret && status == GDSC_OFF && sc->rsupply) { 18162306a36Sopenharmony_ci ret = regulator_disable(sc->rsupply); 18262306a36Sopenharmony_ci if (ret < 0) 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return ret; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic inline int gdsc_deassert_reset(struct gdsc *sc) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int i; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci for (i = 0; i < sc->reset_count; i++) 19462306a36Sopenharmony_ci sc->rcdev->ops->deassert(sc->rcdev, sc->resets[i]); 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic inline int gdsc_assert_reset(struct gdsc *sc) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int i; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci for (i = 0; i < sc->reset_count; i++) 20362306a36Sopenharmony_ci sc->rcdev->ops->assert(sc->rcdev, sc->resets[i]); 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic inline void gdsc_force_mem_on(struct gdsc *sc) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int i; 21062306a36Sopenharmony_ci u32 mask = RETAIN_MEM; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!(sc->flags & NO_RET_PERIPH)) 21362306a36Sopenharmony_ci mask |= RETAIN_PERIPH; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for (i = 0; i < sc->cxc_count; i++) 21662306a36Sopenharmony_ci regmap_update_bits(sc->regmap, sc->cxcs[i], mask, mask); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic inline void gdsc_clear_mem_on(struct gdsc *sc) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int i; 22262306a36Sopenharmony_ci u32 mask = RETAIN_MEM; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (!(sc->flags & NO_RET_PERIPH)) 22562306a36Sopenharmony_ci mask |= RETAIN_PERIPH; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci for (i = 0; i < sc->cxc_count; i++) 22862306a36Sopenharmony_ci regmap_update_bits(sc->regmap, sc->cxcs[i], mask, 0); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic inline void gdsc_deassert_clamp_io(struct gdsc *sc) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci regmap_update_bits(sc->regmap, sc->clamp_io_ctrl, 23462306a36Sopenharmony_ci GMEM_CLAMP_IO_MASK, 0); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic inline void gdsc_assert_clamp_io(struct gdsc *sc) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci regmap_update_bits(sc->regmap, sc->clamp_io_ctrl, 24062306a36Sopenharmony_ci GMEM_CLAMP_IO_MASK, 1); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic inline void gdsc_assert_reset_aon(struct gdsc *sc) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci regmap_update_bits(sc->regmap, sc->clamp_io_ctrl, 24662306a36Sopenharmony_ci GMEM_RESET_MASK, 1); 24762306a36Sopenharmony_ci udelay(1); 24862306a36Sopenharmony_ci regmap_update_bits(sc->regmap, sc->clamp_io_ctrl, 24962306a36Sopenharmony_ci GMEM_RESET_MASK, 0); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void gdsc_retain_ff_on(struct gdsc *sc) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci u32 mask = GDSC_RETAIN_FF_ENABLE; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci regmap_update_bits(sc->regmap, sc->gdscr, mask, mask); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int gdsc_enable(struct generic_pm_domain *domain) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct gdsc *sc = domain_to_gdsc(domain); 26262306a36Sopenharmony_ci int ret; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (sc->pwrsts == PWRSTS_ON) 26562306a36Sopenharmony_ci return gdsc_deassert_reset(sc); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (sc->flags & SW_RESET) { 26862306a36Sopenharmony_ci gdsc_assert_reset(sc); 26962306a36Sopenharmony_ci udelay(1); 27062306a36Sopenharmony_ci gdsc_deassert_reset(sc); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (sc->flags & CLAMP_IO) { 27462306a36Sopenharmony_ci if (sc->flags & AON_RESET) 27562306a36Sopenharmony_ci gdsc_assert_reset_aon(sc); 27662306a36Sopenharmony_ci gdsc_deassert_clamp_io(sc); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = gdsc_toggle_logic(sc, GDSC_ON, false); 28062306a36Sopenharmony_ci if (ret) 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (sc->pwrsts & PWRSTS_OFF) 28462306a36Sopenharmony_ci gdsc_force_mem_on(sc); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * If clocks to this power domain were already on, they will take an 28862306a36Sopenharmony_ci * additional 4 clock cycles to re-enable after the power domain is 28962306a36Sopenharmony_ci * enabled. Delay to account for this. A delay is also needed to ensure 29062306a36Sopenharmony_ci * clocks are not enabled within 400ns of enabling power to the 29162306a36Sopenharmony_ci * memories. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci udelay(1); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Turn on HW trigger mode if supported */ 29662306a36Sopenharmony_ci if (sc->flags & HW_CTRL) { 29762306a36Sopenharmony_ci ret = gdsc_hwctrl(sc, true); 29862306a36Sopenharmony_ci if (ret) 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * Wait for the GDSC to go through a power down and 30262306a36Sopenharmony_ci * up cycle. In case a firmware ends up polling status 30362306a36Sopenharmony_ci * bits for the gdsc, it might read an 'on' status before 30462306a36Sopenharmony_ci * the GDSC can finish the power cycle. 30562306a36Sopenharmony_ci * We wait 1us before returning to ensure the firmware 30662306a36Sopenharmony_ci * can't immediately poll the status bits. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci udelay(1); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (sc->flags & RETAIN_FF_ENABLE) 31262306a36Sopenharmony_ci gdsc_retain_ff_on(sc); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int gdsc_disable(struct generic_pm_domain *domain) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct gdsc *sc = domain_to_gdsc(domain); 32062306a36Sopenharmony_ci int ret; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (sc->pwrsts == PWRSTS_ON) 32362306a36Sopenharmony_ci return gdsc_assert_reset(sc); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Turn off HW trigger mode if supported */ 32662306a36Sopenharmony_ci if (sc->flags & HW_CTRL) { 32762306a36Sopenharmony_ci ret = gdsc_hwctrl(sc, false); 32862306a36Sopenharmony_ci if (ret < 0) 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * Wait for the GDSC to go through a power down and 33262306a36Sopenharmony_ci * up cycle. In case we end up polling status 33362306a36Sopenharmony_ci * bits for the gdsc before the power cycle is completed 33462306a36Sopenharmony_ci * it might read an 'on' status wrongly. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci udelay(1); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci ret = gdsc_poll_status(sc, GDSC_ON); 33962306a36Sopenharmony_ci if (ret) 34062306a36Sopenharmony_ci return ret; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (sc->pwrsts & PWRSTS_OFF) 34462306a36Sopenharmony_ci gdsc_clear_mem_on(sc); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* 34762306a36Sopenharmony_ci * If the GDSC supports only a Retention state, apart from ON, 34862306a36Sopenharmony_ci * leave it in ON state. 34962306a36Sopenharmony_ci * There is no SW control to transition the GDSC into 35062306a36Sopenharmony_ci * Retention state. This happens in HW when the parent 35162306a36Sopenharmony_ci * domain goes down to a Low power state 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci if (sc->pwrsts == PWRSTS_RET_ON) 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ret = gdsc_toggle_logic(sc, GDSC_OFF, domain->synced_poweroff); 35762306a36Sopenharmony_ci if (ret) 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (sc->flags & CLAMP_IO) 36162306a36Sopenharmony_ci gdsc_assert_clamp_io(sc); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int gdsc_init(struct gdsc *sc) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci u32 mask, val; 36962306a36Sopenharmony_ci int on, ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* 37262306a36Sopenharmony_ci * Disable HW trigger: collapse/restore occur based on registers writes. 37362306a36Sopenharmony_ci * Disable SW override: Use hardware state-machine for sequencing. 37462306a36Sopenharmony_ci * Configure wait time between states. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ci mask = HW_CONTROL_MASK | SW_OVERRIDE_MASK | 37762306a36Sopenharmony_ci EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!sc->en_rest_wait_val) 38062306a36Sopenharmony_ci sc->en_rest_wait_val = EN_REST_WAIT_VAL; 38162306a36Sopenharmony_ci if (!sc->en_few_wait_val) 38262306a36Sopenharmony_ci sc->en_few_wait_val = EN_FEW_WAIT_VAL; 38362306a36Sopenharmony_ci if (!sc->clk_dis_wait_val) 38462306a36Sopenharmony_ci sc->clk_dis_wait_val = CLK_DIS_WAIT_VAL; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci val = sc->en_rest_wait_val << EN_REST_WAIT_SHIFT | 38762306a36Sopenharmony_ci sc->en_few_wait_val << EN_FEW_WAIT_SHIFT | 38862306a36Sopenharmony_ci sc->clk_dis_wait_val << CLK_DIS_WAIT_SHIFT; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ret = regmap_update_bits(sc->regmap, sc->gdscr, mask, val); 39162306a36Sopenharmony_ci if (ret) 39262306a36Sopenharmony_ci return ret; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Force gdsc ON if only ON state is supported */ 39562306a36Sopenharmony_ci if (sc->pwrsts == PWRSTS_ON) { 39662306a36Sopenharmony_ci ret = gdsc_toggle_logic(sc, GDSC_ON, false); 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci on = gdsc_check_status(sc, GDSC_ON); 40262306a36Sopenharmony_ci if (on < 0) 40362306a36Sopenharmony_ci return on; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (on) { 40662306a36Sopenharmony_ci /* The regulator must be on, sync the kernel state */ 40762306a36Sopenharmony_ci if (sc->rsupply) { 40862306a36Sopenharmony_ci ret = regulator_enable(sc->rsupply); 40962306a36Sopenharmony_ci if (ret < 0) 41062306a36Sopenharmony_ci return ret; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * Votable GDSCs can be ON due to Vote from other masters. 41562306a36Sopenharmony_ci * If a Votable GDSC is ON, make sure we have a Vote. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci if (sc->flags & VOTABLE) { 41862306a36Sopenharmony_ci ret = gdsc_update_collapse_bit(sc, false); 41962306a36Sopenharmony_ci if (ret) 42062306a36Sopenharmony_ci goto err_disable_supply; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Turn on HW trigger mode if supported */ 42462306a36Sopenharmony_ci if (sc->flags & HW_CTRL) { 42562306a36Sopenharmony_ci ret = gdsc_hwctrl(sc, true); 42662306a36Sopenharmony_ci if (ret < 0) 42762306a36Sopenharmony_ci goto err_disable_supply; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* 43162306a36Sopenharmony_ci * Make sure the retain bit is set if the GDSC is already on, 43262306a36Sopenharmony_ci * otherwise we end up turning off the GDSC and destroying all 43362306a36Sopenharmony_ci * the register contents that we thought we were saving. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci if (sc->flags & RETAIN_FF_ENABLE) 43662306a36Sopenharmony_ci gdsc_retain_ff_on(sc); 43762306a36Sopenharmony_ci } else if (sc->flags & ALWAYS_ON) { 43862306a36Sopenharmony_ci /* If ALWAYS_ON GDSCs are not ON, turn them ON */ 43962306a36Sopenharmony_ci gdsc_enable(&sc->pd); 44062306a36Sopenharmony_ci on = true; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (on || (sc->pwrsts & PWRSTS_RET)) 44462306a36Sopenharmony_ci gdsc_force_mem_on(sc); 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci gdsc_clear_mem_on(sc); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (sc->flags & ALWAYS_ON) 44962306a36Sopenharmony_ci sc->pd.flags |= GENPD_FLAG_ALWAYS_ON; 45062306a36Sopenharmony_ci if (!sc->pd.power_off) 45162306a36Sopenharmony_ci sc->pd.power_off = gdsc_disable; 45262306a36Sopenharmony_ci if (!sc->pd.power_on) 45362306a36Sopenharmony_ci sc->pd.power_on = gdsc_enable; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ret = pm_genpd_init(&sc->pd, NULL, !on); 45662306a36Sopenharmony_ci if (ret) 45762306a36Sopenharmony_ci goto err_disable_supply; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return 0; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cierr_disable_supply: 46262306a36Sopenharmony_ci if (on && sc->rsupply) 46362306a36Sopenharmony_ci regulator_disable(sc->rsupply); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return ret; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ciint gdsc_register(struct gdsc_desc *desc, 46962306a36Sopenharmony_ci struct reset_controller_dev *rcdev, struct regmap *regmap) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci int i, ret; 47262306a36Sopenharmony_ci struct genpd_onecell_data *data; 47362306a36Sopenharmony_ci struct device *dev = desc->dev; 47462306a36Sopenharmony_ci struct gdsc **scs = desc->scs; 47562306a36Sopenharmony_ci size_t num = desc->num; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 47862306a36Sopenharmony_ci if (!data) 47962306a36Sopenharmony_ci return -ENOMEM; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci data->domains = devm_kcalloc(dev, num, sizeof(*data->domains), 48262306a36Sopenharmony_ci GFP_KERNEL); 48362306a36Sopenharmony_ci if (!data->domains) 48462306a36Sopenharmony_ci return -ENOMEM; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci for (i = 0; i < num; i++) { 48762306a36Sopenharmony_ci if (!scs[i] || !scs[i]->supply) 48862306a36Sopenharmony_ci continue; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci scs[i]->rsupply = devm_regulator_get(dev, scs[i]->supply); 49162306a36Sopenharmony_ci if (IS_ERR(scs[i]->rsupply)) 49262306a36Sopenharmony_ci return PTR_ERR(scs[i]->rsupply); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci data->num_domains = num; 49662306a36Sopenharmony_ci for (i = 0; i < num; i++) { 49762306a36Sopenharmony_ci if (!scs[i]) 49862306a36Sopenharmony_ci continue; 49962306a36Sopenharmony_ci scs[i]->regmap = regmap; 50062306a36Sopenharmony_ci scs[i]->rcdev = rcdev; 50162306a36Sopenharmony_ci ret = gdsc_init(scs[i]); 50262306a36Sopenharmony_ci if (ret) 50362306a36Sopenharmony_ci return ret; 50462306a36Sopenharmony_ci data->domains[i] = &scs[i]->pd; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Add subdomains */ 50862306a36Sopenharmony_ci for (i = 0; i < num; i++) { 50962306a36Sopenharmony_ci if (!scs[i]) 51062306a36Sopenharmony_ci continue; 51162306a36Sopenharmony_ci if (scs[i]->parent) 51262306a36Sopenharmony_ci pm_genpd_add_subdomain(scs[i]->parent, &scs[i]->pd); 51362306a36Sopenharmony_ci else if (!IS_ERR_OR_NULL(dev->pm_domain)) 51462306a36Sopenharmony_ci pm_genpd_add_subdomain(pd_to_genpd(dev->pm_domain), &scs[i]->pd); 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return of_genpd_add_provider_onecell(dev->of_node, data); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_civoid gdsc_unregister(struct gdsc_desc *desc) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci int i; 52362306a36Sopenharmony_ci struct device *dev = desc->dev; 52462306a36Sopenharmony_ci struct gdsc **scs = desc->scs; 52562306a36Sopenharmony_ci size_t num = desc->num; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Remove subdomains */ 52862306a36Sopenharmony_ci for (i = 0; i < num; i++) { 52962306a36Sopenharmony_ci if (!scs[i]) 53062306a36Sopenharmony_ci continue; 53162306a36Sopenharmony_ci if (scs[i]->parent) 53262306a36Sopenharmony_ci pm_genpd_remove_subdomain(scs[i]->parent, &scs[i]->pd); 53362306a36Sopenharmony_ci else if (!IS_ERR_OR_NULL(dev->pm_domain)) 53462306a36Sopenharmony_ci pm_genpd_remove_subdomain(pd_to_genpd(dev->pm_domain), &scs[i]->pd); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci of_genpd_del_provider(dev->of_node); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/* 54062306a36Sopenharmony_ci * On SDM845+ the GPU GX domain is *almost* entirely controlled by the GMU 54162306a36Sopenharmony_ci * running in the CX domain so the CPU doesn't need to know anything about the 54262306a36Sopenharmony_ci * GX domain EXCEPT.... 54362306a36Sopenharmony_ci * 54462306a36Sopenharmony_ci * Hardware constraints dictate that the GX be powered down before the CX. If 54562306a36Sopenharmony_ci * the GMU crashes it could leave the GX on. In order to successfully bring back 54662306a36Sopenharmony_ci * the device the CPU needs to disable the GX headswitch. There being no sane 54762306a36Sopenharmony_ci * way to reach in and touch that register from deep inside the GPU driver we 54862306a36Sopenharmony_ci * need to set up the infrastructure to be able to ensure that the GPU can 54962306a36Sopenharmony_ci * ensure that the GX is off during this super special case. We do this by 55062306a36Sopenharmony_ci * defining a GX gdsc with a dummy enable function and a "default" disable 55162306a36Sopenharmony_ci * function. 55262306a36Sopenharmony_ci * 55362306a36Sopenharmony_ci * This allows us to attach with genpd_dev_pm_attach_by_name() in the GPU 55462306a36Sopenharmony_ci * driver. During power up, nothing will happen from the CPU (and the GMU will 55562306a36Sopenharmony_ci * power up normally but during power down this will ensure that the GX domain 55662306a36Sopenharmony_ci * is *really* off - this gives us a semi standard way of doing what we need. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ciint gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci /* Do nothing but give genpd the impression that we were successful */ 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(gdsc_gx_do_nothing_enable); 564