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