diff options
author | Vincent Guittot <vincent.guittot@stericsson.com> | 2011-04-11 14:37:04 +0200 |
---|---|---|
committer | Robert Marklund <robert.marklund@stericsson.com> | 2011-10-05 12:18:50 +0200 |
commit | fece5219b0375dbd8d1741cc1516bff89163ed1a (patch) | |
tree | ec7ce6073a6402cd029d4ba2681518c100e9b2f6 /drivers | |
parent | f8ebb51cbf3240a0c5fad715f2eb81d633f2a65f (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.c | 8 | ||||
-rw-r--r-- | drivers/cpufreq/db8500-cpufreq.c | 45 | ||||
-rw-r--r-- | drivers/cpuidle/governors/menu.c | 58 | ||||
-rw-r--r-- | drivers/mfd/db8500-prcmu.c | 69 |
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); |