|
@@ -73,3 +73,185 @@ static struct powerdomain *_pwrdm_lookup(const char *name)
|
|
|
* Adds a powerdomain to the internal powerdomain list. Returns
|
|
|
* -EINVAL if given a null pointer, -EEXIST if a powerdomain is
|
|
|
* already registered by the provided name, or 0 upon success.
|
|
|
+ */
|
|
|
+static int _pwrdm_register(struct powerdomain *pwrdm)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct voltagedomain *voltdm;
|
|
|
+
|
|
|
+ if (!pwrdm || !pwrdm->name)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (cpu_is_omap44xx() &&
|
|
|
+ pwrdm->prcm_partition == OMAP4430_INVALID_PRCM_PARTITION) {
|
|
|
+ pr_err("powerdomain: %s: missing OMAP4 PRCM partition ID\n",
|
|
|
+ pwrdm->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_pwrdm_lookup(pwrdm->name))
|
|
|
+ return -EEXIST;
|
|
|
+
|
|
|
+ voltdm = voltdm_lookup(pwrdm->voltdm.name);
|
|
|
+ if (!voltdm) {
|
|
|
+ pr_err("powerdomain: %s: voltagedomain %s does not exist\n",
|
|
|
+ pwrdm->name, pwrdm->voltdm.name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ pwrdm->voltdm.ptr = voltdm;
|
|
|
+ INIT_LIST_HEAD(&pwrdm->voltdm_node);
|
|
|
+ voltdm_add_pwrdm(voltdm, pwrdm);
|
|
|
+
|
|
|
+ list_add(&pwrdm->node, &pwrdm_list);
|
|
|
+
|
|
|
+ /* Initialize the powerdomain's state counter */
|
|
|
+ for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
|
|
|
+ pwrdm->state_counter[i] = 0;
|
|
|
+
|
|
|
+ pwrdm->ret_logic_off_counter = 0;
|
|
|
+ for (i = 0; i < pwrdm->banks; i++)
|
|
|
+ pwrdm->ret_mem_off_counter[i] = 0;
|
|
|
+
|
|
|
+ pwrdm_wait_transition(pwrdm);
|
|
|
+ pwrdm->state = pwrdm_read_pwrst(pwrdm);
|
|
|
+ pwrdm->state_counter[pwrdm->state] = 1;
|
|
|
+
|
|
|
+ pr_debug("powerdomain: registered %s\n", pwrdm->name);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void _update_logic_membank_counters(struct powerdomain *pwrdm)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ u8 prev_logic_pwrst, prev_mem_pwrst;
|
|
|
+
|
|
|
+ prev_logic_pwrst = pwrdm_read_prev_logic_pwrst(pwrdm);
|
|
|
+ if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) &&
|
|
|
+ (prev_logic_pwrst == PWRDM_POWER_OFF))
|
|
|
+ pwrdm->ret_logic_off_counter++;
|
|
|
+
|
|
|
+ for (i = 0; i < pwrdm->banks; i++) {
|
|
|
+ prev_mem_pwrst = pwrdm_read_prev_mem_pwrst(pwrdm, i);
|
|
|
+
|
|
|
+ if ((pwrdm->pwrsts_mem_ret[i] == PWRSTS_OFF_RET) &&
|
|
|
+ (prev_mem_pwrst == PWRDM_POWER_OFF))
|
|
|
+ pwrdm->ret_mem_off_counter[i]++;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
|
|
|
+{
|
|
|
+
|
|
|
+ int prev, state, trace_state = 0;
|
|
|
+
|
|
|
+ if (pwrdm == NULL)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ state = pwrdm_read_pwrst(pwrdm);
|
|
|
+
|
|
|
+ switch (flag) {
|
|
|
+ case PWRDM_STATE_NOW:
|
|
|
+ prev = pwrdm->state;
|
|
|
+ break;
|
|
|
+ case PWRDM_STATE_PREV:
|
|
|
+ prev = pwrdm_read_prev_pwrst(pwrdm);
|
|
|
+ if (pwrdm->state != prev)
|
|
|
+ pwrdm->state_counter[prev]++;
|
|
|
+ if (prev == PWRDM_POWER_RET)
|
|
|
+ _update_logic_membank_counters(pwrdm);
|
|
|
+ /*
|
|
|
+ * If the power domain did not hit the desired state,
|
|
|
+ * generate a trace event with both the desired and hit states
|
|
|
+ */
|
|
|
+ if (state != prev) {
|
|
|
+ trace_state = (PWRDM_TRACE_STATES_FLAG |
|
|
|
+ ((state & OMAP_POWERSTATE_MASK) << 8) |
|
|
|
+ ((prev & OMAP_POWERSTATE_MASK) << 0));
|
|
|
+ trace_power_domain_target(pwrdm->name, trace_state,
|
|
|
+ smp_processor_id());
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (state != prev)
|
|
|
+ pwrdm->state_counter[state]++;
|
|
|
+
|
|
|
+ pm_dbg_update_time(pwrdm, prev);
|
|
|
+
|
|
|
+ pwrdm->state = state;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused)
|
|
|
+{
|
|
|
+ pwrdm_clear_all_prev_pwrst(pwrdm);
|
|
|
+ _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
|
|
|
+{
|
|
|
+ _pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Public functions */
|
|
|
+
|
|
|
+/**
|
|
|
+ * pwrdm_register_platform_funcs - register powerdomain implementation fns
|
|
|
+ * @po: func pointers for arch specific implementations
|
|
|
+ *
|
|
|
+ * Register the list of function pointers used to implement the
|
|
|
+ * powerdomain functions on different OMAP SoCs. Should be called
|
|
|
+ * before any other pwrdm_register*() function. Returns -EINVAL if
|
|
|
+ * @po is null, -EEXIST if platform functions have already been
|
|
|
+ * registered, or 0 upon success.
|
|
|
+ */
|
|
|
+int pwrdm_register_platform_funcs(struct pwrdm_ops *po)
|
|
|
+{
|
|
|
+ if (!po)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (arch_pwrdm)
|
|
|
+ return -EEXIST;
|
|
|
+
|
|
|
+ arch_pwrdm = po;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pwrdm_register_pwrdms - register SoC powerdomains
|
|
|
+ * @ps: pointer to an array of struct powerdomain to register
|
|
|
+ *
|
|
|
+ * Register the powerdomains available on a particular OMAP SoC. Must
|
|
|
+ * be called after pwrdm_register_platform_funcs(). May be called
|
|
|
+ * multiple times. Returns -EACCES if called before
|
|
|
+ * pwrdm_register_platform_funcs(); -EINVAL if the argument @ps is
|
|
|
+ * null; or 0 upon success.
|
|
|
+ */
|
|
|
+int pwrdm_register_pwrdms(struct powerdomain **ps)
|
|
|
+{
|
|
|
+ struct powerdomain **p = NULL;
|
|
|
+
|
|
|
+ if (!arch_pwrdm)
|
|
|
+ return -EEXIST;
|
|
|
+
|
|
|
+ if (!ps)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ for (p = ps; *p; p++)
|
|
|
+ _pwrdm_register(*p);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pwrdm_complete_init - set up the powerdomain layer
|
|
|
+ *
|
|
|
+ * Do whatever is necessary to initialize registered powerdomains and
|
|
|
+ * powerdomain code. Currently, this programs the next power state
|