18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2015, 2017-2018, 2022, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/bitops.h>
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/export.h>
108c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/ktime.h>
138c2ecf20Sopenharmony_ci#include <linux/pm_domain.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
168c2ecf20Sopenharmony_ci#include <linux/reset-controller.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include "gdsc.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define PWR_ON_MASK		BIT(31)
218c2ecf20Sopenharmony_ci#define EN_REST_WAIT_MASK	GENMASK_ULL(23, 20)
228c2ecf20Sopenharmony_ci#define EN_FEW_WAIT_MASK	GENMASK_ULL(19, 16)
238c2ecf20Sopenharmony_ci#define CLK_DIS_WAIT_MASK	GENMASK_ULL(15, 12)
248c2ecf20Sopenharmony_ci#define SW_OVERRIDE_MASK	BIT(2)
258c2ecf20Sopenharmony_ci#define HW_CONTROL_MASK		BIT(1)
268c2ecf20Sopenharmony_ci#define SW_COLLAPSE_MASK	BIT(0)
278c2ecf20Sopenharmony_ci#define GMEM_CLAMP_IO_MASK	BIT(0)
288c2ecf20Sopenharmony_ci#define GMEM_RESET_MASK		BIT(4)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* CFG_GDSCR */
318c2ecf20Sopenharmony_ci#define GDSC_POWER_UP_COMPLETE		BIT(16)
328c2ecf20Sopenharmony_ci#define GDSC_POWER_DOWN_COMPLETE	BIT(15)
338c2ecf20Sopenharmony_ci#define GDSC_RETAIN_FF_ENABLE		BIT(11)
348c2ecf20Sopenharmony_ci#define CFG_GDSCR_OFFSET		0x4
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
378c2ecf20Sopenharmony_ci#define EN_REST_WAIT_VAL	0x2
388c2ecf20Sopenharmony_ci#define EN_FEW_WAIT_VAL		0x8
398c2ecf20Sopenharmony_ci#define CLK_DIS_WAIT_VAL	0x2
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* Transition delay shifts */
428c2ecf20Sopenharmony_ci#define EN_REST_WAIT_SHIFT	20
438c2ecf20Sopenharmony_ci#define EN_FEW_WAIT_SHIFT	16
448c2ecf20Sopenharmony_ci#define CLK_DIS_WAIT_SHIFT	12
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define RETAIN_MEM		BIT(14)
478c2ecf20Sopenharmony_ci#define RETAIN_PERIPH		BIT(13)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define TIMEOUT_US		500
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define domain_to_gdsc(domain) container_of(domain, struct gdsc, pd)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cienum gdsc_status {
548c2ecf20Sopenharmony_ci	GDSC_OFF,
558c2ecf20Sopenharmony_ci	GDSC_ON
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Returns 1 if GDSC status is status, 0 if not, and < 0 on error */
598c2ecf20Sopenharmony_cistatic int gdsc_check_status(struct gdsc *sc, enum gdsc_status status)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	unsigned int reg;
628c2ecf20Sopenharmony_ci	u32 val;
638c2ecf20Sopenharmony_ci	int ret;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (sc->flags & POLL_CFG_GDSCR)
668c2ecf20Sopenharmony_ci		reg = sc->gdscr + CFG_GDSCR_OFFSET;
678c2ecf20Sopenharmony_ci	else if (sc->gds_hw_ctrl)
688c2ecf20Sopenharmony_ci		reg = sc->gds_hw_ctrl;
698c2ecf20Sopenharmony_ci	else
708c2ecf20Sopenharmony_ci		reg = sc->gdscr;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	ret = regmap_read(sc->regmap, reg, &val);
738c2ecf20Sopenharmony_ci	if (ret)
748c2ecf20Sopenharmony_ci		return ret;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (sc->flags & POLL_CFG_GDSCR) {
778c2ecf20Sopenharmony_ci		switch (status) {
788c2ecf20Sopenharmony_ci		case GDSC_ON:
798c2ecf20Sopenharmony_ci			return !!(val & GDSC_POWER_UP_COMPLETE);
808c2ecf20Sopenharmony_ci		case GDSC_OFF:
818c2ecf20Sopenharmony_ci			return !!(val & GDSC_POWER_DOWN_COMPLETE);
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	switch (status) {
868c2ecf20Sopenharmony_ci	case GDSC_ON:
878c2ecf20Sopenharmony_ci		return !!(val & PWR_ON_MASK);
888c2ecf20Sopenharmony_ci	case GDSC_OFF:
898c2ecf20Sopenharmony_ci		return !(val & PWR_ON_MASK);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	return -EINVAL;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic int gdsc_hwctrl(struct gdsc *sc, bool en)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	u32 val = en ? HW_CONTROL_MASK : 0;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return regmap_update_bits(sc->regmap, sc->gdscr, HW_CONTROL_MASK, val);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int gdsc_poll_status(struct gdsc *sc, enum gdsc_status status)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	ktime_t start;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	start = ktime_get();
1078c2ecf20Sopenharmony_ci	do {
1088c2ecf20Sopenharmony_ci		if (gdsc_check_status(sc, status))
1098c2ecf20Sopenharmony_ci			return 0;
1108c2ecf20Sopenharmony_ci	} while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (gdsc_check_status(sc, status))
1138c2ecf20Sopenharmony_ci		return 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	int ret;
1218c2ecf20Sopenharmony_ci	u32 val = (status == GDSC_ON) ? 0 : SW_COLLAPSE_MASK;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (status == GDSC_ON && sc->rsupply) {
1248c2ecf20Sopenharmony_ci		ret = regulator_enable(sc->rsupply);
1258c2ecf20Sopenharmony_ci		if (ret < 0)
1268c2ecf20Sopenharmony_ci			return ret;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val);
1308c2ecf20Sopenharmony_ci	if (ret)
1318c2ecf20Sopenharmony_ci		return ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* If disabling votable gdscs, don't poll on status */
1348c2ecf20Sopenharmony_ci	if ((sc->flags & VOTABLE) && status == GDSC_OFF) {
1358c2ecf20Sopenharmony_ci		/*
1368c2ecf20Sopenharmony_ci		 * Add a short delay here to ensure that an enable
1378c2ecf20Sopenharmony_ci		 * right after it was disabled does not put it in an
1388c2ecf20Sopenharmony_ci		 * unknown state
1398c2ecf20Sopenharmony_ci		 */
1408c2ecf20Sopenharmony_ci		udelay(TIMEOUT_US);
1418c2ecf20Sopenharmony_ci		return 0;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (sc->gds_hw_ctrl) {
1458c2ecf20Sopenharmony_ci		/*
1468c2ecf20Sopenharmony_ci		 * The gds hw controller asserts/de-asserts the status bit soon
1478c2ecf20Sopenharmony_ci		 * after it receives a power on/off request from a master.
1488c2ecf20Sopenharmony_ci		 * The controller then takes around 8 xo cycles to start its
1498c2ecf20Sopenharmony_ci		 * internal state machine and update the status bit. During
1508c2ecf20Sopenharmony_ci		 * this time, the status bit does not reflect the true status
1518c2ecf20Sopenharmony_ci		 * of the core.
1528c2ecf20Sopenharmony_ci		 * Add a delay of 1 us between writing to the SW_COLLAPSE bit
1538c2ecf20Sopenharmony_ci		 * and polling the status bit.
1548c2ecf20Sopenharmony_ci		 */
1558c2ecf20Sopenharmony_ci		udelay(1);
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	ret = gdsc_poll_status(sc, status);
1598c2ecf20Sopenharmony_ci	WARN(ret, "%s status stuck at 'o%s'", sc->pd.name, status ? "ff" : "n");
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!ret && status == GDSC_OFF && sc->rsupply) {
1628c2ecf20Sopenharmony_ci		ret = regulator_disable(sc->rsupply);
1638c2ecf20Sopenharmony_ci		if (ret < 0)
1648c2ecf20Sopenharmony_ci			return ret;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return ret;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic inline int gdsc_deassert_reset(struct gdsc *sc)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	int i;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	for (i = 0; i < sc->reset_count; i++)
1758c2ecf20Sopenharmony_ci		sc->rcdev->ops->deassert(sc->rcdev, sc->resets[i]);
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic inline int gdsc_assert_reset(struct gdsc *sc)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	int i;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	for (i = 0; i < sc->reset_count; i++)
1848c2ecf20Sopenharmony_ci		sc->rcdev->ops->assert(sc->rcdev, sc->resets[i]);
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic inline void gdsc_force_mem_on(struct gdsc *sc)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int i;
1918c2ecf20Sopenharmony_ci	u32 mask = RETAIN_MEM;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (!(sc->flags & NO_RET_PERIPH))
1948c2ecf20Sopenharmony_ci		mask |= RETAIN_PERIPH;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	for (i = 0; i < sc->cxc_count; i++)
1978c2ecf20Sopenharmony_ci		regmap_update_bits(sc->regmap, sc->cxcs[i], mask, mask);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic inline void gdsc_clear_mem_on(struct gdsc *sc)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	int i;
2038c2ecf20Sopenharmony_ci	u32 mask = RETAIN_MEM;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (!(sc->flags & NO_RET_PERIPH))
2068c2ecf20Sopenharmony_ci		mask |= RETAIN_PERIPH;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	for (i = 0; i < sc->cxc_count; i++)
2098c2ecf20Sopenharmony_ci		regmap_update_bits(sc->regmap, sc->cxcs[i], mask, 0);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic inline void gdsc_deassert_clamp_io(struct gdsc *sc)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	regmap_update_bits(sc->regmap, sc->clamp_io_ctrl,
2158c2ecf20Sopenharmony_ci			   GMEM_CLAMP_IO_MASK, 0);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic inline void gdsc_assert_clamp_io(struct gdsc *sc)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	regmap_update_bits(sc->regmap, sc->clamp_io_ctrl,
2218c2ecf20Sopenharmony_ci			   GMEM_CLAMP_IO_MASK, 1);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic inline void gdsc_assert_reset_aon(struct gdsc *sc)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	regmap_update_bits(sc->regmap, sc->clamp_io_ctrl,
2278c2ecf20Sopenharmony_ci			   GMEM_RESET_MASK, 1);
2288c2ecf20Sopenharmony_ci	udelay(1);
2298c2ecf20Sopenharmony_ci	regmap_update_bits(sc->regmap, sc->clamp_io_ctrl,
2308c2ecf20Sopenharmony_ci			   GMEM_RESET_MASK, 0);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void gdsc_retain_ff_on(struct gdsc *sc)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	u32 mask = GDSC_RETAIN_FF_ENABLE;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	regmap_update_bits(sc->regmap, sc->gdscr, mask, mask);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int gdsc_enable(struct generic_pm_domain *domain)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct gdsc *sc = domain_to_gdsc(domain);
2438c2ecf20Sopenharmony_ci	int ret;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (sc->pwrsts == PWRSTS_ON)
2468c2ecf20Sopenharmony_ci		return gdsc_deassert_reset(sc);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (sc->flags & SW_RESET) {
2498c2ecf20Sopenharmony_ci		gdsc_assert_reset(sc);
2508c2ecf20Sopenharmony_ci		udelay(1);
2518c2ecf20Sopenharmony_ci		gdsc_deassert_reset(sc);
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (sc->flags & CLAMP_IO) {
2558c2ecf20Sopenharmony_ci		if (sc->flags & AON_RESET)
2568c2ecf20Sopenharmony_ci			gdsc_assert_reset_aon(sc);
2578c2ecf20Sopenharmony_ci		gdsc_deassert_clamp_io(sc);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	ret = gdsc_toggle_logic(sc, GDSC_ON);
2618c2ecf20Sopenharmony_ci	if (ret)
2628c2ecf20Sopenharmony_ci		return ret;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (sc->pwrsts & PWRSTS_OFF)
2658c2ecf20Sopenharmony_ci		gdsc_force_mem_on(sc);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/*
2688c2ecf20Sopenharmony_ci	 * If clocks to this power domain were already on, they will take an
2698c2ecf20Sopenharmony_ci	 * additional 4 clock cycles to re-enable after the power domain is
2708c2ecf20Sopenharmony_ci	 * enabled. Delay to account for this. A delay is also needed to ensure
2718c2ecf20Sopenharmony_ci	 * clocks are not enabled within 400ns of enabling power to the
2728c2ecf20Sopenharmony_ci	 * memories.
2738c2ecf20Sopenharmony_ci	 */
2748c2ecf20Sopenharmony_ci	udelay(1);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* Turn on HW trigger mode if supported */
2778c2ecf20Sopenharmony_ci	if (sc->flags & HW_CTRL) {
2788c2ecf20Sopenharmony_ci		ret = gdsc_hwctrl(sc, true);
2798c2ecf20Sopenharmony_ci		if (ret)
2808c2ecf20Sopenharmony_ci			return ret;
2818c2ecf20Sopenharmony_ci		/*
2828c2ecf20Sopenharmony_ci		 * Wait for the GDSC to go through a power down and
2838c2ecf20Sopenharmony_ci		 * up cycle.  In case a firmware ends up polling status
2848c2ecf20Sopenharmony_ci		 * bits for the gdsc, it might read an 'on' status before
2858c2ecf20Sopenharmony_ci		 * the GDSC can finish the power cycle.
2868c2ecf20Sopenharmony_ci		 * We wait 1us before returning to ensure the firmware
2878c2ecf20Sopenharmony_ci		 * can't immediately poll the status bits.
2888c2ecf20Sopenharmony_ci		 */
2898c2ecf20Sopenharmony_ci		udelay(1);
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (sc->flags & RETAIN_FF_ENABLE)
2938c2ecf20Sopenharmony_ci		gdsc_retain_ff_on(sc);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int gdsc_disable(struct generic_pm_domain *domain)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct gdsc *sc = domain_to_gdsc(domain);
3018c2ecf20Sopenharmony_ci	int ret;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (sc->pwrsts == PWRSTS_ON)
3048c2ecf20Sopenharmony_ci		return gdsc_assert_reset(sc);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Turn off HW trigger mode if supported */
3078c2ecf20Sopenharmony_ci	if (sc->flags & HW_CTRL) {
3088c2ecf20Sopenharmony_ci		ret = gdsc_hwctrl(sc, false);
3098c2ecf20Sopenharmony_ci		if (ret < 0)
3108c2ecf20Sopenharmony_ci			return ret;
3118c2ecf20Sopenharmony_ci		/*
3128c2ecf20Sopenharmony_ci		 * Wait for the GDSC to go through a power down and
3138c2ecf20Sopenharmony_ci		 * up cycle.  In case we end up polling status
3148c2ecf20Sopenharmony_ci		 * bits for the gdsc before the power cycle is completed
3158c2ecf20Sopenharmony_ci		 * it might read an 'on' status wrongly.
3168c2ecf20Sopenharmony_ci		 */
3178c2ecf20Sopenharmony_ci		udelay(1);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		ret = gdsc_poll_status(sc, GDSC_ON);
3208c2ecf20Sopenharmony_ci		if (ret)
3218c2ecf20Sopenharmony_ci			return ret;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (sc->pwrsts & PWRSTS_OFF)
3258c2ecf20Sopenharmony_ci		gdsc_clear_mem_on(sc);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	ret = gdsc_toggle_logic(sc, GDSC_OFF);
3288c2ecf20Sopenharmony_ci	if (ret)
3298c2ecf20Sopenharmony_ci		return ret;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (sc->flags & CLAMP_IO)
3328c2ecf20Sopenharmony_ci		gdsc_assert_clamp_io(sc);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int gdsc_init(struct gdsc *sc)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	u32 mask, val;
3408c2ecf20Sopenharmony_ci	int on, ret;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/*
3438c2ecf20Sopenharmony_ci	 * Disable HW trigger: collapse/restore occur based on registers writes.
3448c2ecf20Sopenharmony_ci	 * Disable SW override: Use hardware state-machine for sequencing.
3458c2ecf20Sopenharmony_ci	 * Configure wait time between states.
3468c2ecf20Sopenharmony_ci	 */
3478c2ecf20Sopenharmony_ci	mask = HW_CONTROL_MASK | SW_OVERRIDE_MASK |
3488c2ecf20Sopenharmony_ci	       EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (!sc->en_rest_wait_val)
3518c2ecf20Sopenharmony_ci		sc->en_rest_wait_val = EN_REST_WAIT_VAL;
3528c2ecf20Sopenharmony_ci	if (!sc->en_few_wait_val)
3538c2ecf20Sopenharmony_ci		sc->en_few_wait_val = EN_FEW_WAIT_VAL;
3548c2ecf20Sopenharmony_ci	if (!sc->clk_dis_wait_val)
3558c2ecf20Sopenharmony_ci		sc->clk_dis_wait_val = CLK_DIS_WAIT_VAL;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	val = sc->en_rest_wait_val << EN_REST_WAIT_SHIFT |
3588c2ecf20Sopenharmony_ci		sc->en_few_wait_val << EN_FEW_WAIT_SHIFT |
3598c2ecf20Sopenharmony_ci		sc->clk_dis_wait_val << CLK_DIS_WAIT_SHIFT;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	ret = regmap_update_bits(sc->regmap, sc->gdscr, mask, val);
3628c2ecf20Sopenharmony_ci	if (ret)
3638c2ecf20Sopenharmony_ci		return ret;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* Force gdsc ON if only ON state is supported */
3668c2ecf20Sopenharmony_ci	if (sc->pwrsts == PWRSTS_ON) {
3678c2ecf20Sopenharmony_ci		ret = gdsc_toggle_logic(sc, GDSC_ON);
3688c2ecf20Sopenharmony_ci		if (ret)
3698c2ecf20Sopenharmony_ci			return ret;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	on = gdsc_check_status(sc, GDSC_ON);
3738c2ecf20Sopenharmony_ci	if (on < 0)
3748c2ecf20Sopenharmony_ci		return on;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (on) {
3778c2ecf20Sopenharmony_ci		/* The regulator must be on, sync the kernel state */
3788c2ecf20Sopenharmony_ci		if (sc->rsupply) {
3798c2ecf20Sopenharmony_ci			ret = regulator_enable(sc->rsupply);
3808c2ecf20Sopenharmony_ci			if (ret < 0)
3818c2ecf20Sopenharmony_ci				return ret;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		/*
3858c2ecf20Sopenharmony_ci		 * Votable GDSCs can be ON due to Vote from other masters.
3868c2ecf20Sopenharmony_ci		 * If a Votable GDSC is ON, make sure we have a Vote.
3878c2ecf20Sopenharmony_ci		 */
3888c2ecf20Sopenharmony_ci		if (sc->flags & VOTABLE) {
3898c2ecf20Sopenharmony_ci			ret = regmap_update_bits(sc->regmap, sc->gdscr,
3908c2ecf20Sopenharmony_ci						 SW_COLLAPSE_MASK, val);
3918c2ecf20Sopenharmony_ci			if (ret)
3928c2ecf20Sopenharmony_ci				return ret;
3938c2ecf20Sopenharmony_ci		}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		/* Turn on HW trigger mode if supported */
3968c2ecf20Sopenharmony_ci		if (sc->flags & HW_CTRL) {
3978c2ecf20Sopenharmony_ci			ret = gdsc_hwctrl(sc, true);
3988c2ecf20Sopenharmony_ci			if (ret < 0)
3998c2ecf20Sopenharmony_ci				return ret;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		/*
4038c2ecf20Sopenharmony_ci		 * Make sure the retain bit is set if the GDSC is already on,
4048c2ecf20Sopenharmony_ci		 * otherwise we end up turning off the GDSC and destroying all
4058c2ecf20Sopenharmony_ci		 * the register contents that we thought we were saving.
4068c2ecf20Sopenharmony_ci		 */
4078c2ecf20Sopenharmony_ci		if (sc->flags & RETAIN_FF_ENABLE)
4088c2ecf20Sopenharmony_ci			gdsc_retain_ff_on(sc);
4098c2ecf20Sopenharmony_ci	} else if (sc->flags & ALWAYS_ON) {
4108c2ecf20Sopenharmony_ci		/* If ALWAYS_ON GDSCs are not ON, turn them ON */
4118c2ecf20Sopenharmony_ci		gdsc_enable(&sc->pd);
4128c2ecf20Sopenharmony_ci		on = true;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (on || (sc->pwrsts & PWRSTS_RET))
4168c2ecf20Sopenharmony_ci		gdsc_force_mem_on(sc);
4178c2ecf20Sopenharmony_ci	else
4188c2ecf20Sopenharmony_ci		gdsc_clear_mem_on(sc);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (sc->flags & ALWAYS_ON)
4218c2ecf20Sopenharmony_ci		sc->pd.flags |= GENPD_FLAG_ALWAYS_ON;
4228c2ecf20Sopenharmony_ci	if (!sc->pd.power_off)
4238c2ecf20Sopenharmony_ci		sc->pd.power_off = gdsc_disable;
4248c2ecf20Sopenharmony_ci	if (!sc->pd.power_on)
4258c2ecf20Sopenharmony_ci		sc->pd.power_on = gdsc_enable;
4268c2ecf20Sopenharmony_ci	pm_genpd_init(&sc->pd, NULL, !on);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ciint gdsc_register(struct gdsc_desc *desc,
4328c2ecf20Sopenharmony_ci		  struct reset_controller_dev *rcdev, struct regmap *regmap)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	int i, ret;
4358c2ecf20Sopenharmony_ci	struct genpd_onecell_data *data;
4368c2ecf20Sopenharmony_ci	struct device *dev = desc->dev;
4378c2ecf20Sopenharmony_ci	struct gdsc **scs = desc->scs;
4388c2ecf20Sopenharmony_ci	size_t num = desc->num;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
4418c2ecf20Sopenharmony_ci	if (!data)
4428c2ecf20Sopenharmony_ci		return -ENOMEM;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	data->domains = devm_kcalloc(dev, num, sizeof(*data->domains),
4458c2ecf20Sopenharmony_ci				     GFP_KERNEL);
4468c2ecf20Sopenharmony_ci	if (!data->domains)
4478c2ecf20Sopenharmony_ci		return -ENOMEM;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
4508c2ecf20Sopenharmony_ci		if (!scs[i] || !scs[i]->supply)
4518c2ecf20Sopenharmony_ci			continue;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		scs[i]->rsupply = devm_regulator_get(dev, scs[i]->supply);
4548c2ecf20Sopenharmony_ci		if (IS_ERR(scs[i]->rsupply))
4558c2ecf20Sopenharmony_ci			return PTR_ERR(scs[i]->rsupply);
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	data->num_domains = num;
4598c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
4608c2ecf20Sopenharmony_ci		if (!scs[i])
4618c2ecf20Sopenharmony_ci			continue;
4628c2ecf20Sopenharmony_ci		scs[i]->regmap = regmap;
4638c2ecf20Sopenharmony_ci		scs[i]->rcdev = rcdev;
4648c2ecf20Sopenharmony_ci		ret = gdsc_init(scs[i]);
4658c2ecf20Sopenharmony_ci		if (ret)
4668c2ecf20Sopenharmony_ci			return ret;
4678c2ecf20Sopenharmony_ci		data->domains[i] = &scs[i]->pd;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/* Add subdomains */
4718c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
4728c2ecf20Sopenharmony_ci		if (!scs[i])
4738c2ecf20Sopenharmony_ci			continue;
4748c2ecf20Sopenharmony_ci		if (scs[i]->parent)
4758c2ecf20Sopenharmony_ci			pm_genpd_add_subdomain(scs[i]->parent, &scs[i]->pd);
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	return of_genpd_add_provider_onecell(dev->of_node, data);
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_civoid gdsc_unregister(struct gdsc_desc *desc)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	int i;
4848c2ecf20Sopenharmony_ci	struct device *dev = desc->dev;
4858c2ecf20Sopenharmony_ci	struct gdsc **scs = desc->scs;
4868c2ecf20Sopenharmony_ci	size_t num = desc->num;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* Remove subdomains */
4898c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++) {
4908c2ecf20Sopenharmony_ci		if (!scs[i])
4918c2ecf20Sopenharmony_ci			continue;
4928c2ecf20Sopenharmony_ci		if (scs[i]->parent)
4938c2ecf20Sopenharmony_ci			pm_genpd_remove_subdomain(scs[i]->parent, &scs[i]->pd);
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci	of_genpd_del_provider(dev->of_node);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci/*
4998c2ecf20Sopenharmony_ci * On SDM845+ the GPU GX domain is *almost* entirely controlled by the GMU
5008c2ecf20Sopenharmony_ci * running in the CX domain so the CPU doesn't need to know anything about the
5018c2ecf20Sopenharmony_ci * GX domain EXCEPT....
5028c2ecf20Sopenharmony_ci *
5038c2ecf20Sopenharmony_ci * Hardware constraints dictate that the GX be powered down before the CX. If
5048c2ecf20Sopenharmony_ci * the GMU crashes it could leave the GX on. In order to successfully bring back
5058c2ecf20Sopenharmony_ci * the device the CPU needs to disable the GX headswitch. There being no sane
5068c2ecf20Sopenharmony_ci * way to reach in and touch that register from deep inside the GPU driver we
5078c2ecf20Sopenharmony_ci * need to set up the infrastructure to be able to ensure that the GPU can
5088c2ecf20Sopenharmony_ci * ensure that the GX is off during this super special case. We do this by
5098c2ecf20Sopenharmony_ci * defining a GX gdsc with a dummy enable function and a "default" disable
5108c2ecf20Sopenharmony_ci * function.
5118c2ecf20Sopenharmony_ci *
5128c2ecf20Sopenharmony_ci * This allows us to attach with genpd_dev_pm_attach_by_name() in the GPU
5138c2ecf20Sopenharmony_ci * driver. During power up, nothing will happen from the CPU (and the GMU will
5148c2ecf20Sopenharmony_ci * power up normally but during power down this will ensure that the GX domain
5158c2ecf20Sopenharmony_ci * is *really* off - this gives us a semi standard way of doing what we need.
5168c2ecf20Sopenharmony_ci */
5178c2ecf20Sopenharmony_ciint gdsc_gx_do_nothing_enable(struct generic_pm_domain *domain)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	/* Do nothing but give genpd the impression that we were successful */
5208c2ecf20Sopenharmony_ci	return 0;
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gdsc_gx_do_nothing_enable);
523