summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorVincent Guittot <vincent.guittot@stericsson.com>2011-04-11 14:37:04 +0200
committerRobert Marklund <robert.marklund@stericsson.com>2011-10-05 12:18:50 +0200
commitfece5219b0375dbd8d1741cc1516bff89163ed1a (patch)
treeec7ce6073a6402cd029d4ba2681518c100e9b2f6 /drivers
parentf8ebb51cbf3240a0c5fad715f2eb81d633f2a65f (diff)
ux500: pm: add use-case governor
Signed-off-by: Vincent Guittot <vincent.guittot@stericsson.com> Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> [ANDROID]: ux500: pm: update sysfs interface for usecase governor A separate sysfs node is added fro each use-case. It is now possible to enable multiple use-cases simultaneously. If multiple use-cases are enabled, each configuration (of all enabled uses-caes) is compared and the configuration which is requesting highest performance is selected. Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> [ANDROID]: ux500: pm: usecase-gov Act on interrupts per s If the number of interrupts per seconds are above a certain level, exit from the asked mode. [ANDROID]: ux500: pm: update low-power-audio config in usecse-governor Update low-power-audio configuration to plug-out second cpu. This makes auto, voice-call and low-power-audio configurations to be same. Auto mode is activated by default (without any user space interaction). This makes voice-call and low-power-audio configurations redundant. However, analysis work is still on going for adding other settings that may become part of usecase governor so keeping all configurations in there for now. Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> [ANDROID]: ux500: pm: update usecase governor dependencies in Kconfig [ANDROID]: ARM: ux500: pm: usecase gov depends on PM Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> [ANDROID]: ux500: pm: minor code cleanup in UC governor Signed-off-by: Martin Persson <martin.persson@stericsson.com> [ANDROID] ux500: pm: disable auto mode in usecase governor In absence of auto mode there is no need to schedule work on early suspend so schedule work only when needed. Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> [ANDROID]: pm: force ondemand governor to choose higher frequency In stead of overriding frequency selected by ondemand governor in u8500_cpufreq_target(), force ondemand governor to select higher frequency when wlan and usb workaround is enabled. ANDROID tag is used to keep the patch internal. Otherwise it does not have any Android dependency. Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> [ANDROID]: ARM: u8500: usecaseg: Tiny code clean-up Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> arm: ux500: halve the frequencies of some clocks in vc use case This patch adds a new APE OPP (50% with ACLK and DMACLK frequencies at 25%) which is used during the voice call use case. Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> Signed-off-by: Mattias Nilsson <mattias.i.nilsson@stericsson.com> ux500: pm: Add min cpufreq to UC governor Add a minimum cpu frequency limit to each use case in the use case governor. Signed-off-by: Martin Persson <martin.persson@stericsson.com> ux500: pm: allow usecase governor to force cpu idle states Also update low-power-audio configuration to force ApSleep state. ux500: pm: move use case definitions to a header file ux500: pm: add pl310 prefetch control in use-case governor ux500: pm: cosmetic changes in use-case governor Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c8
-rw-r--r--drivers/cpufreq/db8500-cpufreq.c45
-rw-r--r--drivers/cpuidle/governors/menu.c58
-rw-r--r--drivers/mfd/db8500-prcmu.c69
4 files changed, 154 insertions, 26 deletions
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index a5bbbf60ae8..bcef88a89b0 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -390,6 +390,10 @@ static struct attribute_group dbs_attr_group = {
.name = "ondemand",
};
+/*** delete after deprecation time ***/
+extern bool wlan_mode_on;
+extern bool usb_mode_on;
+
/************************** sysfs end ************************/
static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq)
@@ -494,7 +498,9 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
}
/* Check for frequency increase */
- if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) {
+ if ((max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) ||
+ /* A work around for wlan and usb performance issues */
+ (usb_mode_on || wlan_mode_on)) {
/* If switching to max speed, apply sampling_down_factor */
if (policy->cur < policy->max)
this_dbs_info->rate_mult =
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c
index 4b863110bd8..629b0b88e6c 100644
--- a/drivers/cpufreq/db8500-cpufreq.c
+++ b/drivers/cpufreq/db8500-cpufreq.c
@@ -58,6 +58,10 @@ static enum arm_opp idx2opp[] = {
#define WLAN_PROBE_DELAY 3000 /* 3 seconds */
#define WLAN_LIMIT (3000/3) /* If we have more than 1000 irqs per second */
+#define USB_PROBE_DELAY 1000 /* 1 seconds */
+#define USB_LIMIT (200) /* If we have more than 200 irqs per second */
+static struct delayed_work work_usb_workaround;
+bool usb_mode_on;
static struct delayed_work work_wlan_workaround;
bool wlan_mode_on;
@@ -84,6 +88,40 @@ static void wlan_load(struct work_struct *work)
msecs_to_jiffies(WLAN_PROBE_DELAY));
}
+static void usb_load(struct work_struct *work)
+{
+ int cpu;
+ unsigned int num_irqs = 0;
+ static unsigned int old_num_irqs = UINT_MAX;
+
+ for_each_online_cpu(cpu)
+ num_irqs += kstat_irqs_cpu(IRQ_DB8500_USBOTG, cpu);
+
+ if ((num_irqs > old_num_irqs) &&
+ (num_irqs - old_num_irqs) > USB_LIMIT)
+ usb_mode_on = true;
+ else
+ usb_mode_on = false;
+
+ old_num_irqs = num_irqs;
+
+ schedule_delayed_work_on(0,
+ &work_usb_workaround,
+ msecs_to_jiffies(USB_PROBE_DELAY));
+}
+
+void cpufreq_usb_connect_notify(bool connect)
+{
+ if (connect) {
+ schedule_delayed_work_on(0,
+ &work_usb_workaround,
+ msecs_to_jiffies(USB_PROBE_DELAY));
+ } else {
+ cancel_delayed_work_sync(&work_usb_workaround);
+ usb_mode_on = false;
+ }
+}
+
static struct freq_attr *db8500_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
@@ -107,10 +145,6 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
if (target_freq > policy->cpuinfo.max_freq)
target_freq = policy->cpuinfo.max_freq;
- /* A work around for wlan performance issues */
- if (wlan_mode_on)
- target_freq = policy->cpuinfo.max_freq;
-
/* Lookup the next frequency */
if (cpufreq_frequency_table_target
(policy, freq_table, target_freq, relation, &idx)) {
@@ -169,6 +203,9 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
&work_wlan_workaround,
msecs_to_jiffies(WLAN_PROBE_DELAY));
+ INIT_DELAYED_WORK_DEFERRABLE(&work_usb_workaround,
+ usb_load);
+
pr_info("db8500-cpufreq : Available frequencies:\n");
while (freq_table[i].frequency != CPUFREQ_TABLE_END)
pr_info(" %d Mhz\n", freq_table[i++].frequency/1000);
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 95f4c391e4f..ed28f774ca4 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -124,6 +124,7 @@ struct menu_device {
};
static int tune_multiplier = 1024;
+static int forced_state;
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
@@ -290,26 +291,34 @@ static int menu_select(struct cpuidle_device *dev)
if (data->expected_us > 5)
data->last_state_idx = CPUIDLE_DRIVER_STATE_START;
- /*
- * Find the idle state with the lowest power while satisfying
- * our constraints.
- */
- for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) {
- struct cpuidle_state *s = &dev->states[i];
-
- if (s->flags & CPUIDLE_FLAG_IGNORE)
- continue;
- if (s->target_residency > data->predicted_us)
- continue;
- if (s->exit_latency > latency_req)
- continue;
- if (s->exit_latency * multiplier > data->predicted_us)
- continue;
-
- if (s->power_usage < power_usage) {
- power_usage = s->power_usage;
- data->last_state_idx = i;
- data->exit_us = s->exit_latency;
+ WARN((forced_state >= dev->state_count), \
+ "Forced state value out of range.\n");
+
+ if ((forced_state != 0) && (forced_state < dev->state_count)) {
+ data->exit_us = dev->states[forced_state].exit_latency;
+ data->last_state_idx = forced_state;
+ } else {
+ /*
+ * Find the idle state with the lowest power while satisfying
+ * our constraints.
+ */
+ for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) {
+ struct cpuidle_state *s = &dev->states[i];
+
+ if (s->flags & CPUIDLE_FLAG_IGNORE)
+ continue;
+ if (s->target_residency > data->predicted_us)
+ continue;
+ if (s->exit_latency > latency_req)
+ continue;
+ if (s->exit_latency * multiplier > data->predicted_us)
+ continue;
+
+ if (s->power_usage < power_usage) {
+ power_usage = s->power_usage;
+ data->last_state_idx = i;
+ data->exit_us = s->exit_latency;
+ }
}
}
@@ -402,6 +411,15 @@ int cpuidle_set_multiplier(unsigned int value)
}
EXPORT_SYMBOL(cpuidle_set_multiplier);
+/* Writing 0 will remove the forced state. */
+int cpuidle_force_state(unsigned int state)
+{
+ forced_state = state;
+
+ return 0;
+}
+EXPORT_SYMBOL(cpuidle_force_state);
+
static ssize_t show_multiplier(struct sysdev_class *class,
struct sysdev_class_attribute *attr,
char *buf)
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index f9e3c5e55d7..b226b155e29 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -340,11 +340,13 @@ static struct {
* mb1_transfer - state needed for mailbox 1 communication.
* @lock: The transaction lock.
* @work: The transaction completion structure.
+ * @ape_opp: The current APE OPP.
* @ack: Reply ("acknowledge") data.
*/
static struct {
struct mutex lock;
struct completion work;
+ u8 ape_opp;
struct {
u8 header;
u8 arm_opp;
@@ -938,6 +940,53 @@ int prcmu_set_ddr_opp(u8 opp)
return 0;
}
+
+/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
+static void request_even_slower_clocks(bool enable)
+{
+ const u8 clock_reg_offset[] = {
+ PRCM_ACLK_MGT_OFF,
+ PRCM_DMACLK_MGT_OFF
+ };
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < ARRAY_SIZE(clock_reg_offset); i++) {
+ u32 val;
+ u32 div;
+
+ val = readl(_PRCMU_BASE + clock_reg_offset[i]);
+ div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
+ if (enable) {
+ if ((div <= 1) || (div > 15)) {
+ pr_err("prcmu: Bad clock divider %d in %s\n",
+ div, __func__);
+ goto unlock_and_return;
+ }
+ div <<= 1;
+ } else {
+ if (div <= 2)
+ goto unlock_and_return;
+ div >>= 1;
+ }
+ val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
+ (div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
+ writel(val, (_PRCMU_BASE + clock_reg_offset[i]));
+ }
+
+unlock_and_return:
+ /* Release the HW semaphore. */
+ writel(0, PRCM_SEM);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+
/**
* set_ape_opp - set the appropriate APE OPP
* @opp: The new APE operating point to which transition is to be made
@@ -949,14 +998,24 @@ int prcmu_set_ape_opp(u8 opp)
{
int r = 0;
+ if (opp == mb1_transfer.ape_opp)
+ return 0;
+
mutex_lock(&mb1_transfer.lock);
+ if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
+ request_even_slower_clocks(false);
+
+ if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP))
+ goto skip_message;
+
while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
- writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
+ writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
+ (tcdm_base + PRCM_REQ_MB1_APE_OPP));
writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
@@ -965,6 +1024,13 @@ int prcmu_set_ape_opp(u8 opp)
(mb1_transfer.ack.ape_opp != opp))
r = -EIO;
+skip_message:
+ if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
+ (r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
+ request_even_slower_clocks(true);
+ if (!r)
+ mb1_transfer.ape_opp = opp;
+
mutex_unlock(&mb1_transfer.lock);
return r;
@@ -2126,6 +2192,7 @@ void __init db8500_prcmu_early_init(void)
init_completion(&mb0_transfer.ac_wake_work);
mutex_init(&mb1_transfer.lock);
init_completion(&mb1_transfer.work);
+ mb1_transfer.ape_opp = APE_NO_CHANGE;
mutex_init(&mb2_transfer.lock);
init_completion(&mb2_transfer.work);
spin_lock_init(&mb2_transfer.auto_pm_lock);