diff options
Diffstat (limited to 'drivers/cpufreq/db8500-cpufreq.c')
-rw-r--r-- | drivers/cpufreq/db8500-cpufreq.c | 115 |
1 files changed, 105 insertions, 10 deletions
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c index d90456a809f..629b0b88e6c 100644 --- a/drivers/cpufreq/db8500-cpufreq.c +++ b/drivers/cpufreq/db8500-cpufreq.c @@ -12,35 +12,116 @@ #include <linux/cpufreq.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/mfd/db8500-prcmu.h> +#include <mach/prcmu.h> #include <mach/id.h> static struct cpufreq_frequency_table freq_table[] = { [0] = { .index = 0, - .frequency = 300000, + .frequency = 200000, }, [1] = { .index = 1, - .frequency = 600000, + .frequency = 300000, }, [2] = { - /* Used for MAX_OPP, if available */ .index = 2, - .frequency = CPUFREQ_TABLE_END, + .frequency = 600000, }, [3] = { + /* Used for MAX_OPP, if available */ .index = 3, .frequency = CPUFREQ_TABLE_END, }, + [4] = { + .index = 4, + .frequency = CPUFREQ_TABLE_END, + }, }; static enum arm_opp idx2opp[] = { + ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP }; +/* + * Below is a temporary workaround for wlan performance issues + */ + +#include <linux/kernel_stat.h> +#include <linux/workqueue.h> +#include <linux/cpu.h> + +#include <mach/irqs.h> + +#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; + +static void wlan_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_SDMMC1, cpu); + + if ((num_irqs > old_num_irqs) && + (num_irqs - old_num_irqs) > WLAN_LIMIT) + wlan_mode_on = true; + else + wlan_mode_on = false; + + old_num_irqs = num_irqs; + + schedule_delayed_work_on(0, + &work_wlan_workaround, + 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, @@ -104,17 +185,31 @@ static unsigned int db8500_cpufreq_getspeed(unsigned int cpu) static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy) { int res; - int i; + int i = 0; BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table)); - if (cpu_is_u8500v2() && !prcmu_is_u8400()) { - freq_table[0].frequency = 400000; - freq_table[1].frequency = 800000; + if (!prcmu_is_u8400()) { + freq_table[1].frequency = 400000; + freq_table[2].frequency = 800000; if (prcmu_has_arm_maxopp()) - freq_table[2].frequency = 1000000; + freq_table[3].frequency = 1000000; } + INIT_DELAYED_WORK_DEFERRABLE(&work_wlan_workaround, + wlan_load); + + schedule_delayed_work_on(0, + &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); + /* get policy fields based on the table */ res = cpufreq_frequency_table_cpuinfo(policy, freq_table); if (!res) |