diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h index 79aebe2ad7a8..ce0a8b95da39 100644 --- a/arch/arm/include/asm/smp_twd.h +++ b/arch/arm/include/asm/smp_twd.h @@ -37,12 +37,4 @@ void twd_timer_setup(struct clock_event_device *); void twd_timer_setup_scalable(struct clock_event_device *, unsigned long target_rate, unsigned int periphclk_prescaler); -/* - * Recalculate the twd prescaler value when the cpu frequency changes. To - * prevent early timer interrupts, must be called before changing the cpu - * frequency if the frequency is increasing, or after if the frequency is - * decreasing. - */ -void twd_recalc_prescaler(unsigned long new_rate); - #endif diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 014862ae170c..3dc62ee45639 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -26,7 +27,7 @@ void __iomem *twd_base; static unsigned long twd_timer_rate; static unsigned long twd_periphclk_prescaler; -static unsigned long twd_target_rate; +static unsigned long twd_cpu_rate; static void twd_set_mode(enum clock_event_mode mode, struct clock_event_device *clk) @@ -82,7 +83,13 @@ int twd_timer_ack(void) return 0; } -void twd_recalc_prescaler(unsigned long new_rate) +/* + * Recalculate the twd prescaler value when the cpu frequency changes. To + * prevent early timer interrupts, must be called before changing the cpu + * frequency if the frequency is increasing, or after if the frequency is + * decreasing. + */ +static void twd_update_prescaler(void *data) { u32 ctrl; int prescaler; @@ -90,7 +97,7 @@ void twd_recalc_prescaler(unsigned long new_rate) BUG_ON(twd_periphclk_prescaler == 0 || twd_timer_rate == 0); - periphclk_rate = new_rate / twd_periphclk_prescaler; + periphclk_rate = twd_cpu_rate / twd_periphclk_prescaler; prescaler = DIV_ROUND_UP(periphclk_rate, twd_timer_rate); prescaler = clamp(prescaler - 1, 0, 0xFF); @@ -101,60 +108,95 @@ void twd_recalc_prescaler(unsigned long new_rate) __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); } +static int twd_cpufreq_transition(struct notifier_block *nb, + unsigned long state, void *data) +{ + struct cpufreq_freqs *freqs = data; + if (((freqs->new > freqs->old) && state == CPUFREQ_PRECHANGE) || + ((freqs->old > freqs->new) && state == CPUFREQ_POSTCHANGE)) { + /* freqs->new is in kHz, twd_cpu_rate is in Hz */ + twd_cpu_rate = freqs->new * 1000; + + smp_call_function_single(freqs->cpu, twd_update_prescaler, + NULL, 1); + } + + return NOTIFY_OK; +} + +static struct notifier_block twd_cpufreq_nb = { + .notifier_call = twd_cpufreq_transition, +}; + +static int twd_cpufreq_init(void) +{ + if (twd_cpu_rate) + return cpufreq_register_notifier(&twd_cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); + + return 0; +} +core_initcall(twd_cpufreq_init); + static void __cpuinit twd_calibrate_rate(unsigned long target_rate, unsigned int periphclk_prescaler) { unsigned long load, count; u64 waitjiffies; - unsigned long cpu_rate; /* * If this is the first time round, we need to work out how fast * the timer ticks */ - printk(KERN_INFO "Calibrating local timer... "); + if (twd_timer_rate == 0) { + printk(KERN_INFO "Calibrating local timer... "); - /* Wait for a tick to start */ - waitjiffies = get_jiffies_64() + 1; + /* Wait for a tick to start */ + waitjiffies = get_jiffies_64() + 1; - while (get_jiffies_64() < waitjiffies) - udelay(10); + while (get_jiffies_64() < waitjiffies) + udelay(10); - /* OK, now the tick has started, let's get the timer going */ - waitjiffies += 5; + /* OK, now the tick has started, let's get the timer going */ + waitjiffies += 5; - /* enable, no interrupt or reload */ - __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); + /* enable, no interrupt or reload */ + __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); - /* maximum value */ - __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); + /* maximum value */ + __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); - while (get_jiffies_64() < waitjiffies) - udelay(10); + while (get_jiffies_64() < waitjiffies) + udelay(10); - count = __raw_readl(twd_base + TWD_TIMER_COUNTER); + count = __raw_readl(twd_base + TWD_TIMER_COUNTER); - twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); + twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); - /* - * If a target rate has been requested, adjust the TWD prescaler - * to get the closest lower frequency. - */ - if (target_rate) { - twd_periphclk_prescaler = periphclk_prescaler; - twd_target_rate = target_rate; + /* + * If a target rate has been requested, adjust the TWD prescaler + * to get the closest lower frequency. + */ + if (target_rate) { + twd_periphclk_prescaler = periphclk_prescaler; - printk("%lu.%02luMHz, setting to ", - twd_timer_rate / 1000000, + printk("%lu.%02luMHz, setting to ", + twd_timer_rate / 1000000, + (twd_timer_rate / 10000) % 100); + twd_cpu_rate = twd_timer_rate * periphclk_prescaler; + twd_timer_rate = target_rate; + twd_update_prescaler(NULL); + } + + printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, (twd_timer_rate / 10000) % 100); - cpu_rate = twd_timer_rate * periphclk_prescaler; - twd_timer_rate = twd_target_rate; - twd_recalc_prescaler(cpu_rate); + } else { + if (target_rate) { + BUG_ON(target_rate != twd_timer_rate); + twd_update_prescaler(NULL); + } } - printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, - (twd_timer_rate / 10000) % 100); - load = twd_timer_rate / HZ; __raw_writel(load, twd_base + TWD_TIMER_LOAD); diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c index a878545d2740..3b8a6b56783e 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -37,7 +37,10 @@ #include #include -/* Frequency table index must be sequential starting at 0 and frequencies must be ascending*/ +/* + * Frequency table index must be sequential starting at 0 and frequencies + * must be ascending. + */ static struct cpufreq_frequency_table freq_table[] = { { 0, 216000 }, { 1, 312000 }, @@ -61,44 +64,35 @@ static bool is_suspended; unsigned int tegra_getspeed(unsigned int cpu); static int tegra_update_cpu_speed(unsigned long rate); - -/* CPU frequency is gradually lowered when throttling is enabled */ -#define THROTTLE_START_INDEX 2 -#define THROTTLE_END_INDEX 6 +static unsigned long tegra_cpu_highest_speed(void); #ifdef CONFIG_TEGRA_THERMAL_THROTTLE +/* CPU frequency is gradually lowered when throttling is enabled */ +#define THROTTLE_LOWEST_INDEX 2 /* 456000 */ +#define THROTTLE_HIGHEST_INDEX 6 /* 912000 */ #define THROTTLE_DELAY msecs_to_jiffies(2000) -#define NO_DELAY msecs_to_jiffies(0) -static DEFINE_MUTEX(throttling_lock); static bool is_throttling; +static int throttle_index; +static int throttle_next_index; static struct delayed_work throttle_work; static struct workqueue_struct *workqueue; #define tegra_cpu_is_throttling() (is_throttling) -static bool tegra_throttling_needed(unsigned long *rate) -{ - unsigned int current_freq = tegra_getspeed(0); - int i; - - for (i = THROTTLE_END_INDEX; i >= THROTTLE_START_INDEX; i--) { - if (freq_table[i].frequency < current_freq) { - *rate = freq_table[i].frequency; - return true; - } - } - - return false; -} - static void tegra_throttle_work_func(struct work_struct *work) { - unsigned long rate; + unsigned int current_freq; mutex_lock(&tegra_cpu_lock); + current_freq = tegra_getspeed(0); + throttle_index = throttle_next_index; - if (tegra_throttling_needed(&rate) && tegra_update_cpu_speed(rate) == 0) { + if (freq_table[throttle_index].frequency < current_freq) + tegra_update_cpu_speed(freq_table[throttle_index].frequency); + + if (throttle_index > THROTTLE_LOWEST_INDEX) { + throttle_next_index = throttle_index - 1; queue_delayed_work(workqueue, &throttle_work, THROTTLE_DELAY); } @@ -111,20 +105,48 @@ static void tegra_throttle_work_func(struct work_struct *work) */ void tegra_throttling_enable(bool enable) { - mutex_lock(&throttling_lock); + mutex_lock(&tegra_cpu_lock); if (enable && !is_throttling) { + unsigned int current_freq = tegra_getspeed(0); + is_throttling = true; - queue_delayed_work(workqueue, &throttle_work, NO_DELAY); + + for (throttle_index = THROTTLE_HIGHEST_INDEX; + throttle_index >= THROTTLE_LOWEST_INDEX; + throttle_index--) + if (freq_table[throttle_index].frequency + < current_freq) + break; + + throttle_index = max(throttle_index, THROTTLE_LOWEST_INDEX); + throttle_next_index = throttle_index; + queue_delayed_work(workqueue, &throttle_work, 0); } else if (!enable && is_throttling) { cancel_delayed_work_sync(&throttle_work); is_throttling = false; + /* restore speed requested by governor */ + tegra_update_cpu_speed(tegra_cpu_highest_speed()); } - mutex_unlock(&throttling_lock); + mutex_unlock(&tegra_cpu_lock); } EXPORT_SYMBOL_GPL(tegra_throttling_enable); +static unsigned int throttle_governor_speed(unsigned int requested_speed) +{ + return tegra_cpu_is_throttling() ? + min(requested_speed, freq_table[throttle_index].frequency) : + requested_speed; +} + +static ssize_t show_throttle(struct cpufreq_policy *policy, char *buf) +{ + return sprintf(buf, "%u\n", is_throttling); +} + +cpufreq_freq_attr_ro(throttle); + #ifdef CONFIG_DEBUG_FS static int throttle_debug_set(void *data, u64 val) { @@ -170,6 +192,7 @@ module_exit(tegra_cpu_debug_exit); #else /* CONFIG_TEGRA_THERMAL_THROTTLE */ #define tegra_cpu_is_throttling() (0) +#define throttle_governor_speed(requested_speed) (requested_speed) void tegra_throttling_enable(bool enable) { @@ -192,23 +215,6 @@ unsigned int tegra_getspeed(unsigned int cpu) return rate; } -#ifdef CONFIG_HAVE_ARM_TWD -static void tegra_cpufreq_rescale_twd_other_cpu(void *data) { - unsigned long new_rate = *(unsigned long *)data; - twd_recalc_prescaler(new_rate); -} - -static void tegra_cpufreq_rescale_twds(unsigned long new_rate) -{ - twd_recalc_prescaler(new_rate); - smp_call_function(tegra_cpufreq_rescale_twd_other_cpu, &new_rate, 1); -} -#else -static inline void tegra_cpufreq_rescale_twds(unsigned long new_rate) -{ -} -#endif - static int tegra_update_cpu_speed(unsigned long rate) { int ret = 0; @@ -234,9 +240,6 @@ static int tegra_update_cpu_speed(unsigned long rate) for_each_online_cpu(freqs.cpu) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - if (freqs.new > freqs.old) - tegra_cpufreq_rescale_twds(freqs.new * 1000); - #ifdef CONFIG_CPU_FREQ_DEBUG printk(KERN_DEBUG "cpufreq-tegra: transition: %u --> %u\n", freqs.old, freqs.new); @@ -249,9 +252,6 @@ static int tegra_update_cpu_speed(unsigned long rate) return ret; } - if (freqs.new < freqs.old) - tegra_cpufreq_rescale_twds(freqs.new * 1000); - for_each_online_cpu(freqs.cpu) cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); @@ -289,25 +289,7 @@ static int tegra_target(struct cpufreq_policy *policy, freq = freq_table[idx].frequency; target_cpu_speed[policy->cpu] = freq; - - new_speed = tegra_cpu_highest_speed(); - - /* Do not go above this frequency when throttling */ - - if (tegra_cpu_is_throttling()) { - unsigned int throttle_limit = - freq_table[THROTTLE_START_INDEX].frequency; - - if (new_speed > throttle_limit) { - if (tegra_getspeed(0) < throttle_limit) { - new_speed = throttle_limit; - } else { - ret = -EBUSY; - goto out; - } - } - } - + new_speed = throttle_governor_speed(tegra_cpu_highest_speed()); ret = tegra_update_cpu_speed(new_speed); out: mutex_unlock(&tegra_cpu_lock); @@ -381,6 +363,9 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy) static struct freq_attr *tegra_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, +#ifdef CONFIG_TEGRA_THERMAL_THROTTLE + &throttle, +#endif NULL, }; @@ -397,7 +382,13 @@ static struct cpufreq_driver tegra_cpufreq_driver = { static int __init tegra_cpufreq_init(void) { #ifdef CONFIG_TEGRA_THERMAL_THROTTLE - workqueue = create_singlethread_workqueue("cpu-tegra"); + /* + * High-priority, others flags default: not bound to a specific + * CPU, has rescue worker task (in case of allocation deadlock, + * etc.). Single-threaded. + */ + workqueue = alloc_workqueue("cpu-tegra", + WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1); if (!workqueue) return -ENOMEM; INIT_DELAYED_WORK(&throttle_work, tegra_throttle_work_func); diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index fd88ba3cd003..e85fd847fb5a 100755 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -568,13 +568,16 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int stop = (i == (num - 1)) ? 1 : 0; ret = tegra_i2c_xfer_msg(i2c_bus, &msgs[i], stop); if (ret) - break; + goto out; } + ret = i; + +out: clk_disable(i2c_dev->clk); rt_mutex_unlock(&i2c_dev->dev_lock); - return i; + return ret; } static u32 tegra_i2c_func(struct i2c_adapter *adap) diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 3c3a4754b7dc..946d3da7dc2c 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -369,7 +369,7 @@ static int get_topmost_window(u32 *depths, unsigned long *wins) { int idx, best = -1; - for_each_set_bit(idx, wins, sizeof(*wins)) { + for_each_set_bit(idx, wins, DC_N_WINDOWS) { if (best == -1 || depths[idx] < depths[best]) best = idx; } @@ -406,13 +406,15 @@ static u32 blend_2win(int idx, unsigned long behind_mask, u32* flags, int xy) static u32 blend_3win(int idx, unsigned long behind_mask, u32* flags) { unsigned long infront_mask; + int first; infront_mask = ~(behind_mask | BIT(idx)); infront_mask &= (BIT(DC_N_WINDOWS) - 1); + first = ffs(infront_mask) - 1; if (!infront_mask) return blend_topwin(flags[idx]); - else if (behind_mask && flags[ffs(infront_mask)]) + else if (behind_mask && first != -1 && flags[first]) return BLEND(NOKEY, DEPENDANT, 0x00, 0x00); else return BLEND(NOKEY, FIX, 0x0, 0x0); diff --git a/drivers/video/tegra/dc/dc_reg.h b/drivers/video/tegra/dc/dc_reg.h index f643ec9ec742..bd1750b78e44 100644 --- a/drivers/video/tegra/dc/dc_reg.h +++ b/drivers/video/tegra/dc/dc_reg.h @@ -396,7 +396,7 @@ #define BLEND_WEIGHT1(x) (((x) & 0xff) << 16) #define BLEND(key, control, weight0, weight1) \ (CKEY_ ## key | BLEND_CONTROL_ ## control | \ - BLEND_WEIGHT0(weight0) | BLEND_WEIGHT0(weight1)) + BLEND_WEIGHT0(weight0) | BLEND_WEIGHT1(weight1)) #define DC_WIN_HP_FETCH_CONTROL 0x714