clk: qcom: gdsc-regulator: Add missing support

Some GDSCs have side effects when enabled without first
performing a special reset sequence.  It is only feasible for
this sequence to be performed by the always-on processor (AOP)
when it is resuming from system sleep.  Add support for a device
tree flag which can be specified in order to skip physically
disabling a GDSC when regulator_disable() is called.  The AOP
will then ensure that the GDSC is disabled upon entering system
sleep and appropriately reset upon resuming from system sleep.

Also change the is_enabled() callback so that it returns the
GDSC logical enable state instead of the physical enable state
whenever the disable skipping flag is specified.  This ensures
that all dependent regulator and bus requests are made in the
case of disabling and re-enabling a GDSC without entering system
sleep (i.e. when the GDSC shows up as still physically enabled.)

If the disable skipping feature is allowed in device tree, then
use regulator_set_mode() to control the feature's usage at
runtime.  This is needed for SM8150 gpu_cx_gdsc in order to
facilitate GMU hang recovery.

REGULATOR_MODE_NORMAL (default) --> physical GDSC disable skipped
REGULATOR_MODE_IDLE             --> physical GDSC disable occurs.

Change-Id: I3966fd1ac59acef215c4c051efb293a49e5ebdbd
Signed-off-by: Taniya Das <tdas@codeaurora.org>
This commit is contained in:
Taniya Das 2021-03-01 17:34:35 +05:30 • committed by Veera Vegivada
commit 2bf474bf4b

View file

@ -19,6 +19,9 @@
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/mfd/syscon.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox/qmp.h>
#include "../../regulator/internal.h"
#include "gdsc-debug.h"
@ -45,6 +48,8 @@
/* Timeout Delay */
#define TIMEOUT_US 100
#define MBOX_TOUT_MS 100
struct collapse_vote {
struct regmap *regmap;
u32 vote_bit;
@ -62,6 +67,8 @@ struct gdsc {
struct regmap *acd_misc_reset;
struct collapse_vote collapse_vote;
struct clk **clocks;
struct mbox_client mbox_client;
struct mbox_chan *mbox;
struct reset_control **reset_clocks;
bool toggle_logic;
bool retain_ff_enable;
@ -78,6 +85,8 @@ struct gdsc {
int root_clk_idx;
u32 gds_timeout;
bool skip_disable_before_enable;
bool skip_disable;
bool bypass_skip_disable;
};
enum gdscr_status {
@ -163,6 +172,13 @@ static int gdsc_is_enabled(struct regulator_dev *rdev)
{
struct gdsc *sc = rdev_get_drvdata(rdev);
/*
* Return the logical GDSC enable state given that it will only be
* physically disabled by AOP during system sleep.
*/
if (sc->skip_disable)
return sc->is_gdsc_enabled;
if (!sc->toggle_logic)
return !sc->resets_asserted;
@ -172,6 +188,35 @@ static int gdsc_is_enabled(struct regulator_dev *rdev)
return sc->is_gdsc_enabled;
}
#define MAX_LEN 96
static int gdsc_qmp_enable(struct gdsc *sc)
{
char buf[MAX_LEN] = "{class: clock, res: gpu_noc_wa}";
struct qmp_pkt pkt;
uint32_t regval;
int ret;
regmap_read(sc->regmap, REG_OFFSET, &regval);
if (!(regval & SW_COLLAPSE_MASK)) {
/*
* Do not enable via a QMP request if the GDSC is already
* enabled by software.
*/
return 0;
}
pkt.size = MAX_LEN;
pkt.data = buf;
ret = mbox_send_message(sc->mbox, &pkt);
if (ret < 0)
dev_err(&sc->rdev->dev, "qmp message send failed, ret=%d\n",
ret);
return ret;
}
static int gdsc_enable(struct regulator_dev *rdev)
{
struct gdsc *sc = rdev_get_drvdata(rdev);
@ -260,7 +305,11 @@ static int gdsc_enable(struct regulator_dev *rdev)
}
/* Enable gdsc */
if (sc->collapse_vote.regmap) {
if (sc->mbox) {
ret = gdsc_qmp_enable(sc);
if (ret < 0)
return ret;
} else if (sc->collapse_vote.regmap) {
regmap_update_bits(sc->collapse_vote.regmap, REG_OFFSET,
BIT(sc->collapse_vote.vote_bit),
~BIT(sc->collapse_vote.vote_bit));
@ -380,7 +429,12 @@ static int gdsc_disable(struct regulator_dev *rdev)
/* Delay to account for staggered memory powerdown. */
udelay(1);
if (sc->toggle_logic) {
if (sc->skip_disable && !sc->bypass_skip_disable) {
/*
* Don't change the GDSCR register state on disable. AOP will
* handle this during system sleep.
*/
} else if (sc->toggle_logic) {
if (sc->sw_reset) {
if (sc->acd_misc_reset)
regmap_update_bits(sc->acd_misc_reset, REG_OFFSET,
@ -466,6 +520,12 @@ static unsigned int gdsc_get_mode(struct regulator_dev *rdev)
{
struct gdsc *sc = rdev_get_drvdata(rdev);
if (sc->skip_disable) {
if (sc->bypass_skip_disable)
return REGULATOR_MODE_IDLE;
return REGULATOR_MODE_NORMAL;
}
return sc->is_gdsc_hw_ctrl_mode ? REGULATOR_MODE_FAST
: REGULATOR_MODE_NORMAL;
}
@ -477,6 +537,22 @@ static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode)
uint32_t regval;
int ret = 0;
if (sc->skip_disable) {
switch (mode) {
case REGULATOR_MODE_IDLE:
sc->bypass_skip_disable = true;
break;
case REGULATOR_MODE_NORMAL:
sc->bypass_skip_disable = false;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
if (rdev->supply) {
parent_rdev = rdev->supply->rdev;
/*
@ -705,6 +781,20 @@ static int gdsc_parse_dt_data(struct gdsc *sc, struct device *dev,
}
}
sc->skip_disable = of_property_read_bool(dev->of_node,
"qcom,skip-disable");
if (sc->skip_disable) {
/*
* If the disable skipping feature is allowed, then use mode
* control to enable and disable the feature at runtime instead
* of using it to enable and disable hardware triggering.
*/
(*init_data)->constraints.valid_ops_mask |=
REGULATOR_CHANGE_MODE;
(*init_data)->constraints.valid_modes_mask =
REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
}
sc->toggle_logic = !of_property_read_bool(dev->of_node,
"qcom,skip-logic-collapse");
if (!sc->toggle_logic) {
@ -840,6 +930,27 @@ static int gdsc_probe(struct platform_device *pdev)
regmap_read(sc->regmap, REG_OFFSET, &regval);
regval &= ~(HW_CONTROL_MASK | SW_OVERRIDE_MASK);
if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) {
sc->mbox_client.dev = &pdev->dev;
sc->mbox_client.tx_block = true;
sc->mbox_client.tx_tout = MBOX_TOUT_MS;
sc->mbox_client.knows_txdone = false;
sc->mbox = mbox_request_channel(&sc->mbox_client, 0);
if (IS_ERR(sc->mbox)) {
ret = PTR_ERR(sc->mbox);
if (ret == -EAGAIN)
ret = -EPROBE_DEFER;
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "mailbox channel request failed, ret=%d\n",
ret);
sc->mbox = NULL;
goto err;
}
}
if (!of_property_read_u32(pdev->dev.of_node, "qcom,clk-dis-wait-val",
&clk_dis_wait_val)) {
clk_dis_wait_val = clk_dis_wait_val << CLK_DIS_WAIT_SHIFT;
@ -859,7 +970,7 @@ static int gdsc_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "%s enable timed out: 0x%x\n",
sc->rdesc.name, regval);
return ret;
goto err;
}
}
@ -867,14 +978,14 @@ static int gdsc_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "%s failed to get initial enable state, ret=%d\n",
sc->rdesc.name, ret);
return ret;
goto err;
}
ret = gdsc_init_hw_ctrl_mode(sc);
if (ret) {
dev_err(dev, "%s failed to get initial hw_ctrl state, ret=%d\n",
sc->rdesc.name, ret);
return ret;
goto err;
}
sc->rdesc.id = atomic_inc_return(&gdsc_count);
@ -893,16 +1004,23 @@ static int gdsc_probe(struct platform_device *pdev)
ret = PTR_ERR(sc->rdev);
dev_err(dev, "regulator_register(\"%s\") failed, ret=%d\n",
sc->rdesc.name, ret);
return ret;
goto err;
}
ret = devm_regulator_proxy_consumer_register(dev, dev->of_node);
if (ret)
if (ret) {
dev_err(dev, "failed to register proxy consumer, ret=%d\n",
ret);
goto err;
}
platform_set_drvdata(pdev, sc);
return 0;
err:
if (sc->mbox)
mbox_free_channel(sc->mbox);
return ret;
}