Blackfin: SMP: implement cpu_freq support
authorGraf Yang <graf.yang@analog.com>
Thu, 28 Jan 2010 10:46:55 +0000 (10:46 +0000)
committerMike Frysinger <vapier@gentoo.org>
Fri, 18 Mar 2011 08:01:03 +0000 (04:01 -0400)
Re-use some of the existing cpu hotplugging code in the process.

Signed-off-by: Graf Yang <graf.yang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
arch/blackfin/include/asm/dpmc.h
arch/blackfin/include/asm/smp.h
arch/blackfin/mach-bf561/hotplug.c
arch/blackfin/mach-bf561/secondary.S
arch/blackfin/mach-common/cpufreq.c
arch/blackfin/mach-common/dpmc.c

index 3047120..edf2a2a 100644 (file)
@@ -125,6 +125,9 @@ void unset_dram_srfs(void);
 
 #define VRPAIR(vlev, freq) (((vlev) << 16) | ((freq) >> 16))
 
+#ifdef CONFIG_CPU_FREQ
+#define CPUFREQ_CPU 0
+#endif
 struct bfin_dpmc_platform_data {
        const unsigned int *tuple_tab;
        unsigned short tabsize;
index f5b5379..9dd4873 100644 (file)
@@ -34,7 +34,7 @@ extern unsigned long dcache_invld_count[NR_CPUS];
 void smp_icache_flush_range_others(unsigned long start,
                                   unsigned long end);
 #ifdef CONFIG_HOTPLUG_CPU
-void coreb_sleep(u32 sic_iwr0, u32 sic_iwr1, u32 sic_iwr2);
+void coreb_die(void);
 void cpu_die(void);
 void platform_cpu_die(void);
 int __cpu_disable(void);
index 4cd3b28..42fc085 100644 (file)
@@ -5,30 +5,27 @@
  * Licensed under the GPL-2 or later.
  */
 
+#include <linux/smp.h>
 #include <asm/blackfin.h>
-#include <asm/irq.h>
-#include <asm/smp.h>
-
-#define SIC_SYSIRQ(irq)        (irq - (IRQ_CORETMR + 1))
+#include <mach/pll.h>
 
 int hotplug_coreb;
 
 void platform_cpu_die(void)
 {
-       unsigned long iwr[2] = {0, 0};
-       unsigned long bank = SIC_SYSIRQ(IRQ_SUPPLE_0) / 32;
-       unsigned long bit = 1 << (SIC_SYSIRQ(IRQ_SUPPLE_0) % 32);
-
+       unsigned long iwr;
        hotplug_coreb = 1;
 
-       iwr[bank] = bit;
-
        /* disable core timer */
        bfin_write_TCNTL(0);
 
-       /* clear ipi interrupt IRQ_SUPPLE_0 */
+       /* clear ipi interrupt IRQ_SUPPLE_0 of CoreB */
        bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | (1 << (10 + 1)));
        SSYNC();
 
-       coreb_sleep(iwr[0], iwr[1], 0);
+       /* set CoreB wakeup by ipi0, iwr will be discarded */
+       bfin_iwr_set_sup0(&iwr, &iwr, &iwr);
+       SSYNC();
+
+       coreb_die();
 }
index ef9acf2..148e507 100644 (file)
@@ -162,39 +162,31 @@ ENTRY(_coreb_trampoline_start)
 ENDPROC(_coreb_trampoline_start)
 ENTRY(_coreb_trampoline_end)
 
+#ifdef CONFIG_HOTPLUG_CPU
 .section ".text"
-ENTRY(_set_sicb_iwr)
-       P0.H = hi(SICB_IWR0);
-       P0.L = lo(SICB_IWR0);
-       P1.H = hi(SICB_IWR1);
-       P1.L = lo(SICB_IWR1);
-       [P0] = R0;
-       [P1] = R1;
-       SSYNC;
-       RTS;
-ENDPROC(_set_sicb_iwr)
-
-ENTRY(_coreb_sleep)
+ENTRY(_coreb_die)
        sp.l = lo(INITIAL_STACK);
        sp.h = hi(INITIAL_STACK);
        fp = sp;
        usp = sp;
 
-       call _set_sicb_iwr;
-
        CLI R2;
        SSYNC;
        IDLE;
        STI R2;
 
        R0 = IWR_DISABLE_ALL;
-       R1 = IWR_DISABLE_ALL;
-       call _set_sicb_iwr;
+       P0.H = hi(SYSMMR_BASE);
+       P0.L = lo(SYSMMR_BASE);
+       [P0 + (SICB_IWR0 - SYSMMR_BASE)] = R0;
+       [P0 + (SICB_IWR1 - SYSMMR_BASE)] = R0;
+       SSYNC;
 
        p0.h = hi(COREB_L1_CODE_START);
        p0.l = lo(COREB_L1_CODE_START);
        jump (p0);
-ENDPROC(_coreb_sleep)
+ENDPROC(_coreb_die)
+#endif
 
 __INIT
 ENTRY(_coreb_start)
index f4cf11d..c33fb61 100644 (file)
@@ -16,8 +16,6 @@
 #include <asm/time.h>
 #include <asm/dpmc.h>
 
-#define CPUFREQ_CPU 0
-
 /* this is the table of CCLK frequencies, in Hz */
 /* .index is the entry in the auxillary dpm_state_table[] */
 static struct cpufreq_frequency_table bfin_freq_table[] = {
index 02c7efd..382099f 100644 (file)
@@ -61,17 +61,63 @@ err_out:
 }
 
 #ifdef CONFIG_CPU_FREQ
+# ifdef CONFIG_SMP
+static void bfin_idle_this_cpu(void *info)
+{
+       unsigned long flags = 0;
+       unsigned long iwr0, iwr1, iwr2;
+       unsigned int cpu = smp_processor_id();
+
+       local_irq_save_hw(flags);
+       bfin_iwr_set_sup0(&iwr0, &iwr1, &iwr2);
+
+       platform_clear_ipi(cpu, IRQ_SUPPLE_0);
+       SSYNC();
+       asm("IDLE;");
+       bfin_iwr_restore(iwr0, iwr1, iwr2);
+
+       local_irq_restore_hw(flags);
+}
+
+static void bfin_idle_cpu(void)
+{
+       smp_call_function(bfin_idle_this_cpu, NULL, 0);
+}
+
+static void bfin_wakeup_cpu(void)
+{
+       unsigned int cpu;
+       unsigned int this_cpu = smp_processor_id();
+       cpumask_t mask = cpu_online_map;
+
+       cpu_clear(this_cpu, mask);
+       for_each_cpu_mask(cpu, mask)
+               platform_send_ipi_cpu(cpu, IRQ_SUPPLE_0);
+}
+
+# else
+static void bfin_idle_cpu(void) {}
+static void bfin_wakeup_cpu(void) {}
+# endif
+
 static int
 vreg_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data)
 {
        struct cpufreq_freqs *freq = data;
 
+       if (freq->cpu != CPUFREQ_CPU)
+               return 0;
+
        if (val == CPUFREQ_PRECHANGE && freq->old < freq->new) {
+               bfin_idle_cpu();
                bfin_set_vlev(bfin_get_vlev(freq->new));
                udelay(pdata->vr_settling_time); /* Wait until Volatge settled */
-
-       } else if (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)
+               bfin_wakeup_cpu();
+       } else if (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) {
+               bfin_idle_cpu();
                bfin_set_vlev(bfin_get_vlev(freq->new));
+               bfin_wakeup_cpu();
+       }
 
        return 0;
 }