|
@@ -16,6 +16,7 @@
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/jiffies.h>
|
|
|
#include <linux/kernel.h>
|
|
|
+#include <linux/ktime.h>
|
|
|
#include <linux/pm_domain.h>
|
|
|
#include <linux/regmap.h>
|
|
|
#include <linux/reset-controller.h>
|
|
@@ -42,12 +43,12 @@
|
|
|
|
|
|
#define domain_to_gdsc(domain) container_of(domain, struct gdsc, pd)
|
|
|
|
|
|
-static int gdsc_is_enabled(struct gdsc *sc)
|
|
|
+static int gdsc_is_enabled(struct gdsc *sc, unsigned int reg)
|
|
|
{
|
|
|
u32 val;
|
|
|
int ret;
|
|
|
|
|
|
- ret = regmap_read(sc->regmap, sc->gdscr, &val);
|
|
|
+ ret = regmap_read(sc->regmap, reg, &val);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
@@ -58,28 +59,35 @@ static int gdsc_toggle_logic(struct gdsc *sc, bool en)
|
|
|
{
|
|
|
int ret;
|
|
|
u32 val = en ? 0 : SW_COLLAPSE_MASK;
|
|
|
- u32 check = en ? PWR_ON_MASK : 0;
|
|
|
- unsigned long timeout;
|
|
|
+ ktime_t start;
|
|
|
+ unsigned int status_reg = sc->gdscr;
|
|
|
|
|
|
ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
|
|
|
- do {
|
|
|
- ret = regmap_read(sc->regmap, sc->gdscr, &val);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ if (sc->gds_hw_ctrl) {
|
|
|
+ status_reg = sc->gds_hw_ctrl;
|
|
|
+ /*
|
|
|
+ * The gds hw controller asserts/de-asserts the status bit soon
|
|
|
+ * after it receives a power on/off request from a master.
|
|
|
+ * The controller then takes around 8 xo cycles to start its
|
|
|
+ * internal state machine and update the status bit. During
|
|
|
+ * this time, the status bit does not reflect the true status
|
|
|
+ * of the core.
|
|
|
+ * Add a delay of 1 us between writing to the SW_COLLAPSE bit
|
|
|
+ * and polling the status bit.
|
|
|
+ */
|
|
|
+ udelay(1);
|
|
|
+ }
|
|
|
|
|
|
- if ((val & PWR_ON_MASK) == check)
|
|
|
+ start = ktime_get();
|
|
|
+ do {
|
|
|
+ if (gdsc_is_enabled(sc, status_reg) == en)
|
|
|
return 0;
|
|
|
- } while (time_before(jiffies, timeout));
|
|
|
-
|
|
|
- ret = regmap_read(sc->regmap, sc->gdscr, &val);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ } while (ktime_us_delta(ktime_get(), start) < TIMEOUT_US);
|
|
|
|
|
|
- if ((val & PWR_ON_MASK) == check)
|
|
|
+ if (gdsc_is_enabled(sc, status_reg) == en)
|
|
|
return 0;
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
@@ -165,6 +173,7 @@ static int gdsc_init(struct gdsc *sc)
|
|
|
{
|
|
|
u32 mask, val;
|
|
|
int on, ret;
|
|
|
+ unsigned int reg;
|
|
|
|
|
|
/*
|
|
|
* Disable HW trigger: collapse/restore occur based on registers writes.
|
|
@@ -185,7 +194,8 @@ static int gdsc_init(struct gdsc *sc)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- on = gdsc_is_enabled(sc);
|
|
|
+ reg = sc->gds_hw_ctrl ? sc->gds_hw_ctrl : sc->gdscr;
|
|
|
+ on = gdsc_is_enabled(sc, reg);
|
|
|
if (on < 0)
|
|
|
return on;
|
|
|
|